devwing 0.1.7 → 0.1.9

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';
@@ -19,10 +19,10 @@ import { marked } from 'marked';
19
19
  import TerminalRenderer from 'marked-terminal';
20
20
  import fs2 from 'fs/promises';
21
21
  import util from 'util';
22
- import readline from 'readline';
23
- import Table from 'cli-table3';
22
+ import Table3 from 'cli-table3';
24
23
  import { fileURLToPath } from 'url';
25
24
  import semver from 'semver';
25
+ import readline from 'readline';
26
26
 
27
27
  var SERVICE_NAME = "DevWing CLI";
28
28
  var API_KEY_ACCOUNT = "api-key";
@@ -401,37 +401,37 @@ var Logger = class {
401
401
  * Success message
402
402
  */
403
403
  success(message) {
404
- console.log(chalk3.green("\u2713"), message);
404
+ console.log(chalk7.green("\u2713"), message);
405
405
  }
406
406
  /**
407
407
  * Error message
408
408
  */
409
409
  error(message, error) {
410
- console.log(chalk3.red("\u2717"), message);
410
+ console.log(chalk7.red("\u2717"), message);
411
411
  if (error && process.env.DEBUG) {
412
- console.error(chalk3.gray(error.stack || error));
412
+ console.error(chalk7.gray(error.stack || error));
413
413
  }
414
414
  }
415
415
  /**
416
416
  * Warning message
417
417
  */
418
418
  warn(message) {
419
- console.log(chalk3.yellow("\u26A0"), message);
419
+ console.log(chalk7.yellow("\u26A0"), message);
420
420
  }
421
421
  /**
422
422
  * Info message
423
423
  */
424
424
  info(message) {
425
- console.log(chalk3.blue("\u2139"), message);
425
+ console.log(chalk7.blue("\u2139"), message);
426
426
  }
427
427
  /**
428
428
  * Debug message (only shown if DEBUG env var is set)
429
429
  */
430
430
  debug(message, data) {
431
431
  if (process.env.DEBUG) {
432
- console.log(chalk3.gray("\u2699"), chalk3.gray(message));
432
+ console.log(chalk7.gray("\u2699"), chalk7.gray(message));
433
433
  if (data) {
434
- console.log(chalk3.gray(JSON.stringify(data, null, 2)));
434
+ console.log(chalk7.gray(JSON.stringify(data, null, 2)));
435
435
  }
436
436
  }
437
437
  }
@@ -530,7 +530,7 @@ async function loginCommand() {
530
530
  try {
531
531
  const existingKey = await configManager.getApiKey();
532
532
  if (existingKey) {
533
- const { overwrite } = await inquirer.prompt([
533
+ const { overwrite } = await inquirer5.prompt([
534
534
  {
535
535
  type: "confirm",
536
536
  name: "overwrite",
@@ -545,7 +545,7 @@ async function loginCommand() {
545
545
  }
546
546
  logger.printBanner();
547
547
  logger.newline();
548
- const { method } = await inquirer.prompt([
548
+ const { method } = await inquirer5.prompt([
549
549
  {
550
550
  type: "list",
551
551
  name: "method",
@@ -573,7 +573,7 @@ async function loginCommand() {
573
573
  }
574
574
  }
575
575
  async function loginWithEmail() {
576
- const { email, password } = await inquirer.prompt([
576
+ const { email, password } = await inquirer5.prompt([
577
577
  {
578
578
  type: "input",
579
579
  name: "email",
@@ -604,7 +604,7 @@ async function loginWithEmail() {
604
604
  if (loginResult.requires_2fa) {
605
605
  logger.succeedSpinner("Credentials verified");
606
606
  logger.info("Two-factor authentication is enabled on your account");
607
- const { totpCode } = await inquirer.prompt([
607
+ const { totpCode } = await inquirer5.prompt([
608
608
  {
609
609
  type: "input",
610
610
  name: "totpCode",
@@ -657,10 +657,10 @@ async function loginWithBrowser() {
657
657
  logger.success("Browser opened! Please log in to authorize this device.");
658
658
  } catch {
659
659
  logger.warn("Could not open browser automatically.");
660
- logger.info(`Please visit: ${chalk3.cyan(verifyUrl)}`);
660
+ logger.info(`Please visit: ${chalk7.cyan(verifyUrl)}`);
661
661
  }
662
662
  logger.newline();
663
- logger.info(chalk3.dim(`Device code: ${initResponse.user_code.substring(0, 8)}...`));
663
+ logger.info(chalk7.dim(`Device code: ${initResponse.user_code.substring(0, 8)}...`));
664
664
  logger.startSpinner("Waiting for authorization...");
665
665
  const pollInterval = (initResponse.interval || 5) * 1e3;
666
666
  const maxAttempts = Math.floor((initResponse.expires_in || 900) / (initResponse.interval || 5));
@@ -719,7 +719,7 @@ async function loginWithApiKey() {
719
719
  );
720
720
  logger.info(`Get your API key from the ${dashboardUrl}`);
721
721
  logger.newline();
722
- const { apiKey } = await inquirer.prompt([
722
+ const { apiKey } = await inquirer5.prompt([
723
723
  {
724
724
  type: "password",
725
725
  name: "apiKey",
@@ -759,7 +759,7 @@ async function logoutCommand() {
759
759
  logger.info("You are not logged in");
760
760
  return;
761
761
  }
762
- const { confirm } = await inquirer.prompt([
762
+ const { confirm } = await inquirer5.prompt([
763
763
  {
764
764
  type: "confirm",
765
765
  name: "confirm",
@@ -796,15 +796,15 @@ async function statusCommand() {
796
796
  const plan = getUserPlan(user);
797
797
  logger.succeedSpinner("Authenticated");
798
798
  logger.newline();
799
- console.log(chalk3.bold("User Profile:"));
799
+ console.log(chalk7.bold("User Profile:"));
800
800
  console.log(` Email: ${user.email}`);
801
801
  console.log(` Name: ${getUserDisplayName(user)}`);
802
802
  console.log(` Plan: ${plan.toUpperCase()}`);
803
803
  if (user.is_verified !== void 0) {
804
- console.log(` Verified: ${user.is_verified ? chalk3.green("Yes") : chalk3.yellow("No")}`);
804
+ console.log(` Verified: ${user.is_verified ? chalk7.green("Yes") : chalk7.yellow("No")}`);
805
805
  }
806
806
  if (user.totp_enabled !== void 0) {
807
- console.log(` 2FA: ${user.totp_enabled ? chalk3.green("Enabled") : chalk3.dim("Disabled")}`);
807
+ console.log(` 2FA: ${user.totp_enabled ? chalk7.green("Enabled") : chalk7.dim("Disabled")}`);
808
808
  }
809
809
  const config = configManager.getAll();
810
810
  if (config.workspaceId) {
@@ -1399,22 +1399,22 @@ Summary: ${result.summary.changes} changes, ${result.summary.insertions} inserti
1399
1399
  // src/streaming/renderer.ts
1400
1400
  marked.setOptions({
1401
1401
  renderer: new TerminalRenderer({
1402
- code: chalk3.cyan,
1403
- blockquote: chalk3.gray.italic,
1404
- html: chalk3.gray,
1405
- heading: chalk3.bold.underline,
1406
- firstHeading: chalk3.bold.magenta,
1407
- hr: chalk3.reset,
1408
- listitem: chalk3.reset,
1409
- list: chalk3.reset,
1410
- table: chalk3.reset,
1411
- paragraph: chalk3.reset,
1412
- strong: chalk3.bold,
1413
- em: chalk3.italic,
1414
- codespan: chalk3.cyan,
1415
- del: chalk3.dim.strikethrough,
1416
- link: chalk3.blue.underline,
1417
- 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
1418
1418
  })
1419
1419
  });
1420
1420
  var StreamingRenderer = class {
@@ -1509,10 +1509,10 @@ var StreamingRenderer = class {
1509
1509
  logger.newline();
1510
1510
  const { tool, args } = message;
1511
1511
  if (!tool) return;
1512
- logger.info(chalk3.yellow(`AI requested tool: ${chalk3.bold(tool)}`));
1512
+ logger.info(chalk7.yellow(`AI requested tool: ${chalk7.bold(tool)}`));
1513
1513
  if (args && Object.keys(args).length > 0) {
1514
- console.log(chalk3.gray("Arguments:"));
1515
- 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)));
1516
1516
  }
1517
1517
  logger.newline();
1518
1518
  }
@@ -1524,11 +1524,11 @@ var StreamingRenderer = class {
1524
1524
  const results = [];
1525
1525
  for (const { tool, args } of this.pendingToolCalls) {
1526
1526
  logger.newline();
1527
- logger.info(chalk3.yellow(`Executing ${chalk3.bold(tool)}...`));
1527
+ logger.info(chalk7.yellow(`Executing ${chalk7.bold(tool)}...`));
1528
1528
  const requiresConfirmation = this.requiresConfirmation(tool);
1529
1529
  let confirmed = true;
1530
1530
  if (requiresConfirmation) {
1531
- const answers = await inquirer.prompt([
1531
+ const answers = await inquirer5.prompt([
1532
1532
  {
1533
1533
  type: "confirm",
1534
1534
  name: "confirmed",
@@ -1557,9 +1557,9 @@ var StreamingRenderer = class {
1557
1557
  const content = result.output || "";
1558
1558
  const preview = content.substring(0, 500);
1559
1559
  if (preview.length < content.length) {
1560
- console.log(chalk3.gray(preview + "... (truncated)"));
1560
+ console.log(chalk7.gray(preview + "... (truncated)"));
1561
1561
  } else {
1562
- console.log(chalk3.gray(preview));
1562
+ console.log(chalk7.gray(preview));
1563
1563
  }
1564
1564
  results.push({
1565
1565
  tool,
@@ -1597,9 +1597,9 @@ var StreamingRenderer = class {
1597
1597
  if (message.content) {
1598
1598
  const preview = message.content.substring(0, 500);
1599
1599
  if (preview.length < message.content.length) {
1600
- console.log(chalk3.gray(preview + "... (truncated)"));
1600
+ console.log(chalk7.gray(preview + "... (truncated)"));
1601
1601
  } else {
1602
- console.log(chalk3.gray(preview));
1602
+ console.log(chalk7.gray(preview));
1603
1603
  }
1604
1604
  }
1605
1605
  logger.newline();
@@ -1613,7 +1613,7 @@ var StreamingRenderer = class {
1613
1613
  if (message.usage) {
1614
1614
  const { tokens_in, tokens_out, cost_usd } = message.usage;
1615
1615
  console.log(
1616
- chalk3.gray(
1616
+ chalk7.gray(
1617
1617
  `\u{1F4CA} Tokens: ${tokens_in.toLocaleString()} in, ${tokens_out.toLocaleString()} out | Cost: $${cost_usd.toFixed(4)}`
1618
1618
  )
1619
1619
  );
@@ -1791,7 +1791,7 @@ async function memorySave(projectId, content) {
1791
1791
  logger.error("Please provide content to save");
1792
1792
  return;
1793
1793
  }
1794
- const { type } = await inquirer.prompt([
1794
+ const { type } = await inquirer5.prompt([
1795
1795
  {
1796
1796
  type: "list",
1797
1797
  name: "type",
@@ -1824,7 +1824,7 @@ async function memoryShow(projectId) {
1824
1824
  }
1825
1825
  logger.success(`Found ${memories.length} memory items`);
1826
1826
  logger.newline();
1827
- const table = new Table({
1827
+ const table = new Table3({
1828
1828
  head: ["Type", "Content", "Created"],
1829
1829
  colWidths: [15, 60, 20],
1830
1830
  wordWrap: true
@@ -1832,7 +1832,7 @@ async function memoryShow(projectId) {
1832
1832
  for (const memory of memories) {
1833
1833
  const preview = memory.content.length > 100 ? memory.content.substring(0, 100) + "..." : memory.content;
1834
1834
  table.push([
1835
- chalk3.cyan(memory.type),
1835
+ chalk7.cyan(memory.type),
1836
1836
  preview,
1837
1837
  new Date(memory.created_at).toLocaleDateString()
1838
1838
  ]);
@@ -1844,7 +1844,7 @@ async function memoryShow(projectId) {
1844
1844
  }
1845
1845
  }
1846
1846
  async function memoryClear(projectId) {
1847
- const { confirm } = await inquirer.prompt([
1847
+ const { confirm } = await inquirer5.prompt([
1848
1848
  {
1849
1849
  type: "confirm",
1850
1850
  name: "confirm",
@@ -1873,7 +1873,7 @@ async function configCommand(action, key, value) {
1873
1873
  if (!action || action === "list") {
1874
1874
  const config = configManager.getAll();
1875
1875
  const apiKey = await configManager.getApiKey();
1876
- console.log(chalk3.bold("Current Configuration:"));
1876
+ console.log(chalk7.bold("Current Configuration:"));
1877
1877
  console.log(` API URL: ${config.apiUrl}`);
1878
1878
  console.log(` API Key: ${apiKey ? apiKey.substring(0, 12) + "..." : "Not set"}`);
1879
1879
  console.log(` Workspace: ${config.workspaceId || "Not set"}`);
@@ -1881,7 +1881,7 @@ async function configCommand(action, key, value) {
1881
1881
  console.log(` Model: ${config.model || "Auto"}`);
1882
1882
  console.log(` Mode: ${config.mode || "Auto"}`);
1883
1883
  console.log();
1884
- console.log(chalk3.gray(`Config file: ${configManager.getPath()}`));
1884
+ console.log(chalk7.gray(`Config file: ${configManager.getPath()}`));
1885
1885
  return;
1886
1886
  }
1887
1887
  if (action === "get") {
@@ -1934,290 +1934,6 @@ async function configCommand(action, key, value) {
1934
1934
  process.exit(1);
1935
1935
  }
1936
1936
  }
1937
-
1938
- // src/commands/chat.ts
1939
- var ChatSession = class {
1940
- conversationHistory = [];
1941
- sessionContext = null;
1942
- sessionId = null;
1943
- addMessage(role, content) {
1944
- this.conversationHistory.push({ role, content });
1945
- }
1946
- getHistory() {
1947
- return this.conversationHistory;
1948
- }
1949
- setContext(context) {
1950
- this.sessionContext = context;
1951
- }
1952
- getContext() {
1953
- return this.sessionContext;
1954
- }
1955
- setSessionId(id) {
1956
- this.sessionId = id;
1957
- }
1958
- getSessionId() {
1959
- return this.sessionId;
1960
- }
1961
- clear() {
1962
- this.conversationHistory = [];
1963
- this.sessionId = null;
1964
- }
1965
- getConversationSummary() {
1966
- return this.conversationHistory.map((msg) => `${msg.role === "user" ? "You" : "DevWing"}: ${msg.content.substring(0, 100)}...`).join("\n");
1967
- }
1968
- };
1969
- async function handleSpecialCommand(command, session, options) {
1970
- const cmd = command.toLowerCase().trim();
1971
- const parts = command.split(" ");
1972
- const mainCmd = parts[0].toLowerCase();
1973
- if (cmd === "/help") {
1974
- logger.box([
1975
- chalk3.bold("DevWing Chat Commands:"),
1976
- "",
1977
- chalk3.bold.yellow("Navigation & Session:"),
1978
- chalk3.cyan("/help") + " - Show this help message",
1979
- chalk3.cyan("/exit, /quit") + " - Exit chat mode",
1980
- chalk3.cyan("/clear") + " - Clear conversation history",
1981
- chalk3.cyan("/context") + " - Show current context",
1982
- chalk3.cyan("/history") + " - Show conversation history",
1983
- "",
1984
- chalk3.bold.yellow("Specialized Commands:"),
1985
- chalk3.cyan("/scan") + " - Run security vulnerability scan",
1986
- chalk3.cyan("/review") + " - Perform code review",
1987
- chalk3.cyan("/explain <target>") + " - Explain code, file, or concept",
1988
- "",
1989
- chalk3.bold.yellow("Project Memory:"),
1990
- chalk3.cyan("/memory save <content>") + " - Save note to project memory",
1991
- chalk3.cyan("/memory show") + " - Show all project memories",
1992
- chalk3.cyan("/memory clear") + " - Clear all project memories",
1993
- "",
1994
- chalk3.dim("Just type your message to chat with DevWing!")
1995
- ].join("\n"), { title: "Chat Commands", color: "blue" });
1996
- return true;
1997
- }
1998
- if (cmd === "/exit" || cmd === "/quit") {
1999
- logger.info("Exiting chat mode...");
2000
- return false;
2001
- }
2002
- if (cmd === "/clear") {
2003
- session.clear();
2004
- console.clear();
2005
- logger.success("Conversation cleared!");
2006
- printWelcomeBanner();
2007
- return true;
2008
- }
2009
- if (cmd === "/context") {
2010
- const context = session.getContext();
2011
- if (!context) {
2012
- logger.warn("No context loaded yet. Send a message to load context.");
2013
- return true;
2014
- }
2015
- logger.box([
2016
- chalk3.bold("Current Context:"),
2017
- "",
2018
- `Working Directory: ${chalk3.cyan(context.cwd)}`,
2019
- `Shell: ${context.shell}`,
2020
- `OS: ${context.os}`,
2021
- `Files: ${context.files.length}`,
2022
- context.git_branch ? `Git Branch: ${context.git_branch}` : "",
2023
- "",
2024
- "Files included:",
2025
- ...context.files.slice(0, 10).map((f) => ` \u2022 ${f.path}`),
2026
- context.files.length > 10 ? ` ... and ${context.files.length - 10} more` : ""
2027
- ].filter(Boolean).join("\n"), { title: "Context Info", color: "cyan" });
2028
- return true;
2029
- }
2030
- if (cmd === "/history") {
2031
- const history = session.getHistory();
2032
- if (history.length === 0) {
2033
- logger.info("No conversation history yet.");
2034
- return true;
2035
- }
2036
- logger.box([
2037
- chalk3.bold("Conversation History:"),
2038
- "",
2039
- session.getConversationSummary()
2040
- ].join("\n"), { title: "History", color: "magenta" });
2041
- return true;
2042
- }
2043
- if (cmd === "/scan") {
2044
- logger.newline();
2045
- await scanCommand(options);
2046
- return true;
2047
- }
2048
- if (cmd === "/review") {
2049
- logger.newline();
2050
- await reviewCommand(options);
2051
- return true;
2052
- }
2053
- if (mainCmd === "/explain") {
2054
- const target = parts.slice(1).join(" ").trim();
2055
- if (!target) {
2056
- logger.error("Please provide something to explain. Example: /explain src/auth.ts");
2057
- return true;
2058
- }
2059
- logger.newline();
2060
- await explainCommand(target, options);
2061
- return true;
2062
- }
2063
- if (mainCmd === "/memory") {
2064
- const subCmd = parts[1]?.toLowerCase();
2065
- if (subCmd === "save") {
2066
- const content = parts.slice(2).join(" ").trim();
2067
- if (!content) {
2068
- logger.error("Please provide content to save. Example: /memory save Auth uses JWT");
2069
- return true;
2070
- }
2071
- logger.newline();
2072
- await memoryCommand("save", content, options);
2073
- return true;
2074
- }
2075
- if (subCmd === "show") {
2076
- logger.newline();
2077
- await memoryCommand("show", void 0, options);
2078
- return true;
2079
- }
2080
- if (subCmd === "clear") {
2081
- logger.newline();
2082
- await memoryCommand("clear", void 0, options);
2083
- return true;
2084
- }
2085
- logger.error("Invalid memory command. Use: /memory save|show|clear");
2086
- return true;
2087
- }
2088
- logger.warn(`Unknown command: ${mainCmd}. Type /help for available commands.`);
2089
- return true;
2090
- }
2091
- function printWelcomeBanner() {
2092
- logger.box([
2093
- chalk3.bold.cyan("DevWing AI - Interactive Chat Mode"),
2094
- "",
2095
- "Chat with AI about your codebase. Type your message and press Enter.",
2096
- "",
2097
- chalk3.dim("Type /help for commands \u2022 /exit to quit")
2098
- ].join("\n"), { title: "\u{1F680} Welcome", color: "cyan" });
2099
- }
2100
- async function chatCommand(options) {
2101
- try {
2102
- const apiKey = await configManager.getApiKey();
2103
- if (!apiKey) {
2104
- logger.error('Not authenticated. Run "devwing login" first.');
2105
- process.exit(1);
2106
- }
2107
- const session = new ChatSession();
2108
- console.clear();
2109
- printWelcomeBanner();
2110
- logger.newline();
2111
- const rl = readline.createInterface({
2112
- input: process.stdin,
2113
- output: process.stdout,
2114
- prompt: chalk3.bold.green("You: ")
2115
- });
2116
- rl.on("SIGINT", () => {
2117
- logger.newline();
2118
- logger.info("Exiting chat mode...");
2119
- rl.close();
2120
- process.exit(0);
2121
- });
2122
- logger.startSpinner("Loading codebase context...");
2123
- const collector = new ContextCollector(process.cwd());
2124
- const initialContext = await collector.collect("");
2125
- session.setContext(initialContext);
2126
- logger.succeedSpinner("Context loaded!");
2127
- logger.newline();
2128
- rl.prompt();
2129
- rl.on("line", async (input) => {
2130
- const userMessage = input.trim();
2131
- if (!userMessage) {
2132
- rl.prompt();
2133
- return;
2134
- }
2135
- if (userMessage.startsWith("/")) {
2136
- const shouldContinue = await handleSpecialCommand(userMessage, session, options);
2137
- if (!shouldContinue) {
2138
- rl.close();
2139
- return;
2140
- }
2141
- logger.newline();
2142
- rl.prompt();
2143
- return;
2144
- }
2145
- session.addMessage("user", userMessage);
2146
- logger.newline();
2147
- logger.info(chalk3.dim("DevWing is thinking..."));
2148
- logger.newline();
2149
- try {
2150
- const request = {
2151
- prompt: buildPromptWithHistory(session),
2152
- mode: options.mode,
2153
- model: options.model,
2154
- project_id: options.project || configManager.getProjectId(),
2155
- context: session.getContext(),
2156
- stream: true,
2157
- max_tokens: 4096
2158
- };
2159
- let assistantResponse = "";
2160
- streamingRenderer.reset();
2161
- streamingRenderer.clearPendingTools();
2162
- for await (const message of apiClient.streamCompletion(request)) {
2163
- await streamingRenderer.processMessage(message);
2164
- if (message.type === "token" && message.content) {
2165
- assistantResponse += message.content;
2166
- }
2167
- if (message.session_id) {
2168
- session.setSessionId(message.session_id);
2169
- }
2170
- }
2171
- if (assistantResponse) {
2172
- session.addMessage("assistant", assistantResponse);
2173
- }
2174
- const pendingTools = streamingRenderer.getPendingToolCalls();
2175
- if (pendingTools.length > 0) {
2176
- logger.newline();
2177
- logger.info("Executing tools...");
2178
- const toolResults = await streamingRenderer.executePendingTools();
2179
- if (session.getSessionId()) {
2180
- for await (const message of apiClient.continueCompletion(session.getSessionId(), toolResults)) {
2181
- await streamingRenderer.processMessage(message);
2182
- }
2183
- }
2184
- }
2185
- logger.newline();
2186
- logger.newline();
2187
- } catch (error) {
2188
- logger.newline();
2189
- logger.error("Error:", error.message || error);
2190
- logger.newline();
2191
- }
2192
- rl.prompt();
2193
- });
2194
- rl.on("close", () => {
2195
- logger.newline();
2196
- logger.success("Chat session ended. Goodbye!");
2197
- process.exit(0);
2198
- });
2199
- } catch (error) {
2200
- logger.error("Chat command failed", error);
2201
- process.exit(1);
2202
- }
2203
- }
2204
- function buildPromptWithHistory(session) {
2205
- const history = session.getHistory();
2206
- if (history.length === 0) {
2207
- return "";
2208
- }
2209
- const lastUserMessage = history[history.length - 1];
2210
- if (history.length === 1) {
2211
- return lastUserMessage.content;
2212
- }
2213
- const recentHistory = history.slice(-10);
2214
- const historyContext = recentHistory.slice(0, -1).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
2215
- return `Previous conversation:
2216
- ${historyContext}
2217
-
2218
- Current question:
2219
- ${lastUserMessage.content}`;
2220
- }
2221
1937
  var __filename2 = fileURLToPath(import.meta.url);
2222
1938
  var __dirname2 = dirname(__filename2);
2223
1939
  function getCurrentVersion() {
@@ -2310,14 +2026,14 @@ function installUpdate(packageName, packageManager, version) {
2310
2026
  }
2311
2027
  function displayUpdateInfo(currentVersion, latestVersion) {
2312
2028
  console.log();
2313
- console.log(chalk3.cyan("\u2501".repeat(60)));
2314
- console.log(chalk3.bold.cyan(" DevWing CLI Update Available"));
2315
- console.log(chalk3.cyan("\u2501".repeat(60)));
2029
+ console.log(chalk7.cyan("\u2501".repeat(60)));
2030
+ console.log(chalk7.bold.cyan(" DevWing CLI Update Available"));
2031
+ console.log(chalk7.cyan("\u2501".repeat(60)));
2316
2032
  console.log();
2317
- console.log(` ${chalk3.gray("Current version:")} ${chalk3.yellow(currentVersion)}`);
2318
- console.log(` ${chalk3.gray("Latest version:")} ${chalk3.green(latestVersion)}`);
2033
+ console.log(` ${chalk7.gray("Current version:")} ${chalk7.yellow(currentVersion)}`);
2034
+ console.log(` ${chalk7.gray("Latest version:")} ${chalk7.green(latestVersion)}`);
2319
2035
  console.log();
2320
- console.log(chalk3.gray(" Changelog: https://github.com/devwing/devwing/releases"));
2036
+ console.log(chalk7.gray(" Changelog: https://github.com/devwing/devwing/releases"));
2321
2037
  console.log();
2322
2038
  }
2323
2039
  async function updateCommand(options) {
@@ -2325,7 +2041,7 @@ async function updateCommand(options) {
2325
2041
  const currentVersion = getCurrentVersion();
2326
2042
  const packageName = getPackageName();
2327
2043
  console.log();
2328
- console.log(chalk3.cyan(`\u{1F4E6} DevWing CLI v${currentVersion}`));
2044
+ console.log(chalk7.cyan(`\u{1F4E6} DevWing CLI v${currentVersion}`));
2329
2045
  console.log();
2330
2046
  logger.startSpinner("Checking for updates...");
2331
2047
  const latestVersion = await getLatestVersion(packageName);
@@ -2349,9 +2065,9 @@ async function updateCommand(options) {
2349
2065
  logger.error("No package manager found. Please install npm, pnpm, or yarn.");
2350
2066
  return;
2351
2067
  }
2352
- logger.info(`Detected package manager: ${chalk3.cyan(packageManager)}`);
2068
+ logger.info(`Detected package manager: ${chalk7.cyan(packageManager)}`);
2353
2069
  if (!options?.force) {
2354
- const { confirm } = await inquirer.prompt([
2070
+ const { confirm } = await inquirer5.prompt([
2355
2071
  {
2356
2072
  type: "confirm",
2357
2073
  name: "confirm",
@@ -2370,13 +2086,13 @@ async function updateCommand(options) {
2370
2086
  console.log();
2371
2087
  logger.success(`Successfully updated to v${latestVersion}`);
2372
2088
  console.log();
2373
- console.log(chalk3.gray(' Run "devwing --version" to verify'));
2089
+ console.log(chalk7.gray(' Run "devwing --version" to verify'));
2374
2090
  console.log();
2375
2091
  } else {
2376
2092
  console.log();
2377
2093
  logger.error("Update failed. Please try manually:");
2378
2094
  console.log();
2379
- console.log(chalk3.gray(` ${packageManager} install -g ${packageName}@${latestVersion}`));
2095
+ console.log(chalk7.gray(` ${packageManager} install -g ${packageName}@${latestVersion}`));
2380
2096
  console.log();
2381
2097
  process.exit(1);
2382
2098
  }
@@ -2385,85 +2101,1691 @@ async function updateCommand(options) {
2385
2101
  process.exit(1);
2386
2102
  }
2387
2103
  }
2388
- var program = new Command();
2389
- var __cliFilename = fileURLToPath(import.meta.url);
2390
- var __cliDirname = dirname(__cliFilename);
2391
- function getVersion() {
2392
- const paths = [
2393
- join(__cliDirname, "../package.json"),
2394
- join(__cliDirname, "../../package.json")
2395
- ];
2396
- for (const p of paths) {
2397
- try {
2398
- return JSON.parse(readFileSync(p, "utf-8")).version;
2399
- } catch {
2400
- continue;
2104
+ var TYPING_SPEED = 16;
2105
+ var PAUSE_SHORT = 300;
2106
+ var PAUSE_MEDIUM = 800;
2107
+ var PAUSE_LONG = 1500;
2108
+ function sleep(ms) {
2109
+ return new Promise((resolve) => setTimeout(resolve, ms));
2110
+ }
2111
+ async function typeOut(text, speed = TYPING_SPEED) {
2112
+ for (const char of text) {
2113
+ process.stdout.write(char);
2114
+ await sleep(speed);
2115
+ }
2116
+ }
2117
+ async function typeLine(text, speed = TYPING_SPEED) {
2118
+ await typeOut(text, speed);
2119
+ process.stdout.write("\n");
2120
+ }
2121
+ var DEMO_USER = {
2122
+ email: "aliyu@devwing.ai",
2123
+ full_name: "Aliyu Mohammed",
2124
+ subscription_plan: "pro"};
2125
+ var DEMO_API_KEY = "dw_sk_demo_4xK9mQzR2pLvBnWcYjEtHuFi";
2126
+ var DEMO_PASSWORD = "DevWing2026!";
2127
+ var DEMO_WORKSPACE = {
2128
+ name: "Kano State Government",
2129
+ plan: "enterprise"};
2130
+ var DEMO_PROJECT = {
2131
+ name: "Kano Smart API"};
2132
+ var DEMO_MEMORIES = [
2133
+ { id: "mem_001", type: "rule", content: "All API responses must include Hausa language support", created_at: "2026-03-01T10:00:00Z" },
2134
+ { 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" },
2135
+ { 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" },
2136
+ { 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" }
2137
+ ];
2138
+ async function demoLoginCommand() {
2139
+ try {
2140
+ const existingKey = await configManager.getApiKey();
2141
+ if (existingKey) {
2142
+ const { overwrite } = await inquirer5.prompt([
2143
+ {
2144
+ type: "confirm",
2145
+ name: "overwrite",
2146
+ message: "You are already logged in. Do you want to log in with a different account?",
2147
+ default: false
2148
+ }
2149
+ ]);
2150
+ if (!overwrite) {
2151
+ logger.info("Login cancelled");
2152
+ return;
2153
+ }
2154
+ }
2155
+ logger.printBanner();
2156
+ logger.newline();
2157
+ const { method } = await inquirer5.prompt([
2158
+ {
2159
+ type: "list",
2160
+ name: "method",
2161
+ message: "How would you like to authenticate?",
2162
+ choices: [
2163
+ { name: "Via Browser (easiest)", value: "browser" },
2164
+ { name: "Email & Password", value: "email" },
2165
+ { name: "API Key (from dashboard)", value: "apikey" }
2166
+ ]
2167
+ }
2168
+ ]);
2169
+ if (method === "email") {
2170
+ await demoLoginEmail();
2171
+ } else if (method === "browser") {
2172
+ await demoLoginBrowser();
2173
+ } else {
2174
+ await demoLoginApiKey();
2401
2175
  }
2176
+ } catch (error) {
2177
+ logger.error("Login failed");
2178
+ if (error.message) logger.error(error.message);
2179
+ process.exit(1);
2402
2180
  }
2403
- return "0.1.6";
2404
2181
  }
2405
- var VERSION = getVersion();
2406
- 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");
2407
- 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");
2408
- program.command("login").description("Authenticate with DevWing").action(async () => {
2409
- await loginCommand();
2410
- });
2411
- program.command("logout").description("Clear stored credentials").action(async () => {
2412
- await logoutCommand();
2413
- });
2414
- program.command("status").description("Show authentication status and profile").action(async () => {
2415
- await statusCommand();
2416
- });
2417
- program.command("chat").description("Start interactive chat mode (like Claude Code)").action(async () => {
2418
- const opts = program.opts();
2419
- await chatCommand(opts);
2420
- });
2421
- program.command("scan").description("Run security vulnerability scan").action(async () => {
2422
- const opts = program.opts();
2423
- await scanCommand(opts);
2424
- });
2425
- program.command("review").description("Perform code review on recent changes").action(async () => {
2426
- const opts = program.opts();
2427
- await reviewCommand(opts);
2428
- });
2429
- program.command("explain <target>").description("Explain code, file, or concept").action(async (target) => {
2430
- const opts = program.opts();
2431
- await explainCommand(target, opts);
2432
- });
2433
- var memoryCmd = program.command("memory").description("Manage project memory");
2434
- memoryCmd.command("save <content>").description("Save information to project memory").action(async (content) => {
2435
- const opts = program.opts();
2436
- await memoryCommand("save", content, opts);
2437
- });
2438
- memoryCmd.command("show").description("Show all project memories").action(async () => {
2439
- const opts = program.opts();
2440
- await memoryCommand("show", void 0, opts);
2441
- });
2442
- memoryCmd.command("clear").description("Clear all project memories").action(async () => {
2443
- const opts = program.opts();
2444
- await memoryCommand("clear", void 0, opts);
2445
- });
2446
- var configCmd = program.command("config").description("Manage CLI configuration");
2447
- configCmd.command("list").description("Show all configuration").action(async () => {
2448
- await configCommand("list");
2449
- });
2450
- configCmd.command("get <key>").description("Get configuration value").action(async (key) => {
2451
- await configCommand("get", key);
2452
- });
2453
- configCmd.command("set <key> <value>").description("Set configuration value").action(async (key, value) => {
2454
- await configCommand("set", key, value);
2455
- });
2456
- program.command("update").description("Check for and install CLI updates").option("--check", "Only check for updates, do not install").option("--force", "Skip confirmation prompt").action(async (options) => {
2457
- await updateCommand(options);
2458
- });
2459
- program.argument("[prompt...]", "Natural language prompt for AI (optional - starts chat mode if empty)").action(async (promptParts) => {
2460
- const opts = program.opts();
2461
- if (promptParts.length === 0) {
2462
- await chatCommand(opts);
2182
+ async function demoLoginEmail() {
2183
+ const { email, password } = await inquirer5.prompt([
2184
+ {
2185
+ type: "input",
2186
+ name: "email",
2187
+ message: "Email:",
2188
+ validate: (input) => {
2189
+ if (!input.includes("@") || !input.includes(".")) return "Please enter a valid email address";
2190
+ return true;
2191
+ }
2192
+ },
2193
+ {
2194
+ type: "password",
2195
+ name: "password",
2196
+ message: "Password:",
2197
+ mask: "*"
2198
+ }
2199
+ ]);
2200
+ logger.startSpinner("Authenticating...");
2201
+ await sleep(1200);
2202
+ if (email !== DEMO_USER.email || password !== DEMO_PASSWORD) {
2203
+ logger.failSpinner("Authentication failed");
2204
+ logger.error("Incorrect email or password");
2463
2205
  return;
2464
2206
  }
2465
- const prompt = promptParts.join(" ");
2466
- await promptCommand(prompt, opts);
2207
+ logger.updateSpinner("Creating API key...");
2208
+ await sleep(800);
2209
+ await configManager.setApiKey(DEMO_API_KEY);
2210
+ logger.succeedSpinner(`Welcome back, ${DEMO_USER.full_name}!`);
2211
+ logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2212
+ logger.newline();
2213
+ showQuickStart2();
2214
+ }
2215
+ async function demoLoginBrowser() {
2216
+ logger.startSpinner("Initiating browser authentication...");
2217
+ await sleep(1e3);
2218
+ logger.succeedSpinner("Device code created");
2219
+ logger.newline();
2220
+ logger.success("Browser opened! Please log in to authorize this device.");
2221
+ logger.newline();
2222
+ logger.info(chalk7.dim("Device code: fSibdCwR..."));
2223
+ logger.startSpinner("Waiting for authorization...");
2224
+ await sleep(3e3);
2225
+ logger.updateSpinner("Authorization received!");
2226
+ await sleep(500);
2227
+ await configManager.setApiKey(DEMO_API_KEY);
2228
+ logger.updateSpinner("Fetching profile...");
2229
+ await sleep(600);
2230
+ logger.succeedSpinner(`Welcome, ${DEMO_USER.full_name}!`);
2231
+ logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2232
+ logger.newline();
2233
+ showQuickStart2();
2234
+ }
2235
+ async function demoLoginApiKey() {
2236
+ logger.info(`Get your API key from the dashboard`);
2237
+ logger.newline();
2238
+ const { apiKey } = await inquirer5.prompt([
2239
+ {
2240
+ type: "password",
2241
+ name: "apiKey",
2242
+ message: "API Key:",
2243
+ mask: "*",
2244
+ validate: (input) => {
2245
+ if (!input.startsWith("dw_sk_")) return 'Invalid API key format. Should start with "dw_sk_"';
2246
+ if (input.length < 20) return "API key seems too short";
2247
+ return true;
2248
+ }
2249
+ }
2250
+ ]);
2251
+ logger.startSpinner("Verifying API key...");
2252
+ await sleep(1e3);
2253
+ if (apiKey !== DEMO_API_KEY) {
2254
+ logger.failSpinner("Invalid API key");
2255
+ logger.error("API key not recognized");
2256
+ return;
2257
+ }
2258
+ await configManager.setApiKey(apiKey);
2259
+ logger.succeedSpinner(`Authenticated as ${DEMO_USER.full_name}!`);
2260
+ logger.success(`Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2261
+ logger.newline();
2262
+ showQuickStart2();
2263
+ }
2264
+ function showQuickStart2() {
2265
+ logger.box(
2266
+ [
2267
+ "Quick Start:",
2268
+ "",
2269
+ ' devwing "fix the auth bug" Ask anything',
2270
+ " devwing chat Interactive chat mode",
2271
+ " devwing scan Security scan",
2272
+ " devwing review Code review",
2273
+ " devwing explain file.js Explain code",
2274
+ "",
2275
+ "For more commands, run: devwing --help"
2276
+ ].join("\n"),
2277
+ { title: "Ready to go!", color: "green" }
2278
+ );
2279
+ }
2280
+ async function demoStatusCommand() {
2281
+ const apiKey = await configManager.getApiKey();
2282
+ if (!apiKey) {
2283
+ logger.warn("Not authenticated");
2284
+ logger.info('Run "devwing login" to get started');
2285
+ return;
2286
+ }
2287
+ logger.startSpinner("Fetching profile...");
2288
+ await sleep(800);
2289
+ logger.succeedSpinner("Authenticated");
2290
+ logger.newline();
2291
+ console.log(chalk7.bold("User Profile:"));
2292
+ console.log(` Email: ${DEMO_USER.email}`);
2293
+ console.log(` Name: ${DEMO_USER.full_name}`);
2294
+ console.log(` Plan: ${DEMO_USER.subscription_plan.toUpperCase()}`);
2295
+ console.log(` Verified: ${chalk7.green("Yes")}`);
2296
+ console.log(` 2FA: ${chalk7.dim("Disabled")}`);
2297
+ console.log(` Workspace: ${DEMO_WORKSPACE.name} (${DEMO_WORKSPACE.plan})`);
2298
+ console.log(` Project: ${DEMO_PROJECT.name}`);
2299
+ logger.newline();
2300
+ }
2301
+ async function demoLogoutCommand() {
2302
+ const apiKey = await configManager.getApiKey();
2303
+ if (!apiKey) {
2304
+ logger.info("You are not logged in");
2305
+ return;
2306
+ }
2307
+ const { confirm } = await inquirer5.prompt([
2308
+ {
2309
+ type: "confirm",
2310
+ name: "confirm",
2311
+ message: "Are you sure you want to log out?",
2312
+ default: false
2313
+ }
2314
+ ]);
2315
+ if (!confirm) {
2316
+ logger.info("Logout cancelled");
2317
+ return;
2318
+ }
2319
+ await configManager.deleteApiKey();
2320
+ const apiUrl = configManager.getApiUrl();
2321
+ configManager.clear();
2322
+ configManager.setApiUrl(apiUrl);
2323
+ logger.success("Successfully logged out");
2324
+ }
2325
+ async function demoMemoryCommand(action, content) {
2326
+ if (action === "show") {
2327
+ logger.startSpinner("Loading project memory...");
2328
+ await sleep(800);
2329
+ logger.stopSpinner();
2330
+ logger.success(`Found ${DEMO_MEMORIES.length} memory items`);
2331
+ logger.newline();
2332
+ const table = new Table3({
2333
+ head: [chalk7.bold("Type"), chalk7.bold("Content"), chalk7.bold("Created")],
2334
+ colWidths: [15, 60, 15],
2335
+ wordWrap: true
2336
+ });
2337
+ for (const memory of DEMO_MEMORIES) {
2338
+ const preview = memory.content.length > 80 ? memory.content.substring(0, 80) + "..." : memory.content;
2339
+ table.push([
2340
+ chalk7.cyan(memory.type),
2341
+ preview,
2342
+ new Date(memory.created_at).toLocaleDateString()
2343
+ ]);
2344
+ }
2345
+ console.log(table.toString());
2346
+ } else if (action === "save") {
2347
+ if (!content) {
2348
+ logger.error("Please provide content to save");
2349
+ return;
2350
+ }
2351
+ const { type } = await inquirer5.prompt([
2352
+ {
2353
+ type: "list",
2354
+ name: "type",
2355
+ message: "What type of memory is this?",
2356
+ choices: [
2357
+ { name: "Context (general information)", value: "context" },
2358
+ { name: "Rule (coding standard or guideline)", value: "rule" },
2359
+ { name: "Decision (architectural decision)", value: "decision" },
2360
+ { name: "Summary (project summary)", value: "summary" }
2361
+ ]
2362
+ }
2363
+ ]);
2364
+ logger.startSpinner("Saving to project memory...");
2365
+ await sleep(1e3);
2366
+ logger.succeedSpinner(`Memory saved as ${type}`);
2367
+ } else if (action === "clear") {
2368
+ const { confirm } = await inquirer5.prompt([
2369
+ {
2370
+ type: "confirm",
2371
+ name: "confirm",
2372
+ message: "Are you sure you want to clear all project memory?",
2373
+ default: false
2374
+ }
2375
+ ]);
2376
+ if (!confirm) {
2377
+ logger.info("Operation cancelled");
2378
+ return;
2379
+ }
2380
+ logger.startSpinner("Clearing project memory...");
2381
+ await sleep(800);
2382
+ logger.succeedSpinner("Project memory cleared");
2383
+ }
2384
+ }
2385
+ async function demoExplainCommand(target) {
2386
+ await requireDemoAuth();
2387
+ await showContextLoading(24, "general");
2388
+ logger.startSpinner("DevWing is thinking...");
2389
+ await sleep(1500);
2390
+ logger.stopSpinner();
2391
+ console.log();
2392
+ await typeLine(chalk7.bold.magenta(`## Explaining: ${target}`));
2393
+ console.log();
2394
+ if (target.includes("auth") || target.includes("middleware")) {
2395
+ await typeLine(`The auth middleware at ${chalk7.cyan(target)} handles request authentication:`);
2396
+ console.log();
2397
+ await typeLine(" 1. Extracts the JWT token from the `Authorization: Bearer` header");
2398
+ await typeLine(" 2. Verifies the token signature using RS256 algorithm");
2399
+ await typeLine(" 3. Checks token expiration and issuer claims");
2400
+ await typeLine(" 4. Loads the user from the database using the `sub` claim");
2401
+ await typeLine(" 5. Attaches the user object to `req.user` for downstream handlers");
2402
+ console.log();
2403
+ await typeLine(chalk7.bold("Key patterns:"));
2404
+ await typeLine(` ${chalk7.green("\u2022")} Uses decorator pattern \u2014 ${chalk7.cyan("@authenticate")} on route handlers`);
2405
+ await typeLine(` ${chalk7.green("\u2022")} Implements token refresh via ${chalk7.cyan("X-Refresh-Token")} header`);
2406
+ await typeLine(` ${chalk7.green("\u2022")} Rate limited to prevent brute force (10 req/min per IP)`);
2407
+ console.log();
2408
+ await typeLine(chalk7.dim("This middleware is critical \u2014 it protects all /api/* routes."));
2409
+ } else {
2410
+ await typeLine(`${chalk7.cyan(target)} is a core component of the system.`);
2411
+ console.log();
2412
+ await typeLine(` ${chalk7.green("\u2022")} Responsible for handling ${target}-related logic`);
2413
+ await typeLine(` ${chalk7.green("\u2022")} Integrates with the main application pipeline`);
2414
+ await typeLine(` ${chalk7.green("\u2022")} Well-structured with clear separation of concerns`);
2415
+ }
2416
+ await showUsage(1200, 680, 45e-4, "general");
2417
+ logger.success("Done!");
2418
+ }
2419
+ async function demoReviewCommand() {
2420
+ await requireDemoAuth();
2421
+ logger.info("Performing code review...");
2422
+ logger.newline();
2423
+ await showContextLoading(38, "general");
2424
+ logger.startSpinner("DevWing is thinking...");
2425
+ await sleep(2e3);
2426
+ logger.stopSpinner();
2427
+ console.log();
2428
+ await typeLine(chalk7.bold.magenta("## Code Review \u2014 Recent Changes"));
2429
+ console.log();
2430
+ await typeLine(chalk7.bold("Files reviewed: 5 | Commits: 3 | Lines changed: +142 / -67"));
2431
+ console.log();
2432
+ await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 Error handling is consistent across all new endpoints");
2433
+ await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 Database queries use parameterized statements");
2434
+ await typeLine(chalk7.green.bold(" \u2713 GOOD") + " \u2014 TypeScript strict mode is properly enforced");
2435
+ console.log();
2436
+ await typeLine(chalk7.yellow.bold(" \u25B2 SUGGESTION") + " \u2014 Consider extracting the validation logic at " + chalk7.cyan("user_service.ts:45"));
2437
+ await typeLine(chalk7.gray(" The email regex pattern is duplicated in 3 files. Move to a shared validator."));
2438
+ console.log();
2439
+ await typeLine(chalk7.yellow.bold(" \u25B2 SUGGESTION") + " \u2014 Missing error boundary in " + chalk7.cyan("api_handler.ts:78"));
2440
+ await typeLine(chalk7.gray(" The async handler doesn't catch unhandled promise rejections. Wrap in try/catch."));
2441
+ console.log();
2442
+ await typeLine(chalk7.red.bold(" \u25A0 ISSUE") + " \u2014 Potential memory leak in " + chalk7.cyan("websocket_manager.ts:120"));
2443
+ await typeLine(chalk7.gray(" Event listeners are added on each connection but never removed on disconnect."));
2444
+ console.log();
2445
+ await typeLine(chalk7.bold("Summary: ") + chalk7.green("3 good") + " | " + chalk7.yellow("2 suggestions") + " | " + chalk7.red("1 issue"));
2446
+ await showUsage(2800, 1200, 95e-4, "general");
2447
+ logger.success("Done!");
2448
+ }
2449
+ async function demoPromptCommand(prompt, options) {
2450
+ await requireDemoAuth();
2451
+ const promptLower = prompt.toLowerCase();
2452
+ if (promptLower.includes("refactor") && promptLower.includes("auth")) {
2453
+ await demoBackendRefactor();
2454
+ } else if (promptLower.includes("scan") || promptLower.includes("owasp") || promptLower.includes("vulnerabilit")) {
2455
+ await demoSecurityScan();
2456
+ } else if (promptLower.includes("explain")) {
2457
+ const target = prompt.replace(/explain\s*/i, "").trim() || "this code";
2458
+ await demoExplainCommand(target);
2459
+ } else if (promptLower.includes("review")) {
2460
+ await demoReviewCommand();
2461
+ } else if (promptLower.includes("deploy") || promptLower.includes("docker") || promptLower.includes("kubernetes")) {
2462
+ await demoDeployAssist();
2463
+ } else if (promptLower.includes("fix") || promptLower.includes("bug") || promptLower.includes("error")) {
2464
+ await demoBugFix();
2465
+ } else if (promptLower.includes("test") || promptLower.includes("spec")) {
2466
+ await demoWriteTests();
2467
+ } else {
2468
+ await demoGenericPrompt(prompt, options);
2469
+ }
2470
+ }
2471
+ async function demoBackendRefactor() {
2472
+ await showContextLoading(47, "backend");
2473
+ logger.startSpinner("DevWing is thinking...");
2474
+ await sleep(2e3);
2475
+ logger.stopSpinner();
2476
+ console.log();
2477
+ await typeLine(chalk7.bold.magenta("## Analysis"));
2478
+ console.log();
2479
+ await typeLine("I've analyzed the auth service across 6 files. Here's what I found:");
2480
+ console.log();
2481
+ await typeLine(` ${chalk7.red("\u2022")} Auth logic, validation, and DB queries are tightly coupled in ${chalk7.cyan("auth_controller.ts")}`);
2482
+ await typeLine(` ${chalk7.red("\u2022")} JWT validation is duplicated across 4 route handlers`);
2483
+ await typeLine(` ${chalk7.red("\u2022")} No rate limiting on login/register endpoints`);
2484
+ await typeLine(` ${chalk7.red("\u2022")} User lookup queries repeated in 3 places`);
2485
+ console.log();
2486
+ await sleep(PAUSE_MEDIUM);
2487
+ await typeLine(chalk7.bold.magenta("## Refactor Plan"));
2488
+ console.log();
2489
+ await typeLine("1. Extract JWT validation into reusable middleware");
2490
+ await typeLine("2. Create `UserRepository` with all DB operations");
2491
+ await typeLine("3. Add `RateLimiter` decorator using sliding window algorithm");
2492
+ await typeLine("4. Separate controller into thin route handlers");
2493
+ console.log();
2494
+ await sleep(PAUSE_MEDIUM);
2495
+ await typeLine(chalk7.bold.magenta("## Executing Refactor"));
2496
+ console.log();
2497
+ await showToolCall("read_file", "src/auth/auth_controller.ts");
2498
+ await showToolResult("Read 156 lines");
2499
+ await showToolCall("read_file", "src/auth/auth_routes.ts");
2500
+ await showToolResult("Read 89 lines");
2501
+ await showToolCall("read_file", "src/middleware/index.ts");
2502
+ await showToolResult("Read 23 lines");
2503
+ await showToolCall("write_file", "src/auth/repositories/user.repository.ts");
2504
+ await sleep(PAUSE_LONG);
2505
+ await showToolResult("Created \u2014 UserRepository with findByEmail, findById, create, updateLastLogin");
2506
+ await showToolCall("write_file", "src/middleware/jwt.middleware.ts");
2507
+ await sleep(PAUSE_MEDIUM);
2508
+ await showToolResult("Created \u2014 JWT validation middleware with token extraction and verification");
2509
+ await showToolCall("write_file", "src/middleware/rate-limiter.ts");
2510
+ await sleep(PAUSE_MEDIUM);
2511
+ await showToolResult("Created \u2014 Sliding window rate limiter: 10 req/min login, 5 req/min register");
2512
+ await showToolCall("write_file", "src/auth/auth_controller.ts");
2513
+ await sleep(PAUSE_LONG);
2514
+ await showToolResult("Refactored \u2014 156 lines \u2192 89 lines, clean separation of concerns");
2515
+ await showToolCall("run_command", "npm test -- --filter auth");
2516
+ await sleep(2e3);
2517
+ await showToolResult("All 12 tests passed");
2518
+ console.log();
2519
+ await sleep(PAUSE_MEDIUM);
2520
+ await typeLine(chalk7.bold.magenta("## Summary"));
2521
+ console.log();
2522
+ await typeLine(chalk7.green("\u2713") + " Extracted JWT validation to dedicated middleware");
2523
+ await typeLine(chalk7.green("\u2713") + " Created UserRepository \u2014 single source for all user DB operations");
2524
+ await typeLine(chalk7.green("\u2713") + " Added rate limiting: 10 req/min (login), 5 req/min (register)");
2525
+ await typeLine(chalk7.green("\u2713") + ` Reduced ${chalk7.red("156")} lines \u2192 ${chalk7.green("89")} lines (${chalk7.cyan("43% reduction")})`);
2526
+ await typeLine(chalk7.green("\u2713") + " All 12 existing tests pass \u2014 no regressions");
2527
+ console.log();
2528
+ await typeLine(chalk7.dim("Files modified: 4 | Files created: 3 | Lines removed: 67"));
2529
+ await showUsage(3420, 1890, 0.0127, "backend");
2530
+ logger.success("Done!");
2531
+ }
2532
+ async function demoSecurityScan() {
2533
+ await showContextLoading(32, "security");
2534
+ logger.startSpinner("DevWing is thinking...");
2535
+ await sleep(1800);
2536
+ logger.stopSpinner();
2537
+ console.log();
2538
+ await typeLine(chalk7.bold.magenta("## OWASP Security Scan \u2014 Auth Service"));
2539
+ console.log();
2540
+ await showToolCall("read_file", "src/auth/auth_controller.ts");
2541
+ await showToolResult("Read 89 lines");
2542
+ await showToolCall("read_file", "src/middleware/jwt.middleware.ts");
2543
+ await showToolResult("Read 34 lines");
2544
+ await showToolCall("read_file", "src/auth/repositories/user.repository.ts");
2545
+ await showToolResult("Read 52 lines");
2546
+ await sleep(PAUSE_LONG);
2547
+ await typeLine(chalk7.bold.red("## Vulnerabilities Found: 3"));
2548
+ console.log();
2549
+ await typeLine(chalk7.red.bold(" \u25A0 CRITICAL") + chalk7.red(" \u2014 Weak JWT Token Configuration"));
2550
+ await typeLine(chalk7.gray(" Location: src/middleware/jwt.middleware.ts:12"));
2551
+ await typeLine(chalk7.gray(" Issue: JWT tokens signed with HS256 and no expiration validation"));
2552
+ await typeLine(chalk7.gray(" OWASP: A2:2021 \u2014 Cryptographic Failures"));
2553
+ console.log();
2554
+ await typeLine(chalk7.yellow.bold(" \u25A0 HIGH") + chalk7.yellow(" \u2014 SQL Injection via User Lookup"));
2555
+ await typeLine(chalk7.gray(" Location: src/auth/repositories/user.repository.ts:18"));
2556
+ await typeLine(chalk7.gray(" Issue: Raw string interpolation in findByEmail query"));
2557
+ await typeLine(chalk7.gray(" OWASP: A3:2021 \u2014 Injection"));
2558
+ console.log();
2559
+ await typeLine(chalk7.yellow.bold(" \u25A0 MEDIUM") + chalk7.yellow(" \u2014 Missing Password Complexity Enforcement"));
2560
+ await typeLine(chalk7.gray(" Location: src/auth/auth_controller.ts:34"));
2561
+ await typeLine(chalk7.gray(" Issue: No minimum length, complexity, or breach database check"));
2562
+ await typeLine(chalk7.gray(" OWASP: A7:2021 \u2014 Identification and Authentication Failures"));
2563
+ console.log();
2564
+ await sleep(PAUSE_MEDIUM);
2565
+ await typeLine(chalk7.bold.magenta("## Applying Security Patches"));
2566
+ console.log();
2567
+ await showToolCall("write_file", "src/middleware/jwt.middleware.ts");
2568
+ await sleep(PAUSE_LONG);
2569
+ await showToolResult("Patched \u2014 Added RS256 signing, exp/iat validation, token rotation support");
2570
+ await showToolCall("write_file", "src/auth/repositories/user.repository.ts");
2571
+ await sleep(PAUSE_MEDIUM);
2572
+ await showToolResult("Patched \u2014 Parameterized queries, input sanitization added");
2573
+ await showToolCall("write_file", "src/auth/validators/password.validator.ts");
2574
+ await sleep(PAUSE_MEDIUM);
2575
+ await showToolResult("Created \u2014 Min 12 chars, uppercase, number, special char, breach check");
2576
+ await showToolCall("run_command", "npm test -- --filter auth");
2577
+ await sleep(1500);
2578
+ await showToolResult("All 15 tests passed (3 new security tests added)");
2579
+ console.log();
2580
+ await typeLine(chalk7.bold.magenta("## Scan Complete"));
2581
+ console.log();
2582
+ await typeLine(chalk7.green("\u2713") + " 3 vulnerabilities found and patched");
2583
+ await typeLine(chalk7.green("\u2713") + " JWT upgraded to RS256 with expiration enforcement");
2584
+ await typeLine(chalk7.green("\u2713") + " SQL injection eliminated via parameterized queries");
2585
+ await typeLine(chalk7.green("\u2713") + " Password policy enforced \u2014 NIST 800-63B compliant");
2586
+ await typeLine(chalk7.green("\u2713") + " 3 new security regression tests added");
2587
+ console.log();
2588
+ await typeLine(chalk7.dim("Severity breakdown: 1 critical, 1 high, 1 medium \u2014 all resolved"));
2589
+ await showUsage(2180, 1450, 89e-4, "security");
2590
+ logger.success("Done!");
2591
+ }
2592
+ async function demoDeployAssist(_prompt) {
2593
+ await showContextLoading(18, "devops");
2594
+ logger.startSpinner("DevWing is thinking...");
2595
+ await sleep(1800);
2596
+ logger.stopSpinner();
2597
+ console.log();
2598
+ await typeLine(chalk7.bold.magenta("## Deployment Analysis"));
2599
+ console.log();
2600
+ await showToolCall("read_file", "Dockerfile");
2601
+ await showToolResult("Read 28 lines");
2602
+ await showToolCall("read_file", "docker-compose.yml");
2603
+ await showToolResult("Read 45 lines");
2604
+ await showToolCall("read_file", ".github/workflows/deploy.yml");
2605
+ await showToolResult("Read 67 lines");
2606
+ await sleep(PAUSE_MEDIUM);
2607
+ await typeLine("Current deployment configuration:");
2608
+ console.log();
2609
+ await typeLine(` ${chalk7.green("\u2022")} Docker image: ${chalk7.cyan("node:20-alpine")} \u2014 multi-stage build`);
2610
+ await typeLine(` ${chalk7.green("\u2022")} Target: Railway (detected from ${chalk7.cyan("railway.toml")})`);
2611
+ await typeLine(` ${chalk7.green("\u2022")} Health check: ${chalk7.cyan("/health")} endpoint configured`);
2612
+ await typeLine(` ${chalk7.green("\u2022")} Environment: 12 vars configured, 3 secrets`);
2613
+ console.log();
2614
+ await typeLine(chalk7.bold.magenta("## Optimizations Applied"));
2615
+ console.log();
2616
+ await showToolCall("write_file", "Dockerfile");
2617
+ await sleep(PAUSE_LONG);
2618
+ await showToolResult("Optimized \u2014 Added layer caching, reduced image size from 340MB to 89MB");
2619
+ await showToolCall("write_file", "docker-compose.yml");
2620
+ await sleep(PAUSE_MEDIUM);
2621
+ await showToolResult("Added health checks, restart policies, resource limits");
2622
+ console.log();
2623
+ await typeLine(chalk7.green("\u2713") + " Docker image optimized: 340MB \u2192 89MB (74% smaller)");
2624
+ await typeLine(chalk7.green("\u2713") + " Added proper health checks and restart policies");
2625
+ await typeLine(chalk7.green("\u2713") + " Build time reduced: ~4min \u2192 ~1.5min with layer caching");
2626
+ await showUsage(1800, 950, 65e-4, "devops");
2627
+ logger.success("Done!");
2628
+ }
2629
+ async function demoBugFix(_prompt) {
2630
+ await showContextLoading(35, "backend");
2631
+ logger.startSpinner("DevWing is thinking...");
2632
+ await sleep(1800);
2633
+ logger.stopSpinner();
2634
+ console.log();
2635
+ await typeLine(chalk7.bold.magenta("## Bug Analysis"));
2636
+ console.log();
2637
+ await showToolCall("read_file", "src/services/user_service.ts");
2638
+ await showToolResult("Read 94 lines");
2639
+ await showToolCall("git_diff", "");
2640
+ await showToolResult("3 files changed, 12 insertions, 4 deletions");
2641
+ await showToolCall("git_log", "last 5 commits");
2642
+ await showToolResult("Loaded recent history");
2643
+ await sleep(PAUSE_MEDIUM);
2644
+ await typeLine("I found the issue. The bug is in " + chalk7.cyan("user_service.ts:67") + ":");
2645
+ console.log();
2646
+ await typeLine(chalk7.red(" - const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);"));
2647
+ await typeLine(chalk7.green(" + const user = await db.query(`SELECT * FROM users WHERE id = $1`, [userId]);"));
2648
+ console.log();
2649
+ await typeLine("The user ID was being interpolated directly into the SQL query instead of");
2650
+ await typeLine("using parameterized binding. This causes:");
2651
+ await typeLine(" 1. SQL injection vulnerability");
2652
+ await typeLine(" 2. Query failure when ID contains special characters");
2653
+ console.log();
2654
+ await showToolCall("write_file", "src/services/user_service.ts");
2655
+ await sleep(PAUSE_LONG);
2656
+ await showToolResult("Fixed \u2014 Parameterized query binding");
2657
+ await showToolCall("run_command", "npm test -- --filter user");
2658
+ await sleep(1500);
2659
+ await showToolResult("All 8 tests passed");
2660
+ console.log();
2661
+ await typeLine(chalk7.green("\u2713") + " Bug fixed \u2014 parameterized SQL query");
2662
+ await typeLine(chalk7.green("\u2713") + " SQL injection vulnerability closed");
2663
+ await typeLine(chalk7.green("\u2713") + " All tests pass");
2664
+ await showUsage(1650, 890, 62e-4, "backend");
2665
+ logger.success("Done!");
2666
+ }
2667
+ async function demoWriteTests(_prompt) {
2668
+ await showContextLoading(22, "backend");
2669
+ logger.startSpinner("DevWing is thinking...");
2670
+ await sleep(1800);
2671
+ logger.stopSpinner();
2672
+ console.log();
2673
+ await typeLine(chalk7.bold.magenta("## Generating Tests"));
2674
+ console.log();
2675
+ await showToolCall("read_file", "src/auth/auth_controller.ts");
2676
+ await showToolResult("Read 89 lines \u2014 4 functions to test");
2677
+ await showToolCall("read_file", "src/auth/repositories/user.repository.ts");
2678
+ await showToolResult("Read 52 lines \u2014 5 methods to test");
2679
+ await sleep(PAUSE_MEDIUM);
2680
+ await showToolCall("write_file", "tests/auth/auth_controller.test.ts");
2681
+ await sleep(PAUSE_LONG);
2682
+ await showToolResult("Created \u2014 8 test cases: login, register, refresh, logout, edge cases");
2683
+ await showToolCall("write_file", "tests/auth/user_repository.test.ts");
2684
+ await sleep(PAUSE_LONG);
2685
+ await showToolResult("Created \u2014 10 test cases: CRUD operations, error handling, constraints");
2686
+ await showToolCall("run_command", "npm test -- --filter auth");
2687
+ await sleep(2e3);
2688
+ await showToolResult("18 / 18 tests passed");
2689
+ console.log();
2690
+ await typeLine(chalk7.bold.magenta("## Test Coverage"));
2691
+ console.log();
2692
+ await typeLine(" auth_controller.ts: " + chalk7.green("94%") + " (was 0%)");
2693
+ await typeLine(" user.repository.ts: " + chalk7.green("98%") + " (was 0%)");
2694
+ await typeLine(" Overall auth module: " + chalk7.green("96%"));
2695
+ console.log();
2696
+ await typeLine(chalk7.green("\u2713") + " 18 tests generated and passing");
2697
+ await typeLine(chalk7.green("\u2713") + " Covers happy paths, edge cases, and error states");
2698
+ await showUsage(2400, 1680, 98e-4, "backend");
2699
+ logger.success("Done!");
2700
+ }
2701
+ async function demoGenericPrompt(prompt, options) {
2702
+ const mode = options.mode || "general";
2703
+ await showContextLoading(28, mode);
2704
+ logger.startSpinner("DevWing is thinking...");
2705
+ await sleep(1500);
2706
+ logger.stopSpinner();
2707
+ console.log();
2708
+ await typeLine(chalk7.bold.magenta("## Response"));
2709
+ console.log();
2710
+ await typeLine(`Based on your codebase analysis, here's my approach to: "${prompt}"`);
2711
+ console.log();
2712
+ await typeLine(` ${chalk7.green("\u2022")} Analyzed project structure and dependencies`);
2713
+ await typeLine(` ${chalk7.green("\u2022")} Reviewed relevant files based on your prompt`);
2714
+ await typeLine(` ${chalk7.green("\u2022")} Generated solution aligned with existing patterns`);
2715
+ console.log();
2716
+ await showUsage(1100, 620, 41e-4, mode);
2717
+ logger.success("Done!");
2718
+ }
2719
+ async function showToolCall(tool, args) {
2720
+ await sleep(PAUSE_SHORT);
2721
+ console.log();
2722
+ console.log(chalk7.yellow(` \u25B6 ${chalk7.bold(tool)}`));
2723
+ if (args) console.log(chalk7.gray(` ${args}`));
2724
+ await sleep(PAUSE_MEDIUM);
2725
+ }
2726
+ async function showToolResult(result) {
2727
+ console.log(chalk7.green(` \u2713 ${result}`));
2728
+ await sleep(PAUSE_SHORT);
2729
+ }
2730
+ async function showContextLoading(fileCount, mode) {
2731
+ logger.startSpinner("Analyzing codebase...");
2732
+ await sleep(1200);
2733
+ logger.updateSpinner(`Scanning ${fileCount} files...`);
2734
+ await sleep(800);
2735
+ logger.updateSpinner("Building dependency graph...");
2736
+ await sleep(600);
2737
+ logger.updateSpinner("Collecting git context...");
2738
+ await sleep(500);
2739
+ logger.succeedSpinner(`Context loaded \u2014 ${fileCount} files, mode: ${chalk7.cyan(mode)}`);
2740
+ console.log();
2741
+ }
2742
+ async function showUsage(tokensIn, tokensOut, cost, mode) {
2743
+ const modelName = mode === "security" ? "devwing-security-1" : mode === "devops" ? "devwing-devops-1" : mode === "frontend" ? "devwing-frontend-1" : "devwing-backend-1";
2744
+ console.log();
2745
+ console.log(
2746
+ chalk7.gray(
2747
+ `\u{1F4CA} Tokens: ${tokensIn.toLocaleString()} in, ${tokensOut.toLocaleString()} out | Cost: $${cost.toFixed(4)} | Model: ${modelName}`
2748
+ )
2749
+ );
2750
+ console.log();
2751
+ }
2752
+ async function requireDemoAuth() {
2753
+ const apiKey = await configManager.getApiKey();
2754
+ if (!apiKey) {
2755
+ logger.error('Not authenticated. Run "devwing login" first.');
2756
+ process.exit(1);
2757
+ }
2758
+ }
2759
+ function isDemoMode() {
2760
+ return process.env.DEVWING_DEMO === "1" || process.env.DEVWING_DEMO === "true";
2761
+ }
2762
+ var __filename3 = fileURLToPath(import.meta.url);
2763
+ var __dirname3 = dirname(__filename3);
2764
+ function getVersion() {
2765
+ const paths = [
2766
+ join(__dirname3, "../../package.json"),
2767
+ join(__dirname3, "../../../package.json")
2768
+ ];
2769
+ for (const p of paths) {
2770
+ try {
2771
+ return JSON.parse(readFileSync(p, "utf-8")).version;
2772
+ } catch {
2773
+ continue;
2774
+ }
2775
+ }
2776
+ return "0.1.8";
2777
+ }
2778
+ var DEMO_USER2 = {
2779
+ id: "usr_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
2780
+ email: "aliyu@devwing.ai",
2781
+ full_name: "Aliyu Mohammed",
2782
+ subscription_plan: "pro",
2783
+ plan: "pro",
2784
+ is_verified: true,
2785
+ totp_enabled: false,
2786
+ created_at: "2026-01-15T08:00:00Z",
2787
+ tokens_used_today: 47832,
2788
+ tokens_reset_at: "2026-04-10T00:00:00Z"
2789
+ };
2790
+ var DEMO_WORKSPACE2 = {
2791
+ name: "Kano State Government",
2792
+ plan: "enterprise"};
2793
+ var DEMO_PROJECT2 = {
2794
+ name: "Kano Smart API"};
2795
+ var DEMO_MODELS = [
2796
+ { name: "devwing-general-1", display_name: "DevWing General", domain: "general", status: "active", min_plan: "free", context_window: 32768, tokens_per_sec: 85, version: "1.0" },
2797
+ { name: "devwing-frontend-1", display_name: "DevWing Frontend", domain: "frontend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 78, version: "1.0" },
2798
+ { name: "devwing-backend-1", display_name: "DevWing Backend", domain: "backend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 82, version: "1.0" },
2799
+ { name: "devwing-security-1", display_name: "DevWing Security", domain: "security", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 74, version: "1.0" },
2800
+ { name: "devwing-devops-1", display_name: "DevWing DevOps", domain: "devops", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 70, version: "0.9" },
2801
+ { name: "devwing-mobile-1", display_name: "DevWing Mobile", domain: "general", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 72, version: "0.8" },
2802
+ { name: "devwing-data-1", display_name: "DevWing Data", domain: "general", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 68, version: "0.7" }
2803
+ ];
2804
+ var DEMO_PLANS = [
2805
+ { name: "Free", price: "$0", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
2806
+ { name: "Pro", price: "$19/mo", tokens_day: "500,000", models: "All models", projects: "10", seats: "1" },
2807
+ { name: "Team", price: "$99/mo", tokens_day: "2,000,000", models: "All + priority", projects: "Unlimited", seats: "5 included" },
2808
+ { name: "Enterprise", price: "Custom", tokens_day: "Unlimited", models: "Dedicated", projects: "Unlimited", seats: "Unlimited" }
2809
+ ];
2810
+ var DEMO_USAGE = {
2811
+ today: { requests: 23, tokens_in: 31420, tokens_out: 16412, cost: 0.1147 },
2812
+ week: { requests: 147, tokens_in: 198340, tokens_out: 102890, cost: 0.7213 },
2813
+ month: { requests: 612, tokens_in: 824100, tokens_out: 428750, cost: 3.0012 }
2814
+ };
2815
+ var InteractiveSession = class {
2816
+ isAuthenticated = false;
2817
+ userProfile = null;
2818
+ currentMode = "general";
2819
+ currentModel = null;
2820
+ conversationHistory = [];
2821
+ sessionContext = null;
2822
+ sessionId = null;
2823
+ isDemo;
2824
+ options;
2825
+ rl;
2826
+ commands = /* @__PURE__ */ new Map();
2827
+ sigintCount = 0;
2828
+ sigintTimer = null;
2829
+ version;
2830
+ constructor(options) {
2831
+ this.options = options;
2832
+ this.isDemo = isDemoMode();
2833
+ this.version = getVersion();
2834
+ if (options.mode) this.currentMode = options.mode;
2835
+ if (options.model) this.currentModel = options.model;
2836
+ this.registerCommands();
2837
+ }
2838
+ // ============================================================
2839
+ // START
2840
+ // ============================================================
2841
+ async start() {
2842
+ console.clear();
2843
+ this.printStartupBanner();
2844
+ await this.checkAuthStatus();
2845
+ this.printSystemInfo();
2846
+ this.rl = readline.createInterface({
2847
+ input: process.stdin,
2848
+ output: process.stdout,
2849
+ prompt: this.buildPrompt(),
2850
+ completer: (line) => this.completer(line)
2851
+ });
2852
+ this.rl.on("SIGINT", () => {
2853
+ this.sigintCount++;
2854
+ if (this.sigintCount >= 2) {
2855
+ this.exitSession();
2856
+ return;
2857
+ }
2858
+ console.log(chalk7.dim("\n Press Ctrl+C again to exit, or type /exit"));
2859
+ this.sigintTimer = setTimeout(() => {
2860
+ this.sigintCount = 0;
2861
+ }, 1500);
2862
+ this.rl.prompt();
2863
+ });
2864
+ this.rl.on("line", async (input) => {
2865
+ const trimmed = input.trim();
2866
+ if (!trimmed) {
2867
+ this.rl.prompt();
2868
+ return;
2869
+ }
2870
+ try {
2871
+ if (trimmed.startsWith("/")) {
2872
+ await this.handleSlashCommand(trimmed);
2873
+ } else {
2874
+ await this.handleAIPrompt(trimmed);
2875
+ }
2876
+ } catch (error) {
2877
+ logger.error(error.message || "An error occurred");
2878
+ }
2879
+ this.rl.setPrompt(this.buildPrompt());
2880
+ this.rl.prompt();
2881
+ });
2882
+ this.rl.on("close", () => {
2883
+ this.exitSession();
2884
+ });
2885
+ console.log();
2886
+ this.rl.prompt();
2887
+ }
2888
+ // ============================================================
2889
+ // STARTUP UI
2890
+ // ============================================================
2891
+ printStartupBanner() {
2892
+ const banner = gradient.pastel.multiline([
2893
+ "",
2894
+ " \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
2895
+ " \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ",
2896
+ " \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557",
2897
+ " \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
2898
+ " \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
2899
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D ",
2900
+ ""
2901
+ ].join("\n"));
2902
+ console.log(banner);
2903
+ console.log(chalk7.dim(" Your AI Wingman in the Terminal") + chalk7.dim(` v${this.version}`));
2904
+ console.log();
2905
+ }
2906
+ printSystemInfo() {
2907
+ const line = chalk7.dim("\u2500".repeat(64));
2908
+ console.log(line);
2909
+ if (this.isAuthenticated && this.userProfile) {
2910
+ const name = this.userProfile.full_name || this.userProfile.email;
2911
+ const plan = (this.userProfile.subscription_plan || "free").toUpperCase();
2912
+ const planColor = plan === "PRO" ? chalk7.cyan : plan === "ENTERPRISE" ? chalk7.magenta : plan === "TEAM" ? chalk7.yellow : chalk7.dim;
2913
+ console.log(
2914
+ ` ${chalk7.bold("Auth")} ${chalk7.green("\u25CF")} ${name} ${planColor(`(${plan})`)}`
2915
+ );
2916
+ } else {
2917
+ console.log(
2918
+ ` ${chalk7.bold("Auth")} ${chalk7.red("\u25CF")} Not logged in ${chalk7.dim("\u2014 type /login")}`
2919
+ );
2920
+ }
2921
+ const modeStr = chalk7.yellow(this.currentMode);
2922
+ const modelStr = this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto");
2923
+ console.log(` ${chalk7.bold("Mode")} ${modeStr} ${chalk7.bold("Model")} ${modelStr}`);
2924
+ const cwd = process.cwd().replace(process.env.HOME || "", "~");
2925
+ if (this.isDemo) {
2926
+ console.log(` ${chalk7.bold("Project")} ${chalk7.cyan(DEMO_PROJECT2.name)} ${chalk7.bold("CWD")} ${chalk7.dim(cwd)}`);
2927
+ } else {
2928
+ const projectId = configManager.getProjectId();
2929
+ const projectStr = projectId ? chalk7.cyan(projectId) : chalk7.dim("none");
2930
+ console.log(` ${chalk7.bold("Project")} ${projectStr} ${chalk7.bold("CWD")} ${chalk7.dim(cwd)}`);
2931
+ }
2932
+ const shell = process.env.SHELL?.split("/").pop() || "unknown";
2933
+ console.log(` ${chalk7.bold("Shell")} ${shell} ${chalk7.bold("OS")} ${process.platform}`);
2934
+ if (this.isDemo) {
2935
+ console.log(` ${chalk7.bold("Demo")} ${chalk7.yellow("\u25CF")} ${chalk7.yellow("Demo mode active")} ${chalk7.dim("\u2014 no backend required")}`);
2936
+ }
2937
+ console.log(line);
2938
+ console.log();
2939
+ console.log(chalk7.dim(" Type a message to chat with AI, or /help for commands."));
2940
+ }
2941
+ // ============================================================
2942
+ // PROMPT
2943
+ // ============================================================
2944
+ buildPrompt() {
2945
+ const parts = [];
2946
+ parts.push(chalk7.bold.cyan("devwing"));
2947
+ parts.push(chalk7.dim("[") + chalk7.yellow(this.currentMode) + chalk7.dim("]"));
2948
+ if (this.isAuthenticated && this.userProfile) {
2949
+ const firstName = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0].toLowerCase();
2950
+ parts.push(chalk7.green(firstName));
2951
+ }
2952
+ if (this.isDemo) {
2953
+ parts.push(chalk7.dim.yellow("demo"));
2954
+ }
2955
+ return "\n" + parts.join(" ") + chalk7.bold(" > ");
2956
+ }
2957
+ // ============================================================
2958
+ // TAB COMPLETION
2959
+ // ============================================================
2960
+ completer(line) {
2961
+ if (!line.startsWith("/")) return [[], line];
2962
+ const allNames = [];
2963
+ for (const cmd of this.commands.values()) {
2964
+ allNames.push("/" + cmd.name);
2965
+ for (const alias of cmd.aliases) {
2966
+ allNames.push("/" + alias);
2967
+ }
2968
+ }
2969
+ const hits = allNames.filter((n) => n.startsWith(line));
2970
+ return [hits.length ? hits : allNames, line];
2971
+ }
2972
+ // ============================================================
2973
+ // AUTH CHECK
2974
+ // ============================================================
2975
+ async checkAuthStatus() {
2976
+ try {
2977
+ const apiKey = await configManager.getApiKey();
2978
+ if (apiKey) {
2979
+ if (this.isDemo) {
2980
+ this.isAuthenticated = true;
2981
+ this.userProfile = { ...DEMO_USER2 };
2982
+ } else {
2983
+ const profile = await apiClient.getProfile();
2984
+ this.isAuthenticated = true;
2985
+ this.userProfile = profile;
2986
+ }
2987
+ }
2988
+ } catch {
2989
+ this.isAuthenticated = false;
2990
+ this.userProfile = null;
2991
+ }
2992
+ }
2993
+ // ============================================================
2994
+ // SLASH COMMAND DISPATCH
2995
+ // ============================================================
2996
+ async handleSlashCommand(input) {
2997
+ const parts = input.split(/\s+/);
2998
+ const cmdName = parts[0].substring(1).toLowerCase();
2999
+ const args = parts.slice(1).join(" ").trim();
3000
+ let command;
3001
+ for (const cmd of this.commands.values()) {
3002
+ if (cmd.name === cmdName || cmd.aliases.includes(cmdName)) {
3003
+ command = cmd;
3004
+ break;
3005
+ }
3006
+ }
3007
+ if (!command) {
3008
+ logger.warn(`Unknown command: /${cmdName}. Type /help for available commands.`);
3009
+ return;
3010
+ }
3011
+ if (command.requiresAuth && !this.isAuthenticated) {
3012
+ logger.warn("You need to be logged in for this command. Type /login first.");
3013
+ return;
3014
+ }
3015
+ this.rl.pause();
3016
+ try {
3017
+ await command.handler(args);
3018
+ } finally {
3019
+ this.rl.resume();
3020
+ }
3021
+ }
3022
+ // ============================================================
3023
+ // AI PROMPT HANDLER
3024
+ // ============================================================
3025
+ async handleAIPrompt(input) {
3026
+ if (!this.isAuthenticated) {
3027
+ console.log();
3028
+ logger.warn("You need to log in to chat with AI. Type /login to authenticate.");
3029
+ return;
3030
+ }
3031
+ console.log();
3032
+ if (this.isDemo) {
3033
+ this.rl.pause();
3034
+ try {
3035
+ await demoPromptCommand(input, { ...this.options, mode: this.currentMode });
3036
+ } finally {
3037
+ this.rl.resume();
3038
+ }
3039
+ this.conversationHistory.push(
3040
+ { role: "user", content: input, timestamp: /* @__PURE__ */ new Date() },
3041
+ { role: "assistant", content: "[demo response]", timestamp: /* @__PURE__ */ new Date() }
3042
+ );
3043
+ return;
3044
+ }
3045
+ if (!this.sessionContext) {
3046
+ logger.startSpinner("Loading codebase context...");
3047
+ try {
3048
+ const collector = new ContextCollector(process.cwd());
3049
+ this.sessionContext = await collector.collect(input);
3050
+ logger.succeedSpinner(`Context loaded \u2014 ${this.sessionContext.files.length} files`);
3051
+ } catch (error) {
3052
+ logger.failSpinner("Failed to load context");
3053
+ logger.error(error.message);
3054
+ return;
3055
+ }
3056
+ }
3057
+ this.conversationHistory.push({ role: "user", content: input, timestamp: /* @__PURE__ */ new Date() });
3058
+ logger.info(chalk7.dim("DevWing is thinking..."));
3059
+ console.log();
3060
+ try {
3061
+ const request = {
3062
+ prompt: this.buildPromptWithHistory(),
3063
+ mode: this.currentMode,
3064
+ model: this.currentModel || void 0,
3065
+ project_id: this.options.project || configManager.getProjectId(),
3066
+ context: this.sessionContext,
3067
+ stream: true,
3068
+ max_tokens: 4096
3069
+ };
3070
+ let assistantResponse = "";
3071
+ streamingRenderer.reset();
3072
+ streamingRenderer.clearPendingTools();
3073
+ for await (const message of apiClient.streamCompletion(request)) {
3074
+ await streamingRenderer.processMessage(message);
3075
+ if (message.type === "token" && message.content) {
3076
+ assistantResponse += message.content;
3077
+ }
3078
+ if (message.session_id) {
3079
+ this.sessionId = message.session_id;
3080
+ }
3081
+ }
3082
+ if (assistantResponse) {
3083
+ this.conversationHistory.push({ role: "assistant", content: assistantResponse, timestamp: /* @__PURE__ */ new Date() });
3084
+ }
3085
+ const pendingTools = streamingRenderer.getPendingToolCalls();
3086
+ if (pendingTools.length > 0) {
3087
+ logger.newline();
3088
+ logger.info("Executing tools...");
3089
+ const toolResults = await streamingRenderer.executePendingTools();
3090
+ if (this.sessionId) {
3091
+ for await (const message of apiClient.continueCompletion(this.sessionId, toolResults)) {
3092
+ await streamingRenderer.processMessage(message);
3093
+ }
3094
+ }
3095
+ }
3096
+ console.log();
3097
+ } catch (error) {
3098
+ logger.error(error.message || "AI request failed");
3099
+ }
3100
+ }
3101
+ buildPromptWithHistory() {
3102
+ const history = this.conversationHistory;
3103
+ if (history.length <= 1) {
3104
+ return history[history.length - 1]?.content || "";
3105
+ }
3106
+ const recent = history.slice(-10);
3107
+ const context = recent.slice(0, -1).map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n\n");
3108
+ return `Previous conversation:
3109
+ ${context}
3110
+
3111
+ Current question:
3112
+ ${recent[recent.length - 1].content}`;
3113
+ }
3114
+ // ============================================================
3115
+ // EXIT
3116
+ // ============================================================
3117
+ exitSession() {
3118
+ if (this.sigintTimer) clearTimeout(this.sigintTimer);
3119
+ console.log();
3120
+ console.log(chalk7.dim(" Goodbye! Happy coding."));
3121
+ console.log();
3122
+ process.exit(0);
3123
+ }
3124
+ // ============================================================
3125
+ // COMMAND REGISTRATION
3126
+ // ============================================================
3127
+ registerCommands() {
3128
+ this.addCommand({
3129
+ name: "help",
3130
+ aliases: ["h", "?"],
3131
+ description: "Show all available commands",
3132
+ category: "session",
3133
+ requiresAuth: false,
3134
+ handler: async () => this.cmdHelp()
3135
+ });
3136
+ this.addCommand({
3137
+ name: "exit",
3138
+ aliases: ["quit", "q"],
3139
+ description: "Exit DevWing",
3140
+ category: "session",
3141
+ requiresAuth: false,
3142
+ handler: async () => this.exitSession()
3143
+ });
3144
+ this.addCommand({
3145
+ name: "clear",
3146
+ aliases: ["cls"],
3147
+ description: "Clear screen and conversation",
3148
+ category: "session",
3149
+ requiresAuth: false,
3150
+ handler: async () => this.cmdClear()
3151
+ });
3152
+ this.addCommand({
3153
+ name: "login",
3154
+ aliases: ["signin"],
3155
+ description: "Authenticate with DevWing",
3156
+ category: "session",
3157
+ requiresAuth: false,
3158
+ handler: async () => this.cmdLogin()
3159
+ });
3160
+ this.addCommand({
3161
+ name: "logout",
3162
+ aliases: ["signout"],
3163
+ description: "Clear credentials and log out",
3164
+ category: "session",
3165
+ requiresAuth: true,
3166
+ handler: async () => this.cmdLogout()
3167
+ });
3168
+ this.addCommand({
3169
+ name: "status",
3170
+ aliases: ["whoami"],
3171
+ description: "Show auth status and profile",
3172
+ category: "session",
3173
+ requiresAuth: false,
3174
+ handler: async () => this.cmdStatus()
3175
+ });
3176
+ this.addCommand({
3177
+ name: "scan",
3178
+ aliases: [],
3179
+ description: "Run security vulnerability scan",
3180
+ category: "tools",
3181
+ requiresAuth: true,
3182
+ handler: async () => this.cmdScan()
3183
+ });
3184
+ this.addCommand({
3185
+ name: "review",
3186
+ aliases: [],
3187
+ description: "Perform code review on recent changes",
3188
+ category: "tools",
3189
+ requiresAuth: true,
3190
+ handler: async () => this.cmdReview()
3191
+ });
3192
+ this.addCommand({
3193
+ name: "explain",
3194
+ aliases: [],
3195
+ description: "Explain code, file, or concept",
3196
+ category: "tools",
3197
+ requiresAuth: true,
3198
+ handler: async (args) => this.cmdExplain(args)
3199
+ });
3200
+ this.addCommand({
3201
+ name: "memory",
3202
+ aliases: ["mem"],
3203
+ description: "Manage project memory (save/show/clear)",
3204
+ category: "tools",
3205
+ requiresAuth: true,
3206
+ handler: async (args) => this.cmdMemory(args)
3207
+ });
3208
+ this.addCommand({
3209
+ name: "mode",
3210
+ aliases: [],
3211
+ description: "Show or set AI mode",
3212
+ category: "config",
3213
+ requiresAuth: false,
3214
+ handler: async (args) => this.cmdMode(args)
3215
+ });
3216
+ this.addCommand({
3217
+ name: "model",
3218
+ aliases: [],
3219
+ description: "Show or set AI model",
3220
+ category: "config",
3221
+ requiresAuth: false,
3222
+ handler: async (args) => this.cmdModel(args)
3223
+ });
3224
+ this.addCommand({
3225
+ name: "models",
3226
+ aliases: [],
3227
+ description: "List all available AI models",
3228
+ category: "config",
3229
+ requiresAuth: false,
3230
+ handler: async () => this.cmdModels()
3231
+ });
3232
+ this.addCommand({
3233
+ name: "usage",
3234
+ aliases: ["stats"],
3235
+ description: "Show token usage statistics",
3236
+ category: "config",
3237
+ requiresAuth: true,
3238
+ handler: async () => this.cmdUsage()
3239
+ });
3240
+ this.addCommand({
3241
+ name: "plans",
3242
+ aliases: ["pricing"],
3243
+ description: "Show available plans and pricing",
3244
+ category: "config",
3245
+ requiresAuth: false,
3246
+ handler: async () => this.cmdPlans()
3247
+ });
3248
+ this.addCommand({
3249
+ name: "context",
3250
+ aliases: ["ctx"],
3251
+ description: "Show loaded codebase context",
3252
+ category: "config",
3253
+ requiresAuth: false,
3254
+ handler: async () => this.cmdContext()
3255
+ });
3256
+ this.addCommand({
3257
+ name: "history",
3258
+ aliases: [],
3259
+ description: "Show conversation history",
3260
+ category: "config",
3261
+ requiresAuth: false,
3262
+ handler: async () => this.cmdHistory()
3263
+ });
3264
+ this.addCommand({
3265
+ name: "config",
3266
+ aliases: ["settings"],
3267
+ description: "Show or set CLI configuration",
3268
+ category: "config",
3269
+ requiresAuth: false,
3270
+ handler: async (args) => this.cmdConfig(args)
3271
+ });
3272
+ }
3273
+ addCommand(cmd) {
3274
+ this.commands.set(cmd.name, cmd);
3275
+ }
3276
+ // ============================================================
3277
+ // COMMAND IMPLEMENTATIONS
3278
+ // ============================================================
3279
+ async cmdHelp() {
3280
+ console.log();
3281
+ const categories = [
3282
+ { key: "session", label: "Session", color: chalk7.bold.green },
3283
+ { key: "tools", label: "AI Tools", color: chalk7.bold.magenta },
3284
+ { key: "config", label: "Configuration", color: chalk7.bold.yellow }
3285
+ ];
3286
+ const lines = [];
3287
+ for (const cat of categories) {
3288
+ lines.push(cat.color(` ${cat.label}`));
3289
+ for (const cmd of this.commands.values()) {
3290
+ if (cmd.category !== cat.key) continue;
3291
+ const aliasStr = cmd.aliases.length > 0 ? chalk7.dim(` (${cmd.aliases.map((a) => "/" + a).join(", ")})`) : "";
3292
+ lines.push(` ${chalk7.cyan("/" + cmd.name.padEnd(12))} ${cmd.description}${aliasStr}`);
3293
+ }
3294
+ lines.push("");
3295
+ }
3296
+ lines.push(chalk7.dim(" Just type a message to chat with DevWing AI."));
3297
+ logger.box(lines.join("\n"), { title: "Commands", color: "cyan" });
3298
+ }
3299
+ async cmdClear() {
3300
+ this.conversationHistory = [];
3301
+ this.sessionContext = null;
3302
+ this.sessionId = null;
3303
+ console.clear();
3304
+ this.printStartupBanner();
3305
+ this.printSystemInfo();
3306
+ }
3307
+ async cmdLogin() {
3308
+ console.log();
3309
+ if (this.isDemo) {
3310
+ await demoLoginCommand();
3311
+ } else {
3312
+ await loginCommand();
3313
+ }
3314
+ await this.checkAuthStatus();
3315
+ if (this.isAuthenticated) {
3316
+ this.rl.setPrompt(this.buildPrompt());
3317
+ }
3318
+ }
3319
+ async cmdLogout() {
3320
+ console.log();
3321
+ if (this.isDemo) {
3322
+ await demoLogoutCommand();
3323
+ } else {
3324
+ await logoutCommand();
3325
+ }
3326
+ this.isAuthenticated = false;
3327
+ this.userProfile = null;
3328
+ this.rl.setPrompt(this.buildPrompt());
3329
+ }
3330
+ async cmdStatus() {
3331
+ console.log();
3332
+ if (!this.isAuthenticated) {
3333
+ logger.info("Not logged in. Type /login to authenticate.");
3334
+ return;
3335
+ }
3336
+ if (this.isDemo) {
3337
+ const table = new Table3({
3338
+ chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3339
+ });
3340
+ table.push(
3341
+ [chalk7.bold("Email"), DEMO_USER2.email],
3342
+ [chalk7.bold("Name"), DEMO_USER2.full_name],
3343
+ [chalk7.bold("Plan"), chalk7.cyan(DEMO_USER2.subscription_plan.toUpperCase())],
3344
+ [chalk7.bold("Verified"), chalk7.green("Yes")],
3345
+ [chalk7.bold("2FA"), chalk7.dim("Disabled")],
3346
+ [chalk7.bold("Workspace"), `${DEMO_WORKSPACE2.name} (${DEMO_WORKSPACE2.plan})`],
3347
+ [chalk7.bold("Project"), DEMO_PROJECT2.name],
3348
+ [chalk7.bold("Tokens Today"), `${DEMO_USER2.tokens_used_today?.toLocaleString()} / 500,000`],
3349
+ [chalk7.bold("Member Since"), new Date(DEMO_USER2.created_at).toLocaleDateString()]
3350
+ );
3351
+ console.log(table.toString());
3352
+ } else {
3353
+ await statusCommand();
3354
+ }
3355
+ }
3356
+ async cmdScan() {
3357
+ console.log();
3358
+ if (this.isDemo) {
3359
+ await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...this.options, mode: "security" });
3360
+ } else {
3361
+ await scanCommand(this.options);
3362
+ }
3363
+ }
3364
+ async cmdReview() {
3365
+ console.log();
3366
+ if (this.isDemo) {
3367
+ await demoReviewCommand();
3368
+ } else {
3369
+ await reviewCommand(this.options);
3370
+ }
3371
+ }
3372
+ async cmdExplain(args) {
3373
+ if (!args) {
3374
+ logger.error("Usage: /explain <file, function, or concept>");
3375
+ logger.info("Example: /explain src/auth/middleware.ts");
3376
+ return;
3377
+ }
3378
+ console.log();
3379
+ if (this.isDemo) {
3380
+ await demoExplainCommand(args);
3381
+ } else {
3382
+ await explainCommand(args, this.options);
3383
+ }
3384
+ }
3385
+ async cmdMemory(args) {
3386
+ const parts = args.split(/\s+/);
3387
+ const sub = parts[0]?.toLowerCase();
3388
+ if (!sub || !["save", "show", "clear"].includes(sub)) {
3389
+ console.log();
3390
+ console.log(` ${chalk7.cyan("/memory show")} \u2014 Show all project memories`);
3391
+ console.log(` ${chalk7.cyan("/memory save <text>")} \u2014 Save a memory`);
3392
+ console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
3393
+ return;
3394
+ }
3395
+ console.log();
3396
+ const content = parts.slice(1).join(" ").trim();
3397
+ if (this.isDemo) {
3398
+ await demoMemoryCommand(sub, content || void 0);
3399
+ } else {
3400
+ await memoryCommand(sub, content || void 0, this.options);
3401
+ }
3402
+ }
3403
+ async cmdMode(args) {
3404
+ const validModes = ["general", "frontend", "backend", "security", "devops"];
3405
+ if (!args) {
3406
+ console.log();
3407
+ console.log(` Current mode: ${chalk7.yellow.bold(this.currentMode)}`);
3408
+ console.log();
3409
+ console.log(" Available modes:");
3410
+ for (const mode2 of validModes) {
3411
+ const indicator = mode2 === this.currentMode ? chalk7.green(" \u25CF") : chalk7.dim(" \u25CB");
3412
+ const desc = mode2 === "general" ? "All-purpose coding assistant" : mode2 === "frontend" ? "React, Vue, CSS, TypeScript, accessibility" : mode2 === "backend" ? "APIs, databases, microservices, system design" : mode2 === "security" ? "OWASP, CVE, penetration testing, compliance" : "Docker, Kubernetes, CI/CD, infrastructure";
3413
+ console.log(` ${indicator} ${chalk7.cyan(mode2.padEnd(12))} ${chalk7.dim(desc)}`);
3414
+ }
3415
+ console.log();
3416
+ console.log(chalk7.dim(` Usage: /mode <name>`));
3417
+ return;
3418
+ }
3419
+ const mode = args.toLowerCase();
3420
+ if (!validModes.includes(mode)) {
3421
+ logger.error(`Invalid mode "${args}". Valid: ${validModes.join(", ")}`);
3422
+ return;
3423
+ }
3424
+ this.currentMode = mode;
3425
+ this.options.mode = mode;
3426
+ logger.success(`Mode set to ${chalk7.yellow.bold(mode)}`);
3427
+ }
3428
+ async cmdModel(args) {
3429
+ if (!args) {
3430
+ console.log();
3431
+ const current = this.currentModel || "auto (selected by mode)";
3432
+ console.log(` Current model: ${chalk7.cyan.bold(current)}`);
3433
+ console.log(chalk7.dim(` Usage: /model <name> | /model auto | /models to list all`));
3434
+ return;
3435
+ }
3436
+ if (args === "auto") {
3437
+ this.currentModel = null;
3438
+ this.options.model = void 0;
3439
+ logger.success("Model set to auto (selected by mode)");
3440
+ return;
3441
+ }
3442
+ this.currentModel = args;
3443
+ this.options.model = args;
3444
+ logger.success(`Model set to ${chalk7.cyan.bold(args)}`);
3445
+ }
3446
+ async cmdModels() {
3447
+ console.log();
3448
+ if (this.isDemo) {
3449
+ const table = new Table3({
3450
+ head: [
3451
+ chalk7.bold("Model"),
3452
+ chalk7.bold("Domain"),
3453
+ chalk7.bold("Status"),
3454
+ chalk7.bold("Min Plan"),
3455
+ chalk7.bold("Context"),
3456
+ chalk7.bold("Speed")
3457
+ ],
3458
+ colWidths: [24, 12, 10, 10, 10, 12]
3459
+ });
3460
+ for (const m of DEMO_MODELS) {
3461
+ const statusColor = m.status === "active" ? chalk7.green : chalk7.yellow;
3462
+ const planColor = m.min_plan === "free" ? chalk7.green : chalk7.cyan;
3463
+ table.push([
3464
+ chalk7.bold(m.display_name),
3465
+ chalk7.yellow(m.domain),
3466
+ statusColor(m.status),
3467
+ planColor(m.min_plan),
3468
+ `${(m.context_window / 1024).toFixed(0)}k`,
3469
+ `${m.tokens_per_sec} tok/s`
3470
+ ]);
3471
+ }
3472
+ console.log(table.toString());
3473
+ console.log();
3474
+ console.log(chalk7.dim(` Set a model: /model devwing-backend-1 | /model auto`));
3475
+ } else {
3476
+ try {
3477
+ const models = await apiClient.getModels();
3478
+ if (models.length === 0) {
3479
+ logger.info("No models available");
3480
+ return;
3481
+ }
3482
+ const table = new Table3({
3483
+ head: [chalk7.bold("Name"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Min Plan")]
3484
+ });
3485
+ for (const m of models) {
3486
+ table.push([m.display_name || m.name, m.domain, m.status, m.min_plan]);
3487
+ }
3488
+ console.log(table.toString());
3489
+ } catch (error) {
3490
+ logger.error("Failed to fetch models: " + error.message);
3491
+ }
3492
+ }
3493
+ }
3494
+ async cmdUsage() {
3495
+ console.log();
3496
+ if (this.isDemo) {
3497
+ console.log(chalk7.bold(" Token Usage"));
3498
+ console.log();
3499
+ const table = new Table3({
3500
+ head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")]
3501
+ });
3502
+ table.push(
3503
+ ["Today", DEMO_USAGE.today.requests.toString(), DEMO_USAGE.today.tokens_in.toLocaleString(), DEMO_USAGE.today.tokens_out.toLocaleString(), `$${DEMO_USAGE.today.cost.toFixed(4)}`],
3504
+ ["This Week", DEMO_USAGE.week.requests.toString(), DEMO_USAGE.week.tokens_in.toLocaleString(), DEMO_USAGE.week.tokens_out.toLocaleString(), `$${DEMO_USAGE.week.cost.toFixed(4)}`],
3505
+ ["This Month", DEMO_USAGE.month.requests.toString(), DEMO_USAGE.month.tokens_in.toLocaleString(), DEMO_USAGE.month.tokens_out.toLocaleString(), `$${DEMO_USAGE.month.cost.toFixed(4)}`]
3506
+ );
3507
+ console.log(table.toString());
3508
+ console.log();
3509
+ const used = DEMO_USER2.tokens_used_today;
3510
+ const limit = 5e5;
3511
+ const pct = Math.round(used / limit * 100);
3512
+ const barWidth = 30;
3513
+ const filled = Math.round(pct / 100 * barWidth);
3514
+ const bar = chalk7.cyan("\u2588".repeat(filled)) + chalk7.dim("\u2591".repeat(barWidth - filled));
3515
+ console.log(` Daily Quota: ${bar} ${pct}% (${used.toLocaleString()} / ${limit.toLocaleString()})`);
3516
+ console.log();
3517
+ console.log(chalk7.bold(" Usage by Model"));
3518
+ console.log();
3519
+ const modelTable = new Table3({
3520
+ head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")]
3521
+ });
3522
+ modelTable.push(
3523
+ ["devwing-backend-1", "312", "421,000", "$1.4721"],
3524
+ ["devwing-security-1", "145", "198,500", "$0.6948"],
3525
+ ["devwing-general-1", "98", "132,200", "$0.4627"],
3526
+ ["devwing-frontend-1", "42", "56,800", "$0.1988"],
3527
+ ["devwing-devops-1", "15", "15,600", "$0.1728"]
3528
+ );
3529
+ console.log(modelTable.toString());
3530
+ } else {
3531
+ try {
3532
+ const usage = await apiClient.getUsage();
3533
+ console.log(` Total Requests: ${usage.total_requests}`);
3534
+ console.log(` Tokens In: ${usage.total_tokens_in.toLocaleString()}`);
3535
+ console.log(` Tokens Out: ${usage.total_tokens_out.toLocaleString()}`);
3536
+ console.log(` Cost: $${usage.total_cost_usd.toFixed(4)}`);
3537
+ } catch (error) {
3538
+ logger.error("Failed to fetch usage: " + error.message);
3539
+ }
3540
+ }
3541
+ }
3542
+ async cmdPlans() {
3543
+ console.log();
3544
+ const table = new Table3({
3545
+ head: [
3546
+ chalk7.bold("Plan"),
3547
+ chalk7.bold("Price"),
3548
+ chalk7.bold("Tokens/Day"),
3549
+ chalk7.bold("Models"),
3550
+ chalk7.bold("Projects"),
3551
+ chalk7.bold("Seats")
3552
+ ],
3553
+ colWidths: [14, 12, 14, 16, 12, 14]
3554
+ });
3555
+ for (const plan of DEMO_PLANS) {
3556
+ const isCurrentPlan = this.isAuthenticated && this.userProfile?.subscription_plan?.toLowerCase() === plan.name.toLowerCase();
3557
+ const nameStr = isCurrentPlan ? chalk7.cyan.bold(plan.name + " \u25CF") : plan.name;
3558
+ table.push([nameStr, plan.price, plan.tokens_day, plan.models, plan.projects, plan.seats]);
3559
+ }
3560
+ console.log(table.toString());
3561
+ console.log();
3562
+ if (this.isAuthenticated) {
3563
+ const currentPlan = this.userProfile?.subscription_plan || "free";
3564
+ console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()}. Upgrade at https://devwing.ai/dashboard/billing`));
3565
+ } else {
3566
+ console.log(chalk7.dim(" Sign up at https://devwing.ai to get started."));
3567
+ }
3568
+ }
3569
+ async cmdContext() {
3570
+ console.log();
3571
+ if (this.isDemo) {
3572
+ const table = new Table3({
3573
+ chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3574
+ });
3575
+ table.push(
3576
+ [chalk7.bold("Working Dir"), chalk7.cyan(process.cwd().replace(process.env.HOME || "", "~"))],
3577
+ [chalk7.bold("Git Branch"), "main"],
3578
+ [chalk7.bold("Files Loaded"), "34"],
3579
+ [chalk7.bold("Framework"), "FastAPI + TypeScript"],
3580
+ [chalk7.bold("Shell"), process.env.SHELL?.split("/").pop() || "unknown"],
3581
+ [chalk7.bold("OS"), process.platform]
3582
+ );
3583
+ console.log(table.toString());
3584
+ console.log();
3585
+ console.log(chalk7.bold(" Top files in context:"));
3586
+ const files = [
3587
+ "src/auth/auth_controller.ts",
3588
+ "src/middleware/jwt.middleware.ts",
3589
+ "src/services/user_service.ts",
3590
+ "src/auth/repositories/user.repository.ts",
3591
+ "package.json",
3592
+ "tsconfig.json",
3593
+ "docker-compose.yml"
3594
+ ];
3595
+ for (const f of files) {
3596
+ console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f)}`);
3597
+ }
3598
+ console.log(chalk7.dim(" ... and 27 more"));
3599
+ } else if (this.sessionContext) {
3600
+ console.log(` Working Dir: ${chalk7.cyan(this.sessionContext.cwd)}`);
3601
+ console.log(` Shell: ${this.sessionContext.shell} | OS: ${this.sessionContext.os}`);
3602
+ console.log(` Files: ${this.sessionContext.files.length}`);
3603
+ if (this.sessionContext.git_branch) {
3604
+ console.log(` Git Branch: ${this.sessionContext.git_branch}`);
3605
+ }
3606
+ console.log();
3607
+ console.log(chalk7.bold(" Files in context:"));
3608
+ for (const f of this.sessionContext.files.slice(0, 10)) {
3609
+ console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f.path)}`);
3610
+ }
3611
+ if (this.sessionContext.files.length > 10) {
3612
+ console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3613
+ }
3614
+ } else {
3615
+ logger.info("No context loaded yet. Send a message to load context.");
3616
+ }
3617
+ }
3618
+ async cmdHistory() {
3619
+ console.log();
3620
+ if (this.conversationHistory.length === 0) {
3621
+ logger.info("No conversation history yet.");
3622
+ return;
3623
+ }
3624
+ for (const msg of this.conversationHistory.slice(-20)) {
3625
+ const label = msg.role === "user" ? chalk7.green.bold("You") : chalk7.cyan.bold("DevWing");
3626
+ const time = chalk7.dim(msg.timestamp.toLocaleTimeString());
3627
+ const preview = msg.content.length > 120 ? msg.content.substring(0, 120) + "..." : msg.content;
3628
+ console.log(` ${time} ${label}: ${preview}`);
3629
+ }
3630
+ }
3631
+ async cmdConfig(args) {
3632
+ if (!args) {
3633
+ console.log();
3634
+ const config = configManager.getAll();
3635
+ const table = new Table3({
3636
+ chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3637
+ });
3638
+ table.push(
3639
+ [chalk7.bold("API URL"), config.apiUrl || chalk7.dim("default")],
3640
+ [chalk7.bold("Workspace"), config.workspaceId || chalk7.dim("none")],
3641
+ [chalk7.bold("Project"), config.projectId || chalk7.dim("none")],
3642
+ [chalk7.bold("Model"), config.model || chalk7.dim("auto")],
3643
+ [chalk7.bold("Mode"), config.mode || chalk7.dim("general")],
3644
+ [chalk7.bold("Config Path"), configManager.getPath()]
3645
+ );
3646
+ console.log(table.toString());
3647
+ console.log();
3648
+ console.log(chalk7.dim(" Usage: /config set <key> <value> | /config get <key>"));
3649
+ return;
3650
+ }
3651
+ const parts = args.split(/\s+/);
3652
+ const sub = parts[0];
3653
+ if (sub === "list") {
3654
+ await configCommand("list");
3655
+ } else if (sub === "get" && parts[1]) {
3656
+ await configCommand("get", parts[1]);
3657
+ } else if (sub === "set" && parts[1] && parts[2]) {
3658
+ await configCommand("set", parts[1], parts[2]);
3659
+ if (parts[1] === "mode") this.currentMode = parts[2];
3660
+ if (parts[1] === "model") this.currentModel = parts[2];
3661
+ } else {
3662
+ logger.error("Usage: /config list | /config get <key> | /config set <key> <value>");
3663
+ }
3664
+ }
3665
+ };
3666
+ async function startSession(options) {
3667
+ const session = new InteractiveSession(options);
3668
+ await session.start();
3669
+ }
3670
+ var program = new Command();
3671
+ var __cliFilename = fileURLToPath(import.meta.url);
3672
+ var __cliDirname = dirname(__cliFilename);
3673
+ function getVersion2() {
3674
+ const paths = [
3675
+ join(__cliDirname, "../package.json"),
3676
+ join(__cliDirname, "../../package.json")
3677
+ ];
3678
+ for (const p of paths) {
3679
+ try {
3680
+ return JSON.parse(readFileSync(p, "utf-8")).version;
3681
+ } catch {
3682
+ continue;
3683
+ }
3684
+ }
3685
+ return "0.1.6";
3686
+ }
3687
+ var VERSION = getVersion2();
3688
+ 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");
3689
+ 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");
3690
+ program.command("login").description("Authenticate with DevWing").action(async () => {
3691
+ if (isDemoMode()) {
3692
+ await demoLoginCommand();
3693
+ } else {
3694
+ await loginCommand();
3695
+ }
3696
+ });
3697
+ program.command("logout").description("Clear stored credentials").action(async () => {
3698
+ if (isDemoMode()) {
3699
+ await demoLogoutCommand();
3700
+ } else {
3701
+ await logoutCommand();
3702
+ }
3703
+ });
3704
+ program.command("status").description("Show authentication status and profile").action(async () => {
3705
+ if (isDemoMode()) {
3706
+ await demoStatusCommand();
3707
+ } else {
3708
+ await statusCommand();
3709
+ }
3710
+ });
3711
+ program.command("chat").description("Start interactive session (like Claude Code)").action(async () => {
3712
+ const opts = program.opts();
3713
+ await startSession(opts);
3714
+ });
3715
+ program.command("scan").description("Run security vulnerability scan").action(async () => {
3716
+ const opts = program.opts();
3717
+ if (isDemoMode()) {
3718
+ await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...opts, mode: "security" });
3719
+ } else {
3720
+ await scanCommand(opts);
3721
+ }
3722
+ });
3723
+ program.command("review").description("Perform code review on recent changes").action(async () => {
3724
+ const opts = program.opts();
3725
+ if (isDemoMode()) {
3726
+ await demoReviewCommand();
3727
+ } else {
3728
+ await reviewCommand(opts);
3729
+ }
3730
+ });
3731
+ program.command("explain <target>").description("Explain code, file, or concept").action(async (target) => {
3732
+ const opts = program.opts();
3733
+ if (isDemoMode()) {
3734
+ await demoExplainCommand(target);
3735
+ } else {
3736
+ await explainCommand(target, opts);
3737
+ }
3738
+ });
3739
+ var memoryCmd = program.command("memory").description("Manage project memory");
3740
+ memoryCmd.command("save <content>").description("Save information to project memory").action(async (content) => {
3741
+ const opts = program.opts();
3742
+ if (isDemoMode()) {
3743
+ await demoMemoryCommand("save", content);
3744
+ } else {
3745
+ await memoryCommand("save", content, opts);
3746
+ }
3747
+ });
3748
+ memoryCmd.command("show").description("Show all project memories").action(async () => {
3749
+ const opts = program.opts();
3750
+ if (isDemoMode()) {
3751
+ await demoMemoryCommand("show");
3752
+ } else {
3753
+ await memoryCommand("show", void 0, opts);
3754
+ }
3755
+ });
3756
+ memoryCmd.command("clear").description("Clear all project memories").action(async () => {
3757
+ const opts = program.opts();
3758
+ if (isDemoMode()) {
3759
+ await demoMemoryCommand("clear");
3760
+ } else {
3761
+ await memoryCommand("clear", void 0, opts);
3762
+ }
3763
+ });
3764
+ var configCmd = program.command("config").description("Manage CLI configuration");
3765
+ configCmd.command("list").description("Show all configuration").action(async () => {
3766
+ await configCommand("list");
3767
+ });
3768
+ configCmd.command("get <key>").description("Get configuration value").action(async (key) => {
3769
+ await configCommand("get", key);
3770
+ });
3771
+ configCmd.command("set <key> <value>").description("Set configuration value").action(async (key, value) => {
3772
+ await configCommand("set", key, value);
3773
+ });
3774
+ program.command("update").description("Check for and install CLI updates").option("--check", "Only check for updates, do not install").option("--force", "Skip confirmation prompt").action(async (options) => {
3775
+ await updateCommand(options);
3776
+ });
3777
+ program.argument("[prompt...]", "Natural language prompt for AI (optional - starts chat mode if empty)").action(async (promptParts) => {
3778
+ const opts = program.opts();
3779
+ if (promptParts.length === 0) {
3780
+ await startSession(opts);
3781
+ return;
3782
+ }
3783
+ const prompt = promptParts.join(" ");
3784
+ if (isDemoMode()) {
3785
+ await demoPromptCommand(prompt, opts);
3786
+ } else {
3787
+ await promptCommand(prompt, opts);
3788
+ }
2467
3789
  });
2468
3790
  program.exitOverride();
2469
3791
  try {