devwing 0.1.8 → 0.1.10

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
@@ -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";
@@ -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
@@ -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
- chalk7.bold("DevWing Chat Commands:"),
1976
- "",
1977
- chalk7.bold.yellow("Navigation & Session:"),
1978
- chalk7.cyan("/help") + " - Show this help message",
1979
- chalk7.cyan("/exit, /quit") + " - Exit chat mode",
1980
- chalk7.cyan("/clear") + " - Clear conversation history",
1981
- chalk7.cyan("/context") + " - Show current context",
1982
- chalk7.cyan("/history") + " - Show conversation history",
1983
- "",
1984
- chalk7.bold.yellow("Specialized Commands:"),
1985
- chalk7.cyan("/scan") + " - Run security vulnerability scan",
1986
- chalk7.cyan("/review") + " - Perform code review",
1987
- chalk7.cyan("/explain <target>") + " - Explain code, file, or concept",
1988
- "",
1989
- chalk7.bold.yellow("Project Memory:"),
1990
- chalk7.cyan("/memory save <content>") + " - Save note to project memory",
1991
- chalk7.cyan("/memory show") + " - Show all project memories",
1992
- chalk7.cyan("/memory clear") + " - Clear all project memories",
1993
- "",
1994
- chalk7.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
- chalk7.bold("Current Context:"),
2017
- "",
2018
- `Working Directory: ${chalk7.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
- chalk7.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
- chalk7.bold.cyan("DevWing AI - Interactive Chat Mode"),
2094
- "",
2095
- "Chat with AI about your codebase. Type your message and press Enter.",
2096
- "",
2097
- chalk7.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: chalk7.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(chalk7.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() {
@@ -2412,7 +2128,7 @@ var DEMO_WORKSPACE = {
2412
2128
  name: "Kano State Government",
2413
2129
  plan: "enterprise"};
2414
2130
  var DEMO_PROJECT = {
2415
- name: "Kano Smart API"};
2131
+ name: "Tallon SaaS ERP"};
2416
2132
  var DEMO_MEMORIES = [
2417
2133
  { id: "mem_001", type: "rule", content: "All API responses must include Hausa language support", created_at: "2026-03-01T10:00:00Z" },
2418
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" },
@@ -2613,7 +2329,7 @@ async function demoMemoryCommand(action, content) {
2613
2329
  logger.stopSpinner();
2614
2330
  logger.success(`Found ${DEMO_MEMORIES.length} memory items`);
2615
2331
  logger.newline();
2616
- const table = new Table({
2332
+ const table = new Table3({
2617
2333
  head: [chalk7.bold("Type"), chalk7.bold("Content"), chalk7.bold("Created")],
2618
2334
  colWidths: [15, 60, 15],
2619
2335
  wordWrap: true
@@ -2752,112 +2468,6 @@ async function demoPromptCommand(prompt, options) {
2752
2468
  await demoGenericPrompt(prompt, options);
2753
2469
  }
2754
2470
  }
2755
- async function demoChatCommand(options) {
2756
- await requireDemoAuth();
2757
- const readline2 = await import('readline');
2758
- console.clear();
2759
- logger.box([
2760
- chalk7.bold.cyan("DevWing AI - Interactive Chat Mode"),
2761
- "",
2762
- "Chat with AI about your codebase. Type your message and press Enter.",
2763
- "",
2764
- chalk7.dim("Type /help for commands | /exit to quit")
2765
- ].join("\n"), { title: "Welcome", color: "cyan" });
2766
- logger.newline();
2767
- logger.startSpinner("Loading codebase context...");
2768
- await sleep(1200);
2769
- logger.succeedSpinner("Context loaded \u2014 34 files");
2770
- logger.newline();
2771
- const rl = readline2.createInterface({
2772
- input: process.stdin,
2773
- output: process.stdout,
2774
- prompt: chalk7.bold.green("You: ")
2775
- });
2776
- rl.on("SIGINT", () => {
2777
- logger.newline();
2778
- logger.info("Exiting chat mode...");
2779
- rl.close();
2780
- process.exit(0);
2781
- });
2782
- rl.prompt();
2783
- rl.on("line", async (input) => {
2784
- const msg = input.trim();
2785
- if (!msg) {
2786
- rl.prompt();
2787
- return;
2788
- }
2789
- if (msg === "/exit" || msg === "/quit") {
2790
- logger.info("Exiting chat mode...");
2791
- rl.close();
2792
- return;
2793
- }
2794
- if (msg === "/help") {
2795
- logger.box([
2796
- chalk7.bold("DevWing Chat Commands:"),
2797
- "",
2798
- chalk7.cyan("/help") + " \u2014 Show this message",
2799
- chalk7.cyan("/exit") + " \u2014 Exit chat mode",
2800
- chalk7.cyan("/scan") + " \u2014 Security scan",
2801
- chalk7.cyan("/review") + " \u2014 Code review",
2802
- chalk7.cyan("/context") + " \u2014 Show loaded context",
2803
- chalk7.cyan("/memory show") + " \u2014 Show project memory"
2804
- ].join("\n"), { title: "Commands", color: "blue" });
2805
- logger.newline();
2806
- rl.prompt();
2807
- return;
2808
- }
2809
- if (msg === "/context") {
2810
- logger.box([
2811
- chalk7.bold("Current Context:"),
2812
- "",
2813
- `Working Directory: ${chalk7.cyan(process.cwd())}`,
2814
- `Shell: zsh | OS: darwin`,
2815
- `Files: 34 | Git Branch: main`,
2816
- "",
2817
- "Top files:",
2818
- " \u2022 src/auth/auth_controller.ts",
2819
- " \u2022 src/middleware/jwt.middleware.ts",
2820
- " \u2022 src/services/user_service.ts",
2821
- " \u2022 package.json",
2822
- " ... and 30 more"
2823
- ].join("\n"), { title: "Context", color: "cyan" });
2824
- logger.newline();
2825
- rl.prompt();
2826
- return;
2827
- }
2828
- if (msg === "/memory show") {
2829
- await demoMemoryCommand("show");
2830
- logger.newline();
2831
- rl.prompt();
2832
- return;
2833
- }
2834
- if (msg === "/scan") {
2835
- logger.newline();
2836
- await demoSecurityScan();
2837
- logger.newline();
2838
- rl.prompt();
2839
- return;
2840
- }
2841
- if (msg === "/review") {
2842
- logger.newline();
2843
- await demoReviewCommand();
2844
- logger.newline();
2845
- rl.prompt();
2846
- return;
2847
- }
2848
- logger.newline();
2849
- logger.info(chalk7.dim("DevWing is thinking..."));
2850
- logger.newline();
2851
- await demoPromptCommand(msg, options);
2852
- logger.newline();
2853
- rl.prompt();
2854
- });
2855
- rl.on("close", () => {
2856
- logger.newline();
2857
- logger.success("Chat session ended. Goodbye!");
2858
- process.exit(0);
2859
- });
2860
- }
2861
2471
  async function demoBackendRefactor() {
2862
2472
  await showContextLoading(47, "backend");
2863
2473
  logger.startSpinner("DevWing is thinking...");
@@ -3149,10 +2759,918 @@ async function requireDemoAuth() {
3149
2759
  function isDemoMode() {
3150
2760
  return process.env.DEVWING_DEMO === "1" || process.env.DEVWING_DEMO === "true";
3151
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: "Tallon SaaS ERP"};
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
+ }
3152
3670
  var program = new Command();
3153
3671
  var __cliFilename = fileURLToPath(import.meta.url);
3154
3672
  var __cliDirname = dirname(__cliFilename);
3155
- function getVersion() {
3673
+ function getVersion2() {
3156
3674
  const paths = [
3157
3675
  join(__cliDirname, "../package.json"),
3158
3676
  join(__cliDirname, "../../package.json")
@@ -3166,7 +3684,7 @@ function getVersion() {
3166
3684
  }
3167
3685
  return "0.1.6";
3168
3686
  }
3169
- var VERSION = getVersion();
3687
+ var VERSION = getVersion2();
3170
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");
3171
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");
3172
3690
  program.command("login").description("Authenticate with DevWing").action(async () => {
@@ -3190,13 +3708,9 @@ program.command("status").description("Show authentication status and profile").
3190
3708
  await statusCommand();
3191
3709
  }
3192
3710
  });
3193
- program.command("chat").description("Start interactive chat mode (like Claude Code)").action(async () => {
3711
+ program.command("chat").description("Start interactive session (like Claude Code)").action(async () => {
3194
3712
  const opts = program.opts();
3195
- if (isDemoMode()) {
3196
- await demoChatCommand(opts);
3197
- } else {
3198
- await chatCommand(opts);
3199
- }
3713
+ await startSession(opts);
3200
3714
  });
3201
3715
  program.command("scan").description("Run security vulnerability scan").action(async () => {
3202
3716
  const opts = program.opts();
@@ -3263,11 +3777,7 @@ program.command("update").description("Check for and install CLI updates").optio
3263
3777
  program.argument("[prompt...]", "Natural language prompt for AI (optional - starts chat mode if empty)").action(async (promptParts) => {
3264
3778
  const opts = program.opts();
3265
3779
  if (promptParts.length === 0) {
3266
- if (isDemoMode()) {
3267
- await demoChatCommand(opts);
3268
- } else {
3269
- await chatCommand(opts);
3270
- }
3780
+ await startSession(opts);
3271
3781
  return;
3272
3782
  }
3273
3783
  const prompt = promptParts.join(" ");