capyai 0.3.5 → 0.3.6

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/AGENTS.md CHANGED
@@ -81,19 +81,22 @@ Config file locations:
81
81
  - Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
82
82
  - Cursor: `.cursor/mcp.json`
83
83
 
84
- 14 MCP tools with full CLI parity:
84
+ 17 MCP tools with full CLI parity:
85
85
 
86
86
  | Tool | What it does | Annotations |
87
87
  |------|-------------|-------------|
88
88
  | `capy_captain` | Start Captain thread | openWorld |
89
89
  | `capy_build` | Start Build agent | openWorld |
90
+ | `capy_start` | Start/resume a backlog task | openWorld |
90
91
  | `capy_wait` | Block until done | readOnly, idempotent |
91
92
  | `capy_review` | Run quality gates | readOnly |
92
93
  | `capy_approve` | Approve task | openWorld |
93
94
  | `capy_retry` | Retry with context | openWorld |
95
+ | `capy_re_review` | Trigger Greptile re-review | openWorld |
94
96
  | `capy_status` | Task/thread details or dashboard | readOnly, idempotent |
95
- | `capy_list` | List tasks (filterable) | readOnly, idempotent |
96
- | `capy_threads` | List threads | readOnly, idempotent |
97
+ | `capy_list` | List tasks (filterable, paginated) | readOnly, idempotent |
98
+ | `capy_threads` | List threads (paginated) | readOnly, idempotent |
99
+ | `capy_thread_messages` | Read thread conversation history | readOnly, idempotent |
97
100
  | `capy_diff` | View diff | readOnly |
98
101
  | `capy_msg` | Message task/thread | openWorld |
99
102
  | `capy_stop` | Stop task/thread | destructive |
@@ -203,21 +206,30 @@ Every command supports `--json` for structured output. Errors always return `{ "
203
206
  |---------|-------------|
204
207
  | `capy captain "<prompt>"` | Start Captain thread |
205
208
  | `capy build "<prompt>"` | Start Build agent (small isolated tasks) |
209
+ | `capy start <id>` | Start/resume a backlog task |
210
+ | `capy stop <id> [reason]` | Stop a running task |
211
+ | `capy msg <id> "<text>"` | Message a running task |
206
212
  | `capy wait <id> --timeout=N` | Block until terminal state |
207
213
  | `capy review <id>` | Run quality gates (pass/fail) |
214
+ | `capy re-review <id>` | Trigger fresh Greptile review |
208
215
  | `capy approve <id>` | Approve if gates pass |
209
- | `capy retry <id> --fix="..."` | Retry with context |
210
- | `capy status` | Dashboard |
211
- | `capy list [status]` | List tasks |
212
- | `capy get <id>` | Task or thread details |
213
- | `capy diff <id>` | View diff |
214
- | `capy pr <id>` | Create PR |
215
- | `capy watch <id>` | Cron poll + notify |
216
- | `capy threads list` | List threads |
217
- | `capy threads get <id>` | Thread details |
216
+ | `capy retry <id> --fix="..."` | Retry with context from failure |
217
+ | `capy status` | Dashboard (all threads + tasks) |
218
+ | `capy list [status]` | List tasks (filter: in_progress, needs_review, backlog, archived) |
219
+ | `capy get <id>` | Task details (jams, PR state, credits) |
220
+ | `capy diff <id>` | View diff (file-by-file with patches) |
221
+ | `capy pr <id> [title]` | Create PR for a task |
222
+ | `capy watch <id>` | Cron poll + notify on completion |
223
+ | `capy unwatch <id>` | Stop watching |
224
+ | `capy watches` | List active watches |
225
+ | `capy threads list` | List Captain threads |
226
+ | `capy threads get <id>` | Thread details (tasks, PRs) |
218
227
  | `capy threads msg <id> "<text>"` | Message a thread |
228
+ | `capy threads stop <id>` | Stop a thread |
229
+ | `capy threads messages <id>` | Read thread conversation history |
219
230
  | `capy config [key] [value]` | Get/set config |
220
231
  | `capy models` | List available models |
232
+ | `capy tools` | Show all commands + env vars |
221
233
 
222
234
  ### Prompting tips
223
235
 
package/README.md CHANGED
@@ -87,7 +87,7 @@ For agents that prefer MCP over CLI:
87
87
  }
88
88
  ```
89
89
 
90
- 14 tools with full CLI parity: `capy_captain`, `capy_build`, `capy_wait`, `capy_review`, `capy_approve`, `capy_retry`, `capy_status`, `capy_list`, `capy_threads`, `capy_diff`, `capy_msg`, `capy_stop`, `capy_pr`, `capy_models`.
90
+ 17 tools with full CLI parity: `capy_captain`, `capy_build`, `capy_start`, `capy_wait`, `capy_review`, `capy_approve`, `capy_retry`, `capy_re_review`, `capy_status`, `capy_list`, `capy_threads`, `capy_thread_messages`, `capy_diff`, `capy_msg`, `capy_stop`, `capy_pr`, `capy_models`.
91
91
 
