@myvillage/cli 2.1.0 → 2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myvillage/cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "MyVillageOS CLI for community developers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,76 @@
1
+ import chalk from 'chalk';
2
+ import { isAuthenticated } from '../utils/auth.js';
3
+ import { getMyStoryBooks } from '../utils/api.js';
4
+
5
+ /**
6
+ * books — the author's own shelf, every status.
7
+ *
8
+ * Exists so an author can see where their submissions stand without being sent
9
+ * to the portal. The portal's story-books screen is a reviewer's surface: it
10
+ * carries other people's books, the approve/reject controls, and the raw state
11
+ * names. None of that belongs in front of the person waiting on a decision.
12
+ *
13
+ * The `--json` shape is what Studio's Story Books screen renders.
14
+ */
15
+
16
+ function fail(json, message) {
17
+ if (json) {
18
+ console.log(JSON.stringify({ success: false, error: message }));
19
+ } else {
20
+ console.log(chalk.red(` ✗ ${message}`));
21
+ }
22
+ process.exitCode = 1;
23
+ }
24
+
25
+ /** Reviewer state name → what it means to the person who wrote the book. */
26
+ const STATE_LABEL = {
27
+ DRAFT: 'Draft',
28
+ PROCESSING: 'Processing',
29
+ SUBMITTED: 'Waiting for review',
30
+ UNDER_REVIEW: 'Being reviewed',
31
+ APPROVED: 'Approved',
32
+ REJECTED: 'Needs changes',
33
+ PUBLISHED: 'Live',
34
+ ARCHIVED: 'Removed',
35
+ };
36
+
37
+ export async function booksListCommand(options = {}) {
38
+ const json = !!options.json;
39
+
40
+ if (!isAuthenticated()) {
41
+ return fail(json, "Authentication required. Run 'myvillage login' first.");
42
+ }
43
+
44
+ let books;
45
+ try {
46
+ const result = await getMyStoryBooks();
47
+ books = result.storyBooks || [];
48
+ } catch (err) {
49
+ return fail(json, err.response?.data?.error || err.message);
50
+ }
51
+
52
+ if (json) {
53
+ console.log(JSON.stringify({ success: true, storyBooks: books }));
54
+ return;
55
+ }
56
+
57
+ if (books.length === 0) {
58
+ console.log('');
59
+ console.log(` No books yet. ${chalk.cyan('myvillage publish-book <pdf>')} publishes one.`);
60
+ console.log('');
61
+ return;
62
+ }
63
+
64
+ console.log('');
65
+ for (const b of books) {
66
+ const label = STATE_LABEL[b.status] || b.status;
67
+ console.log(` ${chalk.bold(b.title)} ${chalk.dim(`(${b.slug})`)}`);
68
+ console.log(
69
+ ` ${chalk.dim('Status:')} ${label} ${chalk.dim('Pages:')} ${b.pageCount ?? 0}`,
70
+ );
71
+ if (b.status === 'REJECTED' && b.rejectionReason) {
72
+ console.log(` ${chalk.red(b.rejectionReason)}`);
73
+ }
74
+ console.log('');
75
+ }
76
+ }
package/src/index.js CHANGED
@@ -19,6 +19,7 @@ import { createGameCommand } from './commands/create-game.js';
19
19
  import { createCommand } from './commands/create-app.js';
20
20
  import { deployCommand } from './commands/deploy.js';
21
21
  import { publishBookCommand } from './commands/publish-book.js';
22
+ import { booksListCommand } from './commands/books.js';
22
23
  import { publishEpisodeCommand } from './commands/publish-episode.js';
23
24
  import {
24
25
  podcastCreateCommand,
@@ -251,6 +252,12 @@ export function run() {
251
252
  .option('--json', 'Output the result as JSON (implies non-interactive)')
252
253
  .action((pdf, options) => publishBookCommand(pdf, options));
253
254
 
255
+ program
256
+ .command('books')
257
+ .description('List your story books and where each one stands')
258
+ .option('--json', 'Output the result as JSON')
259
+ .action((options) => booksListCommand(options));
260
+
254
261
  program
255
262
  .command('publish-episode <audio>')
256
263
  .description('Publish an audio file as a podcast episode')