claude-trello-cli 0.1.5 → 0.1.7
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 +31 -4
- package/dist/index.js +312 -77
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,12 +5,13 @@ Point [Claude Code](https://docs.anthropic.com/en/docs/claude-code) at a Trello
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx claude-trello-cli login
|
|
8
|
+
npx claude-trello-cli register # create an account (or `login` if you have one)
|
|
9
|
+
npx claude-trello-cli setup # connect Trello + save your Anthropic API key
|
|
9
10
|
cd ~/my-project
|
|
10
|
-
npx claude-trello-cli run
|
|
11
|
+
npx claude-trello-cli run # pick a board and start working
|
|
11
12
|
```
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
Everything happens in your terminal — no web app required.
|
|
14
15
|
|
|
15
16
|
## What It Does
|
|
16
17
|
|
|
@@ -78,11 +79,25 @@ claude-trello run
|
|
|
78
79
|
|
|
79
80
|
## Prerequisites
|
|
80
81
|
|
|
81
|
-
- **A
|
|
82
|
+
- **A Trello account** — you'll connect it during `setup`
|
|
83
|
+
- **An Anthropic API key** — get one from [console.anthropic.com](https://console.anthropic.com/settings/keys)
|
|
82
84
|
- **Node.js 20+**
|
|
83
85
|
|
|
84
86
|
## Commands
|
|
85
87
|
|
|
88
|
+
### `claude-trello-cli register`
|
|
89
|
+
|
|
90
|
+
Create a new account. You'll be prompted for your name, email, and password.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx claude-trello-cli register
|
|
94
|
+
npx claude-trello-cli register --server https://your-self-hosted-instance.com
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| Flag | Description |
|
|
98
|
+
|------|-------------|
|
|
99
|
+
| `-s, --server <url>` | Server URL (default: `https://ct.joshualevine.me`) |
|
|
100
|
+
|
|
86
101
|
### `claude-trello-cli login`
|
|
87
102
|
|
|
88
103
|
Sign in with your email and password. Your session is stored at `~/.config/claude-trello/config.json` with restricted file permissions.
|
|
@@ -96,6 +111,14 @@ npx claude-trello-cli login --server https://your-app.vercel.app
|
|
|
96
111
|
|------|-------------|
|
|
97
112
|
| `-s, --server <url>` | Server URL (default: `https://ct.joshualevine.me`) |
|
|
98
113
|
|
|
114
|
+
### `claude-trello-cli setup`
|
|
115
|
+
|
|
116
|
+
Interactive wizard that walks you through connecting Trello and saving your Anthropic API key. Opens your browser for Trello authorization and polls until complete.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx claude-trello-cli setup
|
|
120
|
+
```
|
|
121
|
+
|
|
99
122
|
### `claude-trello-cli run`
|
|
100
123
|
|
|
101
124
|
The main command. Select a board interactively, review the cards, and launch a Claude Code session.
|
|
@@ -104,12 +127,16 @@ The main command. Select a board interactively, review the cards, and launch a C
|
|
|
104
127
|
npx claude-trello-cli run
|
|
105
128
|
npx claude-trello-cli run --board 60d5e2a3f1a2b40017c3d4e5
|
|
106
129
|
npx claude-trello-cli run --dir ~/projects/my-api
|
|
130
|
+
npx claude-trello-cli run --message "Check the development branch for comparison"
|
|
107
131
|
```
|
|
108
132
|
|
|
133
|
+
Without `--message`, you'll be prompted interactively before the session starts. Press Enter to skip.
|
|
134
|
+
|
|
109
135
|
| Flag | Description |
|
|
110
136
|
|------|-------------|
|
|
111
137
|
| `-b, --board <id>` | Board ID — skip interactive selection |
|
|
112
138
|
| `-d, --dir <path>` | Working directory (default: current directory) |
|
|
139
|
+
| `-m, --message <text>` | Initial instructions for Claude (e.g. "focus on API cards first") |
|
|
113
140
|
|
|
114
141
|
### `claude-trello-cli boards`
|
|
115
142
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command8 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/login.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -101,14 +101,14 @@ async function apiFetch(path, options) {
|
|
|
101
101
|
}
|
|
102
102
|
return res.json();
|
|
103
103
|
}
|
|
104
|
-
async function signIn(serverUrl, email,
|
|
104
|
+
async function signIn(serverUrl, email, password4) {
|
|
105
105
|
const res = await fetch(`${serverUrl}/api/auth/sign-in/email`, {
|
|
106
106
|
method: "POST",
|
|
107
107
|
headers: {
|
|
108
108
|
"Content-Type": "application/json",
|
|
109
109
|
Origin: serverUrl
|
|
110
110
|
},
|
|
111
|
-
body: JSON.stringify({ email, password:
|
|
111
|
+
body: JSON.stringify({ email, password: password4 }),
|
|
112
112
|
redirect: "manual"
|
|
113
113
|
});
|
|
114
114
|
if (!res.ok) {
|
|
@@ -128,6 +128,43 @@ async function signIn(serverUrl, email, password2) {
|
|
|
128
128
|
const data = await res.json();
|
|
129
129
|
return { cookies, user: data.user };
|
|
130
130
|
}
|
|
131
|
+
async function signUp(serverUrl, name, email, password4) {
|
|
132
|
+
const res = await fetch(`${serverUrl}/api/auth/sign-up/email`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
"Content-Type": "application/json",
|
|
136
|
+
Origin: serverUrl
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify({ name, email, password: password4 }),
|
|
139
|
+
redirect: "manual"
|
|
140
|
+
});
|
|
141
|
+
if (!res.ok) {
|
|
142
|
+
let errorMsg = "Registration failed";
|
|
143
|
+
try {
|
|
144
|
+
const body = await res.json();
|
|
145
|
+
errorMsg = body.message || body.error || errorMsg;
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
throw new ApiError(res.status, errorMsg);
|
|
149
|
+
}
|
|
150
|
+
const setCookieHeaders = res.headers.getSetCookie?.() ?? [];
|
|
151
|
+
const cookies = setCookieHeaders.map((c) => c.split(";")[0]).join("; ");
|
|
152
|
+
if (!cookies) {
|
|
153
|
+
throw new ApiError(500, "No session cookie received from server");
|
|
154
|
+
}
|
|
155
|
+
const data = await res.json();
|
|
156
|
+
return { cookies, user: data.user };
|
|
157
|
+
}
|
|
158
|
+
async function getTrelloAuthUrl() {
|
|
159
|
+
const data = await apiFetch("/api/trello/authorize");
|
|
160
|
+
return data.url;
|
|
161
|
+
}
|
|
162
|
+
async function saveApiKey(apiKey) {
|
|
163
|
+
await apiFetch("/api/settings/apikey", {
|
|
164
|
+
method: "POST",
|
|
165
|
+
body: JSON.stringify({ apiKey })
|
|
166
|
+
});
|
|
167
|
+
}
|
|
131
168
|
async function getIntegrationStatus() {
|
|
132
169
|
return apiFetch("/api/settings/status");
|
|
133
170
|
}
|
|
@@ -169,18 +206,200 @@ var loginCommand = new Command("login").description("Sign in to Claude Trello Br
|
|
|
169
206
|
}
|
|
170
207
|
});
|
|
171
208
|
|
|
172
|
-
// src/commands/
|
|
209
|
+
// src/commands/register.ts
|
|
173
210
|
import { Command as Command2 } from "commander";
|
|
211
|
+
import { input as input2, password as password2 } from "@inquirer/prompts";
|
|
174
212
|
import chalk2 from "chalk";
|
|
175
|
-
|
|
213
|
+
import ora2 from "ora";
|
|
214
|
+
var registerCommand = new Command2("register").description("Create a new Claude Trello Bridge account").option("-s, --server <url>", "Server URL (default: https://ct.joshualevine.me)").action(async (opts) => {
|
|
215
|
+
const serverUrl = opts.server || getServerUrl();
|
|
216
|
+
console.log(chalk2.bold("Create a Claude Trello Bridge account"));
|
|
217
|
+
console.log(chalk2.dim(`Server: ${serverUrl}
|
|
218
|
+
`));
|
|
219
|
+
const name = await input2({ message: "Name:" });
|
|
220
|
+
const email = await input2({ message: "Email:" });
|
|
221
|
+
const pass = await password2({ message: "Password (min 8 characters):" });
|
|
222
|
+
if (pass.length < 8) {
|
|
223
|
+
console.log(chalk2.red("Password must be at least 8 characters."));
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
const spinner = ora2("Creating account...").start();
|
|
227
|
+
try {
|
|
228
|
+
const { cookies, user } = await signUp(serverUrl, name, email, pass);
|
|
229
|
+
saveConfig({
|
|
230
|
+
serverUrl,
|
|
231
|
+
sessionCookie: cookies,
|
|
232
|
+
userEmail: user.email,
|
|
233
|
+
userName: user.name
|
|
234
|
+
});
|
|
235
|
+
spinner.succeed(
|
|
236
|
+
`Account created! Signed in as ${chalk2.bold(user.name)} (${user.email})`
|
|
237
|
+
);
|
|
238
|
+
console.log(
|
|
239
|
+
chalk2.dim(
|
|
240
|
+
"\n Run `claude-trello setup` to connect Trello and add your API key.\n"
|
|
241
|
+
)
|
|
242
|
+
);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
spinner.fail(err instanceof Error ? err.message : "Registration failed");
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// src/commands/setup.ts
|
|
250
|
+
import { exec } from "child_process";
|
|
251
|
+
import { Command as Command3 } from "commander";
|
|
252
|
+
import { password as password3, confirm } from "@inquirer/prompts";
|
|
253
|
+
import chalk3 from "chalk";
|
|
254
|
+
import ora3 from "ora";
|
|
255
|
+
function openBrowser(url) {
|
|
256
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
257
|
+
exec(`${cmd} ${JSON.stringify(url)}`);
|
|
258
|
+
}
|
|
259
|
+
async function pollForTrello(timeoutMs = 12e4, intervalMs = 2e3) {
|
|
260
|
+
const deadline = Date.now() + timeoutMs;
|
|
261
|
+
while (Date.now() < deadline) {
|
|
262
|
+
try {
|
|
263
|
+
const status = await getIntegrationStatus();
|
|
264
|
+
if (status.trelloLinked) return true;
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
var setupCommand = new Command3("setup").description("Connect Trello and save your Anthropic API key").action(async () => {
|
|
272
|
+
if (!isLoggedIn()) {
|
|
273
|
+
console.log(
|
|
274
|
+
chalk3.red(
|
|
275
|
+
"Not logged in. Run `claude-trello register` or `claude-trello login` first."
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
console.log(chalk3.bold("\nClaude Trello Bridge \u2014 Setup\n"));
|
|
281
|
+
const statusSpinner = ora3("Checking current status...").start();
|
|
282
|
+
let status;
|
|
283
|
+
try {
|
|
284
|
+
status = await getIntegrationStatus();
|
|
285
|
+
statusSpinner.stop();
|
|
286
|
+
} catch (err) {
|
|
287
|
+
statusSpinner.fail(
|
|
288
|
+
err instanceof Error ? err.message : "Failed to check status"
|
|
289
|
+
);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
if (status.trelloLinked) {
|
|
293
|
+
console.log(
|
|
294
|
+
` ${chalk3.green("1.")} Trello ${chalk3.green("Connected")}`
|
|
295
|
+
);
|
|
296
|
+
} else {
|
|
297
|
+
console.log(
|
|
298
|
+
` ${chalk3.yellow("1.")} Trello ${chalk3.yellow("Not connected")}
|
|
299
|
+
`
|
|
300
|
+
);
|
|
301
|
+
const proceed = await confirm({
|
|
302
|
+
message: "Open your browser to connect Trello? (you'll authorize and be redirected back)",
|
|
303
|
+
default: true
|
|
304
|
+
});
|
|
305
|
+
if (!proceed) {
|
|
306
|
+
console.log(chalk3.dim("Setup cancelled."));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const urlSpinner = ora3("Getting Trello authorization URL...").start();
|
|
310
|
+
let authUrl;
|
|
311
|
+
try {
|
|
312
|
+
authUrl = await getTrelloAuthUrl();
|
|
313
|
+
urlSpinner.stop();
|
|
314
|
+
} catch (err) {
|
|
315
|
+
urlSpinner.fail(
|
|
316
|
+
err instanceof Error ? err.message : "Failed to get auth URL"
|
|
317
|
+
);
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
console.log(chalk3.dim(`
|
|
321
|
+
Opening: ${authUrl}
|
|
322
|
+
`));
|
|
323
|
+
openBrowser(authUrl);
|
|
324
|
+
console.log(
|
|
325
|
+
chalk3.dim(
|
|
326
|
+
" Authorize the app on Trello, then come back here.\n If the browser didn't open, copy the URL above manually.\n"
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
const pollSpinner = ora3(
|
|
330
|
+
"Waiting for Trello connection (up to 2 minutes)..."
|
|
331
|
+
).start();
|
|
332
|
+
const connected = await pollForTrello();
|
|
333
|
+
if (connected) {
|
|
334
|
+
pollSpinner.succeed("Trello connected!");
|
|
335
|
+
} else {
|
|
336
|
+
pollSpinner.fail(
|
|
337
|
+
"Timed out waiting for Trello connection. Run `claude-trello setup` to try again."
|
|
338
|
+
);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (status.hasApiKey) {
|
|
343
|
+
console.log(
|
|
344
|
+
` ${chalk3.green("2.")} Anthropic API Key ${chalk3.green("Configured")}`
|
|
345
|
+
);
|
|
346
|
+
} else {
|
|
347
|
+
console.log(
|
|
348
|
+
`
|
|
349
|
+
${chalk3.yellow("2.")} Anthropic API Key ${chalk3.yellow("Not set")}
|
|
350
|
+
`
|
|
351
|
+
);
|
|
352
|
+
console.log(
|
|
353
|
+
chalk3.dim(
|
|
354
|
+
" Get your key from https://console.anthropic.com/settings/keys\n"
|
|
355
|
+
)
|
|
356
|
+
);
|
|
357
|
+
const apiKey = await password3({
|
|
358
|
+
message: "Paste your Anthropic API key (sk-ant-api03-...):"
|
|
359
|
+
});
|
|
360
|
+
if (!apiKey.startsWith("sk-ant-api03-")) {
|
|
361
|
+
console.log(
|
|
362
|
+
chalk3.red('Invalid key format. Must start with "sk-ant-api03-".')
|
|
363
|
+
);
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
const keySpinner = ora3("Saving API key...").start();
|
|
367
|
+
try {
|
|
368
|
+
await saveApiKey(apiKey);
|
|
369
|
+
keySpinner.succeed("API key saved (encrypted on server)");
|
|
370
|
+
} catch (err) {
|
|
371
|
+
keySpinner.fail(
|
|
372
|
+
err instanceof Error ? err.message : "Failed to save API key"
|
|
373
|
+
);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const serverUrl = getServerUrl();
|
|
378
|
+
console.log(
|
|
379
|
+
chalk3.green.bold(
|
|
380
|
+
"\n All set! Run `claude-trello run` to start a session.\n"
|
|
381
|
+
)
|
|
382
|
+
);
|
|
383
|
+
console.log(
|
|
384
|
+
chalk3.dim(
|
|
385
|
+
` You can manage your settings anytime at ${serverUrl}/settings
|
|
386
|
+
`
|
|
387
|
+
)
|
|
388
|
+
);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// src/commands/logout.ts
|
|
392
|
+
import { Command as Command4 } from "commander";
|
|
393
|
+
import chalk4 from "chalk";
|
|
394
|
+
var logoutCommand = new Command4("logout").description("Sign out and clear stored session").action(() => {
|
|
176
395
|
if (!isLoggedIn()) {
|
|
177
|
-
console.log(
|
|
396
|
+
console.log(chalk4.dim("Not currently logged in."));
|
|
178
397
|
return;
|
|
179
398
|
}
|
|
180
399
|
const config = getConfig();
|
|
181
400
|
clearConfig();
|
|
182
401
|
console.log(
|
|
183
|
-
|
|
402
|
+
chalk4.green(
|
|
184
403
|
`Signed out${config.userEmail ? ` (${config.userEmail})` : ""}.`
|
|
185
404
|
)
|
|
186
405
|
);
|
|
@@ -188,10 +407,10 @@ var logoutCommand = new Command2("logout").description("Sign out and clear store
|
|
|
188
407
|
|
|
189
408
|
// src/commands/run.ts
|
|
190
409
|
import { resolve } from "path";
|
|
191
|
-
import { Command as
|
|
192
|
-
import { select, confirm, input as
|
|
193
|
-
import
|
|
194
|
-
import
|
|
410
|
+
import { Command as Command5 } from "commander";
|
|
411
|
+
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
412
|
+
import chalk5 from "chalk";
|
|
413
|
+
import ora4 from "ora";
|
|
195
414
|
|
|
196
415
|
// src/lib/runner.ts
|
|
197
416
|
import {
|
|
@@ -258,13 +477,20 @@ Do not mark items complete unless the code change has actually been made and ver
|
|
|
258
477
|
After completing ALL checklist items on a card, call move_card_to_done with the cardId to move it to the Done list.
|
|
259
478
|
Once a card is in Done, do not interact with it again \u2014 move on to the next card.
|
|
260
479
|
Focus on one card at a time. Complete all its items, move it to Done, then proceed to the next.`;
|
|
261
|
-
function buildUserPrompt(boardData) {
|
|
262
|
-
|
|
480
|
+
function buildUserPrompt(boardData, userMessage) {
|
|
481
|
+
let prompt = `Here is the Trello board with tasks to complete:
|
|
263
482
|
|
|
264
483
|
${JSON.stringify(boardData, null, 2)}`;
|
|
484
|
+
if (userMessage?.trim()) {
|
|
485
|
+
prompt += `
|
|
486
|
+
|
|
487
|
+
Additional instructions from the user:
|
|
488
|
+
${userMessage.trim()}`;
|
|
489
|
+
}
|
|
490
|
+
return prompt;
|
|
265
491
|
}
|
|
266
492
|
function launchSession(options) {
|
|
267
|
-
const { credentials, boardData, cwd, abortController } = options;
|
|
493
|
+
const { credentials, boardData, cwd, userMessage, abortController } = options;
|
|
268
494
|
const trello = createTrelloClient(
|
|
269
495
|
credentials.trelloApiKey,
|
|
270
496
|
credentials.trelloToken
|
|
@@ -316,7 +542,7 @@ function launchSession(options) {
|
|
|
316
542
|
tools: [checkTrelloItem, moveCardToDone]
|
|
317
543
|
});
|
|
318
544
|
return query({
|
|
319
|
-
prompt: buildUserPrompt(activeBoardData),
|
|
545
|
+
prompt: buildUserPrompt(activeBoardData, userMessage),
|
|
320
546
|
options: {
|
|
321
547
|
abortController,
|
|
322
548
|
cwd,
|
|
@@ -340,10 +566,10 @@ function launchSession(options) {
|
|
|
340
566
|
}
|
|
341
567
|
|
|
342
568
|
// src/commands/run.ts
|
|
343
|
-
var runCommand = new
|
|
569
|
+
var runCommand = new Command5("run").description("Select a Trello board and start a Claude Code session").option("-b, --board <id>", "Board ID (skip interactive selection)").option("-d, --dir <path>", "Working directory (default: current)").option("-m, --message <text>", "Initial instructions for Claude").action(async (opts) => {
|
|
344
570
|
if (!isLoggedIn()) {
|
|
345
571
|
console.log(
|
|
346
|
-
|
|
572
|
+
chalk5.red("Not logged in. Run `claude-trello login` first.")
|
|
347
573
|
);
|
|
348
574
|
process.exit(1);
|
|
349
575
|
}
|
|
@@ -351,7 +577,7 @@ var runCommand = new Command3("run").description("Select a Trello board and star
|
|
|
351
577
|
let boardId = opts.board;
|
|
352
578
|
let boardName = "";
|
|
353
579
|
if (!boardId) {
|
|
354
|
-
const spinner =
|
|
580
|
+
const spinner = ora4("Fetching boards...").start();
|
|
355
581
|
let boards;
|
|
356
582
|
try {
|
|
357
583
|
boards = await getBoards();
|
|
@@ -364,7 +590,7 @@ var runCommand = new Command3("run").description("Select a Trello board and star
|
|
|
364
590
|
}
|
|
365
591
|
if (boards.length === 0) {
|
|
366
592
|
console.log(
|
|
367
|
-
|
|
593
|
+
chalk5.red("No boards found. Create a board on Trello first.")
|
|
368
594
|
);
|
|
369
595
|
process.exit(1);
|
|
370
596
|
}
|
|
@@ -378,7 +604,7 @@ var runCommand = new Command3("run").description("Select a Trello board and star
|
|
|
378
604
|
});
|
|
379
605
|
boardName = boards.find((b) => b.id === boardId)?.name ?? boardId;
|
|
380
606
|
}
|
|
381
|
-
const dataSpinner =
|
|
607
|
+
const dataSpinner = ora4("Fetching cards and checklists...").start();
|
|
382
608
|
let cardsResponse;
|
|
383
609
|
try {
|
|
384
610
|
cardsResponse = await getBoardData(boardId);
|
|
@@ -392,8 +618,8 @@ var runCommand = new Command3("run").description("Select a Trello board and star
|
|
|
392
618
|
const { cards, lists, doneListId } = cardsResponse;
|
|
393
619
|
if (!boardName) boardName = boardId;
|
|
394
620
|
console.log(`
|
|
395
|
-
${
|
|
396
|
-
console.log(
|
|
621
|
+
${chalk5.bold(boardName)}`);
|
|
622
|
+
console.log(chalk5.dim("\u2500".repeat(boardName.length)));
|
|
397
623
|
const doneCards = doneListId ? cards.filter((c) => c.idList === doneListId) : [];
|
|
398
624
|
const activeCards = doneListId ? cards.filter((c) => c.idList !== doneListId) : cards;
|
|
399
625
|
const listMap = /* @__PURE__ */ new Map();
|
|
@@ -408,7 +634,7 @@ ${chalk3.bold(boardName)}`);
|
|
|
408
634
|
const listName = listMap.get(listId)?.name ?? "Unknown";
|
|
409
635
|
console.log(
|
|
410
636
|
`
|
|
411
|
-
${
|
|
637
|
+
${chalk5.cyan.bold(listName)} (${listCards.length} card${listCards.length === 1 ? "" : "s"}):`
|
|
412
638
|
);
|
|
413
639
|
for (const card of listCards) {
|
|
414
640
|
const total = card.checklists.reduce(
|
|
@@ -421,31 +647,37 @@ ${chalk3.bold(boardName)}`);
|
|
|
421
647
|
);
|
|
422
648
|
const progress = total > 0 ? ` [${done}/${total}]` : "";
|
|
423
649
|
console.log(
|
|
424
|
-
` ${
|
|
650
|
+
` ${chalk5.white("\u2022")} ${card.name}${chalk5.dim(progress)}`
|
|
425
651
|
);
|
|
426
652
|
}
|
|
427
653
|
}
|
|
428
654
|
if (doneCards.length > 0) {
|
|
429
655
|
console.log(
|
|
430
656
|
`
|
|
431
|
-
${
|
|
657
|
+
${chalk5.dim(`Done (${doneCards.length} card${doneCards.length === 1 ? "" : "s"}) [skipped]`)}`
|
|
432
658
|
);
|
|
433
659
|
}
|
|
434
660
|
if (activeCards.length === 0) {
|
|
435
|
-
console.log(
|
|
661
|
+
console.log(chalk5.dim("\n No active cards to work on."));
|
|
436
662
|
return;
|
|
437
663
|
}
|
|
438
664
|
console.log(`
|
|
439
|
-
${
|
|
440
|
-
const proceed = await
|
|
665
|
+
${chalk5.dim(`Working directory: ${cwd}`)}`);
|
|
666
|
+
const proceed = await confirm2({
|
|
441
667
|
message: `Start Claude Code session? (${activeCards.length} active card${activeCards.length === 1 ? "" : "s"})`,
|
|
442
668
|
default: true
|
|
443
669
|
});
|
|
444
670
|
if (!proceed) {
|
|
445
|
-
console.log(
|
|
671
|
+
console.log(chalk5.dim("Cancelled."));
|
|
446
672
|
return;
|
|
447
673
|
}
|
|
448
|
-
|
|
674
|
+
let userMessage = opts.message;
|
|
675
|
+
if (!userMessage) {
|
|
676
|
+
userMessage = await input3({
|
|
677
|
+
message: "Instructions for Claude (optional \u2014 press Enter to skip):"
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
const credSpinner = ora4("Loading credentials...").start();
|
|
449
681
|
let credentials;
|
|
450
682
|
try {
|
|
451
683
|
credentials = await getCredentials();
|
|
@@ -462,11 +694,11 @@ ${chalk3.bold(boardName)}`);
|
|
|
462
694
|
doneListId: doneListId ?? void 0
|
|
463
695
|
};
|
|
464
696
|
console.log(`
|
|
465
|
-
${
|
|
697
|
+
${chalk5.bold.blue("Starting Claude Code session...")}
|
|
466
698
|
`);
|
|
467
699
|
const abortController = new AbortController();
|
|
468
700
|
const sigintHandler = () => {
|
|
469
|
-
console.log(
|
|
701
|
+
console.log(chalk5.dim("\n\nAborting session..."));
|
|
470
702
|
abortController.abort();
|
|
471
703
|
};
|
|
472
704
|
process.on("SIGINT", sigintHandler);
|
|
@@ -475,20 +707,21 @@ ${chalk3.bold.blue("Starting Claude Code session...")}
|
|
|
475
707
|
credentials,
|
|
476
708
|
boardData,
|
|
477
709
|
cwd,
|
|
710
|
+
userMessage: userMessage || void 0,
|
|
478
711
|
abortController
|
|
479
712
|
});
|
|
480
713
|
for await (const message of session) {
|
|
481
714
|
await handleMessage(message, session);
|
|
482
715
|
}
|
|
483
716
|
console.log(`
|
|
484
|
-
${
|
|
717
|
+
${chalk5.green.bold("Session complete.")}
|
|
485
718
|
`);
|
|
486
719
|
} catch (err) {
|
|
487
720
|
if (abortController.signal.aborted) {
|
|
488
|
-
console.log(
|
|
721
|
+
console.log(chalk5.dim("\nSession aborted."));
|
|
489
722
|
} else {
|
|
490
723
|
console.error(
|
|
491
|
-
|
|
724
|
+
chalk5.red(
|
|
492
725
|
`
|
|
493
726
|
Session error: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
494
727
|
)
|
|
@@ -504,7 +737,7 @@ async function handleMessage(message, session) {
|
|
|
504
737
|
case "system": {
|
|
505
738
|
if (message.subtype === "init") {
|
|
506
739
|
console.log(
|
|
507
|
-
|
|
740
|
+
chalk5.dim(
|
|
508
741
|
` Session initialized (model: ${message.model})
|
|
509
742
|
`
|
|
510
743
|
)
|
|
@@ -523,17 +756,17 @@ async function handleMessage(message, session) {
|
|
|
523
756
|
const name = block.name;
|
|
524
757
|
const toolInput = block.input ?? {};
|
|
525
758
|
if (name === "mcp__trello-tools__check_trello_item") {
|
|
526
|
-
console.log(
|
|
759
|
+
console.log(chalk5.green(` \u2713 Checked item on Trello`));
|
|
527
760
|
} else if (name === "mcp__trello-tools__move_card_to_done") {
|
|
528
|
-
console.log(
|
|
761
|
+
console.log(chalk5.green.bold(` \u2713 Moved card to Done`));
|
|
529
762
|
} else if (name === "AskUserQuestion") {
|
|
530
763
|
const questions = toolInput.questions;
|
|
531
764
|
if (questions && questions.length > 0) {
|
|
532
765
|
const questionText = questions.map((q) => q.question).join("\n");
|
|
533
|
-
console.log(
|
|
766
|
+
console.log(chalk5.yellow(`
|
|
534
767
|
? ${questionText}
|
|
535
768
|
`));
|
|
536
|
-
const answer = await
|
|
769
|
+
const answer = await input3({ message: ">" });
|
|
537
770
|
async function* userInput() {
|
|
538
771
|
yield {
|
|
539
772
|
type: "user",
|
|
@@ -556,7 +789,7 @@ async function handleMessage(message, session) {
|
|
|
556
789
|
}
|
|
557
790
|
case "result": {
|
|
558
791
|
const result = message.result ?? message.subtype;
|
|
559
|
-
console.log(
|
|
792
|
+
console.log(chalk5.dim(`
|
|
560
793
|
Result: ${result}`));
|
|
561
794
|
break;
|
|
562
795
|
}
|
|
@@ -564,62 +797,62 @@ async function handleMessage(message, session) {
|
|
|
564
797
|
}
|
|
565
798
|
function formatToolUse(name, toolInput) {
|
|
566
799
|
const path = toolInput.file_path ?? toolInput.path ?? toolInput.pattern ?? "";
|
|
567
|
-
const shortPath = path ? ` ${
|
|
800
|
+
const shortPath = path ? ` ${chalk5.cyan(path)}` : "";
|
|
568
801
|
switch (name) {
|
|
569
802
|
case "Read":
|
|
570
|
-
return
|
|
803
|
+
return chalk5.dim(` Reading${shortPath}`);
|
|
571
804
|
case "Edit":
|
|
572
|
-
return
|
|
805
|
+
return chalk5.dim(` Editing${shortPath}`);
|
|
573
806
|
case "Write":
|
|
574
|
-
return
|
|
807
|
+
return chalk5.dim(` Writing${shortPath}`);
|
|
575
808
|
case "Glob":
|
|
576
|
-
return
|
|
809
|
+
return chalk5.dim(` Searching files${shortPath}`);
|
|
577
810
|
case "Grep":
|
|
578
|
-
return
|
|
579
|
-
` Searching for ${
|
|
811
|
+
return chalk5.dim(
|
|
812
|
+
` Searching for ${chalk5.cyan(toolInput.pattern || "...")}${toolInput.path ? ` in ${chalk5.cyan(toolInput.path)}` : ""}`
|
|
580
813
|
);
|
|
581
814
|
case "Bash": {
|
|
582
815
|
const cmd = toolInput.command || "";
|
|
583
816
|
const preview = cmd.length > 80 ? cmd.slice(0, 77) + "..." : cmd;
|
|
584
|
-
return
|
|
817
|
+
return chalk5.dim(` Running ${chalk5.cyan(preview)}`);
|
|
585
818
|
}
|
|
586
819
|
case "TodoWrite":
|
|
587
|
-
return
|
|
820
|
+
return chalk5.dim(" Updating task list");
|
|
588
821
|
case "Agent":
|
|
589
|
-
return
|
|
822
|
+
return chalk5.dim(
|
|
590
823
|
` Spawning agent${toolInput.description ? `: ${toolInput.description}` : ""}`
|
|
591
824
|
);
|
|
592
825
|
default:
|
|
593
|
-
return
|
|
826
|
+
return chalk5.dim(` [${name}]${shortPath}`);
|
|
594
827
|
}
|
|
595
828
|
}
|
|
596
829
|
|
|
597
830
|
// src/commands/boards.ts
|
|
598
|
-
import { Command as
|
|
599
|
-
import
|
|
600
|
-
import
|
|
601
|
-
var boardsCommand = new
|
|
831
|
+
import { Command as Command6 } from "commander";
|
|
832
|
+
import chalk6 from "chalk";
|
|
833
|
+
import ora5 from "ora";
|
|
834
|
+
var boardsCommand = new Command6("boards").description("List your Trello boards").action(async () => {
|
|
602
835
|
if (!isLoggedIn()) {
|
|
603
836
|
console.log(
|
|
604
|
-
|
|
837
|
+
chalk6.red("Not logged in. Run `claude-trello login` first.")
|
|
605
838
|
);
|
|
606
839
|
process.exit(1);
|
|
607
840
|
}
|
|
608
|
-
const spinner =
|
|
841
|
+
const spinner = ora5("Fetching boards...").start();
|
|
609
842
|
try {
|
|
610
843
|
const boards = await getBoards();
|
|
611
844
|
spinner.stop();
|
|
612
845
|
if (boards.length === 0) {
|
|
613
|
-
console.log(
|
|
846
|
+
console.log(chalk6.dim("No boards found."));
|
|
614
847
|
return;
|
|
615
848
|
}
|
|
616
|
-
console.log(
|
|
849
|
+
console.log(chalk6.bold(`
|
|
617
850
|
Your Trello Boards (${boards.length}):
|
|
618
851
|
`));
|
|
619
852
|
for (const board of boards) {
|
|
620
|
-
console.log(` ${
|
|
853
|
+
console.log(` ${chalk6.cyan(board.name)} ${chalk6.dim(board.id)}`);
|
|
621
854
|
if (board.desc) {
|
|
622
|
-
console.log(` ${
|
|
855
|
+
console.log(` ${chalk6.dim(board.desc.slice(0, 80))}`);
|
|
623
856
|
}
|
|
624
857
|
console.log();
|
|
625
858
|
}
|
|
@@ -632,40 +865,40 @@ Your Trello Boards (${boards.length}):
|
|
|
632
865
|
});
|
|
633
866
|
|
|
634
867
|
// src/commands/status.ts
|
|
635
|
-
import { Command as
|
|
636
|
-
import
|
|
637
|
-
import
|
|
638
|
-
var statusCommand = new
|
|
868
|
+
import { Command as Command7 } from "commander";
|
|
869
|
+
import chalk7 from "chalk";
|
|
870
|
+
import ora6 from "ora";
|
|
871
|
+
var statusCommand = new Command7("status").description("Check connection and integration status").action(async () => {
|
|
639
872
|
const config = getConfig();
|
|
640
|
-
console.log(
|
|
641
|
-
console.log(` Server: ${
|
|
873
|
+
console.log(chalk7.bold("\nClaude Trello Bridge \u2014 Status\n"));
|
|
874
|
+
console.log(` Server: ${chalk7.dim(getServerUrl())}`);
|
|
642
875
|
if (!isLoggedIn()) {
|
|
643
|
-
console.log(` Auth: ${
|
|
644
|
-
console.log(
|
|
876
|
+
console.log(` Auth: ${chalk7.red("Not logged in")}`);
|
|
877
|
+
console.log(chalk7.dim("\n Run `claude-trello login` to sign in.\n"));
|
|
645
878
|
return;
|
|
646
879
|
}
|
|
647
880
|
console.log(
|
|
648
|
-
` Auth: ${
|
|
881
|
+
` Auth: ${chalk7.green("Signed in")} as ${config.userName ?? config.userEmail ?? "unknown"}`
|
|
649
882
|
);
|
|
650
|
-
const spinner =
|
|
883
|
+
const spinner = ora6("Checking integrations...").start();
|
|
651
884
|
try {
|
|
652
885
|
const status = await getIntegrationStatus();
|
|
653
886
|
spinner.stop();
|
|
654
887
|
console.log(
|
|
655
|
-
` Trello: ${status.trelloLinked ?
|
|
888
|
+
` Trello: ${status.trelloLinked ? chalk7.green("Connected") : chalk7.red("Not connected")}`
|
|
656
889
|
);
|
|
657
890
|
console.log(
|
|
658
|
-
` API Key: ${status.hasApiKey ?
|
|
891
|
+
` API Key: ${status.hasApiKey ? chalk7.green("Configured") : chalk7.red("Not set")}`
|
|
659
892
|
);
|
|
660
893
|
if (!status.trelloLinked || !status.hasApiKey) {
|
|
661
894
|
console.log(
|
|
662
|
-
|
|
895
|
+
chalk7.dim(
|
|
663
896
|
"\n Complete setup at your web dashboard to use the CLI.\n"
|
|
664
897
|
)
|
|
665
898
|
);
|
|
666
899
|
} else {
|
|
667
900
|
console.log(
|
|
668
|
-
|
|
901
|
+
chalk7.green("\n Ready to go! Run `claude-trello run` to start.\n")
|
|
669
902
|
);
|
|
670
903
|
}
|
|
671
904
|
} catch (err) {
|
|
@@ -677,11 +910,13 @@ var statusCommand = new Command5("status").description("Check connection and int
|
|
|
677
910
|
});
|
|
678
911
|
|
|
679
912
|
// src/index.ts
|
|
680
|
-
var program = new
|
|
913
|
+
var program = new Command8();
|
|
681
914
|
program.name("claude-trello").description(
|
|
682
915
|
"Bridge Trello boards and Claude Code \u2014 work through tasks from your terminal"
|
|
683
916
|
).version("0.1.0");
|
|
917
|
+
program.addCommand(registerCommand);
|
|
684
918
|
program.addCommand(loginCommand);
|
|
919
|
+
program.addCommand(setupCommand);
|
|
685
920
|
program.addCommand(logoutCommand);
|
|
686
921
|
program.addCommand(runCommand);
|
|
687
922
|
program.addCommand(boardsCommand);
|