@lifestreamdynamics/vault-cli 1.1.0 → 1.2.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,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAiCommands(program: Command): void;
@@ -0,0 +1,111 @@
1
+ import chalk from 'chalk';
2
+ import { getClientAsync } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerAiCommands(program) {
6
+ const ai = program.command('ai').description('AI chat and document summarization');
7
+ const sessions = ai.command('sessions').description('AI chat session management');
8
+ addGlobalFlags(sessions.command('list')
9
+ .description('List AI chat sessions'))
10
+ .action(async (_opts) => {
11
+ const flags = resolveFlags(_opts);
12
+ const out = createOutput(flags);
13
+ out.startSpinner('Fetching AI sessions...');
14
+ try {
15
+ const client = await getClientAsync();
16
+ const list = await client.ai.listSessions();
17
+ out.stopSpinner();
18
+ out.list(list.map(s => ({ id: s.id, title: s.title ?? 'Untitled', createdAt: s.createdAt })), {
19
+ emptyMessage: 'No AI sessions found.',
20
+ columns: [
21
+ { key: 'id', header: 'ID' },
22
+ { key: 'title', header: 'Title' },
23
+ { key: 'createdAt', header: 'Created' },
24
+ ],
25
+ textFn: (s) => `${chalk.cyan(String(s.id))} — ${String(s.title)}`,
26
+ });
27
+ }
28
+ catch (err) {
29
+ handleError(out, err, 'Failed to fetch AI sessions');
30
+ }
31
+ });
32
+ addGlobalFlags(sessions.command('get')
33
+ .description('Get an AI chat session with messages')
34
+ .argument('<sessionId>', 'Session ID'))
35
+ .action(async (sessionId, _opts) => {
36
+ const flags = resolveFlags(_opts);
37
+ const out = createOutput(flags);
38
+ out.startSpinner('Fetching AI session...');
39
+ try {
40
+ const client = await getClientAsync();
41
+ const result = await client.ai.getSession(sessionId);
42
+ out.stopSpinner();
43
+ if (flags.output === 'json') {
44
+ out.raw(JSON.stringify(result, null, 2) + '\n');
45
+ }
46
+ else {
47
+ process.stdout.write(`Session: ${chalk.cyan(result.session.id)}\n`);
48
+ process.stdout.write(`Title: ${result.session.title ?? 'Untitled'}\n\n`);
49
+ for (const msg of result.messages ?? []) {
50
+ const role = msg.role === 'assistant' ? chalk.green('AI') : chalk.blue('You');
51
+ process.stdout.write(`${role}: ${String(msg.content ?? '')}\n\n`);
52
+ }
53
+ }
54
+ }
55
+ catch (err) {
56
+ handleError(out, err, 'Failed to fetch AI session');
57
+ }
58
+ });
59
+ addGlobalFlags(sessions.command('delete')
60
+ .description('Delete an AI chat session')
61
+ .argument('<sessionId>', 'Session ID'))
62
+ .action(async (sessionId, _opts) => {
63
+ const flags = resolveFlags(_opts);
64
+ const out = createOutput(flags);
65
+ out.startSpinner('Deleting AI session...');
66
+ try {
67
+ const client = await getClientAsync();
68
+ await client.ai.deleteSession(sessionId);
69
+ out.success('Session deleted', { id: sessionId });
70
+ }
71
+ catch (err) {
72
+ handleError(out, err, 'Failed to delete AI session');
73
+ }
74
+ });
75
+ addGlobalFlags(ai.command('chat')
76
+ .description('Send a message in an AI chat session')
77
+ .argument('<sessionId>', 'Session ID')
78
+ .argument('<message>', 'Message to send'))
79
+ .action(async (sessionId, message, _opts) => {
80
+ const flags = resolveFlags(_opts);
81
+ const out = createOutput(flags);
82
+ out.startSpinner('Sending message...');
83
+ try {
84
+ const client = await getClientAsync();
85
+ const response = await client.ai.chat({ message, sessionId });
86
+ out.stopSpinner();
87
+ process.stdout.write(String(response.content ?? JSON.stringify(response)) + '\n');
88
+ }
89
+ catch (err) {
90
+ handleError(out, err, 'Failed to send AI message');
91
+ }
92
+ });
93
+ addGlobalFlags(ai.command('summarize')
94
+ .description('Summarize a document with AI')
95
+ .argument('<vaultId>', 'Vault ID')
96
+ .argument('<docPath>', 'Document path'))
97
+ .action(async (vaultId, docPath, _opts) => {
98
+ const flags = resolveFlags(_opts);
99
+ const out = createOutput(flags);
100
+ out.startSpinner('Summarizing document...');
101
+ try {
102
+ const client = await getClientAsync();
103
+ const summary = await client.ai.summarize(vaultId, docPath);
104
+ out.stopSpinner();
105
+ process.stdout.write(String(summary.summary ?? JSON.stringify(summary)) + '\n');
106
+ }
107
+ catch (err) {
108
+ handleError(out, err, 'Failed to summarize document');
109
+ }
110
+ });
111
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAnalyticsCommands(program: Command): void;
@@ -0,0 +1,84 @@
1
+ import chalk from 'chalk';
2
+ import { getClientAsync } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerAnalyticsCommands(program) {
6
+ const analytics = program.command('analytics').description('Analytics for published documents and share links');
7
+ addGlobalFlags(analytics.command('published')
8
+ .description('Summary of published document views'))
9
+ .action(async (_opts) => {
10
+ const flags = resolveFlags(_opts);
11
+ const out = createOutput(flags);
12
+ out.startSpinner('Fetching analytics...');
13
+ try {
14
+ const client = await getClientAsync();
15
+ const summary = await client.analytics.getPublishedSummary();
16
+ out.stopSpinner();
17
+ if (flags.output === 'json') {
18
+ out.raw(JSON.stringify(summary, null, 2) + '\n');
19
+ }
20
+ else {
21
+ process.stdout.write(`Total published: ${summary.totalPublished}, Total views: ${summary.totalViews}\n\n`);
22
+ out.list(summary.documents.map(d => ({ slug: d.slug, title: d.title ?? '', viewCount: d.viewCount, publishedAt: d.publishedAt })), {
23
+ emptyMessage: 'No published documents.',
24
+ columns: [
25
+ { key: 'slug', header: 'Slug' },
26
+ { key: 'title', header: 'Title' },
27
+ { key: 'viewCount', header: 'Views' },
28
+ { key: 'publishedAt', header: 'Published' },
29
+ ],
30
+ textFn: (d) => `${chalk.cyan(String(d.slug))} — ${String(d.viewCount)} views`,
31
+ });
32
+ }
33
+ }
34
+ catch (err) {
35
+ handleError(out, err, 'Failed to fetch analytics');
36
+ }
37
+ });
38
+ addGlobalFlags(analytics.command('share')
39
+ .description('Analytics for a share link')
40
+ .argument('<vaultId>', 'Vault ID')
41
+ .argument('<shareId>', 'Share ID'))
42
+ .action(async (vaultId, shareId, _opts) => {
43
+ const flags = resolveFlags(_opts);
44
+ const out = createOutput(flags);
45
+ out.startSpinner('Fetching share analytics...');
46
+ try {
47
+ const client = await getClientAsync();
48
+ const data = await client.analytics.getShareAnalytics(vaultId, shareId);
49
+ out.stopSpinner();
50
+ out.record({
51
+ shareId: data.shareId,
52
+ viewCount: data.viewCount,
53
+ uniqueViewers: data.uniqueViewers,
54
+ lastViewedAt: data.lastViewedAt,
55
+ });
56
+ }
57
+ catch (err) {
58
+ handleError(out, err, 'Failed to fetch share analytics');
59
+ }
60
+ });
61
+ addGlobalFlags(analytics.command('doc')
62
+ .description('Analytics for a published document')
63
+ .argument('<vaultId>', 'Vault ID')
64
+ .argument('<publishedDocId>', 'Published document ID'))
65
+ .action(async (vaultId, publishedDocId, _opts) => {
66
+ const flags = resolveFlags(_opts);
67
+ const out = createOutput(flags);
68
+ out.startSpinner('Fetching document analytics...');
69
+ try {
70
+ const client = await getClientAsync();
71
+ const data = await client.analytics.getPublishedDocAnalytics(vaultId, publishedDocId);
72
+ out.stopSpinner();
73
+ out.record({
74
+ publishedDocId: data.publishedDocId,
75
+ viewCount: data.viewCount,
76
+ uniqueViewers: data.uniqueViewers,
77
+ lastViewedAt: data.lastViewedAt,
78
+ });
79
+ }
80
+ catch (err) {
81
+ handleError(out, err, 'Failed to fetch document analytics');
82
+ }
83
+ });
84
+ }
@@ -154,6 +154,59 @@ export function registerCalendarCommands(program) {
154
154
  handleError(out, err, 'Calendar events failed');
155
155
  }
156
156
  });
