@verbatims/cli 1.0.1 → 1.0.3

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,10 @@ verbatims quotes browse
42
42
  # Search
43
43
  verbatims search "love" --type quotes
44
44
 
45
+ # Apply a reproducible theme manifest (safe to preview first)
46
+ verbatims themes apply ./theme.json --dry-run
47
+ verbatims themes apply ./theme.json
48
+
45
49
  # List social platforms with queue stats
46
50
  verbatims social platforms
47
51
 
@@ -58,6 +62,8 @@ verbatims social queue run-now --platform bluesky
58
62
  verbatims --help
59
63
  ```
60
64
 
65
+ Theme manifests use `schema_version: 1`, a stable `slug`, a `name`, and an explicit `filters` array. Optional translations (including homepage copy), language, priority, description, and config are validated before a write. Applying the same manifest reconciles filters by type/value, retaining IDs for unchanged filters; translations reconcile by language, and omitted homepage copy is preserved. Activation and default status are left unchanged. Keep manifests in version control to review theme changes before applying them. See [`examples/themes/quiet-courage.json`](examples/themes/quiet-courage.json).
66
+
61
67
  ### Social commands
62
68
 
63
69
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbatims/cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "CLI for the Verbatims quotes API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "typecheck": "tsc --noEmit"
17
17
  },
18
18
  "dependencies": {
19
- "@verbatims/sdk": "^2.0.1",
19
+ "@verbatims/sdk": "^2.0.2",
20
20
  "@clack/prompts": "^0.10.1",
21
21
  "chalk": "^5.4.1",
22
22
  "commander": "^13.1.0",
@@ -95,12 +95,13 @@ export function registerQuotesCommand(program: Command) {
95
95
  }),
96
96
  format,
97
97
  )
98
- if (options.sourceType && data) {
98
+ const sourceType = options.sourceType
99
+ if (sourceType && data) {
99
100
  const primary = data.attributions?.find(attribution => attribution.is_primary)
100
101
  if (!primary) throw new Error('The created quote has no primary attribution')
101
102
  await withSpinner('Creating source', () => client.quotes.createSource(data.id, {
102
103
  attribution_id: primary.id,
103
- source_type: options.sourceType,
104
+ source_type: sourceType,
104
105
  source_url: options.sourceUrl,
105
106
  is_primary: true,
106
107
  }), format)
@@ -1,4 +1,6 @@
1
1
  import type { Command } from 'commander'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { resolve } from 'node:path'
2
4
  import { text, isCancel, cancel, confirm } from '@clack/prompts'
3
5
  import chalk from 'chalk'
4
6
  import { getClient } from '../utils/client.js'
@@ -47,6 +49,44 @@ export function registerThemesCommand(program: Command) {
47
49
  output(data, format)
48
50
  })
49
51
 
52
+ themes
53
+ .command('apply <file>')
54
+ .description('Create or reconcile a theme from a versioned JSON manifest')
55
+ .option('--dry-run', 'Validate and show the planned operation without changing data')
56
+ .action(async (file: string, options: { dryRun?: boolean }) => {
57
+ const sdk = await import('@verbatims/sdk')
58
+ if (!('parseThemeManifest' in sdk) || typeof sdk.parseThemeManifest !== 'function') {
59
+ throw new Error('Theme manifests require a newer @verbatims/sdk. Update the SDK and try again.')
60
+ }
61
+ const parseThemeManifest = sdk.parseThemeManifest
62
+ const manifest = parseThemeManifest(JSON.parse(await readFile(resolve(file), 'utf8')))
63
+ const client = await getClient()
64
+ if (typeof client.themes.apply !== 'function') {
65
+ throw new Error('Theme manifests require a newer @verbatims/sdk. Update the SDK and try again.')
66
+ }
67
+ const format = getFormat(program)
68
+
69
+ if (options.dryRun) {
70
+ const { data } = await withSpinner('Checking existing themes', () =>
71
+ client.themes.list({ search: manifest.slug, limit: 100 }), format)
72
+ const existing = data?.find(theme => theme.slug === manifest.slug)
73
+ output({
74
+ operation: existing ? 'update' : 'create',
75
+ slug: manifest.slug,
76
+ name: manifest.name,
77
+ filters: manifest.filters?.length ?? 0,
78
+ translations: manifest.translations?.length ?? 'unchanged',
79
+ activation: 'unchanged',
80
+ defaultStatus: 'unchanged',
81
+ }, format)
82
+ return
83
+ }
84
+
85
+ const result = await withSpinner('Applying theme manifest', () => client.themes.apply(manifest), format)
86
+ console.log(chalk.green(` Theme ${result.operation}`))
87
+ output(result.data, format)
88
+ })
89
+
50
90
  themes
51
91
  .command('create')
52
92
  .description('Create a theme')