chore: remove commit message implementation plans from PR

This commit is contained in:
Mark IJbema
2026-02-19 16:26:09 +01:00
parent 561207562b
commit 764f7b203b
2 changed files with 0 additions and 1078 deletions
@@ -1,623 +0,0 @@
# Commit Message Generation — Implementation Plan
## 1. Overview
This plan adds **LLM-powered commit message generation** to the Kilo platform with three surfaces: a **CLI command** (`kilo commit`), an **HTTP route** (`POST /commit-message`), and a **VS Code SCM panel button**. All three delegate to a shared core module that handles git context gathering, prompt building, and LLM interaction.
### How this differs from the old implementation
The old extension (kilocode-5) called LLMs directly from the extension process using `buildApiHandler()` and provider-specific handlers. This architecture uses a **shared backend module** instead — the backend handles model selection, prompt building, and LLM communication. The extension is a thin HTTP client, and the CLI command calls the core directly.
| Aspect | Old extension | This architecture |
|--------|--------------|-------------------|
| LLM calls | Direct from extension → LLM provider | Shared core module in CLI backend |
| Auth | API keys stored in extension settings | OAuth/API key managed by CLI backend's `Auth` module |
| Model selection | User-configurable `commitMessageApiConfigId` | Automatic: `Provider.getSmallModel()` |
| Prompt location | In extension code | In shared core module |
| Surfaces | VS Code + JetBrains adapters | CLI command + HTTP route + VS Code |
| Git context | Gathered in extension | Gathered server-side in shared core |
| Prompt customization | Custom template override setting | Not in v1 |
---
## 2. Backend Investigation
Investigation of the CLI backend (`packages/opencode/`) revealed existing infrastructure that the commit message feature can reuse directly.
### What EXISTS in the backend
| Component | Location | Description |
|-----------|----------|-------------|
| `small_model` config | [`config.ts:1133`](../../packages/opencode/src/config/config.ts:1133) | Optional config field: `small_model: ModelId.describe("Small model to use for tasks like title generation").optional()` |
| `Provider.getSmallModel()` | [`provider.ts:1171`](../../packages/opencode/src/provider/provider.ts:1171) | Resolves small model with priority: user-configured → auto-detected → kilo fallback → undefined |
| Title generation | [`summary.ts:130`](../../packages/opencode/src/session/summary.ts:130) | Uses small_model + "title" agent — reference pattern |
| Agent prompts | `src/agent/prompt/` | Existing agents: ask, compaction, debug, explore, orchestrator, summary, title |
| Server routes | [`server.ts:227`](../../packages/opencode/src/server/server.ts:227) | Hono-based HTTP server with existing route patterns |
| `LLM.stream()` | Various | Streaming LLM infrastructure with auth already handled |
| CLI commands | [`src/cli/cmd/`](../../packages/opencode/src/cli/cmd/) | 18 commands using yargs + [`cmd()`](../../packages/opencode/src/cli/cmd/cmd.ts:5) helper |
| `bootstrap()` | [`bootstrap.ts:4`](../../packages/opencode/src/cli/bootstrap.ts:4) | Initializes project context so Provider/LLM APIs are available |
### What does NOT exist
- No commit message generation logic
- No generic chat completions endpoint
- No "commit" agent or prompt
- No `kilo commit` CLI command
### Implications
Because `Provider.getSmallModel()`, `LLM.stream()`, `bootstrap()`, and the agent prompt infrastructure already exist, the recommended approach is a **shared core module** that both the HTTP route and CLI command delegate to. The extension calls the HTTP route; the CLI command calls the core directly with no HTTP round-trip.
---
## 3. Architecture
### Three-Layer Design
```
+-----------------------------------------------------+
| Shared Core Module |
| packages/opencode/src/commit-message/ |
| - generate.ts git context + LLM call |
| - git-context.ts diff/branch/log gathering |
| - types.ts CommitMessageRequest/Response |
+----------+------------------+-----------+-----------+
| | |
+------+------+ +------+------+ +-------------------+
| CLI Command | | HTTP Route | | VS Code Extension |
| kilo commit | | POST /commit| | SCM panel button |
| cmd/commit.ts| | -message | | calls HTTP route |
+-------------+ +-------------+ +-------------------+
```
**Key design decision:** Git context is gathered **server-side** in the shared core module. Both the CLI command and HTTP route run in the backend process with filesystem access. The VS Code extension does NOT gather git context — it sends the workspace path and the backend does the rest. This avoids duplicating git logic across surfaces.
### Request Flow — VS Code
```mermaid
sequenceDiagram
participant User
participant VSCode as VS Code SCM Panel
participant HTTP as HttpClient
participant Route as POST /commit-message
participant Core as Shared Core Module
participant Git as Git CLI
participant SmallModel as Provider.getSmallModel
participant LLM as LLM Provider
User->>VSCode: Click generate button
VSCode->>HTTP: generateCommitMessage with path
HTTP->>Route: POST /commit-message
Route->>Core: generateCommitMessage with path
Core->>Git: git diff, branch, log
Git-->>Core: diffs + metadata
Core->>SmallModel: Resolve model
SmallModel-->>Core: Model ID
Core->>Core: Build prompt from template + git context
Core->>LLM: LLM.stream with commit prompt
LLM-->>Core: Generated message
Core-->>Route: Commit message string
Route-->>HTTP: JSON response
HTTP-->>VSCode: Commit message string
VSCode-->>User: Message appears in commit input box
```
### Request Flow — CLI
```mermaid
sequenceDiagram
participant User
participant CLI as kilo commit
participant Bootstrap as bootstrap
participant Core as Shared Core Module
participant Git as Git CLI
participant SmallModel as Provider.getSmallModel
participant LLM as LLM Provider
User->>CLI: kilo commit
CLI->>Bootstrap: Initialize project context
Bootstrap-->>CLI: Context ready
CLI->>Core: generateCommitMessage with cwd
Core->>Git: git diff, branch, log
Git-->>Core: diffs + metadata
Core->>SmallModel: Resolve model
SmallModel-->>Core: Model ID
Core->>Core: Build prompt from template + git context
Core->>LLM: LLM.stream with commit prompt
LLM-->>Core: Generated message
Core-->>CLI: Commit message string
CLI-->>User: Print to stdout
```
### Component Diagram
```mermaid
graph TD
CMD[kilo commit CLI] --> CORE[Shared Core: generate.ts]
ROUTE[POST /commit-message route] --> CORE
EXT[VS Code Extension] --> HC[HttpClient.generateCommitMessage]
HC --> ROUTE
CORE --> GC[git-context.ts]
CORE --> SM[Provider.getSmallModel]
CORE --> LLMS[LLM.stream]
CMD --> BOOT[bootstrap]
EXT --> CS[KiloConnectionService]
CS --> HC
style CORE fill:#ff9,stroke:#f90,stroke-width:2px
style GC fill:#ff9,stroke:#f90,stroke-width:2px
style ROUTE fill:#ff9,stroke:#f90,stroke-width:2px
style CMD fill:#ff9,stroke:#f90,stroke-width:2px
style HC fill:#ff9,stroke:#f90,stroke-width:2px
```
Yellow-highlighted components are new code that needs to be written.
### Design Rationale: Backend vs Gateway
Two approaches were considered:
| Aspect | Option A: Backend endpoint — RECOMMENDED | Option B: Gateway endpoint |
|--------|------------------------------------------|---------------------------|
| Endpoint | `POST /commit-message` in opencode server | `POST /kilo/chat` in kilo-gateway |
| Model selection | Backend uses `Provider.getSmallModel()` directly | Extension must read config and pass model |
| Prompt | Backend shared core module | Extension builds prompt locally |
| Auth | Handled by existing `LLM.stream()` | Separate gateway auth flow |
| Consistency | Same pattern as title generation | Different pattern from other features |
| CLI reuse | CLI command shares the same core logic | CLI would need its own implementation |
Option A is recommended because it enables a shared core used by both command-line and HTTP surfaces, reuses existing infrastructure, and minimizes extension-side complexity.
---
## 4. Implementation Phases
### Phase 1: Shared Core Module (packages/opencode)
**Scope:** Core logic for generating commit messages, shared by all surfaces.
**Files to create:**
| File | Purpose |
|------|---------|
| `src/commit-message/generate.ts` | Main `generateCommitMessage()` function — orchestrates git context, prompt building, LLM call |
| `src/commit-message/git-context.ts` | Git CLI operations: diff, branch, log, file status |
| `src/commit-message/types.ts` | `CommitMessageRequest` and `CommitMessageResponse` types |
**`generateCommitMessage()` function:**
```typescript
// src/commit-message/generate.ts
import { getGitContext } from "./git-context"
import type { CommitMessageRequest, CommitMessageResponse } from "./types"
export async function generateCommitMessage(
request: CommitMessageRequest
): Promise<CommitMessageResponse> {
// 1. Gather git context from the working directory
const context = await getGitContext(request.path, request.selectedFiles)
// 2. Resolve small model via Provider.getSmallModel()
// 3. Build prompt: Conventional Commits template + git context
// 4. Call LLM.stream() with the commit prompt
// 5. Clean and return the commit message string
return { message: cleanedMessage }
}
```
**`getGitContext()` function:**
```typescript
// src/commit-message/git-context.ts
export interface GitContext {
stagedFiles: FileChange[]
diffs: Map<string, string>
branch: string
recentCommits: string[]
}
export interface FileChange {
status: "added" | "modified" | "deleted" | "renamed" | "untracked"
path: string
}
export async function getGitContext(
repoPath: string,
selectedFiles?: string[]
): Promise<GitContext>
```
**Types:**
```typescript
// src/commit-message/types.ts
export interface CommitMessageRequest {
path: string // workspace/repo path
selectedFiles?: string[] // optional file subset
}
export interface CommitMessageResponse {
message: string // the generated commit message
}
```
### Phase 2: HTTP Route (packages/opencode)
**Scope:** `POST /commit-message` route that delegates to the shared core.
**Files to create/modify:**
| File | Change |
|------|--------|
| `src/server/routes/commit-message.ts` | New route handler — validates request, calls `generateCommitMessage()`, returns JSON |
| [`src/server/server.ts`](../../packages/opencode/src/server/server.ts) | Register `POST /commit-message` route |
**HTTP interface:**
Request:
```typescript
POST /commit-message
{
path: string // workspace/repo path
selectedFiles?: string[] // optional file subset
}
```
Response:
```typescript
{
message: string // the generated commit message
}
```
The route handler is thin — it validates the request body, calls [`generateCommitMessage()`](../../packages/opencode/src/commit-message/generate.ts), and returns the result as JSON.
### Phase 3: CLI Command (packages/opencode)
**Scope:** `kilo commit` command that delegates to the shared core.
**Files to create/modify:**
| File | Change |
|------|--------|
| `src/cli/cmd/commit.ts` | New CLI command using [`cmd()`](../../packages/opencode/src/cli/cmd/cmd.ts:5) helper |
| [`src/index.ts`](../../packages/opencode/src/index.ts:122) | Register `.command(CommitCommand)` |
**Command:** `kilo commit [--auto]`
| Flag | Default | Description |
|------|---------|-------------|
| `--auto` | `false` | Skip confirmation, auto-stage + commit with the generated message |
| (no flags) | — | Generate and print the commit message to stdout |
**Usage examples:**
```bash
# Generate and print to stdout
kilo commit
# Pipe to git commit
kilo commit | git commit -F -
# Auto-stage and commit
kilo commit --auto
```
**Implementation:**
```typescript
// src/cli/cmd/commit.ts
import { cmd } from "./cmd"
import { bootstrap } from "../bootstrap"
import { generateCommitMessage } from "../../commit-message/generate"
export const CommitCommand = cmd({
command: "commit",
describe: "Generate a commit message using AI",
builder: (yargs) =>
yargs.option("auto", {
type: "boolean",
describe: "Auto-stage and commit with the generated message",
default: false,
}),
handler: async (args) => {
await bootstrap(process.cwd(), async () => {
const result = await generateCommitMessage({
path: process.cwd(),
})
if (args.auto) {
// Stage all changes + git commit -m <message>
execSync("git add -A", { cwd: process.cwd() })
execSync(`git commit -m ${shellEscape(result.message)}`, {
cwd: process.cwd(),
stdio: "inherit",
})
} else {
// Print to stdout for piping
process.stdout.write(result.message + "\n")
}
})
},
})
```
**Registration in [`src/index.ts`](../../packages/opencode/src/index.ts:122):**
```typescript
import { CommitCommand } from "./cli/cmd/commit"
// ...
.command(CommitCommand)
```
### Phase 4: VS Code Extension (packages/kilo-vscode)
**Scope:** Extension-side changes to call the backend endpoint and display results.
#### 4a. HttpClient — `generateCommitMessage()` method
New method in [`src/services/cli-backend/http-client.ts`](src/services/cli-backend/http-client.ts):
```typescript
async generateCommitMessage(request: {
path: string
selectedFiles?: string[]
}): Promise<string>
```
- POST to `${this.baseUrl}/commit-message`
- Returns the commit message string from the JSON response
- No SSE parsing — simple request/response
#### 4b. Commit Message Service
New service at `src/services/commit-message/`:
| File | Purpose |
|------|---------|
| [`index.ts`](src/services/commit-message/index.ts) | `registerCommitMessageService()` entry point |
| [`CommitMessageService.ts`](src/services/commit-message/CommitMessageService.ts) | Orchestrates HTTP call → write to SCM input box |
**CommitMessageService responsibilities:**
1. Determine the workspace/repo path from VS Code's git extension
2. Optionally determine selected files from the SCM view
3. Call `connectionService.getHttpClient().generateCommitMessage({ path, selectedFiles })`
4. Clean response (strip code blocks, quotes if present)
5. Write result to `repository.inputBox.value`
Note: The extension does NOT gather git context — that's handled server-side by the shared core module. This keeps the extension simple.
#### 4c. VS Code Integration
**Changes to [`package.json`](package.json):**
```json
{
"contributes": {
"commands": [
{
"command": "kilo-code.new.generateCommitMessage",
"title": "Generate Commit Message",
"icon": "$(sparkle)",
"category": "Kilo Code"
}
],
"menus": {
"scm/title": [
{
"command": "kilo-code.new.generateCommitMessage",
"group": "navigation",
"when": "scmProvider == git"
}
]
}
}
}
```
**Changes to [`src/extension.ts`](src/extension.ts):**
```typescript
import { registerCommitMessageService } from "./services/commit-message"
// In activate():
registerCommitMessageService(context, connectionService)
```
**Progress UI:**
```typescript
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.SourceControl, title: "Generating commit message..." },
async () => { /* generation logic */ }
)
```
### Phase 5: Testing
**Backend tests (PR A):**
- `packages/opencode/src/commit-message/__tests__/generate.spec.ts`
- `packages/opencode/src/commit-message/__tests__/git-context.spec.ts`
- `packages/opencode/src/cli/cmd/__tests__/commit.spec.ts`
**Extension tests (PR B):**
- `src/services/commit-message/__tests__/CommitMessageService.spec.ts`
---
## 5. File-by-File Changes
### New Files — Backend (PR A)
| File | Purpose |
|------|---------|
| `packages/opencode/src/commit-message/generate.ts` | Main `generateCommitMessage()` function — shared core |
| `packages/opencode/src/commit-message/git-context.ts` | Git CLI operations: diff, branch, log, file status, lock file exclusion |
| `packages/opencode/src/commit-message/types.ts` | `CommitMessageRequest`, `CommitMessageResponse`, `GitContext`, `FileChange` |
| `packages/opencode/src/server/routes/commit-message.ts` | HTTP route handler delegating to shared core |
| `packages/opencode/src/cli/cmd/commit.ts` | `kilo commit` CLI command delegating to shared core |
### Modified Files — Backend (PR A)
| File | Change |
|------|--------|
| [`packages/opencode/src/server/server.ts`](../../packages/opencode/src/server/server.ts) | Register `POST /commit-message` route |
| [`packages/opencode/src/index.ts`](../../packages/opencode/src/index.ts:122) | Register `.command(CommitCommand)` |
### New Files — Extension (PR B)
| File | Purpose |
|------|---------|
| `src/services/commit-message/index.ts` | `registerCommitMessageService()` entry point |
| `src/services/commit-message/CommitMessageService.ts` | Orchestrates HTTP call → write to SCM input box |
| `src/services/commit-message/__tests__/CommitMessageService.spec.ts` | Service tests |
### Modified Files — Extension (PR B)
| File | Change |
|------|--------|
| [`package.json`](package.json) | Add command + `scm/title` menu contribution |
| [`src/extension.ts`](src/extension.ts) | Import and call `registerCommitMessageService()` |
| [`src/services/cli-backend/http-client.ts`](src/services/cli-backend/http-client.ts) | Add `generateCommitMessage()` method |
---
## 6. Git Context Gathering (Server-Side)
The shared core module in [`git-context.ts`](../../packages/opencode/src/commit-message/git-context.ts) runs git CLI commands against the provided workspace path. This runs in the backend process (CLI or HTTP server), which has direct filesystem access.
### Git Commands
| Command | Purpose |
|---------|---------|
| `git rev-parse --show-toplevel` | Find repo root |
| `git diff --name-status --cached` | List staged file changes with status |
| `git status --porcelain` | Fallback: list all changes when nothing is staged |
| `git diff --cached -- <file>` | Per-file diff content (staged) |
| `git diff -- <file>` | Per-file diff content (unstaged fallback) |
| `git branch --show-current` | Current branch name |
| `git log --oneline -5` | Last 5 commit messages for context |
### File Processing Rules
1. **Lock file exclusion:** Files matching lock file patterns are excluded
2. **Binary files:** Replaced with placeholder `"Binary file <path> has been modified"`
3. **Untracked files:** Replaced with placeholder `"New untracked file: <path>"`
4. **Staged vs unstaged:** Prefers staged changes (`--cached`); falls back to all changes if nothing is staged
5. **Selected files:** If `selectedFiles` is provided, only those files are included in the diff
6. **Large diffs:** Truncate individual file diffs at ~4000 chars; include file name even if diff is cut
### Lock File Patterns
```typescript
const LOCK_FILE_PATTERNS = [
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"Cargo.lock",
"poetry.lock",
"Pipfile.lock",
"Gemfile.lock",
"composer.lock",
"go.sum",
"bun.lockb",
// ... ~50 more patterns
]
```
---
## 7. Prompt Engineering
The Conventional Commits prompt is embedded in the shared core module (either as an inline template in [`generate.ts`](../../packages/opencode/src/commit-message/generate.ts) or as a separate `prompt.txt` file alongside it), consistent with the pattern used by title generation in [`summary.ts`](../../packages/opencode/src/session/summary.ts:130).
### Commit Prompt Template
```
You are a commit message generator. Generate a concise commit message following the Conventional Commits specification.
Format: type(scope): description
Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
Rules:
- Keep the subject line under 72 characters
- Use imperative mood ("add feature" not "added feature")
- Do not end the subject with a period
- The scope is optional but encouraged when changes are focused
- For multiple unrelated changes, use the most significant change as the type
- Output ONLY the commit message, nothing else
```
The shared core combines this system prompt with the gathered git context to form the full LLM request.
---
## 8. Error Handling
### CLI errors
| Scenario | Handling |
|----------|---------|
| Not in a git repository | Print error to stderr, exit code 1 |
| No changes detected | Print message to stderr, exit code 0 |
| No provider configured | Print setup instructions to stderr, exit code 1 |
| LLM request fails | Print error to stderr, exit code 1 |
### HTTP route errors
| Scenario | Handling |
|----------|---------|
| Missing `path` in request | 400 Bad Request |
| Path is not a git repository | 400 Bad Request with message |
| No changes detected | 200 with empty message + info field |
| No `small_model` available | Backend falls back through auto-detection chain |
| LLM request fails | 500 Internal Server Error with message |
### VS Code extension errors
| Scenario | Handling |
|----------|---------|
| CLI backend not connected | `vscode.window.showErrorMessage` — check `connectionService` state |
| Not authenticated | `vscode.window.showErrorMessage` with sign-in prompt |
| Backend returns error | `vscode.window.showErrorMessage` with error details |
| Empty response from backend | Show error; do not write to input box |
| User cancels during progress | Abort gracefully via `CancellationToken` |
---
## 9. Simplifications (v1 scope)
What we are **NOT** implementing in v1:
| Feature | Reason |
|---------|--------|
| JetBrains adapter | VS Code extension only — no JetBrains in this codebase |
| User model selection | Backend uses `Provider.getSmallModel()` automatically |
| Custom prompt template override | Keep it simple for v1; can add later |
| `.kilocode-ignore` support | Can add later; lock file exclusion covers the main case |
| Re-generation detection | Can add in v2; first version generates fresh each time |
| Custom instructions from rules files | Can add later |
| Concurrent request debouncing | Low priority for v1 |
| `--auto` with selective staging | v1 `--auto` stages everything; selective staging can come later |
| Interactive confirmation in CLI | v1 just prints to stdout; interactive mode can come later |
---
## 10. PR Sequence
1. **PR A: Backend changes** (`packages/opencode/`) — Shared core module + HTTP route + CLI command. Contains:
- `src/commit-message/generate.ts`, `git-context.ts`, `types.ts` (shared core)
- `src/server/routes/commit-message.ts` + registration in `server.ts`
- `src/cli/cmd/commit.ts` + registration in `src/index.ts`
- Backend tests
- Can be reviewed/merged independently. No extension changes.
2. **PR B: Extension changes** (`packages/kilo-vscode/`) — VS Code integration. Contains:
- `generateCommitMessage()` in `http-client.ts`
- `CommitMessageService` + registration
- Command + SCM menu in `package.json`
- Extension tests
- Depends on PR A being deployed.
@@ -1,455 +0,0 @@
# Commit Message Generation — Reimplementation Guide
## Overview
The commit message generation feature allows users to automatically generate [Conventional Commits](https://www.conventionalcommits.org/) messages from their staged (or unstaged) git changes using an LLM. It is accessible from:
- **VS Code**: The Source Control panel title bar and the command palette (`Kilo Code: Generate Commit Message`)
- **JetBrains**: A button in the commit dialog
The feature collects git context (diffs, branch name, recent commits), builds a prompt, sends it to an LLM, and writes the resulting commit message into the IDE's commit input box.
> **Note:** No screenshots of this feature were found in the repository. A screenshot showing the SCM panel button and generated message would be helpful here.
---
## Architecture
The current implementation has a clean layered architecture that should be preserved. The key change is that the LLM integration layer will use a different calling mechanism — everything else can be largely reused or adapted.
### Layered Design
```mermaid
graph TD
A[IDE Integration Layer] --> B[Orchestrator]
B --> C[Git Context Service]
B --> D[Commit Message Generator]
D --> E[Prompt Builder]
D --> F[LLM Integration Point]
D --> G[Response Cleaner]
A --> H[VS Code Adapter]
A --> I[JetBrains Adapter]
style F fill:#ff9,stroke:#f90,stroke-width:3px
```
### Component Responsibilities
| Layer | Component | Responsibility |
|-------|-----------|---------------|
| Entry Point | `registerCommitMessageProvider()` | Wires everything up during extension activation |
| IDE Integration | `CommitMessageProvider` | Registers IDE commands, dispatches to the correct adapter |
| Adapter | `VSCodeCommitMessageAdapter` | VS Code SCM panel progress + writes to input box |
| Adapter | `JetBrainsCommitMessageAdapter` | Returns result string to Kotlin host |
| Orchestrator | `CommitMessageOrchestrator` | Sequences: git discovery → diff collection → AI generation → result delivery |
| Business Logic | `CommitMessageGenerator` | Builds prompt, calls LLM, cleans response |
| Git Operations | `GitExtensionService` | Runs git CLI commands, collects diffs and metadata |
| Utilities | `exclusionUtils` | Filters lock files from diffs |
---
## Components to Implement
### 1. Entry Point — `registerCommitMessageProvider()`
**Responsibility:** Called during extension activation to wire up all components and register commands.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/index.ts`
**Key details:**
- Creates instances of `GitExtensionService`, `CommitMessageGenerator`, `CommitMessageOrchestrator`
- Creates the appropriate adapter(s) based on the IDE environment
- Registers VS Code commands and disposables
- Returns disposables for cleanup
### 2. CommitMessageProvider — Command Router
**Responsibility:** Registers VS Code commands and dispatches generation requests to the correct adapter.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/CommitMessageProvider.ts`
**Interface:**
```typescript
interface CommitMessageProvider {
// Register VS Code commands and return disposables
register(): vscode.Disposable[]
// Handle generation request from either IDE
handleGenerateRequest(context?: { workspacePath?: string; selectedFiles?: string[] }): Promise<void>
}
```
**Key details:**
- Registers command `kilo-code.vsc.generateCommitMessage` in the `scm/title` menu
- Registers command `kilo-code.jetbrains.generateCommitMessage` for JetBrains RPC
- Determines which adapter to use based on the calling context
### 3. CommitMessageOrchestrator — Workflow Coordinator
**Responsibility:** Sequences the full workflow from git discovery through to delivering the result.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/CommitMessageOrchestrator.ts`
**Interface:**
```typescript
interface CommitMessageOrchestrator {
generate(options?: {
workspacePath?: string
selectedFiles?: string[]
}): Promise<CommitMessageResult>
}
interface CommitMessageResult {
message: string
regenerated: boolean
}
```
**Workflow sequence:**
1. Discover the git repository root
2. Collect git context via `GitExtensionService`
3. Check for re-generation (same diff as last time)
4. Call `CommitMessageGenerator.generateMessage()` with the context
5. Return the result to the adapter for delivery
### 4. CommitMessageGenerator — Business Logic
**Responsibility:** Builds the prompt, calls the LLM, and cleans the response.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/CommitMessageGenerator.ts`
**Interface:**
```typescript
interface CommitMessageGenerator {
generateMessage(context: GitContext, options?: {
isRegeneration?: boolean
previousMessage?: string
}): Promise<string>
}
```
**Key details:**
- Constructs the prompt using `supportPrompt.create("COMMIT_MESSAGE", ...)` or equivalent
- Loads custom instructions for the "commit" context
- Handles re-generation by prepending "generate a completely different message"
- Calls the LLM (see **Integration Point** below)
- Cleans the response: strips code block markers and surrounding quotes
**Response cleaning logic:**
```typescript
function cleanResponse(raw: string): string {
let cleaned = raw.trim()
// Strip code block markers
cleaned = cleaned.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "")
// Strip surrounding quotes
cleaned = cleaned.replace(/^["']|["']$/g, "")
return cleaned.trim()
}
```
### 5. GitExtensionService — Git Operations
**Responsibility:** Runs git CLI commands to gather all context needed for prompt construction.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/GitExtensionService.ts`
**Interface:**
```typescript
interface GitContext {
stagedFiles: FileChange[]
diffs: Map<string, string> // filepath → diff content
branch: string
recentCommits: string[] // last 5 commit summaries
}
interface FileChange {
status: "added" | "modified" | "deleted" | "renamed" | "untracked"
path: string
}
interface GitExtensionService {
getGitContext(repoPath: string, selectedFiles?: string[]): Promise<GitContext>
}
```
**See section: [Git Context Gathering](#git-context-gathering) for full details.**
### 6. Exclusion Utilities
**Responsibility:** Filters lock files and ignored files from the diff set.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/exclusionUtils.ts`
**Key details:**
- Uses the `ignore` library to match 60+ lock file patterns
- Patterns include `package-lock.json`, `yarn.lock`, `Cargo.lock`, `poetry.lock`, `pnpm-lock.yaml`, etc.
- Also respects `.kilocode-ignore` / `.roo-ignore` via `RooIgnoreController`
### 7. IDE Adapters
**VS Code Adapter:**
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/adapters/VSCodeCommitMessageAdapter.ts`
```typescript
interface VSCodeCommitMessageAdapter {
generate(orchestrator: CommitMessageOrchestrator): Promise<void>
}
```
- Shows progress via `vscode.window.withProgress(ProgressLocation.SourceControl)`
- Writes result to `repository.inputBox.value`
**JetBrains Adapter:**
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/adapters/JetBrainsCommitMessageAdapter.ts`
```typescript
interface JetBrainsCommitMessageAdapter {
generate(orchestrator: CommitMessageOrchestrator, workspacePath: string, selectedFiles: string[]): Promise<{ message: string }>
}
```
- Returns the message string for the Kotlin host to use
---
## LLM Integration Point
> **⚠️ INTEGRATION POINT — This is the part that will differ from the current implementation.**
### Current Implementation (for reference only)
The current code calls `singleCompletionHandler(config, prompt)` which internally uses `buildApiHandler(apiConfig)` to create a provider-specific handler. If the handler has a `completePrompt()` method, it uses single-shot completion; otherwise it streams and collects the full response. This mechanism **will not be used** in the new implementation.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/CommitMessageGenerator.ts` — see `callAIForCommitMessage()`
### Required Contract
The LLM integration must satisfy this contract:
```typescript
interface CommitMessageLLMProvider {
/**
* Send a prompt to the LLM and receive a complete text response.
*
* This is a non-streaming, single-shot completion call.
* The full response must be collected before returning.
*
* @param prompt - The complete prompt string including system instructions
* and git context
* @param config - Which model/provider to use. May be a dedicated
* commit message profile or the default profile.
* @returns The raw LLM response text (will be cleaned by the caller)
* @throws If the LLM call fails (network error, auth error, etc.)
*/
complete(prompt: string, config: LLMConfig): Promise<string>
}
interface LLMConfig {
/** The API config ID — either `commitMessageApiConfigId` or the default */
configId: string
/** Any additional model parameters if needed */
[key: string]: unknown
}
```
### What the caller provides
- **Input:** A single prompt string (typically 1002000 tokens depending on diff size). The prompt includes system instructions, git context, and any custom instructions.
- **Config:** An identifier for which API configuration/model to use. This supports the dedicated `commitMessageApiConfigId` setting which lets users pick a different (often cheaper/faster) model for commit messages.
### What the caller expects
- **Output:** A single string containing the commit message. May include code block markers or quotes which will be stripped by the response cleaner.
- **Behavior:** Non-streaming. The call should block until the full response is available.
- **Errors:** Should throw on failure so the orchestrator can catch and display an error to the user.
### Configuration Resolution
The config resolution order is:
1. If `commitMessageApiConfigId` is set in global settings → use that API profile
2. Otherwise → use the default/active API profile
---
## Git Context Gathering
This part is **largely reusable** from the current implementation. It uses `spawnSync` to run git CLI commands.
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/GitExtensionService.ts`
### Git Commands Used
| Command | Purpose |
|---------|---------|
| `git diff --name-status --cached` | List staged file changes with status |
| `git status --porcelain` | List all changes (fallback when nothing is staged) |
| `git diff [--cached] -- <file>` | Per-file diff content |
| `git branch --show-current` | Current branch name |
| `git log --oneline -5` | Last 5 commit messages for context |
### File Processing Rules
1. **Lock file exclusion:** Files matching any of the 60+ lock file patterns are excluded (see `exclusionUtils`)
2. **Ignore file exclusion:** Files matching `.kilocode-ignore` / `.roo-ignore` patterns are excluded via `shouldIncludeFile()`
3. **Binary files:** Replaced with placeholder text `"Binary file <path> has been modified"`
4. **Untracked files:** Replaced with placeholder text `"New untracked file: <path>"`
5. **Staged vs unstaged:** Prefers staged changes (`--cached`); falls back to all changes if nothing is staged
6. **Selected files (JetBrains):** When the JetBrains adapter provides `selectedFiles`, only those files are included
### Fallback Behavior
If no staged changes exist, the service falls back to `git status --porcelain` to capture all modified/untracked files. This ensures the feature works even when users haven't explicitly staged changes.
---
## Prompt Engineering
### Prompt Template
The prompt is built using `supportPrompt.create("COMMIT_MESSAGE", { gitContext, customInstructions })`. The template is a ~70-line Conventional Commits guide that includes:
1. **System instruction:** You are a commit message generator following Conventional Commits format
2. **Format specification:** `type(scope): description` with allowed types (`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`)
3. **Rules:** Keep subject under 72 chars, use imperative mood, no period at end, etc.
4. **Git context injection:** Branch name, recent commits, file changes, diffs
5. **Custom instructions:** User-defined instructions from `.kilocode/rules/` for the "commit" context
### Custom Instructions
Custom instructions are loaded via `addCustomInstructions()` for the `"commit"` mode context. Users can place files in `.kilocode/rules/` that apply to commit message generation.
### Re-generation Logic
When the user requests a new message for the same diff:
1. The orchestrator detects that the diff hash matches the previous generation
2. It prepends to the prompt: `"GENERATE A COMPLETELY DIFFERENT COMMIT MESSAGE. The previous message was: <previous_message>"`
3. This ensures variety when the user isn't satisfied with the first suggestion
### Prompt Template Override
Users can override the entire prompt template via the `customSupportPrompts.COMMIT_MESSAGE` setting. This allows complete customization of the commit message format and style.
---
## IDE Integration
### VS Code
**Command registration:**
```typescript
// In package.json contributes.commands
{ "command": "kilo-code.vsc.generateCommitMessage", "title": "Generate Commit Message" }
// In package.json contributes.menus
{ "scm/title": [{ "command": "kilo-code.vsc.generateCommitMessage" }] }
```
**Progress reporting:**
```typescript
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.SourceControl, title: "Generating commit message..." },
async () => { /* ... generation logic ... */ }
)
```
**Result delivery:**
```typescript
repository.inputBox.value = generatedMessage
```
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/adapters/VSCodeCommitMessageAdapter.ts`
### JetBrains
**Kotlin side:** A `CommitMessageHandler` adds a button to the commit dialog. When clicked, it sends an RPC command.
**RPC command:** `kilo-code.jetbrains.generateCommitMessage` with arguments `[workspacePath, selectedFiles]`
**Result delivery:** The result is returned via RPC to Kotlin which calls `panel.setCommitMessage(result.message)`
**Reference:** `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/adapters/JetBrainsCommitMessageAdapter.ts`
---
## Configuration
| Setting | Type | Description |
|---------|------|-------------|
| `commitMessageApiConfigId` | `string` | ID of a dedicated API profile for commit messages. Allows using a cheaper/faster model. |
| `customSupportPrompts.COMMIT_MESSAGE` | `string` | Override the entire commit message prompt template |
| Custom instructions in `.kilocode/rules/` | files | Per-project or global instructions applied to the "commit" context |
| `.kilocode-ignore` / `.roo-ignore` | files | File exclusion patterns — excluded files won't appear in diffs |
### Settings UI
**Reference:** `/Users/mark/dev/kilo/kilocode-5/webview-ui/src/components/settings/CommitMessagePromptSettings.tsx`
A dropdown in the Settings panel allows users to select which API configuration to use for commit messages. This is separate from the main chat model selection.
---
## Error Handling
### Edge Cases to Handle
| Scenario | Handling |
|----------|----------|
| No git repository found | Show error message: "No git repository found in the current workspace" |
| No changes detected | Show info message: "No changes to generate a commit message for" |
| Empty diff after filtering | Show info message: "All changed files are excluded by lock file or ignore rules" |
| LLM call fails (network/auth) | Show error with details; do not write to input box |
| LLM returns empty response | Retry once; if still empty, show error |
| Very large diff (token limit) | Truncate diffs, prioritize staged files, include file names even if diffs are cut |
| Git command fails | Log the error, attempt to continue with partial context |
| User cancels during progress | Abort the LLM call if possible, clean up gracefully |
| Binary files in diff | Replace with placeholder text instead of including binary content |
| Concurrent generation requests | Debounce or queue — don't send multiple simultaneous LLM requests |
### Error Display
- **VS Code:** Use `vscode.window.showErrorMessage()` or `showInformationMessage()` as appropriate
- **JetBrains:** Return error in the RPC response for Kotlin-side display
---
## Testing Strategy
### Unit Tests
**Reference for existing tests:**
- `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/__tests__/CommitMessageGenerator.spec.ts`
- `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/__tests__/GitExtensionService.spec.ts`
- `/Users/mark/dev/kilo/kilocode-5/src/services/commit-message/__tests__/progress-reporting.spec.ts`
| Component | What to Test |
|-----------|-------------|
| `CommitMessageGenerator` | Prompt construction, response cleaning, re-generation logic, custom instructions injection |
| `GitExtensionService` | Parsing of `git diff --name-status` output, `git status --porcelain` output, branch name extraction, handling of binary files and untracked files |
| `exclusionUtils` | Lock file pattern matching — ensure all 60+ patterns work, edge cases with nested paths |
| `CommitMessageOrchestrator` | Full workflow sequencing, re-generation detection, error propagation |
| `VSCodeCommitMessageAdapter` | Progress reporting, writing to input box, error display |
| `JetBrainsCommitMessageAdapter` | Correct return format, error handling |
| Response cleaner | Stripping code blocks, quotes, whitespace normalization |
### Integration Tests
| Test | Description |
|------|-------------|
| Full generation flow | Mock the LLM call, verify end-to-end from git context to result delivery |
| Re-generation | Verify that requesting a new message for the same diff includes the "different message" instruction |
| Config resolution | Verify `commitMessageApiConfigId` is used when set, falls back to default otherwise |
| Large diff handling | Verify truncation behavior with oversized diffs |
### Mocking Strategy
- **Git commands:** Mock `spawnSync` to return predefined git output
- **LLM calls:** Mock the LLM integration point to return controlled responses
- **VS Code API:** Mock `vscode.window.withProgress`, `repository.inputBox`, and command registration
- **File system:** Mock ignore file reading for exclusion tests
### Test File Convention
Per project convention, test files should use `.spec.ts` extension and live in `__tests__/` directories adjacent to the source code.