@workjournal/cli 0.3.4 → 0.4.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.
- package/README.md +47 -19
- package/dist/api-client.d.ts +23 -10
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +7 -13
- package/dist/api-client.js.map +1 -1
- package/dist/commands/entries.d.ts +6 -6
- package/dist/commands/entries.d.ts.map +1 -1
- package/dist/commands/entries.js +12 -12
- package/dist/commands/entries.js.map +1 -1
- package/dist/commands/export.d.ts +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +2 -2
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/invites.d.ts +3 -3
- package/dist/commands/invites.d.ts.map +1 -1
- package/dist/commands/invites.js +6 -8
- package/dist/commands/invites.js.map +1 -1
- package/dist/commands/journals.d.ts +5 -5
- package/dist/commands/journals.d.ts.map +1 -1
- package/dist/commands/journals.js +24 -19
- package/dist/commands/journals.js.map +1 -1
- package/dist/commands/shares.d.ts +2 -2
- package/dist/commands/shares.d.ts.map +1 -1
- package/dist/commands/shares.js +5 -5
- package/dist/commands/shares.js.map +1 -1
- package/dist/commands/workspaces.d.ts +5 -0
- package/dist/commands/workspaces.d.ts.map +1 -0
- package/dist/commands/workspaces.js +62 -0
- package/dist/commands/workspaces.js.map +1 -0
- package/dist/config.d.ts +10 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -1
- package/dist/config.js.map +1 -1
- package/dist/index.js +356 -162
- package/dist/index.js.map +1 -1
- package/dist/project-config.d.ts +34 -4
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +39 -8
- package/dist/project-config.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { API_PATHS, apiGet, requireAuth } from './api-client.js';
|
|
2
3
|
import { loginFinish, loginInteractive, loginStart } from './auth.js';
|
|
3
4
|
import { createEntry, deleteEntry, getEntry, lastEntries, listEntries, searchEntries, } from './commands/entries.js';
|
|
4
5
|
import { exportJournal } from './commands/export.js';
|
|
5
6
|
import { deleteInvite, listInvites, newInvite } from './commands/invites.js';
|
|
6
7
|
import { createJournal, deleteJournal, getJournal, listJournals, selectJournal, } from './commands/journals.js';
|
|
7
8
|
import { deleteShare, listShares } from './commands/shares.js';
|
|
9
|
+
import { createWorkspace, getWorkspace, listWorkspaces, selectWorkspace, } from './commands/workspaces.js';
|
|
8
10
|
import { readConfig } from './config.js';
|
|
9
11
|
import { CONFIG_DIR, clearCredentials, clearLoginState, readCredentials } from './credentials.js';
|
|
10
|
-
import { findProjectConfig, getProjectConfigPath } from './project-config.js';
|
|
12
|
+
import { findProjectConfig, getProjectConfigPath, rewriteProjectConfigAt, } from './project-config.js';
|
|
11
13
|
// ── Arg parsing ─────────────────────────────────────────────────────────────
|
|
12
14
|
const rawArgs = process.argv.slice(2);
|
|
13
15
|
const jsonOutput = rawArgs.includes('--json');
|
|
@@ -31,17 +33,64 @@ function parseFlags(argv) {
|
|
|
31
33
|
}
|
|
32
34
|
return { flags, positional };
|
|
33
35
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Migrate a legacy `{ journal_id }` project config by walking workspaces and
|
|
38
|
+
* journals to find the entry whose `id` matches. Rewrites the file in place
|
|
39
|
+
* on success. Returns `null` if migration can't run (offline, deleted journal,
|
|
40
|
+
* not authenticated).
|
|
41
|
+
*/
|
|
42
|
+
async function migrateLegacyProjectConfig(journalId, configPath) {
|
|
43
|
+
const token = await requireAuth();
|
|
44
|
+
let workspaces;
|
|
45
|
+
try {
|
|
46
|
+
const result = await apiGet(API_PATHS.workspaces, token);
|
|
47
|
+
workspaces = result.data;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
for (const ws of workspaces) {
|
|
53
|
+
try {
|
|
54
|
+
const journals = await apiGet(API_PATHS.journals(ws.slug), token);
|
|
55
|
+
const match = journals.data.find((j) => j.id === journalId);
|
|
56
|
+
if (match) {
|
|
57
|
+
rewriteProjectConfigAt(configPath, ws.slug, match.slug);
|
|
58
|
+
process.stderr.write(`Migrated .workjournal: journal_id ${journalId} → ${ws.slug}/${match.slug}\n`);
|
|
59
|
+
return { workspaceSlug: ws.slug, journalSlug: match.slug };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Skip this workspace and try the next one.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
async function resolveActiveSelection() {
|
|
69
|
+
const stored = findProjectConfig();
|
|
70
|
+
if (stored) {
|
|
71
|
+
if (stored.kind === 'v2')
|
|
72
|
+
return stored.config;
|
|
73
|
+
// Legacy v1 — try to migrate by resolving the UUID via the API.
|
|
74
|
+
process.stderr.write(`Found legacy .workjournal at ${stored.path} (journal_id: ${stored.config.journal_id}).\n` +
|
|
75
|
+
'Attempting one-shot migration to slug-based selection…\n');
|
|
76
|
+
const migrated = await migrateLegacyProjectConfig(stored.config.journal_id, stored.path);
|
|
77
|
+
if (migrated)
|
|
78
|
+
return migrated;
|
|
79
|
+
process.stderr.write('\nCould not migrate the legacy selection. The journal may have been deleted, or you are offline.\n' +
|
|
80
|
+
'Re-select with:\n' +
|
|
81
|
+
' workjournal workspaces list\n' +
|
|
82
|
+
' workjournal journals list <workspaceSlug>\n' +
|
|
83
|
+
' workjournal journals select <workspaceSlug> <journalSlug>\n');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
39
86
|
const config = readConfig();
|
|
40
|
-
if (config.
|
|
41
|
-
return config.
|
|
87
|
+
if (config.workspaceSlug && config.journalSlug) {
|
|
88
|
+
return { workspaceSlug: config.workspaceSlug, journalSlug: config.journalSlug };
|
|
89
|
+
}
|
|
42
90
|
process.stderr.write(`No journal selected. To select one:
|
|
43
|
-
workjournal
|
|
44
|
-
workjournal journals
|
|
91
|
+
workjournal workspaces list
|
|
92
|
+
workjournal journals list <workspaceSlug>
|
|
93
|
+
workjournal journals select <workspaceSlug> <journalSlug>
|
|
45
94
|
`);
|
|
46
95
|
process.exit(1);
|
|
47
96
|
}
|
|
@@ -105,207 +154,320 @@ async function routeAuth(argv) {
|
|
|
105
154
|
process.exit(1);
|
|
106
155
|
}
|
|
107
156
|
}
|
|
108
|
-
async function
|
|
157
|
+
async function routeWorkspaces(argv) {
|
|
109
158
|
const sub = argv[0];
|
|
110
159
|
switch (sub) {
|
|
111
160
|
case undefined:
|
|
112
161
|
case 'list':
|
|
113
|
-
await
|
|
162
|
+
await listWorkspaces(jsonOutput);
|
|
114
163
|
break;
|
|
115
|
-
case '
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!summary || !body) {
|
|
120
|
-
process.stderr.write('Usage: workjournal journals <id> entries write -s <summary> -b <body>\n');
|
|
164
|
+
case 'get': {
|
|
165
|
+
const slug = argv[1];
|
|
166
|
+
if (!slug) {
|
|
167
|
+
process.stderr.write('Usage: workjournal workspaces get <workspaceSlug>\n');
|
|
121
168
|
process.exit(1);
|
|
122
169
|
}
|
|
123
|
-
await
|
|
170
|
+
await getWorkspace(slug, jsonOutput);
|
|
124
171
|
break;
|
|
125
172
|
}
|
|
126
|
-
case '
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
173
|
+
case 'new':
|
|
174
|
+
case 'create': {
|
|
175
|
+
const { flags, positional } = parseFlags(argv.slice(1));
|
|
176
|
+
const name = positional[0];
|
|
177
|
+
if (!name) {
|
|
178
|
+
process.stderr.write('Usage: workjournal workspaces new <name> [--slug <slug>]\n');
|
|
130
179
|
process.exit(1);
|
|
131
180
|
}
|
|
132
|
-
|
|
181
|
+
const slug = flags['--slug'];
|
|
182
|
+
await createWorkspace(name, slug, jsonOutput);
|
|
133
183
|
break;
|
|
134
184
|
}
|
|
135
|
-
case '
|
|
136
|
-
const
|
|
137
|
-
if (!
|
|
138
|
-
process.stderr.write('Usage: workjournal
|
|
185
|
+
case 'select': {
|
|
186
|
+
const slug = argv[1];
|
|
187
|
+
if (!slug) {
|
|
188
|
+
process.stderr.write('Usage: workjournal workspaces select <workspaceSlug>\n');
|
|
139
189
|
process.exit(1);
|
|
140
190
|
}
|
|
141
|
-
await
|
|
191
|
+
await selectWorkspace(slug);
|
|
142
192
|
break;
|
|
143
193
|
}
|
|
194
|
+
default:
|
|
195
|
+
process.stderr.write(`Unknown workspaces subcommand: ${sub}\n` +
|
|
196
|
+
'Usage: workjournal workspaces <list|get|new|select>\n');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function routeJournals(argv) {
|
|
201
|
+
const sub = argv[0];
|
|
202
|
+
if (sub === undefined) {
|
|
203
|
+
// Shortcut: workjournal journals → show selected journal details
|
|
204
|
+
const sel = await resolveActiveSelection();
|
|
205
|
+
await getJournal(sel.workspaceSlug, sel.journalSlug, jsonOutput);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
switch (sub) {
|
|
209
|
+
case 'list': {
|
|
210
|
+
// `list [workspaceSlug]` — falls back to selected workspace if omitted.
|
|
211
|
+
const wsSlug = argv[1] ?? readConfig().workspaceSlug;
|
|
212
|
+
if (!wsSlug) {
|
|
213
|
+
process.stderr.write('Usage: workjournal journals list <workspaceSlug>\n' +
|
|
214
|
+
'(or run `workjournal workspaces select <slug>` first)\n');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
await listJournals(wsSlug, jsonOutput);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
case 'get': {
|
|
221
|
+
const ws = argv[1];
|
|
222
|
+
const j = argv[2];
|
|
223
|
+
if (!ws || !j) {
|
|
224
|
+
process.stderr.write('Usage: workjournal journals get <workspaceSlug> <journalSlug>\n');
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
await getJournal(ws, j, jsonOutput);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
case 'new':
|
|
231
|
+
case 'create': {
|
|
232
|
+
const { flags, positional } = parseFlags(argv.slice(1));
|
|
233
|
+
const ws = positional[0];
|
|
234
|
+
const name = positional[1];
|
|
235
|
+
if (!ws || !name) {
|
|
236
|
+
process.stderr.write('Usage: workjournal journals new <workspaceSlug> <name> [--slug <slug>]\n');
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
const slug = flags['--slug'];
|
|
240
|
+
await createJournal(ws, name, slug, jsonOutput);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
144
243
|
case 'delete': {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
244
|
+
const ws = argv[1];
|
|
245
|
+
const j = argv[2];
|
|
246
|
+
if (!ws || !j) {
|
|
247
|
+
process.stderr.write('Usage: workjournal journals delete <workspaceSlug> <journalSlug>\n');
|
|
148
248
|
process.exit(1);
|
|
149
249
|
}
|
|
150
|
-
await
|
|
151
|
-
|
|
250
|
+
await deleteJournal(ws, j, jsonOutput);
|
|
251
|
+
return;
|
|
152
252
|
}
|
|
153
|
-
case '
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
253
|
+
case 'select': {
|
|
254
|
+
const ws = argv[1];
|
|
255
|
+
const j = argv[2];
|
|
256
|
+
if (!ws || !j) {
|
|
257
|
+
process.stderr.write('Usage: workjournal journals select <workspaceSlug> <journalSlug>\n');
|
|
157
258
|
process.exit(1);
|
|
158
259
|
}
|
|
159
|
-
await
|
|
160
|
-
|
|
260
|
+
await selectJournal(ws, j);
|
|
261
|
+
return;
|
|
161
262
|
}
|
|
162
263
|
default:
|
|
163
|
-
process.stderr.write(`Unknown
|
|
264
|
+
process.stderr.write(`Unknown journals subcommand: ${sub}\n` +
|
|
265
|
+
'Usage: workjournal journals <list|get|new|delete|select>\n');
|
|
164
266
|
process.exit(1);
|
|
165
267
|
}
|
|
166
268
|
}
|
|
167
|
-
async function
|
|
269
|
+
async function routeEntries(argv) {
|
|
168
270
|
const sub = argv[0];
|
|
169
271
|
switch (sub) {
|
|
170
272
|
case undefined:
|
|
171
|
-
case 'list':
|
|
172
|
-
|
|
173
|
-
|
|
273
|
+
case 'list': {
|
|
274
|
+
const ws = argv[1];
|
|
275
|
+
const j = argv[2];
|
|
276
|
+
if (!ws || !j) {
|
|
277
|
+
process.stderr.write('Usage: workjournal entries list <workspaceSlug> <journalSlug>\n');
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
await listEntries(ws, j, jsonOutput);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
case 'write': {
|
|
284
|
+
const ws = argv[1];
|
|
285
|
+
const j = argv[2];
|
|
286
|
+
if (!ws || !j) {
|
|
287
|
+
process.stderr.write('Usage: workjournal entries write <workspaceSlug> <journalSlug> -s <summary> -b <body>\n');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
const { flags } = parseFlags(argv.slice(3));
|
|
291
|
+
const summary = flags['-s'] ?? flags['--summary'];
|
|
292
|
+
const body = flags['-b'] ?? flags['--body'];
|
|
293
|
+
if (!summary || !body) {
|
|
294
|
+
process.stderr.write('Usage: workjournal entries write <workspaceSlug> <journalSlug> -s <summary> -b <body>\n');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
await createEntry(ws, j, summary, body, jsonOutput);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
case 'last': {
|
|
301
|
+
const ws = argv[1];
|
|
302
|
+
const j = argv[2];
|
|
303
|
+
if (!ws || !j) {
|
|
304
|
+
process.stderr.write('Usage: workjournal entries last <workspaceSlug> <journalSlug> [count]\n');
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
const count = Number.parseInt(argv[3] ?? '1', 10);
|
|
308
|
+
if (Number.isNaN(count) || count < 1) {
|
|
309
|
+
process.stderr.write('Usage: workjournal entries last <workspaceSlug> <journalSlug> [count]\n');
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
await lastEntries(ws, j, count, jsonOutput);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
case 'get': {
|
|
316
|
+
const ws = argv[1];
|
|
317
|
+
const j = argv[2];
|
|
318
|
+
const index = argv[3];
|
|
319
|
+
if (!ws || !j || !index) {
|
|
320
|
+
process.stderr.write('Usage: workjournal entries get <workspaceSlug> <journalSlug> <index>\n');
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
await getEntry(ws, j, index, jsonOutput);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
174
326
|
case 'delete': {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
327
|
+
const ws = argv[1];
|
|
328
|
+
const j = argv[2];
|
|
329
|
+
const index = argv[3];
|
|
330
|
+
if (!ws || !j || !index) {
|
|
331
|
+
process.stderr.write('Usage: workjournal entries delete <workspaceSlug> <journalSlug> <index>\n');
|
|
178
332
|
process.exit(1);
|
|
179
333
|
}
|
|
180
|
-
await
|
|
181
|
-
|
|
334
|
+
await deleteEntry(ws, j, index, jsonOutput);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
case 'search': {
|
|
338
|
+
const ws = argv[1];
|
|
339
|
+
const j = argv[2];
|
|
340
|
+
const query = argv[3];
|
|
341
|
+
if (!ws || !j || !query) {
|
|
342
|
+
process.stderr.write('Usage: workjournal entries search <workspaceSlug> <journalSlug> <query>\n');
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
await searchEntries(ws, j, query, jsonOutput);
|
|
346
|
+
return;
|
|
182
347
|
}
|
|
183
348
|
default:
|
|
184
|
-
process.stderr.write(`Unknown
|
|
349
|
+
process.stderr.write(`Unknown entries subcommand: ${sub}\n` +
|
|
350
|
+
'Usage: workjournal entries <list|write|last|get|delete|search>\n');
|
|
185
351
|
process.exit(1);
|
|
186
352
|
}
|
|
187
353
|
}
|
|
188
|
-
async function
|
|
354
|
+
async function routeShares(argv) {
|
|
189
355
|
const sub = argv[0];
|
|
190
356
|
switch (sub) {
|
|
191
357
|
case undefined:
|
|
192
|
-
case 'list':
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (!email) {
|
|
198
|
-
process.stderr.write('Usage: workjournal journals <id> invites new <email>\n');
|
|
358
|
+
case 'list': {
|
|
359
|
+
const ws = argv[1];
|
|
360
|
+
const j = argv[2];
|
|
361
|
+
if (!ws || !j) {
|
|
362
|
+
process.stderr.write('Usage: workjournal shares list <workspaceSlug> <journalSlug>\n');
|
|
199
363
|
process.exit(1);
|
|
200
364
|
}
|
|
201
|
-
await
|
|
202
|
-
|
|
365
|
+
await listShares(ws, j, jsonOutput);
|
|
366
|
+
return;
|
|
203
367
|
}
|
|
204
368
|
case 'delete': {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
369
|
+
const ws = argv[1];
|
|
370
|
+
const j = argv[2];
|
|
371
|
+
const email = argv[3];
|
|
372
|
+
if (!ws || !j || !email) {
|
|
373
|
+
process.stderr.write('Usage: workjournal shares delete <workspaceSlug> <journalSlug> <email>\n');
|
|
208
374
|
process.exit(1);
|
|
209
375
|
}
|
|
210
|
-
await
|
|
211
|
-
|
|
376
|
+
await deleteShare(ws, j, email, jsonOutput);
|
|
377
|
+
return;
|
|
212
378
|
}
|
|
213
379
|
default:
|
|
214
|
-
process.stderr.write(`Unknown
|
|
380
|
+
process.stderr.write(`Unknown shares subcommand: ${sub}\nUsage: workjournal shares <list|delete>\n`);
|
|
215
381
|
process.exit(1);
|
|
216
382
|
}
|
|
217
383
|
}
|
|
218
|
-
async function
|
|
384
|
+
async function routeInvites(argv) {
|
|
219
385
|
const sub = argv[0];
|
|
220
386
|
switch (sub) {
|
|
221
387
|
case undefined:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
case 'shares':
|
|
228
|
-
await routeShares(journalId, argv.slice(1));
|
|
229
|
-
break;
|
|
230
|
-
case 'invites':
|
|
231
|
-
await routeInvites(journalId, argv.slice(1));
|
|
232
|
-
break;
|
|
233
|
-
case 'export': {
|
|
234
|
-
const { flags } = parseFlags(argv.slice(1));
|
|
235
|
-
const format = flags['-f'] ?? flags['--format'] ?? 'json';
|
|
236
|
-
if (!['json', 'md', 'csv'].includes(format)) {
|
|
237
|
-
process.stderr.write('Invalid format. Use: -f json|md|csv\n');
|
|
388
|
+
case 'list': {
|
|
389
|
+
const ws = argv[1];
|
|
390
|
+
const j = argv[2];
|
|
391
|
+
if (!ws || !j) {
|
|
392
|
+
process.stderr.write('Usage: workjournal invites list <workspaceSlug> <journalSlug>\n');
|
|
238
393
|
process.exit(1);
|
|
239
394
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
395
|
+
await listInvites(ws, j, jsonOutput);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
case 'new':
|
|
399
|
+
case 'create': {
|
|
400
|
+
const ws = argv[1];
|
|
401
|
+
const j = argv[2];
|
|
402
|
+
const email = argv[3];
|
|
403
|
+
if (!ws || !j || !email) {
|
|
404
|
+
process.stderr.write('Usage: workjournal invites new <workspaceSlug> <journalSlug> <email>\n');
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
await newInvite(ws, j, email, jsonOutput);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
case 'delete': {
|
|
411
|
+
const ws = argv[1];
|
|
412
|
+
const j = argv[2];
|
|
413
|
+
const invitationId = argv[3];
|
|
414
|
+
if (!ws || !j || !invitationId) {
|
|
415
|
+
process.stderr.write('Usage: workjournal invites delete <workspaceSlug> <journalSlug> <invitationId>\n');
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
await deleteInvite(ws, j, invitationId, jsonOutput);
|
|
419
|
+
return;
|
|
243
420
|
}
|
|
244
421
|
default:
|
|
245
|
-
process.stderr.write(`Unknown subcommand: ${sub}\n`);
|
|
422
|
+
process.stderr.write(`Unknown invites subcommand: ${sub}\nUsage: workjournal invites <list|new|delete>\n`);
|
|
246
423
|
process.exit(1);
|
|
247
424
|
}
|
|
248
425
|
}
|
|
249
|
-
async function
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
if (sub === 'list') {
|
|
258
|
-
await listJournals(jsonOutput);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (sub === 'new') {
|
|
262
|
-
const name = argv[1];
|
|
263
|
-
if (!name) {
|
|
264
|
-
process.stderr.write('Usage: workjournal journals new <name>\n');
|
|
265
|
-
process.exit(1);
|
|
266
|
-
}
|
|
267
|
-
await createJournal(name, jsonOutput);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (sub === 'delete') {
|
|
271
|
-
const id = argv[1];
|
|
272
|
-
if (!id) {
|
|
273
|
-
process.stderr.write('Usage: workjournal journals delete <journal_id>\n');
|
|
274
|
-
process.exit(1);
|
|
275
|
-
}
|
|
276
|
-
await deleteJournal(id, jsonOutput);
|
|
277
|
-
return;
|
|
426
|
+
async function routeExport(argv) {
|
|
427
|
+
const ws = argv[0];
|
|
428
|
+
const j = argv[1];
|
|
429
|
+
if (!ws || !j) {
|
|
430
|
+
process.stderr.write('Usage: workjournal export <workspaceSlug> <journalSlug> [-f json|md|csv] [-p <path>]\n');
|
|
431
|
+
process.exit(1);
|
|
278
432
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
await selectJournal(id);
|
|
286
|
-
return;
|
|
433
|
+
const { flags } = parseFlags(argv.slice(2));
|
|
434
|
+
const format = flags['-f'] ?? flags['--format'] ?? 'json';
|
|
435
|
+
if (!['json', 'md', 'csv'].includes(format)) {
|
|
436
|
+
process.stderr.write('Invalid format. Use: -f json|md|csv\n');
|
|
437
|
+
process.exit(1);
|
|
287
438
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
await routeJournalResource(journalId, argv.slice(1));
|
|
439
|
+
const outputPath = flags['-p'] ?? flags['--path'];
|
|
440
|
+
await exportJournal(ws, j, format, outputPath);
|
|
291
441
|
}
|
|
292
442
|
async function routeConfig(argv) {
|
|
293
443
|
const sub = argv[0];
|
|
294
444
|
if (sub === 'show') {
|
|
295
445
|
const projectPath = getProjectConfigPath();
|
|
296
|
-
const
|
|
446
|
+
const stored = findProjectConfig();
|
|
297
447
|
const globalCfg = readConfig();
|
|
298
448
|
process.stdout.write('Project config:\n');
|
|
299
|
-
if (projectPath &&
|
|
300
|
-
process.stdout.write(` Path:
|
|
301
|
-
|
|
449
|
+
if (projectPath && stored) {
|
|
450
|
+
process.stdout.write(` Path: ${projectPath}\n`);
|
|
451
|
+
if (stored.kind === 'v2') {
|
|
452
|
+
process.stdout.write(` Workspace: ${stored.config.workspaceSlug}\n`);
|
|
453
|
+
process.stdout.write(` Journal: ${stored.config.journalSlug}\n`);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
process.stdout.write(` Legacy journal_id: ${stored.config.journal_id}\n`);
|
|
457
|
+
process.stdout.write(' (will migrate on next command that needs the selection)\n');
|
|
458
|
+
}
|
|
302
459
|
}
|
|
303
460
|
else {
|
|
304
461
|
process.stdout.write(' (none)\n');
|
|
305
462
|
}
|
|
306
463
|
process.stdout.write('Global config:\n');
|
|
307
|
-
if (globalCfg.
|
|
308
|
-
|
|
464
|
+
if (globalCfg.workspaceSlug || globalCfg.journalSlug) {
|
|
465
|
+
if (globalCfg.workspaceSlug) {
|
|
466
|
+
process.stdout.write(` Workspace: ${globalCfg.workspaceSlug}\n`);
|
|
467
|
+
}
|
|
468
|
+
if (globalCfg.journalSlug) {
|
|
469
|
+
process.stdout.write(` Journal: ${globalCfg.journalSlug}\n`);
|
|
470
|
+
}
|
|
309
471
|
}
|
|
310
472
|
else {
|
|
311
473
|
process.stdout.write(' (none)\n');
|
|
@@ -323,31 +485,41 @@ function printHelp() {
|
|
|
323
485
|
Usage:
|
|
324
486
|
workjournal List entries in selected journal
|
|
325
487
|
|
|
326
|
-
|
|
327
|
-
workjournal
|
|
328
|
-
workjournal
|
|
329
|
-
workjournal
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
workjournal
|
|
334
|
-
workjournal
|
|
335
|
-
workjournal
|
|
336
|
-
workjournal
|
|
337
|
-
workjournal
|
|
338
|
-
workjournal
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
488
|
+
Workspaces:
|
|
489
|
+
workjournal workspaces list List your workspaces
|
|
490
|
+
workjournal workspaces get <ws> Show workspace details
|
|
491
|
+
workjournal workspaces new <name> [--slug <slug>] Create a new workspace
|
|
492
|
+
workjournal workspaces select <ws> Set active workspace
|
|
493
|
+
|
|
494
|
+
Journals:
|
|
495
|
+
workjournal journals list [<ws>] List journals in workspace
|
|
496
|
+
workjournal journals get <ws> <j> Show journal details
|
|
497
|
+
workjournal journals new <ws> <name> [--slug <slug>] Create a new journal
|
|
498
|
+
workjournal journals delete <ws> <j> Delete a journal
|
|
499
|
+
workjournal journals select <ws> <j> Set active journal
|
|
500
|
+
workjournal journals Show selected journal details
|
|
501
|
+
|
|
502
|
+
Entries:
|
|
503
|
+
workjournal entries list <ws> <j> List entries
|
|
504
|
+
workjournal entries write <ws> <j> -s <summary> -b <body>
|
|
505
|
+
Create a new entry
|
|
506
|
+
workjournal entries last <ws> <j> [count] Show most recent entries (full body)
|
|
507
|
+
workjournal entries get <ws> <j> <index> Show a single entry by index
|
|
508
|
+
workjournal entries delete <ws> <j> <index> Delete an entry by index
|
|
509
|
+
workjournal entries search <ws> <j> <query> Search entries
|
|
510
|
+
|
|
511
|
+
Shares (members):
|
|
512
|
+
workjournal shares list <ws> <j> List members
|
|
513
|
+
workjournal shares delete <ws> <j> <email> Remove a member
|
|
342
514
|
|
|
343
|
-
|
|
344
|
-
workjournal
|
|
345
|
-
workjournal
|
|
346
|
-
workjournal
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
workjournal
|
|
350
|
-
|
|
515
|
+
Invites:
|
|
516
|
+
workjournal invites list <ws> <j> List invitations
|
|
517
|
+
workjournal invites new <ws> <j> <email> Invite a collaborator
|
|
518
|
+
workjournal invites delete <ws> <j> <invitationId> Revoke an invitation
|
|
519
|
+
|
|
520
|
+
Export:
|
|
521
|
+
workjournal export <ws> <j> [-f json|md|csv] [-p <path>]
|
|
522
|
+
Export journal data
|
|
351
523
|
|
|
352
524
|
Auth:
|
|
353
525
|
workjournal auth login Interactive login
|
|
@@ -374,22 +546,44 @@ async function run() {
|
|
|
374
546
|
}
|
|
375
547
|
if (command === undefined) {
|
|
376
548
|
// Shortcut: workjournal → list entries in selected journal
|
|
377
|
-
const
|
|
378
|
-
await listEntries(
|
|
549
|
+
const sel = await resolveActiveSelection();
|
|
550
|
+
await listEntries(sel.workspaceSlug, sel.journalSlug, jsonOutput);
|
|
379
551
|
return;
|
|
380
552
|
}
|
|
381
553
|
switch (command) {
|
|
382
554
|
case 'auth':
|
|
383
555
|
await routeAuth(args.slice(1));
|
|
384
556
|
break;
|
|
557
|
+
case 'workspaces':
|
|
558
|
+
await routeWorkspaces(args.slice(1));
|
|
559
|
+
break;
|
|
385
560
|
case 'journal': {
|
|
386
|
-
|
|
387
|
-
await
|
|
561
|
+
// Shortcut: show selected journal details (matches docs/cli-commands.md)
|
|
562
|
+
const sel = await resolveActiveSelection();
|
|
563
|
+
if (args.length > 1) {
|
|
564
|
+
process.stderr.write('`workjournal journal` shows the selected journal. Use `workjournal entries`, ' +
|
|
565
|
+
'`workjournal shares`, etc. with explicit <workspaceSlug> <journalSlug> for ' +
|
|
566
|
+
'resource verbs.\n');
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
await getJournal(sel.workspaceSlug, sel.journalSlug, jsonOutput);
|
|
388
570
|
break;
|
|
389
571
|
}
|
|
390
572
|
case 'journals':
|
|
391
573
|
await routeJournals(args.slice(1));
|
|
392
574
|
break;
|
|
575
|
+
case 'entries':
|
|
576
|
+
await routeEntries(args.slice(1));
|
|
577
|
+
break;
|
|
578
|
+
case 'shares':
|
|
579
|
+
await routeShares(args.slice(1));
|
|
580
|
+
break;
|
|
581
|
+
case 'invites':
|
|
582
|
+
await routeInvites(args.slice(1));
|
|
583
|
+
break;
|
|
584
|
+
case 'export':
|
|
585
|
+
await routeExport(args.slice(1));
|
|
586
|
+
break;
|
|
393
587
|
case 'config':
|
|
394
588
|
await routeConfig(args.slice(1));
|
|
395
589
|
break;
|