157
+ addGlobalFlags(calendar.command('agenda')
158
+ .description('View due-date agenda grouped by time period')
159
+ .argument('<vaultId>', 'Vault ID')
160
+ .option('--status <status>', 'Filter by status')
161
+ .option('--range <range>', 'Time range (e.g., week, month)')
162
+ .option('--group-by <groupBy>', 'Group by field'))
163
+ .action(async (vaultId, _opts) => {
164
+ const flags = resolveFlags(_opts);
165
+ const out = createOutput(flags);
166
+ out.startSpinner('Fetching agenda...');
167
+ try {
168
+ const client = await getClientAsync();
169
+ const agenda = await client.calendar.getAgenda(vaultId, {
170
+ status: _opts.status,
171
+ range: _opts.range,
172
+ groupBy: _opts.groupBy,
173
+ });
174
+ out.stopSpinner();
175
+ if (flags.output === 'json') {
176
+ out.raw(JSON.stringify(agenda, null, 2) + '\n');
177
+ }
178
+ else {
179
+ process.stdout.write(`Total: ${agenda.total} items\n\n`);
180
+ for (const group of agenda.groups) {
181
+ process.stdout.write(`${chalk.bold(group.label)}\n`);
182
+ for (const item of group.items) {
183
+ process.stdout.write(` ${chalk.cyan(item.path ?? '')} — due: ${String(item.dueAt ?? 'N/A')}\n`);
184
+ }
185
+ }
186
+ }
187
+ }
188
+ catch (err) {
189
+ handleError(out, err, 'Failed to fetch agenda');
190
+ }
191
+ });
192
+ addGlobalFlags(calendar.command('ical')
193
+ .description('Output iCal feed for a vault to stdout')
194
+ .argument('<vaultId>', 'Vault ID')
195
+ .option('--include <types>', 'Types to include'))
196
+ .action(async (vaultId, _opts) => {
197
+ const flags = resolveFlags(_opts);
198
+ const out = createOutput(flags);
199
+ try {
200
+ const client = await getClientAsync();
201
+ const ical = await client.calendar.getIcalFeed(vaultId, {
202
+ include: _opts.include,
203
+ });
204
+ process.stdout.write(ical);
205
+ }
206
+ catch (err) {
207
+ handleError(out, err, 'Failed to fetch iCal feed');
208
+ }
209
+ });
157
210
  }
