@autumnai/cli 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/bin/autumn.mjs +97 -6
  2. package/package.json +1 -1
  3. package/skill.md +12 -0
package/bin/autumn.mjs CHANGED
@@ -7,7 +7,7 @@ import os from "node:os";
7
7
  import path from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
 
10
- const VERSION = "0.1.0";
10
+ const VERSION = "0.1.2";
11
11
  const DEFAULT_API_URL = "https://api.autumn.ai";
12
12
  const USER_AGENT = `autumn-cli/${VERSION}`;
13
13
  const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
@@ -214,8 +214,8 @@ async function request(config, route, options) {
214
214
  function parseArgs(tokens) {
215
215
  const flags = {};
216
216
  const positionals = [];
217
- const booleanFlags = new Set(["detach", "force", "json", "no-browser", "wait", "help", "h"]);
218
- const valueFlags = new Set(["out", "timeout", "limit", "format"]);
217
+ const booleanFlags = new Set(["detach", "force", "json", "no-browser", "wait", "help", "h", "yes"]);
218
+ const valueFlags = new Set(["out", "timeout", "limit", "format", "days", "name", "website", "share-by-default", "owners-can-demote"]);
219
219
  for (let index = 0; index < tokens.length; index += 1) {
220
220
  const token = tokens[index];
221
221
  if (token === "--") {
@@ -264,7 +264,9 @@ Commands:
264
264
  status <id> [--json] Show current task status once
265
265
  rows <id> [--out FILE] [--format csv|json]
266
266
  stop <id> Stop a running task
267
- list [--limit N] (default 20) List recent tasks
267
+ list [query] [--limit N] [--json] List or search recent tasks (default limit: 20)
268
+ team [invite|revoke|role|remove|set] Manage your team (writes are owner-only)
269
+ usage [--days N] [--json] Read usage for the last N days (1–90, default: 30)
268
270
  setup [claude|codex|all] Install the Autumn Skill
269
271
  mcp-config [claude|codex|cursor] Print MCP connection configuration
270
272
 
@@ -289,7 +291,9 @@ function commandHelp(command) {
289
291
  status: "autumn status <id> [--json]",
290
292
  rows: "autumn rows <id> [--out FILE] [--format csv|json]",
291
293
  stop: "autumn stop <id>",
292
- list: "autumn list [--limit N] [--json] (default limit: 20)",
294
+ list: "autumn list [query] [--limit N] [--json] (default limit: 20)",
295
+ team: "autumn team [invite <email>|revoke <invitation_id>|role <user_id> owner|member|remove <user_id> --yes|set [--name N] [--website W] [--share-by-default true|false] [--owners-can-demote true|false]]",
296
+ usage: "autumn usage [--days N] [--json] (1–90, default: 30)",
293
297
  setup: "autumn setup [claude|codex|all]",
294
298
  "mcp-config": "autumn mcp-config [claude|codex|cursor]",
295
299
  };
@@ -300,6 +304,88 @@ function requirePositionals(positionals, count, usage) {
300
304
  if (positionals.length < count) throw new CliError(`Usage: ${usage}`);
301
305
  }
302
306
 
307
+ function teamBoolean(value, option) {
308
+ if (value !== "true" && value !== "false") {
309
+ throw new CliError(`--${option} must be true or false.`);
310
+ }
311
+ return value === "true";
312
+ }
313
+
314
+ async function teamCommand(config, positionals, options) {
315
+ const action = positionals[0] || "";
316
+ if (!action) {
317
+ const result = await request(config, "/team");
318
+ if (jsonMode) emit(result);
319
+ else {
320
+ const account = result.account || {};
321
+ writeOut(`account\t${account.type || ""}\t${account.name || ""}\t${account.role || ""}`);
322
+ writeOut("user_id\tname\temail\trole");
323
+ for (const member of result.members || []) {
324
+ writeOut([member.user_id, member.name, member.email, member.role].map((value) => String(value || "").replace(/[\t\r\n]/g, " ")).join("\t"));
325
+ }
326
+ if (account.type === "team" && account.role === "owner") {
327
+ writeOut("\nPending invitations");
328
+ writeOut("id\temail\tcreated_at");
329
+ for (const invitation of result.invitations || []) {
330
+ writeOut([invitation.id, invitation.email, invitation.created_at].map((value) => String(value || "").replace(/[\t\r\n]/g, " ")).join("\t"));
331
+ }
332
+ }
333
+ }
334
+ return 0;
335
+ }
336
+
337
+ let result;
338
+ if (action === "invite") {
339
+ requirePositionals(positionals, 2, commandHelp("team"));
340
+ result = await request(config, "/team/invitations", { method: "POST", body: { email: positionals[1] } });
341
+ } else if (action === "revoke") {
342
+ requirePositionals(positionals, 2, commandHelp("team"));
343
+ result = await request(config, `/team/invitations/${encodeURIComponent(positionals[1])}`, { method: "DELETE" });
344
+ } else if (action === "role") {
345
+ requirePositionals(positionals, 3, commandHelp("team"));
346
+ if (!["owner", "member"].includes(positionals[2])) throw new CliError("Role must be owner or member.");
347
+ result = await request(config, `/team/members/${encodeURIComponent(positionals[1])}`, { method: "PATCH", body: { role: positionals[2] } });
348
+ } else if (action === "remove") {
349
+ requirePositionals(positionals, 2, commandHelp("team"));
350
+ if (!options.yes) throw new CliError(`Rerun with --yes to remove ${positionals[1]}`);
351
+ result = await request(config, `/team/members/${encodeURIComponent(positionals[1])}`, { method: "DELETE" });
352
+ } else if (action === "set") {
353
+ if (positionals.length !== 1) throw new CliError(`Usage: ${commandHelp("team")}`);
354
+ const body = {};
355
+ if (options.name !== undefined || options.website !== undefined) {
356
+ if (options.name !== undefined) body.name = options.name;
357
+ if (options.website !== undefined) body.website = options.website;
358
+ }
359
+ const settings = {};
360
+ if (options["share-by-default"] !== undefined) settings.share_chats_by_default = teamBoolean(options["share-by-default"], "share-by-default");
361
+ if (options["owners-can-demote"] !== undefined) settings.owners_can_demote_owners = teamBoolean(options["owners-can-demote"], "owners-can-demote");
362
+ if (Object.keys(settings).length) {
363
+ const current = await request(config, "/team");
364
+ body.settings = settings;
365
+ body.version = current.settings?.version;
366
+ }
367
+ if (!Object.keys(body).length) throw new CliError(`Usage: ${commandHelp("team")}`);
368
+ result = await request(config, "/team", { method: "PATCH", body });
369
+ } else {
370
+ throw new CliError(`Unknown team command: ${action}\n${commandHelp("team")}`);
371
+ }
372
+ if (jsonMode) emit(result);
373
+ else writeOut(action === "invite" ? `Invitation sent to ${positionals[1]}.` : action === "revoke" ? "Invitation revoked." : action === "remove" ? `Removed ${positionals[1]} from the team.` : action === "role" ? `Set ${positionals[1]} to ${positionals[2]}.` : "Team updated.");
374
+ return 0;
375
+ }
376
+
377
+ async function usageCommand(config, options) {
378
+ const days = Number(options.days || 30);
379
+ if (!Number.isInteger(days) || days < 1 || days > 90) throw new CliError("--days must be an integer from 1 to 90.");
380
+ const result = await request(config, `/team/usage?days=${days}`);
381
+ if (jsonMode) emit(result);
382
+ else {
383
+ writeOut(`${result.credits_used || 0} credits used across ${result.tasks || 0} tasks in the last ${result.days || days} days.`);
384
+ for (const member of result.members || []) writeOut(`${member.name || member.user_id}: ${member.credits_used || 0} credits, ${member.tasks || 0} tasks`);
385
+ }
386
+ return 0;
387
+ }
388
+
303
389
  function taskRoute(taskId) {
304
390
  return `/task/${encodeURIComponent(taskId)}`;
305
391
  }
@@ -863,7 +949,10 @@ async function commandMain(command, options, positionals, config) {
863
949
  if (!Number.isInteger(limit) || limit < 1 || limit > 200) {
864
950
  throw new CliError("--limit must be an integer from 1 to 200.");
865
951
  }
866
- const result = await request(config, `/task?limit=${limit}`);
952
+ const query = positionals.join(" ").trim();
953
+ const params = new URLSearchParams({ limit: String(limit) });
954
+ if (query) params.set("q", query);
955
+ const result = await request(config, `/task?${params}`);
867
956
  if (jsonMode) emit(result);
868
957
  else {
869
958
  const tasks = Array.isArray(result) ? result : result.tasks || [];
@@ -872,6 +961,8 @@ async function commandMain(command, options, positionals, config) {
872
961
  }
873
962
  return 0;
874
963
  }
964
+ if (command === "team") return teamCommand(config, positionals, options);
965
+ if (command === "usage") return usageCommand(config, options);
875
966
  throw new CliError(`Unknown command: ${command}\n\n${globalHelp()}`);
876
967
  }
877
968
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autumnai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Autumn research agent from your terminal or coding agent",
5
5
  "homepage": "https://www.autumn.ai/docs",
6
6
  "type": "module",
package/skill.md CHANGED
@@ -10,6 +10,8 @@ verifies and saves rows with sources, and you read the rows. A task takes minute
10
10
  seconds, and a task is a conversation: you can answer its questions and refine it on the
11
11
  same `task_id`.
12
12
 
13
+ No task_id? Find it with `list_tasks` (MCP), `autumn list "<words>"` (CLI) or `GET /task?q=<words>`.
14
+
13
15
  Don't use Autumn for a quick fact you can answer yourself.
14
16
 
15
17
  ## Fastest path: the CLI
@@ -46,6 +48,16 @@ working or approval pending, and `4` authentication required.
46
48
  - Check it works: `curl -sS https://api.autumn.ai/credits -H "X-API-Key: $AUTUMN_API_KEY"`.
47
49
  - To buy credits, use MCP `buy_credits` or `autumn buy 10000` to get a payment link for the user.
48
50
 
51
+ ## Team and usage
52
+
53
+ - MCP: `get_team`, `get_usage`, `invite_member`, `revoke_invitation`,
54
+ `set_member_role`, `update_team`, `remove_member`.
55
+ - CLI: `autumn team`, `autumn team invite|revoke|role|remove|set`, `autumn usage`.
56
+ - REST: `GET /team`, `PATCH /team`, `POST /team/invitations`,
57
+ `DELETE /team/invitations/{invitation_id}`, `PATCH` / `DELETE /team/members/{user_id}`,
58
+ and `GET /team/usage?days=30`.
59
+ - Team writes are owner-only. Creating or leaving a team is done in the app.
60
+
49
61
  ## The loop
50
62
 
51
63
  ```bash