diff options
| author | bndw <ben@bdw.to> | 2026-02-28 20:45:23 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-02-28 20:45:23 -0800 |
| commit | 0da42e4fa414ab3268d4f71896455097239f8590 (patch) | |
| tree | 72e951bdf8b591f4c949c6fd687ef780580c8783 /src/main/git.ts | |
| parent | dc4156fec54a8efdab84834fe2f5bc90120e32c1 (diff) | |
feat: Complete 9 tasks
- ✅ **Change 1** — `src/main/git.ts`: Add `LOCK_FILES`, `buildTaskSubject`, `getStagedFileNames`, `buildFileSubject` helpers; rewrite `commitMsg` block in `autoCommitTurn`
- ✅ **Change 2a** — `src/main/ipc/handlers.ts`: Update import to include `ensureGitIgnore`; strip branch creation from `sessions:create`; add bare `ensureGitIgnore` call
- ✅ **Change 2b** — `src/main/ipc/handlers.ts`: Update `workflow:advance` to create branch on implement transition; return `{ phase, git_branch }`
- ✅ **Change 3** — `src/main/preload.ts`: Update `advancePhase` return type in `ClaudeFlowAPI` interface
- ✅ **Change 4** — `renderer/src/App.tsx`: Destructure `{ phase, git_branch }` from advance result; spread `git_branch` into `setSelectedSession`
- ✅ **Change 5a** — `renderer/src/components/Header.tsx`: Remove branch from `<option>` text
- ✅ **Change 5b** — `renderer/src/components/Header.tsx`: Add `phase !== "implement"` guard to rename button
- ✅ **Change 5c** — `renderer/src/components/Header.tsx`: Gate badge on `gitBranch` truthy; remove disabled/unavailable state
- ✅ **Change 6** — `renderer/src/styles/globals.css`: Delete `.branch-badge.branch-unavailable` rule
Diffstat (limited to 'src/main/git.ts')
| -rw-r--r-- | src/main/git.ts | 86 |
1 files changed, 80 insertions, 6 deletions
diff --git a/src/main/git.ts b/src/main/git.ts index 58dc860..20362a7 100644 --- a/src/main/git.ts +++ b/src/main/git.ts | |||
| @@ -146,6 +146,81 @@ function extractTaskName(checkboxLine: string): string { | |||
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | // --------------------------------------------------------------------------- | 148 | // --------------------------------------------------------------------------- |
| 149 | // Commit subject builders | ||
| 150 | // --------------------------------------------------------------------------- | ||
| 151 | |||
| 152 | const LOCK_FILES = new Set([ | ||
| 153 | "package-lock.json", | ||
| 154 | "yarn.lock", | ||
| 155 | "pnpm-lock.yaml", | ||
| 156 | "bun.lockb", | ||
| 157 | "Cargo.lock", | ||
| 158 | "Gemfile.lock", | ||
| 159 | "poetry.lock", | ||
| 160 | ]); | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Builds a `feat:` subject from completed task names. | ||
| 164 | * Keeps the total subject ≤ 72 chars. | ||
| 165 | * 1 task → "feat: {name}" | ||
| 166 | * N tasks → "feat: {name} (+N-1 more)" | ||
| 167 | */ | ||
| 168 | function buildTaskSubject(taskNames: string[]): string { | ||
| 169 | const prefix = "feat: "; | ||
| 170 | const MAX = 72; | ||
| 171 | |||
| 172 | if (taskNames.length === 1) { | ||
| 173 | const full = `${prefix}${taskNames[0]}`; | ||
| 174 | if (full.length <= MAX) return full; | ||
| 175 | return `${prefix}${taskNames[0].slice(0, MAX - prefix.length - 1)}\u2026`; | ||
| 176 | } | ||
| 177 | |||
| 178 | const suffix = ` (+${taskNames.length - 1} more)`; | ||
| 179 | const available = MAX - prefix.length - suffix.length; | ||
| 180 | const first = taskNames[0]; | ||
| 181 | const truncated = | ||
| 182 | first.length > available ? `${first.slice(0, available - 1)}\u2026` : first; | ||
| 183 | return `${prefix}${truncated}${suffix}`; | ||
| 184 | } | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Returns the basenames of staged files, excluding lock files. | ||
| 188 | * Returns empty array on any failure. | ||
| 189 | */ | ||
| 190 | function getStagedFileNames(projectPath: string): string[] { | ||
| 191 | try { | ||
| 192 | const raw = execFileSync("git", ["diff", "--cached", "--name-only"], { | ||
| 193 | cwd: projectPath, | ||
| 194 | stdio: "pipe", | ||
| 195 | }) | ||
| 196 | .toString() | ||
| 197 | .trim(); | ||
| 198 | if (!raw) return []; | ||
| 199 | return raw | ||
| 200 | .split("\n") | ||
| 201 | .map((f) => path.basename(f.trim())) | ||
| 202 | .filter((f) => f && !LOCK_FILES.has(f)); | ||
| 203 | } catch { | ||
| 204 | return []; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * Builds a `chore:` subject from the staged file list when no tasks completed. | ||
| 210 | * 1 file → "chore: Update {file}" | ||
| 211 | * 2 files → "chore: Update {file1}, {file2}" | ||
| 212 | * 3+ files → "chore: Update {file1} (+N more files)" | ||
| 213 | * 0 files → "chore: Implement progress" | ||
| 214 | */ | ||
| 215 | function buildFileSubject(projectPath: string): string { | ||
| 216 | const files = getStagedFileNames(projectPath); | ||
| 217 | if (files.length === 0) return "chore: Implement progress"; | ||
| 218 | if (files.length === 1) return `chore: Update ${files[0]}`; | ||
| 219 | if (files.length === 2) return `chore: Update ${files[0]}, ${files[1]}`; | ||
| 220 | return `chore: Update ${files[0]} (+${files.length - 1} more files)`; | ||
| 221 | } | ||
| 222 | |||
| 223 | // --------------------------------------------------------------------------- | ||
| 149 | // Auto-commit | 224 | // Auto-commit |
| 150 | // --------------------------------------------------------------------------- | 225 | // --------------------------------------------------------------------------- |
| 151 | 226 | ||
| @@ -184,13 +259,12 @@ export function autoCommitTurn( | |||
| 184 | 259 | ||
| 185 | let commitMsg: string; | 260 | let commitMsg: string; |
| 186 | if (newlyCompleted.length > 0) { | 261 | if (newlyCompleted.length > 0) { |
| 187 | const count = newlyCompleted.length; | 262 | const taskNames = newlyCompleted.map(extractTaskName); |
| 188 | const taskLines = newlyCompleted | 263 | const subject = buildTaskSubject(taskNames); |
| 189 | .map((l) => `- ✅ ${extractTaskName(l)}`) | 264 | const body = taskNames.map((t) => `- \u2705 ${t}`).join("\n"); |
| 190 | .join("\n"); | 265 | commitMsg = `${subject}\n\n${body}`; |
| 191 | commitMsg = `feat: Complete ${count} task${count > 1 ? "s" : ""}\n\n${taskLines}`; | ||
| 192 | } else { | 266 | } else { |
| 193 | commitMsg = "chore: Implement progress (no tasks completed this turn)"; | 267 | commitMsg = buildFileSubject(projectPath); |
| 194 | } | 268 | } |
| 195 | 269 | ||
| 196 | execFileSync("git", ["commit", "-m", commitMsg], { cwd: projectPath }); | 270 | execFileSync("git", ["commit", "-m", commitMsg], { cwd: projectPath }); |
