devwing 0.1.6 → 0.1.8

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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import chalk3 from 'chalk';
4
- import inquirer from 'inquirer';
3
+ import chalk7 from 'chalk';
4
+ import inquirer5 from 'inquirer';
5
5
  import axios2 from 'axios';
6
6
  import Conf from 'conf';
7
7
  import { AsyncEntry } from '@napi-rs/keyring';
@@ -10,7 +10,7 @@ import ora from 'ora';
10
10
  import boxen from 'boxen';
11
11
  import gradient from 'gradient-string';
12
12
  import terminalLink from 'terminal-link';
13
- import { promises, readFileSync } from 'fs';
13
+ import { readFileSync, promises } from 'fs';
14
14
  import path, { dirname, join } from 'path';
15
15
  import os from 'os';
16
16
  import { exec, execSync } from 'child_process';
@@ -72,16 +72,21 @@ var ConfigManager = class {
72
72
  }
73
73
  }
74
74
  /**
75
- * Get API URL
75
+ * Get API URL — ensures /api/v1 suffix is always present
76
76
  */
77
77
  getApiUrl() {
78
- return this.conf.get("apiUrl");
78
+ let url = this.conf.get("apiUrl");
79
+ if (url && !url.endsWith("/api/v1") && url.includes("devwing.ai")) {
80
+ url = url.replace(/\/+$/, "") + "/api/v1";
81
+ this.conf.set("apiUrl", url);
82
+ }
83
+ return url;
79
84
  }
80
85
  /**
81
86
  * Set API URL (for development/testing)
82
87
  */
83
88
  setApiUrl(url) {
84
- this.conf.set("apiUrl", url);
89
+ this.conf.set("apiUrl", url.replace(/\/+$/, ""));
85
90
  }
86
91
  /**
87
92
  * Get current workspace ID
@@ -157,6 +162,11 @@ var ConfigManager = class {
157
162
  }
158
163
  };
159
164
  var configManager = new ConfigManager();
165
+ function extractErrorMessage(data) {
166
+ if (!data) return "Unknown error";
167
+ if (typeof data === "string") return data;
168
+ return data.detail || data.message || data.error || JSON.stringify(data);
169
+ }
160
170
  var APIClient = class {
161
171
  client;
162
172
  jwtToken = null;
@@ -180,9 +190,18 @@ var APIClient = class {
180
190
  (response) => response,
181
191
  (error) => {
182
192
  if (error.response?.data) {
183
- throw new Error(error.response.data.message || error.response.data.error);
193
+ const message = extractErrorMessage(error.response.data);
194
+ const err = new Error(message);
195
+ err.statusCode = error.response.status;
196
+ throw err;
197
+ }
198
+ if (error.code === "ECONNREFUSED") {
199
+ throw new Error("Cannot connect to DevWing API. Is the server running?");
184
200
  }
185
- throw error;
201
+ if (error.code === "ENOTFOUND") {
202
+ throw new Error("Cannot resolve DevWing API hostname. Check your internet connection.");
203
+ }
204
+ throw new Error(error.message || "Network error");
186
205
  }
187
206
  );
188
207
  }
@@ -194,11 +213,22 @@ var APIClient = class {
194
213
  }
195
214
  /**
196
215
  * Login with email and password
216
+ * Returns full login response including 2FA handling
197
217
  */
198
218
  async login(email, password) {
199
219
  const response = await this.client.post("/auth/login", { email, password });
200
220
  return response.data;
201
221
  }
222
+ /**
223
+ * Complete 2FA login
224
+ */
225
+ async complete2FA(tempToken, totpCode) {
226
+ const response = await this.client.post("/auth/2fa/complete", {
227
+ temp_token: tempToken,
228
+ totp_code: totpCode
229
+ });
230
+ return response.data;
231
+ }
202
232
  /**
203
233
  * Get current user profile
204
234
  */
@@ -249,8 +279,13 @@ var APIClient = class {
249
279
  body: JSON.stringify(request)
250
280
  });
251
281
  if (!response.ok) {
252
- const error = await response.json();
253
- throw new Error(error.message || "Failed to get completion");
282
+ let errorMsg = `API error (${response.status})`;
283
+ try {
284
+ const error = await response.json();
285
+ errorMsg = extractErrorMessage(error);
286
+ } catch {
287
+ }
288
+ throw new Error(errorMsg);
254
289
  }
255
290
  if (!response.body) {
256
291
  throw new Error("No response body");
@@ -261,8 +296,7 @@ var APIClient = class {
261
296
  try {
262
297
  const message = JSON.parse(event.data);
263
298
  yield message;
264
- } catch (error) {
265
- console.error("Failed to parse SSE message:", error);
299
+ } catch {
266
300
  }
267
301
  }
268
302
  }
@@ -288,8 +322,13 @@ var APIClient = class {
288
322
  })
289
323
  });
290
324
  if (!response.ok) {
291
- const error = await response.json();
292
- throw new Error(error.message || "Failed to continue completion");
325
+ let errorMsg = `API error (${response.status})`;
326
+ try {
327
+ const error = await response.json();
328
+ errorMsg = extractErrorMessage(error);
329
+ } catch {
330
+ }
331
+ throw new Error(errorMsg);
293
332
  }
294
333
  if (!response.body) {
295
334
  throw new Error("No response body");
@@ -300,18 +339,25 @@ var APIClient = class {
300
339
  try {
301
340
  const message = JSON.parse(event.data);
302
341
  yield message;
303
- } catch (error) {
304
- console.error("Failed to parse SSE message:", error);
342
+ } catch {
305
343
  }
306
344
  }
307
345
  }
308
346
  }
309
347
  /**
310
348
  * Get project memory
349
+ * Backend returns { memories: [...], total: N }
311
350
  */
312
351
  async getProjectMemory(projectId) {
313
352
  const response = await this.client.get(`/memory/${projectId}`);
314
- return response.data;
353
+ const data = response.data;
354
+ if (data.memories && Array.isArray(data.memories)) {
355
+ return data.memories;
356
+ }
357
+ if (Array.isArray(data)) {
358
+ return data;
359
+ }
360
+ return [];
315
361
  }
316
362
  /**
317
363
  * Add project memory
@@ -331,7 +377,14 @@ var APIClient = class {
331
377
  */
332
378
  async getModels() {
333
379
  const response = await this.client.get("/models");
334
- return response.data;
380
+ const data = response.data;
381
+ if (data.models && Array.isArray(data.models)) {
382
+ return data.models;
383
+ }
384
+ if (Array.isArray(data)) {
385
+ return data;
386
+ }
387
+ return [];
335
388
  }
336
389
  /**
337
390
  * Get usage stats
@@ -348,37 +401,37 @@ var Logger = class {
348
401
  * Success message
349
402
  */
350
403
  success(message) {
351
- console.log(chalk3.green("\u2713"), message);
404
+ console.log(chalk7.green("\u2713"), message);
352
405
  }
353
406
  /**
354
407
  * Error message
355
408
  */
356
409
  error(message, error) {
357
- console.log(chalk3.red("\u2717"), message);
410
+ console.log(chalk7.red("\u2717"), message);
358
411
  if (error && process.env.DEBUG) {
359
- console.error(chalk3.gray(error.stack || error));
412
+ console.error(chalk7.gray(error.stack || error));
360
413
  }
361
414
  }
362
415
  /**
363
416
  * Warning message
364
417
  */
365
418
  warn(message) {
366
- console.log(chalk3.yellow("\u26A0"), message);
419
+ console.log(chalk7.yellow("\u26A0"), message);
367
420
  }
368
421
  /**
369
422
  * Info message
370
423
  */
371
424
  info(message) {
372
- console.log(chalk3.blue("\u2139"), message);
425
+ console.log(chalk7.blue("\u2139"), message);
373
426
  }
374
427
  /**
375
428
  * Debug message (only shown if DEBUG env var is set)
376
429
  */
377
430
  debug(message, data) {
378
431
  if (process.env.DEBUG) {
379
- console.log(chalk3.gray("\u2699"), chalk3.gray(message));
432
+ console.log(chalk7.gray("\u2699"), chalk7.gray(message));
380
433
  if (data) {
381
- console.log(chalk3.gray(JSON.stringify(data, null, 2)));
434
+ console.log(chalk7.gray(JSON.stringify(data, null, 2)));
382
435
  }
383
436
  }
384
437
  }
@@ -467,11 +520,17 @@ var Logger = class {
467
520
  }
468
521
  };
469
522
  var logger = new Logger();
