git-tidy-cli 1.0.1 → 2.1.0

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 CHANGED
@@ -15,6 +15,7 @@ An interactive CLI tool for cleaning up unused git branches. Built with [Ink](ht
15
15
  - **Batch selection** - Select all, none, or invert selection with keyboard shortcuts
16
16
  - **Local & remote** - Delete both local and remote branches in one go
17
17
  - **Delete after dry-run** - Option to execute deletion right after reviewing dry-run results
18
+ - **Non-interactive mode** - Run headless with flags and JSON output for scripts, CI, and AI agents
18
19
 
19
20
  ## Installation
20
21
 
@@ -53,8 +54,17 @@ git-tidy
53
54
  | Flag | Description |
54
55
  |------|-------------|
55
56
  | `-x, --execute` | Actually delete branches (default: dry-run mode) |
57
+ | `--dry-run` | Preview only; never delete (this is the default) |
56
58
  | `-y, --yes` | Skip confirmations (for scripting) |
57
59
  | `-t, --token <token>` | GitHub personal access token (or use `GITHUB_TOKEN` env) |
60
+ | `--json` | Output machine-readable JSON (implies non-interactive mode) |
61
+ | `--scope <scope>` | Branch scope: `local`, `remote`, or `both` (default: `both`) |
62
+ | `--match <mode>` | How filters combine: `any` (OR, default) or `all` (AND) |
63
+ | `--merged` | Select branches already merged into the default branch |
64
+ | `--stale [days]` | Select branches with no commits in N days (default: 30) |
65
+ | `--age [days]` | Select branches older than N days (default: 60) |
66
+ | `--newer-than [days]` | Select branches with a commit within the last N days (default: 7) |
67
+ | `--pattern <glob>` | Select branches whose name matches a glob (e.g. `"feature/*"`) |
58
68
  | `-V, --version` | Show version |
59
69
  | `-h, --help` | Show help |
60
70
 
@@ -73,6 +83,81 @@ git-tidy --token ghp_xxxxxxxxxxxx
73
83
  GITHUB_TOKEN=ghp_xxxxxxxxxxxx git-tidy
74
84
  ```
75
85
 
86
+ ## Non-interactive mode (scripts & agents)
87
+
88
+ Passing any selection flag (`--merged`, `--stale`, `--age`, `--pattern`) or `--json`
89
+ switches git-tidy into a headless mode — no wizard, no prompts. It stays **dry-run by
90
+ default**; add `--execute` to actually delete. This makes it easy to drive from scripts,
91
+ CI pipelines, or AI agents.
92
+
93
+ ```bash
94
+ # List local merged branches that would be deleted (dry-run, human-readable)
95
+ git-tidy --merged --scope local
96
+
97
+ # Same, as JSON for machine parsing
98
+ git-tidy --merged --scope local --json
99
+
100
+ # Combine criteria (OR logic): stale OR matching a pattern
101
+ git-tidy --stale 30 --pattern 'feature/*' --json
102
+
103
+ # Actually delete local merged branches, no prompts
104
+ git-tidy --merged --scope local --execute --yes
105
+ ```
106
+
107
+ ### Combining filters: `--match any` (OR) vs `--match all` (AND)
108
+
109
+ By default, multiple filters are OR'd — a branch matches if it matches **any**
110
+ of them. Use `--match all` to require **all** active filters (AND), which lets
111
+ you target a precise intersection like "sync branches older than a week" or
112
+ "merged feature branches older than 90 days":
113
+
114
+ ```bash
115
+ # AND: sync-* branches older than 1 week (not just any sync branch)
116
+ git-tidy --match all --pattern 'sync-*' --age 7 --json
117
+
118
+ # AND: sync-* branches NEWER than 1 week (--newer-than is the inverse of --age)
119
+ git-tidy --match all --pattern 'sync-*' --newer-than 7 --json
120
+
121
+ # AND: feature branches merged into the default branch AND older than 90 days
122
+ git-tidy --match all --merged --pattern 'feature/*' --age 90 --scope remote --json
123
+ ```
124
+
125
+ Example `--json` output:
126
+
127
+ ```json
128
+ {
129
+ "dryRun": true,
130
+ "scope": "local",
131
+ "defaultBranch": "main",
132
+ "criteria": ["merged"],
133
+ "matched": [
134
+ { "name": "feature/login", "merged": true, "location": "local", "lastCommit": "2026-06-17T08:18:11.000Z" }
135
+ ],
136
+ "deleted": [],
137
+ "errors": []
138
+ }
139
+ ```
140
+
141
+ The process exits non-zero if a deletion fails (or on invalid input), so scripts can
142
+ check the exit code. Protected and current branches are always excluded automatically.
143
+
144
+ ## Agent skill
145
+
146
+ git-tidy ships a skill at [`skills/git-tidy/`](skills/git-tidy/) that teaches an AI agent
147
+ to use git-tidy safely — the preview-first workflow, the safety model, and the filter
148
+ recipes.
149
+
150
+ Install it into your agent's skills directory:
151
+
152
+ ```bash
153
+ cp -r skills/git-tidy ~/.claude/skills/
154
+ ```
155
+
156
+ If you installed git-tidy from npm, it's bundled at `node_modules/git-tidy-cli/skills/git-tidy/`.
157
+
158
+ Then ask your agent things like *"tidy up the merged branches older than 90 days on
159
+ origin"* — it'll dry-run, show you the matches, and delete only after you confirm.
160
+
76
161
  ## Workflow
77
162
 
78
163
  1. **Scope Selection** - Choose to clean local, remote, or both
package/dist/index.js CHANGED
@@ -9,9 +9,11 @@ import { Box as Box8, Text as Text8 } from "ink";
9
9
  import { Spinner as Spinner2 } from "@inkjs/ui";
10
10
 
11
11
  // src/components/Header.tsx
12
+ import { basename } from "path";
12
13
  import { Box, Text } from "ink";
13
14
  import { jsx, jsxs } from "react/jsx-runtime";
14
15
  function Header({ repoInfo }) {
16
+ const repoName = repoInfo && repoInfo.owner && repoInfo.repo ? `${repoInfo.owner}/${repoInfo.repo}` : basename(process.cwd());
15
17
  return /* @__PURE__ */ jsxs(
16
18
  Box,
17
19
  {
@@ -30,11 +32,7 @@ function Header({ repoInfo }) {
30
32
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
31
33
  "Repository:",
32
34
  " ",
33
- /* @__PURE__ */ jsxs(Text, { color: "white", children: [
34
- repoInfo.owner,
35
- "/",
36
- repoInfo.repo
37
- ] })
35
+ /* @__PURE__ */ jsx(Text, { color: "white", children: repoName })
38
36
  ] }),
39
37
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
40
38
  "Branch:",
@@ -95,6 +93,7 @@ import { MultiSelect, TextInput } from "@inkjs/ui";
95
93
  // src/utils/config.ts
96
94
  var DEFAULT_STALE_DAYS = 30;
97
95
  var DEFAULT_AGE_DAYS = 60;
96
+ var DEFAULT_NEWER_DAYS = 7;
98
97
  var PROTECTED_PATTERNS = [
99
98
  "main",
100
99
  "master",
@@ -187,7 +186,10 @@ function CriteriaStep({ onSelect, onBack }) {
187
186
  pattern: selectedCriteria.includes("pattern"),
188
187
  patternValue: pattern,
189
188
  age: selectedCriteria.includes("age"),
190
- ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS
189
+ ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS,
190
+ newerThan: false,
191
+ newerThanDays: DEFAULT_NEWER_DAYS,
192
+ matchMode: "any"
191
193
  };
192
194
  onSelect(filters);
193
195
  };
@@ -267,7 +269,10 @@ function CriteriaStep({ onSelect, onBack }) {
267
269
  pattern: selectedCriteria.includes("pattern"),
268
270
  patternValue: value,
269
271
  age: selectedCriteria.includes("age"),
270
- ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS
272
+ ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS,
273
+ newerThan: false,
274
+ newerThanDays: DEFAULT_NEWER_DAYS,
275
+ matchMode: "any"
271
276
  };
272
277
  onSelect(filters);
273
278
  }
@@ -334,6 +339,9 @@ function formatDaysAgo(date) {
334
339
  function isOlderThan(date, days) {
335
340
  return daysAgo(date) > days;
336
341
  }
342
+ function isNewerThan(date, days) {
343
+ return daysAgo(date) <= days;
344
+ }
337
345
 
338
346
  // src/components/BranchSelectStep.tsx
339
347
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -955,29 +963,32 @@ async function fetchBranches(scope, defaultBranch, currentBranch) {
955
963
  return branches;
956
964
  }
957
965
  function filterBranches(branches, filters) {
958
- let filtered = [...branches];
959
- filtered = filtered.filter((b) => !b.isProtected && !b.isCurrentBranch);
960
- const hasFilters = filters.merged || filters.stale || filters.pattern || filters.age;
966
+ const candidates = branches.filter(
967
+ (b) => !b.isProtected && !b.isCurrentBranch
968
+ );
969
+ const hasFilters = filters.merged || filters.stale || filters.age || filters.newerThan || filters.pattern && Boolean(filters.patternValue);
961
970
  if (!hasFilters) {
962
- return filtered;
971
+ return candidates;
963
972
  }
964
- filtered = filtered.filter((branch) => {
965
- const matches = [];
973
+ return candidates.filter((branch) => {
974
+ const results = [];
966
975
  if (filters.merged) {
967
- matches.push(branch.isMerged);
976
+ results.push(branch.isMerged);
968
977
  }
969
978
  if (filters.stale) {
970
- matches.push(isOlderThan(branch.lastCommitDate, filters.staleDays));
979
+ results.push(isOlderThan(branch.lastCommitDate, filters.staleDays));
971
980
  }
972
981
  if (filters.age) {
973
- matches.push(isOlderThan(branch.lastCommitDate, filters.ageDays));
982
+ results.push(isOlderThan(branch.lastCommitDate, filters.ageDays));
983
+ }
984
+ if (filters.newerThan) {
985
+ results.push(isNewerThan(branch.lastCommitDate, filters.newerThanDays));
974
986
  }
975
987
  if (filters.pattern && filters.patternValue) {
976
- matches.push(matchesPattern(branch.name, filters.patternValue));
988
+ results.push(matchesPattern(branch.name, filters.patternValue));
977
989
  }
978
- return matches.some((m) => m === true);
990
+ return filters.matchMode === "all" ? results.every((m) => m === true) : results.some((m) => m === true);
979
991
  });
980
- return filtered;
981
992
  }
982
993
  function matchesPattern(branchName, pattern) {
983
994
  const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
@@ -998,7 +1009,10 @@ function App({ options: options2 }) {
998
1009
  pattern: false,
999
1010
  patternValue: "",
1000
1011
  age: false,
1001
- ageDays: DEFAULT_AGE_DAYS
1012
+ ageDays: DEFAULT_AGE_DAYS,
1013
+ newerThan: false,
1014
+ newerThanDays: DEFAULT_NEWER_DAYS,
1015
+ matchMode: "any"
1002
1016
  });
1003
1017
  const [allBranches, setAllBranches] = useState5([]);
1004
1018
  const [filteredBranches, setFilteredBranches] = useState5([]);
@@ -1141,19 +1155,199 @@ function App({ options: options2 }) {
1141
1155
 
1142
1156
  // src/cli.ts
1143
1157
  import { Command } from "commander";
1158
+ var VALID_SCOPES = ["local", "remote", "both"];
1159
+ var VALID_MATCH_MODES = ["any", "all"];
1160
+ function parseOptionalDays(value, fallback) {
1161
+ if (typeof value === "string") {
1162
+ const parsed = parseInt(value, 10);
1163
+ if (!Number.isNaN(parsed) && parsed > 0) {
1164
+ return parsed;
1165
+ }
1166
+ }
1167
+ return fallback;
1168
+ }
1144
1169
  function createCLI() {
1145
1170
  const program = new Command();
1146
- program.name("git-tidy").description("Interactive CLI tool for cleaning up unused git branches").version("1.0.0").option("-x, --execute", "Actually delete branches (default: dry-run mode)", false).option("-y, --yes", "Skip all confirmations (for scripting)", false).option("-t, --token <token>", "GitHub personal access token (or use GITHUB_TOKEN env)").parse();
1171
+ program.name("git-tidy").description(
1172
+ "Interactive CLI tool for cleaning up unused git branches.\n\nRun with no selection flags to launch the interactive wizard. Pass any of\n--merged/--stale/--age/--pattern (or --json) to run non-interactively \u2014 ideal\nfor scripts and agents. Defaults to a dry run; add --execute to delete."
1173
+ ).version("2.1.0").option("-x, --execute", "Actually delete branches (default: dry-run mode)", false).option("--dry-run", "Preview only; never delete (this is the default)", false).option("-y, --yes", "Skip all confirmations (for scripting)", false).option("-t, --token <token>", "GitHub personal access token (or use GITHUB_TOKEN env)").option("--json", "Output machine-readable JSON (implies non-interactive mode)", false).option("--scope <scope>", "Branch scope: local, remote, or both", "both").option("--match <mode>", 'How filters combine: "any" (OR) or "all" (AND)', "any").option("--merged", "Select branches already merged into the default branch", false).option("--stale [days]", `Select branches with no commits in N days (default: ${DEFAULT_STALE_DAYS})`).option("--age [days]", `Select branches older than N days (default: ${DEFAULT_AGE_DAYS})`).option("--newer-than [days]", `Select branches with a commit within the last N days (default: ${DEFAULT_NEWER_DAYS})`).option("--pattern <glob>", 'Select branches whose name matches a glob (e.g. "feature/*")').parse();
1147
1174
  const opts = program.opts();
1175
+ const scope = String(opts.scope);
1176
+ if (!VALID_SCOPES.includes(scope)) {
1177
+ program.error(
1178
+ `error: invalid --scope "${opts.scope}" (expected one of: ${VALID_SCOPES.join(", ")})`
1179
+ );
1180
+ }
1181
+ const matchMode = String(opts.match);
1182
+ if (!VALID_MATCH_MODES.includes(matchMode)) {
1183
+ program.error(
1184
+ `error: invalid --match "${opts.match}" (expected one of: ${VALID_MATCH_MODES.join(", ")})`
1185
+ );
1186
+ }
1187
+ const staleProvided = opts.stale !== void 0;
1188
+ const ageProvided = opts.age !== void 0;
1189
+ const newerProvided = opts.newerThan !== void 0;
1190
+ const merged = Boolean(opts.merged);
1191
+ const patternProvided = typeof opts.pattern === "string" && opts.pattern.length > 0;
1192
+ const json = Boolean(opts.json);
1193
+ const headless = merged || staleProvided || ageProvided || newerProvided || patternProvided || json;
1148
1194
  return {
1149
- execute: opts.execute || false,
1150
- yes: opts.yes || false,
1151
- token: opts.token
1195
+ execute: Boolean(opts.execute) && !opts.dryRun,
1196
+ yes: Boolean(opts.yes),
1197
+ token: opts.token,
1198
+ headless,
1199
+ json,
1200
+ scope,
1201
+ matchMode,
1202
+ merged,
1203
+ stale: staleProvided,
1204
+ staleDays: parseOptionalDays(opts.stale, DEFAULT_STALE_DAYS),
1205
+ age: ageProvided,
1206
+ ageDays: parseOptionalDays(opts.age, DEFAULT_AGE_DAYS),
1207
+ newerThan: newerProvided,
1208
+ newerThanDays: parseOptionalDays(opts.newerThan, DEFAULT_NEWER_DAYS),
1209
+ pattern: patternProvided,
1210
+ patternValue: patternProvided ? String(opts.pattern) : ""
1152
1211
  };
1153
1212
  }
1154
1213
 
1214
+ // src/headless.ts
1215
+ function locationOf(branch) {
1216
+ if (branch.isLocal && branch.isRemote) return "local+remote";
1217
+ if (branch.isLocal) return "local";
1218
+ return "remote";
1219
+ }
1220
+ function activeCriteria(filters) {
1221
+ const criteria = [];
1222
+ if (filters.merged) criteria.push("merged");
1223
+ if (filters.stale) criteria.push(`stale>${filters.staleDays}d`);
1224
+ if (filters.age) criteria.push(`age>${filters.ageDays}d`);
1225
+ if (filters.newerThan) criteria.push(`newer<${filters.newerThanDays}d`);
1226
+ if (filters.pattern) criteria.push(`pattern:${filters.patternValue}`);
1227
+ return criteria;
1228
+ }
1229
+ async function runHeadless(options2) {
1230
+ const filters = {
1231
+ merged: options2.merged,
1232
+ stale: options2.stale,
1233
+ staleDays: options2.staleDays,
1234
+ age: options2.age,
1235
+ ageDays: options2.ageDays,
1236
+ newerThan: options2.newerThan,
1237
+ newerThanDays: options2.newerThanDays,
1238
+ pattern: options2.pattern,
1239
+ patternValue: options2.patternValue,
1240
+ matchMode: options2.matchMode
1241
+ };
1242
+ const criteria = activeCriteria(filters);
1243
+ const fail = (message) => {
1244
+ if (options2.json) {
1245
+ console.log(JSON.stringify({ error: message }, null, 2));
1246
+ } else {
1247
+ console.error(`Error: ${message}`);
1248
+ }
1249
+ return 1;
1250
+ };
1251
+ initGit();
1252
+ if (!await isGitRepo()) {
1253
+ return fail("Not a git repository. Please run this command in a git repository.");
1254
+ }
1255
+ const hasToken = Boolean(options2.token || process.env.GITHUB_TOKEN);
1256
+ if (hasToken) {
1257
+ initGitHub(options2.token);
1258
+ }
1259
+ let info = await getRepoInfo();
1260
+ if (!info) {
1261
+ return fail("Could not read repository information.");
1262
+ }
1263
+ if (info.isGitHub && hasToken) {
1264
+ info = { ...info, defaultBranch: await getDefaultBranch(info.owner, info.repo) };
1265
+ }
1266
+ const allBranches = await fetchBranches(options2.scope, info.defaultBranch, info.currentBranch);
1267
+ const matched = filterBranches(allBranches, filters);
1268
+ const dryRun = !options2.execute;
1269
+ const deleted = [];
1270
+ const errors = [];
1271
+ if (!dryRun) {
1272
+ for (const branch of matched) {
1273
+ try {
1274
+ if (branch.isLocal) await deleteLocalBranch(branch.name, true);
1275
+ if (branch.isRemote) await deleteRemoteBranch(branch.name);
1276
+ deleted.push({ name: branch.name, location: locationOf(branch) });
1277
+ } catch (err) {
1278
+ errors.push({
1279
+ name: branch.name,
1280
+ error: err instanceof Error ? err.message : "Unknown error"
1281
+ });
1282
+ }
1283
+ }
1284
+ }
1285
+ if (options2.json) {
1286
+ console.log(
1287
+ JSON.stringify(
1288
+ {
1289
+ dryRun,
1290
+ scope: options2.scope,
1291
+ defaultBranch: info.defaultBranch,
1292
+ match: options2.matchMode,
1293
+ criteria,
1294
+ matched: matched.map((branch) => ({
1295
+ name: branch.name,
1296
+ merged: branch.isMerged,
1297
+ location: locationOf(branch),
1298
+ lastCommit: branch.lastCommitDate.toISOString()
1299
+ })),
1300
+ deleted,
1301
+ errors
1302
+ },
1303
+ null,
1304
+ 2
1305
+ )
1306
+ );
1307
+ return errors.length > 0 ? 1 : 0;
1308
+ }
1309
+ const repoLabel = info.owner && info.repo ? `${info.owner}/${info.repo}` : info.currentBranch;
1310
+ console.log(`Repository: ${repoLabel} (default: ${info.defaultBranch})`);
1311
+ const joiner = options2.matchMode === "all" ? " AND " : " OR ";
1312
+ console.log(`Scope: ${options2.scope} Criteria: ${criteria.length ? criteria.join(joiner) : "(none \u2014 all unprotected branches)"}`);
1313
+ console.log("");
1314
+ if (matched.length === 0) {
1315
+ console.log("No branches match the given criteria.");
1316
+ return 0;
1317
+ }
1318
+ console.log(`Matched ${matched.length} branch(es):`);
1319
+ for (const branch of matched) {
1320
+ const status = branch.isMerged ? "merged" : "not merged";
1321
+ console.log(` - ${branch.name} (${status}, ${formatDaysAgo(branch.lastCommitDate)}, ${locationOf(branch)})`);
1322
+ }
1323
+ console.log("");
1324
+ if (dryRun) {
1325
+ console.log(`Dry run \u2014 nothing deleted. Re-run with --execute to delete ${matched.length} branch(es).`);
1326
+ return 0;
1327
+ }
1328
+ for (const result of deleted) {
1329
+ console.log(` \u2713 deleted ${result.name} (${result.location})`);
1330
+ }
1331
+ for (const result of errors) {
1332
+ console.error(` \u2717 failed ${result.name}: ${result.error}`);
1333
+ }
1334
+ console.log("");
1335
+ console.log(`Deleted ${deleted.length} branch(es)${errors.length ? `, ${errors.length} failed` : ""}.`);
1336
+ return errors.length > 0 ? 1 : 0;
1337
+ }
1338
+
1155
1339
  // src/index.tsx
1156
1340
  import { jsx as jsx9 } from "react/jsx-runtime";
1157
1341
  var options = createCLI();
1158
- render(/* @__PURE__ */ jsx9(App, { options }));
1342
+ if (options.headless) {
1343
+ runHeadless(options).then(
1344
+ (code) => process.exit(code),
1345
+ (err) => {
1346
+ console.error(err instanceof Error ? err.message : String(err));
1347
+ process.exit(1);
1348
+ }
1349
+ );
1350
+ } else {
1351
+ render(/* @__PURE__ */ jsx9(App, { options }));
1352
+ }
1159
1353
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/app.tsx","../src/components/Header.tsx","../src/components/ScopeStep.tsx","../src/components/CriteriaStep.tsx","../src/utils/config.ts","../src/components/BranchSelectStep.tsx","../src/utils/date.ts","../src/components/ConfirmStep.tsx","../src/components/ExecutionStep.tsx","../src/services/git.ts","../src/components/SummaryStep.tsx","../src/hooks/useWizard.ts","../src/services/github.ts","../src/services/branch-analyzer.ts","../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport React from 'react';\nimport { render } from 'ink';\nimport { App } from './app.js';\nimport { createCLI } from './cli.js';\n\n// Parse CLI arguments\nconst options = createCLI();\n\n// Render the app\nrender(<App options={options} />);\n","import React, { useState, useEffect, useCallback } from 'react';\nimport { Box, Text } from 'ink';\nimport { Spinner } from '@inkjs/ui';\n\nimport { Header } from './components/Header.js';\nimport { ScopeStep } from './components/ScopeStep.js';\nimport { CriteriaStep } from './components/CriteriaStep.js';\nimport { BranchSelectStep } from './components/BranchSelectStep.js';\nimport { ConfirmStep } from './components/ConfirmStep.js';\nimport { ExecutionStep } from './components/ExecutionStep.js';\nimport { SummaryStep } from './components/SummaryStep.js';\n\nimport { useWizard } from './hooks/useWizard.js';\nimport { initGit, isGitRepo, getRepoInfo } from './services/git.js';\nimport { initGitHub, getDefaultBranch } from './services/github.js';\nimport { fetchBranches, filterBranches } from './services/branch-analyzer.js';\n\nimport type {\n RepoInfo,\n BranchScope,\n FilterOptions,\n Branch,\n DeletionSummary,\n CLIOptions,\n} from './types/index.js';\nimport { DEFAULT_STALE_DAYS, DEFAULT_AGE_DAYS } from './utils/config.js';\n\ninterface AppProps {\n options: CLIOptions;\n}\n\nexport function App({ options }: AppProps) {\n const { step, goToStep } = useWizard('init');\n\n // State\n const [repoInfo, setRepoInfo] = useState<RepoInfo | null>(null);\n const [scope, setScope] = useState<BranchScope>('both');\n const [filters, setFilters] = useState<FilterOptions>({\n merged: false,\n stale: false,\n staleDays: DEFAULT_STALE_DAYS,\n pattern: false,\n patternValue: '',\n age: false,\n ageDays: DEFAULT_AGE_DAYS,\n });\n const [allBranches, setAllBranches] = useState<Branch[]>([]);\n const [filteredBranches, setFilteredBranches] = useState<Branch[]>([]);\n const [selectedBranches, setSelectedBranches] = useState<Branch[]>([]);\n const [deletionSummary, setDeletionSummary] = useState<DeletionSummary | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loadingMessage, setLoadingMessage] = useState('Initializing...');\n const [executeForReal, setExecuteForReal] = useState(false);\n\n // Initialize on mount\n useEffect(() => {\n const initialize = async () => {\n try {\n // Initialize git\n initGit();\n\n // Check if we're in a git repo\n const isRepo = await isGitRepo();\n if (!isRepo) {\n setError('Not a git repository. Please run this command in a git repository.');\n return;\n }\n\n // Initialize GitHub if token is provided\n if (options.token || process.env.GITHUB_TOKEN) {\n initGitHub(options.token);\n }\n\n // Get repo info\n setLoadingMessage('Detecting repository...');\n let info = await getRepoInfo();\n\n if (info?.isGitHub && (options.token || process.env.GITHUB_TOKEN)) {\n // Get default branch from GitHub API\n setLoadingMessage('Fetching repository info from GitHub...');\n const defaultBranch = await getDefaultBranch(info.owner, info.repo);\n info = { ...info, defaultBranch };\n }\n\n setRepoInfo(info);\n goToStep('scope');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Unknown error occurred');\n }\n };\n\n initialize();\n }, [options.token, goToStep]);\n\n // Handle scope selection\n const handleScopeSelect = useCallback((selectedScope: BranchScope) => {\n setScope(selectedScope);\n goToStep('criteria');\n }, [goToStep]);\n\n // Handle criteria selection\n const handleCriteriaSelect = useCallback(async (selectedFilters: FilterOptions) => {\n setFilters(selectedFilters);\n goToStep('loading');\n setLoadingMessage('Fetching branches...');\n\n try {\n if (!repoInfo) {\n throw new Error('Repository info not available');\n }\n\n // Fetch branches\n const branches = await fetchBranches(\n scope,\n repoInfo.defaultBranch,\n repoInfo.currentBranch\n );\n setAllBranches(branches);\n\n // Apply filters\n setLoadingMessage('Analyzing branches...');\n const filtered = filterBranches(branches, selectedFilters);\n setFilteredBranches(filtered);\n\n goToStep('select');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to fetch branches');\n }\n }, [repoInfo, scope, goToStep]);\n\n // Handle branch selection\n const handleBranchSelect = useCallback((selected: Branch[]) => {\n setSelectedBranches(selected);\n goToStep('confirm');\n }, [goToStep]);\n\n // Handle confirmation\n const handleConfirm = useCallback(() => {\n goToStep('execute');\n }, [goToStep]);\n\n // Handle deletion complete\n const handleDeletionComplete = useCallback((summary: DeletionSummary) => {\n setDeletionSummary(summary);\n goToStep('summary');\n }, [goToStep]);\n\n // Handle delete for real after dry run\n const handleDeleteForReal = useCallback(() => {\n setExecuteForReal(true);\n setDeletionSummary(null);\n goToStep('execute');\n }, [goToStep]);\n\n // Handle cancel/back\n const handleBack = useCallback(() => {\n switch (step) {\n case 'criteria':\n goToStep('scope');\n break;\n case 'select':\n goToStep('criteria');\n break;\n case 'confirm':\n goToStep('select');\n break;\n default:\n break;\n }\n }, [step, goToStep]);\n\n // Render error state\n if (error) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text color=\"red\" bold>\n Error: {error}\n </Text>\n </Box>\n );\n }\n\n // Render based on current step\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Header repoInfo={repoInfo} />\n\n {step === 'init' && (\n <Box gap={1}>\n <Spinner label={loadingMessage} />\n </Box>\n )}\n\n {step === 'loading' && (\n <Box gap={1}>\n <Spinner label={loadingMessage} />\n </Box>\n )}\n\n {step === 'scope' && <ScopeStep onSelect={handleScopeSelect} />}\n\n {step === 'criteria' && (\n <CriteriaStep onSelect={handleCriteriaSelect} onBack={handleBack} />\n )}\n\n {step === 'select' && (\n <BranchSelectStep\n branches={filteredBranches}\n onSelect={handleBranchSelect}\n onBack={handleBack}\n />\n )}\n\n {step === 'confirm' && (\n <ConfirmStep\n branches={selectedBranches}\n isDryRun={!options.execute}\n onConfirm={handleConfirm}\n onCancel={handleBack}\n />\n )}\n\n {step === 'execute' && (\n <ExecutionStep\n branches={selectedBranches}\n isDryRun={!options.execute && !executeForReal}\n onComplete={handleDeletionComplete}\n />\n )}\n\n {step === 'summary' && deletionSummary && (\n <SummaryStep\n summary={deletionSummary}\n isDryRun={!options.execute && !executeForReal}\n onDeleteForReal={handleDeleteForReal}\n />\n )}\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { RepoInfo } from '../types/index.js';\n\ninterface HeaderProps {\n repoInfo: RepoInfo | null;\n}\n\nexport function Header({ repoInfo }: HeaderProps) {\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor=\"cyan\"\n paddingX={2}\n paddingY={0}\n marginBottom={1}\n >\n <Box>\n <Text color=\"cyan\" bold>\n git-tidy\n </Text>\n <Text color=\"gray\"> - Branch Cleanup Tool</Text>\n </Box>\n\n {repoInfo && (\n <Box gap={2}>\n <Text color=\"gray\">\n Repository:{' '}\n <Text color=\"white\">\n {repoInfo.owner}/{repoInfo.repo}\n </Text>\n </Text>\n <Text color=\"gray\">\n Branch:{' '}\n <Text color=\"green\">{repoInfo.currentBranch}</Text>\n </Text>\n <Text color=\"gray\">\n Default:{' '}\n <Text color=\"yellow\">{repoInfo.defaultBranch}</Text>\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport { Select } from '@inkjs/ui';\nimport type { BranchScope } from '../types/index.js';\n\ninterface ScopeStepProps {\n onSelect: (scope: BranchScope) => void;\n}\n\nexport function ScopeStep({ onSelect }: ScopeStepProps) {\n const options = [\n {\n label: 'Both local and remote branches',\n value: 'both' as BranchScope,\n },\n {\n label: 'Local branches only',\n value: 'local' as BranchScope,\n },\n {\n label: 'Remote branches only',\n value: 'remote' as BranchScope,\n },\n ];\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 1:\n </Text>\n <Text> What would you like to clean up?</Text>\n </Box>\n\n <Box marginLeft={2}>\n <Select\n options={options}\n onChange={(value) => onSelect(value as BranchScope)}\n />\n </Box>\n\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Use ↑↓ to navigate, Enter to select\n </Text>\n </Box>\n </Box>\n );\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { MultiSelect, TextInput } from '@inkjs/ui';\nimport type { FilterOptions, FilterCriteria } from '../types/index.js';\nimport { DEFAULT_STALE_DAYS, DEFAULT_AGE_DAYS } from '../utils/config.js';\n\ninterface CriteriaStepProps {\n onSelect: (filters: FilterOptions) => void;\n onBack: () => void;\n}\n\ntype InputMode = 'select' | 'staleDays' | 'ageDays' | 'pattern';\n\nexport function CriteriaStep({ onSelect, onBack }: CriteriaStepProps) {\n const [selectedCriteria, setSelectedCriteria] = useState<FilterCriteria[]>([]);\n const [inputMode, setInputMode] = useState<InputMode>('select');\n const [staleDays, setStaleDays] = useState(String(DEFAULT_STALE_DAYS));\n const [ageDays, setAgeDays] = useState(String(DEFAULT_AGE_DAYS));\n const [pattern, setPattern] = useState('feature/*');\n const [inputError, setInputError] = useState<string | null>(null);\n\n // Validate numeric input\n const validateNumber = (value: string): number | null => {\n const num = parseInt(value, 10);\n if (isNaN(num) || num <= 0) {\n return null;\n }\n return num;\n };\n\n const options = [\n {\n label: 'Merged branches (already merged into default branch)',\n value: 'merged' as FilterCriteria,\n },\n {\n label: `Stale branches (no commits in ${staleDays} days)`,\n value: 'stale' as FilterCriteria,\n },\n {\n label: `Branches older than ${ageDays} days`,\n value: 'age' as FilterCriteria,\n },\n {\n label: `Pattern matching: ${pattern}`,\n value: 'pattern' as FilterCriteria,\n },\n ];\n\n useInput((input, key) => {\n if (key.escape) {\n if (inputMode !== 'select') {\n setInputMode('select');\n } else {\n onBack();\n }\n }\n\n // Handle Enter to submit when in select mode\n if (key.return && inputMode === 'select') {\n handleSubmit();\n }\n });\n\n const handleCriteriaChange = (values: string[]) => {\n setSelectedCriteria(values as FilterCriteria[]);\n };\n\n const handleSubmit = () => {\n // Check if we need additional input\n if (selectedCriteria.includes('stale') && inputMode === 'select') {\n setInputMode('staleDays');\n return;\n }\n if (selectedCriteria.includes('age') && inputMode === 'staleDays') {\n setInputMode('ageDays');\n return;\n }\n if (selectedCriteria.includes('pattern') && inputMode === 'ageDays') {\n setInputMode('pattern');\n return;\n }\n if (selectedCriteria.includes('pattern') && inputMode === 'select') {\n setInputMode('pattern');\n return;\n }\n\n // All inputs collected, proceed\n const filters: FilterOptions = {\n merged: selectedCriteria.includes('merged'),\n stale: selectedCriteria.includes('stale'),\n staleDays: parseInt(staleDays, 10) || DEFAULT_STALE_DAYS,\n pattern: selectedCriteria.includes('pattern'),\n patternValue: pattern,\n age: selectedCriteria.includes('age'),\n ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS,\n };\n\n onSelect(filters);\n };\n\n if (inputMode === 'staleDays') {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Text color=\"cyan\">How many days without commits is considered stale?</Text>\n <Box>\n <TextInput\n defaultValue={staleDays}\n onSubmit={(value) => {\n const num = validateNumber(value);\n if (num === null) {\n setInputError('Please enter a valid number greater than 0');\n return;\n }\n setInputError(null);\n setStaleDays(String(num));\n if (selectedCriteria.includes('age')) {\n setInputMode('ageDays');\n } else if (selectedCriteria.includes('pattern')) {\n setInputMode('pattern');\n } else {\n handleSubmit();\n }\n }}\n />\n <Text color=\"gray\"> days</Text>\n </Box>\n {inputError && <Text color=\"red\">{inputError}</Text>}\n </Box>\n );\n }\n\n if (inputMode === 'ageDays') {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Text color=\"cyan\">How many days old should a branch be?</Text>\n <Box>\n <TextInput\n defaultValue={ageDays}\n onSubmit={(value) => {\n const num = validateNumber(value);\n if (num === null) {\n setInputError('Please enter a valid number greater than 0');\n return;\n }\n setInputError(null);\n setAgeDays(String(num));\n if (selectedCriteria.includes('pattern')) {\n setInputMode('pattern');\n } else {\n handleSubmit();\n }\n }}\n />\n <Text color=\"gray\"> days</Text>\n </Box>\n {inputError && <Text color=\"red\">{inputError}</Text>}\n </Box>\n );\n }\n\n if (inputMode === 'pattern') {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Text color=\"cyan\">Enter branch name pattern (use * as wildcard):</Text>\n <TextInput\n defaultValue={pattern}\n onSubmit={(value) => {\n setPattern(value);\n const filters: FilterOptions = {\n merged: selectedCriteria.includes('merged'),\n stale: selectedCriteria.includes('stale'),\n staleDays: parseInt(staleDays, 10) || DEFAULT_STALE_DAYS,\n pattern: selectedCriteria.includes('pattern'),\n patternValue: value,\n age: selectedCriteria.includes('age'),\n ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS,\n };\n onSelect(filters);\n }}\n />\n <Text color=\"gray\" dimColor>\n Examples: feature/*, hotfix/*, *-old, test-*\n </Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 2:\n </Text>\n <Text> Which branches should be included? (select multiple)</Text>\n </Box>\n\n <Box marginLeft={2}>\n <MultiSelect\n options={options}\n onChange={handleCriteriaChange}\n />\n </Box>\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"gray\" dimColor>\n Use ↑↓ to navigate, Space to toggle, Enter to confirm\n </Text>\n <Text color=\"gray\" dimColor>\n Press Esc to go back\n </Text>\n </Box>\n\n {selectedCriteria.length > 0 && (\n <Box marginTop={1}>\n <Text color=\"green\">\n Press Enter to continue with {selectedCriteria.length} filter(s)\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","// Default configuration values\nexport const DEFAULT_STALE_DAYS = 30;\nexport const DEFAULT_AGE_DAYS = 60;\n\n// Protected branch patterns (in addition to auto-detected default branch)\nexport const PROTECTED_PATTERNS = [\n 'main',\n 'master',\n 'develop',\n 'development',\n 'staging',\n 'production',\n 'release/*',\n];\n\n/**\n * Check if a branch name matches any protected pattern\n */\nexport function matchesProtectedPattern(branchName: string): boolean {\n return PROTECTED_PATTERNS.some((pattern) => {\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n return branchName.startsWith(prefix + '/');\n }\n return branchName === pattern;\n });\n}\n","import React, { useState, useMemo } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { MultiSelect } from '@inkjs/ui';\nimport type { Branch } from '../types/index.js';\nimport { formatDaysAgo } from '../utils/date.js';\n\ninterface BranchSelectStepProps {\n branches: Branch[];\n onSelect: (selected: Branch[]) => void;\n onBack: () => void;\n}\n\nexport function BranchSelectStep({\n branches,\n onSelect,\n onBack,\n}: BranchSelectStepProps) {\n const [selectedValues, setSelectedValues] = useState<string[]>([]);\n const [selectKey, setSelectKey] = useState(0);\n\n // Create options for MultiSelect\n const options = useMemo(() => {\n return branches.map((branch) => {\n const location = branch.isLocal && branch.isRemote\n ? 'local+remote'\n : branch.isLocal\n ? 'local'\n : 'remote';\n\n const status = branch.isMerged ? 'merged' : 'not merged';\n const age = formatDaysAgo(branch.lastCommitDate);\n\n return {\n label: `${branch.name}`,\n value: branch.name,\n hint: `(${status}, ${age}, ${location})`,\n };\n });\n }, [branches]);\n\n useInput((input, key) => {\n if (key.escape) {\n onBack();\n return;\n }\n\n // Submit on Enter\n if (key.return && selectedValues.length > 0) {\n const selected = branches.filter((b) => selectedValues.includes(b.name));\n onSelect(selected);\n return;\n }\n\n // Select all\n if (input === 'a') {\n setSelectedValues(branches.map((b) => b.name));\n setSelectKey(k => k + 1);\n }\n\n // Select none\n if (input === 'n') {\n setSelectedValues([]);\n setSelectKey(k => k + 1);\n }\n\n // Invert selection\n if (input === 'i') {\n const inverted = branches\n .filter((b) => !selectedValues.includes(b.name))\n .map((b) => b.name);\n setSelectedValues(inverted);\n setSelectKey(k => k + 1);\n }\n });\n\n const handleChange = (values: string[]) => {\n setSelectedValues(values);\n };\n\n const handleSubmit = () => {\n const selected = branches.filter((b) => selectedValues.includes(b.name));\n onSelect(selected);\n };\n\n if (branches.length === 0) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 3:\n </Text>\n <Text> Select branches to delete</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"yellow\">No branches match your criteria.</Text>\n </Box>\n <Text color=\"gray\" dimColor>\n Press Esc to go back and adjust filters\n </Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 3:\n </Text>\n <Text> Select branches to delete ({branches.length} found)</Text>\n </Box>\n\n <Box marginLeft={2} gap={2}>\n <Text color=\"gray\">[a] Select all</Text>\n <Text color=\"gray\">[n] Select none</Text>\n <Text color=\"gray\">[i] Invert</Text>\n </Box>\n\n <Box marginLeft={2} flexDirection=\"column\">\n <MultiSelect\n key={selectKey}\n options={options}\n defaultValue={selectedValues}\n onChange={handleChange}\n />\n </Box>\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"gray\" dimColor>\n ↑↓ navigate, Space toggle, Enter confirm, Esc back\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text color={selectedValues.length > 0 ? 'green' : 'gray'}>\n {selectedValues.length} branch(es) selected\n </Text>\n {selectedValues.length > 0 && (\n <Text color=\"gray\"> - Press Enter to continue</Text>\n )}\n </Box>\n </Box>\n );\n}\n","/**\n * Calculate the number of days between a date and now\n */\nexport function daysAgo(date: Date): number {\n const now = new Date();\n const diffTime = Math.abs(now.getTime() - date.getTime());\n const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));\n return diffDays;\n}\n\n/**\n * Format a date as a human-readable \"X days ago\" string\n */\nexport function formatDaysAgo(date: Date): string {\n const days = daysAgo(date);\n\n if (days === 0) {\n return 'today';\n } else if (days === 1) {\n return '1 day ago';\n } else if (days < 7) {\n return `${days} days ago`;\n } else if (days < 30) {\n const weeks = Math.floor(days / 7);\n return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`;\n } else if (days < 365) {\n const months = Math.floor(days / 30);\n return months === 1 ? '1 month ago' : `${months} months ago`;\n } else {\n const years = Math.floor(days / 365);\n return years === 1 ? '1 year ago' : `${years} years ago`;\n }\n}\n\n/**\n * Check if a date is older than X days\n */\nexport function isOlderThan(date: Date, days: number): boolean {\n return daysAgo(date) > days;\n}\n","import React from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport type { Branch } from '../types/index.js';\n\ninterface ConfirmStepProps {\n branches: Branch[];\n isDryRun: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function ConfirmStep({\n branches,\n isDryRun,\n onConfirm,\n onCancel,\n}: ConfirmStepProps) {\n const localCount = branches.filter((b) => b.isLocal).length;\n const remoteCount = branches.filter((b) => b.isRemote).length;\n\n useInput((input, key) => {\n if (key.return) {\n onConfirm();\n }\n if (key.escape || input === 'n' || input === 'N') {\n onCancel();\n }\n if (input === 'y' || input === 'Y') {\n onConfirm();\n }\n });\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 4:\n </Text>\n <Text> Confirm deletion</Text>\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor=\"yellow\"\n paddingX={2}\n paddingY={1}\n marginY={1}\n >\n <Text bold>Ready to delete {branches.length} branch(es)</Text>\n\n <Box marginTop={1} gap={2}>\n <Text>\n Local: <Text color=\"cyan\">{localCount}</Text>\n </Text>\n <Text>\n Remote: <Text color=\"magenta\">{remoteCount}</Text>\n </Text>\n </Box>\n\n {isDryRun && (\n <Box marginTop={1}>\n <Text color=\"yellow\" bold>\n DRY RUN MODE - No branches will actually be deleted\n </Text>\n </Box>\n )}\n\n {!isDryRun && (\n <Box marginTop={1}>\n <Text color=\"red\" bold>\n WARNING: This will permanently delete these branches!\n </Text>\n </Box>\n )}\n </Box>\n\n <Box flexDirection=\"column\">\n <Text color=\"gray\">Branches to delete:</Text>\n <Box marginLeft={2} flexDirection=\"column\">\n {branches.slice(0, 10).map((branch) => (\n <Text key={branch.name} color=\"gray\">\n - {branch.name}{' '}\n <Text dimColor>\n ({branch.isLocal && 'local'}\n {branch.isLocal && branch.isRemote && ', '}\n {branch.isRemote && 'remote'})\n </Text>\n </Text>\n ))}\n {branches.length > 10 && (\n <Text color=\"gray\" dimColor>\n ... and {branches.length - 10} more\n </Text>\n )}\n </Box>\n </Box>\n\n <Box marginTop={1} gap={2}>\n <Text color=\"green\">[Enter/Y] Confirm</Text>\n <Text color=\"red\">[Esc/N] Cancel</Text>\n </Box>\n </Box>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { Box, Text } from 'ink';\nimport { Spinner } from '@inkjs/ui';\nimport type { Branch, DeletionResult, DeletionSummary } from '../types/index.js';\nimport {\n deleteLocalBranch,\n deleteRemoteBranch as deleteRemoteBranchGit,\n} from '../services/git.js';\n\ninterface ExecutionStepProps {\n branches: Branch[];\n isDryRun: boolean;\n onComplete: (summary: DeletionSummary) => void;\n}\n\nexport function ExecutionStep({\n branches,\n isDryRun,\n onComplete,\n}: ExecutionStepProps) {\n const [currentIndex, setCurrentIndex] = useState(0);\n const [results, setResults] = useState<DeletionResult[]>([]);\n const [isDeleting, setIsDeleting] = useState(true);\n\n useEffect(() => {\n const deleteBranches = async () => {\n const allResults: DeletionResult[] = [];\n\n for (let i = 0; i < branches.length; i++) {\n const branch = branches[i];\n setCurrentIndex(i);\n\n const result: DeletionResult = {\n branch,\n success: true,\n deletedLocal: false,\n deletedRemote: false,\n };\n\n if (isDryRun) {\n // Simulate deletion in dry run mode\n await new Promise((resolve) => setTimeout(resolve, 100));\n result.deletedLocal = branch.isLocal;\n result.deletedRemote = branch.isRemote;\n } else {\n // Actually delete the branch\n try {\n if (branch.isLocal) {\n await deleteLocalBranch(branch.name, true);\n result.deletedLocal = true;\n }\n } catch (error) {\n result.success = false;\n result.error = `Local: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n\n try {\n if (branch.isRemote) {\n await deleteRemoteBranchGit(branch.name);\n result.deletedRemote = true;\n }\n } catch (error) {\n if (!result.error) {\n result.success = false;\n result.error = `Remote: ${error instanceof Error ? error.message : 'Unknown error'}`;\n } else {\n result.error += `; Remote: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n }\n }\n\n allResults.push(result);\n setResults([...allResults]);\n }\n\n setIsDeleting(false);\n\n // Create summary\n const summary: DeletionSummary = {\n total: branches.length,\n successful: allResults.filter((r) => r.success).length,\n failed: allResults.filter((r) => !r.success).length,\n skipped: 0,\n results: allResults,\n };\n\n // Small delay before completing to show final state\n await new Promise((resolve) => setTimeout(resolve, 500));\n onComplete(summary);\n };\n\n deleteBranches();\n }, [branches, isDryRun, onComplete]);\n\n const currentBranch = branches[currentIndex];\n const completedCount = results.length;\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 5:\n </Text>\n <Text> {isDryRun ? 'Simulating deletion...' : 'Deleting branches...'}</Text>\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor=\"blue\"\n paddingX={2}\n paddingY={1}\n >\n {isDeleting && currentBranch && (\n <Box gap={1}>\n <Spinner label=\"\" />\n <Text>\n {isDryRun ? 'Processing' : 'Deleting'} {currentBranch.name}\n </Text>\n <Text color=\"gray\">\n ({completedCount + 1}/{branches.length})\n </Text>\n </Box>\n )}\n\n {!isDeleting && (\n <Text color=\"green\">\n {isDryRun ? 'Simulation' : 'Deletion'} complete!\n </Text>\n )}\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color=\"gray\">Results:</Text>\n <Box marginLeft={2} flexDirection=\"column\">\n {results.slice(-8).map((result, index) => (\n <Box key={result.branch.name} gap={1}>\n <Text color={result.success ? 'green' : 'red'}>\n {result.success ? '✓' : '✗'}\n </Text>\n <Text color={result.success ? 'gray' : 'red'}>\n {result.branch.name}\n </Text>\n {result.deletedLocal && <Text color=\"cyan\" dimColor>(local)</Text>}\n {result.deletedRemote && <Text color=\"magenta\" dimColor>(remote)</Text>}\n {result.error && (\n <Text color=\"red\" dimColor>\n - {result.error}\n </Text>\n )}\n </Box>\n ))}\n {results.length > 8 && (\n <Text color=\"gray\" dimColor>\n ... and {results.length - 8} more\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n );\n}\n","import simpleGit, { SimpleGit } from 'simple-git';\nimport type { Branch, RepoInfo } from '../types/index.js';\nimport { matchesProtectedPattern } from '../utils/config.js';\n\nlet git: SimpleGit;\n\n/**\n * Initialize git instance for the current directory\n */\nexport function initGit(cwd?: string): SimpleGit {\n git = simpleGit(cwd);\n return git;\n}\n\n/**\n * Check if current directory is a git repository\n */\nexport async function isGitRepo(): Promise<boolean> {\n try {\n await git.revparse(['--git-dir']);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(): Promise<string> {\n const result = await git.revparse(['--abbrev-ref', 'HEAD']);\n return result.trim();\n}\n\n/**\n * Get remote URL and extract owner/repo for GitHub\n */\nexport async function getRemoteInfo(): Promise<{ owner: string; repo: string; isGitHub: boolean } | null> {\n try {\n const remotes = await git.getRemotes(true);\n const origin = remotes.find((r) => r.name === 'origin');\n\n if (!origin?.refs?.fetch) {\n return null;\n }\n\n const url = origin.refs.fetch;\n\n // Parse GitHub URL (SSH or HTTPS)\n // SSH: git@github.com:owner/repo.git\n // HTTPS: https://github.com/owner/repo.git\n const sshMatch = url.match(/git@github\\.com:([^/]+)\\/(.+?)(?:\\.git)?$/);\n const httpsMatch = url.match(/https:\\/\\/github\\.com\\/([^/]+)\\/(.+?)(?:\\.git)?$/);\n\n const match = sshMatch || httpsMatch;\n if (match) {\n return {\n owner: match[1],\n repo: match[2],\n isGitHub: true,\n };\n }\n\n return { owner: '', repo: '', isGitHub: false };\n } catch {\n return null;\n }\n}\n\n/**\n * Get all local branches with their last commit dates\n */\nexport async function getLocalBranches(defaultBranch: string, currentBranch: string): Promise<Branch[]> {\n const branches: Branch[] = [];\n\n try {\n // Get all local branches\n const branchSummary = await git.branchLocal();\n\n for (const branchName of branchSummary.all) {\n // Get last commit date for this branch\n const logResult = await git.log({\n [branchName]: null,\n maxCount: 1,\n format: { date: '%aI' },\n });\n\n const lastCommitDate = logResult.latest?.date\n ? new Date(logResult.latest.date)\n : new Date();\n\n // Check if branch is merged into default branch\n const isMerged = await isBranchMerged(branchName, defaultBranch);\n\n // Check if protected\n const isProtected =\n branchName === defaultBranch || matchesProtectedPattern(branchName);\n\n branches.push({\n name: branchName,\n isLocal: true,\n isRemote: false,\n lastCommitDate,\n isMerged,\n isProtected,\n isCurrentBranch: branchName === currentBranch,\n });\n }\n } catch (error) {\n console.error('Error getting local branches:', error);\n }\n\n return branches;\n}\n\n/**\n * Get all remote branches\n */\nexport async function getRemoteBranches(defaultBranch: string): Promise<Branch[]> {\n const branches: Branch[] = [];\n\n try {\n // Fetch to ensure we have latest remote info\n await git.fetch(['--prune']);\n\n // Get all remote branches\n const result = await git.branch(['-r']);\n\n for (const branchName of result.all) {\n // Skip HEAD pointer\n if (branchName.includes('HEAD')) continue;\n\n // Remove 'origin/' prefix for display\n const shortName = branchName.replace(/^origin\\//, '');\n\n // Skip if it's the default branch\n if (shortName === defaultBranch) continue;\n\n // Get last commit date\n const logResult = await git.log({\n [branchName]: null,\n maxCount: 1,\n format: { date: '%aI' },\n });\n\n const lastCommitDate = logResult.latest?.date\n ? new Date(logResult.latest.date)\n : new Date();\n\n // Check if merged into default branch\n const isMerged = await isBranchMerged(branchName, `origin/${defaultBranch}`);\n\n // Check if protected\n const isProtected =\n shortName === defaultBranch || matchesProtectedPattern(shortName);\n\n branches.push({\n name: shortName,\n isLocal: false,\n isRemote: true,\n lastCommitDate,\n isMerged,\n isProtected,\n isCurrentBranch: false,\n });\n }\n } catch (error) {\n console.error('Error getting remote branches:', error);\n }\n\n return branches;\n}\n\n/**\n * Check if a branch is merged into the target branch\n */\nexport async function isBranchMerged(branch: string, target: string): Promise<boolean> {\n try {\n const result = await git.raw(['branch', '--merged', target]);\n const mergedBranches = result\n .split('\\n')\n .map((b) => b.trim().replace(/^\\*\\s*/, ''));\n return mergedBranches.includes(branch) || mergedBranches.includes(branch.replace(/^origin\\//, ''));\n } catch {\n return false;\n }\n}\n\n/**\n * Delete a local branch\n */\nexport async function deleteLocalBranch(branchName: string, force = false): Promise<void> {\n const flag = force ? '-D' : '-d';\n await git.branch([flag, branchName]);\n}\n\n/**\n * Delete a remote branch\n */\nexport async function deleteRemoteBranch(branchName: string, remote = 'origin'): Promise<void> {\n await git.push([remote, '--delete', branchName]);\n}\n\n/**\n * Get full repository info\n */\nexport async function getRepoInfo(defaultBranch?: string): Promise<RepoInfo | null> {\n try {\n const currentBranch = await getCurrentBranch();\n const remoteInfo = await getRemoteInfo();\n\n return {\n owner: remoteInfo?.owner || '',\n repo: remoteInfo?.repo || '',\n defaultBranch: defaultBranch || 'main',\n currentBranch,\n isGitHub: remoteInfo?.isGitHub || false,\n };\n } catch {\n return null;\n }\n}\n","import React from 'react';\nimport { Box, Text, useInput, useApp } from 'ink';\nimport type { DeletionSummary } from '../types/index.js';\n\ninterface SummaryStepProps {\n summary: DeletionSummary;\n isDryRun: boolean;\n onDeleteForReal?: () => void;\n}\n\nexport function SummaryStep({ summary, isDryRun, onDeleteForReal }: SummaryStepProps) {\n const { exit } = useApp();\n\n useInput((input, key) => {\n // Delete for real after dry run\n if (isDryRun && (input === 'd' || input === 'D')) {\n onDeleteForReal?.();\n return;\n }\n\n // Exit on any other key\n if (input === 'q' || input === 'Q' || key.escape) {\n exit();\n return;\n }\n\n // If not dry run, any key exits\n if (!isDryRun) {\n exit();\n }\n });\n\n const successColor = summary.successful > 0 ? 'green' : 'gray';\n const failedColor = summary.failed > 0 ? 'red' : 'gray';\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n {isDryRun ? 'Dry Run Complete!' : 'Cleanup Complete!'}\n </Text>\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={summary.failed > 0 ? 'yellow' : 'green'}\n paddingX={2}\n paddingY={1}\n >\n <Box flexDirection=\"column\" gap={0}>\n <Box gap={1}>\n <Text color={successColor}>✓</Text>\n <Text>\n {summary.successful} branch(es){' '}\n {isDryRun ? 'would be deleted' : 'deleted successfully'}\n </Text>\n </Box>\n\n {summary.failed > 0 && (\n <Box gap={1}>\n <Text color={failedColor}>✗</Text>\n <Text color={failedColor}>{summary.failed} branch(es) failed</Text>\n </Box>\n )}\n\n {summary.skipped > 0 && (\n <Box gap={1}>\n <Text color=\"gray\">○</Text>\n <Text color=\"gray\">{summary.skipped} branch(es) skipped</Text>\n </Box>\n )}\n </Box>\n </Box>\n\n {isDryRun && summary.successful > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Box gap={2}>\n <Text color=\"red\" bold>[d] Delete for real</Text>\n <Text color=\"gray\">[q] Quit</Text>\n </Box>\n </Box>\n )}\n\n {isDryRun && summary.successful === 0 && (\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Press any key to exit\n </Text>\n </Box>\n )}\n\n {summary.failed > 0 && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color=\"red\">Failed branches:</Text>\n <Box marginLeft={2} flexDirection=\"column\">\n {summary.results\n .filter((r) => !r.success)\n .slice(0, 5)\n .map((result) => (\n <Text key={result.branch.name} color=\"red\">\n - {result.branch.name}: {result.error}\n </Text>\n ))}\n {summary.results.filter((r) => !r.success).length > 5 && (\n <Text color=\"gray\" dimColor>\n ... and {summary.results.filter((r) => !r.success).length - 5} more\n </Text>\n )}\n </Box>\n </Box>\n )}\n\n {!isDryRun && (\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Press any key to exit\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","import { useState, useCallback } from 'react';\nimport type { WizardStep } from '../types/index.js';\n\nconst STEP_ORDER: WizardStep[] = [\n 'init',\n 'scope',\n 'criteria',\n 'loading',\n 'select',\n 'confirm',\n 'execute',\n 'summary',\n];\n\nexport function useWizard(initialStep: WizardStep = 'init') {\n const [step, setStep] = useState<WizardStep>(initialStep);\n\n const nextStep = useCallback(() => {\n const currentIndex = STEP_ORDER.indexOf(step);\n if (currentIndex < STEP_ORDER.length - 1) {\n setStep(STEP_ORDER[currentIndex + 1]);\n }\n }, [step]);\n\n const prevStep = useCallback(() => {\n const currentIndex = STEP_ORDER.indexOf(step);\n if (currentIndex > 0) {\n setStep(STEP_ORDER[currentIndex - 1]);\n }\n }, [step]);\n\n const goToStep = useCallback((newStep: WizardStep) => {\n setStep(newStep);\n }, []);\n\n return {\n step,\n nextStep,\n prevStep,\n goToStep,\n };\n}\n","import { Octokit } from '@octokit/rest';\n\nlet octokit: Octokit | null = null;\n\n/**\n * Initialize GitHub API client with token\n */\nexport function initGitHub(token?: string): Octokit {\n const authToken = token || process.env.GITHUB_TOKEN;\n\n octokit = new Octokit({\n auth: authToken,\n });\n\n return octokit;\n}\n\n/**\n * Check if GitHub client is authenticated\n */\nexport function isAuthenticated(): boolean {\n return octokit !== null;\n}\n\n/**\n * Get the default branch for a repository\n */\nexport async function getDefaultBranch(owner: string, repo: string): Promise<string> {\n if (!octokit) {\n return 'main'; // Fallback if not authenticated\n }\n\n try {\n const { data } = await octokit.repos.get({ owner, repo });\n return data.default_branch;\n } catch (error) {\n console.error('Error fetching default branch:', error);\n return 'main';\n }\n}\n\n/**\n * Check if a branch has any open pull requests\n */\nexport async function branchHasOpenPR(\n owner: string,\n repo: string,\n branchName: string\n): Promise<boolean> {\n if (!octokit) {\n return false;\n }\n\n try {\n const { data } = await octokit.pulls.list({\n owner,\n repo,\n state: 'open',\n head: `${owner}:${branchName}`,\n });\n return data.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Delete a branch via GitHub API\n */\nexport async function deleteBranchViaAPI(\n owner: string,\n repo: string,\n branchName: string\n): Promise<void> {\n if (!octokit) {\n throw new Error('GitHub client not initialized');\n }\n\n await octokit.git.deleteRef({\n owner,\n repo,\n ref: `heads/${branchName}`,\n });\n}\n\n/**\n * Get repository information\n */\nexport async function getRepoInfo(owner: string, repo: string): Promise<{\n defaultBranch: string;\n private: boolean;\n description: string | null;\n} | null> {\n if (!octokit) {\n return null;\n }\n\n try {\n const { data } = await octokit.repos.get({ owner, repo });\n return {\n defaultBranch: data.default_branch,\n private: data.private,\n description: data.description,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * List all branches from GitHub API\n */\nexport async function listBranches(\n owner: string,\n repo: string\n): Promise<Array<{ name: string; protected: boolean }>> {\n if (!octokit) {\n return [];\n }\n\n try {\n const branches: Array<{ name: string; protected: boolean }> = [];\n let page = 1;\n\n while (true) {\n const { data } = await octokit.repos.listBranches({\n owner,\n repo,\n per_page: 100,\n page,\n });\n\n if (data.length === 0) break;\n\n for (const branch of data) {\n branches.push({\n name: branch.name,\n protected: branch.protected,\n });\n }\n\n if (data.length < 100) break;\n page++;\n }\n\n return branches;\n } catch {\n return [];\n }\n}\n","import type { Branch, FilterOptions, BranchScope } from '../types/index.js';\nimport { isOlderThan } from '../utils/date.js';\nimport {\n getLocalBranches,\n getRemoteBranches,\n} from './git.js';\n\n/**\n * Fetch branches based on scope\n */\nexport async function fetchBranches(\n scope: BranchScope,\n defaultBranch: string,\n currentBranch: string\n): Promise<Branch[]> {\n let branches: Branch[] = [];\n\n if (scope === 'local' || scope === 'both') {\n const localBranches = await getLocalBranches(defaultBranch, currentBranch);\n branches = [...branches, ...localBranches];\n }\n\n if (scope === 'remote' || scope === 'both') {\n const remoteBranches = await getRemoteBranches(defaultBranch);\n\n // If 'both', merge remote info with local branches\n if (scope === 'both') {\n for (const remoteBranch of remoteBranches) {\n const existingIndex = branches.findIndex(\n (b) => b.name === remoteBranch.name\n );\n if (existingIndex >= 0) {\n // Branch exists locally and remotely\n branches[existingIndex].isRemote = true;\n } else {\n // Remote-only branch\n branches.push(remoteBranch);\n }\n }\n } else {\n branches = remoteBranches;\n }\n }\n\n return branches;\n}\n\n/**\n * Apply all filters to branches\n */\nexport function filterBranches(\n branches: Branch[],\n filters: FilterOptions\n): Branch[] {\n let filtered = [...branches];\n\n // Always exclude protected and current branches\n filtered = filtered.filter((b) => !b.isProtected && !b.isCurrentBranch);\n\n // If no filters selected, return all non-protected branches\n const hasFilters =\n filters.merged || filters.stale || filters.pattern || filters.age;\n\n if (!hasFilters) {\n return filtered;\n }\n\n // Apply filters (OR logic - branch matches if it matches ANY filter)\n filtered = filtered.filter((branch) => {\n const matches: boolean[] = [];\n\n if (filters.merged) {\n matches.push(branch.isMerged);\n }\n\n if (filters.stale) {\n matches.push(isOlderThan(branch.lastCommitDate, filters.staleDays));\n }\n\n if (filters.age) {\n matches.push(isOlderThan(branch.lastCommitDate, filters.ageDays));\n }\n\n if (filters.pattern && filters.patternValue) {\n matches.push(matchesPattern(branch.name, filters.patternValue));\n }\n\n // Return true if branch matches any of the active filters\n return matches.some((m) => m === true);\n });\n\n return filtered;\n}\n\n/**\n * Check if branch name matches a glob-like pattern\n */\nexport function matchesPattern(branchName: string, pattern: string): boolean {\n // Convert glob pattern to regex\n // Supports: * (any characters), ? (single character)\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars\n .replace(/\\*/g, '.*') // * -> .*\n .replace(/\\?/g, '.'); // ? -> .\n\n const regex = new RegExp(`^${regexPattern}$`, 'i');\n return regex.test(branchName);\n}\n\n/**\n * Sort branches by date (oldest first)\n */\nexport function sortByAge(branches: Branch[]): Branch[] {\n return [...branches].sort(\n (a, b) => a.lastCommitDate.getTime() - b.lastCommitDate.getTime()\n );\n}\n\n/**\n * Sort branches by name\n */\nexport function sortByName(branches: Branch[]): Branch[] {\n return [...branches].sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Get branch statistics\n */\nexport function getBranchStats(branches: Branch[]): {\n total: number;\n local: number;\n remote: number;\n merged: number;\n protected: number;\n} {\n return {\n total: branches.length,\n local: branches.filter((b) => b.isLocal).length,\n remote: branches.filter((b) => b.isRemote).length,\n merged: branches.filter((b) => b.isMerged).length,\n protected: branches.filter((b) => b.isProtected).length,\n };\n}\n","import { Command } from 'commander';\nimport type { CLIOptions } from './types/index.js';\n\nexport function createCLI(): CLIOptions {\n const program = new Command();\n\n program\n .name('git-tidy')\n .description('Interactive CLI tool for cleaning up unused git branches')\n .version('1.0.0')\n .option('-x, --execute', 'Actually delete branches (default: dry-run mode)', false)\n .option('-y, --yes', 'Skip all confirmations (for scripting)', false)\n .option('-t, --token <token>', 'GitHub personal access token (or use GITHUB_TOKEN env)')\n .parse();\n\n const opts = program.opts();\n\n return {\n execute: opts.execute || false,\n yes: opts.yes || false,\n token: opts.token,\n };\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","Box","Text","Spinner","Box","Text","jsx","jsxs","options","Box","Text","jsx","jsxs","options","Box","Text","useState","Box","Text","useInput","MultiSelect","jsx","jsxs","useState","options","useInput","Box","Text","MultiSelect","Box","Text","useInput","jsx","jsxs","useState","Box","Text","jsx","jsxs","useState","Box","Text","Box","Text","useInput","jsx","jsxs","useState","jsx","jsxs","options","useState","useEffect","useCallback","Box","Text","Spinner","jsx"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/app.tsx","../src/components/Header.tsx","../src/components/ScopeStep.tsx","../src/components/CriteriaStep.tsx","../src/utils/config.ts","../src/components/BranchSelectStep.tsx","../src/utils/date.ts","../src/components/ConfirmStep.tsx","../src/components/ExecutionStep.tsx","../src/services/git.ts","../src/components/SummaryStep.tsx","../src/hooks/useWizard.ts","../src/services/github.ts","../src/services/branch-analyzer.ts","../src/cli.ts","../src/headless.ts"],"sourcesContent":["#!/usr/bin/env node\nimport React from 'react';\nimport { render } from 'ink';\nimport { App } from './app.js';\nimport { createCLI } from './cli.js';\nimport { runHeadless } from './headless.js';\n\n// Parse CLI arguments\nconst options = createCLI();\n\nif (options.headless) {\n // Non-interactive mode for scripts and agents.\n runHeadless(options).then(\n (code) => process.exit(code),\n (err) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n );\n} else {\n // Interactive wizard.\n render(<App options={options} />);\n}\n","import React, { useState, useEffect, useCallback } from 'react';\nimport { Box, Text } from 'ink';\nimport { Spinner } from '@inkjs/ui';\n\nimport { Header } from './components/Header.js';\nimport { ScopeStep } from './components/ScopeStep.js';\nimport { CriteriaStep } from './components/CriteriaStep.js';\nimport { BranchSelectStep } from './components/BranchSelectStep.js';\nimport { ConfirmStep } from './components/ConfirmStep.js';\nimport { ExecutionStep } from './components/ExecutionStep.js';\nimport { SummaryStep } from './components/SummaryStep.js';\n\nimport { useWizard } from './hooks/useWizard.js';\nimport { initGit, isGitRepo, getRepoInfo } from './services/git.js';\nimport { initGitHub, getDefaultBranch } from './services/github.js';\nimport { fetchBranches, filterBranches } from './services/branch-analyzer.js';\n\nimport type {\n RepoInfo,\n BranchScope,\n FilterOptions,\n Branch,\n DeletionSummary,\n CLIOptions,\n} from './types/index.js';\nimport { DEFAULT_STALE_DAYS, DEFAULT_AGE_DAYS, DEFAULT_NEWER_DAYS } from './utils/config.js';\n\ninterface AppProps {\n options: CLIOptions;\n}\n\nexport function App({ options }: AppProps) {\n const { step, goToStep } = useWizard('init');\n\n // State\n const [repoInfo, setRepoInfo] = useState<RepoInfo | null>(null);\n const [scope, setScope] = useState<BranchScope>('both');\n const [filters, setFilters] = useState<FilterOptions>({\n merged: false,\n stale: false,\n staleDays: DEFAULT_STALE_DAYS,\n pattern: false,\n patternValue: '',\n age: false,\n ageDays: DEFAULT_AGE_DAYS,\n newerThan: false,\n newerThanDays: DEFAULT_NEWER_DAYS,\n matchMode: 'any',\n });\n const [allBranches, setAllBranches] = useState<Branch[]>([]);\n const [filteredBranches, setFilteredBranches] = useState<Branch[]>([]);\n const [selectedBranches, setSelectedBranches] = useState<Branch[]>([]);\n const [deletionSummary, setDeletionSummary] = useState<DeletionSummary | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loadingMessage, setLoadingMessage] = useState('Initializing...');\n const [executeForReal, setExecuteForReal] = useState(false);\n\n // Initialize on mount\n useEffect(() => {\n const initialize = async () => {\n try {\n // Initialize git\n initGit();\n\n // Check if we're in a git repo\n const isRepo = await isGitRepo();\n if (!isRepo) {\n setError('Not a git repository. Please run this command in a git repository.');\n return;\n }\n\n // Initialize GitHub if token is provided\n if (options.token || process.env.GITHUB_TOKEN) {\n initGitHub(options.token);\n }\n\n // Get repo info\n setLoadingMessage('Detecting repository...');\n let info = await getRepoInfo();\n\n if (info?.isGitHub && (options.token || process.env.GITHUB_TOKEN)) {\n // Get default branch from GitHub API\n setLoadingMessage('Fetching repository info from GitHub...');\n const defaultBranch = await getDefaultBranch(info.owner, info.repo);\n info = { ...info, defaultBranch };\n }\n\n setRepoInfo(info);\n goToStep('scope');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Unknown error occurred');\n }\n };\n\n initialize();\n }, [options.token, goToStep]);\n\n // Handle scope selection\n const handleScopeSelect = useCallback((selectedScope: BranchScope) => {\n setScope(selectedScope);\n goToStep('criteria');\n }, [goToStep]);\n\n // Handle criteria selection\n const handleCriteriaSelect = useCallback(async (selectedFilters: FilterOptions) => {\n setFilters(selectedFilters);\n goToStep('loading');\n setLoadingMessage('Fetching branches...');\n\n try {\n if (!repoInfo) {\n throw new Error('Repository info not available');\n }\n\n // Fetch branches\n const branches = await fetchBranches(\n scope,\n repoInfo.defaultBranch,\n repoInfo.currentBranch\n );\n setAllBranches(branches);\n\n // Apply filters\n setLoadingMessage('Analyzing branches...');\n const filtered = filterBranches(branches, selectedFilters);\n setFilteredBranches(filtered);\n\n goToStep('select');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to fetch branches');\n }\n }, [repoInfo, scope, goToStep]);\n\n // Handle branch selection\n const handleBranchSelect = useCallback((selected: Branch[]) => {\n setSelectedBranches(selected);\n goToStep('confirm');\n }, [goToStep]);\n\n // Handle confirmation\n const handleConfirm = useCallback(() => {\n goToStep('execute');\n }, [goToStep]);\n\n // Handle deletion complete\n const handleDeletionComplete = useCallback((summary: DeletionSummary) => {\n setDeletionSummary(summary);\n goToStep('summary');\n }, [goToStep]);\n\n // Handle delete for real after dry run\n const handleDeleteForReal = useCallback(() => {\n setExecuteForReal(true);\n setDeletionSummary(null);\n goToStep('execute');\n }, [goToStep]);\n\n // Handle cancel/back\n const handleBack = useCallback(() => {\n switch (step) {\n case 'criteria':\n goToStep('scope');\n break;\n case 'select':\n goToStep('criteria');\n break;\n case 'confirm':\n goToStep('select');\n break;\n default:\n break;\n }\n }, [step, goToStep]);\n\n // Render error state\n if (error) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text color=\"red\" bold>\n Error: {error}\n </Text>\n </Box>\n );\n }\n\n // Render based on current step\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Header repoInfo={repoInfo} />\n\n {step === 'init' && (\n <Box gap={1}>\n <Spinner label={loadingMessage} />\n </Box>\n )}\n\n {step === 'loading' && (\n <Box gap={1}>\n <Spinner label={loadingMessage} />\n </Box>\n )}\n\n {step === 'scope' && <ScopeStep onSelect={handleScopeSelect} />}\n\n {step === 'criteria' && (\n <CriteriaStep onSelect={handleCriteriaSelect} onBack={handleBack} />\n )}\n\n {step === 'select' && (\n <BranchSelectStep\n branches={filteredBranches}\n onSelect={handleBranchSelect}\n onBack={handleBack}\n />\n )}\n\n {step === 'confirm' && (\n <ConfirmStep\n branches={selectedBranches}\n isDryRun={!options.execute}\n onConfirm={handleConfirm}\n onCancel={handleBack}\n />\n )}\n\n {step === 'execute' && (\n <ExecutionStep\n branches={selectedBranches}\n isDryRun={!options.execute && !executeForReal}\n onComplete={handleDeletionComplete}\n />\n )}\n\n {step === 'summary' && deletionSummary && (\n <SummaryStep\n summary={deletionSummary}\n isDryRun={!options.execute && !executeForReal}\n onDeleteForReal={handleDeleteForReal}\n />\n )}\n </Box>\n );\n}\n","import React from 'react';\nimport { basename } from 'node:path';\nimport { Box, Text } from 'ink';\nimport type { RepoInfo } from '../types/index.js';\n\ninterface HeaderProps {\n repoInfo: RepoInfo | null;\n}\n\nexport function Header({ repoInfo }: HeaderProps) {\n const repoName =\n repoInfo && repoInfo.owner && repoInfo.repo\n ? `${repoInfo.owner}/${repoInfo.repo}`\n : basename(process.cwd());\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor=\"cyan\"\n paddingX={2}\n paddingY={0}\n marginBottom={1}\n >\n <Box>\n <Text color=\"cyan\" bold>\n git-tidy\n </Text>\n <Text color=\"gray\"> - Branch Cleanup Tool</Text>\n </Box>\n\n {repoInfo && (\n <Box gap={2}>\n <Text color=\"gray\">\n Repository:{' '}\n <Text color=\"white\">{repoName}</Text>\n </Text>\n <Text color=\"gray\">\n Branch:{' '}\n <Text color=\"green\">{repoInfo.currentBranch}</Text>\n </Text>\n <Text color=\"gray\">\n Default:{' '}\n <Text color=\"yellow\">{repoInfo.defaultBranch}</Text>\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport { Select } from '@inkjs/ui';\nimport type { BranchScope } from '../types/index.js';\n\ninterface ScopeStepProps {\n onSelect: (scope: BranchScope) => void;\n}\n\nexport function ScopeStep({ onSelect }: ScopeStepProps) {\n const options = [\n {\n label: 'Both local and remote branches',\n value: 'both' as BranchScope,\n },\n {\n label: 'Local branches only',\n value: 'local' as BranchScope,\n },\n {\n label: 'Remote branches only',\n value: 'remote' as BranchScope,\n },\n ];\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 1:\n </Text>\n <Text> What would you like to clean up?</Text>\n </Box>\n\n <Box marginLeft={2}>\n <Select\n options={options}\n onChange={(value) => onSelect(value as BranchScope)}\n />\n </Box>\n\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Use ↑↓ to navigate, Enter to select\n </Text>\n </Box>\n </Box>\n );\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { MultiSelect, TextInput } from '@inkjs/ui';\nimport type { FilterOptions, FilterCriteria } from '../types/index.js';\nimport { DEFAULT_STALE_DAYS, DEFAULT_AGE_DAYS, DEFAULT_NEWER_DAYS } from '../utils/config.js';\n\ninterface CriteriaStepProps {\n onSelect: (filters: FilterOptions) => void;\n onBack: () => void;\n}\n\ntype InputMode = 'select' | 'staleDays' | 'ageDays' | 'pattern';\n\nexport function CriteriaStep({ onSelect, onBack }: CriteriaStepProps) {\n const [selectedCriteria, setSelectedCriteria] = useState<FilterCriteria[]>([]);\n const [inputMode, setInputMode] = useState<InputMode>('select');\n const [staleDays, setStaleDays] = useState(String(DEFAULT_STALE_DAYS));\n const [ageDays, setAgeDays] = useState(String(DEFAULT_AGE_DAYS));\n const [pattern, setPattern] = useState('feature/*');\n const [inputError, setInputError] = useState<string | null>(null);\n\n // Validate numeric input\n const validateNumber = (value: string): number | null => {\n const num = parseInt(value, 10);\n if (isNaN(num) || num <= 0) {\n return null;\n }\n return num;\n };\n\n const options = [\n {\n label: 'Merged branches (already merged into default branch)',\n value: 'merged' as FilterCriteria,\n },\n {\n label: `Stale branches (no commits in ${staleDays} days)`,\n value: 'stale' as FilterCriteria,\n },\n {\n label: `Branches older than ${ageDays} days`,\n value: 'age' as FilterCriteria,\n },\n {\n label: `Pattern matching: ${pattern}`,\n value: 'pattern' as FilterCriteria,\n },\n ];\n\n useInput((input, key) => {\n if (key.escape) {\n if (inputMode !== 'select') {\n setInputMode('select');\n } else {\n onBack();\n }\n }\n\n // Handle Enter to submit when in select mode\n if (key.return && inputMode === 'select') {\n handleSubmit();\n }\n });\n\n const handleCriteriaChange = (values: string[]) => {\n setSelectedCriteria(values as FilterCriteria[]);\n };\n\n const handleSubmit = () => {\n // Check if we need additional input\n if (selectedCriteria.includes('stale') && inputMode === 'select') {\n setInputMode('staleDays');\n return;\n }\n if (selectedCriteria.includes('age') && inputMode === 'staleDays') {\n setInputMode('ageDays');\n return;\n }\n if (selectedCriteria.includes('pattern') && inputMode === 'ageDays') {\n setInputMode('pattern');\n return;\n }\n if (selectedCriteria.includes('pattern') && inputMode === 'select') {\n setInputMode('pattern');\n return;\n }\n\n // All inputs collected, proceed\n const filters: FilterOptions = {\n merged: selectedCriteria.includes('merged'),\n stale: selectedCriteria.includes('stale'),\n staleDays: parseInt(staleDays, 10) || DEFAULT_STALE_DAYS,\n pattern: selectedCriteria.includes('pattern'),\n patternValue: pattern,\n age: selectedCriteria.includes('age'),\n ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS,\n newerThan: false,\n newerThanDays: DEFAULT_NEWER_DAYS,\n matchMode: 'any',\n };\n\n onSelect(filters);\n };\n\n if (inputMode === 'staleDays') {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Text color=\"cyan\">How many days without commits is considered stale?</Text>\n <Box>\n <TextInput\n defaultValue={staleDays}\n onSubmit={(value) => {\n const num = validateNumber(value);\n if (num === null) {\n setInputError('Please enter a valid number greater than 0');\n return;\n }\n setInputError(null);\n setStaleDays(String(num));\n if (selectedCriteria.includes('age')) {\n setInputMode('ageDays');\n } else if (selectedCriteria.includes('pattern')) {\n setInputMode('pattern');\n } else {\n handleSubmit();\n }\n }}\n />\n <Text color=\"gray\"> days</Text>\n </Box>\n {inputError && <Text color=\"red\">{inputError}</Text>}\n </Box>\n );\n }\n\n if (inputMode === 'ageDays') {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Text color=\"cyan\">How many days old should a branch be?</Text>\n <Box>\n <TextInput\n defaultValue={ageDays}\n onSubmit={(value) => {\n const num = validateNumber(value);\n if (num === null) {\n setInputError('Please enter a valid number greater than 0');\n return;\n }\n setInputError(null);\n setAgeDays(String(num));\n if (selectedCriteria.includes('pattern')) {\n setInputMode('pattern');\n } else {\n handleSubmit();\n }\n }}\n />\n <Text color=\"gray\"> days</Text>\n </Box>\n {inputError && <Text color=\"red\">{inputError}</Text>}\n </Box>\n );\n }\n\n if (inputMode === 'pattern') {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Text color=\"cyan\">Enter branch name pattern (use * as wildcard):</Text>\n <TextInput\n defaultValue={pattern}\n onSubmit={(value) => {\n setPattern(value);\n const filters: FilterOptions = {\n merged: selectedCriteria.includes('merged'),\n stale: selectedCriteria.includes('stale'),\n staleDays: parseInt(staleDays, 10) || DEFAULT_STALE_DAYS,\n pattern: selectedCriteria.includes('pattern'),\n patternValue: value,\n age: selectedCriteria.includes('age'),\n ageDays: parseInt(ageDays, 10) || DEFAULT_AGE_DAYS,\n newerThan: false,\n newerThanDays: DEFAULT_NEWER_DAYS,\n matchMode: 'any',\n };\n onSelect(filters);\n }}\n />\n <Text color=\"gray\" dimColor>\n Examples: feature/*, hotfix/*, *-old, test-*\n </Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 2:\n </Text>\n <Text> Which branches should be included? (select multiple)</Text>\n </Box>\n\n <Box marginLeft={2}>\n <MultiSelect\n options={options}\n onChange={handleCriteriaChange}\n />\n </Box>\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"gray\" dimColor>\n Use ↑↓ to navigate, Space to toggle, Enter to confirm\n </Text>\n <Text color=\"gray\" dimColor>\n Press Esc to go back\n </Text>\n </Box>\n\n {selectedCriteria.length > 0 && (\n <Box marginTop={1}>\n <Text color=\"green\">\n Press Enter to continue with {selectedCriteria.length} filter(s)\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","// Default configuration values\nexport const DEFAULT_STALE_DAYS = 30;\nexport const DEFAULT_AGE_DAYS = 60;\nexport const DEFAULT_NEWER_DAYS = 7;\n\n// Protected branch patterns (in addition to auto-detected default branch)\nexport const PROTECTED_PATTERNS = [\n 'main',\n 'master',\n 'develop',\n 'development',\n 'staging',\n 'production',\n 'release/*',\n];\n\n/**\n * Check if a branch name matches any protected pattern\n */\nexport function matchesProtectedPattern(branchName: string): boolean {\n return PROTECTED_PATTERNS.some((pattern) => {\n if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -2);\n return branchName.startsWith(prefix + '/');\n }\n return branchName === pattern;\n });\n}\n","import React, { useState, useMemo } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { MultiSelect } from '@inkjs/ui';\nimport type { Branch } from '../types/index.js';\nimport { formatDaysAgo } from '../utils/date.js';\n\ninterface BranchSelectStepProps {\n branches: Branch[];\n onSelect: (selected: Branch[]) => void;\n onBack: () => void;\n}\n\nexport function BranchSelectStep({\n branches,\n onSelect,\n onBack,\n}: BranchSelectStepProps) {\n const [selectedValues, setSelectedValues] = useState<string[]>([]);\n const [selectKey, setSelectKey] = useState(0);\n\n // Create options for MultiSelect\n const options = useMemo(() => {\n return branches.map((branch) => {\n const location = branch.isLocal && branch.isRemote\n ? 'local+remote'\n : branch.isLocal\n ? 'local'\n : 'remote';\n\n const status = branch.isMerged ? 'merged' : 'not merged';\n const age = formatDaysAgo(branch.lastCommitDate);\n\n return {\n label: `${branch.name}`,\n value: branch.name,\n hint: `(${status}, ${age}, ${location})`,\n };\n });\n }, [branches]);\n\n useInput((input, key) => {\n if (key.escape) {\n onBack();\n return;\n }\n\n // Submit on Enter\n if (key.return && selectedValues.length > 0) {\n const selected = branches.filter((b) => selectedValues.includes(b.name));\n onSelect(selected);\n return;\n }\n\n // Select all\n if (input === 'a') {\n setSelectedValues(branches.map((b) => b.name));\n setSelectKey(k => k + 1);\n }\n\n // Select none\n if (input === 'n') {\n setSelectedValues([]);\n setSelectKey(k => k + 1);\n }\n\n // Invert selection\n if (input === 'i') {\n const inverted = branches\n .filter((b) => !selectedValues.includes(b.name))\n .map((b) => b.name);\n setSelectedValues(inverted);\n setSelectKey(k => k + 1);\n }\n });\n\n const handleChange = (values: string[]) => {\n setSelectedValues(values);\n };\n\n const handleSubmit = () => {\n const selected = branches.filter((b) => selectedValues.includes(b.name));\n onSelect(selected);\n };\n\n if (branches.length === 0) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 3:\n </Text>\n <Text> Select branches to delete</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"yellow\">No branches match your criteria.</Text>\n </Box>\n <Text color=\"gray\" dimColor>\n Press Esc to go back and adjust filters\n </Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 3:\n </Text>\n <Text> Select branches to delete ({branches.length} found)</Text>\n </Box>\n\n <Box marginLeft={2} gap={2}>\n <Text color=\"gray\">[a] Select all</Text>\n <Text color=\"gray\">[n] Select none</Text>\n <Text color=\"gray\">[i] Invert</Text>\n </Box>\n\n <Box marginLeft={2} flexDirection=\"column\">\n <MultiSelect\n key={selectKey}\n options={options}\n defaultValue={selectedValues}\n onChange={handleChange}\n />\n </Box>\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text color=\"gray\" dimColor>\n ↑↓ navigate, Space toggle, Enter confirm, Esc back\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text color={selectedValues.length > 0 ? 'green' : 'gray'}>\n {selectedValues.length} branch(es) selected\n </Text>\n {selectedValues.length > 0 && (\n <Text color=\"gray\"> - Press Enter to continue</Text>\n )}\n </Box>\n </Box>\n );\n}\n","/**\n * Calculate the number of days between a date and now\n */\nexport function daysAgo(date: Date): number {\n const now = new Date();\n const diffTime = Math.abs(now.getTime() - date.getTime());\n const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));\n return diffDays;\n}\n\n/**\n * Format a date as a human-readable \"X days ago\" string\n */\nexport function formatDaysAgo(date: Date): string {\n const days = daysAgo(date);\n\n if (days === 0) {\n return 'today';\n } else if (days === 1) {\n return '1 day ago';\n } else if (days < 7) {\n return `${days} days ago`;\n } else if (days < 30) {\n const weeks = Math.floor(days / 7);\n return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`;\n } else if (days < 365) {\n const months = Math.floor(days / 30);\n return months === 1 ? '1 month ago' : `${months} months ago`;\n } else {\n const years = Math.floor(days / 365);\n return years === 1 ? '1 year ago' : `${years} years ago`;\n }\n}\n\n/**\n * Check if a date is older than X days\n */\nexport function isOlderThan(date: Date, days: number): boolean {\n return daysAgo(date) > days;\n}\n\n/**\n * Check if a date is newer than (within) X days\n */\nexport function isNewerThan(date: Date, days: number): boolean {\n return daysAgo(date) <= days;\n}\n","import React from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport type { Branch } from '../types/index.js';\n\ninterface ConfirmStepProps {\n branches: Branch[];\n isDryRun: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function ConfirmStep({\n branches,\n isDryRun,\n onConfirm,\n onCancel,\n}: ConfirmStepProps) {\n const localCount = branches.filter((b) => b.isLocal).length;\n const remoteCount = branches.filter((b) => b.isRemote).length;\n\n useInput((input, key) => {\n if (key.return) {\n onConfirm();\n }\n if (key.escape || input === 'n' || input === 'N') {\n onCancel();\n }\n if (input === 'y' || input === 'Y') {\n onConfirm();\n }\n });\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 4:\n </Text>\n <Text> Confirm deletion</Text>\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor=\"yellow\"\n paddingX={2}\n paddingY={1}\n marginY={1}\n >\n <Text bold>Ready to delete {branches.length} branch(es)</Text>\n\n <Box marginTop={1} gap={2}>\n <Text>\n Local: <Text color=\"cyan\">{localCount}</Text>\n </Text>\n <Text>\n Remote: <Text color=\"magenta\">{remoteCount}</Text>\n </Text>\n </Box>\n\n {isDryRun && (\n <Box marginTop={1}>\n <Text color=\"yellow\" bold>\n DRY RUN MODE - No branches will actually be deleted\n </Text>\n </Box>\n )}\n\n {!isDryRun && (\n <Box marginTop={1}>\n <Text color=\"red\" bold>\n WARNING: This will permanently delete these branches!\n </Text>\n </Box>\n )}\n </Box>\n\n <Box flexDirection=\"column\">\n <Text color=\"gray\">Branches to delete:</Text>\n <Box marginLeft={2} flexDirection=\"column\">\n {branches.slice(0, 10).map((branch) => (\n <Text key={branch.name} color=\"gray\">\n - {branch.name}{' '}\n <Text dimColor>\n ({branch.isLocal && 'local'}\n {branch.isLocal && branch.isRemote && ', '}\n {branch.isRemote && 'remote'})\n </Text>\n </Text>\n ))}\n {branches.length > 10 && (\n <Text color=\"gray\" dimColor>\n ... and {branches.length - 10} more\n </Text>\n )}\n </Box>\n </Box>\n\n <Box marginTop={1} gap={2}>\n <Text color=\"green\">[Enter/Y] Confirm</Text>\n <Text color=\"red\">[Esc/N] Cancel</Text>\n </Box>\n </Box>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { Box, Text } from 'ink';\nimport { Spinner } from '@inkjs/ui';\nimport type { Branch, DeletionResult, DeletionSummary } from '../types/index.js';\nimport {\n deleteLocalBranch,\n deleteRemoteBranch as deleteRemoteBranchGit,\n} from '../services/git.js';\n\ninterface ExecutionStepProps {\n branches: Branch[];\n isDryRun: boolean;\n onComplete: (summary: DeletionSummary) => void;\n}\n\nexport function ExecutionStep({\n branches,\n isDryRun,\n onComplete,\n}: ExecutionStepProps) {\n const [currentIndex, setCurrentIndex] = useState(0);\n const [results, setResults] = useState<DeletionResult[]>([]);\n const [isDeleting, setIsDeleting] = useState(true);\n\n useEffect(() => {\n const deleteBranches = async () => {\n const allResults: DeletionResult[] = [];\n\n for (let i = 0; i < branches.length; i++) {\n const branch = branches[i];\n setCurrentIndex(i);\n\n const result: DeletionResult = {\n branch,\n success: true,\n deletedLocal: false,\n deletedRemote: false,\n };\n\n if (isDryRun) {\n // Simulate deletion in dry run mode\n await new Promise((resolve) => setTimeout(resolve, 100));\n result.deletedLocal = branch.isLocal;\n result.deletedRemote = branch.isRemote;\n } else {\n // Actually delete the branch\n try {\n if (branch.isLocal) {\n await deleteLocalBranch(branch.name, true);\n result.deletedLocal = true;\n }\n } catch (error) {\n result.success = false;\n result.error = `Local: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n\n try {\n if (branch.isRemote) {\n await deleteRemoteBranchGit(branch.name);\n result.deletedRemote = true;\n }\n } catch (error) {\n if (!result.error) {\n result.success = false;\n result.error = `Remote: ${error instanceof Error ? error.message : 'Unknown error'}`;\n } else {\n result.error += `; Remote: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n }\n }\n\n allResults.push(result);\n setResults([...allResults]);\n }\n\n setIsDeleting(false);\n\n // Create summary\n const summary: DeletionSummary = {\n total: branches.length,\n successful: allResults.filter((r) => r.success).length,\n failed: allResults.filter((r) => !r.success).length,\n skipped: 0,\n results: allResults,\n };\n\n // Small delay before completing to show final state\n await new Promise((resolve) => setTimeout(resolve, 500));\n onComplete(summary);\n };\n\n deleteBranches();\n }, [branches, isDryRun, onComplete]);\n\n const currentBranch = branches[currentIndex];\n const completedCount = results.length;\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n Step 5:\n </Text>\n <Text> {isDryRun ? 'Simulating deletion...' : 'Deleting branches...'}</Text>\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor=\"blue\"\n paddingX={2}\n paddingY={1}\n >\n {isDeleting && currentBranch && (\n <Box gap={1}>\n <Spinner label=\"\" />\n <Text>\n {isDryRun ? 'Processing' : 'Deleting'} {currentBranch.name}\n </Text>\n <Text color=\"gray\">\n ({completedCount + 1}/{branches.length})\n </Text>\n </Box>\n )}\n\n {!isDeleting && (\n <Text color=\"green\">\n {isDryRun ? 'Simulation' : 'Deletion'} complete!\n </Text>\n )}\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color=\"gray\">Results:</Text>\n <Box marginLeft={2} flexDirection=\"column\">\n {results.slice(-8).map((result, index) => (\n <Box key={result.branch.name} gap={1}>\n <Text color={result.success ? 'green' : 'red'}>\n {result.success ? '✓' : '✗'}\n </Text>\n <Text color={result.success ? 'gray' : 'red'}>\n {result.branch.name}\n </Text>\n {result.deletedLocal && <Text color=\"cyan\" dimColor>(local)</Text>}\n {result.deletedRemote && <Text color=\"magenta\" dimColor>(remote)</Text>}\n {result.error && (\n <Text color=\"red\" dimColor>\n - {result.error}\n </Text>\n )}\n </Box>\n ))}\n {results.length > 8 && (\n <Text color=\"gray\" dimColor>\n ... and {results.length - 8} more\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n );\n}\n","import simpleGit, { SimpleGit } from 'simple-git';\nimport type { Branch, RepoInfo } from '../types/index.js';\nimport { matchesProtectedPattern } from '../utils/config.js';\n\nlet git: SimpleGit;\n\n/**\n * Initialize git instance for the current directory\n */\nexport function initGit(cwd?: string): SimpleGit {\n git = simpleGit(cwd);\n return git;\n}\n\n/**\n * Check if current directory is a git repository\n */\nexport async function isGitRepo(): Promise<boolean> {\n try {\n await git.revparse(['--git-dir']);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(): Promise<string> {\n const result = await git.revparse(['--abbrev-ref', 'HEAD']);\n return result.trim();\n}\n\n/**\n * Get remote URL and extract owner/repo for GitHub\n */\nexport async function getRemoteInfo(): Promise<{ owner: string; repo: string; isGitHub: boolean } | null> {\n try {\n const remotes = await git.getRemotes(true);\n const origin = remotes.find((r) => r.name === 'origin');\n\n if (!origin?.refs?.fetch) {\n return null;\n }\n\n const url = origin.refs.fetch;\n\n // Parse GitHub URL (SSH or HTTPS)\n // SSH: git@github.com:owner/repo.git\n // HTTPS: https://github.com/owner/repo.git\n const sshMatch = url.match(/git@github\\.com:([^/]+)\\/(.+?)(?:\\.git)?$/);\n const httpsMatch = url.match(/https:\\/\\/github\\.com\\/([^/]+)\\/(.+?)(?:\\.git)?$/);\n\n const match = sshMatch || httpsMatch;\n if (match) {\n return {\n owner: match[1],\n repo: match[2],\n isGitHub: true,\n };\n }\n\n return { owner: '', repo: '', isGitHub: false };\n } catch {\n return null;\n }\n}\n\n/**\n * Get all local branches with their last commit dates\n */\nexport async function getLocalBranches(defaultBranch: string, currentBranch: string): Promise<Branch[]> {\n const branches: Branch[] = [];\n\n try {\n // Get all local branches\n const branchSummary = await git.branchLocal();\n\n for (const branchName of branchSummary.all) {\n // Get last commit date for this branch\n const logResult = await git.log({\n [branchName]: null,\n maxCount: 1,\n format: { date: '%aI' },\n });\n\n const lastCommitDate = logResult.latest?.date\n ? new Date(logResult.latest.date)\n : new Date();\n\n // Check if branch is merged into default branch\n const isMerged = await isBranchMerged(branchName, defaultBranch);\n\n // Check if protected\n const isProtected =\n branchName === defaultBranch || matchesProtectedPattern(branchName);\n\n branches.push({\n name: branchName,\n isLocal: true,\n isRemote: false,\n lastCommitDate,\n isMerged,\n isProtected,\n isCurrentBranch: branchName === currentBranch,\n });\n }\n } catch (error) {\n console.error('Error getting local branches:', error);\n }\n\n return branches;\n}\n\n/**\n * Get all remote branches\n */\nexport async function getRemoteBranches(defaultBranch: string): Promise<Branch[]> {\n const branches: Branch[] = [];\n\n try {\n // Fetch to ensure we have latest remote info\n await git.fetch(['--prune']);\n\n // Get all remote branches\n const result = await git.branch(['-r']);\n\n for (const branchName of result.all) {\n // Skip HEAD pointer\n if (branchName.includes('HEAD')) continue;\n\n // Remove 'origin/' prefix for display\n const shortName = branchName.replace(/^origin\\//, '');\n\n // Skip if it's the default branch\n if (shortName === defaultBranch) continue;\n\n // Get last commit date\n const logResult = await git.log({\n [branchName]: null,\n maxCount: 1,\n format: { date: '%aI' },\n });\n\n const lastCommitDate = logResult.latest?.date\n ? new Date(logResult.latest.date)\n : new Date();\n\n // Check if merged into default branch\n const isMerged = await isBranchMerged(branchName, `origin/${defaultBranch}`);\n\n // Check if protected\n const isProtected =\n shortName === defaultBranch || matchesProtectedPattern(shortName);\n\n branches.push({\n name: shortName,\n isLocal: false,\n isRemote: true,\n lastCommitDate,\n isMerged,\n isProtected,\n isCurrentBranch: false,\n });\n }\n } catch (error) {\n console.error('Error getting remote branches:', error);\n }\n\n return branches;\n}\n\n/**\n * Check if a branch is merged into the target branch\n */\nexport async function isBranchMerged(branch: string, target: string): Promise<boolean> {\n try {\n const result = await git.raw(['branch', '--merged', target]);\n const mergedBranches = result\n .split('\\n')\n .map((b) => b.trim().replace(/^\\*\\s*/, ''));\n return mergedBranches.includes(branch) || mergedBranches.includes(branch.replace(/^origin\\//, ''));\n } catch {\n return false;\n }\n}\n\n/**\n * Delete a local branch\n */\nexport async function deleteLocalBranch(branchName: string, force = false): Promise<void> {\n const flag = force ? '-D' : '-d';\n await git.branch([flag, branchName]);\n}\n\n/**\n * Delete a remote branch\n */\nexport async function deleteRemoteBranch(branchName: string, remote = 'origin'): Promise<void> {\n await git.push([remote, '--delete', branchName]);\n}\n\n/**\n * Get full repository info\n */\nexport async function getRepoInfo(defaultBranch?: string): Promise<RepoInfo | null> {\n try {\n const currentBranch = await getCurrentBranch();\n const remoteInfo = await getRemoteInfo();\n\n return {\n owner: remoteInfo?.owner || '',\n repo: remoteInfo?.repo || '',\n defaultBranch: defaultBranch || 'main',\n currentBranch,\n isGitHub: remoteInfo?.isGitHub || false,\n };\n } catch {\n return null;\n }\n}\n","import React from 'react';\nimport { Box, Text, useInput, useApp } from 'ink';\nimport type { DeletionSummary } from '../types/index.js';\n\ninterface SummaryStepProps {\n summary: DeletionSummary;\n isDryRun: boolean;\n onDeleteForReal?: () => void;\n}\n\nexport function SummaryStep({ summary, isDryRun, onDeleteForReal }: SummaryStepProps) {\n const { exit } = useApp();\n\n useInput((input, key) => {\n // Delete for real after dry run\n if (isDryRun && (input === 'd' || input === 'D')) {\n onDeleteForReal?.();\n return;\n }\n\n // Exit on any other key\n if (input === 'q' || input === 'Q' || key.escape) {\n exit();\n return;\n }\n\n // If not dry run, any key exits\n if (!isDryRun) {\n exit();\n }\n });\n\n const successColor = summary.successful > 0 ? 'green' : 'gray';\n const failedColor = summary.failed > 0 ? 'red' : 'gray';\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <Box>\n <Text color=\"cyan\" bold>\n {isDryRun ? 'Dry Run Complete!' : 'Cleanup Complete!'}\n </Text>\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={summary.failed > 0 ? 'yellow' : 'green'}\n paddingX={2}\n paddingY={1}\n >\n <Box flexDirection=\"column\" gap={0}>\n <Box gap={1}>\n <Text color={successColor}>✓</Text>\n <Text>\n {summary.successful} branch(es){' '}\n {isDryRun ? 'would be deleted' : 'deleted successfully'}\n </Text>\n </Box>\n\n {summary.failed > 0 && (\n <Box gap={1}>\n <Text color={failedColor}>✗</Text>\n <Text color={failedColor}>{summary.failed} branch(es) failed</Text>\n </Box>\n )}\n\n {summary.skipped > 0 && (\n <Box gap={1}>\n <Text color=\"gray\">○</Text>\n <Text color=\"gray\">{summary.skipped} branch(es) skipped</Text>\n </Box>\n )}\n </Box>\n </Box>\n\n {isDryRun && summary.successful > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Box gap={2}>\n <Text color=\"red\" bold>[d] Delete for real</Text>\n <Text color=\"gray\">[q] Quit</Text>\n </Box>\n </Box>\n )}\n\n {isDryRun && summary.successful === 0 && (\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Press any key to exit\n </Text>\n </Box>\n )}\n\n {summary.failed > 0 && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color=\"red\">Failed branches:</Text>\n <Box marginLeft={2} flexDirection=\"column\">\n {summary.results\n .filter((r) => !r.success)\n .slice(0, 5)\n .map((result) => (\n <Text key={result.branch.name} color=\"red\">\n - {result.branch.name}: {result.error}\n </Text>\n ))}\n {summary.results.filter((r) => !r.success).length > 5 && (\n <Text color=\"gray\" dimColor>\n ... and {summary.results.filter((r) => !r.success).length - 5} more\n </Text>\n )}\n </Box>\n </Box>\n )}\n\n {!isDryRun && (\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Press any key to exit\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n","import { useState, useCallback } from 'react';\nimport type { WizardStep } from '../types/index.js';\n\nconst STEP_ORDER: WizardStep[] = [\n 'init',\n 'scope',\n 'criteria',\n 'loading',\n 'select',\n 'confirm',\n 'execute',\n 'summary',\n];\n\nexport function useWizard(initialStep: WizardStep = 'init') {\n const [step, setStep] = useState<WizardStep>(initialStep);\n\n const nextStep = useCallback(() => {\n const currentIndex = STEP_ORDER.indexOf(step);\n if (currentIndex < STEP_ORDER.length - 1) {\n setStep(STEP_ORDER[currentIndex + 1]);\n }\n }, [step]);\n\n const prevStep = useCallback(() => {\n const currentIndex = STEP_ORDER.indexOf(step);\n if (currentIndex > 0) {\n setStep(STEP_ORDER[currentIndex - 1]);\n }\n }, [step]);\n\n const goToStep = useCallback((newStep: WizardStep) => {\n setStep(newStep);\n }, []);\n\n return {\n step,\n nextStep,\n prevStep,\n goToStep,\n };\n}\n","import { Octokit } from '@octokit/rest';\n\nlet octokit: Octokit | null = null;\n\n/**\n * Initialize GitHub API client with token\n */\nexport function initGitHub(token?: string): Octokit {\n const authToken = token || process.env.GITHUB_TOKEN;\n\n octokit = new Octokit({\n auth: authToken,\n });\n\n return octokit;\n}\n\n/**\n * Check if GitHub client is authenticated\n */\nexport function isAuthenticated(): boolean {\n return octokit !== null;\n}\n\n/**\n * Get the default branch for a repository\n */\nexport async function getDefaultBranch(owner: string, repo: string): Promise<string> {\n if (!octokit) {\n return 'main'; // Fallback if not authenticated\n }\n\n try {\n const { data } = await octokit.repos.get({ owner, repo });\n return data.default_branch;\n } catch (error) {\n console.error('Error fetching default branch:', error);\n return 'main';\n }\n}\n\n/**\n * Check if a branch has any open pull requests\n */\nexport async function branchHasOpenPR(\n owner: string,\n repo: string,\n branchName: string\n): Promise<boolean> {\n if (!octokit) {\n return false;\n }\n\n try {\n const { data } = await octokit.pulls.list({\n owner,\n repo,\n state: 'open',\n head: `${owner}:${branchName}`,\n });\n return data.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Delete a branch via GitHub API\n */\nexport async function deleteBranchViaAPI(\n owner: string,\n repo: string,\n branchName: string\n): Promise<void> {\n if (!octokit) {\n throw new Error('GitHub client not initialized');\n }\n\n await octokit.git.deleteRef({\n owner,\n repo,\n ref: `heads/${branchName}`,\n });\n}\n\n/**\n * Get repository information\n */\nexport async function getRepoInfo(owner: string, repo: string): Promise<{\n defaultBranch: string;\n private: boolean;\n description: string | null;\n} | null> {\n if (!octokit) {\n return null;\n }\n\n try {\n const { data } = await octokit.repos.get({ owner, repo });\n return {\n defaultBranch: data.default_branch,\n private: data.private,\n description: data.description,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * List all branches from GitHub API\n */\nexport async function listBranches(\n owner: string,\n repo: string\n): Promise<Array<{ name: string; protected: boolean }>> {\n if (!octokit) {\n return [];\n }\n\n try {\n const branches: Array<{ name: string; protected: boolean }> = [];\n let page = 1;\n\n while (true) {\n const { data } = await octokit.repos.listBranches({\n owner,\n repo,\n per_page: 100,\n page,\n });\n\n if (data.length === 0) break;\n\n for (const branch of data) {\n branches.push({\n name: branch.name,\n protected: branch.protected,\n });\n }\n\n if (data.length < 100) break;\n page++;\n }\n\n return branches;\n } catch {\n return [];\n }\n}\n","import type { Branch, FilterOptions, BranchScope } from '../types/index.js';\nimport { isOlderThan, isNewerThan } from '../utils/date.js';\nimport {\n getLocalBranches,\n getRemoteBranches,\n} from './git.js';\n\n/**\n * Fetch branches based on scope\n */\nexport async function fetchBranches(\n scope: BranchScope,\n defaultBranch: string,\n currentBranch: string\n): Promise<Branch[]> {\n let branches: Branch[] = [];\n\n if (scope === 'local' || scope === 'both') {\n const localBranches = await getLocalBranches(defaultBranch, currentBranch);\n branches = [...branches, ...localBranches];\n }\n\n if (scope === 'remote' || scope === 'both') {\n const remoteBranches = await getRemoteBranches(defaultBranch);\n\n // If 'both', merge remote info with local branches\n if (scope === 'both') {\n for (const remoteBranch of remoteBranches) {\n const existingIndex = branches.findIndex(\n (b) => b.name === remoteBranch.name\n );\n if (existingIndex >= 0) {\n // Branch exists locally and remotely\n branches[existingIndex].isRemote = true;\n } else {\n // Remote-only branch\n branches.push(remoteBranch);\n }\n }\n } else {\n branches = remoteBranches;\n }\n }\n\n return branches;\n}\n\n/**\n * Apply all filters to branches.\n *\n * Protected and current branches are always excluded. Active filters combine\n * according to `filters.matchMode`: 'any' (OR — matches any active filter, the\n * default) or 'all' (AND — must match every active filter). With no active\n * filters, all non-protected branches are returned.\n */\nexport function filterBranches(\n branches: Branch[],\n filters: FilterOptions\n): Branch[] {\n // Always exclude protected and current branches\n const candidates = branches.filter(\n (b) => !b.isProtected && !b.isCurrentBranch\n );\n\n const hasFilters =\n filters.merged ||\n filters.stale ||\n filters.age ||\n filters.newerThan ||\n (filters.pattern && Boolean(filters.patternValue));\n\n if (!hasFilters) {\n return candidates;\n }\n\n return candidates.filter((branch) => {\n // Each active filter contributes one predicate result.\n const results: boolean[] = [];\n\n if (filters.merged) {\n results.push(branch.isMerged);\n }\n if (filters.stale) {\n results.push(isOlderThan(branch.lastCommitDate, filters.staleDays));\n }\n if (filters.age) {\n results.push(isOlderThan(branch.lastCommitDate, filters.ageDays));\n }\n if (filters.newerThan) {\n results.push(isNewerThan(branch.lastCommitDate, filters.newerThanDays));\n }\n if (filters.pattern && filters.patternValue) {\n results.push(matchesPattern(branch.name, filters.patternValue));\n }\n\n return filters.matchMode === 'all'\n ? results.every((m) => m === true)\n : results.some((m) => m === true);\n });\n}\n\n/**\n * Check if branch name matches a glob-like pattern\n */\nexport function matchesPattern(branchName: string, pattern: string): boolean {\n // Convert glob pattern to regex\n // Supports: * (any characters), ? (single character)\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars\n .replace(/\\*/g, '.*') // * -> .*\n .replace(/\\?/g, '.'); // ? -> .\n\n const regex = new RegExp(`^${regexPattern}$`, 'i');\n return regex.test(branchName);\n}\n\n/**\n * Sort branches by date (oldest first)\n */\nexport function sortByAge(branches: Branch[]): Branch[] {\n return [...branches].sort(\n (a, b) => a.lastCommitDate.getTime() - b.lastCommitDate.getTime()\n );\n}\n\n/**\n * Sort branches by name\n */\nexport function sortByName(branches: Branch[]): Branch[] {\n return [...branches].sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Get branch statistics\n */\nexport function getBranchStats(branches: Branch[]): {\n total: number;\n local: number;\n remote: number;\n merged: number;\n protected: number;\n} {\n return {\n total: branches.length,\n local: branches.filter((b) => b.isLocal).length,\n remote: branches.filter((b) => b.isRemote).length,\n merged: branches.filter((b) => b.isMerged).length,\n protected: branches.filter((b) => b.isProtected).length,\n };\n}\n","import { Command } from 'commander';\nimport type { BranchScope, CLIOptions, MatchMode } from './types/index.js';\nimport { DEFAULT_STALE_DAYS, DEFAULT_AGE_DAYS, DEFAULT_NEWER_DAYS } from './utils/config.js';\n\nconst VALID_SCOPES: BranchScope[] = ['local', 'remote', 'both'];\nconst VALID_MATCH_MODES: MatchMode[] = ['any', 'all'];\n\n/**\n * Parse an optional numeric flag value (e.g. `--stale` or `--stale 14`).\n * `value` is `undefined` when absent, `true` when bare, or a string when given.\n */\nfunction parseOptionalDays(value: unknown, fallback: number): number {\n if (typeof value === 'string') {\n const parsed = parseInt(value, 10);\n if (!Number.isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n return fallback;\n}\n\nexport function createCLI(): CLIOptions {\n const program = new Command();\n\n program\n .name('git-tidy')\n .description(\n 'Interactive CLI tool for cleaning up unused git branches.\\n\\n' +\n 'Run with no selection flags to launch the interactive wizard. Pass any of\\n' +\n '--merged/--stale/--age/--pattern (or --json) to run non-interactively — ideal\\n' +\n 'for scripts and agents. Defaults to a dry run; add --execute to delete.'\n )\n .version('2.1.0')\n .option('-x, --execute', 'Actually delete branches (default: dry-run mode)', false)\n .option('--dry-run', 'Preview only; never delete (this is the default)', false)\n .option('-y, --yes', 'Skip all confirmations (for scripting)', false)\n .option('-t, --token <token>', 'GitHub personal access token (or use GITHUB_TOKEN env)')\n .option('--json', 'Output machine-readable JSON (implies non-interactive mode)', false)\n .option('--scope <scope>', 'Branch scope: local, remote, or both', 'both')\n .option('--match <mode>', 'How filters combine: \"any\" (OR) or \"all\" (AND)', 'any')\n .option('--merged', 'Select branches already merged into the default branch', false)\n .option('--stale [days]', `Select branches with no commits in N days (default: ${DEFAULT_STALE_DAYS})`)\n .option('--age [days]', `Select branches older than N days (default: ${DEFAULT_AGE_DAYS})`)\n .option('--newer-than [days]', `Select branches with a commit within the last N days (default: ${DEFAULT_NEWER_DAYS})`)\n .option('--pattern <glob>', 'Select branches whose name matches a glob (e.g. \"feature/*\")')\n .parse();\n\n const opts = program.opts();\n\n const scope = String(opts.scope) as BranchScope;\n if (!VALID_SCOPES.includes(scope)) {\n program.error(\n `error: invalid --scope \"${opts.scope}\" (expected one of: ${VALID_SCOPES.join(', ')})`\n );\n }\n\n const matchMode = String(opts.match) as MatchMode;\n if (!VALID_MATCH_MODES.includes(matchMode)) {\n program.error(\n `error: invalid --match \"${opts.match}\" (expected one of: ${VALID_MATCH_MODES.join(', ')})`\n );\n }\n\n const staleProvided = opts.stale !== undefined;\n const ageProvided = opts.age !== undefined;\n const newerProvided = opts.newerThan !== undefined;\n const merged = Boolean(opts.merged);\n const patternProvided = typeof opts.pattern === 'string' && opts.pattern.length > 0;\n const json = Boolean(opts.json);\n\n // Headless mode is triggered by any selection flag or by requesting JSON.\n // Bare `git-tidy` (and flags like --execute/--token alone) keep the wizard.\n const headless =\n merged || staleProvided || ageProvided || newerProvided || patternProvided || json;\n\n return {\n execute: Boolean(opts.execute) && !opts.dryRun,\n yes: Boolean(opts.yes),\n token: opts.token,\n headless,\n json,\n scope,\n matchMode,\n merged,\n stale: staleProvided,\n staleDays: parseOptionalDays(opts.stale, DEFAULT_STALE_DAYS),\n age: ageProvided,\n ageDays: parseOptionalDays(opts.age, DEFAULT_AGE_DAYS),\n newerThan: newerProvided,\n newerThanDays: parseOptionalDays(opts.newerThan, DEFAULT_NEWER_DAYS),\n pattern: patternProvided,\n patternValue: patternProvided ? String(opts.pattern) : '',\n };\n}\n","import {\n initGit,\n isGitRepo,\n getRepoInfo,\n deleteLocalBranch,\n deleteRemoteBranch,\n} from './services/git.js';\nimport { initGitHub, getDefaultBranch } from './services/github.js';\nimport { fetchBranches, filterBranches } from './services/branch-analyzer.js';\nimport { formatDaysAgo } from './utils/date.js';\nimport type { Branch, CLIOptions, FilterOptions } from './types/index.js';\n\ntype Location = 'local' | 'remote' | 'local+remote';\n\nfunction locationOf(branch: Branch): Location {\n if (branch.isLocal && branch.isRemote) return 'local+remote';\n if (branch.isLocal) return 'local';\n return 'remote';\n}\n\nfunction activeCriteria(filters: FilterOptions): string[] {\n const criteria: string[] = [];\n if (filters.merged) criteria.push('merged');\n if (filters.stale) criteria.push(`stale>${filters.staleDays}d`);\n if (filters.age) criteria.push(`age>${filters.ageDays}d`);\n if (filters.newerThan) criteria.push(`newer<${filters.newerThanDays}d`);\n if (filters.pattern) criteria.push(`pattern:${filters.patternValue}`);\n return criteria;\n}\n\n/**\n * Run git-tidy non-interactively. Resolves to the process exit code.\n */\nexport async function runHeadless(options: CLIOptions): Promise<number> {\n const filters: FilterOptions = {\n merged: options.merged,\n stale: options.stale,\n staleDays: options.staleDays,\n age: options.age,\n ageDays: options.ageDays,\n newerThan: options.newerThan,\n newerThanDays: options.newerThanDays,\n pattern: options.pattern,\n patternValue: options.patternValue,\n matchMode: options.matchMode,\n };\n const criteria = activeCriteria(filters);\n\n const fail = (message: string): number => {\n if (options.json) {\n console.log(JSON.stringify({ error: message }, null, 2));\n } else {\n console.error(`Error: ${message}`);\n }\n return 1;\n };\n\n // Initialize git and confirm we're inside a repository.\n initGit();\n if (!(await isGitRepo())) {\n return fail('Not a git repository. Please run this command in a git repository.');\n }\n\n const hasToken = Boolean(options.token || process.env.GITHUB_TOKEN);\n if (hasToken) {\n initGitHub(options.token);\n }\n\n let info = await getRepoInfo();\n if (!info) {\n return fail('Could not read repository information.');\n }\n if (info.isGitHub && hasToken) {\n info = { ...info, defaultBranch: await getDefaultBranch(info.owner, info.repo) };\n }\n\n const allBranches = await fetchBranches(options.scope, info.defaultBranch, info.currentBranch);\n const matched = filterBranches(allBranches, filters);\n\n const dryRun = !options.execute;\n const deleted: Array<{ name: string; location: Location }> = [];\n const errors: Array<{ name: string; error: string }> = [];\n\n if (!dryRun) {\n for (const branch of matched) {\n try {\n if (branch.isLocal) await deleteLocalBranch(branch.name, true);\n if (branch.isRemote) await deleteRemoteBranch(branch.name);\n deleted.push({ name: branch.name, location: locationOf(branch) });\n } catch (err) {\n errors.push({\n name: branch.name,\n error: err instanceof Error ? err.message : 'Unknown error',\n });\n }\n }\n }\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n dryRun,\n scope: options.scope,\n defaultBranch: info.defaultBranch,\n match: options.matchMode,\n criteria,\n matched: matched.map((branch) => ({\n name: branch.name,\n merged: branch.isMerged,\n location: locationOf(branch),\n lastCommit: branch.lastCommitDate.toISOString(),\n })),\n deleted,\n errors,\n },\n null,\n 2\n )\n );\n return errors.length > 0 ? 1 : 0;\n }\n\n // Human-readable text output.\n const repoLabel = info.owner && info.repo ? `${info.owner}/${info.repo}` : info.currentBranch;\n console.log(`Repository: ${repoLabel} (default: ${info.defaultBranch})`);\n const joiner = options.matchMode === 'all' ? ' AND ' : ' OR ';\n console.log(`Scope: ${options.scope} Criteria: ${criteria.length ? criteria.join(joiner) : '(none — all unprotected branches)'}`);\n console.log('');\n\n if (matched.length === 0) {\n console.log('No branches match the given criteria.');\n return 0;\n }\n\n console.log(`Matched ${matched.length} branch(es):`);\n for (const branch of matched) {\n const status = branch.isMerged ? 'merged' : 'not merged';\n console.log(` - ${branch.name} (${status}, ${formatDaysAgo(branch.lastCommitDate)}, ${locationOf(branch)})`);\n }\n console.log('');\n\n if (dryRun) {\n console.log(`Dry run — nothing deleted. Re-run with --execute to delete ${matched.length} branch(es).`);\n return 0;\n }\n\n for (const result of deleted) {\n console.log(` ✓ deleted ${result.name} (${result.location})`);\n }\n for (const result of errors) {\n console.error(` ✗ failed ${result.name}: ${result.error}`);\n }\n console.log('');\n console.log(`Deleted ${deleted.length} branch(es)${errors.length ? `, ${errors.length} failed` : ''}.`);\n return errors.length > 0 ? 1 : 0;\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","Box","Text","Spinner","Box","Text","jsx","jsxs","options","Box","Text","jsx","jsxs","options","Box","Text","useState","Box","Text","useInput","MultiSelect","jsx","jsxs","useState","options","useInput","Box","Text","MultiSelect","Box","Text","useInput","jsx","jsxs","useState","Box","Text","jsx","jsxs","useState","Box","Text","Box","Text","useInput","jsx","jsxs","useState","jsx","jsxs","options","useState","useEffect","useCallback","Box","Text","Spinner","options","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-tidy-cli",
3
- "version": "1.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Interactive CLI tool for cleaning up unused git branches with a beautiful TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist",
12
12
  "bin",
13
+ "skills",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
@@ -44,20 +45,23 @@
44
45
  "homepage": "https://github.com/ArmandBrworworworx/git-tidy#readme",
45
46
  "dependencies": {
46
47
  "@inkjs/ui": "^2.0.0",
47
- "@octokit/rest": "^21.0.0",
48
- "chalk": "^5.3.0",
49
- "commander": "^12.1.0",
50
- "ink": "^5.0.1",
51
- "react": "^18.3.1",
52
- "simple-git": "^3.25.0"
48
+ "@octokit/rest": "^22.0.1",
49
+ "chalk": "^5.6.2",
50
+ "commander": "^15.0.0",
51
+ "ink": "^7.0.6",
52
+ "react": "^19.2.0",
53
+ "simple-git": "^3.36.0"
53
54
  },
54
55
  "devDependencies": {
55
- "@types/node": "^20.14.0",
56
- "@types/react": "^18.3.3",
57
- "tsup": "^8.1.0",
58
- "typescript": "^5.5.0"
56
+ "@types/node": "^25.9.3",
57
+ "@types/react": "^19.2.17",
58
+ "tsup": "^8.5.1",
59
+ "typescript": "^6.0.3"
60
+ },
61
+ "overrides": {
62
+ "esbuild": "^0.28.1"
59
63
  },
60
64
  "engines": {
61
- "node": ">=18"
65
+ "node": ">=22"
62
66
  }
63
67
  }
@@ -0,0 +1,54 @@
1
+ # git-tidy recipes & output
2
+
3
+ All examples are dry-run (safe) unless they include `--execute`. Add `--execute --yes`
4
+ to actually delete once the preview looks right.
5
+
6
+ ## Recipes
7
+
8
+ ```bash
9
+ # Safe local cleanup: branches merged into the default branch
10
+ git-tidy --merged --scope local --json
11
+
12
+ # Old merged branches on origin (merged AND >= 90 days) — classic housekeeping
13
+ git-tidy --match all --merged --age 90 --scope remote --json
14
+
15
+ # Stale auto-sync branches: sync-* older than a week
16
+ git-tidy --match all --pattern 'sync-*' --age 7 --scope remote --json
17
+
18
+ # The inverse — sync-* created within the last week
19
+ git-tidy --match all --pattern 'sync-*' --newer-than 7 --scope remote --json
20
+
21
+ # Broad OR sweep: anything merged OR stale 30d OR matching a pattern
22
+ git-tidy --merged --stale 30 --pattern 'wip/*' --scope local --json
23
+
24
+ # Merged feature branches older than 60 days, on origin, actually delete
25
+ git-tidy --match all --merged --pattern 'feature/*' --age 60 --scope remote --execute --yes --json
26
+ ```
27
+
28
+ ## JSON output shape
29
+
30
+ ```json
31
+ {
32
+ "dryRun": true,
33
+ "scope": "remote",
34
+ "defaultBranch": "main",
35
+ "match": "all",
36
+ "criteria": ["merged", "age>90d"],
37
+ "matched": [
38
+ { "name": "feature/old-thing", "merged": true, "location": "remote", "lastCommit": "2025-01-04T12:00:00.000Z" }
39
+ ],
40
+ "deleted": [],
41
+ "errors": []
42
+ }
43
+ ```
44
+
45
+ - `matched` — branches selected by the filters (what *would* be deleted in dry-run).
46
+ - `deleted` — populated only with `--execute`; `errors` lists per-branch failures.
47
+ - On invalid input (e.g. bad `--scope`/`--match`) the process prints an error and exits non-zero.
48
+
49
+ ## Agent tips
50
+
51
+ - **Verify your view is complete** before reporting: there is no substitute for the tool's own output, but if you cross-check with raw git, use `git branch -r` or `git ls-remote --heads origin` (a `for-each-ref 'refs/remotes/origin/*'` glob silently skips names containing `/`).
52
+ - **Never delete on `remote`/`both` without explicit user confirmation** of the count and scope — these changes hit the shared origin and are irreversible.
53
+ - **`--merged` only protects work** when it's merged into the *default* branch. Branches merged only into a non-default integration branch still count as unmerged here, so they're preserved.
54
+ - To get accurate default-branch detection on GitHub repos, pass `--token` or set `GITHUB_TOKEN`.
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: git-tidy
3
+ description: Safely clean up stale, merged, or pattern-matched git branches using the git-tidy CLI in non-interactive mode. Use when asked to tidy/clean up/prune/delete git branches, remove merged or stale branches, clean a repo's branch list, or do branch housekeeping on local or remote (origin) branches.
4
+ ---
5
+
6
+ # git-tidy — safe branch cleanup for agents
7
+
8
+ `git-tidy` deletes unused git branches. In headless mode it takes filter flags and
9
+ emits JSON, so an agent can preview and act without the interactive wizard.
10
+
11
+ Requires `git-tidy-cli` (`npm i -g git-tidy-cli`, needs Node ≥ 22). Run from inside the repo.
12
+
13
+ ## Safety model (read first)
14
+
15
+ - **Dry-run by default.** Without `--execute`, nothing is deleted — it only reports what *would* be.
16
+ - **Protected branches are always excluded:** `main`, `master`, `develop`, `development`, `staging`, `production`, `release/*`, and the current branch.
17
+ - **"Merged" means merged into the default branch** — deleting such a branch loses no work (its commits live on in the default branch). This is the safest criterion.
18
+ - **Remote deletions are irreversible and affect the whole team.** Always dry-run first, and confirm with the user before `--execute` on `--scope remote`/`both`.
19
+ - Exit code is non-zero on any deletion failure or invalid input.
20
+
21
+ ## Workflow
22
+
23
+ 1. **Preview** with `--json` (dry-run). Parse `matched[]`.
24
+ 2. **Review** the list. For remote or large (>~20) sets, show the user and get explicit confirmation.
25
+ 3. **Execute** by re-running the same flags plus `--execute --yes`.
26
+ 4. **Verify** with another `--json` run (should match 0) or `git ls-remote --heads origin`.
27
+
28
+ ## Key flags
29
+
30
+ | Flag | Meaning |
31
+ |---|---|
32
+ | `--json` | Machine-readable output; implies non-interactive mode |
33
+ | `--scope local\|remote\|both` | Where to look (default `both`) |
34
+ | `--merged` | Branches merged into the default branch (safe to delete) |
35
+ | `--stale [days]` / `--age [days]` | No commits in / older than N days |
36
+ | `--newer-than [days]` | Commit within the last N days (inverse of `--age`) |
37
+ | `--pattern <glob>` | Name matches a glob, e.g. `'sync-*'`, `'feature/*'` |
38
+ | `--match any\|all` | Combine filters with OR (`any`, default) or AND (`all`) |
39
+ | `--execute` / `--dry-run` | Actually delete / preview only (default) |
40
+ | `--yes` | Skip confirmations (use with `--execute` in scripts) |
41
+ | `-t, --token` | GitHub token (or `GITHUB_TOKEN` env) for default-branch detection |
42
+
43
+ **Use `--match all` for precise intersections** (e.g. `sync-*` AND older than a week). With the default `any`, adding `--age` to `--pattern` *broadens* the set (OR) — a common footgun.
44
+
45
+ ## Most common task
46
+
47
+ ```bash
48
+ # 1. preview merged branches older than 90 days on origin
49
+ git-tidy --match all --merged --age 90 --scope remote --json
50
+ # 2. (after user confirms) delete them
51
+ git-tidy --match all --merged --age 90 --scope remote --execute --yes --json
52
+ ```
53
+
54
+ See [EXAMPLES.md](EXAMPLES.md) for more recipes and the JSON output shape.
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node