158
211
  function getDefaultStart() {
159
212
  const d = new Date();
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCustomDomainCommands(program: Command): void;
@@ -0,0 +1,149 @@
1
+ import chalk from 'chalk';
2
+ import { getClientAsync } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerCustomDomainCommands(program) {
6
+ const domains = program.command('custom-domains').description('Manage custom domains for published vaults');
7
+ addGlobalFlags(domains.command('list')
8
+ .description('List custom domains'))
9
+ .action(async (_opts) => {
10
+ const flags = resolveFlags(_opts);
11
+ const out = createOutput(flags);
12
+ out.startSpinner('Fetching custom domains...');
13
+ try {
14
+ const client = await getClientAsync();
15
+ const list = await client.customDomains.list();
16
+ out.stopSpinner();
17
+ out.list(list.map(d => ({ id: d.id, domain: d.domain, verified: d.verified ? 'yes' : 'no', createdAt: d.createdAt })), {
18
+ emptyMessage: 'No custom domains found.',
19
+ columns: [
20
+ { key: 'id', header: 'ID' },
21
+ { key: 'domain', header: 'Domain' },
22
+ { key: 'verified', header: 'Verified' },
23
+ { key: 'createdAt', header: 'Created' },
24
+ ],
25
+ textFn: (d) => `${chalk.cyan(String(d.domain))} — ${d.verified === 'yes' ? chalk.green('verified') : chalk.yellow('unverified')}`,
26
+ });
27
+ }
28
+ catch (err) {
29
+ handleError(out, err, 'Failed to fetch custom domains');
30
+ }
31
+ });
32
+ addGlobalFlags(domains.command('get')
33
+ .description('Get a custom domain')
34
+ .argument('<domainId>', 'Domain ID'))
35
+ .action(async (domainId, _opts) => {
36
+ const flags = resolveFlags(_opts);
37
+ const out = createOutput(flags);
38
+ out.startSpinner('Fetching custom domain...');
39
+ try {
40
+ const client = await getClientAsync();
41
+ const d = await client.customDomains.get(domainId);
42
+ out.stopSpinner();
43
+ out.record({ id: d.id, domain: d.domain, verified: d.verified, verificationToken: d.verificationToken, createdAt: d.createdAt });
44
+ }
45
+ catch (err) {
46
+ handleError(out, err, 'Failed to fetch custom domain');
47
+ }
48
+ });
49
+ addGlobalFlags(domains.command('add')
50
+ .description('Add a custom domain')
51
+ .argument('<domain>', 'Domain name (e.g., docs.example.com)'))
52
+ .action(async (domain, _opts) => {
53
+ const flags = resolveFlags(_opts);
54
+ const out = createOutput(flags);
55
+ out.startSpinner('Adding custom domain...');
56
+ try {
57
+ const client = await getClientAsync();
58
+ const d = await client.customDomains.create({ domain });
59
+ out.success(`Domain added: ${d.domain}`, { id: d.id, domain: d.domain, verificationToken: d.verificationToken });
60
+ if (flags.output !== 'json') {
61
+ process.stdout.write(`\nTo verify, add this DNS TXT record:\n`);
62
+ process.stdout.write(` ${chalk.cyan('_lsvault-verification.' + d.domain)} TXT ${chalk.green(d.verificationToken)}\n`);
63
+ process.stdout.write(`\nThen run: lsvault custom-domains verify ${d.id}\n`);
64
+ }
65
+ }
66
+ catch (err) {
67
+ handleError(out, err, 'Failed to add custom domain');
68
+ }
69
+ });
70
+ addGlobalFlags(domains.command('update')
71
+ .description('Update a custom domain')
72
+ .argument('<domainId>', 'Domain ID')
73
+ .requiredOption('--domain <domain>', 'New domain name'))
74
+ .action(async (domainId, _opts) => {
75
+ const flags = resolveFlags(_opts);
76
+ const out = createOutput(flags);
77
+ out.startSpinner('Updating custom domain...');
78
+ try {
79
+ const client = await getClientAsync();
80
+ const d = await client.customDomains.update(domainId, { domain: _opts.domain });
81
+ out.success(`Domain updated: ${d.domain}`, { id: d.id, domain: d.domain });
82
+ }
83
+ catch (err) {
84
+ handleError(out, err, 'Failed to update custom domain');
85
+ }
86
+ });
87
+ addGlobalFlags(domains.command('remove')
88
+ .description('Remove a custom domain')
89
+ .argument('<domainId>', 'Domain ID'))
90
+ .action(async (domainId, _opts) => {
91
+ const flags = resolveFlags(_opts);
92
+ const out = createOutput(flags);
93
+ out.startSpinner('Removing custom domain...');
94
+ try {
95
+ const client = await getClientAsync();
96
+ await client.customDomains.delete(domainId);
97
+ out.success('Custom domain removed', { id: domainId });
98
+ }
99
+ catch (err) {
100
+ handleError(out, err, 'Failed to remove custom domain');
101
+ }
102
+ });
103
+ addGlobalFlags(domains.command('verify')
104
+ .description('Verify a custom domain via DNS')
105
+ .argument('<domainId>', 'Domain ID'))
106
+ .action(async (domainId, _opts) => {
107
+ const flags = resolveFlags(_opts);
108
+ const out = createOutput(flags);
109
+ out.startSpinner('Verifying custom domain...');
110
+ try {
111
+ const client = await getClientAsync();
112
+ const d = await client.customDomains.verify(domainId);
113
+ out.success(`Domain ${d.verified ? 'verified' : 'not yet verified'}: ${d.domain}`, { id: d.id, domain: d.domain, verified: d.verified });
114
+ }
115
+ catch (err) {
116
+ handleError(out, err, 'Failed to verify custom domain');
117
+ }
118
+ });
119
+ addGlobalFlags(domains.command('check')
120
+ .description('Check DNS configuration for a custom domain')
121
+ .argument('<domainId>', 'Domain ID'))
122
+ .action(async (domainId, _opts) => {
123
+ const flags = resolveFlags(_opts);
124
+ const out = createOutput(flags);
125
+ out.startSpinner('Checking DNS...');
126
+ try {
127
+ const client = await getClientAsync();
128
+ const result = await client.customDomains.checkDns(domainId);
129
+ out.stopSpinner();
130
+ out.record({
131
+ domain: result.domain,
132
+ resolved: result.resolved,
133
+ expectedValue: result.expectedValue,
134
+ actualValue: result.actualValue ?? 'N/A',
135
+ });
136
+ if (flags.output !== 'json') {
137
+ if (result.resolved) {
138
+ process.stdout.write(chalk.green('\n✓ DNS configured correctly\n'));
139
+ }
140
+ else {
141
+ process.stdout.write(chalk.yellow(`\n⚠ DNS not yet propagated. Expected: ${result.expectedValue}\n`));
142
+ }
143
+ }
144
+ }
145
+ catch (err) {
146
+ handleError(out, err, 'Failed to check DNS');
147
+ }
148
+ });
149
+ }
@@ -191,4 +191,144 @@ EXAMPLES
191
191
  handleError(out, err, 'Failed to move document');
192
192
  }