92
92
  ## Config
93
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capyai",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "type": "module",
5
5
  "description": "Unofficial Capy.ai CLI for agent orchestration with quality gates",
6
6
  "bin": {
@@ -3,7 +3,7 @@ name: capy
3
3
  description: Orchestrate Capy.ai coding agents with quality gates. Delegate coding work, wait for completion, review quality, approve or retry.
4
4
  metadata:
5
5
  author: yazcaleb
6
- version: "0.3.5"
6
+ version: "0.3.6"
7
7
  ---
8
8
 
9
9
  # capy
package/src/api.ts CHANGED
@@ -72,6 +72,11 @@ export async function listModelsWithKey(apiKey: string, server = "https://capy.a
72
72
  return data.models || [];
73
73
  }
74
74
 
75
+ export async function getProject(id?: string): Promise<Project & { createdAt?: string; updatedAt?: string }> {
76
+ const pid = id || config.load().projectId;
77
+ return request("GET", `/projects/${pid}`);
78
+ }
79
+
75
80
  // --- Threads ---
76
81
  export async function createThread(prompt: string, model?: string, repos?: unknown[]): Promise<Thread> {
77
82
  const cfg = config.load();
@@ -83,10 +88,11 @@ export async function createThread(prompt: string, model?: string, repos?: unkno
83
88
  });
84
89
  }
85
90
 
86
- export async function listThreads(opts: { limit?: number; status?: string } = {}): Promise<ListResponse<Thread>> {
91
+ export async function listThreads(opts: { limit?: number; status?: string; cursor?: string } = {}): Promise<ListResponse<Thread>> {
87
92
  const cfg = config.load();
88
93
  const p = new URLSearchParams({ projectId: cfg.projectId, limit: String(opts.limit || 10) });
89
94
  if (opts.status) p.set("status", opts.status);
95
+ if (opts.cursor) p.set("cursor", opts.cursor);
90
96
  return request("GET", `/threads?${p}`);
91
97
  }
92
98
 
@@ -121,10 +127,11 @@ export async function createTask(prompt: string, model?: string, opts: { title?:
121
127
  });
122
128
  }
123
129
 
124
- export async function listTasks(opts: { limit?: number; status?: string } = {}): Promise<ListResponse<Task>> {
130
+ export async function listTasks(opts: { limit?: number; status?: string; cursor?: string } = {}): Promise<ListResponse<Task>> {
125
131
  const cfg = config.load();
126
132
  const p = new URLSearchParams({ projectId: cfg.projectId, limit: String(opts.limit || 30) });
127
133
  if (opts.status) p.set("status", opts.status);
134
+ if (opts.cursor) p.set("cursor", opts.cursor);
128
135
  return request("GET", `/tasks?${p}`);
129
136
  }
130
137
 
package/src/mcp.ts CHANGED
@@ -234,12 +234,13 @@ server.registerTool("capy_list", {
234
234
  inputSchema: {
235
235
  status: z.string().optional().describe("Filter by status"),
236
236
  limit: z.number().optional().describe("Max results (default 30)"),
237
+ cursor: z.string().optional().describe("Pagination cursor from previous response"),
237
238
  },
238
239
  annotations: { readOnlyHint: true, idempotentHint: true },
239
- }, async ({ status, limit }) => {
240
+ }, async ({ status, limit, cursor }) => {
240
241
  try {
241
- const data = await api.listTasks({ status, limit: limit || 30 });
242
- return text(data.items || []);
242
+ const data = await api.listTasks({ status, limit: limit || 30, cursor });
243
+ return text({ items: data.items || [], nextCursor: data.nextCursor, hasMore: data.hasMore });
243
244
  } catch (e) { return err(e); }
244
245
  });
245
246
 
@@ -247,12 +248,13 @@ server.registerTool("capy_threads", {
247
248
  description: "List Captain threads",
248
249
  inputSchema: {
249
250
  limit: z.number().optional().describe("Max results (default 10)"),
251
+ cursor: z.string().optional().describe("Pagination cursor from previous response"),
250
252
  },
251
253
  annotations: { readOnlyHint: true, idempotentHint: true },
252
- }, async ({ limit }) => {
254
+ }, async ({ limit, cursor }) => {
253
255
  try {
254
- const data = await api.listThreads({ limit: limit || 10 });
255
- return text(data.items || []);
256
+ const data = await api.listThreads({ limit: limit || 10, cursor });
257
+ return text({ items: data.items || [], nextCursor: data.nextCursor, hasMore: data.hasMore });
256
258
  } catch (e) { return err(e); }
257
259
  });
258
260
 
@@ -315,6 +317,61 @@ server.registerTool("capy_pr", {
315
317
  } catch (e) { return err(e); }
316
318
  });
