@mangomagic/cli 0.1.1 → 0.1.2

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
@@ -17,6 +17,9 @@ at `~/.mangomagic/credentials.json` (mode 0600) so the next runs skip auth.
17
17
  | `mangomagic login` | Browser-based device-code sign-in + splash. Add `--no-open` to print the URL without opening a browser. |
18
18
  | `mangomagic logout` | Forget the cached token. |
19
19
  | `mangomagic whoami` | Show the cached identity. |
20
+ | `mangomagic home` | Show fast wins after sign-in. |
21
+ | `mangomagic tools` | Show available MCP tools and near-next workflows. Add `--json` for machine-readable output. |
22
+ | `mangomagic cards` | Open the Talking Cards workspace. Add `--no-open` to print the URL only. |
20
23
  | `mangomagic splash` | Replay the splash animation. `--static`, `--loop` supported. |
21
24
  | `mangomagic mcp` | Run as a stdio MCP server. Wire into Claude Desktop / Cursor. |
22
25
  | `mangomagic mcp-config` | Print MCP client JSON for copy-paste. |
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@mangomagic/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "MangoMagic CLI — sign in, manage episodes, and expose MangoMagic to MCP clients (Claude Desktop, Cursor).",
5
5
  "type": "module",
6
6
  "bin": {
7
+ "cli": "bin/mangomagic.mjs",
7
8
  "mangomagic": "bin/mangomagic.mjs"
8
9
  },