523
+ function getUserPlan(user) {
524
+ return user.subscription_plan || user.plan || "free";
525
+ }
526
+ function getUserDisplayName(user) {
527
+ return user.full_name || user.username || user.email;
528
+ }
470
529
  async function loginCommand() {
471
530
  try {
472
531
  const existingKey = await configManager.getApiKey();
473
532
  if (existingKey) {
474
- const { overwrite } = await inquirer.prompt([
533
+ const { overwrite } = await inquirer5.prompt([
475
534
  {
476
535
  type: "confirm",
477
536
  name: "overwrite",
@@ -486,14 +545,14 @@ async function loginCommand() {
486
545
  }
487
546
  logger.printBanner();
488
547
  logger.newline();
489
- const { method } = await inquirer.prompt([
548
+ const { method } = await inquirer5.prompt([
490
549
  {
491
550
  type: "list",
492
551
  name: "method",
493
552
  message: "How would you like to authenticate?",
494
553
  choices: [
495
- { name: "Email & Password", value: "email" },
496
554
  { name: "Via Browser (easiest)", value: "browser" },
555
+ { name: "Email & Password", value: "email" },
497
556
  { name: "API Key (from dashboard)", value: "apikey" }
498
557
  ]
499
558
  }
@@ -506,18 +565,21 @@ async function loginCommand() {
506
565
  await loginWithApiKey();
507
566
  }
508
567
  } catch (error) {
509
- logger.error("Login failed", error);
568
+ logger.error("Login failed");
569
+ if (error.message) {
570
+ logger.error(error.message);
571
+ }
510
572
  process.exit(1);
511
573
  }
512
574
  }
513
575
  async function loginWithEmail() {
514
- const { email, password } = await inquirer.prompt([
576
+ const { email, password } = await inquirer5.prompt([
515
577
  {
516
578
  type: "input",
517
579
  name: "email",
518
580
  message: "Email:",
519
581
  validate: (input) => {
520
- if (!input.includes("@")) {
582
+ if (!input.includes("@") || !input.includes(".")) {
521
583
  return "Please enter a valid email address";
522
584
  }
523
585
  return true;
@@ -527,23 +589,56 @@ async function loginWithEmail() {
527
589
  type: "password",
528
590
  name: "password",
529
591
  message: "Password:",
530
- mask: "*"
592
+ mask: "*",
593
+ validate: (input) => {
594
+ if (!input || input.length < 1) {
595
+ return "Password is required";
596
+ }
597
+ return true;
598
+ }
531
599
  }
532
600
  ]);
533
601
  logger.startSpinner("Authenticating...");
534
602
  try {
535
- const { access_token, user } = await apiClient.login(email, password);
536
- apiClient.setJwtToken(access_token);
603
+ let loginResult = await apiClient.login(email, password);
604
+ if (loginResult.requires_2fa) {
605
+ logger.succeedSpinner("Credentials verified");
606
+ logger.info("Two-factor authentication is enabled on your account");
607
+ const { totpCode } = await inquirer5.prompt([
608
+ {
609
+ type: "input",
610
+ name: "totpCode",
611
+ message: "2FA Code (from authenticator app):",
612
+ validate: (input) => {
613
+ if (!/^\d{6}$/.test(input)) {
614
+ return "Please enter a 6-digit code";
615
+ }
616
+ return true;
617
+ }
618
+ }
619
+ ]);
620
+ logger.startSpinner("Verifying 2FA code...");
621
+ loginResult = await apiClient.complete2FA(loginResult.temp_token, totpCode);
622
+ }
623
+ if (!loginResult.access_token || !loginResult.user) {
624
+ logger.failSpinner("Login failed");
625
+ logger.error("Unexpected response from server");
626
+ return;
627
+ }
628
+ apiClient.setJwtToken(loginResult.access_token);
537
629
  logger.updateSpinner("Creating API key...");
538
- const { key } = await apiClient.createApiKey("CLI - Auto-generated");
630
+ const apiKeyResult = await apiClient.createApiKey("CLI - Auto-generated");
539
631
  apiClient.setJwtToken(null);
540
- await configManager.setApiKey(key);
541
- logger.succeedSpinner(`Welcome back, ${user.full_name || user.email}!`);
542
- logger.success(`Plan: ${user.plan.toUpperCase()}`);
632
+ await configManager.setApiKey(apiKeyResult.key);
633
+ const user = loginResult.user;
634
+ const plan = getUserPlan(user);
635
+ logger.succeedSpinner(`Welcome back, ${getUserDisplayName(user)}!`);
636
+ logger.success(`Plan: ${plan.toUpperCase()}`);
543
637
  logger.newline();
544
638
  showQuickStart();
545
639
  } catch (error) {
546
640
  logger.failSpinner("Authentication failed");
641
+ logger.error(error.message || "Unknown error occurred");
547
642
  throw error;
548
643
  }
549
644
  }
@@ -552,21 +647,23 @@ async function loginWithBrowser() {
552
647
  const open = await import('open');
553
648
  logger.startSpinner("Initiating browser authentication...");
554
649
  try {
555
- const deviceInfo = `DevWing CLI on ${os2.platform()}`;
650
+ const deviceInfo = `DevWing CLI on ${os2.platform()} (${os2.hostname()})`;
556
651
  const initResponse = await apiClient.initiateCliAuth(deviceInfo);
557
- logger.succeedSpinner("Opening browser...");
652
+ logger.succeedSpinner("Device code created");
558
653
  logger.newline();
654
+ const verifyUrl = initResponse.verification_url_complete;
559
655
  try {
560
- await open.default(initResponse.verification_url_complete);
561
- logger.success("Browser opened! Please log in to complete authentication.");
562
- } catch (err) {
563
- logger.warn("Could not open browser automatically");
564
- logger.info(`Please visit: ${chalk3.cyan(initResponse.verification_url_complete)}`);
656
+ await open.default(verifyUrl);
657
+ logger.success("Browser opened! Please log in to authorize this device.");
658
+ } catch {
659
+ logger.warn("Could not open browser automatically.");
660
+ logger.info(`Please visit: ${chalk7.cyan(verifyUrl)}`);
565
661
  }
566
662
  logger.newline();
663
+ logger.info(chalk7.dim(`Device code: ${initResponse.user_code.substring(0, 8)}...`));
567
664
  logger.startSpinner("Waiting for authorization...");
568
- const pollInterval = initResponse.interval * 1e3;
569
- const maxAttempts = Math.floor(initResponse.expires_in / initResponse.interval);
665
+ const pollInterval = (initResponse.interval || 5) * 1e3;
666
+ const maxAttempts = Math.floor((initResponse.expires_in || 900) / (initResponse.interval || 5));
570
667
  let attempts = 0;
571
668
  const pollForAuth = async () => {
572
669
  return new Promise((resolve, reject) => {
@@ -574,13 +671,11 @@ async function loginWithBrowser() {
574
671
  attempts++;
575
672
  if (attempts >= maxAttempts) {
576
673
  clearInterval(interval);
577
- reject(new Error("Authentication timed out. Please try again."));
674
+ reject(new Error('Authentication timed out. Please try again with "devwing login".'));
578
675
  return;
579
676
  }
580
677
  try {
581
- const pollResponse = await apiClient.pollCliAuth(
582
- initResponse.device_code
583
- );
678
+ const pollResponse = await apiClient.pollCliAuth(initResponse.device_code);
584
679
  if (pollResponse.status === "authorized" && pollResponse.api_key) {
585
680
  clearInterval(interval);
586
681
  resolve(pollResponse.api_key);
@@ -589,11 +684,12 @@ async function loginWithBrowser() {
589
684
  reject(new Error("Authorization denied by user."));
590
685
  } else if (pollResponse.status === "expired") {
591
686
  clearInterval(interval);
592
- reject(
593
- new Error('Device code expired. Please run "devwing login" again.')
594
- );
687
+ reject(new Error('Device code expired. Please run "devwing login" again.'));
595
688
  }
596
689
  } catch (error) {
690
+ if (error.statusCode === 429) {
691
+ return;
692
+ }
597
693
  clearInterval(interval);
598
694
  reject(error);
599
695
  }
@@ -604,12 +700,14 @@ async function loginWithBrowser() {
604
700
  await configManager.setApiKey(apiKey);
605
701
  logger.updateSpinner("Fetching profile...");
606
702
  const user = await apiClient.getProfile();
607
- logger.succeedSpinner(`Welcome, ${user.full_name || user.email}!`);
608
- logger.success(`Plan: ${user.plan.toUpperCase()}`);
703
+ const plan = getUserPlan(user);
704
+ logger.succeedSpinner(`Welcome, ${getUserDisplayName(user)}!`);
705
+ logger.success(`Plan: ${plan.toUpperCase()}`);
609
706
  logger.newline();
610
707
  showQuickStart();
611
708
  } catch (error) {
612
709
  logger.failSpinner("Browser authentication failed");
710
+ logger.error(error.message || "Unknown error");
613
711
  throw error;
614
712
  }
615
713
  }
@@ -621,7 +719,7 @@ async function loginWithApiKey() {
621
719
  );
622
720
  logger.info(`Get your API key from the ${dashboardUrl}`);
623
721
  logger.newline();
624
- const { apiKey } = await inquirer.prompt([
722
+ const { apiKey } = await inquirer5.prompt([
625
723
  {
626
724
  type: "password",
627
725
  name: "apiKey",
@@ -642,13 +740,15 @@ async function loginWithApiKey() {
642
740
  try {
643
741
  await configManager.setApiKey(apiKey);
644
742
  const user = await apiClient.getProfile();
645
- logger.succeedSpinner(`Successfully authenticated as ${user.full_name || user.email}!`);
646
- logger.success(`Plan: ${user.plan.toUpperCase()}`);
743
+ const plan = getUserPlan(user);
744
+ logger.succeedSpinner(`Authenticated as ${getUserDisplayName(user)}!`);
745
+ logger.success(`Plan: ${plan.toUpperCase()}`);
647
746
  logger.newline();
648
747
  showQuickStart();
649
748
  } catch (error) {
650
749
  await configManager.deleteApiKey();
651
750
  logger.failSpinner("Invalid API key");
751
+ logger.error(error.message || "Could not verify API key");
652
752
  throw error;
653
753
  }
654
754
  }
@@ -659,7 +759,7 @@ async function logoutCommand() {
659
759
  logger.info("You are not logged in");
660
760
  return;
661
761
  }
662
- const { confirm } = await inquirer.prompt([
762
+ const { confirm } = await inquirer5.prompt([
663
763
  {
664
764
  type: "confirm",
665
765
  name: "confirm",
@@ -677,7 +777,8 @@ async function logoutCommand() {
677
777
  configManager.setApiUrl(apiUrl);
678
778
  logger.success("Successfully logged out");
679
779
  } catch (error) {
680
- logger.error("Logout failed", error);
780
+ logger.error("Logout failed");
781
+ logger.error(error.message || "Unknown error");
681
782
  process.exit(1);
682
783
  }
683
784
  }
@@ -692,15 +793,19 @@ async function statusCommand() {
692
793
  logger.startSpinner("Fetching profile...");
693
794
  try {
694
795
  const user = await apiClient.getProfile();
796
+ const plan = getUserPlan(user);
695
797
  logger.succeedSpinner("Authenticated");
696
798
  logger.newline();
697
- console.log(chalk3.bold("User Profile:"));
799
+ console.log(chalk7.bold("User Profile:"));
698
800
  console.log(` Email: ${user.email}`);
699
- console.log(` Name: ${user.full_name || "Not set"}`);
700
- console.log(` Plan: ${user.plan.toUpperCase()}`);
701
- console.log(
702
- ` Tokens used today: ${user.tokens_used_today.toLocaleString()}`
703
- );
801
+ console.log(` Name: ${getUserDisplayName(user)}`);
802
+ console.log(` Plan: ${plan.toUpperCase()}`);
803
+ if (user.is_verified !== void 0) {
804
+ console.log(` Verified: ${user.is_verified ? chalk7.green("Yes") : chalk7.yellow("No")}`);
805
+ }
806
+ if (user.totp_enabled !== void 0) {
807
+ console.log(` 2FA: ${user.totp_enabled ? chalk7.green("Enabled") : chalk7.dim("Disabled")}`);
808
+ }
704
809
  const config = configManager.getAll();
705
810
  if (config.workspaceId) {
706
811
  console.log(` Workspace: ${config.workspaceId}`);
@@ -711,11 +816,12 @@ async function statusCommand() {
711
816
  logger.newline();
712
817
  } catch (error) {
713
818
  logger.failSpinner("Failed to fetch profile");
714
- logger.error("API key may be invalid or expired");
819
+ logger.error(error.message || "API key may be invalid or expired");
715
820
  logger.info('Run "devwing login" to re-authenticate');
716
821
  }
717
822
  } catch (error) {
718
- logger.error("Failed to check status", error);
823
+ logger.error("Failed to check status");
824
+ logger.error(error.message || "Unknown error");
719
825
  process.exit(1);
720
826
  }
721
827
  }
@@ -725,6 +831,7 @@ function showQuickStart() {
725
831
  "Quick Start:",
726
832
  "",
727
833
  ' devwing "fix the auth bug" Ask anything',
834
+ " devwing chat Interactive chat mode",
728
835
  " devwing scan Security scan",
729
836
  " devwing review Code review",
730
837
  " devwing explain file.js Explain code",
@@ -1292,22 +1399,22 @@ Summary: ${result.summary.changes} changes, ${result.summary.insertions} inserti
1292
1399
  // src/streaming/renderer.ts
1293
1400
  marked.setOptions({
1294
1401
  renderer: new TerminalRenderer({
1295
- code: chalk3.cyan,
1296
- blockquote: chalk3.gray.italic,
1297
- html: chalk3.gray,
1298
- heading: chalk3.bold.underline,
1299
- firstHeading: chalk3.bold.magenta,
1300
- hr: chalk3.reset,
1301
- listitem: chalk3.reset,
1302
- list: chalk3.reset,
1303
- table: chalk3.reset,
1304
- paragraph: chalk3.reset,
1305
- strong: chalk3.bold,
1306
- em: chalk3.italic,
1307
- codespan: chalk3.cyan,
1308
- del: chalk3.dim.strikethrough,
1309
- link: chalk3.blue.underline,
1310
- href: chalk3.blue.underline
1402
+ code: chalk7.cyan,
1403
+ blockquote: chalk7.gray.italic,
1404
+ html: chalk7.gray,
1405
+ heading: chalk7.bold.underline,
1406
+ firstHeading: chalk7.bold.magenta,
1407
+ hr: chalk7.reset,
1408
+ listitem: chalk7.reset,
1409
+ list: chalk7.reset,
1410
+ table: chalk7.reset,
1411
+ paragraph: chalk7.reset,
1412
+ strong: chalk7.bold,
1413
+ em: chalk7.italic,
1414
+ codespan: chalk7.cyan,
1415
+ del: chalk7.dim.strikethrough,
1416
+ link: chalk7.blue.underline,
1417
+ href: chalk7.blue.underline
1311
1418
  })
1312
1419
  });
1313
1420
  var StreamingRenderer = class {
@@ -1402,10 +1509,10 @@ var StreamingRenderer = class {
1402
1509
  logger.newline();
1403
1510
  const { tool, args } = message;
1404
1511
  if (!tool) return;
1405
- logger.info(chalk3.yellow(`AI requested tool: ${chalk3.bold(tool)}`));
1512
+ logger.info(chalk7.yellow(`AI requested tool: ${chalk7.bold(tool)}`));
1406
1513
  if (args && Object.keys(args).length > 0) {
1407
- console.log(chalk3.gray("Arguments:"));
1408
- console.log(chalk3.gray(JSON.stringify(args, null, 2)));
1514
+ console.log(chalk7.gray("Arguments:"));
1515
+ console.log(chalk7.gray(JSON.stringify(args, null, 2)));
1409
1516
  }
1410
1517
  logger.newline();
1411
1518
  }
@@ -1417,11 +1524,11 @@ var StreamingRenderer = class {
1417
1524
  const results = [];
1418
1525
  for (const { tool, args } of this.pendingToolCalls) {
1419
1526
  logger.newline();
1420
- logger.info(chalk3.yellow(`Executing ${chalk3.bold(tool)}...`));
1527
+ logger.info(chalk7.yellow(`Executing ${chalk7.bold(tool)}...`));
1421
1528
  const requiresConfirmation = this.requiresConfirmation(tool);
1422
1529
  let confirmed = true;
1423
1530
  if (requiresConfirmation) {
1424
- const answers = await inquirer.prompt([
1531
+ const answers = await inquirer5.prompt([
1425
1532
  {
1426
1533
  type: "confirm",
1427
1534
  name: "confirmed",
@@ -1450,9 +1557,9 @@ var StreamingRenderer = class {
1450
1557
  const content = result.output || "";
1451
1558
  const preview = content.substring(0, 500);
1452
1559
  if (preview.length < content.length) {
1453
- console.log(chalk3.gray(preview + "... (truncated)"));
1560
+ console.log(chalk7.gray(preview + "... (truncated)"));
1454
1561
  } else {
1455
- console.log(chalk3.gray(preview));
1562
+ console.log(chalk7.gray(preview));
1456
1563
  }
1457
1564
  results.push({
1458
1565
  tool,
@@ -1490,9 +1597,9 @@ var StreamingRenderer = class {
1490
1597
  if (message.content) {
1491
1598
  const preview = message.content.substring(0, 500);
1492
1599
  if (preview.length < message.content.length) {
1493
- console.log(chalk3.gray(preview + "... (truncated)"));
1600
+ console.log(chalk7.gray(preview + "... (truncated)"));
1494
1601
  } else {
1495
- console.log(chalk3.gray(preview));
1602
+ console.log(chalk7.gray(preview));
1496
1603
  }
1497
1604
  }
1498
1605
  logger.newline();
@@ -1506,7 +1613,7 @@ var StreamingRenderer = class {
1506
1613
  if (message.usage) {
1507
1614
  const { tokens_in, tokens_out, cost_usd } = message.usage;
1508
1615
  console.log(
1509
- chalk3.gray(
1616
+ chalk7.gray(
1510
1617
  `\u{1F4CA} Tokens: ${tokens_in.toLocaleString()} in, ${tokens_out.toLocaleString()} out | Cost: $${cost_usd.toFixed(4)}`
1511
1618
  )
1512
1619
  );
@@ -1684,7 +1791,7 @@ async function memorySave(projectId, content) {
1684
1791
  logger.error("Please provide content to save");
1685
1792
  return;
1686
1793
  }
1687
- const { type } = await inquirer.prompt([
1794
+ const { type } = await inquirer5.prompt([
1688
1795
  {
1689
1796
  type: "list",
1690
1797
  name: "type",
@@ -1725,7 +1832,7 @@ async function memoryShow(projectId) {
1725
1832
  for (const memory of memories) {
1726
1833
  const preview = memory.content.length > 100 ? memory.content.substring(0, 100) + "..." : memory.content;
1727
1834
  table.push([
1728
- chalk3.cyan(memory.type),
1835
+ chalk7.cyan(memory.type),
1729
1836
  preview,
1730
1837
  new Date(memory.created_at).toLocaleDateString()
1731
1838
  ]);
@@ -1737,7 +1844,7 @@ async function memoryShow(projectId) {
1737
1844
  }
1738
1845
  }
1739
1846
  async function memoryClear(projectId) {
1740
- const { confirm } = await inquirer.prompt([
1847
+ const { confirm } = await inquirer5.prompt([
1741
1848
  {
1742
1849
  type: "confirm",
1743
1850
  name: "confirm",
@@ -1766,7 +1873,7 @@ async function configCommand(action, key, value) {
1766
1873
  if (!action || action === "list") {
1767
1874
  const config = configManager.getAll();
1768
1875
  const apiKey = await configManager.getApiKey();
1769
- console.log(chalk3.bold("Current Configuration:"));
1876
+ console.log(chalk7.bold("Current Configuration:"));
1770
1877
  console.log(` API URL: ${config.apiUrl}`);
1771
1878
  console.log(` API Key: ${apiKey ? apiKey.substring(0, 12) + "..." : "Not set"}`);
1772
1879
  console.log(` Workspace: ${config.workspaceId || "Not set"}`);
@@ -1774,7 +1881,7 @@ async function configCommand(action, key, value) {
1774
1881
  console.log(` Model: ${config.model || "Auto"}`);
1775
1882
  console.log(` Mode: ${config.mode || "Auto"}`);
1776
1883
  console.log();
1777
- console.log(chalk3.gray(`Config file: ${configManager.getPath()}`));
1884
+ console.log(chalk7.gray(`Config file: ${configManager.getPath()}`));
1778
1885
  return;
1779
1886
  }
1780
1887
  if (action === "get") {
@@ -1865,26 +1972,26 @@ async function handleSpecialCommand(command, session, options) {
1865
1972
  const mainCmd = parts[0].toLowerCase();
1866
1973
  if (cmd === "/help") {
1867
1974
  logger.box([
1868
- chalk3.bold("DevWing Chat Commands:"),
1975
+ chalk7.bold("DevWing Chat Commands:"),
1869
1976
  "",
1870
- chalk3.bold.yellow("Navigation & Session:"),
1871
- chalk3.cyan("/help") + " - Show this help message",
1872
- chalk3.cyan("/exit, /quit") + " - Exit chat mode",
1873
- chalk3.cyan("/clear") + " - Clear conversation history",
1874
- chalk3.cyan("/context") + " - Show current context",
1875
- chalk3.cyan("/history") + " - Show conversation history",
1977
+ chalk7.bold.yellow("Navigation & Session:"),
1978
+ chalk7.cyan("/help") + " - Show this help message",
1979
+ chalk7.cyan("/exit, /quit") + " - Exit chat mode",
1980
+ chalk7.cyan("/clear") + " - Clear conversation history",
1981
+ chalk7.cyan("/context") + " - Show current context",
1982
+ chalk7.cyan("/history") + " - Show conversation history",
1876
1983
  "",
1877
- chalk3.bold.yellow("Specialized Commands:"),
1878
- chalk3.cyan("/scan") + " - Run security vulnerability scan",
1879
- chalk3.cyan("/review") + " - Perform code review",
1880
- chalk3.cyan("/explain <target>") + " - Explain code, file, or concept",
1984
+ chalk7.bold.yellow("Specialized Commands:"),
1985
+ chalk7.cyan("/scan") + " - Run security vulnerability scan",
1986
+ chalk7.cyan("/review") + " - Perform code review",
1987
+ chalk7.cyan("/explain <target>") + " - Explain code, file, or concept",
1881
1988
  "",
1882
- chalk3.bold.yellow("Project Memory:"),
1883
- chalk3.cyan("/memory save <content>") + " - Save note to project memory",
1884
- chalk3.cyan("/memory show") + " - Show all project memories",
1885
- chalk3.cyan("/memory clear") + " - Clear all project memories",
1989
+ chalk7.bold.yellow("Project Memory:"),
1990
+ chalk7.cyan("/memory save <content>") + " - Save note to project memory",
1991
+ chalk7.cyan("/memory show") + " - Show all project memories",
1992
+ chalk7.cyan("/memory clear") + " - Clear all project memories",
1886
1993
  "",
1887
- chalk3.dim("Just type your message to chat with DevWing!")
1994
+ chalk7.dim("Just type your message to chat with DevWing!")
1888
1995
  ].join("\n"), { title: "Chat Commands", color: "blue" });
1889
1996
  return true;
1890
1997
  }
@@ -1906,9 +2013,9 @@ async function handleSpecialCommand(command, session, options) {
1906
2013
  return true;
1907
2014
  }
1908
2015
  logger.box([
1909
- chalk3.bold("Current Context:"),
2016
+ chalk7.bold("Current Context:"),
1910
2017
  "",
1911
- `Working Directory: ${chalk3.cyan(context.cwd)}`,
2018
+ `Working Directory: ${chalk7.cyan(context.cwd)}`,
1912
2019
  `Shell: ${context.shell}`,
1913
2020
  `OS: ${context.os}`,
1914
2021
  `Files: ${context.files.length}`,
@@ -1927,7 +2034,7 @@ async function handleSpecialCommand(command, session, options) {
1927
2034
  return true;
1928
2035
  }
1929
2036
  logger.box([
1930
- chalk3.bold("Conversation History:"),
2037
+ chalk7.bold("Conversation History:"),
1931
2038
  "",
1932
2039
  session.getConversationSummary()
1933
2040
  ].join("\n"), { title: "History", color: "magenta" });
@@ -1983,11 +2090,11 @@ async function handleSpecialCommand(command, session, options) {
1983
2090
  }
1984
2091
  function printWelcomeBanner() {
1985
2092
  logger.box([
1986
- chalk3.bold.cyan("DevWing AI - Interactive Chat Mode"),
2093
+ chalk7.bold.cyan("DevWing AI - Interactive Chat Mode"),
1987
2094
  "",
1988
2095
  "Chat with AI about your codebase. Type your message and press Enter.",
1989
2096
  "",
1990
- chalk3.dim("Type /help for commands \u2022 /exit to quit")
2097
+ chalk7.dim("Type /help for commands \u2022 /exit to quit")
1991
2098
  ].join("\n"), { title: "\u{1F680} Welcome", color: "cyan" });
1992
2099
  }
1993
2100
  async function chatCommand(options) {
@@ -2004,7 +2111,7 @@ async function chatCommand(options) {
2004
2111
  const rl = readline.createInterface({
2005
2112
  input: process.stdin,
2006
2113
  output: process.stdout,
2007
- prompt: chalk3.bold.green("You: ")
2114
+ prompt: chalk7.bold.green("You: ")
2008
2115
  });
2009
2116
  rl.on("SIGINT", () => {
2010
2117
  logger.newline();
@@ -2037,7 +2144,7 @@ async function chatCommand(options) {
2037
2144
  }
2038
2145
  session.addMessage("user", userMessage);
2039
2146
  logger.newline();
2040
- logger.info(chalk3.dim("DevWing is thinking..."));
2147
+ logger.info(chalk7.dim("DevWing is thinking..."));
2041
2148
  logger.newline();
2042
2149
  try {
2043
2150
  const request = {
@@ -2203,14 +2310,14 @@ function installUpdate(packageName, packageManager, version) {
2203
2310
  }
2204
2311
  function displayUpdateInfo(currentVersion, latestVersion) {
2205
2312
  console.log();
2206
- console.log(chalk3.cyan("\u2501".repeat(60)));
2207
- console.log(chalk3.bold.cyan(" DevWing CLI Update Available"));
2208
- console.log(chalk3.cyan("\u2501".repeat(60)));
2313
+ console.log(chalk7.cyan("\u2501".repeat(60)));
2314
+ console.log(chalk7.bold.cyan(" DevWing CLI Update Available"));
2315
+ console.log(chalk7.cyan("\u2501".repeat(60)));
2209
2316
  console.log();
2210
- console.log(` ${chalk3.gray("Current version:")} ${chalk3.yellow(currentVersion)}`);
2211
- console.log(` ${chalk3.gray("Latest version:")} ${chalk3.green(latestVersion)}`);
2317
+ console.log(` ${chalk7.gray("Current version:")} ${chalk7.yellow(currentVersion)}`);
2318
+ console.log(` ${chalk7.gray("Latest version:")} ${chalk7.green(latestVersion)}`);
2212
2319
  console.log();
2213
- console.log(chalk3.gray(" Changelog: https://github.com/devwing/devwing/releases"));
2320
+ console.log(chalk7.gray(" Changelog: https://github.com/devwing/devwing/releases"));
2214
2321
  console.log();
2215
2322
  }
2216
2323
  async function updateCommand(options) {
@@ -2218,7 +2325,7 @@ async function updateCommand(options) {
2218
2325
  const currentVersion = getCurrentVersion();
2219
2326
  const packageName = getPackageName();
2220
2327
  console.log();
2221
- console.log(chalk3.cyan(`\u{1F4E6} DevWing CLI v${currentVersion}`));
2328
+ console.log(chalk7.cyan(`\u{1F4E6} DevWing CLI v${currentVersion}`));
2222
2329
  console.log();
2223
2330
  logger.startSpinner("Checking for updates...");
2224
2331
  const latestVersion = await getLatestVersion(packageName);
@@ -2242,9 +2349,9 @@ async function updateCommand(options) {
2242
2349
  logger.error("No package manager found. Please install npm, pnpm, or yarn.");
2243
2350
  return;
2244
2351
  }
2245
- logger.info(`Detected package manager: ${chalk3.cyan(packageManager)}`);
2352
+ logger.info(`Detected package manager: ${chalk7.cyan(packageManager)}`);
2246
2353
  if (!options?.force) {
2247
- const { confirm } = await inquirer.prompt([
2354
+ const { confirm } = await inquirer5.prompt([
2248
2355
  {
2249
2356
  type: "confirm",
2250
2357
  name: "confirm",
@@ -2263,13 +2370,13 @@ async function updateCommand(options) {
2263
2370
  console.log();
2264
2371
  logger.success(`Successfully updated to v${latestVersion}`);
2265
2372
  console.log();
2266
- console.log(chalk3.gray(' Run "devwing --version" to verify'));
2373
+ console.log(chalk7.gray(' Run "devwing --version" to verify'));
2267
2374
  console.log();
2268
2375
  } else {
2269
2376
  console.log();
2270
2377
  logger.error("Update failed. Please try manually:");
2271
2378
  console.log();
2272
- console.log(chalk3.gray(` ${packageManager} install -g ${packageName}@${latestVersion}`));
2379
+ console.log(chalk7.gray(` ${packageManager} install -g ${packageName}@${latestVersion}`));
2273
2380
  console.log();
2274
2381
  process.exit(1);
2275
2382
  }
@@ -2278,49 +2385,867 @@ async function updateCommand(options) {
2278
2385
  process.exit(1);
2279
2386
  }
2280
2387
  }
2281
-
2282
- // src/index.ts
2388
+ var TYPING_SPEED = 16;
2389
+ var PAUSE_SHORT = 300;
2390
+ var PAUSE_MEDIUM = 800;
2391
+ var PAUSE_LONG = 1500;
2392
+ function sleep(ms) {
2393
+ return new Promise((resolve) => setTimeout(resolve, ms));
2394
+ }
2395
+ async function typeOut(text, speed = TYPING_SPEED) {
2396
+ for (const char of text) {
2397
+ process.stdout.write(char);
2398
+ await sleep(speed);
2399
+ }
2400
+ }
2401
+ async function typeLine(text, speed = TYPING_SPEED) {
2402
+ await typeOut(text, speed);
2403
+ process.stdout.write("\n");
2404
+ }
2405
+ var DEMO_USER = {
2406
+ email: "aliyu@devwing.ai",
2407
+ full_name: "Aliyu Mohammed",
2408
+ subscription_plan: "pro"};
2409
+ var DEMO_API_KEY = "dw_sk_demo_4xK9mQzR2pLvBnWcYjEtHuFi";
2410
+ var DEMO_PASSWORD = "DevWing2026!";
2411
+ var DEMO_WORKSPACE = {
2412
+ name: "Kano State Government",
2413
+ plan: "enterprise"};
2414
+ var DEMO_PROJECT = {
2415
+ name: "Kano Smart API"};
2416
+ var DEMO_MEMORIES = [
2417
+ { id: "mem_001", type: "rule", content: "All API responses must include Hausa language support", created_at: "2026-03-01T10:00:00Z" },
2418
+ { id: "mem_002", type: "context", content: "Auth uses JWT with RS256 signing, 1hr expiry, refresh tokens in Redis", created_at: "2026-03-05T14:00:00Z" },
2419
+ { id: "mem_003", type: "decision", content: "Chose PostgreSQL over MongoDB for ACID compliance \u2014 government data requires strict consistency", created_at: "2026-03-10T09:00:00Z" },
2420
+ { id: "mem_004", type: "summary", content: 'Kano Smart serves 16M residents via kanostate.gov.ng and smart.kano.gov.ng \u2014 all responses branded "Powered by devwing.ai"', created_at: "2026-03-15T11:00:00Z" }
2421
+ ];
2422
+ async function demoLoginCommand() {
2423
+ try {
2424
+ const existingKey = await configManager.getApiKey();
2425
+ if (existingKey) {
2426
+ const { overwrite } = await inquirer5.prompt([
2427
+ {
2428
+ type: "confirm",
2429
+ name: "overwrite",
2430
+ message: "You are already logged in. Do you want to log in with a different account?",
2431
+ default: false
2432
+ }
2433
+ ]);
2434
+ if (!overwrite) {
2435
+ logger.info("Login cancelled");
2436
+ return;
2437
+ }
2438
+ }
2439
+ logger.printBanner();
2440
+ logger.newline();
2441
+ const { method } = await inquirer5.prompt([
2442
+ {
2443
+ type: "list",
2444
+ name: "method",
2445
+ message: "How would you like to authenticate?",
2446
+ choices: [
2447
+ { name: "Via Browser (easiest)", value: "browser" },
2448
+ { name: "Email & Password", value: "email" },
2449
+ { name: "API Key (from dashboard)", value: "apikey" }
2450
+ ]
2451
+ }
2452
+ ]);
2453
+ if (method === "email") {
2454
+ await demoLoginEmail();
2455
+ } else if (method === "browser") {
2456
+ await demoLoginBrowser();
2457
+ } else {
2458
+ await demoLoginApiKey();
2459
+ }
2460
+ } catch (error) {
2461
+ logger.error("Login failed");
2462
+ if (error.message) logger.error(error.message);
2463
+ process.exit(1);
2464
+ }
2465
+ }
2466
+ async function demoLoginEmail() {
2467
+ const { email, password } = await inquirer5.prompt([
2468
+ {
2469
+ type: "input",
2470
+ name: "email",
2471
+ message: "Email:",
2472
+ validate: (input) => {
2473
+ if (!input.includes("@") || !input.includes(".")) return "Please enter a valid email address";
2474
+ return true;
2475
+ }
2476
+ },
2477
+ {
2478
+ type: "password",
2479
+ name: "password",
2480
+ message: "Password:",
2481
+ mask: "*"
2482
+ }
2483
+ ]);
2484
+ logger.startSpinner("Authenticating...");
2485
+ await sleep(1200);
2486
+ if (email !== DEMO_USER.email || password !== DEMO_PASSWORD) {
2487
+ logger.failSpinner("Authentication failed");
2488
+ logger.error("Incorrect email or password");
2489
+ return;
2490
+ }
2491
+ logger.updateSpinner("Creating API key...");
2492
+ await sleep(800);
2493
+ await configManager.setApiKey(DEMO_API_KEY);
2494
+ logger.succeedSpinner(`Welcome back, ${DEMO_USER.full_name}!`);
2495
+ logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2496
+ logger.newline();
2497
+ showQuickStart2();
2498
+ }
2499
+ async function demoLoginBrowser() {
2500
+ logger.startSpinner("Initiating browser authentication...");
2501
+ await sleep(1e3);
2502
+ logger.succeedSpinner("Device code created");
2503
+ logger.newline();
2504
+ logger.success("Browser opened! Please log in to authorize this device.");
2505
+ logger.newline();
2506
+ logger.info(chalk7.dim("Device code: fSibdCwR..."));
2507
+ logger.startSpinner("Waiting for authorization...");
2508
+ await sleep(3e3);
2509
+ logger.updateSpinner("Authorization received!");
2510
+ await sleep(500);
2511
+ await configManager.setApiKey(DEMO_API_KEY);
2512
+ logger.updateSpinner("Fetching profile...");
2513
+ await sleep(600);
2514
+ logger.succeedSpinner(`Welcome, ${DEMO_USER.full_name}!`);
2515
+ logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2516
+ logger.newline();
2517
+ showQuickStart2();
2518
+ }
2519
+ async function demoLoginApiKey() {
2520
+ logger.info(`Get your API key from the dashboard`);
2521
+ logger.newline();
2522
+ const { apiKey } = await inquirer5.prompt([
2523
+ {
2524
+ type: "password",
2525
+ name: "apiKey",
2526
+ message: "API Key:",
2527
+ mask: "*",
2528
+ validate: (input) => {
2529
+ if (!input.startsWith("dw_sk_")) return 'Invalid API key format. Should start with "dw_sk_"';
2530
+ if (input.length < 20) return "API key seems too short";
2531
+ return true;
2532
+ }
2533
+ }
2534
+ ]);
2535
+ logger.startSpinner("Verifying API key...");
2536
+ await sleep(1e3);
2537
+ if (apiKey !== DEMO_API_KEY) {
2538
+ logger.failSpinner("Invalid API key");
2539
+ logger.error("API key not recognized");
2540
+ return;
2541
+ }
2542
+ await configManager.setApiKey(apiKey);
2543
+ logger.succeedSpinner(`Authenticated as ${DEMO_USER.full_name}!`);
2544
+ logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2545
+ logger.newline();
2546
+ showQuickStart2();
2547
+ }
2548
+ function showQuickStart2() {
2549
+ logger.box(
2550
+ [
2551
+ "Quick Start:",
2552
+ "",
2553
+ ' devwing "fix the auth bug" Ask anything',
2554
+ " devwing chat Interactive chat mode",
2555
+ " devwing scan Security scan",
2556
+ " devwing review Code review",
2557
+ " devwing explain file.js Explain code",
2558
+ "",
2559
+ "For more commands, run: devwing --help"
2560
+ ].join("\n"),
2561
+ { title: "Ready to go!", color: "green" }
2562
+ );
2563
+ }
2564
+ async function demoStatusCommand() {
2565
+ const apiKey = await configManager.getApiKey();
2566
+ if (!apiKey) {
2567
+ logger.warn("Not authenticated");
2568
+ logger.info('Run "devwing login" to get started');
2569
+ return;
2570
+ }
2571
+ logger.startSpinner("Fetching profile...");
2572
+ await sleep(800);
2573
+ logger.succeedSpinner("Authenticated");
2574
+ logger.newline();
2575
+ console.log(chalk7.bold("User Profile:"));
2576
+ console.log(` Email: ${DEMO_USER.email}`);
2577
+ console.log(` Name: ${DEMO_USER.full_name}`);
2578
+ console.log(` Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2579
+ console.log(` Verified: ${chalk7.green("Yes")}`);
2580
+ console.log(` 2FA: ${chalk7.dim("Disabled")}`);
2581
+ console.log(` Workspace: ${DEMO_WORKSPACE.name} (${DEMO_WORKSPACE.plan})`);
2582
+ console.log(` Project: ${DEMO_PROJECT.name}`);
2583
+ logger.newline();
2584
+ }
2585
+ async function demoLogoutCommand() {
2586
+ const apiKey = await configManager.getApiKey();
2587
+ if (!apiKey) {
2588
+ logger.info("You are not logged in");
2589
+ return;
2590
+ }
2591
+ const { confirm } = await inquirer5.prompt([
2592
+ {
2593
+ type: "confirm",
2594
+ name: "confirm",
2595
+ message: "Are you sure you want to log out?",
2596
+ default: false
2597
+ }
2598
+ ]);
2599
+ if (!confirm) {
2600
+ logger.info("Logout cancelled");
2601
+ return;
2602
+ }
2603
+ await configManager.deleteApiKey();
2604
+ const apiUrl = configManager.getApiUrl();
2605
+ configManager.clear();
2606
+ configManager.setApiUrl(apiUrl);
2607
+ logger.success("Successfully logged out");
2608
+ }
2609
+ async function demoMemoryCommand(action, content) {
2610
+ if (action === "show") {
2611
+ logger.startSpinner("Loading project memory...");
2612
+ await sleep(800);
2613
+ logger.stopSpinner();
2614
+ logger.success(`Found ${DEMO_MEMORIES.length} memory items`);
2615
+ logger.newline();
2616
+ const table = new Table({
2617
+ head: [chalk7.bold("Type"), chalk7.bold("Content"), chalk7.bold("Created")],
2618
+ colWidths: [15, 60, 15],
2619
+ wordWrap: true
2620
+ });
2621
+ for (const memory of DEMO_MEMORIES) {
2622
+ const preview = memory.content.length > 80 ? memory.content.substring(0, 80) + "..." : memory.content;
2623
+ table.push([
2624
+ chalk7.cyan(memory.type),
2625
+ preview,
2626
+ new Date(memory.created_at).toLocaleDateString()
2627
+ ]);
2628
+ }
2629
+ console.log(table.toString());
2630
+ } else if (action === "save") {
2631
+ if (!content) {
2632
+ logger.error("Please provide content to save");
2633
+ return;
2634
+ }
2635
+ const { type } = await inquirer5.prompt([
2636
+ {
2637
+ type: "list",
2638
+ name: "type",
2639
+ message: "What type of memory is this?",
2640
+ choices: [
2641
+ { name: "Context (general information)", value: "context" },
2642
+ { name: "Rule (coding standard or guideline)", value: "rule" },
2643
+ { name: "Decision (architectural decision)", value: "decision" },
2644
+ { name: "Summary (project summary)", value: "summary" }
2645
+ ]
2646
+ }
2647
+ ]);
2648
+ logger.startSpinner("Saving to project memory...");
2649
+ await sleep(1e3);
2650
+ logger.succeedSpinner(`Memory saved as ${type}`);
2651
+ } else if (action === "clear") {
2652
+ const { confirm } = await inquirer5.prompt([
2653
+ {
2654
+ type: "confirm",
2655
+ name: "confirm",
2656
+ message: "Are you sure you want to clear all project memory?",
2657
+ default: false
2658
+ }
2659
+ ]);
2660
+ if (!confirm) {
2661
+ logger.info("Operation cancelled");
2662
+ return;
2663
+ }
2664
+ logger.startSpinner("Clearing project memory...");
2665
+ await sleep(800);
2666
+ logger.succeedSpinner("Project memory cleared");
2667
+ }
2668
+ }
2669
+ async function demoExplainCommand(target) {
2670
+ await requireDemoAuth();
2671
+ await showContextLoading(24, "general");
2672
+ logger.startSpinner("DevWing is thinking...");
2673
+ await sleep(1500);
2674
+ logger.stopSpinner();
2675
+ console.log();
2676
+ await typeLine(chalk7.bold.magenta(`## Explaining: ${target}`));
2677
+ console.log();
2678
+ if (target.includes("auth") || target.includes("middleware")) {
2679
+ await typeLine(`The auth middleware at ${chalk7.cyan(target)} handles request authentication:`);
2680
+ console.log();
2681
+ await typeLine(" 1. Extracts the JWT token from the `Authorization: Bearer` header");
2682
+ await typeLine(" 2. Verifies the token signature using RS256 algorithm");
2683
+ await typeLine(" 3. Checks token expiration and issuer claims");
2684
+ await typeLine(" 4. Loads the user from the database using the `sub` claim");
2685
+ await typeLine(" 5. Attaches the user object to `req.user` for downstream handlers");
2686
+ console.log();
2687
+ await typeLine(chalk7.bold("Key patterns:"));
2688
+ await typeLine(` ${chalk7.green("\u2022")} Uses decorator pattern \u2014 ${chalk7.cyan("@authenticate")} on route handlers`);
2689
+ await typeLine(` ${chalk7.green("\u2022")} Implements token refresh via ${chalk7.cyan("X-Refresh-Token")} header`);
2690
+ await typeLine(` ${chalk7.green("\u2022")} Rate limited to prevent brute force (10 req/min per IP)`);
2691
+ console.log();
2692
+ await typeLine(chalk7.dim("This middleware is critical \u2014 it protects all /api/* routes."));
2693
+ } else {
2694
+ await typeLine(`${chalk7.cyan(target)} is a core component of the system.`);
2695
+ console.log();
2696
+ await typeLine(` ${chalk7.green("\u2022")} Responsible for handling ${target}-related logic`);
2697
+ await typeLine(` ${chalk7.green("\u2022")} Integrates with the main application pipeline`);
2698
+ await typeLine(` ${chalk7.green("\u2022")} Well-structured with clear separation of concerns`);
2699
+ }
2700
+ await showUsage(1200, 680, 45e-4, "general");
2701
+ logger.success("Done!");
2702
+ }
2703
+ async function demoReviewCommand() {
2704
+ await requireDemoAuth();
2705
+ logger.info("Performing code review...");
2706
+ logger.newline();
2707
+ await showContextLoading(38, "general");
2708
+ logger.startSpinner("DevWing is thinking...");
2709
+ await sleep(2e3);
2710
+ logger.stopSpinner();
2711
+ console.log();
2712
+ await typeLine(chalk7.bold.magenta("## Code Review \u2014 Recent Changes"));
2713
+ console.log();
2714
+ await typeLine(chalk7.bold("Files reviewed: 5 | Commits: 3 | Lines changed: +142 / -67"));
2715
+ console.log();
2716
+ await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 Error handling is consistent across all new endpoints");
2717
+ await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 Database queries use parameterized statements");
2718
+ await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 TypeScript strict mode is properly enforced");
2719
+ console.log();
2720
+ await typeLine(chalk7.yellow.bold(" \u25B2 SUGGESTION") + " \u2014 Consider extracting the validation logic at " + chalk7.cyan("user_service.ts:45"));
2721
+ await typeLine(chalk7.gray(" The email regex pattern is duplicated in 3 files. Move to a shared validator."));
2722
+ console.log();
2723
+ await typeLine(chalk7.yellow.bold(" \u25B2 SUGGESTION") + " \u2014 Missing error boundary in " + chalk7.cyan("api_handler.ts:78"));
2724
+ await typeLine(chalk7.gray(" The async handler doesn't catch unhandled promise rejections. Wrap in try/catch."));
2725
+ console.log();
2726
+ await typeLine(chalk7.red.bold(" \u25A0 ISSUE") + " \u2014 Potential memory leak in " + chalk7.cyan("websocket_manager.ts:120"));
2727
+ await typeLine(chalk7.gray(" Event listeners are added on each connection but never removed on disconnect."));
2728
+ console.log();
2729
+ await typeLine(chalk7.bold("Summary: ") + chalk7.green("3 good") + " | " + chalk7.yellow("2 suggestions") + " | " + chalk7.red("1 issue"));
2730
+ await showUsage(2800, 1200, 95e-4, "general");
2731
+ logger.success("Done!");
2732
+ }
2733
+ async function demoPromptCommand(prompt, options) {
2734
+ await requireDemoAuth();
2735
+ const promptLower = prompt.toLowerCase();
2736
+ if (promptLower.includes("refactor") && promptLower.includes("auth")) {
2737
+ await demoBackendRefactor();
2738
+ } else if (promptLower.includes("scan") || promptLower.includes("owasp") || promptLower.includes("vulnerabilit")) {
2739
+ await demoSecurityScan();
2740
+ } else if (promptLower.includes("explain")) {
2741
+ const target = prompt.replace(/explain\s*/i, "").trim() || "this code";
2742
+ await demoExplainCommand(target);
2743
+ } else if (promptLower.includes("review")) {
2744
+ await demoReviewCommand();
2745
+ } else if (promptLower.includes("deploy") || promptLower.includes("docker") || promptLower.includes("kubernetes")) {
2746
+ await demoDeployAssist();
2747
+ } else if (promptLower.includes("fix") || promptLower.includes("bug") || promptLower.includes("error")) {
2748
+ await demoBugFix();
2749
+ } else if (promptLower.includes("test") || promptLower.includes("spec")) {
2750
+ await demoWriteTests();
2751
+ } else {
2752
+ await demoGenericPrompt(prompt, options);
2753
+ }
2754
+ }
2755
+ async function demoChatCommand(options) {
2756
+ await requireDemoAuth();
2757
+ const readline2 = await import('readline');
2758
+ console.clear();
2759
+ logger.box([
2760
+ chalk7.bold.cyan("DevWing AI - Interactive Chat Mode"),
2761
+ "",
2762
+ "Chat with AI about your codebase. Type your message and press Enter.",
2763
+ "",
2764
+ chalk7.dim("Type /help for commands | /exit to quit")
2765
+ ].join("\n"), { title: "Welcome", color: "cyan" });
2766
+ logger.newline();
2767
+ logger.startSpinner("Loading codebase context...");
2768
+ await sleep(1200);
2769
+ logger.succeedSpinner("Context loaded \u2014 34 files");
2770
+ logger.newline();
2771
+ const rl = readline2.createInterface({
2772
+ input: process.stdin,
2773
+ output: process.stdout,
2774
+ prompt: chalk7.bold.green("You: ")
2775
+ });
2776
+ rl.on("SIGINT", () => {
2777
+ logger.newline();
2778
+ logger.info("Exiting chat mode...");
2779
+ rl.close();
2780
+ process.exit(0);
2781
+ });
2782
+ rl.prompt();
2783
+ rl.on("line", async (input) => {
2784
+ const msg = input.trim();
2785
+ if (!msg) {
2786
+ rl.prompt();
2787
+ return;
2788
+ }
2789
+ if (msg === "/exit" || msg === "/quit") {
2790
+ logger.info("Exiting chat mode...");
2791
+ rl.close();
2792
+ return;
2793
+ }
2794
+ if (msg === "/help") {
2795
+ logger.box([
2796
+ chalk7.bold("DevWing Chat Commands:"),
2797
+ "",
2798
+ chalk7.cyan("/help") + " \u2014 Show this message",
2799
+ chalk7.cyan("/exit") + " \u2014 Exit chat mode",
2800
+ chalk7.cyan("/scan") + " \u2014 Security scan",
2801
+ chalk7.cyan("/review") + " \u2014 Code review",
2802
+ chalk7.cyan("/context") + " \u2014 Show loaded context",
2803
+ chalk7.cyan("/memory show") + " \u2014 Show project memory"
2804
+ ].join("\n"), { title: "Commands", color: "blue" });
2805
+ logger.newline();
2806
+ rl.prompt();
2807
+ return;
2808
+ }
2809
+ if (msg === "/context") {
2810
+ logger.box([
2811
+ chalk7.bold("Current Context:"),
2812
+ "",
2813
+ `Working Directory: ${chalk7.cyan(process.cwd())}`,
2814
+ `Shell: zsh | OS: darwin`,
2815
+ `Files: 34 | Git Branch: main`,
2816
+ "",
2817
+ "Top files:",
2818
+ " \u2022 src/auth/auth_controller.ts",
2819
+ " \u2022 src/middleware/jwt.middleware.ts",
2820
+ " \u2022 src/services/user_service.ts",
2821
+ " \u2022 package.json",
2822
+ " ... and 30 more"
2823
+ ].join("\n"), { title: "Context", color: "cyan" });
2824
+ logger.newline();
2825
+ rl.prompt();
2826
+ return;
2827
+ }
2828
+ if (msg === "/memory show") {
2829
+ await demoMemoryCommand("show");
2830
+ logger.newline();
2831
+ rl.prompt();
2832
+ return;
2833
+ }
2834
+ if (msg === "/scan") {
2835
+ logger.newline();
2836
+ await demoSecurityScan();
2837
+ logger.newline();
2838
+ rl.prompt();
2839
+ return;
2840
+ }
2841
+ if (msg === "/review") {
2842
+ logger.newline();
2843
+ await demoReviewCommand();
2844
+ logger.newline();
2845
+ rl.prompt();
2846
+ return;
2847
+ }
2848
+ logger.newline();
2849
+ logger.info(chalk7.dim("DevWing is thinking..."));
2850
+ logger.newline();
2851
+ await demoPromptCommand(msg, options);
2852
+ logger.newline();
2853
+ rl.prompt();
2854
+ });
2855
+ rl.on("close", () => {
2856
+ logger.newline();
2857
+ logger.success("Chat session ended. Goodbye!");
2858
+ process.exit(0);
2859
+ });
2860
+ }
2861
+ async function demoBackendRefactor() {
2862
+ await showContextLoading(47, "backend");
2863
+ logger.startSpinner("DevWing is thinking...");
2864
+ await sleep(2e3);
2865
+ logger.stopSpinner();
2866
+ console.log();
2867
+ await typeLine(chalk7.bold.magenta("## Analysis"));
2868
+ console.log();
2869
+ await typeLine("I've analyzed the auth service across 6 files. Here's what I found:");
2870
+ console.log();
2871
+ await typeLine(` ${chalk7.red("\u2022")} Auth logic, validation, and DB queries are tightly coupled in ${chalk7.cyan("auth_controller.ts")}`);
2872
+ await typeLine(` ${chalk7.red("\u2022")} JWT validation is duplicated across 4 route handlers`);
2873
+ await typeLine(` ${chalk7.red("\u2022")} No rate limiting on login/register endpoints`);
2874
+ await typeLine(` ${chalk7.red("\u2022")} User lookup queries repeated in 3 places`);
2875
+ console.log();
2876
+ await sleep(PAUSE_MEDIUM);
2877
+ await typeLine(chalk7.bold.magenta("## Refactor Plan"));
2878
+ console.log();
2879
+ await typeLine("1. Extract JWT validation into reusable middleware");
2880
+ await typeLine("2. Create `UserRepository` with all DB operations");
2881
+ await typeLine("3. Add `RateLimiter` decorator using sliding window algorithm");
2882
+ await typeLine("4. Separate controller into thin route handlers");
2883
+ console.log();
2884
+ await sleep(PAUSE_MEDIUM);
2885
+ await typeLine(chalk7.bold.magenta("## Executing Refactor"));
2886
+ console.log();
2887
+ await showToolCall("read_file", "src/auth/auth_controller.ts");
2888
+ await showToolResult("Read 156 lines");
2889
+ await showToolCall("read_file", "src/auth/auth_routes.ts");
2890
+ await showToolResult("Read 89 lines");
2891
+ await showToolCall("read_file", "src/middleware/index.ts");
2892
+ await showToolResult("Read 23 lines");
2893
+ await showToolCall("write_file", "src/auth/repositories/user.repository.ts");
2894
+ await sleep(PAUSE_LONG);
2895
+ await showToolResult("Created \u2014 UserRepository with findByEmail, findById, create, updateLastLogin");
2896
+ await showToolCall("write_file", "src/middleware/jwt.middleware.ts");
2897
+ await sleep(PAUSE_MEDIUM);
2898
+ await showToolResult("Created \u2014 JWT validation middleware with token extraction and verification");
2899
+ await showToolCall("write_file", "src/middleware/rate-limiter.ts");
2900
+ await sleep(PAUSE_MEDIUM);
2901
+ await showToolResult("Created \u2014 Sliding window rate limiter: 10 req/min login, 5 req/min register");
2902
+ await showToolCall("write_file", "src/auth/auth_controller.ts");
2903
+ await sleep(PAUSE_LONG);
2904
+ await showToolResult("Refactored \u2014 156 lines \u2192 89 lines, clean separation of concerns");
2905
+ await showToolCall("run_command", "npm test -- --filter auth");
2906
+ await sleep(2e3);
2907
+ await showToolResult("All 12 tests passed");
2908
+ console.log();
2909
+ await sleep(PAUSE_MEDIUM);
2910
+ await typeLine(chalk7.bold.magenta("## Summary"));
2911
+ console.log();
2912
+ await typeLine(chalk7.green("\u2713") + " Extracted JWT validation to dedicated middleware");
2913
+ await typeLine(chalk7.green("\u2713") + " Created UserRepository \u2014 single source for all user DB operations");
2914
+ await typeLine(chalk7.green("\u2713") + " Added rate limiting: 10 req/min (login), 5 req/min (register)");
2915
+ await typeLine(chalk7.green("\u2713") + ` Reduced ${chalk7.red("156")} lines \u2192 ${chalk7.green("89")} lines (${chalk7.cyan("43% reduction")})`);
2916
+ await typeLine(chalk7.green("\u2713") + " All 12 existing tests pass \u2014 no regressions");
2917
+ console.log();
2918
+ await typeLine(chalk7.dim("Files modified: 4 | Files created: 3 | Lines removed: 67"));
2919
+ await showUsage(3420, 1890, 0.0127, "backend");
2920
+ logger.success("Done!");
2921
+ }
2922
+ async function demoSecurityScan() {
2923
+ await showContextLoading(32, "security");
2924
+ logger.startSpinner("DevWing is thinking...");
2925
+ await sleep(1800);
2926
+ logger.stopSpinner();
2927
+ console.log();
2928
+ await typeLine(chalk7.bold.magenta("## OWASP Security Scan \u2014 Auth Service"));
2929
+ console.log();
2930
+ await showToolCall("read_file", "src/auth/auth_controller.ts");
2931
+ await showToolResult("Read 89 lines");
2932
+ await showToolCall("read_file", "src/middleware/jwt.middleware.ts");
2933
+ await showToolResult("Read 34 lines");
2934
+ await showToolCall("read_file", "src/auth/repositories/user.repository.ts");
2935
+ await showToolResult("Read 52 lines");
2936
+ await sleep(PAUSE_LONG);
2937
+ await typeLine(chalk7.bold.red("## Vulnerabilities Found: 3"));
2938
+ console.log();
2939
+ await typeLine(chalk7.red.bold(" \u25A0 CRITICAL") + chalk7.red(" \u2014 Weak JWT Token Configuration"));
2940
+ await typeLine(chalk7.gray(" Location: src/middleware/jwt.middleware.ts:12"));
2941
+ await typeLine(chalk7.gray(" Issue: JWT tokens signed with HS256 and no expiration validation"));
2942
+ await typeLine(chalk7.gray(" OWASP: A2:2021 \u2014 Cryptographic Failures"));
2943
+ console.log();
2944
+ await typeLine(chalk7.yellow.bold(" \u25A0 HIGH") + chalk7.yellow(" \u2014 SQL Injection via User Lookup"));
2945
+ await typeLine(chalk7.gray(" Location: src/auth/repositories/user.repository.ts:18"));
2946
+ await typeLine(chalk7.gray(" Issue: Raw string interpolation in findByEmail query"));
2947
+ await typeLine(chalk7.gray(" OWASP: A3:2021 \u2014 Injection"));
2948
+ console.log();
2949
+ await typeLine(chalk7.yellow.bold(" \u25A0 MEDIUM") + chalk7.yellow(" \u2014 Missing Password Complexity Enforcement"));
2950
+ await typeLine(chalk7.gray(" Location: src/auth/auth_controller.ts:34"));
2951
+ await typeLine(chalk7.gray(" Issue: No minimum length, complexity, or breach database check"));
2952
+ await typeLine(chalk7.gray(" OWASP: A7:2021 \u2014 Identification and Authentication Failures"));
2953
+ console.log();
2954
+ await sleep(PAUSE_MEDIUM);
2955
+ await typeLine(chalk7.bold.magenta("## Applying Security Patches"));
2956
+ console.log();
2957
+ await showToolCall("write_file", "src/middleware/jwt.middleware.ts");
2958
+ await sleep(PAUSE_LONG);
2959
+ await showToolResult("Patched \u2014 Added RS256 signing, exp/iat validation, token rotation support");
2960
+ await showToolCall("write_file", "src/auth/repositories/user.repository.ts");
2961
+ await sleep(PAUSE_MEDIUM);
2962
+ await showToolResult("Patched \u2014 Parameterized queries, input sanitization added");
2963
+ await showToolCall("write_file", "src/auth/validators/password.validator.ts");
2964
+ await sleep(PAUSE_MEDIUM);
2965
+ await showToolResult("Created \u2014 Min 12 chars, uppercase, number, special char, breach check");
2966
+ await showToolCall("run_command", "npm test -- --filter auth");
2967
+ await sleep(1500);
2968
+ await showToolResult("All 15 tests passed (3 new security tests added)");
2969
+ console.log();
2970
+ await typeLine(chalk7.bold.magenta("## Scan Complete"));
2971
+ console.log();
2972
+ await typeLine(chalk7.green("\u2713") + " 3 vulnerabilities found and patched");
2973
+ await typeLine(chalk7.green("\u2713") + " JWT upgraded to RS256 with expiration enforcement");
2974
+ await typeLine(chalk7.green("\u2713") + " SQL injection eliminated via parameterized queries");
2975
+ await typeLine(chalk7.green("\u2713") + " Password policy enforced \u2014 NIST 800-63B compliant");
2976
+ await typeLine(chalk7.green("\u2713") + " 3 new security regression tests added");
2977
+ console.log();
2978
+ await typeLine(chalk7.dim("Severity breakdown: 1 critical, 1 high, 1 medium \u2014 all resolved"));
2979
+ await showUsage(2180, 1450, 89e-4, "security");
2980
+ logger.success("Done!");
2981
+ }
2982
+ async function demoDeployAssist(_prompt) {
2983
+ await showContextLoading(18, "devops");
2984
+ logger.startSpinner("DevWing is thinking...");
2985
+ await sleep(1800);
2986
+ logger.stopSpinner();
2987
+ console.log();
2988
+ await typeLine(chalk7.bold.magenta("## Deployment Analysis"));
2989
+ console.log();
2990
+ await showToolCall("read_file", "Dockerfile");
2991
+ await showToolResult("Read 28 lines");
2992
+ await showToolCall("read_file", "docker-compose.yml");
2993
+ await showToolResult("Read 45 lines");
2994
+ await showToolCall("read_file", ".github/workflows/deploy.yml");
2995
+ await showToolResult("Read 67 lines");
2996
+ await sleep(PAUSE_MEDIUM);
2997
+ await typeLine("Current deployment configuration:");
2998
+ console.log();
2999
+ await typeLine(` ${chalk7.green("\u2022")} Docker image: ${chalk7.cyan("node:20-alpine")} \u2014 multi-stage build`);
3000
+ await typeLine(` ${chalk7.green("\u2022")} Target: Railway (detected from ${chalk7.cyan("railway.toml")})`);
3001
+ await typeLine(` ${chalk7.green("\u2022")} Health check: ${chalk7.cyan("/health")} endpoint configured`);
3002
+ await typeLine(` ${chalk7.green("\u2022")} Environment: 12 vars configured, 3 secrets`);
3003
+ console.log();
3004
+ await typeLine(chalk7.bold.magenta("## Optimizations Applied"));
3005
+ console.log();
3006
+ await showToolCall("write_file", "Dockerfile");
3007
+ await sleep(PAUSE_LONG);
3008
+ await showToolResult("Optimized \u2014 Added layer caching, reduced image size from 340MB to 89MB");
3009
+ await showToolCall("write_file", "docker-compose.yml");
3010
+ await sleep(PAUSE_MEDIUM);
3011
+ await showToolResult("Added health checks, restart policies, resource limits");
3012
+ console.log();
3013
+ await typeLine(chalk7.green("\u2713") + " Docker image optimized: 340MB \u2192 89MB (74% smaller)");
3014
+ await typeLine(chalk7.green("\u2713") + " Added proper health checks and restart policies");
3015
+ await typeLine(chalk7.green("\u2713") + " Build time reduced: ~4min \u2192 ~1.5min with layer caching");
3016
+ await showUsage(1800, 950, 65e-4, "devops");
3017
+ logger.success("Done!");
3018
+ }
3019
+ async function demoBugFix(_prompt) {
3020
+ await showContextLoading(35, "backend");
3021
+ logger.startSpinner("DevWing is thinking...");
3022
+ await sleep(1800);
3023
+ logger.stopSpinner();
3024
+ console.log();
3025
+ await typeLine(chalk7.bold.magenta("## Bug Analysis"));
3026
+ console.log();
3027
+ await showToolCall("read_file", "src/services/user_service.ts");
3028
+ await showToolResult("Read 94 lines");
3029
+ await showToolCall("git_diff", "");
3030
+ await showToolResult("3 files changed, 12 insertions, 4 deletions");
3031
+ await showToolCall("git_log", "last 5 commits");
3032
+ await showToolResult("Loaded recent history");
3033
+ await sleep(PAUSE_MEDIUM);
3034
+ await typeLine("I found the issue. The bug is in " + chalk7.cyan("user_service.ts:67") + ":");
3035
+ console.log();
3036
+ await typeLine(chalk7.red(" - const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);"));
3037
+ await typeLine(chalk7.green(" + const user = await db.query(`SELECT * FROM users WHERE id = $1`, [userId]);"));
3038
+ console.log();
3039
+ await typeLine("The user ID was being interpolated directly into the SQL query instead of");
3040
+ await typeLine("using parameterized binding. This causes:");
3041
+ await typeLine(" 1. SQL injection vulnerability");
3042
+ await typeLine(" 2. Query failure when ID contains special characters");
3043
+ console.log();
3044
+ await showToolCall("write_file", "src/services/user_service.ts");
3045
+ await sleep(PAUSE_LONG);
3046
+ await showToolResult("Fixed \u2014 Parameterized query binding");
3047
+ await showToolCall("run_command", "npm test -- --filter user");
3048
+ await sleep(1500);
3049
+ await showToolResult("All 8 tests passed");
3050
+ console.log();
3051
+ await typeLine(chalk7.green("\u2713") + " Bug fixed \u2014 parameterized SQL query");
3052
+ await typeLine(chalk7.green("\u2713") + " SQL injection vulnerability closed");
3053
+ await typeLine(chalk7.green("\u2713") + " All tests pass");
3054
+ await showUsage(1650, 890, 62e-4, "backend");
3055
+ logger.success("Done!");
3056
+ }
3057
+ async function demoWriteTests(_prompt) {
3058
+ await showContextLoading(22, "backend");
3059
+ logger.startSpinner("DevWing is thinking...");
3060
+ await sleep(1800);
3061
+ logger.stopSpinner();
3062
+ console.log();
3063
+ await typeLine(chalk7.bold.magenta("## Generating Tests"));
3064
+ console.log();
3065
+ await showToolCall("read_file", "src/auth/auth_controller.ts");
3066
+ await showToolResult("Read 89 lines \u2014 4 functions to test");
3067
+ await showToolCall("read_file", "src/auth/repositories/user.repository.ts");
3068
+ await showToolResult("Read 52 lines \u2014 5 methods to test");
3069
+ await sleep(PAUSE_MEDIUM);
3070
+ await showToolCall("write_file", "tests/auth/auth_controller.test.ts");
3071
+ await sleep(PAUSE_LONG);
3072
+ await showToolResult("Created \u2014 8 test cases: login, register, refresh, logout, edge cases");
3073
+ await showToolCall("write_file", "tests/auth/user_repository.test.ts");
3074
+ await sleep(PAUSE_LONG);
3075
+ await showToolResult("Created \u2014 10 test cases: CRUD operations, error handling, constraints");
3076
+ await showToolCall("run_command", "npm test -- --filter auth");
3077
+ await sleep(2e3);
3078
+ await showToolResult("18 / 18 tests passed");
3079
+ console.log();
3080
+ await typeLine(chalk7.bold.magenta("## Test Coverage"));
3081
+ console.log();
3082
+ await typeLine(" auth_controller.ts: " + chalk7.green("94%") + " (was 0%)");
3083
+ await typeLine(" user.repository.ts: " + chalk7.green("98%") + " (was 0%)");
3084
+ await typeLine(" Overall auth module: " + chalk7.green("96%"));
3085
+ console.log();
3086
+ await typeLine(chalk7.green("\u2713") + " 18 tests generated and passing");
3087
+ await typeLine(chalk7.green("\u2713") + " Covers happy paths, edge cases, and error states");
3088
+ await showUsage(2400, 1680, 98e-4, "backend");
3089
+ logger.success("Done!");
3090
+ }
3091
+ async function demoGenericPrompt(prompt, options) {
3092
+ const mode = options.mode || "general";
3093
+ await showContextLoading(28, mode);
3094
+ logger.startSpinner("DevWing is thinking...");
3095
+ await sleep(1500);
3096
+ logger.stopSpinner();
3097
+ console.log();
3098
+ await typeLine(chalk7.bold.magenta("## Response"));
3099
+ console.log();
3100
+ await typeLine(`Based on your codebase analysis, here's my approach to: "${prompt}"`);
3101
+ console.log();
3102
+ await typeLine(` ${chalk7.green("\u2022")} Analyzed project structure and dependencies`);
3103
+ await typeLine(` ${chalk7.green("\u2022")} Reviewed relevant files based on your prompt`);
3104
+ await typeLine(` ${chalk7.green("\u2022")} Generated solution aligned with existing patterns`);
3105
+ console.log();
3106
+ await showUsage(1100, 620, 41e-4, mode);
3107
+ logger.success("Done!");
3108
+ }
3109
+ async function showToolCall(tool, args) {
3110
+ await sleep(PAUSE_SHORT);
3111
+ console.log();
3112
+ console.log(chalk7.yellow(` \u25B6 ${chalk7.bold(tool)}`));
3113
+ if (args) console.log(chalk7.gray(` ${args}`));
3114
+ await sleep(PAUSE_MEDIUM);
3115
+ }
3116
+ async function showToolResult(result) {
3117
+ console.log(chalk7.green(` \u2713 ${result}`));
3118
+ await sleep(PAUSE_SHORT);
3119
+ }
3120
+ async function showContextLoading(fileCount, mode) {
3121
+ logger.startSpinner("Analyzing codebase...");
3122
+ await sleep(1200);
3123
+ logger.updateSpinner(`Scanning ${fileCount} files...`);
3124
+ await sleep(800);
3125
+ logger.updateSpinner("Building dependency graph...");
3126
+ await sleep(600);
3127
+ logger.updateSpinner("Collecting git context...");
3128
+ await sleep(500);
3129
+ logger.succeedSpinner(`Context loaded \u2014 ${fileCount} files, mode: ${chalk7.cyan(mode)}`);
3130
+ console.log();
3131
+ }
3132
+ async function showUsage(tokensIn, tokensOut, cost, mode) {
3133
+ const modelName = mode === "security" ? "devwing-security-1" : mode === "devops" ? "devwing-devops-1" : mode === "frontend" ? "devwing-frontend-1" : "devwing-backend-1";
3134
+ console.log();
3135
+ console.log(
3136
+ chalk7.gray(
3137
+ `\u{1F4CA} Tokens: ${tokensIn.toLocaleString()} in, ${tokensOut.toLocaleString()} out | Cost: $${cost.toFixed(4)} | Model: ${modelName}`
3138
+ )
3139
+ );
3140
+ console.log();
3141
+ }
3142
+ async function requireDemoAuth() {
3143
+ const apiKey = await configManager.getApiKey();
3144
+ if (!apiKey) {
3145
+ logger.error('Not authenticated. Run "devwing login" first.');
3146
+ process.exit(1);
3147
+ }
3148
+ }
3149
+ function isDemoMode() {
3150
+ return process.env.DEVWING_DEMO === "1" || process.env.DEVWING_DEMO === "true";
3151
+ }
2283
3152
  var program = new Command();
2284
- var VERSION = "0.1.4";
3153
+ var __cliFilename = fileURLToPath(import.meta.url);
3154
+ var __cliDirname = dirname(__cliFilename);
3155
+ function getVersion() {
3156
+ const paths = [
3157
+ join(__cliDirname, "../package.json"),
3158
+ join(__cliDirname, "../../package.json")
3159
+ ];
3160
+ for (const p of paths) {
3161
+ try {
3162
+ return JSON.parse(readFileSync(p, "utf-8")).version;
3163
+ } catch {
3164
+ continue;
3165
+ }
3166
+ }
3167
+ return "0.1.6";
3168
+ }
3169
+ var VERSION = getVersion();
2285
3170
  program.name("devwing").description("DevWing.ai - Your AI Wingman in the Terminal").version(VERSION, "-v, --version", "Display version number").helpOption("-h, --help", "Display help information");
2286
3171
  program.option("--mode <mode>", "AI mode: general|frontend|backend|security|devops").option("--model <model>", "Specific model to use").option("--project <id>", "Project ID").option("--workspace <id>", "Workspace ID").option("--verbose", "Verbose output for debugging").option("-y, --yes", "Auto-confirm destructive actions");
2287
3172
  program.command("login").description("Authenticate with DevWing").action(async () => {
2288
- await loginCommand();
3173
+ if (isDemoMode()) {
3174
+ await demoLoginCommand();
3175
+ } else {
3176
+ await loginCommand();
3177
+ }
2289
3178
  });
2290
3179
  program.command("logout").description("Clear stored credentials").action(async () => {
2291
- await logoutCommand();
3180
+ if (isDemoMode()) {
3181
+ await demoLogoutCommand();
3182
+ } else {
3183
+ await logoutCommand();
3184
+ }
2292
3185
  });
2293
3186
  program.command("status").description("Show authentication status and profile").action(async () => {
2294
- await statusCommand();
3187
+ if (isDemoMode()) {
3188
+ await demoStatusCommand();
3189
+ } else {
3190
+ await statusCommand();
3191
+ }
2295
3192
  });
2296
3193
  program.command("chat").description("Start interactive chat mode (like Claude Code)").action(async () => {
2297
3194
  const opts = program.opts();
2298
- await chatCommand(opts);
3195
+ if (isDemoMode()) {
3196
+ await demoChatCommand(opts);
3197
+ } else {
3198
+ await chatCommand(opts);
3199
+ }
2299
3200
  });
2300
3201
  program.command("scan").description("Run security vulnerability scan").action(async () => {
2301
3202
  const opts = program.opts();
2302
- await scanCommand(opts);
3203
+ if (isDemoMode()) {
3204
+ await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...opts, mode: "security" });
3205
+ } else {
3206
+ await scanCommand(opts);
3207
+ }
2303
3208
  });
2304
3209
  program.command("review").description("Perform code review on recent changes").action(async () => {
2305
3210
  const opts = program.opts();
2306
- await reviewCommand(opts);
3211
+ if (isDemoMode()) {
3212
+ await demoReviewCommand();
3213
+ } else {
3214
+ await reviewCommand(opts);
3215
+ }
2307
3216
  });
2308
3217
  program.command("explain <target>").description("Explain code, file, or concept").action(async (target) => {
2309
3218
  const opts = program.opts();
2310
- await explainCommand(target, opts);
3219
+ if (isDemoMode()) {
3220
+ await demoExplainCommand(target);
3221
+ } else {
3222
+ await explainCommand(target, opts);
3223
+ }
2311
3224
  });
2312
3225
  var memoryCmd = program.command("memory").description("Manage project memory");
2313
3226
  memoryCmd.command("save <content>").description("Save information to project memory").action(async (content) => {
2314
3227
  const opts = program.opts();
2315
- await memoryCommand("save", content, opts);
3228
+ if (isDemoMode()) {
3229
+ await demoMemoryCommand("save", content);
3230
+ } else {
3231
+ await memoryCommand("save", content, opts);
3232
+ }
2316
3233
  });
2317
3234
  memoryCmd.command("show").description("Show all project memories").action(async () => {
2318
3235
  const opts = program.opts();
2319
- await memoryCommand("show", void 0, opts);
3236
+ if (isDemoMode()) {
3237
+ await demoMemoryCommand("show");
3238
+ } else {
3239
+ await memoryCommand("show", void 0, opts);
3240
+ }
2320
3241
  });
2321
3242
  memoryCmd.command("clear").description("Clear all project memories").action(async () => {
2322
3243
  const opts = program.opts();
2323
- await memoryCommand("clear", void 0, opts);
3244
+ if (isDemoMode()) {
3245
+ await demoMemoryCommand("clear");
3246
+ } else {
3247
+ await memoryCommand("clear", void 0, opts);
3248
+ }
2324
3249
  });
2325
3250
  var configCmd = program.command("config").description("Manage CLI configuration");
2326
3251
  configCmd.command("list").description("Show all configuration").action(async () => {
@@ -2338,20 +3263,30 @@ program.command("update").description("Check for and install CLI updates").optio
2338
3263
  program.argument("[prompt...]", "Natural language prompt for AI (optional - starts chat mode if empty)").action(async (promptParts) => {
2339
3264
  const opts = program.opts();
2340
3265
  if (promptParts.length === 0) {
2341
- await chatCommand(opts);
3266
+ if (isDemoMode()) {
3267
+ await demoChatCommand(opts);
3268
+ } else {
3269
+ await chatCommand(opts);
3270
+ }
2342
3271
  return;
2343
3272
  }
2344
3273
  const prompt = promptParts.join(" ");
2345
- await promptCommand(prompt, opts);
3274
+ if (isDemoMode()) {
3275
+ await demoPromptCommand(prompt, opts);
3276
+ } else {
3277
+ await promptCommand(prompt, opts);
3278
+ }
2346
3279
  });
2347
3280
  program.exitOverride();
2348
3281
  try {
2349
3282
  await program.parseAsync(process.argv);
2350
3283
  } catch (error) {
2351
- if (error.code === "commander.help" || error.code === "commander.version") {
3284
+ if (error.code === "commander.help" || error.code === "commander.helpDisplayed" || error.code === "commander.version") {
2352
3285
  process.exit(0);
2353
3286
  }
2354
- logger.error("An error occurred", error);
3287
+ if (error.message && !error.message.includes("process.exit")) {
3288
+ logger.error(error.message);
3289
+ }
2355
3290
  if (process.env.DEBUG) {
2356
3291
  console.error(error);
2357
3292
  }