317
319
 
320
+ server.registerTool("capy_start", {
321
+ description: "Start a backlog task (resume a task that was created but not yet running)",
322
+ inputSchema: {
323
+ id: z.string().describe("Task ID"),
324
+ model: z.string().optional().describe("Model ID override"),
325
+ },
326
+ annotations: { openWorldHint: true },
327
+ }, async ({ id, model }) => {
328
+ try {
329
+ const data = await api.startTask(id, model);
330
+ return text(data);
331
+ } catch (e) { return err(e); }
332
+ });
333
+
334
+ server.registerTool("capy_thread_messages", {
335
+ description: "Read the conversation history of a Captain thread",
336
+ inputSchema: {
337
+ id: z.string().describe("Thread ID"),
338
+ limit: z.number().optional().describe("Max messages (default 50)"),
339
+ },
340
+ annotations: { readOnlyHint: true, idempotentHint: true },
341
+ }, async ({ id, limit }) => {
342
+ try {
343
+ const data = await api.getThreadMessages(id, { limit: limit || 50 });
344
+ return text(data.items || []);
345
+ } catch (e) { return err(e); }
346
+ });
347
+
348
+ server.registerTool("capy_re_review", {
349
+ description: "Trigger a fresh Greptile code review on a task's PR",
350
+ inputSchema: {
351
+ id: z.string().describe("Task ID"),
352
+ },
353
+ annotations: { openWorldHint: true },
354
+ }, async ({ id }) => {
355
+ try {
356
+ const greptileApi = await import("./greptile.js");
357
+ const task = await api.getTask(id);
358
+ const cfg = config.load();
359
+
360
+ if (!task.pullRequest?.number) {
361
+ return { content: [{ type: "text" as const, text: JSON.stringify({ error: { code: "no_pr", message: `Task ${task.identifier} has no PR` } }) }], isError: true as const };
362
+ }
363
+
364
+ const repo = task.pullRequest.repoFullName || cfg.repos[0]?.repoFullName || "";
365
+ const prNum = task.pullRequest.number;
366
+ const defaultBranch = cfg.repos.find((r: { repoFullName: string; branch: string }) => r.repoFullName === repo)?.branch || "main";
367
+
368
+ const result = await greptileApi.freshReview(repo, prNum, defaultBranch);
369
+ const unaddressed = await greptileApi.getUnaddressedIssues(repo, prNum, defaultBranch);
370
+
371
+ return text({ task: task.identifier, pr: prNum, reviewStatus: result?.status || "triggered", unaddressed });
372
+ } catch (e) { return err(e); }
373
+ });
374
+
318
375
  server.registerTool("capy_models", {
319
376
  description: "List available AI models",
320
377
  inputSchema: {},
package/src/types.ts CHANGED
@@ -31,9 +31,16 @@ export interface Credits {
31
31
  }
32
32
 
33
33
  export interface Jam {
34
+ id?: string;
34
35
  model?: string;
35
36
  status?: string;
36
37
  credits?: Credits | number;
38
+ pullRequest?: PullRequestRef;
39
+ branches?: Record<string, unknown>;
40
+ git?: unknown;
41
+ slackThreads?: unknown[];
42
+ createdAt?: string;
43
+ updatedAt?: string;
37
44
  }
38
45
 
39
46
  export interface PullRequestRef {
@@ -48,21 +55,29 @@ export interface PullRequestRef {
48
55
 
49
56
  export interface Task {
50
57
  id: string;
58
+ projectId?: string;
51
59
  identifier: string;
52
60
  title: string;
53
61
  status: string;
54
62
  prompt?: string;
55
- createdAt?: string;
63
+ labels?: string[];
56
64
  pullRequest?: PullRequestRef;
65
+ slackThreads?: unknown[];
66
+ createdAt?: string;
67
+ updatedAt?: string;
57
68
  jams?: Jam[];
58
69
  }
59
70
 
60
71
  export interface Thread {
61
72
  id: string;
73
+ projectId?: string;
62
74
  title?: string;
63
75
  status: string;
64
76
  tasks?: Task[];
65
77
  pullRequests?: PullRequestRef[];
78
+ slackThreads?: unknown[];
79
+ createdAt?: string;
80
+ updatedAt?: string;
66
81
  }
67
82
 
68
83
  export interface ThreadMessage {
@@ -178,5 +193,7 @@ export interface ApiResponse {
178
193
 
179
194
  export interface ListResponse<T> {
180
195
  items?: T[];
196
+ nextCursor?: string;
197
+ hasMore?: boolean;
181
198
  [key: string]: unknown;
182
199
  }