9
10
  "publishConfig": {
@@ -1,22 +1,8 @@
1
1
  // Browser-device OAuth-style flow against the cli-device-authorize / cli-device-poll edge fns.
2
2
  import { FN_BASE, SUPABASE_ANON_KEY } from "../config.mjs";
3
- import { spawn } from "node:child_process";
3
+ import { openUrl } from "../system/open-url.mjs";
4
4
  import { hostname, platform } from "node:os";
5
5
 
6
- function openBrowser(url) {
7
- const cmd = process.platform === "darwin" ? "open"
8
- : process.platform === "win32" ? "cmd"
9
- : "xdg-open";
10
- const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
11
- try {
12
- const child = spawn(cmd, args, { stdio: "ignore", detached: true });
13
- child.unref();
14
- return true;
15
- } catch {
16
- return false;
17
- }
18
- }
19
-
20
6
  async function call(path, body) {
21
7
  const res = await fetch(`${FN_BASE}/${path}`, {
22
8
  method: "POST",
@@ -40,7 +26,7 @@ export async function deviceLogin({ onCode, onWaiting, openInBrowser = true } =
40
26
  const start = await call("cli-device-authorize", { client_name });
41
27
 
42
28
  onCode?.(start);
43
- if (openInBrowser) openBrowser(start.verification_uri_complete);
29
+ if (openInBrowser) openUrl(start.verification_uri_complete);
44
30
 
45
31
  const deadline = Date.now() + (start.expires_in ?? 600) * 1000;
46
32
  let interval = (start.interval ?? 2) * 1000;
package/src/index.mjs CHANGED
@@ -1,5 +1,8 @@
1
1
  import { deviceLogin } from "./auth/device-flow.mjs";
2
2
  import { loadToken, saveToken, clearToken, credentialsPath } from "./auth/token-store.mjs";
3
+ import { APP_ORIGIN } from "./config.mjs";
4
+ import { openUrl } from "./system/open-url.mjs";
5
+ import { MCP_TOOL_CATALOG, NEXT_WORKFLOWS, QUICK_WINS } from "./tools/catalog.mjs";
3
6
  import { playSplash } from "./ui/splash.mjs";
4
7
 
5
8
  const GOLD = "\x1b[38;2;241;171;28m";
@@ -14,6 +17,9 @@ ${BOLD}mangomagic${RESET} ${DIM}— sign into MangoMagic and bring your account
14
17
  ${GOLD}mangomagic login${RESET} Authorize this terminal (opens your browser; add --no-open to print only).
15
18
  ${GOLD}mangomagic logout${RESET} Forget the token cached on this machine.
16
19
  ${GOLD}mangomagic whoami${RESET} Show the cached identity.
20
+ ${GOLD}mangomagic home${RESET} Show fast wins for creating value right now.
21
+ ${GOLD}mangomagic tools${RESET} Show available MCP tools and next workflows.
22
+ ${GOLD}mangomagic cards${RESET} Open the Talking Cards workspace.
17
23
  ${GOLD}mangomagic splash${RESET} Play the MangoMagic splash animation.
18
24
  ${GOLD}mangomagic mcp${RESET} Run as a stdio MCP server (for Claude Desktop / Cursor).
19
25
  ${GOLD}mangomagic mcp-config${RESET} Print MCP client config for copy-paste.
@@ -23,10 +29,63 @@ Token cache: ${credentialsPath()}
23
29
  `);
24
30
  }
25
31
 
32
+ function home() {
33
+ process.stdout.write(`
34
+ ${BOLD}What can MangoMagic do now?${RESET}
35
+
36
+ ${DIM}Fast wins:${RESET}
37
+ `);
38
+ for (const item of QUICK_WINS) {
39
+ process.stdout.write(` ${GOLD}${item.command.padEnd(24)}${RESET} ${BOLD}${item.title}${RESET}\n`);
40
+ process.stdout.write(` ${DIM}${"".padEnd(24)} ${item.description}${RESET}\n`);
41
+ }
42
+ process.stdout.write(`
43
+ ${DIM}Try this in Claude, Cursor, or Codex after MCP is connected:${RESET}
44
+ "Use MangoMagic to search my episodes for AI adoption."
45
+ "Use MangoMagic to get my latest episode and draft five LinkedIn post ideas."
46
+
47
+ ${DIM}Open app:${RESET} ${APP_ORIGIN}
48
+ ${DIM}Token cache:${RESET} ${credentialsPath()}
49
+ `);
50
+ }
51
+
52
+ function tools({ json = false } = {}) {
53
+ if (json) {
54
+ process.stdout.write(JSON.stringify({
55
+ mcpTools: MCP_TOOL_CATALOG,
56
+ quickWins: QUICK_WINS,
57
+ nextWorkflows: NEXT_WORKFLOWS,
58
+ }, null, 2) + "\n");
59
+ return;
60
+ }
61
+
62
+ process.stdout.write(`
63
+ ${BOLD}MangoMagic MCP Tools${RESET}
64
+
65
+ ${DIM}Available now:${RESET}
66
+ `);
67
+ for (const tool of MCP_TOOL_CATALOG) {
68
+ process.stdout.write(` ${GOLD}${tool.name.padEnd(18)}${RESET} ${tool.description}\n`);
69
+ process.stdout.write(` ${DIM}${"".padEnd(18)} Try: ${tool.example}${RESET}\n`);
70
+ }
71
+
72
+ process.stdout.write(`
73
+ ${DIM}Next high-value workflows to wire in:${RESET}
74
+ `);
75
+ for (const workflow of NEXT_WORKFLOWS) {
76
+ process.stdout.write(` ${GOLD}${workflow.name.padEnd(28)}${RESET} ${workflow.description}\n`);
77
+ }
78
+
79
+ process.stdout.write(`
80
+ ${DIM}Use \`mangomagic mcp-config\` to connect an MCP client.${RESET}
81
+ `);
82
+ }
83
+
26
84
  async function login({ openInBrowser = true } = {}) {
27
85
  if (loadToken()) {
28
86
  process.stdout.write(`${DIM}You're already signed in. Run \`mangomagic logout\` first to switch accounts.${RESET}\n`);
29
87
  await playSplash({ greetingLine: "Welcome back to MangoMagic." });
88
+ home();
30
89
  return;
31
90
  }
32
91
 
@@ -47,6 +106,7 @@ ${BOLD}Sign in to MangoMagic${RESET}
47
106
  saveToken(creds);
48
107
  process.stdout.write("\n\n");
49
108
  await playSplash({ greetingLine: "You're in. Welcome to MangoMagic." });
109
+ home();
50
110
  });
51
111
  }
52
112
 
@@ -61,6 +121,15 @@ function whoami() {
61
121
  process.stdout.write(`Signed in (user ${t.userId ?? "?"}), approved ${t.approvedAt}\n`);
62
122
  }
63
123
 
124
+ function cards({ openInBrowser = true } = {}) {
125
+ const url = `${APP_ORIGIN}/carousels`;
126
+ if (openInBrowser) {
127
+ const opened = openUrl(url);
128
+ process.stdout.write(opened ? "Opening Talking Cards...\n" : "Could not open a browser automatically.\n");
129
+ }
130
+ process.stdout.write(`${GOLD}${url}${RESET}\n`);
131
+ }
132
+
64
133
  async function mcpServer() {
65
134
  // Lazy-load so users who only run `login` don't pay the import cost.
66
135
  const { startMcpServer } = await import("./mcp/server.mjs");
@@ -80,11 +149,14 @@ function mcpConfig() {
80
149
  }
81
150
 
82
151
  export async function run(argv) {
83
- const cmd = argv[0] ?? (loadToken() ? "splash" : "login");
152
+ const cmd = argv[0] ?? (loadToken() ? "home" : "login");
84
153
  switch (cmd) {
85
154
  case "login": return login({ openInBrowser: !argv.includes("--no-open") && process.env.MANGOMAGIC_CLI_NO_OPEN !== "1" });
86
155
  case "logout": return logout();
87
156
  case "whoami": return whoami();
157
+ case "home": return home();
158
+ case "tools": return tools({ json: argv.includes("--json") });
159
+ case "cards": return cards({ openInBrowser: !argv.includes("--no-open") && process.env.MANGOMAGIC_CLI_NO_OPEN !== "1" });
88
160
  case "splash": return playSplash({ mode: argv.includes("--loop") ? "loop" : argv.includes("--static") ? "static" : "anim" });
89
161
  case "mcp": return mcpServer();
90
162
  case "mcp-config": return mcpConfig();
@@ -9,38 +9,18 @@ import {
9
9
  } from "@modelcontextprotocol/sdk/types.js";
10
10
  import { apiCall } from "../api.mjs";
11
11
  import { loadToken } from "../auth/token-store.mjs";
12
+ import { MCP_TOOL_CATALOG } from "../tools/catalog.mjs";
12
13
 
13
- const TOOLS = [
14
- {
15
- name: "list_episodes",
16
- description: "List the signed-in user's MangoMagic episodes (most recent first).",
17
- inputSchema: {
18
- type: "object",
19
- properties: { limit: { type: "integer", minimum: 1, maximum: 50, default: 10 } },
20
- },
21
- handler: async ({ limit = 10 }) => apiCall("cli-list-episodes", { body: { limit } }),
22
- },
23
- {
24
- name: "get_episode",
25
- description: "Get full details for one episode (transcript, summary, takeaways) by ID or slug.",
26
- inputSchema: {
27
- type: "object",
28
- properties: { episode: { type: "string", description: "Episode id or slug" } },
29
- required: ["episode"],
30
- },
31
- handler: async ({ episode }) => apiCall("cli-get-episode", { body: { episode } }),
32
- },
33
- {
34
- name: "search_episodes",
35
- description: "Full-text search across the signed-in user's episodes.",
36
- inputSchema: {
37
- type: "object",
38
- properties: { query: { type: "string" } },
39
- required: ["query"],
40
- },
41
- handler: async ({ query }) => apiCall("cli-search-episodes", { body: { query } }),
42
- },
43
- ];
14
+ const TOOL_HANDLERS = {
15
+ list_episodes: async ({ limit = 10 }) => apiCall("cli-list-episodes", { body: { limit } }),
16
+ get_episode: async ({ episode }) => apiCall("cli-get-episode", { body: { episode } }),
17
+ search_episodes: async ({ query }) => apiCall("cli-search-episodes", { body: { query } }),
18
+ };
19
+
20
+ const TOOLS = MCP_TOOL_CATALOG.map((tool) => ({
21
+ ...tool,
22
+ handler: TOOL_HANDLERS[tool.name],
23
+ }));
44
24
 
45
25
  export async function startMcpServer() {
46
26
  if (!loadToken()) {
@@ -0,0 +1,16 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ export function openUrl(url) {
4
+ const cmd = process.platform === "darwin" ? "open"
5
+ : process.platform === "win32" ? "cmd"
6
+ : "xdg-open";
7
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
8
+
9
+ try {
10
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
11
+ child.unref();
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
@@ -0,0 +1,76 @@
1
+ import { APP_ORIGIN } from "../config.mjs";
2
+
3
+ export const MCP_TOOL_CATALOG = [
4
+ {
5
+ name: "list_episodes",
6
+ description: "List the signed-in user's MangoMagic episodes, newest first.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ limit: { type: "integer", minimum: 1, maximum: 50, default: 10 },
11
+ },
12
+ },
13
+ example: "Use MangoMagic to list my three most recent episodes.",
14
+ },
15
+ {
16
+ name: "get_episode",
17
+ description: "Get full details for one episode, including transcript, summary, and takeaways.",
18
+ inputSchema: {
19
+ type: "object",
20
+ properties: {
21
+ episode: { type: "string", description: "Episode id or slug" },
22
+ },
23
+ required: ["episode"],
24
+ },
25
+ example: "Use MangoMagic to get episode <id> and turn it into LinkedIn talking points.",
26
+ },
27
+ {
28
+ name: "search_episodes",
29
+ description: "Search across the signed-in user's episodes.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ query: { type: "string" },
34
+ },
35
+ required: ["query"],
36
+ },
37
+ example: "Use MangoMagic to search my episodes for customer discovery.",
38
+ },
39
+ ];
40
+
41
+ export const QUICK_WINS = [
42
+ {
43
+ command: "mangomagic cards",
44
+ title: "Create Talking Cards",
45
+ description: "Open the card workspace, build a Brand Doc, choose colours, and generate LinkedIn-ready cards.",
46
+ url: `${APP_ORIGIN}/carousels`,
47
+ status: "ready",
48
+ },
49
+ {
50
+ command: "mangomagic mcp-config",
51
+ title: "Connect Claude, Cursor, or Codex",
52
+ description: "Print the MCP config that lets your AI client use MangoMagic tools.",
53
+ status: "ready",
54
+ },
55
+ {
56
+ command: "mangomagic tools",
57
+ title: "See Every Tool",
58
+ description: "Show the full MCP tool list plus the next high-value workflows to wire in.",
59
+ status: "ready",
60
+ },
61
+ ];
62
+
63
+ export const NEXT_WORKFLOWS = [
64
+ {
65
+ name: "create_talking_cards",
66
+ description: "Generate a batch of branded cards from a short idea, saved to the user's carousel library.",
67
+ },
68
+ {
69
+ name: "sync_linkedin_brand_context",
70
+ description: "Use LinkedIn profile context to draft or refresh the user's Brand Doc.",
71
+ },
72
+ {
73
+ name: "publish_talking_card",
74
+ description: "Publish an approved card to LinkedIn or prepare the post for manual approval.",
75
+ },
76
+ ];