@conceptcraft/mindframes 0.1.2 → 0.1.3

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 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
- // src/index.ts
5
- import { Command as Command13 } from "commander";
6
- import chalk12 from "chalk";
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 COMMAND_TO_BRAND = {
31
- cc: "conceptcraft",
32
- conceptcraft: "conceptcraft",
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/auth.ts
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
- SUCCESS: 0,
126
- GENERAL_ERROR: 1,
127
- AUTH_ERROR: 2,
128
- NOT_FOUND: 3,
129
- RATE_LIMIT: 4,
130
- NETWORK_ERROR: 5,
131
- INVALID_INPUT: 6
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
- function requireAuth() {
136
- if (!hasApiKey()) {
137
- console.error(chalk.red("Error: Not authenticated."));
138
- console.error();
139
- console.error("To authenticate, either:");
140
- console.error(
141
- chalk.gray(" 1. Set the CC_MINDFRAMES_API_KEY environment variable")
142
- );
143
- console.error(
144
- chalk.gray(" 2. Run: mindframes config init")
145
- );
146
- console.error();
147
- console.error(
148
- chalk.gray(`Config file location: ${getConfigPath()}`)
149
- );
150
- process.exit(EXIT_CODES.AUTH_ERROR);
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
- var ApiError = class extends Error {
174
- constructor(message, statusCode, exitCode = 1) {
175
- super(message);
176
- this.statusCode = statusCode;
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 (!apiKey) {
427
+ if (!hasAuth()) {
185
428
  throw new ApiError(
186
- "API key not configured. Run 'mindframes config init' or set CC_MINDFRAMES_API_KEY environment variable.",
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
- "x-api-key": apiKey,
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 (!apiKey) {
496
+ if (!hasAuth()) {
254
497
  throw new ApiError(
255
- "API key not configured. Run 'mindframes config init' or set CC_MINDFRAMES_API_KEY environment variable.",
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
- "x-api-key": apiKey
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 (!apiKey) {
572
+ if (!hasAuth()) {
330
573
  throw new ApiError(
331
- "API key not configured. Run 'mindframes config init' or set CC_MINDFRAMES_API_KEY environment variable.",
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
- "x-api-key": apiKey
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 (!apiKey) {
732
+ if (!hasAuth()) {
487
733
  throw new ApiError(
488
- "API key not configured. Run 'mindframes config init' or set CC_MINDFRAMES_API_KEY environment variable.",
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
- "x-api-key": apiKey
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 (!apiKey) {
766
+ if (!hasAuth()) {
521
767
  throw new ApiError(
522
- "API key not configured. Run 'mindframes config init' or set CC_MINDFRAMES_API_KEY environment variable.",
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 (!apiKey) {
819
+ if (!hasAuth()) {
576
820
  throw new ApiError(
577
- "API key not configured. Run 'mindframes config init' or set CC_MINDFRAMES_API_KEY environment variable.",
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
- "x-api-key": apiKey
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,406 @@ 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/lib/output.ts
703
- import chalk2 from "chalk";
704
- import Table from "cli-table3";
705
- function isTTY() {
706
- return process.stdout.isTTY ?? false;
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 success(message, format = "human") {
709
- if (format === "quiet") return;
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 error(message, format = "human") {
714
- if (format === "quiet") return;
715
- if (format === "json") {
716
- console.error(JSON.stringify({ error: message }));
717
- return;
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
- console.error(chalk2.red("\u2717"), message);
1007
+ throw new Error(`No available port found between ${start} and ${end}`);
720
1008
  }
721
- function warn(message, format = "human") {
722
- if (format === "quiet") return;
723
- if (format === "json") return;
724
- console.warn(chalk2.yellow("\u26A0"), message);
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();
725
1015
  }
726
- function info(message, format = "human") {
727
- if (format === "quiet") return;
728
- if (format === "json") return;
729
- console.log(chalk2.blue("\u2139"), message);
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
+ });
1144
+ }
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 buildViewUrl(slug, language = "en") {
732
- if (!slug) return "N/A";
1154
+ async function runLoginFlow(options) {
733
1155
  const apiUrl = getApiUrl();
734
- return `${apiUrl}/${language}/view/presentations/${slug}`;
735
- }
736
- function formatPresentationTable(presentations, options = {}) {
737
- const { showLinks = false, language = "en" } = options;
738
- const truncate = (str, len) => str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
739
- const shortDateTime = (dateStr) => {
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
- const d = new Date(dateStr);
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
- if (showLinks) {
754
- const table2 = new Table({
755
- head: [
756
- chalk2.cyan("Slug"),
757
- chalk2.cyan("Title"),
758
- chalk2.cyan("Slides"),
759
- chalk2.cyan("Created"),
760
- chalk2.cyan("URL")
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
- const table = new Table({
775
- head: [
776
- chalk2.cyan("Slug"),
777
- chalk2.cyan("Title"),
778
- chalk2.cyan("Slides"),
779
- chalk2.cyan("Mode"),
780
- chalk2.cyan("Created")
781
- ],
782
- colWidths: [32, 28, 8, 11, 18]
783
- });
784
- for (const p of presentations) {
785
- table.push([
786
- p.slug || p.id.slice(0, 8),
787
- p.title || "Untitled",
788
- String(p.numberOfSlides || "-"),
789
- p.mode || "-",
790
- shortDateTime(p.createdAt)
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
+ },
1264
+ mindframes: {
1265
+ id: "mindframes",
1266
+ name: "mindframes",
1267
+ displayName: "Mindframes",
1268
+ description: "CLI tool for Mindframes presentation generation",
1269
+ commands: ["mf", "mindframes"],
1270
+ apiUrl: "https://mindframes.app",
1271
+ docsUrl: "https://docs.mindframes.app"
792
1272
  }
793
- return table.toString();
794
- }
795
- function formatPresentationIds(presentations) {
796
- return presentations.map((p) => p.slug || p.id).join("\n");
1273
+ };
1274
+ var COMMAND_TO_BRAND = {
1275
+ cc: "conceptcraft",
1276
+ conceptcraft: "conceptcraft",
1277
+ mf: "mindframes",
1278
+ mindframes: "mindframes"
1279
+ };
1280
+ function detectBrand() {
1281
+ const binaryPath = process.argv[1] || "";
1282
+ const binaryName = path.basename(binaryPath).replace(/\.(js|ts)$/, "");
1283
+ const brandId = COMMAND_TO_BRAND[binaryName];
1284
+ if (brandId) {
1285
+ return BRANDS[brandId];
1286
+ }
1287
+ for (const [cmd2, id] of Object.entries(COMMAND_TO_BRAND)) {
1288
+ if (binaryPath.includes(`/${cmd2}`) || binaryPath.includes(`\\${cmd2}`)) {
1289
+ return BRANDS[id];
1290
+ }
1291
+ }
1292
+ return BRANDS.mindframes;
797
1293
  }
798
- function formatBrandingTable(brandings) {
799
- const sorted = [...brandings].sort((a, b) => {
800
- if (a.isDefault && !b.isDefault) return -1;
801
- if (!a.isDefault && b.isDefault) return 1;
802
- const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
803
- const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
804
- return dateB - dateA;
805
- });
806
- const formatUrl = (url) => {
807
- if (!url) return "-";
1294
+ var brand = detectBrand();
1295
+
1296
+ // src/index.ts
1297
+ init_login();
1298
+
1299
+ // src/commands/logout.ts
1300
+ init_config();
1301
+ init_feature_cache();
1302
+ init_output();
1303
+ import { Command as Command2 } from "commander";
1304
+ import { confirm } from "@inquirer/prompts";
1305
+ var logoutCommand = new Command2("logout").description("Log out and clear authentication").option("--all", "Clear all config including API key").action(async (options) => {
1306
+ console.log();
1307
+ const hasTokens = hasOAuthTokens();
1308
+ const hasKey = hasApiKey();
1309
+ if (!hasTokens && !hasKey) {
1310
+ warn("You are not logged in.");
1311
+ console.log();
1312
+ return;
1313
+ }
1314
+ if (options.all) {
808
1315
  try {
809
- const u = new URL(url);
810
- return u.href;
1316
+ const confirmed = await confirm({
1317
+ message: "Clear all configuration including API key?",
1318
+ default: false
1319
+ });
1320
+ if (confirmed) {
1321
+ clearConfig();
1322
+ invalidateCache();
1323
+ success("All configuration cleared.");
1324
+ } else {
1325
+ info("Cancelled.");
1326
+ }
811
1327
  } catch {
812
- return url.startsWith("http") ? url : `https://${url}`;
1328
+ info("Cancelled.");
1329
+ }
1330
+ } else {
1331
+ clearOAuthTokens();
1332
+ invalidateCache();
1333
+ success("Logged out successfully.");
1334
+ if (hasKey) {
1335
+ info("Note: Your API key is still configured. Use --all to clear everything.");
813
1336
  }
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
1337
  }
845
- }
846
- function header(text) {
847
1338
  console.log();
848
- console.log(chalk2.bold(text));
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
- }
1339
+ });
861
1340
 
862
1341
  // src/commands/config.ts
863
- var configCommand = new Command("config").description("Manage CLI configuration").addCommand(
864
- new Command("init").description("Initialize configuration interactively").action(async () => {
1342
+ init_config();
1343
+ init_auth();
1344
+ init_api();
1345
+ init_feature_cache();
1346
+ init_output();
1347
+ import { Command as Command3 } from "commander";
1348
+ import chalk4 from "chalk";
1349
+ import { input, password, confirm as confirm2, select } from "@inquirer/prompts";
1350
+ import ora2 from "ora";
1351
+ var configCommand = new Command3("config").description("Manage CLI configuration").addCommand(
1352
+ new Command3("init").description("Initialize configuration interactively").action(async () => {
865
1353
  console.log();
866
- console.log(chalk3.bold("ConceptCraft CLI Configuration"));
867
- console.log(chalk3.gray("\u2500".repeat(35)));
1354
+ console.log(chalk4.bold("ConceptCraft CLI Configuration"));
1355
+ console.log(chalk4.gray("\u2500".repeat(35)));
868
1356
  console.log();
869
1357
  try {
870
1358
  const apiKey = await password({
@@ -881,7 +1369,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
881
1369
  }
882
1370
  });
883
1371
  setApiKey(apiKey.trim());
884
- const useCustomUrl = await confirm({
1372
+ const useCustomUrl = await confirm2({
885
1373
  message: "Use a custom API URL? (default: www.mindframes.app)",
886
1374
  default: false
887
1375
  });
@@ -901,7 +1389,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
901
1389
  setApiUrl(customUrl);
902
1390
  }
903
1391
  console.log();
904
- const spinner = ora("Verifying API key...").start();
1392
+ const spinner = ora2("Verifying API key...").start();
905
1393
  try {
906
1394
  const result = await whoami();
907
1395
  spinner.succeed("API key verified!");
@@ -953,18 +1441,18 @@ var configCommand = new Command("config").description("Manage CLI configuration"
953
1441
  }
954
1442
  })
955
1443
  ).addCommand(
956
- new Command("show").description("Show current configuration").option("--verify", "Verify API key and show team details").action(async (options) => {
1444
+ new Command3("show").description("Show current configuration").option("--verify", "Verify API key and show team details").action(async (options) => {
957
1445
  const config2 = getConfig();
958
1446
  header("Current Configuration");
959
1447
  console.log();
960
- keyValue("API Key", getMaskedApiKey() ?? chalk3.red("Not set"));
1448
+ keyValue("API Key", getMaskedApiKey() ?? chalk4.red("Not set"));
961
1449
  keyValue("API URL", config2.apiUrl);
962
- keyValue("Default Team ID", config2.defaultTeamId ?? chalk3.gray("Not set"));
1450
+ keyValue("Default Team ID", config2.defaultTeamId ?? chalk4.gray("Not set"));
963
1451
  console.log();
964
1452
  keyValue("Config file", getConfigPath());
965
1453
  if (options.verify && config2.apiKey) {
966
1454
  console.log();
967
- const spinner = ora("Verifying...").start();
1455
+ const spinner = ora2("Verifying...").start();
968
1456
  try {
969
1457
  const result = await whoami();
970
1458
  spinner.succeed("Verified");
@@ -984,7 +1472,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
984
1472
  console.log();
985
1473
  })
986
1474
  ).addCommand(
987
- new Command("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) => {
1475
+ 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
1476
  switch (key) {
989
1477
  case "api-key":
990
1478
  if (!isValidApiKeyFormat(value)) {
@@ -1013,14 +1501,14 @@ var configCommand = new Command("config").description("Manage CLI configuration"
1013
1501
  break;
1014
1502
  default:
1015
1503
  error(`Unknown config key: ${key}`);
1016
- console.log(chalk3.gray("Valid keys: api-key, api-url, team-id"));
1504
+ console.log(chalk4.gray("Valid keys: api-key, api-url, team-id"));
1017
1505
  process.exit(6);
1018
1506
  }
1019
1507
  })
1020
1508
  ).addCommand(
1021
- new Command("clear").description("Clear all configuration").action(async () => {
1509
+ new Command3("clear").description("Clear all configuration").action(async () => {
1022
1510
  try {
1023
- const confirmed = await confirm({
1511
+ const confirmed = await confirm2({
1024
1512
  message: "Are you sure you want to clear all configuration?",
1025
1513
  default: false
1026
1514
  });
@@ -1036,8 +1524,8 @@ var configCommand = new Command("config").description("Manage CLI configuration"
1036
1524
  }
1037
1525
  })
1038
1526
  ).addCommand(
1039
- new Command("refresh").description("Refresh cached feature flags").action(async () => {
1040
- const spinner = ora("Refreshing feature flags...").start();
1527
+ new Command3("refresh").description("Refresh cached feature flags").action(async () => {
1528
+ const spinner = ora2("Refreshing feature flags...").start();
1041
1529
  try {
1042
1530
  await fetchAndCache();
1043
1531
  spinner.succeed("Feature flags refreshed");
@@ -1049,7 +1537,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
1049
1537
  }
1050
1538
  })
1051
1539
  ).addCommand(
1052
- new Command("path").description("Show configuration file path").option("--cache", "Show feature cache file path").action((options) => {
1540
+ new Command3("path").description("Show configuration file path").option("--cache", "Show feature cache file path").action((options) => {
1053
1541
  if (options.cache) {
1054
1542
  console.log(getCachePath());
1055
1543
  } else {
@@ -1059,13 +1547,16 @@ var configCommand = new Command("config").description("Manage CLI configuration"
1059
1547
  );
1060
1548
 
1061
1549
  // src/commands/create.ts
1062
- import { Command as Command2 } from "commander";
1063
- import chalk5 from "chalk";
1550
+ init_api();
1551
+ init_auth();
1552
+ import { Command as Command4 } from "commander";
1553
+ import chalk6 from "chalk";
1064
1554
 
1065
1555
  // src/lib/streaming.ts
1556
+ init_output();
1066
1557
  import { createParser } from "eventsource-parser";
1067
- import chalk4 from "chalk";
1068
- import ora2 from "ora";
1558
+ import chalk5 from "chalk";
1559
+ import ora3 from "ora";
1069
1560
  async function handleStream(response, callbacks, options = {}) {
1070
1561
  const reader = response.body?.getReader();
1071
1562
  if (!reader) {
@@ -1086,7 +1577,7 @@ async function handleStream(response, callbacks, options = {}) {
1086
1577
  const parsed = JSON.parse(event.data);
1087
1578
  const { type, data } = parsed;
1088
1579
  if (options.debug) {
1089
- console.log(chalk4.gray(`[SSE] ${type}:`), data);
1580
+ console.log(chalk5.gray(`[SSE] ${type}:`), data);
1090
1581
  }
1091
1582
  callbacks.onData?.(type, data);
1092
1583
  switch (type) {
@@ -1156,7 +1647,7 @@ async function handleStream(response, callbacks, options = {}) {
1156
1647
  }
1157
1648
  } catch (e) {
1158
1649
  if (options.debug) {
1159
- console.log(chalk4.gray("[SSE Raw]"), event.data);
1650
+ console.log(chalk5.gray("[SSE Raw]"), event.data);
1160
1651
  }
1161
1652
  }
1162
1653
  }
@@ -1181,9 +1672,9 @@ async function streamWithProgress(response, topic, options) {
1181
1672
  const useTTY = isTTY() && !options.noStream && !options.quiet && !options.json;
1182
1673
  if (!options.json) {
1183
1674
  console.log();
1184
- console.log(chalk4.bold(`Creating presentation: "${topic}"`));
1675
+ console.log(chalk5.bold(`Creating presentation: "${topic}"`));
1185
1676
  console.log(
1186
- chalk4.gray(
1677
+ chalk5.gray(
1187
1678
  `Quality: ${options.mode} | Tone: ${options.tone ?? "professional"} | Slides: ${options.slideCount} | Language: ${options.language}`
1188
1679
  )
1189
1680
  );
@@ -1234,10 +1725,10 @@ async function streamWithProgress(response, topic, options) {
1234
1725
  const tokens = formatTokens(currentTokens);
1235
1726
  const elapsed = formatTime(getElapsed()).padStart(5, " ");
1236
1727
  const remaining = getRemaining().padStart(5, " ");
1237
- return `${bar} ${chalk4.bold(pct)}${chalk4.gray("%")} ${chalk4.cyan(phase)} ${chalk4.gray(slides)} ${chalk4.yellow(tokens)} ${chalk4.gray(elapsed)} ${chalk4.green(remaining + " left")}`;
1728
+ return `${bar} ${chalk5.bold(pct)}${chalk5.gray("%")} ${chalk5.cyan(phase)} ${chalk5.gray(slides)} ${chalk5.yellow(tokens)} ${chalk5.gray(elapsed)} ${chalk5.green(remaining + " left")}`;
1238
1729
  };
1239
1730
  if (useTTY) {
1240
- spinner = ora2({
1731
+ spinner = ora3({
1241
1732
  text: buildProgressLine(),
1242
1733
  spinner: "dots"
1243
1734
  }).start();
@@ -1273,9 +1764,9 @@ async function streamWithProgress(response, topic, options) {
1273
1764
  onError: (error2) => {
1274
1765
  if (timer) clearInterval(timer);
1275
1766
  if (spinner) {
1276
- spinner.fail(chalk4.red(`Error: ${error2}`));
1767
+ spinner.fail(chalk5.red(`Error: ${error2}`));
1277
1768
  } else if (!options.json) {
1278
- console.error(chalk4.red(`Error: ${error2}`));
1769
+ console.error(chalk5.red(`Error: ${error2}`));
1279
1770
  }
1280
1771
  },
1281
1772
  onComplete: (data) => {
@@ -1324,6 +1815,7 @@ async function streamTextContent(response, options = {}) {
1324
1815
  }
1325
1816
 
1326
1817
  // src/commands/create.ts
1818
+ init_output();
1327
1819
  import { readFileSync as readFileSync2, existsSync } from "fs";
1328
1820
  import { resolve } from "path";
1329
1821
  var VALID_GOALS = [
@@ -1334,7 +1826,7 @@ var VALID_GOALS = [
1334
1826
  "entertain",
1335
1827
  "report"
1336
1828
  ];
1337
- var createCommand = new Command2("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(
1829
+ 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
1830
  "-m, --mode <mode>",
1339
1831
  "Generation quality mode (best, balanced, fast, ultrafast, instant)",
1340
1832
  "balanced"
@@ -1375,67 +1867,83 @@ var createCommand = new Command2("create").description("Create a new presentatio
1375
1867
  "--thinking-depth <depth>",
1376
1868
  "AI thinking depth (quick, moderate, deep, profound)",
1377
1869
  "moderate"
1378
- ).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)").addHelpText(
1870
+ ).option(
1871
+ "--theme <preset>",
1872
+ "Color theme preset (blue, violet, rose, orange, green)"
1873
+ ).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(
1874
+ "--decorations <style>",
1875
+ "Background decoration style (none, waves-bottom-left, waves-top-right, blob-corners, minimal)"
1876
+ ).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
1877
  "after",
1380
1878
  `
1381
- ${chalk5.bold("Recommended Usage:")}
1879
+ ${chalk6.bold("Recommended Usage:")}
1382
1880
  Always specify slides (-n), mode (-m), audience, and context for best results.
1383
1881
 
1384
- ${chalk5.bold("Examples:")}
1385
- ${chalk5.gray("# Content from PDF + style from image reference")}
1882
+ ${chalk6.bold("Examples:")}
1883
+ ${chalk6.gray("# Content from PDF + style from image reference")}
1386
1884
  $ conceptcraft create "Quarterly Report" \\
1387
1885
  --file ./report.pdf \\
1388
1886
  --reference-url https://example.com/style-template.png \\
1389
1887
  -n 12 -m best --audience "Executive team"
1390
1888
 
1391
- ${chalk5.gray("# Upload content files (PDF, PPTX, DOCX)")}
1889
+ ${chalk6.gray("# Upload content files (PDF, PPTX, DOCX)")}
1392
1890
  $ conceptcraft create "Product Demo" \\
1393
1891
  --file ./existing-deck.pptx --file ./specs.pdf \\
1394
1892
  --goal persuade --audience "Enterprise buyers"
1395
1893
 
1396
- ${chalk5.gray("# Inline context with custom styling")}
1894
+ ${chalk6.gray("# Inline context with custom styling")}
1397
1895
  $ conceptcraft create "Q4 Business Review" \\
1398
1896
  -n 12 -m best --goal inform \\
1399
1897
  --context "Revenue: $50M (+25% YoY), EBITDA: $8M" \\
1400
1898
  --reference-url https://example.com/brand-style.jpg
1401
1899
 
1402
- ${chalk5.gray("# Research presentation from URLs")}
1900
+ ${chalk6.gray("# Research presentation from URLs")}
1403
1901
  $ conceptcraft create "AI Industry Trends" \\
1404
1902
  -n 10 -m best -t educational \\
1405
1903
  --sources https://example.com/ai-report.pdf
1406
1904
 
1407
- ${chalk5.gray("# Pipe content from another command")}
1905
+ ${chalk6.gray("# Pipe content from another command")}
1408
1906
  $ cat meeting-notes.md | conceptcraft create "Meeting Summary" \\
1409
1907
  -n 6 -m balanced --goal inform
1410
1908
 
1411
- ${chalk5.bold("Content Options (what to include in slides):")}
1909
+ ${chalk6.bold("Content Options (what to include in slides):")}
1412
1910
  -f, --file <paths...> Upload files for content extraction (PDF, PPTX, DOCX)
1413
1911
  -c, --context <text> Inline text (key facts, data points)
1414
1912
  --context-file <path> Read content from file (markdown, text, JSON)
1415
1913
  --sources <urls...> URLs to scrape for content
1416
1914
  --stdin Pipe content from another command
1417
1915
 
1418
- ${chalk5.bold("Styling Options (how slides look):")}
1419
- --reference-url <url> ${chalk5.yellow("Image URL to guide visual style")} (colors, layout, feel)
1916
+ ${chalk6.bold("Styling Options (how slides look):")}
1917
+ --reference-url <url> ${chalk6.yellow("Image URL to guide visual style")} (colors, layout, feel)
1420
1918
  --styling <mode> freeform, brand-only, style-only, no-styling
1421
1919
  -b, --brand <id> Apply saved brand settings
1422
1920
 
1423
- ${chalk5.gray("NOTE: --file uploads provide CONTENT (data, text to include)")}
1424
- ${chalk5.gray(" --reference-url provides STYLE (visual design inspiration)")}
1921
+ ${chalk6.gray("NOTE: --file uploads provide CONTENT (data, text to include)")}
1922
+ ${chalk6.gray(" --reference-url provides STYLE (visual design inspiration)")}
1425
1923
 
1426
- ${chalk5.bold("Goal Options:")}
1924
+ ${chalk6.bold("Goal Options:")}
1427
1925
  -g, --goal <type> inform, persuade, train, learn, entertain, report
1428
1926
  --custom-goal <text> Custom goal description
1429
1927
 
1430
- ${chalk5.bold("Mode Reference:")}
1928
+ ${chalk6.bold("Theme Options:")}
1929
+ --theme <preset> Preset color scheme (blue, violet, rose, orange, green)
1930
+ --primary-color <hex> Primary brand color (e.g., #0066CC)
1931
+ --secondary-color <hex> Secondary color for accents
1932
+ --decorations <style> Background style (none, waves-bottom-left, waves-top-right, blob-corners, minimal)
1933
+
1934
+ ${chalk6.gray("Example: Apply corporate colors")}
1935
+ $ conceptcraft create "Brand Deck" \\
1936
+ --theme blue --primary-color "#1E40AF" --decorations waves-bottom-left
1937
+
1938
+ ${chalk6.bold("Mode Reference:")}
1431
1939
  best Highest quality, thorough research (recommended for important decks)
1432
1940
  balanced Good quality with reasonable speed (default)
1433
1941
  fast Quick generation, less refinement
1434
1942
  ultrafast Very quick, minimal processing
1435
- instant Fastest, basic output
1943
+ instant Fastest, basic output (theme options work best with instant mode)
1436
1944
  `
1437
1945
  ).action(async (topic, options) => {
1438
- requireAuth();
1946
+ await requireAuth();
1439
1947
  const slideCount = parseInt(options.slides, 10);
1440
1948
  if (isNaN(slideCount) || slideCount < 1 || slideCount > 20) {
1441
1949
  error("Slide count must be between 1 and 20");
@@ -1510,15 +2018,68 @@ ${chalk5.bold("Mode Reference:")}
1510
2018
  );
1511
2019
  process.exit(6);
1512
2020
  }
2021
+ const validThemePresets = ["blue", "violet", "rose", "orange", "green"];
2022
+ if (options.theme && !validThemePresets.includes(options.theme)) {
2023
+ error(
2024
+ `Invalid theme: ${options.theme}. Valid themes: ${validThemePresets.join(", ")}`
2025
+ );
2026
+ process.exit(6);
2027
+ }
2028
+ const validDecorations = [
2029
+ "none",
2030
+ "waves-bottom-left",
2031
+ "waves-top-right",
2032
+ "blob-corners",
2033
+ "minimal"
2034
+ ];
2035
+ if (options.decorations && !validDecorations.includes(options.decorations)) {
2036
+ error(
2037
+ `Invalid decorations: ${options.decorations}. Valid styles: ${validDecorations.join(", ")}`
2038
+ );
2039
+ process.exit(6);
2040
+ }
2041
+ const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
2042
+ const colorOptions = [
2043
+ { name: "primary-color", value: options.primaryColor },
2044
+ { name: "secondary-color", value: options.secondaryColor },
2045
+ { name: "accent-color", value: options.accentColor },
2046
+ { name: "background-color", value: options.backgroundColor },
2047
+ { name: "foreground-color", value: options.foregroundColor }
2048
+ ];
2049
+ for (const { name, value } of colorOptions) {
2050
+ if (value && !hexColorRegex.test(value)) {
2051
+ error(`Invalid ${name}: ${value}. Must be a hex color (e.g., #0066CC or #06C)`);
2052
+ process.exit(6);
2053
+ }
2054
+ }
2055
+ let theme;
2056
+ const hasCustomColors = options.primaryColor || options.secondaryColor || options.accentColor || options.backgroundColor || options.foregroundColor;
2057
+ if (options.theme || hasCustomColors || options.decorations) {
2058
+ theme = {};
2059
+ if (options.theme) {
2060
+ theme.preset = options.theme;
2061
+ }
2062
+ if (hasCustomColors) {
2063
+ theme.custom = {};
2064
+ if (options.primaryColor) theme.custom.primary = options.primaryColor;
2065
+ if (options.secondaryColor) theme.custom.secondary = options.secondaryColor;
2066
+ if (options.accentColor) theme.custom.accent = options.accentColor;
2067
+ if (options.backgroundColor) theme.custom.background = options.backgroundColor;
2068
+ if (options.foregroundColor) theme.custom.foreground = options.foregroundColor;
2069
+ }
2070
+ if (options.decorations) {
2071
+ theme.decorations = options.decorations;
2072
+ }
2073
+ }
1513
2074
  try {
1514
2075
  if (options.output !== "json" && options.output !== "quiet") {
1515
- process.stdout.write(chalk5.gray("Checking generation limits... "));
2076
+ process.stdout.write(chalk6.gray("Checking generation limits... "));
1516
2077
  }
1517
2078
  const limits = await validateGeneration(options.mode, slideCount, options.teamId);
1518
2079
  if (options.output !== "json" && options.output !== "quiet") {
1519
- console.log(chalk5.green("\u2713"));
2080
+ console.log(chalk6.green("\u2713"));
1520
2081
  console.log(
1521
- chalk5.gray(
2082
+ chalk6.gray(
1522
2083
  ` Plan: ${limits.planName} | ${options.mode}: ${limits.remaining[options.mode]}/${limits.limits[options.mode]} remaining`
1523
2084
  )
1524
2085
  );
@@ -1532,7 +2093,7 @@ ${chalk5.bold("Mode Reference:")}
1532
2093
  })
1533
2094
  );
1534
2095
  } else {
1535
- console.log(chalk5.red("\u2717"));
2096
+ console.log(chalk6.red("\u2717"));
1536
2097
  error(err instanceof Error ? err.message : String(err));
1537
2098
  }
1538
2099
  process.exit(err.exitCode ?? 1);
@@ -1547,7 +2108,7 @@ ${chalk5.bold("Mode Reference:")}
1547
2108
  }
1548
2109
  }
1549
2110
  if (options.output !== "json" && options.output !== "quiet") {
1550
- console.log(chalk5.gray(`
2111
+ console.log(chalk6.gray(`
1551
2112
  Uploading ${options.file.length} file(s)...`));
1552
2113
  }
1553
2114
  try {
@@ -1556,13 +2117,13 @@ Uploading ${options.file.length} file(s)...`));
1556
2117
  (completed, total, fileName) => {
1557
2118
  if (options.output !== "json" && options.output !== "quiet" && fileName) {
1558
2119
  process.stdout.write(
1559
- `\r ${chalk5.cyan("\u2B06")} Uploading: ${fileName} (${completed + 1}/${total})`
2120
+ `\r ${chalk6.cyan("\u2B06")} Uploading: ${fileName} (${completed + 1}/${total})`
1560
2121
  );
1561
2122
  }
1562
2123
  }
1563
2124
  );
1564
2125
  if (options.output !== "json" && options.output !== "quiet") {
1565
- console.log(`\r ${chalk5.green("\u2713")} Uploaded ${uploadedFiles.length} file(s) `);
2126
+ console.log(`\r ${chalk6.green("\u2713")} Uploaded ${uploadedFiles.length} file(s) `);
1566
2127
  }
1567
2128
  } catch (err) {
1568
2129
  error(
@@ -1601,16 +2162,16 @@ Uploading ${options.file.length} file(s)...`));
1601
2162
  if (!hasContext) {
1602
2163
  error("Context is required to create a presentation.");
1603
2164
  console.log();
1604
- console.log(chalk5.gray("Provide context using one of these methods:"));
1605
- console.log(chalk5.gray(" -f, --file <paths...> Upload files (PDF, PPTX, images)"));
1606
- console.log(chalk5.gray(" -c, --context <text> Direct text context"));
1607
- console.log(chalk5.gray(" --context-file <path> Read from a file"));
1608
- console.log(chalk5.gray(" --sources <urls...> URLs to scrape"));
1609
- console.log(chalk5.gray(" cat file | conceptcraft Pipe content"));
2165
+ console.log(chalk6.gray("Provide context using one of these methods:"));
2166
+ console.log(chalk6.gray(" -f, --file <paths...> Upload files (PDF, PPTX, images)"));
2167
+ console.log(chalk6.gray(" -c, --context <text> Direct text context"));
2168
+ console.log(chalk6.gray(" --context-file <path> Read from a file"));
2169
+ console.log(chalk6.gray(" --sources <urls...> URLs to scrape"));
2170
+ console.log(chalk6.gray(" cat file | conceptcraft Pipe content"));
1610
2171
  console.log();
1611
- console.log(chalk5.gray("Example:"));
1612
- console.log(chalk5.cyan(' conceptcraft create "Q4 Report" --file ./report.pdf'));
1613
- console.log(chalk5.cyan(' conceptcraft create "Q4 Report" --context "Revenue: $10M, Growth: 25%"'));
2172
+ console.log(chalk6.gray("Example:"));
2173
+ console.log(chalk6.cyan(' conceptcraft create "Q4 Report" --file ./report.pdf'));
2174
+ console.log(chalk6.cyan(' conceptcraft create "Q4 Report" --context "Revenue: $10M, Growth: 25%"'));
1614
2175
  process.exit(6);
1615
2176
  }
1616
2177
  try {
@@ -1633,7 +2194,8 @@ Uploading ${options.file.length} file(s)...`));
1633
2194
  uploadedFiles: uploadedFiles.length > 0 ? uploadedFiles : void 0,
1634
2195
  goal: options.goal,
1635
2196
  customGoal: options.customGoal,
1636
- teamId: options.teamId
2197
+ teamId: options.teamId,
2198
+ theme
1637
2199
  });
1638
2200
  const result = await streamWithProgress(response, topic, {
1639
2201
  slideCount,
@@ -1684,7 +2246,11 @@ Uploading ${options.file.length} file(s)...`));
1684
2246
  console.log();
1685
2247
  const viewUrl = buildViewUrl(result.slug, options.language);
1686
2248
  if (viewUrl !== "N/A") {
1687
- console.log(chalk5.bold(" Open: ") + chalk5.cyan.underline(viewUrl));
2249
+ console.log(chalk6.bold(" Open: ") + chalk6.cyan.underline(viewUrl));
2250
+ if (options.open) {
2251
+ const open2 = await import("open");
2252
+ await open2.default(viewUrl);
2253
+ }
1688
2254
  }
1689
2255
  console.log();
1690
2256
  }
@@ -1731,13 +2297,17 @@ function isUrl(str) {
1731
2297
  }
1732
2298
 
1733
2299
  // src/commands/list.ts
1734
- import { Command as Command3 } from "commander";
1735
- var listCommand = new Command3("list").description("List presentations").option("-n, --limit <count>", "Number of results to return", "20").option(
2300
+ init_api();
2301
+ init_auth();
2302
+ init_config();
2303
+ init_output();
2304
+ import { Command as Command5 } from "commander";
2305
+ var listCommand = new Command5("list").description("List presentations").option("-n, --limit <count>", "Number of results to return", "20").option(
1736
2306
  "-f, --format <format>",
1737
2307
  "Output format (table, json, ids)",
1738
2308
  "table"
1739
2309
  ).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();
2310
+ await requireAuth();
1741
2311
  const limit = parseInt(options.limit, 10);
1742
2312
  if (isNaN(limit) || limit < 1) {
1743
2313
  error("Invalid limit value");
@@ -1794,14 +2364,17 @@ var listCommand = new Command3("list").description("List presentations").option(
1794
2364
  });
1795
2365
 
1796
2366
  // src/commands/get.ts
1797
- import { Command as Command4 } from "commander";
1798
- import chalk6 from "chalk";
1799
- var getCommand = new Command4("get").description("Get presentation details").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option(
2367
+ init_api();
2368
+ init_auth();
2369
+ init_output();
2370
+ import { Command as Command6 } from "commander";
2371
+ import chalk7 from "chalk";
2372
+ var getCommand = new Command6("get").description("Get presentation details").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option(
1800
2373
  "-f, --format <format>",
1801
2374
  "Output format (summary, full, json)",
1802
2375
  "summary"
1803
2376
  ).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();
2377
+ await requireAuth();
1805
2378
  try {
1806
2379
  const presentation = await getPresentation(slug);
1807
2380
  if (options.format === "json") {
@@ -1813,7 +2386,7 @@ var getCommand = new Command4("get").description("Get presentation details").arg
1813
2386
  console.log();
1814
2387
  keyValue("Slug", presentation.slug);
1815
2388
  keyValue("Title", presentation.title || "Untitled");
1816
- keyValue("Description", presentation.description || chalk6.gray("None"));
2389
+ keyValue("Description", presentation.description || chalk7.gray("None"));
1817
2390
  keyValue("Slides", String(presentation.numberOfSlides || "-"));
1818
2391
  keyValue("Mode", presentation.mode || "-");
1819
2392
  keyValue("Created", formatDate(presentation.createdAt));
@@ -1822,11 +2395,11 @@ var getCommand = new Command4("get").description("Get presentation details").arg
1822
2395
  }
1823
2396
  console.log();
1824
2397
  if (viewUrl !== "N/A") {
1825
- console.log(chalk6.bold(" Open: ") + chalk6.cyan.underline(viewUrl));
2398
+ console.log(chalk7.bold(" Open: ") + chalk7.cyan.underline(viewUrl));
1826
2399
  }
1827
2400
  console.log();
1828
2401
  if (options.format === "full" && options.include?.includes("slides")) {
1829
- console.log(chalk6.gray("(Full slide content available in JSON format)"));
2402
+ console.log(chalk7.gray("(Full slide content available in JSON format)"));
1830
2403
  console.log();
1831
2404
  }
1832
2405
  } catch (err) {
@@ -1836,13 +2409,16 @@ var getCommand = new Command4("get").description("Get presentation details").arg
1836
2409
  });
1837
2410
 
1838
2411
  // src/commands/delete.ts
1839
- import { Command as Command5 } from "commander";
1840
- import { confirm as confirm2 } from "@inquirer/prompts";
1841
- var deleteCommand = new Command5("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) => {
1842
- requireAuth();
2412
+ init_api();
2413
+ init_auth();
2414
+ init_output();
2415
+ import { Command as Command7 } from "commander";
2416
+ import { confirm as confirm3 } from "@inquirer/prompts";
2417
+ 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) => {
2418
+ await requireAuth();
1843
2419
  if (!options.force) {
1844
2420
  try {
1845
- const confirmed = await confirm2({
2421
+ const confirmed = await confirm3({
1846
2422
  message: `Are you sure you want to delete presentation "${slug}"?`,
1847
2423
  default: false
1848
2424
  });
@@ -1871,13 +2447,16 @@ var deleteCommand = new Command5("delete").description("Delete a presentation").
1871
2447
  });
1872
2448
 
1873
2449
  // src/commands/export.ts
1874
- import { Command as Command6 } from "commander";
2450
+ init_api();
2451
+ init_auth();
2452
+ init_output();
2453
+ import { Command as Command8 } from "commander";
1875
2454
  import { writeFile } from "fs/promises";
1876
2455
  import { resolve as resolve2 } from "path";
1877
- import ora3 from "ora";
1878
- var exportCommand = new Command6("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) => {
1879
- requireAuth();
1880
- const spinner = ora3("Fetching presentation...").start();
2456
+ import ora4 from "ora";
2457
+ 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) => {
2458
+ await requireAuth();
2459
+ const spinner = ora4("Fetching presentation...").start();
1881
2460
  try {
1882
2461
  const presentation = await getPresentation(slug);
1883
2462
  const title = presentation.title || slug;
@@ -1912,14 +2491,18 @@ function formatBytes(bytes) {
1912
2491
  }
1913
2492
 
1914
2493
  // src/commands/import.ts
1915
- import { Command as Command7 } from "commander";
2494
+ init_api();
2495
+ init_auth();
2496
+ init_output();
2497
+ init_config();
2498
+ import { Command as Command9 } from "commander";
1916
2499
  import { readFile } from "fs/promises";
1917
2500
  import { resolve as resolve3, basename as basename2 } from "path";
1918
- import ora4 from "ora";
1919
- var cmd = new Command7("import").description("Import a presentation from ZIP (admin only)").argument("<file>", "Path to ZIP file");
2501
+ import ora5 from "ora";
2502
+ var cmd = new Command9("import").description("Import a presentation from ZIP (admin only)").argument("<file>", "Path to ZIP file");
1920
2503
  cmd._hidden = true;
1921
2504
  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();
2505
+ await requireAuth();
1923
2506
  try {
1924
2507
  const me = await whoami();
1925
2508
  if (me.user.role !== "admin") {
@@ -1936,7 +2519,7 @@ var importCommand = cmd.option("--dry-run", "Validate without importing").option
1936
2519
  error("File must be a ZIP archive");
1937
2520
  process.exit(6);
1938
2521
  }
1939
- const spinner = ora4("Reading file...").start();
2522
+ const spinner = ora5("Reading file...").start();
1940
2523
  try {
1941
2524
  const fileBuffer = await readFile(filePath);
1942
2525
  if (options.dryRun) {
@@ -2001,12 +2584,16 @@ var importCommand = cmd.option("--dry-run", "Validate without importing").option
2001
2584
  });
2002
2585
 
2003
2586
  // src/commands/branding.ts
2004
- import { Command as Command8 } from "commander";
2005
- import chalk7 from "chalk";
2006
- import ora5 from "ora";
2007
- var brandingCommand = new Command8("branding").description("Manage brand profiles").addCommand(
2008
- new Command8("list").description("List brand profiles").option("-f, --format <format>", "Output format (table, json)", "table").action(async (options) => {
2009
- requireAuth();
2587
+ init_api();
2588
+ init_auth();
2589
+ init_config();
2590
+ init_output();
2591
+ import { Command as Command10 } from "commander";
2592
+ import chalk8 from "chalk";
2593
+ import ora6 from "ora";
2594
+ var brandingCommand = new Command10("branding").description("Manage brand profiles").addCommand(
2595
+ new Command10("list").description("List brand profiles").option("-f, --format <format>", "Output format (table, json)", "table").action(async (options) => {
2596
+ await requireAuth();
2010
2597
  try {
2011
2598
  const result = await listBrandings();
2012
2599
  if (result.brandings.length === 0) {
@@ -2016,7 +2603,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2016
2603
  info("No brand profiles found");
2017
2604
  console.log();
2018
2605
  console.log(
2019
- chalk7.gray(
2606
+ chalk8.gray(
2020
2607
  "Create one with: conceptcraft branding extract <url>"
2021
2608
  )
2022
2609
  );
@@ -2034,8 +2621,8 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2034
2621
  }
2035
2622
  })
2036
2623
  ).addCommand(
2037
- new Command8("get").description("Get brand profile details").argument("<id>", "Brand profile ID").option("-f, --format <format>", "Output format (summary, json)", "summary").action(async (id, options) => {
2038
- requireAuth();
2624
+ 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) => {
2625
+ await requireAuth();
2039
2626
  try {
2040
2627
  const brand2 = await getBranding(id);
2041
2628
  if (options.format === "json") {
@@ -2045,36 +2632,36 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2045
2632
  console.log();
2046
2633
  keyValue("ID", brand2.id);
2047
2634
  keyValue("Name", brand2.name);
2048
- keyValue("Source URL", brand2.sourceUrl ?? chalk7.gray("None"));
2049
- keyValue("Default", brand2.isDefault ? chalk7.green("Yes") : "No");
2635
+ keyValue("Source URL", brand2.sourceUrl ?? chalk8.gray("None"));
2636
+ keyValue("Default", brand2.isDefault ? chalk8.green("Yes") : "No");
2050
2637
  if (brand2.createdAt) {
2051
2638
  keyValue("Created", new Date(brand2.createdAt).toLocaleString());
2052
2639
  }
2053
2640
  console.log();
2054
2641
  if (brand2.primaryColor || brand2.colors.length > 0) {
2055
- console.log(chalk7.bold(" Colors"));
2642
+ console.log(chalk8.bold(" Colors"));
2056
2643
  if (brand2.primaryColor) {
2057
- const swatch = chalk7.bgHex(brand2.primaryColor)(" ");
2644
+ const swatch = chalk8.bgHex(brand2.primaryColor)(" ");
2058
2645
  console.log(` Primary: ${swatch} ${brand2.primaryColor}`);
2059
2646
  }
2060
2647
  if (brand2.colors.length > 0) {
2061
2648
  console.log(" Palette:");
2062
2649
  for (const c of brand2.colors.slice(0, 10)) {
2063
- const swatch = chalk7.bgHex(c.hex)(" ");
2064
- const role = c.role ? chalk7.gray(` (${c.role})`) : "";
2650
+ const swatch = chalk8.bgHex(c.hex)(" ");
2651
+ const role = c.role ? chalk8.gray(` (${c.role})`) : "";
2065
2652
  console.log(` ${swatch} ${c.hex}${role}`);
2066
2653
  }
2067
2654
  }
2068
2655
  console.log();
2069
2656
  }
2070
2657
  if (brand2.logoUrl) {
2071
- console.log(chalk7.bold(" Logo"));
2658
+ console.log(chalk8.bold(" Logo"));
2072
2659
  console.log(` ${brand2.logoUrl}`);
2073
2660
  console.log();
2074
2661
  }
2075
2662
  const typo = brand2.typography;
2076
2663
  if (typo && Object.keys(typo).length > 0) {
2077
- console.log(chalk7.bold(" Typography"));
2664
+ console.log(chalk8.bold(" Typography"));
2078
2665
  if (typo.primary || typo.headings) {
2079
2666
  console.log(` Headings: ${typo.primary || typo.headings || "-"}`);
2080
2667
  }
@@ -2084,7 +2671,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2084
2671
  console.log();
2085
2672
  }
2086
2673
  if (brand2.confidence || brand2.extractionMethod) {
2087
- console.log(chalk7.bold(" Extraction"));
2674
+ console.log(chalk8.bold(" Extraction"));
2088
2675
  if (brand2.confidence) {
2089
2676
  console.log(` Confidence: ${Math.round(brand2.confidence * 100)}%`);
2090
2677
  }
@@ -2093,7 +2680,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2093
2680
  }
2094
2681
  console.log();
2095
2682
  }
2096
- console.log(chalk7.gray(" Use --format json for full details"));
2683
+ console.log(chalk8.gray(" Use --format json for full details"));
2097
2684
  console.log();
2098
2685
  }
2099
2686
  } catch (err) {
@@ -2102,8 +2689,8 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2102
2689
  }
2103
2690
  })
2104
2691
  ).addCommand(
2105
- new Command8("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) => {
2106
- requireAuth();
2692
+ 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) => {
2693
+ await requireAuth();
2107
2694
  try {
2108
2695
  new URL(url.startsWith("http") ? url : `https://${url}`);
2109
2696
  } catch {
@@ -2112,7 +2699,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2112
2699
  }
2113
2700
  const fullUrl = url.startsWith("http") ? url : `https://${url}`;
2114
2701
  const teamId = options.teamId ?? getDefaultTeamId();
2115
- const spinner = ora5({ text: `Extracting branding from ${fullUrl}...`, stream: process.stdout }).start();
2702
+ const spinner = ora6({ text: `Extracting branding from ${fullUrl}...`, stream: process.stdout }).start();
2116
2703
  try {
2117
2704
  const result = await extractBranding(fullUrl, teamId);
2118
2705
  const brand2 = await getBranding(result.id);
@@ -2125,28 +2712,28 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2125
2712
  keyValue("Source URL", brand2.sourceUrl ?? fullUrl);
2126
2713
  console.log();
2127
2714
  if (brand2.primaryColor || brand2.colors.length > 0) {
2128
- console.log(chalk7.bold(" Colors"));
2715
+ console.log(chalk8.bold(" Colors"));
2129
2716
  if (brand2.primaryColor) {
2130
- const swatch = chalk7.bgHex(brand2.primaryColor)(" ");
2717
+ const swatch = chalk8.bgHex(brand2.primaryColor)(" ");
2131
2718
  console.log(` Primary: ${swatch} ${brand2.primaryColor}`);
2132
2719
  }
2133
2720
  if (brand2.colors.length > 0) {
2134
2721
  console.log(" Palette:");
2135
2722
  for (const c of brand2.colors.slice(0, 6)) {
2136
- const swatch = chalk7.bgHex(c.hex)(" ");
2137
- const role = c.role ? chalk7.gray(` (${c.role})`) : "";
2723
+ const swatch = chalk8.bgHex(c.hex)(" ");
2724
+ const role = c.role ? chalk8.gray(` (${c.role})`) : "";
2138
2725
  console.log(` ${swatch} ${c.hex}${role}`);
2139
2726
  }
2140
2727
  }
2141
2728
  console.log();
2142
2729
  }
2143
2730
  if (brand2.logoUrl) {
2144
- console.log(chalk7.bold(" Logo"));
2731
+ console.log(chalk8.bold(" Logo"));
2145
2732
  console.log(` ${brand2.logoUrl}`);
2146
2733
  console.log();
2147
2734
  }
2148
2735
  if (brand2.confidence) {
2149
- console.log(chalk7.bold(" Extraction"));
2736
+ console.log(chalk8.bold(" Extraction"));
2150
2737
  console.log(` Confidence: ${Math.round(brand2.confidence * 100)}%`);
2151
2738
  console.log();
2152
2739
  }
@@ -2159,14 +2746,14 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2159
2746
  }
2160
2747
  })
2161
2748
  ).addCommand(
2162
- new Command8("set-default").description("Set a brand profile as default").argument("<id>", "Brand profile ID").action(async (id) => {
2163
- requireAuth();
2749
+ new Command10("set-default").description("Set a brand profile as default").argument("<id>", "Brand profile ID").action(async (id) => {
2750
+ await requireAuth();
2164
2751
  info(
2165
2752
  `To set brand ${id} as default, use the web dashboard.`
2166
2753
  );
2167
2754
  console.log();
2168
2755
  console.log(
2169
- chalk7.gray(
2756
+ chalk8.gray(
2170
2757
  "API support for setting default brand is coming soon."
2171
2758
  )
2172
2759
  );
@@ -2174,11 +2761,15 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
2174
2761
  );
2175
2762
 
2176
2763
  // src/commands/derive.ts
2177
- import { Command as Command9 } from "commander";
2178
- import chalk8 from "chalk";
2179
- import ora6 from "ora";
2764
+ init_api();
2765
+ init_auth();
2766
+ init_feature_cache();
2767
+ import { Command as Command11 } from "commander";
2768
+ import chalk9 from "chalk";
2769
+ import ora7 from "ora";
2770
+ init_output();
2180
2771
  function createBlogCommand() {
2181
- return new Command9("blog").description("Generate a blog post from a presentation").argument("<slug>", "Presentation slug").option("--words <count>", "Target word count (50-2000)", "300").option(
2772
+ 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
2773
  "--tone <tone>",
2183
2774
  "Writing tone (professional, casual, educational)",
2184
2775
  "professional"
@@ -2187,7 +2778,7 @@ function createBlogCommand() {
2187
2778
  "Output format (human, json, markdown)",
2188
2779
  "human"
2189
2780
  ).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();
2781
+ await requireAuth();
2191
2782
  const wordCount = parseInt(options.words, 10);
2192
2783
  if (isNaN(wordCount) || wordCount < 50 || wordCount > 2e3) {
2193
2784
  error("Word count must be between 50 and 2000");
@@ -2198,7 +2789,7 @@ function createBlogCommand() {
2198
2789
  error(`Invalid tone. Valid options: ${validTones.join(", ")}`);
2199
2790
  process.exit(6);
2200
2791
  }
2201
- const spinner = ora6("Fetching presentation...").start();
2792
+ const spinner = ora7("Fetching presentation...").start();
2202
2793
  try {
2203
2794
  const presentation = await getPresentation(presentationId);
2204
2795
  spinner.text = "Generating blog post...";
@@ -2234,8 +2825,8 @@ function createBlogCommand() {
2234
2825
  console.log(content);
2235
2826
  } else {
2236
2827
  console.log();
2237
- console.log(chalk8.bold(`Blog: ${presentation.title}`));
2238
- console.log(chalk8.gray("\u2500".repeat(40)));
2828
+ console.log(chalk9.bold(`Blog: ${presentation.title}`));
2829
+ console.log(chalk9.gray("\u2500".repeat(40)));
2239
2830
  console.log();
2240
2831
  console.log(content);
2241
2832
  console.log();
@@ -2248,35 +2839,35 @@ function createBlogCommand() {
2248
2839
  });
2249
2840
  }
2250
2841
  function createTweetsCommand() {
2251
- return new Command9("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) => {
2252
- requireAuth();
2842
+ 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) => {
2843
+ await requireAuth();
2253
2844
  info("Tweet generation coming soon.");
2254
2845
  });
2255
2846
  }
2256
2847
  function createLinkedInCommand() {
2257
- return new Command9("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) => {
2258
- requireAuth();
2848
+ 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) => {
2849
+ await requireAuth();
2259
2850
  info("LinkedIn carousel generation coming soon.");
2260
2851
  });
2261
2852
  }
2262
2853
  function createQuestionsCommand() {
2263
- return new Command9("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) => {
2264
- requireAuth();
2854
+ 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) => {
2855
+ await requireAuth();
2265
2856
  info("Question generation coming soon.");
2266
2857
  });
2267
2858
  }
2268
2859
  function createCheatsheetCommand() {
2269
- return new Command9("cheatsheet").description("Generate a presenter cheat sheet").argument("<slug>", "Presentation slug").option("--mode <mode>", "Cheat sheet mode (qa-prep, talking-points)", "qa-prep").option(
2860
+ 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
2861
  "-f, --format <format>",
2271
2862
  "Output format (human, json, markdown)",
2272
2863
  "human"
2273
2864
  ).action(async (presentationId, options) => {
2274
- requireAuth();
2865
+ await requireAuth();
2275
2866
  info("Cheat sheet generation coming soon.");
2276
2867
  });
2277
2868
  }
2278
2869
  function buildDeriveCommand() {
2279
- const derive = new Command9("derive").description("Generate derivative content from a presentation");
2870
+ const derive = new Command11("derive").description("Generate derivative content from a presentation");
2280
2871
  const flags = getCachedFlags();
2281
2872
  if (!flags) {
2282
2873
  return derive;
@@ -2300,33 +2891,39 @@ function buildDeriveCommand() {
2300
2891
  }
2301
2892
 
2302
2893
  // src/commands/ideas.ts
2303
- import { Command as Command10 } from "commander";
2304
- import chalk9 from "chalk";
2305
- var ideasCommand = new Command10("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) => {
2306
- requireAuth();
2894
+ init_auth();
2895
+ init_output();
2896
+ import { Command as Command12 } from "commander";
2897
+ import chalk10 from "chalk";
2898
+ 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) => {
2899
+ await requireAuth();
2307
2900
  info("Idea generation is available in the web dashboard.");
2308
2901
  console.log();
2309
2902
  console.log(
2310
- chalk9.gray("The CLI will support idea generation in a future release.")
2903
+ chalk10.gray("The CLI will support idea generation in a future release.")
2311
2904
  );
2312
2905
  console.log();
2313
2906
  console.log("For now, try these approaches:");
2314
2907
  console.log(
2315
- chalk9.gray(
2908
+ chalk10.gray(
2316
2909
  " 1. Visit the ConceptCraft dashboard and use the idea generator"
2317
2910
  )
2318
2911
  );
2319
2912
  console.log(
2320
- chalk9.gray(" 2. Create a presentation with a broad topic and refine it")
2913
+ chalk10.gray(" 2. Create a presentation with a broad topic and refine it")
2321
2914
  );
2322
2915
  console.log();
2323
2916
  });
2324
2917
 
2325
2918
  // src/commands/whoami.ts
2326
- import { Command as Command11 } from "commander";
2327
- import chalk10 from "chalk";
2328
- var whoamiCommand = new Command11("whoami").description("Show current user and team information").option("-f, --format <format>", "Output format (human, json)", "human").action(async (options) => {
2329
- requireAuth();
2919
+ init_api();
2920
+ init_auth();
2921
+ init_feature_cache();
2922
+ init_output();
2923
+ import { Command as Command13 } from "commander";
2924
+ import chalk11 from "chalk";
2925
+ var whoamiCommand = new Command13("whoami").description("Show current user and team information").option("-f, --format <format>", "Output format (human, json)", "human").action(async (options) => {
2926
+ await requireAuth();
2330
2927
  try {
2331
2928
  const result = await whoami();
2332
2929
  refreshInBackground();
@@ -2361,12 +2958,12 @@ var whoamiCommand = new Command11("whoami").description("Show current user and t
2361
2958
  header("All Teams");
2362
2959
  console.log();
2363
2960
  for (const team of result.teams) {
2364
- const current = team.isCurrent ? chalk10.green(" (current)") : "";
2961
+ const current = team.isCurrent ? chalk11.green(" (current)") : "";
2365
2962
  console.log(
2366
- ` ${chalk10.bold(team.name)}${current}`
2963
+ ` ${chalk11.bold(team.name)}${current}`
2367
2964
  );
2368
2965
  console.log(
2369
- chalk10.gray(` ID: ${team.id} | Plan: ${team.planName} | Role: ${team.role}`)
2966
+ chalk11.gray(` ID: ${team.id} | Plan: ${team.planName} | Role: ${team.role}`)
2370
2967
  );
2371
2968
  }
2372
2969
  }
@@ -2378,8 +2975,9 @@ var whoamiCommand = new Command11("whoami").description("Show current user and t
2378
2975
  });
2379
2976
 
2380
2977
  // src/commands/skill.ts
2381
- import { Command as Command12 } from "commander";
2382
- import chalk11 from "chalk";
2978
+ init_output();
2979
+ import { Command as Command14 } from "commander";
2980
+ import chalk12 from "chalk";
2383
2981
  import { mkdirSync, writeFileSync, existsSync as existsSync2 } from "fs";
2384
2982
  import { join } from "path";
2385
2983
  import { homedir } from "os";
@@ -2582,17 +3180,17 @@ var EDITORS = [
2582
3180
  { name: "Windsurf", dir: ".windsurf" },
2583
3181
  { name: "Agent", dir: ".agent" }
2584
3182
  ];
2585
- var skillCommand = new Command12("skill").description("Manage ConceptCraft skill for AI coding assistants").addHelpText(
3183
+ var skillCommand = new Command14("skill").description("Manage ConceptCraft skill for AI coding assistants").addHelpText(
2586
3184
  "after",
2587
3185
  `
2588
- ${chalk11.bold("Examples:")}
2589
- ${chalk11.gray("# Install skill for all detected editors")}
3186
+ ${chalk12.bold("Examples:")}
3187
+ ${chalk12.gray("# Install skill for all detected editors")}
2590
3188
  $ conceptcraft skill install
2591
3189
 
2592
- ${chalk11.gray("# Install to specific directory")}
3190
+ ${chalk12.gray("# Install to specific directory")}
2593
3191
  $ conceptcraft skill install --dir ~/.claude
2594
3192
 
2595
- ${chalk11.gray("# Show skill content")}
3193
+ ${chalk12.gray("# Show skill content")}
2596
3194
  $ conceptcraft skill show
2597
3195
  `
2598
3196
  );
@@ -2638,7 +3236,7 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
2638
3236
  if (skipped.length > 0) {
2639
3237
  console.log();
2640
3238
  info(`Skipped (already exists): ${skipped.join(", ")}`);
2641
- console.log(chalk11.gray(" Use --force to overwrite"));
3239
+ console.log(chalk12.gray(" Use --force to overwrite"));
2642
3240
  }
2643
3241
  if (errors.length > 0) {
2644
3242
  console.log();
@@ -2649,8 +3247,8 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
2649
3247
  if (installed.length === 0 && skipped.length === 0 && errors.length === 0) {
2650
3248
  info("No supported AI coding assistants detected.");
2651
3249
  console.log();
2652
- console.log(chalk11.gray("Supported editors: " + EDITORS.map((e) => e.name).join(", ")));
2653
- console.log(chalk11.gray("Use --dir <path> to install to a specific directory"));
3250
+ console.log(chalk12.gray("Supported editors: " + EDITORS.map((e) => e.name).join(", ")));
3251
+ console.log(chalk12.gray("Use --dir <path> to install to a specific directory"));
2654
3252
  }
2655
3253
  console.log();
2656
3254
  });
@@ -2687,14 +3285,16 @@ function installSkill(skillPath, force) {
2687
3285
  }
2688
3286
 
2689
3287
  // src/index.ts
2690
- var VERSION = "0.1.0";
2691
- var program = new Command13();
3288
+ var VERSION = "0.1.3";
3289
+ var program = new Command15();
2692
3290
  var cmdName = brand.commands[0];
2693
3291
  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
3292
  outputError: (str, write) => {
2695
- write(chalk12.red(str));
3293
+ write(chalk13.red(str));
2696
3294
  }
2697
3295
  });
3296
+ program.addCommand(loginCommand);
3297
+ program.addCommand(logoutCommand);
2698
3298
  program.addCommand(configCommand);
2699
3299
  program.addCommand(createCommand);
2700
3300
  program.addCommand(listCommand);
@@ -2711,7 +3311,7 @@ if (deriveCommand.commands.length > 0) {
2711
3311
  program.addCommand(deriveCommand);
2712
3312
  }
2713
3313
  program.on("command:*", (operands) => {
2714
- console.error(chalk12.red(`Error: Unknown command '${operands[0]}'`));
3314
+ console.error(chalk13.red(`Error: Unknown command '${operands[0]}'`));
2715
3315
  console.error();
2716
3316
  console.error(`Run '${cmdName} --help' to see available commands.`);
2717
3317
  process.exit(1);
@@ -2719,38 +3319,38 @@ program.on("command:*", (operands) => {
2719
3319
  program.addHelpText(
2720
3320
  "after",
2721
3321
  `
2722
- ${chalk12.bold("Examples:")}
2723
- ${chalk12.gray("# Configure CLI")}
2724
- $ ${cmdName} config init
3322
+ ${chalk13.bold("Examples:")}
3323
+ ${chalk13.gray("# Log in (opens browser)")}
3324
+ $ ${cmdName} login
2725
3325
 
2726
- ${chalk12.gray("# Create a presentation (recommended pattern)")}
3326
+ ${chalk13.gray("# Create a presentation (recommended pattern)")}
2727
3327
  $ ${cmdName} create "Q4 Business Review" \\
2728
3328
  -n 12 -m best \\
2729
3329
  --audience "Executive leadership team" \\
2730
3330
  --context "Revenue: $50M (+25% YoY), New customers: 150"
2731
3331
 
2732
- ${chalk12.gray("# Create from a file")}
3332
+ ${chalk13.gray("# Create from a file")}
2733
3333
  $ ${cmdName} create "Product Launch" \\
2734
3334
  -n 10 -m balanced \\
2735
3335
  --audience "Sales team and partners" \\
2736
3336
  --context-file ./launch-brief.md
2737
3337
 
2738
- ${chalk12.gray("# Create from URLs")}
3338
+ ${chalk13.gray("# Create from URLs")}
2739
3339
  $ ${cmdName} create "Market Analysis" \\
2740
3340
  -n 15 -m best \\
2741
3341
  --audience "Strategy team" \\
2742
3342
  --sources https://example.com/report.pdf
2743
3343
 
2744
- ${chalk12.gray("# List and export")}
3344
+ ${chalk13.gray("# List and export")}
2745
3345
  $ ${cmdName} list --format table
2746
3346
  $ ${cmdName} export <slug> -o backup.zip
2747
3347
 
2748
- ${chalk12.bold("Environment Variables:")}
2749
- CC_MINDFRAMES_API_KEY API key for authentication
2750
- CC_SLIDES_API_URL Custom API URL (optional)
3348
+ ${chalk13.bold("Authentication:")}
3349
+ Run '${cmdName} login' to authenticate (recommended).
3350
+ Or set CC_MINDFRAMES_API_KEY environment variable for API key auth.
2751
3351
 
2752
- ${chalk12.bold("More Info:")}
2753
- ${chalk12.blue(brand.docsUrl)}
3352
+ ${chalk13.bold("More Info:")}
3353
+ ${chalk13.blue(brand.docsUrl)}
2754
3354
  `
2755
3355
  );
2756
3356
  program.parse();