@verbatims/cli 1.0.3 → 1.0.4

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 CHANGED
@@ -42,6 +42,15 @@ verbatims quotes browse
42
42
  # Search
43
43
  verbatims search "love" --type quotes
44
44
 
45
+ # Manage collections
46
+ verbatims collections list --limit 20
47
+ verbatims collections get 12 --page 1 --limit 20
48
+ verbatims collections create --name "Favorite quotes" --public
49
+ verbatims collections update 12 --name "Reading notes" --private
50
+ verbatims collections add 12 123
51
+ verbatims collections remove 12 123
52
+ verbatims collections delete 12 --yes
53
+
45
54
  # Apply a reproducible theme manifest (safe to preview first)
46
55
  verbatims themes apply ./theme.json --dry-run
47
56
  verbatims themes apply ./theme.json
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@verbatims/cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "CLI for the Verbatims quotes API",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": "^22 || ^24"
8
+ },
6
9
  "bin": {
7
10
  "verbatims": "./bin/verbatims.js"
8
11
  },
@@ -16,7 +19,7 @@
16
19
  "typecheck": "tsc --noEmit"
17
20
  },
18
21
  "dependencies": {
19
- "@verbatims/sdk": "^2.0.2",
22
+ "@verbatims/sdk": "^2.0.3",
20
23
  "@clack/prompts": "^0.10.1",
21
24
  "chalk": "^5.4.1",
22
25
  "commander": "^13.1.0",
@@ -1,5 +1,5 @@
1
1
  import type { Command } from 'commander'
2
- import { text, isCancel, cancel } from '@clack/prompts'
2
+ import { text, isCancel, cancel, confirm } from '@clack/prompts'
3
3
  import chalk from 'chalk'
4
4
  import { getClient } from '../utils/client.js'
5
5
  import { output, type Format } from '../utils/format.js'
@@ -12,6 +12,39 @@ function getFormat(program: Command): Format {
12
12
  export function registerCollectionsCommand(program: Command) {
13
13
  const collections = program.command('collections').description('Manage collections')
14
14
 
15
+ collections
16
+ .command('list')
17
+ .description('List collections')
18
+ .option('--page <n>', 'Page number', '1')
19
+ .option('--limit <n>', 'Number of results', '20')
20
+ .action(async (options: Record<string, string>) => {
21
+ const client = await getClient()
22
+ const format = getFormat(program)
23
+ const { data, pagination } = await withSpinner('Fetching collections', () =>
24
+ client.collections.list({ page: Number(options.page), limit: Number(options.limit) }),
25
+ format,
26
+ )
27
+ output(data, format)
28
+ if (pagination && format !== 'json') {
29
+ console.log(chalk.dim(`\nPage ${pagination.page}/${pagination.totalPages} — ${pagination.total} total`))
30
+ }
31
+ })
32
+
33
+ collections
34
+ .command('get <id>')
35
+ .description('Get a collection and its quotes')
36
+ .option('--page <n>', 'Quote page number', '1')
37
+ .option('--limit <n>', 'Quotes per page', '20')
38
+ .action(async (id: string, options: Record<string, string>) => {
39
+ const client = await getClient()
40
+ const format = getFormat(program)
41
+ const { data } = await withSpinner('Fetching collection', () =>
42
+ client.collections.get(Number(id), { page: Number(options.page), limit: Number(options.limit) }),
43
+ format,
44
+ )
45
+ output(data, format)
46
+ })
47
+
15
48
  collections
16
49
  .command('create')
17
50
  .description('Create a collection')
@@ -33,6 +66,51 @@ export function registerCollectionsCommand(program: Command) {
33
66
  output(data, format)
34
67
  })
35
68
 
69
+ collections
70
+ .command('update <id>')
71
+ .description('Update a collection')
72
+ .option('--name <name>', 'Collection name')
73
+ .option('--description <text>', 'Description (use an empty value to clear)')
74
+ .option('--public', 'Make collection public')
75
+ .option('--private', 'Make collection private')
76
+ .action(async (id: string, options: Record<string, string | boolean>) => {
77
+ if (options.public && options.private) {
78
+ throw new Error('Choose either --public or --private')
79
+ }
80
+ const data: { name?: string; description?: string | null; is_public?: boolean } = {}
81
+ if (options.name) data.name = options.name as string
82
+ if (options.description !== undefined) data.description = options.description ? options.description as string : null
83
+ if (options.public) data.is_public = true
84
+ if (options.private) data.is_public = false
85
+ if (Object.keys(data).length === 0) {
86
+ throw new Error('Provide at least one field to update')
87
+ }
88
+
89
+ const client = await getClient()
90
+ const format = getFormat(program)
91
+ const { data: updated } = await withSpinner('Updating collection', () =>
92
+ client.collections.update(Number(id), data),
93
+ format,
94
+ )
95
+ console.log(chalk.green(' Collection updated'))
96
+ output(updated, format)
97
+ })
98
+
99
+ collections
100
+ .command('delete <id>')
101
+ .description('Delete a collection')
102
+ .option('--yes', 'Skip confirmation')
103
+ .action(async (id: string, options: { yes?: boolean }) => {
104
+ if (!options.yes) {
105
+ const confirmed = await confirm({ message: `Delete collection #${id}?` })
106
+ if (isCancel(confirmed) || !confirmed) cancel('Cancelled')
107
+ }
108
+ const client = await getClient()
109
+ const format = getFormat(program)
110
+ await withSpinner('Deleting collection', () => client.collections.delete(Number(id)), format)
111
+ console.log(chalk.green(` Collection #${id} deleted`))
112
+ })
113
+
36
114
  collections
37
115
  .command('add <collection-id> <quote-id>')
38
116
  .description('Add a quote to a collection')