193
193
  });
194
+ addGlobalFlags(docs.command('bulk-move')
195
+ .description('Move multiple documents to a target directory')
196
+ .argument('<vaultId>', 'Vault ID')
197
+ .requiredOption('--paths <csv>', 'Comma-separated list of document paths')
198
+ .requiredOption('--target <dir>', 'Target directory'))
199
+ .action(async (vaultId, _opts) => {
200
+ const flags = resolveFlags(_opts);
201
+ const out = createOutput(flags);
202
+ out.startSpinner('Moving documents...');
203
+ try {
204
+ const client = await getClientAsync();
205
+ const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
206
+ const result = await client.documents.bulkMove(vaultId, { paths, targetDirectory: _opts.target });
207
+ out.stopSpinner();
208
+ if (flags.output === 'json') {
209
+ out.raw(JSON.stringify(result, null, 2) + '\n');
210
+ }
211
+ else {
212
+ process.stdout.write(`Succeeded: ${result.succeeded.length}, Failed: ${result.failed.length}\n`);
213
+ if (result.failed.length > 0) {
214
+ for (const f of result.failed)
215
+ process.stdout.write(` ${chalk.red('✗')} ${f.path}: ${f.error}\n`);
216
+ }
217
+ }
218
+ }
219
+ catch (err) {
220
+ handleError(out, err, 'Failed to bulk move documents');
221
+ }
222
+ });
223
+ addGlobalFlags(docs.command('bulk-copy')
224
+ .description('Copy multiple documents to a target directory')
225
+ .argument('<vaultId>', 'Vault ID')
226
+ .requiredOption('--paths <csv>', 'Comma-separated list of document paths')
227
+ .requiredOption('--target <dir>', 'Target directory'))
228
+ .action(async (vaultId, _opts) => {
229
+ const flags = resolveFlags(_opts);
230
+ const out = createOutput(flags);
231
+ out.startSpinner('Copying documents...');
232
+ try {
233
+ const client = await getClientAsync();
234
+ const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
235
+ const result = await client.documents.bulkCopy(vaultId, { paths, targetDirectory: _opts.target });
236
+ out.stopSpinner();
237
+ if (flags.output === 'json') {
238
+ out.raw(JSON.stringify(result, null, 2) + '\n');
239
+ }
240
+ else {
241
+ process.stdout.write(`Succeeded: ${result.succeeded.length}, Failed: ${result.failed.length}\n`);
242
+ if (result.failed.length > 0) {
243
+ for (const f of result.failed)
244
+ process.stdout.write(` ${chalk.red('✗')} ${f.path}: ${f.error}\n`);
245
+ }
246
+ }
247
+ }
248
+ catch (err) {
249
+ handleError(out, err, 'Failed to bulk copy documents');
250
+ }
251
+ });
252
+ addGlobalFlags(docs.command('bulk-delete')
253
+ .description('Delete multiple documents')
254
+ .argument('<vaultId>', 'Vault ID')
255
+ .requiredOption('--paths <csv>', 'Comma-separated list of document paths'))
256
+ .action(async (vaultId, _opts) => {
257
+ const flags = resolveFlags(_opts);
258
+ const out = createOutput(flags);
259
+ out.startSpinner('Deleting documents...');
260
+ try {
261
+ const client = await getClientAsync();
262
+ const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
263
+ const result = await client.documents.bulkDelete(vaultId, { paths });
264
+ out.stopSpinner();
265
+ if (flags.output === 'json') {
266
+ out.raw(JSON.stringify(result, null, 2) + '\n');
267
+ }
268
+ else {
269
+ process.stdout.write(`Succeeded: ${result.succeeded.length}, Failed: ${result.failed.length}\n`);
270
+ if (result.failed.length > 0) {
271
+ for (const f of result.failed)
272
+ process.stdout.write(` ${chalk.red('✗')} ${f.path}: ${f.error}\n`);
273
+ }
274
+ }
275
+ }
276
+ catch (err) {
277
+ handleError(out, err, 'Failed to bulk delete documents');
278
+ }
279
+ });
280
+ addGlobalFlags(docs.command('bulk-tag')
281
+ .description('Add or remove tags from multiple documents')
282
+ .argument('<vaultId>', 'Vault ID')
283
+ .requiredOption('--paths <csv>', 'Comma-separated list of document paths')
284
+ .option('--add <csv>', 'Tags to add (comma-separated)')
285
+ .option('--remove <csv>', 'Tags to remove (comma-separated)'))
286
+ .action(async (vaultId, _opts) => {
287
+ const flags = resolveFlags(_opts);
288
+ const out = createOutput(flags);
289
+ if (!_opts.add && !_opts.remove) {
290
+ out.error('At least one of --add or --remove must be specified');
291
+ process.exitCode = 1;
292
+ return;
293
+ }
294
+ out.startSpinner('Tagging documents...');
295
+ try {
296
+ const client = await getClientAsync();
297
+ const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
298
+ const addTags = _opts.add ? (String(_opts.add)).split(',').map(t => t.trim()).filter(Boolean) : undefined;
299
+ const removeTags = _opts.remove ? (String(_opts.remove)).split(',').map(t => t.trim()).filter(Boolean) : undefined;
300
+ const result = await client.documents.bulkTag(vaultId, { paths, addTags, removeTags });
301
+ out.stopSpinner();
302
+ if (flags.output === 'json') {
303
+ out.raw(JSON.stringify(result, null, 2) + '\n');
304
+ }
305
+ else {
306
+ process.stdout.write(`Succeeded: ${result.succeeded.length}, Failed: ${result.failed.length}\n`);
307
+ if (result.failed.length > 0) {
308
+ for (const f of result.failed)
309
+ process.stdout.write(` ${chalk.red('✗')} ${f.path}: ${f.error}\n`);
310
+ }
311
+ }
312
+ }
313
+ catch (err) {
314
+ handleError(out, err, 'Failed to bulk tag documents');
315
+ }
316
+ });
317
+ addGlobalFlags(docs.command('mkdir')
318
+ .description('Create a directory in a vault')
319
+ .argument('<vaultId>', 'Vault ID')
320
+ .argument('<path>', 'Directory path to create'))
321
+ .action(async (vaultId, path, _opts) => {
322
+ const flags = resolveFlags(_opts);
323
+ const out = createOutput(flags);
324
+ out.startSpinner('Creating directory...');
325
+ try {
326
+ const client = await getClientAsync();
327
+ const result = await client.documents.createDirectory(vaultId, path);
328
+ out.success(`Directory ${result.created ? 'created' : 'already exists'}: ${result.path}`, { path: result.path, created: result.created });
329
+ }
330
+ catch (err) {
331
+ handleError(out, err, 'Failed to create directory');
332
+ }
333
+ });
194
334
  }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerPublishVaultCommands(program: Command): void;