@quire-io/quire-cli 0.1.6 → 1.0.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.
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Builds the whole `quire` command tree.
3
+ *
4
+ * Kept separate from cli.ts — which is the executable entry point and runs
5
+ * on import — so that tooling can construct the tree without invoking the
6
+ * CLI. scripts/gen-coverage.ts walks the object this returns, which is what
7
+ * keeps COVERAGE.md honest: the doc is generated from the same tree the CLI
8
+ * actually registers, not a second hand-written list of it.
9
+ */
10
+ import { Command } from "commander";
11
+ import { registerChatCommand } from "./commands/chat.js";
12
+ import { registerColorsCommand } from "./commands/colors.js";
13
+ import { registerCommentCommand } from "./commands/comment.js";
14
+ import { registerDashboardCommand } from "./commands/dashboard.js";
15
+ import { registerDocCommand } from "./commands/doc.js";
16
+ import { registerInsightCommand } from "./commands/insight.js";
17
+ import { registerLoginCommand } from "./commands/login.js";
18
+ import { registerLogoutCommand } from "./commands/logout.js";
19
+ import { registerMineCommand } from "./commands/mine.js";
20
+ import { registerNotifyCommand } from "./commands/notify.js";
21
+ import { registerOrgCommand } from "./commands/org.js";
22
+ import { registerPartnerCommand } from "./commands/partner.js";
23
+ import { registerProjectCommand } from "./commands/project.js";
24
+ import { registerReminderCommand } from "./commands/reminder.js";
25
+ import { registerResolveCommand } from "./commands/resolve.js";
26
+ import { registerStatusCommand } from "./commands/status.js";
27
+ import { registerSublistCommand } from "./commands/sublist.js";
28
+ import { registerTagCommand } from "./commands/tag.js";
29
+ import { registerTaskCommand } from "./commands/task.js";
30
+ import { registerUndoCommand } from "./commands/undo.js";
31
+ import { registerUserCommand } from "./commands/user.js";
32
+ import { registerWhoamiCommand } from "./commands/whoami.js";
33
+ import { readVersion } from "./version.js";
34
+ export function buildProgram() {
35
+ const program = new Command();
36
+ program
37
+ .name("quire")
38
+ .description("Command-line interface for the Quire API.")
39
+ .version(readVersion(), "-v, --version", "Print the CLI version")
40
+ .option("--verbose", "Enable verbose (debug) logging on stderr")
41
+ .option("--json", "Emit raw API JSON (no human formatting)")
42
+ .option("-q, --quiet", "Print only IDs (one per line) — designed for xargs pipelines")
43
+ .option("--color-mode <mode>", "Color output mode: always|never|auto", "auto")
44
+ .option("--profile <name>", "Use a named credential profile (default: $QUIRE_PROFILE or 'default')")
45
+ .option("--yes", "Auto-confirm destructive prompts (required for non-interactive mutating commands)")
46
+ .option("--no-truncate", "Disable per-cell truncation in human-readable tables");
47
+ registerLoginCommand(program);
48
+ registerLogoutCommand(program);
49
+ registerWhoamiCommand(program);
50
+ registerUserCommand(program);
51
+ registerOrgCommand(program);
52
+ registerProjectCommand(program);
53
+ registerPartnerCommand(program);
54
+ registerTaskCommand(program);
55
+ registerMineCommand(program);
56
+ registerTagCommand(program);
57
+ registerSublistCommand(program);
58
+ registerStatusCommand(program);
59
+ registerCommentCommand(program);
60
+ registerChatCommand(program);
61
+ registerDocCommand(program);
62
+ registerInsightCommand(program);
63
+ registerDashboardCommand(program);
64
+ registerReminderCommand(program);
65
+ registerResolveCommand(program);
66
+ registerColorsCommand(program);
67
+ registerNotifyCommand(program);
68
+ registerUndoCommand(program);
69
+ program.addHelpText("after", `
70
+ Auth:
71
+ quire login Sign in via OAuth (loopback + PKCE)
72
+ quire logout Remove local credentials (server-side token stays valid; revoke at quire.io/apps)
73
+ quire whoami Show the signed-in user
74
+ quire user get <oid> Show one user by OID
75
+
76
+ Orgs / projects:
77
+ quire org list List your organizations
78
+ quire org get <id> Show one organization
79
+ quire org update <id> Update name / description / followers (--follower / --add-follower / --remove-follower)
80
+ quire org limit <id> Show API rate-limit usage for an organization
81
+ quire project list List projects you can see (or --org <id> to scope)
82
+ quire project get <id> Show one project
83
+ quire project update <id> Update name / description / dates / archive / public / followers (--follower / --add-follower / --remove-follower)
84
+ quire project members <id> List a project's members
85
+ quire project export <id> Export the project as CSV (default) or JSON (--format json [--output file])
86
+
87
+ Tasks (read):
88
+ quire task list <project> List tasks in a project
89
+ quire task get <id> Show task details (id = OID, slug/#N, or URL)
90
+ quire task tree <id> Render the recursive subtree (default depth 3)
91
+ quire task search <query> Search tasks; scope with --project / --org / --folder; filter with --assignee / --follower / --tag
92
+ quire task subtasks <id> List a task's direct subtasks
93
+ quire task comments <id> List a task's comments
94
+ quire mine List tasks assigned to me; scope with --project / --inbox / --org / --all-orgs
95
+
96
+ Tasks (write):
97
+ quire task create <project> --name "..." Create a new task (--parent / --sibling+--position to nest; --assignee / --follower)
98
+ quire task subtask <parent> --name "..." Shorthand for "task create --parent"
99
+ quire task update <id> Update fields: --name / --status / --priority / --add-tag / etc.
100
+ Followers: --follower (full replace) / --add-follower / --remove-follower
101
+ Follower values: OID, ID, email, 'me', 'app', or 'inherit' (the parent task's followers)
102
+ quire task complete <id> / uncomplete <id> Toggle status to 100 / 0
103
+ quire task move <id> --to <id|root> Re-parent within the same project
104
+ quire task transfer <id> --to <project> Cross-project transfer (--keep-tags / --keep-status / --invite)
105
+ quire task dates <id> --start ... --due ... Set / clear dates (pass 'null' to clear)
106
+ quire task peekaboo <id> [--reshow-at ISO | --show] Hide or un-hide a task
107
+ quire task delete <id> Delete a task (prompts unless --yes)
108
+ quire task undo-remove <oid> Restore a deleted task
109
+ quire task attach <id> <file> Attach a file to a task ('-' = stdin; --filename / --content-type optional)
110
+
111
+ Tasks (bulk):
112
+ quire task bulk-create <project> --from-file tasks.json Create up to 300 tasks atomically
113
+ quire task bulk-subtasks <parent> --from-file tasks.json Create up to 300 subtasks under one parent
114
+ quire task bulk-update <project> --from-file updates.json Update many tasks atomically (each item needs an oid)
115
+ quire task bulk-delete <project> --from-file ids.txt Delete many (prompts unless --yes); refs one per line or JSON array
116
+ quire task bulk-move <project> --to <id|root> --from-file ids.txt
117
+ quire task bulk-transfer <project> --to <project> --from-file ids.txt
118
+ quire task bulk-approve <project> --state request|approve|reject|change --from-file ids.txt
119
+
120
+ Tasks (approval / timelogs / recurrence):
121
+ quire task approve <id> --state ... Set a task's approval state
122
+ Add --comment <text|-|@file> [--comment-pinned / --comment-as-user] to post a companion comment
123
+ quire task revoke-approval <id> Revoke any approval state
124
+ quire task timelog add <id> --start --end [--user / --billable / --note]
125
+ quire task timelog update <id> --start --end [--new-start / --new-end / --note / ...]
126
+ quire task timelog remove <id> --start --end (prompts unless --yes)
127
+ --recurrence-freq / --recurrence-interval / --recurrence-byweekday / --recurrence-until
128
+ Add / clear recurrence on "task create" / "task subtask" / "task update"
129
+
130
+ Project metadata (approval categories):
131
+ quire project approval-category add <project> --id ... --name ... [--claimer / --approver]
132
+ quire project approval-category update <project> <id> [--name / --claimer / --approver / --claimers-anyone / --claimers-admins-only / ...]
133
+ quire project approval-category remove <project> <id> (prompts unless --yes)
134
+
135
+ Project metadata (custom-field definitions):
136
+ quire project field add <project> --name --type [--hidden / --private / --percent / --multiple / --clear-on-dup / --extra k=v]
137
+ quire project field update <project> <name> [--type / --hidden / --private / --percent / --multiple / --extra k=v]
138
+ quire project field rename <project> <name> --new-name ...
139
+ quire project field move <project> <name> [--before <name>] [--to-end]
140
+ quire project field remove <project> <name> (prompts unless --yes)
141
+ quire insight field add <insight-oid> --name --type [...] (formula / lookup only)
142
+ quire insight field {update / rename / move / remove} (same flag shape as project field)
143
+
144
+ Project metadata (read):
145
+ quire tag list <project> List tags defined on a project (also: 'tag get <oid>')
146
+ quire sublist list <project> List sublists on a project (also: 'sublist get <oid>')
147
+ quire status list <project> List custom statuses on a project (also: 'status get <project> <value>')
148
+ quire partner list <project> List partner orgs (external teams) on a project
149
+ quire partner get <oid> Show one partner organization
150
+
151
+ Project metadata (write):
152
+ quire tag create <project> Create a tag (--name / --color)
153
+ quire tag update <oid> Update tag --name / --color
154
+ quire tag delete <oid> Delete a tag (prompts unless --yes)
155
+ quire sublist create <project> Create a sublist (--name / --description / --member / --members-admins-only)
156
+ quire sublist update <oid> Update sublist (--name / --description / --start / --due / --archive / --unarchive)
157
+ quire sublist add-task <oid> <task> Add a task to a sublist
158
+ quire sublist remove-task <oid> <task> Remove a task from a sublist
159
+ quire sublist delete <oid> Delete a sublist (prompts unless --yes)
160
+ quire sublist undo-remove <oid> Restore a deleted sublist
161
+ quire status create <project> Create a status (--name / --value / --color)
162
+ quire status update <project> <value> Update a status (--name / --color / --new-value)
163
+ quire status delete <project> <value> Delete a status (prompts unless --yes)
164
+
165
+ Comments / chats / docs / insights / dashboards (read):
166
+ quire comment list <task> List comments on a task (alias for "quire task comments"; also: 'comment get <oid>')
167
+ quire chat list <project> List chats / chat get <id> / chat comments <id>
168
+ quire doc list <project> List documents / doc get <id>
169
+ quire insight list <project> List insights / insight get <id>
170
+ quire dashboard list <owner> List dashboards / dashboard get <id> ([--owner-type project|organization|folder|smart-folder])
171
+
172
+ Comments (write):
173
+ quire comment add <task> --text "..." Add a comment ('-' = stdin, '@file' = read file)
174
+ quire comment update <oid> Update --text and/or --pin / --unpin
175
+ quire comment attach <oid> <file> Attach a file to a comment ('-' = stdin; --filename / --content-type optional)
176
+ quire comment delete <oid> Delete a comment (prompts unless --yes)
177
+
178
+ Chats / docs / insights / dashboards (write):
179
+ quire chat create <project> --name [--description / --partner / --follower / --member / --members-admins-only]
180
+ quire chat update <oid> [--name / --description / --archive / --unarchive / --follower / --add-follower / --remove-follower]
181
+ quire chat delete <oid> (prompts unless --yes)
182
+ quire chat undo-remove <oid>
183
+ quire chat comment add <chat-id> --text [--pin]
184
+ quire doc create <project> --name [--description / --follower / --member / --members-admins-only]
185
+ quire doc update <oid> [--name / --description / --archive / --unarchive / --follower / --add-follower / --remove-follower]
186
+ quire doc delete <oid> (prompts unless --yes)
187
+ quire doc undo-remove <oid>
188
+ quire insight create <project> --name [--id / --description / --icon-color / --image / --member / --members-admins-only]
189
+ quire insight update <oid> [--name / --description / --icon-color / --image / --archive / --unarchive]
190
+ quire insight delete <oid> (prompts unless --yes)
191
+ quire insight undo-remove <oid>
192
+ quire insight run <oid> Run an insight and print the aggregated rows ([--group-by member|section] [--status active|completed|all])
193
+ quire dashboard create <owner> --name [--owner-type / --id / --description / --icon-color / --image / --partner / --start / --due / --member]
194
+ quire dashboard update <oid> [--id / --name / --description / --icon-color / --image / --start / --due / --archive / --unarchive] ('null' clears a date)
195
+ quire dashboard delete <oid> (prompts unless --yes)
196
+ quire dashboard undo-remove <oid>
197
+
198
+ Reminders:
199
+ quire reminder list <owner> List reminders ([--owner-type project|organization|folder|smart-folder|task]; '-' = your Inbox)
200
+ A project's list includes its tasks' reminders; ordered by OID, not fire time
201
+ quire reminder get <oid> Show one reminder
202
+ quire reminder create <owner> [--owner-type / --when / --lead / --name / --partner / --member / --recurrence-*]
203
+ --when is required unless the task already has a start or due date (that supplies the fire time)
204
+ --lead '<n>m' | '<n>d' | '<n>d@HH:mm', repeatable (max 30); omit for one notification at the fire time
205
+ quire reminder update <oid> [--when / --lead / --name / --clear-recurrence / --recurrence-*] ('null' clears --when / --name)
206
+ Members and partner are create-only — recreate the reminder to change who can see it
207
+ quire reminder delete <oid> Delete permanently — no trash, no undo (prompts unless --yes)
208
+
209
+ Record visibility (create-only):
210
+ --member <user> Restrict a sublist / doc / chat / insight / dashboard / reminder to these users; repeat for multiple
211
+ --members-admins-only Restrict it to the owner's admins
212
+ Omit both for every member of the owner. Cannot be changed after creation.
213
+
214
+ Generic undo:
215
+ quire undo <kind> <oid> kind = task | chat | comment | dashboard | document | insight | sublist
216
+
217
+ URL resolver:
218
+ quire resolve <url> Paste any Quire URL, get the typed resource back
219
+
220
+ Notifications:
221
+ quire notify --message ... Send an in-app notification to yourself ('-' / '@file' for stdin / file)
222
+
223
+ Reference:
224
+ quire colors List Quire's 48-slot palette (code, hex, name)
225
+ `);
226
+ return program;
227
+ }
228
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { ValidationError } from "../errors.js";
2
+ const append = (val, prev) => [...(prev ?? []), val];
3
+ /** Adds the two shared member options to a `create` command. */
4
+ export function addMemberOptions(cmd) {
5
+ return cmd
6
+ .option("--member <user>", "Restrict visibility to this user (OID, ID, email, or 'me'); repeat for multiple. Omit for every member of the owner. Cannot be changed after creation.", append, [])
7
+ .option("--members-admins-only", "Restrict visibility to the owner's admins (empty member list). Cannot be changed after creation.");
8
+ }
9
+ /**
10
+ * Resolve the flags to the wire value, or `undefined` when the field should be
11
+ * left off the request entirely. Unlike `project approval-category add` — where
12
+ * `--claimers-admins-only` silently overrides `--claimer` — combining the two
13
+ * is an error here, because the result is immutable once created.
14
+ */
15
+ export function resolveMembers(flags) {
16
+ const hasList = (flags.member?.length ?? 0) > 0;
17
+ if (flags.membersAdminsOnly === true) {
18
+ if (hasList) {
19
+ throw new ValidationError("Cannot combine --member with --members-admins-only.");
20
+ }
21
+ return [];
22
+ }
23
+ return hasList ? flags.member : undefined;
24
+ }
25
+ /**
26
+ * Render a record's `members` for `… get` output. The server returns all three
27
+ * states explicitly, so `null` (everyone) and `[]` (admins only) are told
28
+ * apart the same way `project approval-category` renders claimers / approvers.
29
+ *
30
+ * As of the Sep 21 2026 server release these are user objects; older servers
31
+ * sent bare OID strings, so both shapes are accepted.
32
+ */
33
+ export function formatMembers(members) {
34
+ if (members === null || members === undefined)
35
+ return "(everyone)";
36
+ if (members.length === 0)
37
+ return "(admins only)";
38
+ return members.map((m) => (typeof m === "string" ? m : (m.name ?? m.oid))).join(", ");
39
+ }
40
+ //# sourceMappingURL=member-flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"member-flags.js","sourceRoot":"","sources":["../../src/util/member-flags.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,IAA0B,EAAY,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;AAO7F,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,GAAG;SACP,MAAM,CACL,iBAAiB,EACjB,wJAAwJ,EACxJ,MAAM,EACN,EAAc,CACf;SACA,MAAM,CAAC,uBAAuB,EAAE,kGAAkG,CAAC,CAAC;AACzI,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;QACrC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,eAAe,CAAC,qDAAqD,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,OAAuE;IACnG,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,YAAY,CAAC;IACnE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC;IACjD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxF,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { ValidationError } from "../errors.js";
2
+ /** Quire caps a lead at 10,000 days out, and a reminder at 30 leads. */
3
+ const MAX_DAYS = 10_000;
4
+ const MAX_MINUTES = MAX_DAYS * 24 * 60;
5
+ const MAX_LEADS = 30;
6
+ const LEAD_RE = /^(\d+)(m|d)?(?:@(\d{1,2}):(\d{2}))?$/;
7
+ function parseLead(spec) {
8
+ const m = LEAD_RE.exec(spec.trim());
9
+ if (!m) {
10
+ throw new ValidationError(`Invalid --lead "${spec}". Expected <n>m (minutes), <n>d (days), or <n>d@HH:mm — e.g. '30m', '2d', '1d@09:00'.`);
11
+ }
12
+ const value = Number.parseInt(m[1], 10);
13
+ const unit = m[2] ?? "m";
14
+ const hh = m[3];
15
+ const mm = m[4];
16
+ if (hh !== undefined && unit !== "d") {
17
+ throw new ValidationError(`Invalid --lead "${spec}". The @HH:mm suffix is only valid on a day lead — e.g. '1d@09:00'.`);
18
+ }
19
+ if (unit === "d") {
20
+ if (value > MAX_DAYS) {
21
+ throw new ValidationError(`--lead "${spec}" exceeds Quire's maximum of ${MAX_DAYS} days.`);
22
+ }
23
+ const lead = { days: value };
24
+ if (hh !== undefined && mm !== undefined) {
25
+ const hours = Number.parseInt(hh, 10);
26
+ const mins = Number.parseInt(mm, 10);
27
+ if (hours > 23 || mins > 59) {
28
+ throw new ValidationError(`Invalid --lead "${spec}". The time must be 24-hour HH:mm, 00:00-23:59.`);
29
+ }
30
+ lead.at = `${String(hours).padStart(2, "0")}:${mm}`;
31
+ }
32
+ return lead;
33
+ }
34
+ if (value > MAX_MINUTES) {
35
+ throw new ValidationError(`--lead "${spec}" exceeds Quire's maximum of ${MAX_DAYS} days.`);
36
+ }
37
+ return { minutes: value };
38
+ }
39
+ /**
40
+ * Parse every `--lead` value. Returns `undefined` when the flag was not
41
+ * passed, so the caller can omit the field and let Quire apply its default of
42
+ * one notification at the fire time.
43
+ */
44
+ export function parseLeads(specs) {
45
+ if (specs === undefined || specs.length === 0)
46
+ return undefined;
47
+ if (specs.length > MAX_LEADS) {
48
+ throw new ValidationError(`A reminder carries at most ${MAX_LEADS} leads; got ${specs.length}.`);
49
+ }
50
+ return specs.map(parseLead);
51
+ }
52
+ /** Render a reminder's leads back in the `--lead` grammar, for `… get` output. */
53
+ export function formatLeads(leads) {
54
+ if (leads === undefined || leads.length === 0)
55
+ return undefined;
56
+ return leads
57
+ .map((l) => (l.days !== undefined ? `${l.days}d${l.at !== undefined ? `@${l.at}` : ""}` : `${l.minutes ?? 0}m`))
58
+ .join(", ");
59
+ }
60
+ //# sourceMappingURL=reminder-lead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reminder-lead.js","sourceRoot":"","sources":["../../src/util/reminder-lead.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quire-io/quire-cli",
3
- "version": "0.1.6",
3
+ "version": "1.0.0",
4
4
  "description": "Command-line interface for the Quire API.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -24,6 +24,8 @@
24
24
  "build": "tsc",
25
25
  "start": "node dist/cli.js",
26
26
  "typecheck": "tsc --noEmit",
27
+ "gen-coverage": "tsx scripts/gen-coverage.ts",
28
+ "check-coverage": "tsx scripts/gen-coverage.ts && git diff --exit-code COVERAGE.md",
27
29
  "test": "vitest run",
28
30
  "test:watch": "vitest",
29
31
  "test:live": "vitest run --config vitest.live.config.ts",
@@ -48,7 +50,7 @@
48
50
  "url": "https://github.com/quire-io/quire-cli/issues"
49
51
  },
50
52
  "dependencies": {
51
- "@quire-io/api-client": "^0.1.11",
53
+ "@quire-io/api-client": "^1.0.0",
52
54
  "commander": "^12.1.0"
53
55
  },
54
56
  "devDependencies": {