@towles/tool 0.0.96 → 0.0.104
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 +2 -2
- package/bin/run.ts +4 -3
- package/package.json +10 -37
- package/src/cli.ts +19 -0
- package/src/commands/agentboard.ts +386 -214
- package/src/commands/auto-claude/index.ts +74 -91
- package/src/commands/auto-claude/list.ts +33 -43
- package/src/commands/auto-claude/retry.test.ts +10 -6
- package/src/commands/auto-claude/retry.ts +26 -39
- package/src/commands/auto-claude/status.ts +10 -17
- package/src/commands/config.test.ts +4 -10
- package/src/commands/config.ts +14 -28
- package/src/commands/doctor.ts +156 -178
- package/src/commands/gh/branch-clean.ts +28 -43
- package/src/commands/gh/branch.ts +22 -37
- package/src/commands/gh/index.ts +10 -0
- package/src/commands/gh/pr.ts +82 -100
- package/src/commands/graph/index.ts +59 -70
- package/src/commands/install.ts +91 -115
- package/src/commands/journal/daily-notes.ts +16 -24
- package/src/commands/journal/index.ts +10 -0
- package/src/commands/journal/meeting.ts +16 -34
- package/src/commands/journal/note.ts +16 -34
- package/src/commands/shared.ts +21 -0
- package/src/lib/auto-claude/templates.test.ts +16 -11
- package/src/lib/graph/parser.test.ts +11 -10
- package/src/utils/git/gh-cli-wrapper.test.ts +6 -5
- package/src/commands/base.ts +0 -32
package/src/commands/doctor.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolve, join } from "node:path";
|
|
3
|
+
import { defineCommand } from "citty";
|
|
3
4
|
import consola from "consola";
|
|
4
5
|
import { x } from "tinyexec";
|
|
5
6
|
import { colors } from "consola/utils";
|
|
6
|
-
import {
|
|
7
|
+
import { debugArg } from "./shared.js";
|
|
7
8
|
|
|
8
9
|
interface CheckResult {
|
|
9
10
|
name: string;
|
|
@@ -12,35 +13,146 @@ interface CheckResult {
|
|
|
12
13
|
warning?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
async function checkCommand(
|
|
17
|
+
name: string,
|
|
18
|
+
args: string[],
|
|
19
|
+
versionPattern: RegExp,
|
|
20
|
+
optional = false,
|
|
21
|
+
): Promise<CheckResult> {
|
|
22
|
+
try {
|
|
23
|
+
const result = await x(name, args);
|
|
24
|
+
const output = result.stdout + result.stderr;
|
|
25
|
+
const match = output.match(versionPattern);
|
|
26
|
+
return {
|
|
27
|
+
name,
|
|
28
|
+
version: match?.[1] ?? output.trim().slice(0, 20),
|
|
29
|
+
ok: true,
|
|
30
|
+
};
|
|
31
|
+
} catch {
|
|
32
|
+
consola.debug(`Tool check failed for "${name}"`);
|
|
33
|
+
return {
|
|
34
|
+
name,
|
|
35
|
+
version: null,
|
|
36
|
+
ok: optional,
|
|
37
|
+
warning: optional ? "optional, not installed" : undefined,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function checkGhAuth(): Promise<{ ok: boolean }> {
|
|
43
|
+
try {
|
|
44
|
+
const result = await x("gh", ["auth", "status"]);
|
|
45
|
+
return { ok: result.exitCode === 0 };
|
|
46
|
+
} catch {
|
|
47
|
+
consola.debug("GitHub CLI auth check failed");
|
|
48
|
+
return { ok: false };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function checkAgentBoard(): {
|
|
53
|
+
name: string;
|
|
54
|
+
value: string;
|
|
55
|
+
ok: boolean;
|
|
56
|
+
warning?: string;
|
|
57
|
+
hint?: string;
|
|
58
|
+
}[] {
|
|
59
|
+
const results: { name: string; value: string; ok: boolean; warning?: string; hint?: string }[] =
|
|
60
|
+
[];
|
|
61
|
+
|
|
62
|
+
const defaultDataDir = resolve(
|
|
63
|
+
process.env.XDG_CONFIG_HOME ?? resolve(process.env.HOME ?? "~", ".config"),
|
|
64
|
+
"towles-tool",
|
|
65
|
+
"agentboard",
|
|
66
|
+
);
|
|
67
|
+
const dataDir = process.env.AGENTBOARD_DATA_DIR ?? defaultDataDir;
|
|
68
|
+
const dbPath = join(dataDir, "agentboard.db");
|
|
69
|
+
const configPath = join(dataDir, "config.json");
|
|
70
|
+
|
|
71
|
+
const dbExists = existsSync(dbPath);
|
|
72
|
+
results.push({
|
|
73
|
+
name: "database",
|
|
74
|
+
value: dbExists ? dbPath : "not found",
|
|
75
|
+
ok: dbExists,
|
|
76
|
+
hint: dbExists ? undefined : "Run: tt ag (starts server and creates DB automatically)",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
let repoPaths: string[] = [];
|
|
80
|
+
if (existsSync(configPath)) {
|
|
81
|
+
try {
|
|
82
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
83
|
+
repoPaths = config.repoPaths ?? [];
|
|
84
|
+
} catch {
|
|
85
|
+
// Corrupted config
|
|
86
|
+
}
|
|
87
|
+
}
|
|
20
88
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
89
|
+
results.push({
|
|
90
|
+
name: "scan paths",
|
|
91
|
+
value: repoPaths.length > 0 ? repoPaths.join(", ") : "none configured",
|
|
92
|
+
ok: repoPaths.length > 0,
|
|
93
|
+
warning: repoPaths.length === 0 ? "no scan paths" : undefined,
|
|
94
|
+
hint:
|
|
95
|
+
repoPaths.length === 0
|
|
96
|
+
? "Run: tt ag → open Workspaces → run the onboarding wizard"
|
|
97
|
+
: undefined,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
results.push({
|
|
101
|
+
name: "data dir",
|
|
102
|
+
value: dataDir,
|
|
103
|
+
ok: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return results;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function checkClaudePlugins(): Promise<
|
|
110
|
+
{ name: string; ok: boolean; installHint?: string }[]
|
|
111
|
+
> {
|
|
112
|
+
const requiredPlugins = [
|
|
113
|
+
{
|
|
114
|
+
id: "code-simplifier@claude-plugins-official",
|
|
115
|
+
name: "code-simplifier",
|
|
116
|
+
installCmd: "claude plugin install code-simplifier@claude-plugins-official --scope user",
|
|
117
|
+
},
|
|
24
118
|
];
|
|
25
119
|
|
|
26
|
-
|
|
27
|
-
await
|
|
120
|
+
try {
|
|
121
|
+
const result = await x("claude", ["plugin", "list", "--json"]);
|
|
122
|
+
const plugins: { id: string }[] = JSON.parse(result.stdout);
|
|
123
|
+
const installedIds = new Set(plugins.map((p) => p.id));
|
|
124
|
+
|
|
125
|
+
return requiredPlugins.map((p) => ({
|
|
126
|
+
name: p.name,
|
|
127
|
+
ok: installedIds.has(p.id),
|
|
128
|
+
installHint: installedIds.has(p.id) ? undefined : `Run: ${p.installCmd}`,
|
|
129
|
+
}));
|
|
130
|
+
} catch {
|
|
131
|
+
consola.debug("Failed to list Claude plugins");
|
|
132
|
+
return requiredPlugins.map((p) => ({
|
|
133
|
+
name: p.name,
|
|
134
|
+
ok: false,
|
|
135
|
+
installHint: `Run: ${p.installCmd}`,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
28
139
|
|
|
29
|
-
|
|
140
|
+
export default defineCommand({
|
|
141
|
+
meta: { name: "doctor", description: "Check system dependencies and environment" },
|
|
142
|
+
args: { debug: debugArg },
|
|
143
|
+
async run() {
|
|
144
|
+
consola.info("Checking dependencies...\n");
|
|
30
145
|
|
|
31
146
|
const checks: CheckResult[] = await Promise.all([
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.checkCommand("tmux", ["-V"], /tmux ([\d.]+)/),
|
|
40
|
-
this.checkCommand("ttyd", ["--version"], /ttyd version ([\d.]+)/, true),
|
|
147
|
+
checkCommand("git", ["--version"], /git version ([\d.]+)/),
|
|
148
|
+
checkCommand("gh", ["--version"], /gh version ([\d.]+)/),
|
|
149
|
+
checkCommand("node", ["--version"], /v?([\d.]+)/),
|
|
150
|
+
checkCommand("bun", ["--version"], /([\d.]+)/),
|
|
151
|
+
checkCommand("claude", ["--version"], /([\d.]+)/),
|
|
152
|
+
checkCommand("tmux", ["-V"], /tmux ([\d.]+)/),
|
|
153
|
+
checkCommand("ttyd", ["--version"], /ttyd version ([\d.]+)/, true),
|
|
41
154
|
]);
|
|
42
155
|
|
|
43
|
-
// Display results
|
|
44
156
|
for (const check of checks) {
|
|
45
157
|
const icon = check.ok
|
|
46
158
|
? colors.green("✓")
|
|
@@ -48,199 +160,65 @@ export default class Doctor extends BaseCommand {
|
|
|
48
160
|
? colors.yellow("⚠")
|
|
49
161
|
: colors.red("✗");
|
|
50
162
|
const version = check.version ?? "not found";
|
|
51
|
-
|
|
163
|
+
consola.log(`${icon} ${check.name}: ${version}`);
|
|
52
164
|
if (check.warning) {
|
|
53
|
-
|
|
165
|
+
consola.log(` ${colors.yellow("⚠")} ${check.warning}`);
|
|
54
166
|
}
|
|
55
167
|
}
|
|
56
168
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const ghAuth = await this.checkGhAuth();
|
|
169
|
+
consola.log("");
|
|
170
|
+
const ghAuth = await checkGhAuth();
|
|
60
171
|
const authIcon = ghAuth.ok ? colors.green("✓") : colors.yellow("⚠");
|
|
61
|
-
|
|
172
|
+
consola.log(`${authIcon} gh auth: ${ghAuth.ok ? "authenticated" : "not authenticated"}`);
|
|
62
173
|
if (!ghAuth.ok) {
|
|
63
|
-
|
|
174
|
+
consola.log(` ${colors.dim("Run: gh auth login")}`);
|
|
64
175
|
}
|
|
65
176
|
|
|
66
|
-
// Node version check
|
|
67
177
|
const nodeCheck = checks.find((c) => c.name === "node");
|
|
68
178
|
if (nodeCheck?.version) {
|
|
69
179
|
const major = Number.parseInt(nodeCheck.version.split(".")[0], 10);
|
|
70
180
|
if (major < 18) {
|
|
71
|
-
|
|
72
|
-
|
|
181
|
+
consola.log("");
|
|
182
|
+
consola.log(`${colors.yellow("⚠")} Node.js 18+ recommended (found ${nodeCheck.version})`);
|
|
73
183
|
}
|
|
74
184
|
}
|
|
75
185
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const pluginChecks = await this.checkClaudePlugins();
|
|
186
|
+
consola.log("");
|
|
187
|
+
const pluginChecks = await checkClaudePlugins();
|
|
79
188
|
for (const check of pluginChecks) {
|
|
80
189
|
const icon = check.ok ? colors.green("✓") : colors.red("✗");
|
|
81
190
|
const status = check.ok ? "installed" : "not installed";
|
|
82
|
-
|
|
191
|
+
consola.log(`${icon} claude plugin ${check.name}: ${status}`);
|
|
83
192
|
if (!check.ok && check.installHint) {
|
|
84
|
-
|
|
193
|
+
consola.log(` ${colors.dim(check.installHint)}`);
|
|
85
194
|
}
|
|
86
195
|
}
|
|
87
196
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const agentboardChecks = this.checkAgentBoard();
|
|
197
|
+
consola.log("");
|
|
198
|
+
consola.log(colors.bold("AgentBoard:"));
|
|
199
|
+
const agentboardChecks = checkAgentBoard();
|
|
92
200
|
for (const check of agentboardChecks) {
|
|
93
201
|
const icon = check.ok
|
|
94
202
|
? colors.green("✓")
|
|
95
203
|
: check.warning
|
|
96
204
|
? colors.yellow("⚠")
|
|
97
205
|
: colors.red("✗");
|
|
98
|
-
|
|
206
|
+
consola.log(`${icon} ${check.name}: ${check.value}`);
|
|
99
207
|
if (check.hint) {
|
|
100
|
-
|
|
208
|
+
consola.log(` ${colors.dim(check.hint)}`);
|
|
101
209
|
}
|
|
102
210
|
}
|
|
103
211
|
|
|
104
|
-
// Summary
|
|
105
212
|
const allOk =
|
|
106
213
|
checks.every((c) => c.ok || !!c.warning) &&
|
|
107
214
|
ghAuth.ok &&
|
|
108
215
|
pluginChecks.every((c) => c.ok) &&
|
|
109
216
|
agentboardChecks.every((c) => c.ok || !!c.warning);
|
|
110
|
-
|
|
217
|
+
consola.log("");
|
|
111
218
|
if (allOk) {
|
|
112
|
-
|
|
219
|
+
consola.log(colors.green("All checks passed!"));
|
|
113
220
|
} else {
|
|
114
|
-
|
|
221
|
+
consola.log(colors.yellow("Some checks failed. See above for details."));
|
|
115
222
|
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private async checkCommand(
|
|
119
|
-
name: string,
|
|
120
|
-
args: string[],
|
|
121
|
-
versionPattern: RegExp,
|
|
122
|
-
optional = false,
|
|
123
|
-
): Promise<CheckResult> {
|
|
124
|
-
try {
|
|
125
|
-
// tinyexec is safe - uses execFile internally, no shell injection risk
|
|
126
|
-
const result = await x(name, args);
|
|
127
|
-
const output = result.stdout + result.stderr;
|
|
128
|
-
const match = output.match(versionPattern);
|
|
129
|
-
return {
|
|
130
|
-
name,
|
|
131
|
-
version: match?.[1] ?? output.trim().slice(0, 20),
|
|
132
|
-
ok: true,
|
|
133
|
-
};
|
|
134
|
-
} catch {
|
|
135
|
-
consola.debug(`Tool check failed for "${name}"`);
|
|
136
|
-
return {
|
|
137
|
-
name,
|
|
138
|
-
version: null,
|
|
139
|
-
ok: optional,
|
|
140
|
-
warning: optional ? "optional, not installed" : undefined,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private async checkGhAuth(): Promise<{ ok: boolean }> {
|
|
146
|
-
try {
|
|
147
|
-
// tinyexec is safe - uses execFile internally, no shell injection risk
|
|
148
|
-
const result = await x("gh", ["auth", "status"]);
|
|
149
|
-
return { ok: result.exitCode === 0 };
|
|
150
|
-
} catch {
|
|
151
|
-
consola.debug("GitHub CLI auth check failed");
|
|
152
|
-
return { ok: false };
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
private checkAgentBoard(): {
|
|
157
|
-
name: string;
|
|
158
|
-
value: string;
|
|
159
|
-
ok: boolean;
|
|
160
|
-
warning?: string;
|
|
161
|
-
hint?: string;
|
|
162
|
-
}[] {
|
|
163
|
-
const results: { name: string; value: string; ok: boolean; warning?: string; hint?: string }[] =
|
|
164
|
-
[];
|
|
165
|
-
|
|
166
|
-
const defaultDataDir = resolve(
|
|
167
|
-
process.env.XDG_CONFIG_HOME ?? resolve(process.env.HOME ?? "~", ".config"),
|
|
168
|
-
"towles-tool",
|
|
169
|
-
"agentboard",
|
|
170
|
-
);
|
|
171
|
-
const dataDir = process.env.AGENTBOARD_DATA_DIR ?? defaultDataDir;
|
|
172
|
-
const dbPath = join(dataDir, "agentboard.db");
|
|
173
|
-
const configPath = join(dataDir, "config.json");
|
|
174
|
-
|
|
175
|
-
// DB exists
|
|
176
|
-
const dbExists = existsSync(dbPath);
|
|
177
|
-
results.push({
|
|
178
|
-
name: "database",
|
|
179
|
-
value: dbExists ? dbPath : "not found",
|
|
180
|
-
ok: dbExists,
|
|
181
|
-
hint: dbExists ? undefined : "Run: tt ag (starts server and creates DB automatically)",
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Config exists with repoPaths
|
|
185
|
-
let repoPaths: string[] = [];
|
|
186
|
-
if (existsSync(configPath)) {
|
|
187
|
-
try {
|
|
188
|
-
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
189
|
-
repoPaths = config.repoPaths ?? [];
|
|
190
|
-
} catch {
|
|
191
|
-
// Corrupted config
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
results.push({
|
|
196
|
-
name: "scan paths",
|
|
197
|
-
value: repoPaths.length > 0 ? repoPaths.join(", ") : "none configured",
|
|
198
|
-
ok: repoPaths.length > 0,
|
|
199
|
-
warning: repoPaths.length === 0 ? "no scan paths" : undefined,
|
|
200
|
-
hint:
|
|
201
|
-
repoPaths.length === 0
|
|
202
|
-
? "Run: tt ag → open Workspaces → run the onboarding wizard"
|
|
203
|
-
: undefined,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Data directory
|
|
207
|
-
results.push({
|
|
208
|
-
name: "data dir",
|
|
209
|
-
value: dataDir,
|
|
210
|
-
ok: true,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
return results;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private async checkClaudePlugins(): Promise<
|
|
217
|
-
{ name: string; ok: boolean; installHint?: string }[]
|
|
218
|
-
> {
|
|
219
|
-
const requiredPlugins = [
|
|
220
|
-
{
|
|
221
|
-
id: "code-simplifier@claude-plugins-official",
|
|
222
|
-
name: "code-simplifier",
|
|
223
|
-
installCmd: "claude plugin install code-simplifier@claude-plugins-official --scope user",
|
|
224
|
-
},
|
|
225
|
-
];
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
const result = await x("claude", ["plugin", "list", "--json"]);
|
|
229
|
-
const plugins: { id: string }[] = JSON.parse(result.stdout);
|
|
230
|
-
const installedIds = new Set(plugins.map((p) => p.id));
|
|
231
|
-
|
|
232
|
-
return requiredPlugins.map((p) => ({
|
|
233
|
-
name: p.name,
|
|
234
|
-
ok: installedIds.has(p.id),
|
|
235
|
-
installHint: installedIds.has(p.id) ? undefined : `Run: ${p.installCmd}`,
|
|
236
|
-
}));
|
|
237
|
-
} catch {
|
|
238
|
-
consola.debug("Failed to list Claude plugins");
|
|
239
|
-
return requiredPlugins.map((p) => ({
|
|
240
|
-
name: p.name,
|
|
241
|
-
ok: false,
|
|
242
|
-
installHint: `Run: ${p.installCmd}`,
|
|
243
|
-
}));
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
223
|
+
},
|
|
224
|
+
});
|
|
@@ -1,52 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
2
|
import { colors } from "consola/utils";
|
|
3
3
|
import consola from "consola";
|
|
4
4
|
import { x } from "tinyexec";
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
command: "<%= config.bin %> <%= command.id %> --dry-run",
|
|
19
|
-
},
|
|
20
|
-
{ description: "Skip confirmation", command: "<%= config.bin %> <%= command.id %> --force" },
|
|
21
|
-
{
|
|
22
|
-
description: "Check against develop",
|
|
23
|
-
command: "<%= config.bin %> <%= command.id %> --base develop",
|
|
24
|
-
},
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
static override flags = {
|
|
28
|
-
...BaseCommand.baseFlags,
|
|
29
|
-
force: Flags.boolean({
|
|
30
|
-
char: "f",
|
|
6
|
+
import { debugArg } from "../shared.js";
|
|
7
|
+
|
|
8
|
+
export default defineCommand({
|
|
9
|
+
meta: {
|
|
10
|
+
name: "branch-clean",
|
|
11
|
+
description: "Delete local branches that have been merged into main",
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
debug: debugArg,
|
|
15
|
+
force: {
|
|
16
|
+
type: "boolean",
|
|
17
|
+
alias: "f",
|
|
31
18
|
description: "Skip confirmation prompt",
|
|
32
19
|
default: false,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
},
|
|
21
|
+
dryRun: {
|
|
22
|
+
type: "boolean",
|
|
36
23
|
description: "Preview branches without deleting",
|
|
37
24
|
default: false,
|
|
38
|
-
}
|
|
39
|
-
base:
|
|
40
|
-
|
|
25
|
+
},
|
|
26
|
+
base: {
|
|
27
|
+
type: "string",
|
|
28
|
+
alias: "b",
|
|
41
29
|
description: "Base branch to check against",
|
|
42
30
|
default: "main",
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const { flags } = await this.parse(BranchClean);
|
|
48
|
-
const baseBranch = flags.base;
|
|
49
|
-
const dryRun = flags["dry-run"];
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
async run({ args }) {
|
|
34
|
+
const baseBranch = args.base;
|
|
50
35
|
|
|
51
36
|
// Get current branch
|
|
52
37
|
const currentResult = await x("git", ["branch", "--show-current"]);
|
|
@@ -73,12 +58,12 @@ export default class BranchClean extends BaseCommand {
|
|
|
73
58
|
consola.log(` - ${branch}`);
|
|
74
59
|
}
|
|
75
60
|
|
|
76
|
-
if (dryRun) {
|
|
61
|
+
if (args.dryRun) {
|
|
77
62
|
consola.info(colors.yellow("Dry run - no branches deleted"));
|
|
78
63
|
return;
|
|
79
64
|
}
|
|
80
65
|
|
|
81
|
-
if (!
|
|
66
|
+
if (!args.force) {
|
|
82
67
|
const answer = await consola.prompt(`Delete ${toDelete.length} branch(es)?`, {
|
|
83
68
|
type: "confirm",
|
|
84
69
|
initial: false,
|
|
@@ -112,5 +97,5 @@ export default class BranchClean extends BaseCommand {
|
|
|
112
97
|
if (failed > 0) {
|
|
113
98
|
consola.warn(colors.yellow(`Failed to delete ${failed} branch(es)`));
|
|
114
99
|
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
100
|
+
},
|
|
101
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
2
|
import consola from "consola";
|
|
3
3
|
import prompts from "prompts";
|
|
4
4
|
import type { Choice } from "prompts";
|
|
5
5
|
import { colors } from "consola/utils";
|
|
6
6
|
import { Fzf } from "fzf";
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { debugArg } from "../shared.js";
|
|
9
9
|
import type { Issue } from "../../utils/git/gh-cli-wrapper.js";
|
|
10
10
|
import { getIssues, isGithubCliInstalled } from "../../utils/git/gh-cli-wrapper.js";
|
|
11
11
|
import { git } from "../../utils/git/exec.js";
|
|
@@ -46,46 +46,31 @@ export function buildIssueChoices(issues: Issue[], layout: ColumnLayout): Choice
|
|
|
46
46
|
return choices;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{ description: "Browse all open issues", command: "<%= config.bin %> <%= command.id %>" },
|
|
57
|
-
{
|
|
58
|
-
description: "Only issues assigned to me",
|
|
59
|
-
command: "<%= config.bin %> <%= command.id %> --assignedToMe",
|
|
60
|
-
},
|
|
61
|
-
{ description: "Short flag for assigned", command: "<%= config.bin %> <%= command.id %> -a" },
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
static override flags = {
|
|
65
|
-
...BaseCommand.baseFlags,
|
|
66
|
-
assignedToMe: Flags.boolean({
|
|
67
|
-
char: "a",
|
|
49
|
+
export default defineCommand({
|
|
50
|
+
meta: { name: "branch", description: "Create a git branch from a GitHub issue" },
|
|
51
|
+
args: {
|
|
52
|
+
debug: debugArg,
|
|
53
|
+
assignedToMe: {
|
|
54
|
+
type: "boolean",
|
|
55
|
+
alias: "a",
|
|
68
56
|
description: "Only show issues assigned to me",
|
|
69
57
|
default: false,
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async run(): Promise<void> {
|
|
74
|
-
const { flags } = await this.parse(GhBranch);
|
|
75
|
-
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
async run({ args }) {
|
|
76
61
|
// Check prerequisites
|
|
77
62
|
const cliInstalled = await isGithubCliInstalled();
|
|
78
63
|
if (!cliInstalled) {
|
|
79
|
-
consola.
|
|
80
|
-
|
|
64
|
+
consola.error("Github CLI not installed");
|
|
65
|
+
process.exit(1);
|
|
81
66
|
}
|
|
82
67
|
|
|
83
|
-
consola.log("Assigned to me:",
|
|
68
|
+
consola.log("Assigned to me:", args.assignedToMe);
|
|
84
69
|
|
|
85
|
-
const currentIssues = await getIssues({ assignedToMe:
|
|
70
|
+
const currentIssues = await getIssues({ assignedToMe: args.assignedToMe, cwd: process.cwd() });
|
|
86
71
|
if (currentIssues.length === 0) {
|
|
87
72
|
consola.log(colors.yellow("No issues found, check assignments"));
|
|
88
|
-
|
|
73
|
+
process.exit(1);
|
|
89
74
|
} else {
|
|
90
75
|
consola.log(colors.green(`${currentIssues.length} Issues found assigned to you`));
|
|
91
76
|
}
|
|
@@ -114,14 +99,14 @@ export default class GhBranch extends BaseCommand {
|
|
|
114
99
|
{
|
|
115
100
|
onCancel: () => {
|
|
116
101
|
consola.info(colors.dim("Canceled"));
|
|
117
|
-
|
|
102
|
+
process.exit(0);
|
|
118
103
|
},
|
|
119
104
|
},
|
|
120
105
|
);
|
|
121
106
|
|
|
122
107
|
if (result.issueNumber === "cancel") {
|
|
123
108
|
consola.log(colors.dim("Canceled"));
|
|
124
|
-
|
|
109
|
+
process.exit(0);
|
|
125
110
|
}
|
|
126
111
|
|
|
127
112
|
const selectedIssue = currentIssues.find((i) => i.number === result.issueNumber)!;
|
|
@@ -133,7 +118,7 @@ export default class GhBranch extends BaseCommand {
|
|
|
133
118
|
await git(["checkout", "-b", branchName]);
|
|
134
119
|
} catch {
|
|
135
120
|
consola.debug("Branch checkout failed");
|
|
136
|
-
|
|
121
|
+
process.exit(1);
|
|
137
122
|
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
123
|
+
},
|
|
124
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
|
|
3
|
+
export default defineCommand({
|
|
4
|
+
meta: { name: "gh", description: "GitHub utilities" },
|
|
5
|
+
subCommands: {
|
|
6
|
+
branch: () => import("./branch.js").then((m) => m.default),
|
|
7
|
+
"branch-clean": () => import("./branch-clean.js").then((m) => m.default),
|
|
8
|
+
pr: () => import("./pr.js").then((m) => m.default),
|
|
9
|
+
},
|
|
10
|
+
});
|