@mangomagic/cli 0.1.5 → 0.1.6
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 +3 -1
- package/package.json +1 -1
- package/src/chat/natural-language.mjs +61 -2
- package/src/index.mjs +26 -3
- package/src/tools/catalog.mjs +3 -4
package/README.md
CHANGED
|
@@ -21,7 +21,8 @@ at `~/.mangomagic/credentials.json` (mode 0600) so the next runs skip auth.
|
|
|
21
21
|
| `mangomagic chat` | Start an interactive natural-language session. |
|
|
22
22
|
| `mangomagic ask "..."` | Run one natural-language request. |
|
|
23
23
|
| `mangomagic tools` | Show available MCP tools and near-next workflows. Add `--json` for machine-readable output. |
|
|
24
|
-
| `mangomagic cards` |
|
|
24
|
+
| `mangomagic cards "idea"` | Create talking cards in the terminal and save them to your library. Add `--count 3`. |
|
|
25
|
+
| `mangomagic open cards` | Open the Talking Cards workspace in a browser. |
|
|
25
26
|
| `mangomagic splash` | Show the splash once. `--anim`, `--loop` supported. |
|
|
26
27
|
| `mangomagic mcp` | Run as a stdio MCP server. Wire into Claude Desktop / Cursor. |
|
|
27
28
|
| `mangomagic mcp-config` | Print MCP client JSON for copy-paste. |
|
|
@@ -49,6 +50,7 @@ you sign in first.
|
|
|
49
50
|
```bash
|
|
50
51
|
npx -y @mangomagic/cli chat
|
|
51
52
|
npx -y @mangomagic/cli create talking cards about AI adoption
|
|
53
|
+
npx -y @mangomagic/cli cards "why founder-led sales stalls" --count 3
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
Obvious requests are handled locally. Fuzzy routing uses MangoMagic's backend AI
|
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@ function localPlan(text) {
|
|
|
22
22
|
return { action: "open_brand_doc", args: {} };
|
|
23
23
|
}
|
|
24
24
|
if (t.includes("talking card") || t.includes("carousel") || /\bcards?\b/.test(t)) {
|
|
25
|
-
return { action: "
|
|
25
|
+
return { action: "create_talking_cards", args: { focus: raw, count: inferCount(raw) } };
|
|
26
26
|
}
|
|
27
27
|
if (t.includes("mcp") && (t.includes("config") || t.includes("connect") || t.includes("claude") || t.includes("cursor") || t.includes("codex"))) {
|
|
28
28
|
return { action: "mcp_config", args: {} };
|
|
@@ -40,6 +40,12 @@ function localPlan(text) {
|
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function inferCount(text) {
|
|
44
|
+
const match = text.match(/(?:^|\s)(?:make|create|generate)?\s*(\d{1,2})\s+(?:talking\s+)?cards?\b/i);
|
|
45
|
+
if (!match) return 3;
|
|
46
|
+
return Math.max(1, Math.min(Number(match[1]), 10));
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
function asArray(data) {
|
|
44
50
|
if (Array.isArray(data)) return data;
|
|
45
51
|
if (Array.isArray(data?.episodes)) return data.episodes;
|
|
@@ -76,12 +82,63 @@ function printEpisode(data) {
|
|
|
76
82
|
if (ep.id) process.stdout.write(`\n${DIM}${ep.id}${RESET}\n`);
|
|
77
83
|
}
|
|
78
84
|
|
|
85
|
+
function cardUrl(card) {
|
|
86
|
+
return card?.id ? `https://mangomagic.live/carousels/${card.id}` : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function printCards(result) {
|
|
90
|
+
const cards = Array.isArray(result?.carousels) ? result.carousels : [];
|
|
91
|
+
if (!cards.length) {
|
|
92
|
+
process.stdout.write("No cards were created.\n");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
process.stdout.write(`${BOLD}Created ${cards.length} talking card${cards.length === 1 ? "" : "s"}.${RESET}\n\n`);
|
|
97
|
+
for (const [idx, card] of cards.entries()) {
|
|
98
|
+
process.stdout.write(`${GOLD}${idx + 1}. ${card.title || "Untitled card"}${RESET}\n`);
|
|
99
|
+
if (card.seam) process.stdout.write(` ${DIM}${card.seam}${RESET}\n`);
|
|
100
|
+
const slides = Array.isArray(card?.spec?.slides) ? card.spec.slides : [];
|
|
101
|
+
for (const slide of slides.slice(0, 8)) {
|
|
102
|
+
const line = (slide.lines || []).map((l) => l.text).filter(Boolean).join(" / ");
|
|
103
|
+
if (line) process.stdout.write(` - ${line}\n`);
|
|
104
|
+
}
|
|
105
|
+
if (card.caption) process.stdout.write(`\n Caption: ${card.caption.replace(/\n/g, "\n ")}\n`);
|
|
106
|
+
const url = cardUrl(card);
|
|
107
|
+
if (url) process.stdout.write(` Edit: ${url}\n`);
|
|
108
|
+
process.stdout.write("\n");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function createTalkingCards({ focus, count = 3 } = {}) {
|
|
113
|
+
const cleanFocus = compact(focus);
|
|
114
|
+
if (!cleanFocus) {
|
|
115
|
+
process.stdout.write(`Give me an idea, for example:\n ${GOLD}mangomagic cards "why founder-led sales stalls" --count 3${RESET}\n`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
process.stdout.write(`${DIM}Creating ${count} talking card${count === 1 ? "" : "s"}...${RESET}\n`);
|
|
120
|
+
try {
|
|
121
|
+
const result = await apiCall("cli-create-talking-cards", {
|
|
122
|
+
body: { focus: cleanFocus, count: Math.max(1, Math.min(Number(count || 3), 10)) },
|
|
123
|
+
});
|
|
124
|
+
printCards(result);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
const message = err?.message ?? String(err);
|
|
127
|
+
if (message.includes("HTTP 404")) {
|
|
128
|
+
process.stdout.write(`${BOLD}The terminal card-creation backend is not deployed yet.${RESET}\n`);
|
|
129
|
+
process.stdout.write(`This CLI is ready, but MangoMagic Cloud still needs the \`cli-create-talking-cards\` function deployed.\n`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
79
136
|
export async function handleNaturalLanguage(text, actions, { allowModel = true } = {}) {
|
|
80
137
|
let plan = localPlan(text);
|
|
81
138
|
if (!plan && allowModel) {
|
|
82
139
|
try {
|
|
83
140
|
plan = await planWithKimi(text, {
|
|
84
|
-
availableTools: ["open_cards", "open_brand_doc", "show_tools", "mcp_config", "list_episodes", "search_episodes", "get_episode", "home"],
|
|
141
|
+
availableTools: ["create_talking_cards", "open_cards", "open_brand_doc", "show_tools", "mcp_config", "list_episodes", "search_episodes", "get_episode", "home"],
|
|
85
142
|
});
|
|
86
143
|
} catch (err) {
|
|
87
144
|
process.stdout.write(`
|
|
@@ -107,6 +164,8 @@ ${DIM}${err?.message ?? err}${RESET}
|
|
|
107
164
|
switch (action) {
|
|
108
165
|
case "exit":
|
|
109
166
|
return "exit";
|
|
167
|
+
case "create_talking_cards":
|
|
168
|
+
return createTalkingCards({ focus: args.focus || text, count: args.count || 3 });
|
|
110
169
|
case "open_cards":
|
|
111
170
|
return actions.cards({ path: "/carousels/generate", focus: args.focus });
|
|
112
171
|
case "open_brand_doc":
|
package/src/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deviceLogin } from "./auth/device-flow.mjs";
|
|
2
|
-
import { chat, handleNaturalLanguage } from "./chat/natural-language.mjs";
|
|
2
|
+
import { chat, createTalkingCards, handleNaturalLanguage } from "./chat/natural-language.mjs";
|
|
3
3
|
import { loadToken, saveToken, clearToken, credentialsPath } from "./auth/token-store.mjs";
|
|
4
4
|
import { APP_ORIGIN } from "./config.mjs";
|
|
5
5
|
import { openUrl } from "./system/open-url.mjs";
|
|
@@ -21,7 +21,8 @@ ${BOLD}mangomagic${RESET} ${DIM}— sign into MangoMagic and bring your account
|
|
|
21
21
|
${GOLD}mangomagic home${RESET} Show fast wins for creating value right now.
|
|
22
22
|
${GOLD}mangomagic chat${RESET} Talk to MangoMagic in natural language.
|
|
23
23
|
${GOLD}mangomagic tools${RESET} Show available MCP tools and next workflows.
|
|
24
|
-
${GOLD}mangomagic cards${RESET}
|
|
24
|
+
${GOLD}mangomagic cards "idea"${RESET} Create talking cards in the terminal.
|
|
25
|
+
${GOLD}mangomagic open cards${RESET} Open the Talking Cards workspace in a browser.
|
|
25
26
|
${GOLD}mangomagic splash${RESET} Show the MangoMagic splash once. Add --anim or --loop for motion.
|
|
26
27
|
${GOLD}mangomagic mcp${RESET} Run as a stdio MCP server (for Claude Desktop / Cursor).
|
|
27
28
|
${GOLD}mangomagic mcp-config${RESET} Print MCP client config for copy-paste.
|
|
@@ -148,6 +149,25 @@ function cards({ openInBrowser = process.env.MANGOMAGIC_CLI_NO_OPEN !== "1", pat
|
|
|
148
149
|
if (focus) process.stdout.write(`${DIM}Idea: ${focus}${RESET}\n`);
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
function parseCount(argv, fallback = 3) {
|
|
153
|
+
const flagIndex = argv.findIndex((v) => v === "--count" || v === "-n");
|
|
154
|
+
if (flagIndex >= 0 && argv[flagIndex + 1]) return Math.max(1, Math.min(Number(argv[flagIndex + 1]), 10));
|
|
155
|
+
const inline = argv.find((v) => /^--count=/.test(v));
|
|
156
|
+
if (inline) return Math.max(1, Math.min(Number(inline.split("=")[1]), 10));
|
|
157
|
+
return fallback;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function stripFlags(argv) {
|
|
161
|
+
const out = [];
|
|
162
|
+
for (let i = 0; i < argv.length; i++) {
|
|
163
|
+
const v = argv[i];
|
|
164
|
+
if (v === "--count" || v === "-n") { i++; continue; }
|
|
165
|
+
if (v.startsWith("--count=")) continue;
|
|
166
|
+
out.push(v);
|
|
167
|
+
}
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
|
|
151
171
|
async function mcpServer() {
|
|
152
172
|
// Lazy-load so users who only run `login` don't pay the import cost.
|
|
153
173
|
const { startMcpServer } = await import("./mcp/server.mjs");
|
|
@@ -180,7 +200,10 @@ export async function run(argv) {
|
|
|
180
200
|
case "chat": return chat(actions());
|
|
181
201
|
case "ask": return handleNaturalLanguage(argv.slice(1).join(" "), actions());
|
|
182
202
|
case "tools": return tools({ json: argv.includes("--json") });
|
|
183
|
-
case "cards": return
|
|
203
|
+
case "cards": return createTalkingCards({ focus: stripFlags(argv.slice(1)).join(" "), count: parseCount(argv.slice(1)) });
|
|
204
|
+
case "open":
|
|
205
|
+
if (argv[1] === "cards" || argv[1] === "carousels") return cards({ openInBrowser: !argv.includes("--no-open") && process.env.MANGOMAGIC_CLI_NO_OPEN !== "1" });
|
|
206
|
+
return openPath(`/${argv[1] || ""}`);
|
|
184
207
|
case "splash": return playSplash({ mode: argv.includes("--loop") ? "loop" : argv.includes("--anim") ? "anim" : "static" });
|
|
185
208
|
case "mcp": return mcpServer();
|
|
186
209
|
case "mcp-config": return mcpConfig();
|
package/src/tools/catalog.mjs
CHANGED
|
@@ -42,10 +42,9 @@ export const MCP_TOOL_CATALOG = [
|
|
|
42
42
|
|
|
43
43
|
export const QUICK_WINS = [
|
|
44
44
|
{
|
|
45
|
-
command: `${COMMAND_PREFIX} cards`,
|
|
45
|
+
command: `${COMMAND_PREFIX} cards "why founder-led sales stalls" --count 3`,
|
|
46
46
|
title: "Create Talking Cards",
|
|
47
|
-
description: "
|
|
48
|
-
url: `${APP_ORIGIN}/carousels`,
|
|
47
|
+
description: "Generate LinkedIn-ready talking cards from the terminal and save them to your library.",
|
|
49
48
|
status: "ready",
|
|
50
49
|
},
|
|
51
50
|
{
|
|
@@ -71,7 +70,7 @@ export const QUICK_WINS = [
|
|
|
71
70
|
export const NEXT_WORKFLOWS = [
|
|
72
71
|
{
|
|
73
72
|
name: "create_talking_cards",
|
|
74
|
-
description: "Generate a batch of branded cards from a short idea, saved to the user's carousel library.",
|
|
73
|
+
description: "Generate a batch of branded talking cards from a short idea, saved to the user's carousel library.",
|
|
75
74
|
},
|
|
76
75
|
{
|
|
77
76
|
name: "sync_linkedin_brand_context",
|