@conceptcraft/mindframes 0.1.2 → 0.1.4
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/dist/index.js +1110 -499
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,84 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// src/lib/brand.ts
|
|
9
|
-
import path from "path";
|
|
10
|
-
var BRANDS = {
|
|
11
|
-
conceptcraft: {
|
|
12
|
-
id: "conceptcraft",
|
|
13
|
-
name: "conceptcraft",
|
|
14
|
-
displayName: "Conceptcraft",
|
|
15
|
-
description: "CLI tool for Conceptcraft presentation generation",
|
|
16
|
-
commands: ["cc", "conceptcraft"],
|
|
17
|
-
apiUrl: "https://conceptcraft.ai",
|
|
18
|
-
docsUrl: "https://docs.conceptcraft.ai"
|
|
19
|
-
},
|
|
20
|
-
mindframes: {
|
|
21
|
-
id: "mindframes",
|
|
22
|
-
name: "mindframes",
|
|
23
|
-
displayName: "Mindframes",
|
|
24
|
-
description: "CLI tool for Mindframes presentation generation",
|
|
25
|
-
commands: ["mf", "mindframes"],
|
|
26
|
-
apiUrl: "https://mindframes.app",
|
|
27
|
-
docsUrl: "https://docs.mindframes.app"
|
|
28
|
-
}
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
29
7
|
};
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
mf: "mindframes",
|
|
34
|
-
mindframes: "mindframes"
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
35
11
|
};
|
|
36
|
-
function detectBrand() {
|
|
37
|
-
const binaryPath = process.argv[1] || "";
|
|
38
|
-
const binaryName = path.basename(binaryPath).replace(/\.(js|ts)$/, "");
|
|
39
|
-
const brandId = COMMAND_TO_BRAND[binaryName];
|
|
40
|
-
if (brandId) {
|
|
41
|
-
return BRANDS[brandId];
|
|
42
|
-
}
|
|
43
|
-
for (const [cmd2, id] of Object.entries(COMMAND_TO_BRAND)) {
|
|
44
|
-
if (binaryPath.includes(`/${cmd2}`) || binaryPath.includes(`\\${cmd2}`)) {
|
|
45
|
-
return BRANDS[id];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return BRANDS.mindframes;
|
|
49
|
-
}
|
|
50
|
-
var brand = detectBrand();
|
|
51
|
-
|
|
52
|
-
// src/commands/config.ts
|
|
53
|
-
import { Command } from "commander";
|
|
54
|
-
import chalk3 from "chalk";
|
|
55
|
-
import { input, password, confirm, select } from "@inquirer/prompts";
|
|
56
|
-
import ora from "ora";
|
|
57
12
|
|
|
58
13
|
// src/lib/config.ts
|
|
59
14
|
import Conf from "conf";
|
|
60
|
-
var DEFAULT_API_URL = "https://www.mindframes.app";
|
|
61
|
-
var schema = {
|
|
62
|
-
apiKey: {
|
|
63
|
-
type: "string"
|
|
64
|
-
},
|
|
65
|
-
apiUrl: {
|
|
66
|
-
type: "string",
|
|
67
|
-
default: DEFAULT_API_URL
|
|
68
|
-
},
|
|
69
|
-
defaultTeamId: {
|
|
70
|
-
type: "string"
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
var config = new Conf({
|
|
74
|
-
projectName: "mindframes",
|
|
75
|
-
schema
|
|
76
|
-
});
|
|
77
15
|
function getConfig() {
|
|
78
16
|
return {
|
|
79
17
|
apiKey: getApiKey(),
|
|
80
18
|
apiUrl: getApiUrl(),
|
|
81
|
-
defaultTeamId: config.get("defaultTeamId")
|
|
19
|
+
defaultTeamId: config.get("defaultTeamId"),
|
|
20
|
+
accessToken: config.get("accessToken"),
|
|
21
|
+
refreshToken: config.get("refreshToken"),
|
|
22
|
+
tokenExpiresAt: config.get("tokenExpiresAt"),
|
|
23
|
+
clientId: config.get("clientId"),
|
|
24
|
+
clientSecret: config.get("clientSecret")
|
|
82
25
|
};
|
|
83
26
|
}
|
|
84
27
|
function getApiKey() {
|
|
@@ -116,38 +59,329 @@ function getConfigPath() {
|
|
|
116
59
|
function hasApiKey() {
|
|
117
60
|
return !!getApiKey();
|
|
118
61
|
}
|
|
62
|
+
function getAccessToken() {
|
|
63
|
+
return config.get("accessToken");
|
|
64
|
+
}
|
|
65
|
+
function getRefreshToken() {
|
|
66
|
+
return config.get("refreshToken");
|
|
67
|
+
}
|
|
68
|
+
function setOAuthTokens(accessToken, refreshToken, expiresIn) {
|
|
69
|
+
config.set("accessToken", accessToken);
|
|
70
|
+
config.set("refreshToken", refreshToken);
|
|
71
|
+
config.set("tokenExpiresAt", Date.now() + (expiresIn - 60) * 1e3);
|
|
72
|
+
}
|
|
73
|
+
function clearOAuthTokens() {
|
|
74
|
+
config.delete("accessToken");
|
|
75
|
+
config.delete("refreshToken");
|
|
76
|
+
config.delete("tokenExpiresAt");
|
|
77
|
+
}
|
|
78
|
+
function isTokenExpired() {
|
|
79
|
+
const expiresAt = config.get("tokenExpiresAt");
|
|
80
|
+
if (!expiresAt) return true;
|
|
81
|
+
return Date.now() >= expiresAt;
|
|
82
|
+
}
|
|
83
|
+
function hasOAuthTokens() {
|
|
84
|
+
return !!config.get("accessToken") && !!config.get("refreshToken");
|
|
85
|
+
}
|
|
86
|
+
function getClientId() {
|
|
87
|
+
return config.get("clientId");
|
|
88
|
+
}
|
|
89
|
+
function getClientSecret() {
|
|
90
|
+
return config.get("clientSecret");
|
|
91
|
+
}
|
|
92
|
+
function setOAuthClient(clientId, clientSecret) {
|
|
93
|
+
config.set("clientId", clientId);
|
|
94
|
+
config.set("clientSecret", clientSecret);
|
|
95
|
+
}
|
|
96
|
+
var DEFAULT_API_URL, schema, config;
|
|
97
|
+
var init_config = __esm({
|
|
98
|
+
"src/lib/config.ts"() {
|
|
99
|
+
"use strict";
|
|
100
|
+
DEFAULT_API_URL = "https://www.mindframes.app";
|
|
101
|
+
schema = {
|
|
102
|
+
apiKey: {
|
|
103
|
+
type: "string"
|
|
104
|
+
},
|
|
105
|
+
apiUrl: {
|
|
106
|
+
type: "string",
|
|
107
|
+
default: DEFAULT_API_URL
|
|
108
|
+
},
|
|
109
|
+
defaultTeamId: {
|
|
110
|
+
type: "string"
|
|
111
|
+
},
|
|
112
|
+
// OAuth tokens (preferred over API key)
|
|
113
|
+
accessToken: {
|
|
114
|
+
type: "string"
|
|
115
|
+
},
|
|
116
|
+
refreshToken: {
|
|
117
|
+
type: "string"
|
|
118
|
+
},
|
|
119
|
+
tokenExpiresAt: {
|
|
120
|
+
type: "number"
|
|
121
|
+
},
|
|
122
|
+
// OAuth client registration (cached per-server)
|
|
123
|
+
clientId: {
|
|
124
|
+
type: "string"
|
|
125
|
+
},
|
|
126
|
+
clientSecret: {
|
|
127
|
+
type: "string"
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
config = new Conf({
|
|
131
|
+
projectName: "mindframes",
|
|
132
|
+
schema
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
119
136
|
|
|
120
|
-
// src/lib/
|
|
137
|
+
// src/lib/output.ts
|
|
121
138
|
import chalk from "chalk";
|
|
139
|
+
import Table from "cli-table3";
|
|
140
|
+
function isTTY() {
|
|
141
|
+
return process.stdout.isTTY ?? false;
|
|
142
|
+
}
|
|
143
|
+
function success(message, format = "human") {
|
|
144
|
+
if (format === "quiet") return;
|
|
145
|
+
if (format === "json") return;
|
|
146
|
+
console.log(chalk.green("\u2713"), message);
|
|
147
|
+
}
|
|
148
|
+
function error(message, format = "human") {
|
|
149
|
+
if (format === "quiet") return;
|
|
150
|
+
if (format === "json") {
|
|
151
|
+
console.error(JSON.stringify({ error: message }));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.error(chalk.red("\u2717"), message);
|
|
155
|
+
}
|
|
156
|
+
function warn(message, format = "human") {
|
|
157
|
+
if (format === "quiet") return;
|
|
158
|
+
if (format === "json") return;
|
|
159
|
+
console.warn(chalk.yellow("\u26A0"), message);
|
|
160
|
+
}
|
|
161
|
+
function info(message, format = "human") {
|
|
162
|
+
if (format === "quiet") return;
|
|
163
|
+
if (format === "json") return;
|
|
164
|
+
console.log(chalk.blue("\u2139"), message);
|
|
165
|
+
}
|
|
166
|
+
function buildViewUrl(slug, language = "en") {
|
|
167
|
+
if (!slug) return "N/A";
|
|
168
|
+
const apiUrl = getApiUrl();
|
|
169
|
+
return `${apiUrl}/${language}/view/presentations/${slug}`;
|
|
170
|
+
}
|
|
171
|
+
function formatPresentationTable(presentations, options = {}) {
|
|
172
|
+
const { showLinks = false, language = "en" } = options;
|
|
173
|
+
const truncate = (str, len) => str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
|
|
174
|
+
const shortDateTime = (dateStr) => {
|
|
175
|
+
try {
|
|
176
|
+
const d = new Date(dateStr);
|
|
177
|
+
return d.toLocaleString("en-US", {
|
|
178
|
+
month: "short",
|
|
179
|
+
day: "numeric",
|
|
180
|
+
hour: "numeric",
|
|
181
|
+
minute: "2-digit",
|
|
182
|
+
hour12: true
|
|
183
|
+
}).replace(" at ", ", ");
|
|
184
|
+
} catch {
|
|
185
|
+
return "-";
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
if (showLinks) {
|
|
189
|
+
const table2 = new Table({
|
|
190
|
+
head: [
|
|
191
|
+
chalk.cyan("Slug"),
|
|
192
|
+
chalk.cyan("Title"),
|
|
193
|
+
chalk.cyan("Slides"),
|
|
194
|
+
chalk.cyan("Created"),
|
|
195
|
+
chalk.cyan("URL")
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
for (const p of presentations) {
|
|
199
|
+
table2.push([
|
|
200
|
+
truncate(p.slug || p.id.slice(0, 8), 30),
|
|
201
|
+
truncate(p.title || "Untitled", 22),
|
|
202
|
+
String(p.numberOfSlides || "-"),
|
|
203
|
+
shortDateTime(p.createdAt),
|
|
204
|
+
buildViewUrl(p.slug, language)
|
|
205
|
+
]);
|
|
206
|
+
}
|
|
207
|
+
return table2.toString();
|
|
208
|
+
}
|
|
209
|
+
const table = new Table({
|
|
210
|
+
head: [
|
|
211
|
+
chalk.cyan("Slug"),
|
|
212
|
+
chalk.cyan("Title"),
|
|
213
|
+
chalk.cyan("Slides"),
|
|
214
|
+
chalk.cyan("Mode"),
|
|
215
|
+
chalk.cyan("Created")
|
|
216
|
+
],
|
|
217
|
+
colWidths: [32, 28, 8, 11, 18]
|
|
218
|
+
});
|
|
219
|
+
for (const p of presentations) {
|
|
220
|
+
table.push([
|
|
221
|
+
p.slug || p.id.slice(0, 8),
|
|
222
|
+
p.title || "Untitled",
|
|
223
|
+
String(p.numberOfSlides || "-"),
|
|
224
|
+
p.mode || "-",
|
|
225
|
+
shortDateTime(p.createdAt)
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
return table.toString();
|
|
229
|
+
}
|
|
230
|
+
function formatPresentationIds(presentations) {
|
|
231
|
+
return presentations.map((p) => p.slug || p.id).join("\n");
|
|
232
|
+
}
|
|
233
|
+
function formatBrandingTable(brandings) {
|
|
234
|
+
const sorted = [...brandings].sort((a, b) => {
|
|
235
|
+
if (a.isDefault && !b.isDefault) return -1;
|
|
236
|
+
if (!a.isDefault && b.isDefault) return 1;
|
|
237
|
+
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
238
|
+
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
239
|
+
return dateB - dateA;
|
|
240
|
+
});
|
|
241
|
+
const formatUrl = (url) => {
|
|
242
|
+
if (!url) return "-";
|
|
243
|
+
try {
|
|
244
|
+
const u = new URL(url);
|
|
245
|
+
return u.href;
|
|
246
|
+
} catch {
|
|
247
|
+
return url.startsWith("http") ? url : `https://${url}`;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const table = new Table({
|
|
251
|
+
head: [
|
|
252
|
+
chalk.cyan("Name"),
|
|
253
|
+
chalk.cyan("Source"),
|
|
254
|
+
chalk.cyan("Color"),
|
|
255
|
+
chalk.cyan("Logo"),
|
|
256
|
+
chalk.cyan("Default"),
|
|
257
|
+
chalk.cyan("ID")
|
|
258
|
+
]
|
|
259
|
+
});
|
|
260
|
+
for (const b of sorted) {
|
|
261
|
+
const colorDisplay = b.primaryColor ? `${chalk.bgHex(b.primaryColor)(" ")} ${b.primaryColor}` : "-";
|
|
262
|
+
table.push([
|
|
263
|
+
b.name,
|
|
264
|
+
formatUrl(b.sourceUrl),
|
|
265
|
+
colorDisplay,
|
|
266
|
+
b.logoUrl ? chalk.green("\u2713") : chalk.gray("\u2717"),
|
|
267
|
+
b.isDefault ? chalk.green("\u2605") : "",
|
|
268
|
+
b.id
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
return table.toString();
|
|
272
|
+
}
|
|
273
|
+
function formatDate(dateStr) {
|
|
274
|
+
try {
|
|
275
|
+
const date = new Date(dateStr);
|
|
276
|
+
return date.toLocaleString();
|
|
277
|
+
} catch {
|
|
278
|
+
return dateStr;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function header(text) {
|
|
282
|
+
console.log();
|
|
283
|
+
console.log(chalk.bold(text));
|
|
284
|
+
console.log(chalk.gray("\u2500".repeat(text.length)));
|
|
285
|
+
}
|
|
286
|
+
function keyValue(key, value) {
|
|
287
|
+
console.log(` ${chalk.gray(key + ":")} ${value ?? "-"}`);
|
|
288
|
+
}
|
|
289
|
+
function progressBar(current, total, width = 30, showPercentage = true) {
|
|
290
|
+
const percentage = Math.min(100, Math.round(current / total * 100));
|
|
291
|
+
const filled = Math.round(width * current / total);
|
|
292
|
+
const empty = width - filled;
|
|
293
|
+
const bar = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
|
|
294
|
+
return showPercentage ? `[${bar}] ${percentage}%` : `[${bar}]`;
|
|
295
|
+
}
|
|
296
|
+
var init_output = __esm({
|
|
297
|
+
"src/lib/output.ts"() {
|
|
298
|
+
"use strict";
|
|
299
|
+
init_config();
|
|
300
|
+
}
|
|
301
|
+
});
|
|
122
302
|
|
|
123
303
|
// src/types/index.ts
|
|
124
|
-
var EXIT_CODES
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
304
|
+
var EXIT_CODES;
|
|
305
|
+
var init_types = __esm({
|
|
306
|
+
"src/types/index.ts"() {
|
|
307
|
+
"use strict";
|
|
308
|
+
EXIT_CODES = {
|
|
309
|
+
SUCCESS: 0,
|
|
310
|
+
GENERAL_ERROR: 1,
|
|
311
|
+
AUTH_ERROR: 2,
|
|
312
|
+
NOT_FOUND: 3,
|
|
313
|
+
RATE_LIMIT: 4,
|
|
314
|
+
NETWORK_ERROR: 5,
|
|
315
|
+
INVALID_INPUT: 6
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
});
|
|
133
319
|
|
|
134
320
|
// src/lib/auth.ts
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
321
|
+
import chalk2 from "chalk";
|
|
322
|
+
async function refreshAccessToken() {
|
|
323
|
+
const refreshToken = getRefreshToken();
|
|
324
|
+
const clientId = getClientId();
|
|
325
|
+
const apiUrl = getApiUrl();
|
|
326
|
+
if (!refreshToken || !clientId) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
const metaResponse = await fetch(`${apiUrl}/.well-known/oauth-authorization-server`);
|
|
331
|
+
if (!metaResponse.ok) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const metadata = await metaResponse.json();
|
|
335
|
+
const params = new URLSearchParams({
|
|
336
|
+
grant_type: "refresh_token",
|
|
337
|
+
refresh_token: refreshToken,
|
|
338
|
+
client_id: clientId
|
|
339
|
+
});
|
|
340
|
+
const response = await fetch(metadata.token_endpoint, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
343
|
+
body: params.toString()
|
|
344
|
+
});
|
|
345
|
+
if (!response.ok) {
|
|
346
|
+
clearOAuthTokens();
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const tokens = await response.json();
|
|
350
|
+
setOAuthTokens(tokens.access_token, tokens.refresh_token, tokens.expires_in);
|
|
351
|
+
return tokens.access_token;
|
|
352
|
+
} catch {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function getValidAccessToken() {
|
|
357
|
+
if (!hasOAuthTokens()) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
if (isTokenExpired()) {
|
|
361
|
+
return refreshAccessToken();
|
|
362
|
+
}
|
|
363
|
+
return getAccessToken() || null;
|
|
364
|
+
}
|
|
365
|
+
function hasAuth() {
|
|
366
|
+
return hasOAuthTokens() || hasApiKey();
|
|
367
|
+
}
|
|
368
|
+
async function requireAuth() {
|
|
369
|
+
if (!hasAuth()) {
|
|
370
|
+
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
371
|
+
try {
|
|
372
|
+
const shouldLogin = await confirm4({
|
|
373
|
+
message: chalk2.red("Not authenticated.") + " Log in now?",
|
|
374
|
+
default: true
|
|
375
|
+
});
|
|
376
|
+
if (!shouldLogin) {
|
|
377
|
+
console.log(chalk2.gray("\nTip: You can also set CC_MINDFRAMES_API_KEY environment variable."));
|
|
378
|
+
process.exit(EXIT_CODES.AUTH_ERROR);
|
|
379
|
+
}
|
|
380
|
+
const { runLoginFlow: runLoginFlow2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
381
|
+
await runLoginFlow2({ browser: true });
|
|
382
|
+
} catch {
|
|
383
|
+
process.exit(EXIT_CODES.AUTH_ERROR);
|
|
384
|
+
}
|
|
151
385
|
}
|
|
152
386
|
}
|
|
153
387
|
function maskApiKey(key) {
|
|
@@ -165,33 +399,43 @@ function isValidApiKeyFormat(key) {
|
|
|
165
399
|
const validPrefixes = ["cc_slides_", "mcp_", "cc_sk_", "sk_"];
|
|
166
400
|
return validPrefixes.some((prefix) => key.startsWith(prefix));
|
|
167
401
|
}
|
|
402
|
+
var init_auth = __esm({
|
|
403
|
+
"src/lib/auth.ts"() {
|
|
404
|
+
"use strict";
|
|
405
|
+
init_config();
|
|
406
|
+
init_types();
|
|
407
|
+
}
|
|
408
|
+
});
|
|
168
409
|
|
|
169
410
|
// src/lib/api.ts
|
|
170
411
|
import { readFileSync, statSync } from "fs";
|
|
171
412
|
import { basename } from "path";
|
|
172
413
|
import { randomUUID } from "crypto";
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.exitCode = exitCode;
|
|
178
|
-
this.name = "ApiError";
|
|
414
|
+
async function getAuthHeaders() {
|
|
415
|
+
const accessToken = await getValidAccessToken();
|
|
416
|
+
if (accessToken) {
|
|
417
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
179
418
|
}
|
|
180
|
-
};
|
|
181
|
-
async function request(endpoint, options = {}) {
|
|
182
419
|
const apiKey = getApiKey();
|
|
420
|
+
if (apiKey) {
|
|
421
|
+
return { "x-api-key": apiKey };
|
|
422
|
+
}
|
|
423
|
+
return {};
|
|
424
|
+
}
|
|
425
|
+
async function request(endpoint, options = {}) {
|
|
183
426
|
const apiUrl = getApiUrl();
|
|
184
|
-
if (!
|
|
427
|
+
if (!hasAuth()) {
|
|
185
428
|
throw new ApiError(
|
|
186
|
-
"
|
|
429
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
187
430
|
401,
|
|
188
431
|
2
|
|
189
432
|
// AUTH_ERROR
|
|
190
433
|
);
|
|
191
434
|
}
|
|
435
|
+
const authHeaders = await getAuthHeaders();
|
|
192
436
|
const url = `${apiUrl}${endpoint}`;
|
|
193
437
|
const headers = {
|
|
194
|
-
|
|
438
|
+
...authHeaders,
|
|
195
439
|
...options.headers
|
|
196
440
|
};
|
|
197
441
|
if (options.body && !options.stream) {
|
|
@@ -248,15 +492,15 @@ async function request(endpoint, options = {}) {
|
|
|
248
492
|
return response.text();
|
|
249
493
|
}
|
|
250
494
|
async function streamRequest(endpoint, body) {
|
|
251
|
-
const apiKey = getApiKey();
|
|
252
495
|
const apiUrl = getApiUrl();
|
|
253
|
-
if (!
|
|
496
|
+
if (!hasAuth()) {
|
|
254
497
|
throw new ApiError(
|
|
255
|
-
"
|
|
498
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
256
499
|
401,
|
|
257
500
|
2
|
|
258
501
|
);
|
|
259
502
|
}
|
|
503
|
+
const authHeaders = await getAuthHeaders();
|
|
260
504
|
const url = `${apiUrl}${endpoint}`;
|
|
261
505
|
let response;
|
|
262
506
|
try {
|
|
@@ -264,7 +508,7 @@ async function streamRequest(endpoint, body) {
|
|
|
264
508
|
method: "POST",
|
|
265
509
|
headers: {
|
|
266
510
|
"Content-Type": "application/json",
|
|
267
|
-
|
|
511
|
+
...authHeaders
|
|
268
512
|
},
|
|
269
513
|
body: JSON.stringify(body)
|
|
270
514
|
});
|
|
@@ -324,15 +568,15 @@ function getMimeType(filePath) {
|
|
|
324
568
|
return mimeTypes[ext || ""] || "application/octet-stream";
|
|
325
569
|
}
|
|
326
570
|
async function uploadFile(filePath) {
|
|
327
|
-
const apiKey = getApiKey();
|
|
328
571
|
const apiUrl = getApiUrl();
|
|
329
|
-
if (!
|
|
572
|
+
if (!hasAuth()) {
|
|
330
573
|
throw new ApiError(
|
|
331
|
-
"
|
|
574
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
332
575
|
401,
|
|
333
576
|
2
|
|
334
577
|
);
|
|
335
578
|
}
|
|
579
|
+
const authHeaders = await getAuthHeaders();
|
|
336
580
|
const stat = statSync(filePath);
|
|
337
581
|
const fileName = basename(filePath);
|
|
338
582
|
const mimeType = getMimeType(filePath);
|
|
@@ -340,7 +584,7 @@ async function uploadFile(filePath) {
|
|
|
340
584
|
method: "POST",
|
|
341
585
|
headers: {
|
|
342
586
|
"Content-Type": "application/json",
|
|
343
|
-
|
|
587
|
+
...authHeaders
|
|
344
588
|
},
|
|
345
589
|
body: JSON.stringify({
|
|
346
590
|
fileMetadata: {
|
|
@@ -461,6 +705,9 @@ async function createPresentation(options) {
|
|
|
461
705
|
if (options.teamId) {
|
|
462
706
|
body.teamId = options.teamId;
|
|
463
707
|
}
|
|
708
|
+
if (options.theme) {
|
|
709
|
+
body.theme = options.theme;
|
|
710
|
+
}
|
|
464
711
|
return streamRequest("/api/slides/skills/create-presentation", body);
|
|
465
712
|
}
|
|
466
713
|
async function listPresentations(teamId, limit = 20) {
|
|
@@ -481,20 +728,20 @@ async function deletePresentation(slugOrId) {
|
|
|
481
728
|
await request(`/api/cli/presentation/${slugOrId}`, { method: "DELETE" });
|
|
482
729
|
}
|
|
483
730
|
async function exportPresentation(presentationId, options = {}) {
|
|
484
|
-
const apiKey = getApiKey();
|
|
485
731
|
const apiUrl = getApiUrl();
|
|
486
|
-
if (!
|
|
732
|
+
if (!hasAuth()) {
|
|
487
733
|
throw new ApiError(
|
|
488
|
-
"
|
|
734
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
489
735
|
401,
|
|
490
736
|
2
|
|
491
737
|
);
|
|
492
738
|
}
|
|
739
|
+
const authHeaders = await getAuthHeaders();
|
|
493
740
|
const response = await fetch(`${apiUrl}/api/presentations/export`, {
|
|
494
741
|
method: "POST",
|
|
495
742
|
headers: {
|
|
496
743
|
"Content-Type": "application/json",
|
|
497
|
-
|
|
744
|
+
...authHeaders
|
|
498
745
|
},
|
|
499
746
|
body: JSON.stringify({
|
|
500
747
|
presentationId,
|
|
@@ -515,15 +762,15 @@ async function exportPresentation(presentationId, options = {}) {
|
|
|
515
762
|
return response.arrayBuffer();
|
|
516
763
|
}
|
|
517
764
|
async function importPresentation(fileBuffer, fileName, options = {}) {
|
|
518
|
-
const apiKey = getApiKey();
|
|
519
765
|
const apiUrl = getApiUrl();
|
|
520
|
-
if (!
|
|
766
|
+
if (!hasAuth()) {
|
|
521
767
|
throw new ApiError(
|
|
522
|
-
"
|
|
768
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
523
769
|
401,
|
|
524
770
|
2
|
|
525
771
|
);
|
|
526
772
|
}
|
|
773
|
+
const authHeaders = await getAuthHeaders();
|
|
527
774
|
const formData = new FormData();
|
|
528
775
|
const blob = new Blob([fileBuffer], { type: "application/zip" });
|
|
529
776
|
formData.append("file", blob, fileName);
|
|
@@ -536,9 +783,7 @@ async function importPresentation(fileBuffer, fileName, options = {}) {
|
|
|
536
783
|
);
|
|
537
784
|
const response = await fetch(`${apiUrl}/api/presentations/import`, {
|
|
538
785
|
method: "POST",
|
|
539
|
-
headers:
|
|
540
|
-
"x-api-key": apiKey
|
|
541
|
-
},
|
|
786
|
+
headers: authHeaders,
|
|
542
787
|
body: formData
|
|
543
788
|
});
|
|
544
789
|
const result = await response.json();
|
|
@@ -570,22 +815,22 @@ async function getFeatureFlags() {
|
|
|
570
815
|
return request("/api/cli/features");
|
|
571
816
|
}
|
|
572
817
|
async function generateBlog(presentationId, documentCreatedAt, options = {}) {
|
|
573
|
-
const apiKey = getApiKey();
|
|
574
818
|
const apiUrl = getApiUrl();
|
|
575
|
-
if (!
|
|
819
|
+
if (!hasAuth()) {
|
|
576
820
|
throw new ApiError(
|
|
577
|
-
"
|
|
821
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
578
822
|
401,
|
|
579
823
|
2
|
|
580
824
|
);
|
|
581
825
|
}
|
|
826
|
+
const authHeaders = await getAuthHeaders();
|
|
582
827
|
const response = await fetch(
|
|
583
828
|
`${apiUrl}/api/presentations/${presentationId}/generate-blog`,
|
|
584
829
|
{
|
|
585
830
|
method: "POST",
|
|
586
831
|
headers: {
|
|
587
832
|
"Content-Type": "application/json",
|
|
588
|
-
|
|
833
|
+
...authHeaders
|
|
589
834
|
},
|
|
590
835
|
body: JSON.stringify({
|
|
591
836
|
documentCreatedAt,
|
|
@@ -644,15 +889,25 @@ async function validateGeneration(mode, slideCount, teamId) {
|
|
|
644
889
|
}
|
|
645
890
|
return limits;
|
|
646
891
|
}
|
|
892
|
+
var ApiError;
|
|
893
|
+
var init_api = __esm({
|
|
894
|
+
"src/lib/api.ts"() {
|
|
895
|
+
"use strict";
|
|
896
|
+
init_config();
|
|
897
|
+
init_auth();
|
|
898
|
+
ApiError = class extends Error {
|
|
899
|
+
constructor(message, statusCode, exitCode = 1) {
|
|
900
|
+
super(message);
|
|
901
|
+
this.statusCode = statusCode;
|
|
902
|
+
this.exitCode = exitCode;
|
|
903
|
+
this.name = "ApiError";
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
});
|
|
647
908
|
|
|
648
909
|
// src/lib/feature-cache.ts
|
|
649
910
|
import Conf2 from "conf";
|
|
650
|
-
var cache = new Conf2({
|
|
651
|
-
projectName: "conceptcraft",
|
|
652
|
-
configName: "feature-cache"
|
|
653
|
-
});
|
|
654
|
-
var FRESH_TTL = 60 * 60 * 1e3;
|
|
655
|
-
var STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
656
911
|
function hashApiKey(key) {
|
|
657
912
|
let hash = 0;
|
|
658
913
|
for (let i = 0; i < key.length; i++) {
|
|
@@ -698,173 +953,410 @@ function invalidateCache() {
|
|
|
698
953
|
function getCachePath() {
|
|
699
954
|
return cache.path;
|
|
700
955
|
}
|
|
956
|
+
var cache, FRESH_TTL, STALE_TTL;
|
|
957
|
+
var init_feature_cache = __esm({
|
|
958
|
+
"src/lib/feature-cache.ts"() {
|
|
959
|
+
"use strict";
|
|
960
|
+
init_api();
|
|
961
|
+
init_config();
|
|
962
|
+
cache = new Conf2({
|
|
963
|
+
projectName: "conceptcraft",
|
|
964
|
+
configName: "feature-cache"
|
|
965
|
+
});
|
|
966
|
+
FRESH_TTL = 60 * 60 * 1e3;
|
|
967
|
+
STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
968
|
+
}
|
|
969
|
+
});
|
|
701
970
|
|
|
702
|
-
// src/
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
971
|
+
// src/commands/login.ts
|
|
972
|
+
var login_exports = {};
|
|
973
|
+
__export(login_exports, {
|
|
974
|
+
loginCommand: () => loginCommand,
|
|
975
|
+
runLoginFlow: () => runLoginFlow
|
|
976
|
+
});
|
|
977
|
+
import { Command } from "commander";
|
|
978
|
+
import chalk3 from "chalk";
|
|
979
|
+
import ora from "ora";
|
|
980
|
+
import http from "http";
|
|
981
|
+
import { randomBytes, createHash } from "crypto";
|
|
982
|
+
import open from "open";
|
|
983
|
+
function generateCodeVerifier() {
|
|
984
|
+
return randomBytes(32).toString("base64url");
|
|
707
985
|
}
|
|
708
|
-
function
|
|
709
|
-
|
|
710
|
-
if (format === "json") return;
|
|
711
|
-
console.log(chalk2.green("\u2713"), message);
|
|
986
|
+
function generateCodeChallenge(verifier) {
|
|
987
|
+
return createHash("sha256").update(verifier).digest("base64url");
|
|
712
988
|
}
|
|
713
|
-
function
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
989
|
+
function generateState() {
|
|
990
|
+
return randomBytes(16).toString("hex");
|
|
991
|
+
}
|
|
992
|
+
async function findAvailablePort(start, end) {
|
|
993
|
+
for (let port = start; port <= end; port++) {
|
|
994
|
+
try {
|
|
995
|
+
await new Promise((resolve4, reject) => {
|
|
996
|
+
const server = http.createServer();
|
|
997
|
+
server.listen(port, () => {
|
|
998
|
+
server.close(() => resolve4());
|
|
999
|
+
});
|
|
1000
|
+
server.on("error", reject);
|
|
1001
|
+
});
|
|
1002
|
+
return port;
|
|
1003
|
+
} catch {
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
718
1006
|
}
|
|
719
|
-
|
|
1007
|
+
throw new Error(`No available port found between ${start} and ${end}`);
|
|
720
1008
|
}
|
|
721
|
-
function
|
|
722
|
-
|
|
723
|
-
if (
|
|
724
|
-
|
|
1009
|
+
async function getAuthServerMetadata(apiUrl) {
|
|
1010
|
+
const response = await fetch(`${apiUrl}/.well-known/oauth-authorization-server`);
|
|
1011
|
+
if (!response.ok) {
|
|
1012
|
+
throw new Error(`Failed to fetch OAuth metadata: ${response.status}`);
|
|
1013
|
+
}
|
|
1014
|
+
return response.json();
|
|
1015
|
+
}
|
|
1016
|
+
async function registerClient(registrationEndpoint, redirectUri) {
|
|
1017
|
+
const response = await fetch(registrationEndpoint, {
|
|
1018
|
+
method: "POST",
|
|
1019
|
+
headers: { "Content-Type": "application/json" },
|
|
1020
|
+
body: JSON.stringify({
|
|
1021
|
+
client_name: CLI_CLIENT_NAME,
|
|
1022
|
+
redirect_uris: [redirectUri],
|
|
1023
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
1024
|
+
response_types: ["code"],
|
|
1025
|
+
token_endpoint_auth_method: "none",
|
|
1026
|
+
// Public client with PKCE
|
|
1027
|
+
scope: "presentations:read presentations:write"
|
|
1028
|
+
})
|
|
1029
|
+
});
|
|
1030
|
+
if (!response.ok) {
|
|
1031
|
+
const err = await response.text();
|
|
1032
|
+
throw new Error(`Client registration failed: ${err}`);
|
|
1033
|
+
}
|
|
1034
|
+
return response.json();
|
|
1035
|
+
}
|
|
1036
|
+
async function exchangeCodeForTokens(tokenEndpoint, code, codeVerifier, redirectUri, clientId) {
|
|
1037
|
+
const params = new URLSearchParams({
|
|
1038
|
+
grant_type: "authorization_code",
|
|
1039
|
+
code,
|
|
1040
|
+
redirect_uri: redirectUri,
|
|
1041
|
+
client_id: clientId,
|
|
1042
|
+
code_verifier: codeVerifier
|
|
1043
|
+
});
|
|
1044
|
+
const response = await fetch(tokenEndpoint, {
|
|
1045
|
+
method: "POST",
|
|
1046
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1047
|
+
body: params.toString()
|
|
1048
|
+
});
|
|
1049
|
+
if (!response.ok) {
|
|
1050
|
+
const err = await response.text();
|
|
1051
|
+
throw new Error(`Token exchange failed: ${err}`);
|
|
1052
|
+
}
|
|
1053
|
+
return response.json();
|
|
1054
|
+
}
|
|
1055
|
+
function startCallbackServer(port, expectedState) {
|
|
1056
|
+
return new Promise((resolve4, reject) => {
|
|
1057
|
+
const server = http.createServer((req, res) => {
|
|
1058
|
+
const url = new URL(req.url || "", `http://localhost:${port}`);
|
|
1059
|
+
if (url.pathname !== "/callback") {
|
|
1060
|
+
res.writeHead(404);
|
|
1061
|
+
res.end("Not found");
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const code = url.searchParams.get("code");
|
|
1065
|
+
const state = url.searchParams.get("state");
|
|
1066
|
+
const errorParam = url.searchParams.get("error");
|
|
1067
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
1068
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1069
|
+
if (errorParam) {
|
|
1070
|
+
res.end(`
|
|
1071
|
+
<!DOCTYPE html>
|
|
1072
|
+
<html>
|
|
1073
|
+
<head><title>Login Failed</title></head>
|
|
1074
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1075
|
+
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
1076
|
+
<p>${errorDescription || errorParam}</p>
|
|
1077
|
+
<p>You can close this window.</p>
|
|
1078
|
+
</body>
|
|
1079
|
+
</html>
|
|
1080
|
+
`);
|
|
1081
|
+
server.close();
|
|
1082
|
+
reject(new Error(errorDescription || errorParam));
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if (!code || !state) {
|
|
1086
|
+
res.end(`
|
|
1087
|
+
<!DOCTYPE html>
|
|
1088
|
+
<html>
|
|
1089
|
+
<head><title>Login Failed</title></head>
|
|
1090
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1091
|
+
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
1092
|
+
<p>Missing authorization code or state</p>
|
|
1093
|
+
<p>You can close this window.</p>
|
|
1094
|
+
</body>
|
|
1095
|
+
</html>
|
|
1096
|
+
`);
|
|
1097
|
+
server.close();
|
|
1098
|
+
reject(new Error("Missing authorization code or state"));
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
if (state !== expectedState) {
|
|
1102
|
+
res.end(`
|
|
1103
|
+
<!DOCTYPE html>
|
|
1104
|
+
<html>
|
|
1105
|
+
<head><title>Login Failed</title></head>
|
|
1106
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1107
|
+
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
1108
|
+
<p>State mismatch - possible CSRF attack</p>
|
|
1109
|
+
<p>You can close this window.</p>
|
|
1110
|
+
</body>
|
|
1111
|
+
</html>
|
|
1112
|
+
`);
|
|
1113
|
+
server.close();
|
|
1114
|
+
reject(new Error("State mismatch"));
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
res.end(`
|
|
1118
|
+
<!DOCTYPE html>
|
|
1119
|
+
<html>
|
|
1120
|
+
<head><title>Login Successful</title></head>
|
|
1121
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1122
|
+
<h1 style="color: #16a34a;">Login Successful!</h1>
|
|
1123
|
+
<p>You can close this window and return to the terminal.</p>
|
|
1124
|
+
</body>
|
|
1125
|
+
</html>
|
|
1126
|
+
`);
|
|
1127
|
+
server.close();
|
|
1128
|
+
resolve4({ code, state });
|
|
1129
|
+
});
|
|
1130
|
+
server.listen(port);
|
|
1131
|
+
const cleanup = () => {
|
|
1132
|
+
server.close();
|
|
1133
|
+
reject(new Error("Login cancelled"));
|
|
1134
|
+
};
|
|
1135
|
+
process.once("SIGINT", cleanup);
|
|
1136
|
+
process.once("SIGTERM", cleanup);
|
|
1137
|
+
setTimeout(() => {
|
|
1138
|
+
process.off("SIGINT", cleanup);
|
|
1139
|
+
process.off("SIGTERM", cleanup);
|
|
1140
|
+
server.close();
|
|
1141
|
+
reject(new Error("Login timed out - no callback received within 5 minutes"));
|
|
1142
|
+
}, 5 * 60 * 1e3);
|
|
1143
|
+
});
|
|
725
1144
|
}
|
|
726
|
-
function
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
1145
|
+
async function fetchWhoami(apiUrl, accessToken) {
|
|
1146
|
+
const response = await fetch(`${apiUrl}/api/cli/whoami`, {
|
|
1147
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
1148
|
+
});
|
|
1149
|
+
if (!response.ok) {
|
|
1150
|
+
throw new Error(`Failed to fetch user info: ${response.status}`);
|
|
1151
|
+
}
|
|
1152
|
+
return response.json();
|
|
730
1153
|
}
|
|
731
|
-
function
|
|
732
|
-
if (!slug) return "N/A";
|
|
1154
|
+
async function runLoginFlow(options) {
|
|
733
1155
|
const apiUrl = getApiUrl();
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1156
|
+
let spinner = ora("Discovering OAuth server...").start();
|
|
1157
|
+
try {
|
|
1158
|
+
const metadata = await getAuthServerMetadata(apiUrl);
|
|
1159
|
+
spinner.succeed("Connecting to " + apiUrl);
|
|
1160
|
+
const port = await findAvailablePort(CALLBACK_PORT_START, CALLBACK_PORT_END);
|
|
1161
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
1162
|
+
let clientId = getClientId();
|
|
1163
|
+
let clientSecret = getClientSecret();
|
|
1164
|
+
if (!clientId) {
|
|
1165
|
+
const client = await registerClient(metadata.registration_endpoint, redirectUri);
|
|
1166
|
+
clientId = client.client_id;
|
|
1167
|
+
clientSecret = client.client_secret;
|
|
1168
|
+
setOAuthClient(clientId, clientSecret);
|
|
1169
|
+
}
|
|
1170
|
+
const codeVerifier = generateCodeVerifier();
|
|
1171
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
1172
|
+
const state = generateState();
|
|
1173
|
+
const authParams = new URLSearchParams({
|
|
1174
|
+
client_id: clientId,
|
|
1175
|
+
redirect_uri: redirectUri,
|
|
1176
|
+
response_type: "code",
|
|
1177
|
+
scope: "presentations:read presentations:write",
|
|
1178
|
+
state,
|
|
1179
|
+
code_challenge: codeChallenge,
|
|
1180
|
+
code_challenge_method: "S256"
|
|
1181
|
+
});
|
|
1182
|
+
const authUrl = `${metadata.authorization_endpoint}?${authParams}`;
|
|
1183
|
+
if (options.browser) {
|
|
1184
|
+
info("Opening browser...");
|
|
1185
|
+
await open(authUrl);
|
|
1186
|
+
} else {
|
|
1187
|
+
console.log(chalk3.bold("Open this URL in your browser:"));
|
|
1188
|
+
console.log(chalk3.cyan(authUrl));
|
|
1189
|
+
}
|
|
1190
|
+
const callbackPromise = startCallbackServer(port, state);
|
|
1191
|
+
const { code } = await callbackPromise;
|
|
1192
|
+
spinner = ora("Completing login...").start();
|
|
1193
|
+
const tokens = await exchangeCodeForTokens(
|
|
1194
|
+
metadata.token_endpoint,
|
|
1195
|
+
code,
|
|
1196
|
+
codeVerifier,
|
|
1197
|
+
redirectUri,
|
|
1198
|
+
clientId
|
|
1199
|
+
);
|
|
1200
|
+
setOAuthTokens(tokens.access_token, tokens.refresh_token, tokens.expires_in);
|
|
1201
|
+
const whoami2 = await fetchWhoami(apiUrl, tokens.access_token);
|
|
1202
|
+
spinner.succeed("Logged in!");
|
|
1203
|
+
console.log();
|
|
1204
|
+
keyValue("Logged in as", whoami2.user.email);
|
|
1205
|
+
if (whoami2.currentTeam) {
|
|
1206
|
+
setDefaultTeamId(whoami2.currentTeam.id);
|
|
1207
|
+
keyValue("Team", `${whoami2.currentTeam.name} (${whoami2.currentTeam.planName})`);
|
|
1208
|
+
}
|
|
740
1209
|
try {
|
|
741
|
-
|
|
742
|
-
return d.toLocaleString("en-US", {
|
|
743
|
-
month: "short",
|
|
744
|
-
day: "numeric",
|
|
745
|
-
hour: "numeric",
|
|
746
|
-
minute: "2-digit",
|
|
747
|
-
hour12: true
|
|
748
|
-
}).replace(" at ", ", ");
|
|
1210
|
+
await fetchAndCache();
|
|
749
1211
|
} catch {
|
|
750
|
-
return "-";
|
|
751
1212
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1213
|
+
console.log();
|
|
1214
|
+
success("You're all set!");
|
|
1215
|
+
console.log();
|
|
1216
|
+
} catch (err) {
|
|
1217
|
+
spinner.fail("Login failed");
|
|
1218
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1219
|
+
throw err;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
var CLI_CLIENT_NAME, CALLBACK_PORT_START, CALLBACK_PORT_END, loginCommand;
|
|
1223
|
+
var init_login = __esm({
|
|
1224
|
+
"src/commands/login.ts"() {
|
|
1225
|
+
"use strict";
|
|
1226
|
+
init_config();
|
|
1227
|
+
init_output();
|
|
1228
|
+
init_feature_cache();
|
|
1229
|
+
CLI_CLIENT_NAME = "ConceptCraft CLI";
|
|
1230
|
+
CALLBACK_PORT_START = 8765;
|
|
1231
|
+
CALLBACK_PORT_END = 8775;
|
|
1232
|
+
loginCommand = new Command("login").description("Authenticate with ConceptCraft (opens browser)").option("--no-browser", "Print URL instead of opening browser").action(async (options) => {
|
|
1233
|
+
console.log();
|
|
1234
|
+
if (hasOAuthTokens()) {
|
|
1235
|
+
warn("You are already logged in.");
|
|
1236
|
+
info("Run 'conceptcraft logout' to log out first, or continue to re-authenticate.");
|
|
1237
|
+
console.log();
|
|
1238
|
+
}
|
|
1239
|
+
try {
|
|
1240
|
+
await runLoginFlow(options);
|
|
1241
|
+
} catch {
|
|
1242
|
+
process.exit(1);
|
|
1243
|
+
}
|
|
762
1244
|
});
|
|
763
|
-
for (const p of presentations) {
|
|
764
|
-
table2.push([
|
|
765
|
-
truncate(p.slug || p.id.slice(0, 8), 30),
|
|
766
|
-
truncate(p.title || "Untitled", 22),
|
|
767
|
-
String(p.numberOfSlides || "-"),
|
|
768
|
-
shortDateTime(p.createdAt),
|
|
769
|
-
buildViewUrl(p.slug, language)
|
|
770
|
-
]);
|
|
771
|
-
}
|
|
772
|
-
return table2.toString();
|
|
773
1245
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
// src/index.ts
|
|
1249
|
+
import { Command as Command15 } from "commander";
|
|
1250
|
+
import chalk13 from "chalk";
|
|
1251
|
+
|
|
1252
|
+
// src/lib/brand.ts
|
|
1253
|
+
import path from "path";
|
|
1254
|
+
var BRANDS = {
|
|
1255
|
+
conceptcraft: {
|
|
1256
|
+
id: "conceptcraft",
|
|
1257
|
+
name: "conceptcraft",
|
|
1258
|
+
displayName: "ConceptCraft",
|
|
1259
|
+
description: "CLI tool for ConceptCraft presentation generation",
|
|
1260
|
+
commands: ["cc", "conceptcraft"],
|
|
1261
|
+
apiUrl: "https://conceptcraft.ai",
|
|
1262
|
+
docsUrl: "https://docs.conceptcraft.ai",
|
|
1263
|
+
packageName: "@conceptcraft/cli",
|
|
1264
|
+
configDir: ".conceptcraft"
|
|
1265
|
+
},
|
|
1266
|
+
mindframes: {
|
|
1267
|
+
id: "mindframes",
|
|
1268
|
+
name: "mindframes",
|
|
1269
|
+
displayName: "Mindframes",
|
|
1270
|
+
description: "CLI tool for Mindframes presentation generation",
|
|
1271
|
+
commands: ["mf", "mindframes"],
|
|
1272
|
+
apiUrl: "https://mindframes.app",
|
|
1273
|
+
docsUrl: "https://docs.mindframes.app",
|
|
1274
|
+
packageName: "@mindframes/cli",
|
|
1275
|
+
configDir: ".mindframes"
|
|
792
1276
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1277
|
+
};
|
|
1278
|
+
var COMMAND_TO_BRAND = {
|
|
1279
|
+
cc: "conceptcraft",
|
|
1280
|
+
conceptcraft: "conceptcraft",
|
|
1281
|
+
mf: "mindframes",
|
|
1282
|
+
mindframes: "mindframes"
|
|
1283
|
+
};
|
|
1284
|
+
function detectBrand() {
|
|
1285
|
+
const binaryPath = process.argv[1] || "";
|
|
1286
|
+
const binaryName = path.basename(binaryPath).replace(/\.(js|ts)$/, "");
|
|
1287
|
+
const brandId = COMMAND_TO_BRAND[binaryName];
|
|
1288
|
+
if (brandId) {
|
|
1289
|
+
return BRANDS[brandId];
|
|
1290
|
+
}
|
|
1291
|
+
for (const [cmd2, id] of Object.entries(COMMAND_TO_BRAND)) {
|
|
1292
|
+
if (binaryPath.includes(`/${cmd2}`) || binaryPath.includes(`\\${cmd2}`)) {
|
|
1293
|
+
return BRANDS[id];
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return BRANDS.mindframes;
|
|
797
1297
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1298
|
+
var brand = detectBrand();
|
|
1299
|
+
|
|
1300
|
+
// src/index.ts
|
|
1301
|
+
init_login();
|
|
1302
|
+
|
|
1303
|
+
// src/commands/logout.ts
|
|
1304
|
+
init_config();
|
|
1305
|
+
init_feature_cache();
|
|
1306
|
+
init_output();
|
|
1307
|
+
import { Command as Command2 } from "commander";
|
|
1308
|
+
import { confirm } from "@inquirer/prompts";
|
|
1309
|
+
var logoutCommand = new Command2("logout").description("Log out and clear authentication").option("--all", "Clear all config including API key").action(async (options) => {
|
|
1310
|
+
console.log();
|
|
1311
|
+
const hasTokens = hasOAuthTokens();
|
|
1312
|
+
const hasKey = hasApiKey();
|
|
1313
|
+
if (!hasTokens && !hasKey) {
|
|
1314
|
+
warn("You are not logged in.");
|
|
1315
|
+
console.log();
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
if (options.all) {
|
|
808
1319
|
try {
|
|
809
|
-
const
|
|
810
|
-
|
|
1320
|
+
const confirmed = await confirm({
|
|
1321
|
+
message: "Clear all configuration including API key?",
|
|
1322
|
+
default: false
|
|
1323
|
+
});
|
|
1324
|
+
if (confirmed) {
|
|
1325
|
+
clearConfig();
|
|
1326
|
+
invalidateCache();
|
|
1327
|
+
success("All configuration cleared.");
|
|
1328
|
+
} else {
|
|
1329
|
+
info("Cancelled.");
|
|
1330
|
+
}
|
|
811
1331
|
} catch {
|
|
812
|
-
|
|
1332
|
+
info("Cancelled.");
|
|
1333
|
+
}
|
|
1334
|
+
} else {
|
|
1335
|
+
clearOAuthTokens();
|
|
1336
|
+
invalidateCache();
|
|
1337
|
+
success("Logged out successfully.");
|
|
1338
|
+
if (hasKey) {
|
|
1339
|
+
info("Note: Your API key is still configured. Use --all to clear everything.");
|
|
813
1340
|
}
|
|
814
|
-
};
|
|
815
|
-
const table = new Table({
|
|
816
|
-
head: [
|
|
817
|
-
chalk2.cyan("Name"),
|
|
818
|
-
chalk2.cyan("Source"),
|
|
819
|
-
chalk2.cyan("Color"),
|
|
820
|
-
chalk2.cyan("Logo"),
|
|
821
|
-
chalk2.cyan("Default"),
|
|
822
|
-
chalk2.cyan("ID")
|
|
823
|
-
]
|
|
824
|
-
});
|
|
825
|
-
for (const b of sorted) {
|
|
826
|
-
const colorDisplay = b.primaryColor ? `${chalk2.bgHex(b.primaryColor)(" ")} ${b.primaryColor}` : "-";
|
|
827
|
-
table.push([
|
|
828
|
-
b.name,
|
|
829
|
-
formatUrl(b.sourceUrl),
|
|
830
|
-
colorDisplay,
|
|
831
|
-
b.logoUrl ? chalk2.green("\u2713") : chalk2.gray("\u2717"),
|
|
832
|
-
b.isDefault ? chalk2.green("\u2605") : "",
|
|
833
|
-
b.id
|
|
834
|
-
]);
|
|
835
|
-
}
|
|
836
|
-
return table.toString();
|
|
837
|
-
}
|
|
838
|
-
function formatDate(dateStr) {
|
|
839
|
-
try {
|
|
840
|
-
const date = new Date(dateStr);
|
|
841
|
-
return date.toLocaleString();
|
|
842
|
-
} catch {
|
|
843
|
-
return dateStr;
|
|
844
1341
|
}
|
|
845
|
-
}
|
|
846
|
-
function header(text) {
|
|
847
1342
|
console.log();
|
|
848
|
-
|
|
849
|
-
console.log(chalk2.gray("\u2500".repeat(text.length)));
|
|
850
|
-
}
|
|
851
|
-
function keyValue(key, value) {
|
|
852
|
-
console.log(` ${chalk2.gray(key + ":")} ${value ?? "-"}`);
|
|
853
|
-
}
|
|
854
|
-
function progressBar(current, total, width = 30, showPercentage = true) {
|
|
855
|
-
const percentage = Math.min(100, Math.round(current / total * 100));
|
|
856
|
-
const filled = Math.round(width * current / total);
|
|
857
|
-
const empty = width - filled;
|
|
858
|
-
const bar = chalk2.green("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(empty));
|
|
859
|
-
return showPercentage ? `[${bar}] ${percentage}%` : `[${bar}]`;
|
|
860
|
-
}
|
|
1343
|
+
});
|
|
861
1344
|
|
|
862
1345
|
// src/commands/config.ts
|
|
863
|
-
|
|
864
|
-
|
|
1346
|
+
init_config();
|
|
1347
|
+
init_auth();
|
|
1348
|
+
init_api();
|
|
1349
|
+
init_feature_cache();
|
|
1350
|
+
init_output();
|
|
1351
|
+
import { Command as Command3 } from "commander";
|
|
1352
|
+
import chalk4 from "chalk";
|
|
1353
|
+
import { input, password, confirm as confirm2, select } from "@inquirer/prompts";
|
|
1354
|
+
import ora2 from "ora";
|
|
1355
|
+
var configCommand = new Command3("config").description("Manage CLI configuration").addCommand(
|
|
1356
|
+
new Command3("init").description("Initialize configuration interactively").action(async () => {
|
|
865
1357
|
console.log();
|
|
866
|
-
console.log(
|
|
867
|
-
console.log(
|
|
1358
|
+
console.log(chalk4.bold("ConceptCraft CLI Configuration"));
|
|
1359
|
+
console.log(chalk4.gray("\u2500".repeat(35)));
|
|
868
1360
|
console.log();
|
|
869
1361
|
try {
|
|
870
1362
|
const apiKey = await password({
|
|
@@ -881,7 +1373,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
881
1373
|
}
|
|
882
1374
|
});
|
|
883
1375
|
setApiKey(apiKey.trim());
|
|
884
|
-
const useCustomUrl = await
|
|
1376
|
+
const useCustomUrl = await confirm2({
|
|
885
1377
|
message: "Use a custom API URL? (default: www.mindframes.app)",
|
|
886
1378
|
default: false
|
|
887
1379
|
});
|
|
@@ -901,7 +1393,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
901
1393
|
setApiUrl(customUrl);
|
|
902
1394
|
}
|
|
903
1395
|
console.log();
|
|
904
|
-
const spinner =
|
|
1396
|
+
const spinner = ora2("Verifying API key...").start();
|
|
905
1397
|
try {
|
|
906
1398
|
const result = await whoami();
|
|
907
1399
|
spinner.succeed("API key verified!");
|
|
@@ -953,18 +1445,18 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
953
1445
|
}
|
|
954
1446
|
})
|
|
955
1447
|
).addCommand(
|
|
956
|
-
new
|
|
1448
|
+
new Command3("show").description("Show current configuration").option("--verify", "Verify API key and show team details").action(async (options) => {
|
|
957
1449
|
const config2 = getConfig();
|
|
958
1450
|
header("Current Configuration");
|
|
959
1451
|
console.log();
|
|
960
|
-
keyValue("API Key", getMaskedApiKey() ??
|
|
1452
|
+
keyValue("API Key", getMaskedApiKey() ?? chalk4.red("Not set"));
|
|
961
1453
|
keyValue("API URL", config2.apiUrl);
|
|
962
|
-
keyValue("Default Team ID", config2.defaultTeamId ??
|
|
1454
|
+
keyValue("Default Team ID", config2.defaultTeamId ?? chalk4.gray("Not set"));
|
|
963
1455
|
console.log();
|
|
964
1456
|
keyValue("Config file", getConfigPath());
|
|
965
1457
|
if (options.verify && config2.apiKey) {
|
|
966
1458
|
console.log();
|
|
967
|
-
const spinner =
|
|
1459
|
+
const spinner = ora2("Verifying...").start();
|
|
968
1460
|
try {
|
|
969
1461
|
const result = await whoami();
|
|
970
1462
|
spinner.succeed("Verified");
|
|
@@ -984,7 +1476,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
984
1476
|
console.log();
|
|
985
1477
|
})
|
|
986
1478
|
).addCommand(
|
|
987
|
-
new
|
|
1479
|
+
new Command3("set").description("Set a configuration value").argument("<key>", "Configuration key (api-key, api-url, team-id)").argument("<value>", "Value to set").action(async (key, value) => {
|
|
988
1480
|
switch (key) {
|
|
989
1481
|
case "api-key":
|
|
990
1482
|
if (!isValidApiKeyFormat(value)) {
|
|
@@ -1013,14 +1505,14 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1013
1505
|
break;
|
|
1014
1506
|
default:
|
|
1015
1507
|
error(`Unknown config key: ${key}`);
|
|
1016
|
-
console.log(
|
|
1508
|
+
console.log(chalk4.gray("Valid keys: api-key, api-url, team-id"));
|
|
1017
1509
|
process.exit(6);
|
|
1018
1510
|
}
|
|
1019
1511
|
})
|
|
1020
1512
|
).addCommand(
|
|
1021
|
-
new
|
|
1513
|
+
new Command3("clear").description("Clear all configuration").action(async () => {
|
|
1022
1514
|
try {
|
|
1023
|
-
const confirmed = await
|
|
1515
|
+
const confirmed = await confirm2({
|
|
1024
1516
|
message: "Are you sure you want to clear all configuration?",
|
|
1025
1517
|
default: false
|
|
1026
1518
|
});
|
|
@@ -1036,8 +1528,8 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1036
1528
|
}
|
|
1037
1529
|
})
|
|
1038
1530
|
).addCommand(
|
|
1039
|
-
new
|
|
1040
|
-
const spinner =
|
|
1531
|
+
new Command3("refresh").description("Refresh cached feature flags").action(async () => {
|
|
1532
|
+
const spinner = ora2("Refreshing feature flags...").start();
|
|
1041
1533
|
try {
|
|
1042
1534
|
await fetchAndCache();
|
|
1043
1535
|
spinner.succeed("Feature flags refreshed");
|
|
@@ -1049,7 +1541,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1049
1541
|
}
|
|
1050
1542
|
})
|
|
1051
1543
|
).addCommand(
|
|
1052
|
-
new
|
|
1544
|
+
new Command3("path").description("Show configuration file path").option("--cache", "Show feature cache file path").action((options) => {
|
|
1053
1545
|
if (options.cache) {
|
|
1054
1546
|
console.log(getCachePath());
|
|
1055
1547
|
} else {
|
|
@@ -1059,13 +1551,16 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1059
1551
|
);
|
|
1060
1552
|
|
|
1061
1553
|
// src/commands/create.ts
|
|
1062
|
-
|
|
1063
|
-
|
|
1554
|
+
init_api();
|
|
1555
|
+
init_auth();
|
|
1556
|
+
import { Command as Command4 } from "commander";
|
|
1557
|
+
import chalk6 from "chalk";
|
|
1064
1558
|
|
|
1065
1559
|
// src/lib/streaming.ts
|
|
1560
|
+
init_output();
|
|
1066
1561
|
import { createParser } from "eventsource-parser";
|
|
1067
|
-
import
|
|
1068
|
-
import
|
|
1562
|
+
import chalk5 from "chalk";
|
|
1563
|
+
import ora3 from "ora";
|
|
1069
1564
|
async function handleStream(response, callbacks, options = {}) {
|
|
1070
1565
|
const reader = response.body?.getReader();
|
|
1071
1566
|
if (!reader) {
|
|
@@ -1086,7 +1581,7 @@ async function handleStream(response, callbacks, options = {}) {
|
|
|
1086
1581
|
const parsed = JSON.parse(event.data);
|
|
1087
1582
|
const { type, data } = parsed;
|
|
1088
1583
|
if (options.debug) {
|
|
1089
|
-
console.log(
|
|
1584
|
+
console.log(chalk5.gray(`[SSE] ${type}:`), data);
|
|
1090
1585
|
}
|
|
1091
1586
|
callbacks.onData?.(type, data);
|
|
1092
1587
|
switch (type) {
|
|
@@ -1156,7 +1651,7 @@ async function handleStream(response, callbacks, options = {}) {
|
|
|
1156
1651
|
}
|
|
1157
1652
|
} catch (e) {
|
|
1158
1653
|
if (options.debug) {
|
|
1159
|
-
console.log(
|
|
1654
|
+
console.log(chalk5.gray("[SSE Raw]"), event.data);
|
|
1160
1655
|
}
|
|
1161
1656
|
}
|
|
1162
1657
|
}
|
|
@@ -1181,9 +1676,9 @@ async function streamWithProgress(response, topic, options) {
|
|
|
1181
1676
|
const useTTY = isTTY() && !options.noStream && !options.quiet && !options.json;
|
|
1182
1677
|
if (!options.json) {
|
|
1183
1678
|
console.log();
|
|
1184
|
-
console.log(
|
|
1679
|
+
console.log(chalk5.bold(`Creating presentation: "${topic}"`));
|
|
1185
1680
|
console.log(
|
|
1186
|
-
|
|
1681
|
+
chalk5.gray(
|
|
1187
1682
|
`Quality: ${options.mode} | Tone: ${options.tone ?? "professional"} | Slides: ${options.slideCount} | Language: ${options.language}`
|
|
1188
1683
|
)
|
|
1189
1684
|
);
|
|
@@ -1234,10 +1729,10 @@ async function streamWithProgress(response, topic, options) {
|
|
|
1234
1729
|
const tokens = formatTokens(currentTokens);
|
|
1235
1730
|
const elapsed = formatTime(getElapsed()).padStart(5, " ");
|
|
1236
1731
|
const remaining = getRemaining().padStart(5, " ");
|
|
1237
|
-
return `${bar} ${
|
|
1732
|
+
return `${bar} ${chalk5.bold(pct)}${chalk5.gray("%")} ${chalk5.cyan(phase)} ${chalk5.gray(slides)} ${chalk5.yellow(tokens)} ${chalk5.gray(elapsed)} ${chalk5.green(remaining + " left")}`;
|
|
1238
1733
|
};
|
|
1239
1734
|
if (useTTY) {
|
|
1240
|
-
spinner =
|
|
1735
|
+
spinner = ora3({
|
|
1241
1736
|
text: buildProgressLine(),
|
|
1242
1737
|
spinner: "dots"
|
|
1243
1738
|
}).start();
|
|
@@ -1273,9 +1768,9 @@ async function streamWithProgress(response, topic, options) {
|
|
|
1273
1768
|
onError: (error2) => {
|
|
1274
1769
|
if (timer) clearInterval(timer);
|
|
1275
1770
|
if (spinner) {
|
|
1276
|
-
spinner.fail(
|
|
1771
|
+
spinner.fail(chalk5.red(`Error: ${error2}`));
|
|
1277
1772
|
} else if (!options.json) {
|
|
1278
|
-
console.error(
|
|
1773
|
+
console.error(chalk5.red(`Error: ${error2}`));
|
|
1279
1774
|
}
|
|
1280
1775
|
},
|
|
1281
1776
|
onComplete: (data) => {
|
|
@@ -1324,6 +1819,7 @@ async function streamTextContent(response, options = {}) {
|
|
|
1324
1819
|
}
|
|
1325
1820
|
|
|
1326
1821
|
// src/commands/create.ts
|
|
1822
|
+
init_output();
|
|
1327
1823
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
1328
1824
|
import { resolve } from "path";
|
|
1329
1825
|
var VALID_GOALS = [
|
|
@@ -1334,7 +1830,7 @@ var VALID_GOALS = [
|
|
|
1334
1830
|
"entertain",
|
|
1335
1831
|
"report"
|
|
1336
1832
|
];
|
|
1337
|
-
var createCommand = new
|
|
1833
|
+
var createCommand = new Command4("create").description("Create a new presentation").argument("<topic>", "The topic or title for the presentation").option("-n, --slides <count>", "Number of slides (1-20)", "10").option(
|
|
1338
1834
|
"-m, --mode <mode>",
|
|
1339
1835
|
"Generation quality mode (best, balanced, fast, ultrafast, instant)",
|
|
1340
1836
|
"balanced"
|
|
@@ -1375,67 +1871,83 @@ var createCommand = new Command2("create").description("Create a new presentatio
|
|
|
1375
1871
|
"--thinking-depth <depth>",
|
|
1376
1872
|
"AI thinking depth (quick, moderate, deep, profound)",
|
|
1377
1873
|
"moderate"
|
|
1378
|
-
).option(
|
|
1874
|
+
).option(
|
|
1875
|
+
"--theme <preset>",
|
|
1876
|
+
"Color theme preset (blue, violet, rose, orange, green)"
|
|
1877
|
+
).option("--primary-color <hex>", "Primary color in hex (e.g., #0066CC)").option("--secondary-color <hex>", "Secondary color in hex").option("--accent-color <hex>", "Accent color in hex").option("--background-color <hex>", "Background color in hex").option("--foreground-color <hex>", "Foreground/text color in hex").option(
|
|
1878
|
+
"--decorations <style>",
|
|
1879
|
+
"Background decoration style (none, waves-bottom-left, waves-top-right, blob-corners, minimal)"
|
|
1880
|
+
).option("-o, --output <format>", "Output format (human, json, quiet)", "human").option("--no-stream", "Wait for completion without progress display").option("--debug", "Enable debug logging").option("--team-id <id>", "Team ID to create presentation for (switch teams)").option("--open", "Open the presentation in browser after creation").addHelpText(
|
|
1379
1881
|
"after",
|
|
1380
1882
|
`
|
|
1381
|
-
${
|
|
1883
|
+
${chalk6.bold("Recommended Usage:")}
|
|
1382
1884
|
Always specify slides (-n), mode (-m), audience, and context for best results.
|
|
1383
1885
|
|
|
1384
|
-
${
|
|
1385
|
-
${
|
|
1886
|
+
${chalk6.bold("Examples:")}
|
|
1887
|
+
${chalk6.gray("# Content from PDF + style from image reference")}
|
|
1386
1888
|
$ conceptcraft create "Quarterly Report" \\
|
|
1387
1889
|
--file ./report.pdf \\
|
|
1388
1890
|
--reference-url https://example.com/style-template.png \\
|
|
1389
1891
|
-n 12 -m best --audience "Executive team"
|
|
1390
1892
|
|
|
1391
|
-
${
|
|
1893
|
+
${chalk6.gray("# Upload content files (PDF, PPTX, DOCX)")}
|
|
1392
1894
|
$ conceptcraft create "Product Demo" \\
|
|
1393
1895
|
--file ./existing-deck.pptx --file ./specs.pdf \\
|
|
1394
1896
|
--goal persuade --audience "Enterprise buyers"
|
|
1395
1897
|
|
|
1396
|
-
${
|
|
1898
|
+
${chalk6.gray("# Inline context with custom styling")}
|
|
1397
1899
|
$ conceptcraft create "Q4 Business Review" \\
|
|
1398
1900
|
-n 12 -m best --goal inform \\
|
|
1399
1901
|
--context "Revenue: $50M (+25% YoY), EBITDA: $8M" \\
|
|
1400
1902
|
--reference-url https://example.com/brand-style.jpg
|
|
1401
1903
|
|
|
1402
|
-
${
|
|
1904
|
+
${chalk6.gray("# Research presentation from URLs")}
|
|
1403
1905
|
$ conceptcraft create "AI Industry Trends" \\
|
|
1404
1906
|
-n 10 -m best -t educational \\
|
|
1405
1907
|
--sources https://example.com/ai-report.pdf
|
|
1406
1908
|
|
|
1407
|
-
${
|
|
1909
|
+
${chalk6.gray("# Pipe content from another command")}
|
|
1408
1910
|
$ cat meeting-notes.md | conceptcraft create "Meeting Summary" \\
|
|
1409
1911
|
-n 6 -m balanced --goal inform
|
|
1410
1912
|
|
|
1411
|
-
${
|
|
1913
|
+
${chalk6.bold("Content Options (what to include in slides):")}
|
|
1412
1914
|
-f, --file <paths...> Upload files for content extraction (PDF, PPTX, DOCX)
|
|
1413
1915
|
-c, --context <text> Inline text (key facts, data points)
|
|
1414
1916
|
--context-file <path> Read content from file (markdown, text, JSON)
|
|
1415
1917
|
--sources <urls...> URLs to scrape for content
|
|
1416
1918
|
--stdin Pipe content from another command
|
|
1417
1919
|
|
|
1418
|
-
${
|
|
1419
|
-
--reference-url <url> ${
|
|
1920
|
+
${chalk6.bold("Styling Options (how slides look):")}
|
|
1921
|
+
--reference-url <url> ${chalk6.yellow("Image URL to guide visual style")} (colors, layout, feel)
|
|
1420
1922
|
--styling <mode> freeform, brand-only, style-only, no-styling
|
|
1421
1923
|
-b, --brand <id> Apply saved brand settings
|
|
1422
1924
|
|
|
1423
|
-
${
|
|
1424
|
-
${
|
|
1925
|
+
${chalk6.gray("NOTE: --file uploads provide CONTENT (data, text to include)")}
|
|
1926
|
+
${chalk6.gray(" --reference-url provides STYLE (visual design inspiration)")}
|
|
1425
1927
|
|
|
1426
|
-
${
|
|
1928
|
+
${chalk6.bold("Goal Options:")}
|
|
1427
1929
|
-g, --goal <type> inform, persuade, train, learn, entertain, report
|
|
1428
1930
|
--custom-goal <text> Custom goal description
|
|
1429
1931
|
|
|
1430
|
-
${
|
|
1932
|
+
${chalk6.bold("Theme Options:")}
|
|
1933
|
+
--theme <preset> Preset color scheme (blue, violet, rose, orange, green)
|
|
1934
|
+
--primary-color <hex> Primary brand color (e.g., #0066CC)
|
|
1935
|
+
--secondary-color <hex> Secondary color for accents
|
|
1936
|
+
--decorations <style> Background style (none, waves-bottom-left, waves-top-right, blob-corners, minimal)
|
|
1937
|
+
|
|
1938
|
+
${chalk6.gray("Example: Apply corporate colors")}
|
|
1939
|
+
$ conceptcraft create "Brand Deck" \\
|
|
1940
|
+
--theme blue --primary-color "#1E40AF" --decorations waves-bottom-left
|
|
1941
|
+
|
|
1942
|
+
${chalk6.bold("Mode Reference:")}
|
|
1431
1943
|
best Highest quality, thorough research (recommended for important decks)
|
|
1432
1944
|
balanced Good quality with reasonable speed (default)
|
|
1433
1945
|
fast Quick generation, less refinement
|
|
1434
1946
|
ultrafast Very quick, minimal processing
|
|
1435
|
-
instant Fastest, basic output
|
|
1947
|
+
instant Fastest, basic output (theme options work best with instant mode)
|
|
1436
1948
|
`
|
|
1437
1949
|
).action(async (topic, options) => {
|
|
1438
|
-
requireAuth();
|
|
1950
|
+
await requireAuth();
|
|
1439
1951
|
const slideCount = parseInt(options.slides, 10);
|
|
1440
1952
|
if (isNaN(slideCount) || slideCount < 1 || slideCount > 20) {
|
|
1441
1953
|
error("Slide count must be between 1 and 20");
|
|
@@ -1510,15 +2022,68 @@ ${chalk5.bold("Mode Reference:")}
|
|
|
1510
2022
|
);
|
|
1511
2023
|
process.exit(6);
|
|
1512
2024
|
}
|
|
2025
|
+
const validThemePresets = ["blue", "violet", "rose", "orange", "green"];
|
|
2026
|
+
if (options.theme && !validThemePresets.includes(options.theme)) {
|
|
2027
|
+
error(
|
|
2028
|
+
`Invalid theme: ${options.theme}. Valid themes: ${validThemePresets.join(", ")}`
|
|
2029
|
+
);
|
|
2030
|
+
process.exit(6);
|
|
2031
|
+
}
|
|
2032
|
+
const validDecorations = [
|
|
2033
|
+
"none",
|
|
2034
|
+
"waves-bottom-left",
|
|
2035
|
+
"waves-top-right",
|
|
2036
|
+
"blob-corners",
|
|
2037
|
+
"minimal"
|
|
2038
|
+
];
|
|
2039
|
+
if (options.decorations && !validDecorations.includes(options.decorations)) {
|
|
2040
|
+
error(
|
|
2041
|
+
`Invalid decorations: ${options.decorations}. Valid styles: ${validDecorations.join(", ")}`
|
|
2042
|
+
);
|
|
2043
|
+
process.exit(6);
|
|
2044
|
+
}
|
|
2045
|
+
const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
2046
|
+
const colorOptions = [
|
|
2047
|
+
{ name: "primary-color", value: options.primaryColor },
|
|
2048
|
+
{ name: "secondary-color", value: options.secondaryColor },
|
|
2049
|
+
{ name: "accent-color", value: options.accentColor },
|
|
2050
|
+
{ name: "background-color", value: options.backgroundColor },
|
|
2051
|
+
{ name: "foreground-color", value: options.foregroundColor }
|
|
2052
|
+
];
|
|
2053
|
+
for (const { name, value } of colorOptions) {
|
|
2054
|
+
if (value && !hexColorRegex.test(value)) {
|
|
2055
|
+
error(`Invalid ${name}: ${value}. Must be a hex color (e.g., #0066CC or #06C)`);
|
|
2056
|
+
process.exit(6);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
let theme;
|
|
2060
|
+
const hasCustomColors = options.primaryColor || options.secondaryColor || options.accentColor || options.backgroundColor || options.foregroundColor;
|
|
2061
|
+
if (options.theme || hasCustomColors || options.decorations) {
|
|
2062
|
+
theme = {};
|
|
2063
|
+
if (options.theme) {
|
|
2064
|
+
theme.preset = options.theme;
|
|
2065
|
+
}
|
|
2066
|
+
if (hasCustomColors) {
|
|
2067
|
+
theme.custom = {};
|
|
2068
|
+
if (options.primaryColor) theme.custom.primary = options.primaryColor;
|
|
2069
|
+
if (options.secondaryColor) theme.custom.secondary = options.secondaryColor;
|
|
2070
|
+
if (options.accentColor) theme.custom.accent = options.accentColor;
|
|
2071
|
+
if (options.backgroundColor) theme.custom.background = options.backgroundColor;
|
|
2072
|
+
if (options.foregroundColor) theme.custom.foreground = options.foregroundColor;
|
|
2073
|
+
}
|
|
2074
|
+
if (options.decorations) {
|
|
2075
|
+
theme.decorations = options.decorations;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
1513
2078
|
try {
|
|
1514
2079
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1515
|
-
process.stdout.write(
|
|
2080
|
+
process.stdout.write(chalk6.gray("Checking generation limits... "));
|
|
1516
2081
|
}
|
|
1517
2082
|
const limits = await validateGeneration(options.mode, slideCount, options.teamId);
|
|
1518
2083
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1519
|
-
console.log(
|
|
2084
|
+
console.log(chalk6.green("\u2713"));
|
|
1520
2085
|
console.log(
|
|
1521
|
-
|
|
2086
|
+
chalk6.gray(
|
|
1522
2087
|
` Plan: ${limits.planName} | ${options.mode}: ${limits.remaining[options.mode]}/${limits.limits[options.mode]} remaining`
|
|
1523
2088
|
)
|
|
1524
2089
|
);
|
|
@@ -1532,7 +2097,7 @@ ${chalk5.bold("Mode Reference:")}
|
|
|
1532
2097
|
})
|
|
1533
2098
|
);
|
|
1534
2099
|
} else {
|
|
1535
|
-
console.log(
|
|
2100
|
+
console.log(chalk6.red("\u2717"));
|
|
1536
2101
|
error(err instanceof Error ? err.message : String(err));
|
|
1537
2102
|
}
|
|
1538
2103
|
process.exit(err.exitCode ?? 1);
|
|
@@ -1547,7 +2112,7 @@ ${chalk5.bold("Mode Reference:")}
|
|
|
1547
2112
|
}
|
|
1548
2113
|
}
|
|
1549
2114
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1550
|
-
console.log(
|
|
2115
|
+
console.log(chalk6.gray(`
|
|
1551
2116
|
Uploading ${options.file.length} file(s)...`));
|
|
1552
2117
|
}
|
|
1553
2118
|
try {
|
|
@@ -1556,13 +2121,13 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1556
2121
|
(completed, total, fileName) => {
|
|
1557
2122
|
if (options.output !== "json" && options.output !== "quiet" && fileName) {
|
|
1558
2123
|
process.stdout.write(
|
|
1559
|
-
`\r ${
|
|
2124
|
+
`\r ${chalk6.cyan("\u2B06")} Uploading: ${fileName} (${completed + 1}/${total})`
|
|
1560
2125
|
);
|
|
1561
2126
|
}
|
|
1562
2127
|
}
|
|
1563
2128
|
);
|
|
1564
2129
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1565
|
-
console.log(`\r ${
|
|
2130
|
+
console.log(`\r ${chalk6.green("\u2713")} Uploaded ${uploadedFiles.length} file(s) `);
|
|
1566
2131
|
}
|
|
1567
2132
|
} catch (err) {
|
|
1568
2133
|
error(
|
|
@@ -1601,16 +2166,16 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1601
2166
|
if (!hasContext) {
|
|
1602
2167
|
error("Context is required to create a presentation.");
|
|
1603
2168
|
console.log();
|
|
1604
|
-
console.log(
|
|
1605
|
-
console.log(
|
|
1606
|
-
console.log(
|
|
1607
|
-
console.log(
|
|
1608
|
-
console.log(
|
|
1609
|
-
console.log(
|
|
2169
|
+
console.log(chalk6.gray("Provide context using one of these methods:"));
|
|
2170
|
+
console.log(chalk6.gray(" -f, --file <paths...> Upload files (PDF, PPTX, images)"));
|
|
2171
|
+
console.log(chalk6.gray(" -c, --context <text> Direct text context"));
|
|
2172
|
+
console.log(chalk6.gray(" --context-file <path> Read from a file"));
|
|
2173
|
+
console.log(chalk6.gray(" --sources <urls...> URLs to scrape"));
|
|
2174
|
+
console.log(chalk6.gray(" cat file | conceptcraft Pipe content"));
|
|
1610
2175
|
console.log();
|
|
1611
|
-
console.log(
|
|
1612
|
-
console.log(
|
|
1613
|
-
console.log(
|
|
2176
|
+
console.log(chalk6.gray("Example:"));
|
|
2177
|
+
console.log(chalk6.cyan(' conceptcraft create "Q4 Report" --file ./report.pdf'));
|
|
2178
|
+
console.log(chalk6.cyan(' conceptcraft create "Q4 Report" --context "Revenue: $10M, Growth: 25%"'));
|
|
1614
2179
|
process.exit(6);
|
|
1615
2180
|
}
|
|
1616
2181
|
try {
|
|
@@ -1633,7 +2198,8 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1633
2198
|
uploadedFiles: uploadedFiles.length > 0 ? uploadedFiles : void 0,
|
|
1634
2199
|
goal: options.goal,
|
|
1635
2200
|
customGoal: options.customGoal,
|
|
1636
|
-
teamId: options.teamId
|
|
2201
|
+
teamId: options.teamId,
|
|
2202
|
+
theme
|
|
1637
2203
|
});
|
|
1638
2204
|
const result = await streamWithProgress(response, topic, {
|
|
1639
2205
|
slideCount,
|
|
@@ -1684,7 +2250,11 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1684
2250
|
console.log();
|
|
1685
2251
|
const viewUrl = buildViewUrl(result.slug, options.language);
|
|
1686
2252
|
if (viewUrl !== "N/A") {
|
|
1687
|
-
console.log(
|
|
2253
|
+
console.log(chalk6.bold(" Open: ") + chalk6.cyan.underline(viewUrl));
|
|
2254
|
+
if (options.open) {
|
|
2255
|
+
const open2 = await import("open");
|
|
2256
|
+
await open2.default(viewUrl);
|
|
2257
|
+
}
|
|
1688
2258
|
}
|
|
1689
2259
|
console.log();
|
|
1690
2260
|
}
|
|
@@ -1731,13 +2301,17 @@ function isUrl(str) {
|
|
|
1731
2301
|
}
|
|
1732
2302
|
|
|
1733
2303
|
// src/commands/list.ts
|
|
1734
|
-
|
|
1735
|
-
|
|
2304
|
+
init_api();
|
|
2305
|
+
init_auth();
|
|
2306
|
+
init_config();
|
|
2307
|
+
init_output();
|
|
2308
|
+
import { Command as Command5 } from "commander";
|
|
2309
|
+
var listCommand = new Command5("list").description("List presentations").option("-n, --limit <count>", "Number of results to return", "20").option(
|
|
1736
2310
|
"-f, --format <format>",
|
|
1737
2311
|
"Output format (table, json, ids)",
|
|
1738
2312
|
"table"
|
|
1739
2313
|
).option("--sort <field>", "Sort by field (created, updated, title)").option("--team-id <id>", "Team ID (uses default if not specified)").option("-d, --detail", "Show detailed output including URLs").option("-l, --language <lang>", "Language for URLs", "en").action(async (options) => {
|
|
1740
|
-
requireAuth();
|
|
2314
|
+
await requireAuth();
|
|
1741
2315
|
const limit = parseInt(options.limit, 10);
|
|
1742
2316
|
if (isNaN(limit) || limit < 1) {
|
|
1743
2317
|
error("Invalid limit value");
|
|
@@ -1794,14 +2368,17 @@ var listCommand = new Command3("list").description("List presentations").option(
|
|
|
1794
2368
|
});
|
|
1795
2369
|
|
|
1796
2370
|
// src/commands/get.ts
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2371
|
+
init_api();
|
|
2372
|
+
init_auth();
|
|
2373
|
+
init_output();
|
|
2374
|
+
import { Command as Command6 } from "commander";
|
|
2375
|
+
import chalk7 from "chalk";
|
|
2376
|
+
var getCommand = new Command6("get").description("Get presentation details").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option(
|
|
1800
2377
|
"-f, --format <format>",
|
|
1801
2378
|
"Output format (summary, full, json)",
|
|
1802
2379
|
"summary"
|
|
1803
2380
|
).option("--include <fields>", "Fields to include (comma-separated: slides,styling)").option("-l, --language <lang>", "Language for view URL", "en").action(async (slug, options) => {
|
|
1804
|
-
requireAuth();
|
|
2381
|
+
await requireAuth();
|
|
1805
2382
|
try {
|
|
1806
2383
|
const presentation = await getPresentation(slug);
|
|
1807
2384
|
if (options.format === "json") {
|
|
@@ -1813,7 +2390,7 @@ var getCommand = new Command4("get").description("Get presentation details").arg
|
|
|
1813
2390
|
console.log();
|
|
1814
2391
|
keyValue("Slug", presentation.slug);
|
|
1815
2392
|
keyValue("Title", presentation.title || "Untitled");
|
|
1816
|
-
keyValue("Description", presentation.description ||
|
|
2393
|
+
keyValue("Description", presentation.description || chalk7.gray("None"));
|
|
1817
2394
|
keyValue("Slides", String(presentation.numberOfSlides || "-"));
|
|
1818
2395
|
keyValue("Mode", presentation.mode || "-");
|
|
1819
2396
|
keyValue("Created", formatDate(presentation.createdAt));
|
|
@@ -1822,11 +2399,11 @@ var getCommand = new Command4("get").description("Get presentation details").arg
|
|
|
1822
2399
|
}
|
|
1823
2400
|
console.log();
|
|
1824
2401
|
if (viewUrl !== "N/A") {
|
|
1825
|
-
console.log(
|
|
2402
|
+
console.log(chalk7.bold(" Open: ") + chalk7.cyan.underline(viewUrl));
|
|
1826
2403
|
}
|
|
1827
2404
|
console.log();
|
|
1828
2405
|
if (options.format === "full" && options.include?.includes("slides")) {
|
|
1829
|
-
console.log(
|
|
2406
|
+
console.log(chalk7.gray("(Full slide content available in JSON format)"));
|
|
1830
2407
|
console.log();
|
|
1831
2408
|
}
|
|
1832
2409
|
} catch (err) {
|
|
@@ -1836,13 +2413,16 @@ var getCommand = new Command4("get").description("Get presentation details").arg
|
|
|
1836
2413
|
});
|
|
1837
2414
|
|
|
1838
2415
|
// src/commands/delete.ts
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2416
|
+
init_api();
|
|
2417
|
+
init_auth();
|
|
2418
|
+
init_output();
|
|
2419
|
+
import { Command as Command7 } from "commander";
|
|
2420
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
2421
|
+
var deleteCommand = new Command7("delete").description("Delete a presentation").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option("-f, --force", "Skip confirmation prompt").option("-q, --quiet", "Suppress output").action(async (slug, options) => {
|
|
2422
|
+
await requireAuth();
|
|
1843
2423
|
if (!options.force) {
|
|
1844
2424
|
try {
|
|
1845
|
-
const confirmed = await
|
|
2425
|
+
const confirmed = await confirm3({
|
|
1846
2426
|
message: `Are you sure you want to delete presentation "${slug}"?`,
|
|
1847
2427
|
default: false
|
|
1848
2428
|
});
|
|
@@ -1871,13 +2451,16 @@ var deleteCommand = new Command5("delete").description("Delete a presentation").
|
|
|
1871
2451
|
});
|
|
1872
2452
|
|
|
1873
2453
|
// src/commands/export.ts
|
|
1874
|
-
|
|
2454
|
+
init_api();
|
|
2455
|
+
init_auth();
|
|
2456
|
+
init_output();
|
|
2457
|
+
import { Command as Command8 } from "commander";
|
|
1875
2458
|
import { writeFile } from "fs/promises";
|
|
1876
2459
|
import { resolve as resolve2 } from "path";
|
|
1877
|
-
import
|
|
1878
|
-
var exportCommand = new
|
|
1879
|
-
requireAuth();
|
|
1880
|
-
const spinner =
|
|
2460
|
+
import ora4 from "ora";
|
|
2461
|
+
var exportCommand = new Command8("export").description("Export a presentation to ZIP").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option("-o, --output <path>", "Output file path").option("--include-images", "Include images (default: true)", true).option("--include-branding", "Include branding (default: true)", true).option("--no-external", "Skip external image downloads").action(async (slug, options) => {
|
|
2462
|
+
await requireAuth();
|
|
2463
|
+
const spinner = ora4("Fetching presentation...").start();
|
|
1881
2464
|
try {
|
|
1882
2465
|
const presentation = await getPresentation(slug);
|
|
1883
2466
|
const title = presentation.title || slug;
|
|
@@ -1912,14 +2495,18 @@ function formatBytes(bytes) {
|
|
|
1912
2495
|
}
|
|
1913
2496
|
|
|
1914
2497
|
// src/commands/import.ts
|
|
1915
|
-
|
|
2498
|
+
init_api();
|
|
2499
|
+
init_auth();
|
|
2500
|
+
init_output();
|
|
2501
|
+
init_config();
|
|
2502
|
+
import { Command as Command9 } from "commander";
|
|
1916
2503
|
import { readFile } from "fs/promises";
|
|
1917
2504
|
import { resolve as resolve3, basename as basename2 } from "path";
|
|
1918
|
-
import
|
|
1919
|
-
var cmd = new
|
|
2505
|
+
import ora5 from "ora";
|
|
2506
|
+
var cmd = new Command9("import").description("Import a presentation from ZIP (admin only)").argument("<file>", "Path to ZIP file");
|
|
1920
2507
|
cmd._hidden = true;
|
|
1921
2508
|
var importCommand = cmd.option("--dry-run", "Validate without importing").option("--overwrite", "Overwrite existing presentations").option("--remap-ids", "Generate new IDs (default: true)", true).action(async (file, options) => {
|
|
1922
|
-
requireAuth();
|
|
2509
|
+
await requireAuth();
|
|
1923
2510
|
try {
|
|
1924
2511
|
const me = await whoami();
|
|
1925
2512
|
if (me.user.role !== "admin") {
|
|
@@ -1936,7 +2523,7 @@ var importCommand = cmd.option("--dry-run", "Validate without importing").option
|
|
|
1936
2523
|
error("File must be a ZIP archive");
|
|
1937
2524
|
process.exit(6);
|
|
1938
2525
|
}
|
|
1939
|
-
const spinner =
|
|
2526
|
+
const spinner = ora5("Reading file...").start();
|
|
1940
2527
|
try {
|
|
1941
2528
|
const fileBuffer = await readFile(filePath);
|
|
1942
2529
|
if (options.dryRun) {
|
|
@@ -2001,12 +2588,16 @@ var importCommand = cmd.option("--dry-run", "Validate without importing").option
|
|
|
2001
2588
|
});
|
|
2002
2589
|
|
|
2003
2590
|
// src/commands/branding.ts
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2591
|
+
init_api();
|
|
2592
|
+
init_auth();
|
|
2593
|
+
init_config();
|
|
2594
|
+
init_output();
|
|
2595
|
+
import { Command as Command10 } from "commander";
|
|
2596
|
+
import chalk8 from "chalk";
|
|
2597
|
+
import ora6 from "ora";
|
|
2598
|
+
var brandingCommand = new Command10("branding").description("Manage brand profiles").addCommand(
|
|
2599
|
+
new Command10("list").description("List brand profiles").option("-f, --format <format>", "Output format (table, json)", "table").action(async (options) => {
|
|
2600
|
+
await requireAuth();
|
|
2010
2601
|
try {
|
|
2011
2602
|
const result = await listBrandings();
|
|
2012
2603
|
if (result.brandings.length === 0) {
|
|
@@ -2016,7 +2607,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2016
2607
|
info("No brand profiles found");
|
|
2017
2608
|
console.log();
|
|
2018
2609
|
console.log(
|
|
2019
|
-
|
|
2610
|
+
chalk8.gray(
|
|
2020
2611
|
"Create one with: conceptcraft branding extract <url>"
|
|
2021
2612
|
)
|
|
2022
2613
|
);
|
|
@@ -2034,8 +2625,8 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2034
2625
|
}
|
|
2035
2626
|
})
|
|
2036
2627
|
).addCommand(
|
|
2037
|
-
new
|
|
2038
|
-
requireAuth();
|
|
2628
|
+
new Command10("get").description("Get brand profile details").argument("<id>", "Brand profile ID").option("-f, --format <format>", "Output format (summary, json)", "summary").action(async (id, options) => {
|
|
2629
|
+
await requireAuth();
|
|
2039
2630
|
try {
|
|
2040
2631
|
const brand2 = await getBranding(id);
|
|
2041
2632
|
if (options.format === "json") {
|
|
@@ -2045,36 +2636,36 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2045
2636
|
console.log();
|
|
2046
2637
|
keyValue("ID", brand2.id);
|
|
2047
2638
|
keyValue("Name", brand2.name);
|
|
2048
|
-
keyValue("Source URL", brand2.sourceUrl ??
|
|
2049
|
-
keyValue("Default", brand2.isDefault ?
|
|
2639
|
+
keyValue("Source URL", brand2.sourceUrl ?? chalk8.gray("None"));
|
|
2640
|
+
keyValue("Default", brand2.isDefault ? chalk8.green("Yes") : "No");
|
|
2050
2641
|
if (brand2.createdAt) {
|
|
2051
2642
|
keyValue("Created", new Date(brand2.createdAt).toLocaleString());
|
|
2052
2643
|
}
|
|
2053
2644
|
console.log();
|
|
2054
2645
|
if (brand2.primaryColor || brand2.colors.length > 0) {
|
|
2055
|
-
console.log(
|
|
2646
|
+
console.log(chalk8.bold(" Colors"));
|
|
2056
2647
|
if (brand2.primaryColor) {
|
|
2057
|
-
const swatch =
|
|
2648
|
+
const swatch = chalk8.bgHex(brand2.primaryColor)(" ");
|
|
2058
2649
|
console.log(` Primary: ${swatch} ${brand2.primaryColor}`);
|
|
2059
2650
|
}
|
|
2060
2651
|
if (brand2.colors.length > 0) {
|
|
2061
2652
|
console.log(" Palette:");
|
|
2062
2653
|
for (const c of brand2.colors.slice(0, 10)) {
|
|
2063
|
-
const swatch =
|
|
2064
|
-
const role = c.role ?
|
|
2654
|
+
const swatch = chalk8.bgHex(c.hex)(" ");
|
|
2655
|
+
const role = c.role ? chalk8.gray(` (${c.role})`) : "";
|
|
2065
2656
|
console.log(` ${swatch} ${c.hex}${role}`);
|
|
2066
2657
|
}
|
|
2067
2658
|
}
|
|
2068
2659
|
console.log();
|
|
2069
2660
|
}
|
|
2070
2661
|
if (brand2.logoUrl) {
|
|
2071
|
-
console.log(
|
|
2662
|
+
console.log(chalk8.bold(" Logo"));
|
|
2072
2663
|
console.log(` ${brand2.logoUrl}`);
|
|
2073
2664
|
console.log();
|
|
2074
2665
|
}
|
|
2075
2666
|
const typo = brand2.typography;
|
|
2076
2667
|
if (typo && Object.keys(typo).length > 0) {
|
|
2077
|
-
console.log(
|
|
2668
|
+
console.log(chalk8.bold(" Typography"));
|
|
2078
2669
|
if (typo.primary || typo.headings) {
|
|
2079
2670
|
console.log(` Headings: ${typo.primary || typo.headings || "-"}`);
|
|
2080
2671
|
}
|
|
@@ -2084,7 +2675,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2084
2675
|
console.log();
|
|
2085
2676
|
}
|
|
2086
2677
|
if (brand2.confidence || brand2.extractionMethod) {
|
|
2087
|
-
console.log(
|
|
2678
|
+
console.log(chalk8.bold(" Extraction"));
|
|
2088
2679
|
if (brand2.confidence) {
|
|
2089
2680
|
console.log(` Confidence: ${Math.round(brand2.confidence * 100)}%`);
|
|
2090
2681
|
}
|
|
@@ -2093,7 +2684,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2093
2684
|
}
|
|
2094
2685
|
console.log();
|
|
2095
2686
|
}
|
|
2096
|
-
console.log(
|
|
2687
|
+
console.log(chalk8.gray(" Use --format json for full details"));
|
|
2097
2688
|
console.log();
|
|
2098
2689
|
}
|
|
2099
2690
|
} catch (err) {
|
|
@@ -2102,8 +2693,8 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2102
2693
|
}
|
|
2103
2694
|
})
|
|
2104
2695
|
).addCommand(
|
|
2105
|
-
new
|
|
2106
|
-
requireAuth();
|
|
2696
|
+
new Command10("extract").description("Extract brand profile from a website").argument("<url>", "Website URL to extract branding from").option("--team-id <id>", "Team ID").option("--no-save", "Extract without saving").action(async (url, options) => {
|
|
2697
|
+
await requireAuth();
|
|
2107
2698
|
try {
|
|
2108
2699
|
new URL(url.startsWith("http") ? url : `https://${url}`);
|
|
2109
2700
|
} catch {
|
|
@@ -2112,7 +2703,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2112
2703
|
}
|
|
2113
2704
|
const fullUrl = url.startsWith("http") ? url : `https://${url}`;
|
|
2114
2705
|
const teamId = options.teamId ?? getDefaultTeamId();
|
|
2115
|
-
const spinner =
|
|
2706
|
+
const spinner = ora6({ text: `Extracting branding from ${fullUrl}...`, stream: process.stdout }).start();
|
|
2116
2707
|
try {
|
|
2117
2708
|
const result = await extractBranding(fullUrl, teamId);
|
|
2118
2709
|
const brand2 = await getBranding(result.id);
|
|
@@ -2125,28 +2716,28 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2125
2716
|
keyValue("Source URL", brand2.sourceUrl ?? fullUrl);
|
|
2126
2717
|
console.log();
|
|
2127
2718
|
if (brand2.primaryColor || brand2.colors.length > 0) {
|
|
2128
|
-
console.log(
|
|
2719
|
+
console.log(chalk8.bold(" Colors"));
|
|
2129
2720
|
if (brand2.primaryColor) {
|
|
2130
|
-
const swatch =
|
|
2721
|
+
const swatch = chalk8.bgHex(brand2.primaryColor)(" ");
|
|
2131
2722
|
console.log(` Primary: ${swatch} ${brand2.primaryColor}`);
|
|
2132
2723
|
}
|
|
2133
2724
|
if (brand2.colors.length > 0) {
|
|
2134
2725
|
console.log(" Palette:");
|
|
2135
2726
|
for (const c of brand2.colors.slice(0, 6)) {
|
|
2136
|
-
const swatch =
|
|
2137
|
-
const role = c.role ?
|
|
2727
|
+
const swatch = chalk8.bgHex(c.hex)(" ");
|
|
2728
|
+
const role = c.role ? chalk8.gray(` (${c.role})`) : "";
|
|
2138
2729
|
console.log(` ${swatch} ${c.hex}${role}`);
|
|
2139
2730
|
}
|
|
2140
2731
|
}
|
|
2141
2732
|
console.log();
|
|
2142
2733
|
}
|
|
2143
2734
|
if (brand2.logoUrl) {
|
|
2144
|
-
console.log(
|
|
2735
|
+
console.log(chalk8.bold(" Logo"));
|
|
2145
2736
|
console.log(` ${brand2.logoUrl}`);
|
|
2146
2737
|
console.log();
|
|
2147
2738
|
}
|
|
2148
2739
|
if (brand2.confidence) {
|
|
2149
|
-
console.log(
|
|
2740
|
+
console.log(chalk8.bold(" Extraction"));
|
|
2150
2741
|
console.log(` Confidence: ${Math.round(brand2.confidence * 100)}%`);
|
|
2151
2742
|
console.log();
|
|
2152
2743
|
}
|
|
@@ -2159,14 +2750,14 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2159
2750
|
}
|
|
2160
2751
|
})
|
|
2161
2752
|
).addCommand(
|
|
2162
|
-
new
|
|
2163
|
-
requireAuth();
|
|
2753
|
+
new Command10("set-default").description("Set a brand profile as default").argument("<id>", "Brand profile ID").action(async (id) => {
|
|
2754
|
+
await requireAuth();
|
|
2164
2755
|
info(
|
|
2165
2756
|
`To set brand ${id} as default, use the web dashboard.`
|
|
2166
2757
|
);
|
|
2167
2758
|
console.log();
|
|
2168
2759
|
console.log(
|
|
2169
|
-
|
|
2760
|
+
chalk8.gray(
|
|
2170
2761
|
"API support for setting default brand is coming soon."
|
|
2171
2762
|
)
|
|
2172
2763
|
);
|
|
@@ -2174,11 +2765,15 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2174
2765
|
);
|
|
2175
2766
|
|
|
2176
2767
|
// src/commands/derive.ts
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2768
|
+
init_api();
|
|
2769
|
+
init_auth();
|
|
2770
|
+
init_feature_cache();
|
|
2771
|
+
import { Command as Command11 } from "commander";
|
|
2772
|
+
import chalk9 from "chalk";
|
|
2773
|
+
import ora7 from "ora";
|
|
2774
|
+
init_output();
|
|
2180
2775
|
function createBlogCommand() {
|
|
2181
|
-
return new
|
|
2776
|
+
return new Command11("blog").description("Generate a blog post from a presentation").argument("<slug>", "Presentation slug").option("--words <count>", "Target word count (50-2000)", "300").option(
|
|
2182
2777
|
"--tone <tone>",
|
|
2183
2778
|
"Writing tone (professional, casual, educational)",
|
|
2184
2779
|
"professional"
|
|
@@ -2187,7 +2782,7 @@ function createBlogCommand() {
|
|
|
2187
2782
|
"Output format (human, json, markdown)",
|
|
2188
2783
|
"human"
|
|
2189
2784
|
).option("--instructions <text>", "Custom instructions for the blog").option("--no-images", "Exclude image references").option("--toc", "Include table of contents").action(async (presentationId, options) => {
|
|
2190
|
-
requireAuth();
|
|
2785
|
+
await requireAuth();
|
|
2191
2786
|
const wordCount = parseInt(options.words, 10);
|
|
2192
2787
|
if (isNaN(wordCount) || wordCount < 50 || wordCount > 2e3) {
|
|
2193
2788
|
error("Word count must be between 50 and 2000");
|
|
@@ -2198,7 +2793,7 @@ function createBlogCommand() {
|
|
|
2198
2793
|
error(`Invalid tone. Valid options: ${validTones.join(", ")}`);
|
|
2199
2794
|
process.exit(6);
|
|
2200
2795
|
}
|
|
2201
|
-
const spinner =
|
|
2796
|
+
const spinner = ora7("Fetching presentation...").start();
|
|
2202
2797
|
try {
|
|
2203
2798
|
const presentation = await getPresentation(presentationId);
|
|
2204
2799
|
spinner.text = "Generating blog post...";
|
|
@@ -2234,8 +2829,8 @@ function createBlogCommand() {
|
|
|
2234
2829
|
console.log(content);
|
|
2235
2830
|
} else {
|
|
2236
2831
|
console.log();
|
|
2237
|
-
console.log(
|
|
2238
|
-
console.log(
|
|
2832
|
+
console.log(chalk9.bold(`Blog: ${presentation.title}`));
|
|
2833
|
+
console.log(chalk9.gray("\u2500".repeat(40)));
|
|
2239
2834
|
console.log();
|
|
2240
2835
|
console.log(content);
|
|
2241
2836
|
console.log();
|
|
@@ -2248,35 +2843,35 @@ function createBlogCommand() {
|
|
|
2248
2843
|
});
|
|
2249
2844
|
}
|
|
2250
2845
|
function createTweetsCommand() {
|
|
2251
|
-
return new
|
|
2252
|
-
requireAuth();
|
|
2846
|
+
return new Command11("tweets").description("Generate tweets from a presentation").argument("<slug>", "Presentation slug").option("--count <n>", "Number of tweets to generate", "5").option("-f, --format <format>", "Output format (human, json)", "human").action(async (presentationId, options) => {
|
|
2847
|
+
await requireAuth();
|
|
2253
2848
|
info("Tweet generation coming soon.");
|
|
2254
2849
|
});
|
|
2255
2850
|
}
|
|
2256
2851
|
function createLinkedInCommand() {
|
|
2257
|
-
return new
|
|
2258
|
-
requireAuth();
|
|
2852
|
+
return new Command11("linkedin").description("Generate a LinkedIn carousel from a presentation").argument("<slug>", "Presentation slug").option("-f, --format <format>", "Output format (human, json)", "human").action(async (presentationId) => {
|
|
2853
|
+
await requireAuth();
|
|
2259
2854
|
info("LinkedIn carousel generation coming soon.");
|
|
2260
2855
|
});
|
|
2261
2856
|
}
|
|
2262
2857
|
function createQuestionsCommand() {
|
|
2263
|
-
return new
|
|
2264
|
-
requireAuth();
|
|
2858
|
+
return new Command11("questions").description("Generate questions for a presentation").argument("<slug>", "Presentation slug").option("--count <n>", "Number of questions", "10").option("-f, --format <format>", "Output format (human, json)", "human").action(async (presentationId, options) => {
|
|
2859
|
+
await requireAuth();
|
|
2265
2860
|
info("Question generation coming soon.");
|
|
2266
2861
|
});
|
|
2267
2862
|
}
|
|
2268
2863
|
function createCheatsheetCommand() {
|
|
2269
|
-
return new
|
|
2864
|
+
return new Command11("cheatsheet").description("Generate a presenter cheat sheet").argument("<slug>", "Presentation slug").option("--mode <mode>", "Cheat sheet mode (qa-prep, talking-points)", "qa-prep").option(
|
|
2270
2865
|
"-f, --format <format>",
|
|
2271
2866
|
"Output format (human, json, markdown)",
|
|
2272
2867
|
"human"
|
|
2273
2868
|
).action(async (presentationId, options) => {
|
|
2274
|
-
requireAuth();
|
|
2869
|
+
await requireAuth();
|
|
2275
2870
|
info("Cheat sheet generation coming soon.");
|
|
2276
2871
|
});
|
|
2277
2872
|
}
|
|
2278
2873
|
function buildDeriveCommand() {
|
|
2279
|
-
const derive = new
|
|
2874
|
+
const derive = new Command11("derive").description("Generate derivative content from a presentation");
|
|
2280
2875
|
const flags = getCachedFlags();
|
|
2281
2876
|
if (!flags) {
|
|
2282
2877
|
return derive;
|
|
@@ -2300,33 +2895,39 @@ function buildDeriveCommand() {
|
|
|
2300
2895
|
}
|
|
2301
2896
|
|
|
2302
2897
|
// src/commands/ideas.ts
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2898
|
+
init_auth();
|
|
2899
|
+
init_output();
|
|
2900
|
+
import { Command as Command12 } from "commander";
|
|
2901
|
+
import chalk10 from "chalk";
|
|
2902
|
+
var ideasCommand = new Command12("ideas").description("Generate presentation topic ideas").option("--context <text>", "Custom context for idea generation").option("--count <n>", "Number of ideas to generate", "5").option("-f, --format <format>", "Output format (human, json)", "human").action(async (options) => {
|
|
2903
|
+
await requireAuth();
|
|
2307
2904
|
info("Idea generation is available in the web dashboard.");
|
|
2308
2905
|
console.log();
|
|
2309
2906
|
console.log(
|
|
2310
|
-
|
|
2907
|
+
chalk10.gray("The CLI will support idea generation in a future release.")
|
|
2311
2908
|
);
|
|
2312
2909
|
console.log();
|
|
2313
2910
|
console.log("For now, try these approaches:");
|
|
2314
2911
|
console.log(
|
|
2315
|
-
|
|
2912
|
+
chalk10.gray(
|
|
2316
2913
|
" 1. Visit the ConceptCraft dashboard and use the idea generator"
|
|
2317
2914
|
)
|
|
2318
2915
|
);
|
|
2319
2916
|
console.log(
|
|
2320
|
-
|
|
2917
|
+
chalk10.gray(" 2. Create a presentation with a broad topic and refine it")
|
|
2321
2918
|
);
|
|
2322
2919
|
console.log();
|
|
2323
2920
|
});
|
|
2324
2921
|
|
|
2325
2922
|
// src/commands/whoami.ts
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2923
|
+
init_api();
|
|
2924
|
+
init_auth();
|
|
2925
|
+
init_feature_cache();
|
|
2926
|
+
init_output();
|
|
2927
|
+
import { Command as Command13 } from "commander";
|
|
2928
|
+
import chalk11 from "chalk";
|
|
2929
|
+
var whoamiCommand = new Command13("whoami").description("Show current user and team information").option("-f, --format <format>", "Output format (human, json)", "human").action(async (options) => {
|
|
2930
|
+
await requireAuth();
|
|
2330
2931
|
try {
|
|
2331
2932
|
const result = await whoami();
|
|
2332
2933
|
refreshInBackground();
|
|
@@ -2361,12 +2962,12 @@ var whoamiCommand = new Command11("whoami").description("Show current user and t
|
|
|
2361
2962
|
header("All Teams");
|
|
2362
2963
|
console.log();
|
|
2363
2964
|
for (const team of result.teams) {
|
|
2364
|
-
const current = team.isCurrent ?
|
|
2965
|
+
const current = team.isCurrent ? chalk11.green(" (current)") : "";
|
|
2365
2966
|
console.log(
|
|
2366
|
-
` ${
|
|
2967
|
+
` ${chalk11.bold(team.name)}${current}`
|
|
2367
2968
|
);
|
|
2368
2969
|
console.log(
|
|
2369
|
-
|
|
2970
|
+
chalk11.gray(` ID: ${team.id} | Plan: ${team.planName} | Role: ${team.role}`)
|
|
2370
2971
|
);
|
|
2371
2972
|
}
|
|
2372
2973
|
}
|
|
@@ -2378,17 +2979,23 @@ var whoamiCommand = new Command11("whoami").description("Show current user and t
|
|
|
2378
2979
|
});
|
|
2379
2980
|
|
|
2380
2981
|
// src/commands/skill.ts
|
|
2381
|
-
|
|
2382
|
-
import
|
|
2982
|
+
init_output();
|
|
2983
|
+
import { Command as Command14 } from "commander";
|
|
2984
|
+
import chalk12 from "chalk";
|
|
2383
2985
|
import { mkdirSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
2384
2986
|
import { join } from "path";
|
|
2385
2987
|
import { homedir } from "os";
|
|
2386
|
-
|
|
2387
|
-
name
|
|
2988
|
+
function generateSkillContent(b) {
|
|
2989
|
+
const cmd2 = b.name;
|
|
2990
|
+
const pkg = b.packageName;
|
|
2991
|
+
const url = b.apiUrl;
|
|
2992
|
+
const name = b.displayName;
|
|
2993
|
+
return `---
|
|
2994
|
+
name: ${cmd2}
|
|
2388
2995
|
description: Create AI-powered presentations from code, documentation, files, or any content. Use when the user wants to generate slides, presentations, or decks about their project, codebase, research, or ideas.
|
|
2389
2996
|
---
|
|
2390
2997
|
|
|
2391
|
-
#
|
|
2998
|
+
# ${name} CLI
|
|
2392
2999
|
|
|
2393
3000
|
Create professional presentations directly from your terminal. The CLI generates AI-powered slides from any context you provide - text, files, URLs, or piped content.
|
|
2394
3001
|
|
|
@@ -2396,16 +3003,16 @@ Create professional presentations directly from your terminal. The CLI generates
|
|
|
2396
3003
|
|
|
2397
3004
|
\`\`\`bash
|
|
2398
3005
|
# Install globally
|
|
2399
|
-
npm install -g
|
|
3006
|
+
npm install -g ${pkg}
|
|
2400
3007
|
|
|
2401
|
-
# Configure API key (get from
|
|
2402
|
-
|
|
3008
|
+
# Configure API key (get from ${url}/settings/api-keys)
|
|
3009
|
+
${cmd2} config init
|
|
2403
3010
|
\`\`\`
|
|
2404
3011
|
|
|
2405
3012
|
## Core Workflow
|
|
2406
3013
|
|
|
2407
3014
|
1. **Gather context** - Read relevant files, code, or documentation
|
|
2408
|
-
2. **Create presentation** - Pass context to
|
|
3015
|
+
2. **Create presentation** - Pass context to \`${cmd2} create\`
|
|
2409
3016
|
3. **Share URL** - Return the presentation link to the user
|
|
2410
3017
|
|
|
2411
3018
|
## Commands
|
|
@@ -2416,22 +3023,22 @@ Context is **required**. Provide it via one of these methods:
|
|
|
2416
3023
|
|
|
2417
3024
|
\`\`\`bash
|
|
2418
3025
|
# Upload files (PDFs, PPTX, images, docs)
|
|
2419
|
-
|
|
3026
|
+
${cmd2} create "Product Overview" --file ./deck.pptx --file ./logo.png
|
|
2420
3027
|
|
|
2421
3028
|
# Direct text context
|
|
2422
|
-
|
|
3029
|
+
${cmd2} create "Topic Title" --context "Key points, data, facts..."
|
|
2423
3030
|
|
|
2424
3031
|
# From a text file
|
|
2425
|
-
|
|
3032
|
+
${cmd2} create "Topic Title" --context-file ./notes.md
|
|
2426
3033
|
|
|
2427
3034
|
# Pipe content (auto-detected)
|
|
2428
|
-
cat README.md |
|
|
3035
|
+
cat README.md | ${cmd2} create "Project Overview"
|
|
2429
3036
|
|
|
2430
3037
|
# From URLs (scraped automatically)
|
|
2431
|
-
|
|
3038
|
+
${cmd2} create "Competitor Analysis" --sources https://example.com/report
|
|
2432
3039
|
|
|
2433
3040
|
# Combine multiple sources
|
|
2434
|
-
cat src/auth/*.ts |
|
|
3041
|
+
cat src/auth/*.ts | ${cmd2} create "Auth System" \\
|
|
2435
3042
|
--file ./architecture.png \\
|
|
2436
3043
|
--context "Focus on security patterns"
|
|
2437
3044
|
\`\`\`
|
|
@@ -2456,28 +3063,28 @@ cat src/auth/*.ts | conceptcraft create "Auth System" \\
|
|
|
2456
3063
|
|
|
2457
3064
|
\`\`\`bash
|
|
2458
3065
|
# Check authentication
|
|
2459
|
-
|
|
3066
|
+
${cmd2} whoami
|
|
2460
3067
|
|
|
2461
3068
|
# List presentations
|
|
2462
|
-
|
|
2463
|
-
|
|
3069
|
+
${cmd2} list
|
|
3070
|
+
${cmd2} list --format json
|
|
2464
3071
|
|
|
2465
3072
|
# Get presentation details
|
|
2466
|
-
|
|
3073
|
+
${cmd2} get <id-or-slug>
|
|
2467
3074
|
|
|
2468
3075
|
# Export to ZIP
|
|
2469
|
-
|
|
3076
|
+
${cmd2} export <id-or-slug> -o presentation.zip
|
|
2470
3077
|
|
|
2471
3078
|
# Import presentation
|
|
2472
|
-
|
|
3079
|
+
${cmd2} import ./presentation.zip
|
|
2473
3080
|
|
|
2474
3081
|
# Manage branding
|
|
2475
|
-
|
|
2476
|
-
|
|
3082
|
+
${cmd2} branding list
|
|
3083
|
+
${cmd2} branding extract https://company.com
|
|
2477
3084
|
|
|
2478
3085
|
# Install/manage this skill
|
|
2479
|
-
|
|
2480
|
-
|
|
3086
|
+
${cmd2} skill install
|
|
3087
|
+
${cmd2} skill show
|
|
2481
3088
|
\`\`\`
|
|
2482
3089
|
|
|
2483
3090
|
## Examples
|
|
@@ -2486,7 +3093,7 @@ conceptcraft skill show
|
|
|
2486
3093
|
|
|
2487
3094
|
\`\`\`bash
|
|
2488
3095
|
# Read the relevant files and create presentation
|
|
2489
|
-
cat src/lib/auth.ts src/lib/session.ts |
|
|
3096
|
+
cat src/lib/auth.ts src/lib/session.ts | ${cmd2} create "Authentication System" \\
|
|
2490
3097
|
--slides 8 --tone educational --audience "New developers" \\
|
|
2491
3098
|
--goal train
|
|
2492
3099
|
\`\`\`
|
|
@@ -2494,7 +3101,7 @@ cat src/lib/auth.ts src/lib/session.ts | conceptcraft create "Authentication Sys
|
|
|
2494
3101
|
### Technical Documentation with Diagrams
|
|
2495
3102
|
|
|
2496
3103
|
\`\`\`bash
|
|
2497
|
-
|
|
3104
|
+
${cmd2} create "API Reference" \\
|
|
2498
3105
|
--file ./docs/api.md \\
|
|
2499
3106
|
--file ./diagrams/architecture.png \\
|
|
2500
3107
|
--mode best --amount detailed \\
|
|
@@ -2504,14 +3111,14 @@ conceptcraft create "API Reference" \\
|
|
|
2504
3111
|
### Quick Project Overview
|
|
2505
3112
|
|
|
2506
3113
|
\`\`\`bash
|
|
2507
|
-
cat README.md package.json |
|
|
3114
|
+
cat README.md package.json | ${cmd2} create "Project Introduction" \\
|
|
2508
3115
|
-m instant --slides 5
|
|
2509
3116
|
\`\`\`
|
|
2510
3117
|
|
|
2511
3118
|
### Sales Deck from Existing Presentation
|
|
2512
3119
|
|
|
2513
3120
|
\`\`\`bash
|
|
2514
|
-
|
|
3121
|
+
${cmd2} create "Product Demo" \\
|
|
2515
3122
|
--file ./existing-deck.pptx \\
|
|
2516
3123
|
--goal persuade \\
|
|
2517
3124
|
--audience "Enterprise buyers" \\
|
|
@@ -2521,7 +3128,7 @@ conceptcraft create "Product Demo" \\
|
|
|
2521
3128
|
### Research Presentation
|
|
2522
3129
|
|
|
2523
3130
|
\`\`\`bash
|
|
2524
|
-
|
|
3131
|
+
${cmd2} create "Market Analysis" \\
|
|
2525
3132
|
--file ./research.pdf \\
|
|
2526
3133
|
--sources https://report.com/industry.pdf \\
|
|
2527
3134
|
--tone formal --audience "Executive team" \\
|
|
@@ -2538,12 +3145,12 @@ Successful creation returns:
|
|
|
2538
3145
|
Slides: 8
|
|
2539
3146
|
Generated in: 45s \xB7 12,500 tokens
|
|
2540
3147
|
|
|
2541
|
-
Open:
|
|
3148
|
+
Open: ${url}/en/view/presentations/auth-system-v1-abc123
|
|
2542
3149
|
\`\`\`
|
|
2543
3150
|
|
|
2544
3151
|
For scripting, use JSON output:
|
|
2545
3152
|
\`\`\`bash
|
|
2546
|
-
URL=$(
|
|
3153
|
+
URL=$(${cmd2} create "Demo" --context "..." -o json | jq -r '.viewUrl')
|
|
2547
3154
|
\`\`\`
|
|
2548
3155
|
|
|
2549
3156
|
## Best Practices
|
|
@@ -2565,15 +3172,16 @@ URL=$(conceptcraft create "Demo" --context "..." -o json | jq -r '.viewUrl')
|
|
|
2565
3172
|
|
|
2566
3173
|
\`\`\`bash
|
|
2567
3174
|
# Check if authenticated
|
|
2568
|
-
|
|
3175
|
+
${cmd2} whoami
|
|
2569
3176
|
|
|
2570
3177
|
# Verify API key
|
|
2571
|
-
|
|
3178
|
+
${cmd2} config show
|
|
2572
3179
|
|
|
2573
3180
|
# Debug mode
|
|
2574
|
-
|
|
3181
|
+
${cmd2} create "Test" --context "test" --debug
|
|
2575
3182
|
\`\`\`
|
|
2576
3183
|
`;
|
|
3184
|
+
}
|
|
2577
3185
|
var EDITORS = [
|
|
2578
3186
|
{ name: "Claude Code", dir: ".claude" },
|
|
2579
3187
|
{ name: "Cursor", dir: ".cursor" },
|
|
@@ -2582,29 +3190,30 @@ var EDITORS = [
|
|
|
2582
3190
|
{ name: "Windsurf", dir: ".windsurf" },
|
|
2583
3191
|
{ name: "Agent", dir: ".agent" }
|
|
2584
3192
|
];
|
|
2585
|
-
var skillCommand = new
|
|
3193
|
+
var skillCommand = new Command14("skill").description(`Manage ${brand.displayName} skill for AI coding assistants`).addHelpText(
|
|
2586
3194
|
"after",
|
|
2587
3195
|
`
|
|
2588
|
-
${
|
|
2589
|
-
${
|
|
2590
|
-
$
|
|
3196
|
+
${chalk12.bold("Examples:")}
|
|
3197
|
+
${chalk12.gray("# Install skill for all detected editors")}
|
|
3198
|
+
$ ${brand.name} skill install
|
|
2591
3199
|
|
|
2592
|
-
${
|
|
2593
|
-
$
|
|
3200
|
+
${chalk12.gray("# Install to specific directory")}
|
|
3201
|
+
$ ${brand.name} skill install --dir ~/.claude
|
|
2594
3202
|
|
|
2595
|
-
${
|
|
2596
|
-
$
|
|
3203
|
+
${chalk12.gray("# Show skill content")}
|
|
3204
|
+
$ ${brand.name} skill show
|
|
2597
3205
|
`
|
|
2598
3206
|
);
|
|
2599
|
-
skillCommand.command("install").description(
|
|
3207
|
+
skillCommand.command("install").description(`Install the ${brand.displayName} skill for AI coding assistants`).option("-d, --dir <path>", "Install to specific directory").option("-g, --global", "Install globally (to home directory)", true).option("-l, --local", "Install locally (to current directory)").option("-f, --force", "Overwrite existing skill files").action(async (options) => {
|
|
2600
3208
|
const installed = [];
|
|
2601
3209
|
const skipped = [];
|
|
2602
3210
|
const errors = [];
|
|
2603
3211
|
const baseDir = options.local ? process.cwd() : homedir();
|
|
3212
|
+
const skillContent = generateSkillContent(brand);
|
|
2604
3213
|
if (options.dir) {
|
|
2605
|
-
const skillPath = join(options.dir, "skills",
|
|
3214
|
+
const skillPath = join(options.dir, "skills", brand.name);
|
|
2606
3215
|
try {
|
|
2607
|
-
installSkill(skillPath, options.force);
|
|
3216
|
+
installSkill(skillPath, skillContent, options.force);
|
|
2608
3217
|
installed.push(options.dir);
|
|
2609
3218
|
} catch (err) {
|
|
2610
3219
|
errors.push(`${options.dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2612,7 +3221,7 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
|
|
|
2612
3221
|
} else {
|
|
2613
3222
|
for (const editor of EDITORS) {
|
|
2614
3223
|
const editorDir = join(baseDir, editor.dir);
|
|
2615
|
-
const skillPath = join(editorDir, "skills",
|
|
3224
|
+
const skillPath = join(editorDir, "skills", brand.name);
|
|
2616
3225
|
const skillFile = join(skillPath, "SKILL.md");
|
|
2617
3226
|
if (!existsSync2(editorDir)) {
|
|
2618
3227
|
continue;
|
|
@@ -2622,7 +3231,7 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
|
|
|
2622
3231
|
continue;
|
|
2623
3232
|
}
|
|
2624
3233
|
try {
|
|
2625
|
-
installSkill(skillPath, options.force);
|
|
3234
|
+
installSkill(skillPath, skillContent, options.force);
|
|
2626
3235
|
installed.push(editor.name);
|
|
2627
3236
|
} catch (err) {
|
|
2628
3237
|
errors.push(`${editor.name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2638,7 +3247,7 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
|
|
|
2638
3247
|
if (skipped.length > 0) {
|
|
2639
3248
|
console.log();
|
|
2640
3249
|
info(`Skipped (already exists): ${skipped.join(", ")}`);
|
|
2641
|
-
console.log(
|
|
3250
|
+
console.log(chalk12.gray(" Use --force to overwrite"));
|
|
2642
3251
|
}
|
|
2643
3252
|
if (errors.length > 0) {
|
|
2644
3253
|
console.log();
|
|
@@ -2649,20 +3258,20 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
|
|
|
2649
3258
|
if (installed.length === 0 && skipped.length === 0 && errors.length === 0) {
|
|
2650
3259
|
info("No supported AI coding assistants detected.");
|
|
2651
3260
|
console.log();
|
|
2652
|
-
console.log(
|
|
2653
|
-
console.log(
|
|
3261
|
+
console.log(chalk12.gray("Supported editors: " + EDITORS.map((e) => e.name).join(", ")));
|
|
3262
|
+
console.log(chalk12.gray("Use --dir <path> to install to a specific directory"));
|
|
2654
3263
|
}
|
|
2655
3264
|
console.log();
|
|
2656
3265
|
});
|
|
2657
3266
|
skillCommand.command("show").description("Display the skill content").action(() => {
|
|
2658
|
-
console.log(
|
|
3267
|
+
console.log(generateSkillContent(brand));
|
|
2659
3268
|
});
|
|
2660
|
-
skillCommand.command("uninstall").description(
|
|
3269
|
+
skillCommand.command("uninstall").description(`Remove the ${brand.displayName} skill from AI coding assistants`).option("-g, --global", "Uninstall globally (from home directory)", true).option("-l, --local", "Uninstall locally (from current directory)").action(async (options) => {
|
|
2661
3270
|
const { rmSync } = await import("fs");
|
|
2662
3271
|
const removed = [];
|
|
2663
3272
|
const baseDir = options.local ? process.cwd() : homedir();
|
|
2664
3273
|
for (const editor of EDITORS) {
|
|
2665
|
-
const skillPath = join(baseDir, editor.dir, "skills",
|
|
3274
|
+
const skillPath = join(baseDir, editor.dir, "skills", brand.name);
|
|
2666
3275
|
if (existsSync2(skillPath)) {
|
|
2667
3276
|
try {
|
|
2668
3277
|
rmSync(skillPath, { recursive: true });
|
|
@@ -2680,21 +3289,23 @@ skillCommand.command("uninstall").description("Remove the ConceptCraft skill fro
|
|
|
2680
3289
|
}
|
|
2681
3290
|
console.log();
|
|
2682
3291
|
});
|
|
2683
|
-
function installSkill(skillPath, force) {
|
|
3292
|
+
function installSkill(skillPath, content, force) {
|
|
2684
3293
|
const skillFile = join(skillPath, "SKILL.md");
|
|
2685
3294
|
mkdirSync(skillPath, { recursive: true });
|
|
2686
|
-
writeFileSync(skillFile,
|
|
3295
|
+
writeFileSync(skillFile, content, "utf-8");
|
|
2687
3296
|
}
|
|
2688
3297
|
|
|
2689
3298
|
// src/index.ts
|
|
2690
|
-
var VERSION = "0.1.
|
|
2691
|
-
var program = new
|
|
3299
|
+
var VERSION = "0.1.4";
|
|
3300
|
+
var program = new Command15();
|
|
2692
3301
|
var cmdName = brand.commands[0];
|
|
2693
3302
|
program.name(cmdName).description(brand.description).version(VERSION, "-v, --version", "Show version number").option("--debug", "Enable debug logging").option("--no-color", "Disable colored output").configureOutput({
|
|
2694
3303
|
outputError: (str, write) => {
|
|
2695
|
-
write(
|
|
3304
|
+
write(chalk13.red(str));
|
|
2696
3305
|
}
|
|
2697
3306
|
});
|
|
3307
|
+
program.addCommand(loginCommand);
|
|
3308
|
+
program.addCommand(logoutCommand);
|
|
2698
3309
|
program.addCommand(configCommand);
|
|
2699
3310
|
program.addCommand(createCommand);
|
|
2700
3311
|
program.addCommand(listCommand);
|
|
@@ -2711,7 +3322,7 @@ if (deriveCommand.commands.length > 0) {
|
|
|
2711
3322
|
program.addCommand(deriveCommand);
|
|
2712
3323
|
}
|
|
2713
3324
|
program.on("command:*", (operands) => {
|
|
2714
|
-
console.error(
|
|
3325
|
+
console.error(chalk13.red(`Error: Unknown command '${operands[0]}'`));
|
|
2715
3326
|
console.error();
|
|
2716
3327
|
console.error(`Run '${cmdName} --help' to see available commands.`);
|
|
2717
3328
|
process.exit(1);
|
|
@@ -2719,38 +3330,38 @@ program.on("command:*", (operands) => {
|
|
|
2719
3330
|
program.addHelpText(
|
|
2720
3331
|
"after",
|
|
2721
3332
|
`
|
|
2722
|
-
${
|
|
2723
|
-
${
|
|
2724
|
-
$ ${cmdName}
|
|
3333
|
+
${chalk13.bold("Examples:")}
|
|
3334
|
+
${chalk13.gray("# Log in (opens browser)")}
|
|
3335
|
+
$ ${cmdName} login
|
|
2725
3336
|
|
|
2726
|
-
${
|
|
3337
|
+
${chalk13.gray("# Create a presentation (recommended pattern)")}
|
|
2727
3338
|
$ ${cmdName} create "Q4 Business Review" \\
|
|
2728
3339
|
-n 12 -m best \\
|
|
2729
3340
|
--audience "Executive leadership team" \\
|
|
2730
3341
|
--context "Revenue: $50M (+25% YoY), New customers: 150"
|
|
2731
3342
|
|
|
2732
|
-
${
|
|
3343
|
+
${chalk13.gray("# Create from a file")}
|
|
2733
3344
|
$ ${cmdName} create "Product Launch" \\
|
|
2734
3345
|
-n 10 -m balanced \\
|
|
2735
3346
|
--audience "Sales team and partners" \\
|
|
2736
3347
|
--context-file ./launch-brief.md
|
|
2737
3348
|
|
|
2738
|
-
${
|
|
3349
|
+
${chalk13.gray("# Create from URLs")}
|
|
2739
3350
|
$ ${cmdName} create "Market Analysis" \\
|
|
2740
3351
|
-n 15 -m best \\
|
|
2741
3352
|
--audience "Strategy team" \\
|
|
2742
3353
|
--sources https://example.com/report.pdf
|
|
2743
3354
|
|
|
2744
|
-
${
|
|
3355
|
+
${chalk13.gray("# List and export")}
|
|
2745
3356
|
$ ${cmdName} list --format table
|
|
2746
3357
|
$ ${cmdName} export <slug> -o backup.zip
|
|
2747
3358
|
|
|
2748
|
-
${
|
|
2749
|
-
|
|
2750
|
-
|
|
3359
|
+
${chalk13.bold("Authentication:")}
|
|
3360
|
+
Run '${cmdName} login' to authenticate (recommended).
|
|
3361
|
+
Or set CC_MINDFRAMES_API_KEY environment variable for API key auth.
|
|
2751
3362
|
|
|
2752
|
-
${
|
|
2753
|
-
${
|
|
3363
|
+
${chalk13.bold("More Info:")}
|
|
3364
|
+
${chalk13.blue(brand.docsUrl)}
|
|
2754
3365
|
`
|
|
2755
3366
|
);
|
|
2756
3367
|
program.parse();
|