devwing 0.1.22 → 0.1.24

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
@@ -22,7 +22,6 @@ import util from 'util';
22
22
  import Table3 from 'cli-table3';
23
23
  import { fileURLToPath } from 'url';
24
24
  import semver from 'semver';
25
- import readline from 'readline';
26
25
 
27
26
  var SERVICE_NAME = "DevWing CLI";
28
27
  var API_KEY_ACCOUNT = "api-key";
@@ -2772,7 +2771,7 @@ function isDemoMode() {
2772
2771
  }
2773
2772
  var __sessionFile = fileURLToPath(import.meta.url);
2774
2773
  var __sessionDir = dirname(__sessionFile);
2775
- var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2774
+ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
2776
2775
  function getVersion() {
2777
2776
  const paths = [
2778
2777
  join(__sessionDir, "../package.json"),
@@ -2787,10 +2786,10 @@ function getVersion() {
2787
2786
  continue;
2788
2787
  }
2789
2788
  }
2790
- return "0.1.22";
2789
+ return "0.1.24";
2791
2790
  }
2792
2791
  var DEMO_USER2 = {
2793
- id: "usr_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
2792
+ id: "usr_a1b2c3d4",
2794
2793
  email: "aliyu@devwing.ai",
2795
2794
  full_name: "Aliyu Mohammed",
2796
2795
  subscription_plan: "pro",
@@ -2801,18 +2800,16 @@ var DEMO_USER2 = {
2801
2800
  tokens_used_today: 47832,
2802
2801
  tokens_reset_at: "2026-04-10T00:00:00Z"
2803
2802
  };
2804
- var DEMO_WORKSPACE2 = {
2805
- name: "Kano State Government"};
2806
- var DEMO_PROJECT2 = {
2807
- name: "Tallon SaaS ERP"};
2803
+ var DEMO_WORKSPACE2 = { name: "Kano State Government"};
2804
+ var DEMO_PROJECT2 = { name: "Tallon SaaS ERP"};
2808
2805
  var DEMO_MODELS = [
2809
- { 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" },
2810
- { 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" },
2811
- { 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" },
2812
- { 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" },
2813
- { 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" },
2814
- { name: "devwing-mobile-1", display_name: "DevWing Mobile", domain: "mobile", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 72, version: "0.8" },
2815
- { name: "devwing-data-1", display_name: "DevWing Data", domain: "data", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 68, version: "0.7" }
2806
+ { name: "devwing-general-1", display_name: "DevWing General", domain: "general", status: "active", min_plan: "free", context_window: 32768, tokens_per_sec: 85 },
2807
+ { name: "devwing-frontend-1", display_name: "DevWing Frontend", domain: "frontend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 78 },
2808
+ { name: "devwing-backend-1", display_name: "DevWing Backend", domain: "backend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 82 },
2809
+ { name: "devwing-security-1", display_name: "DevWing Security", domain: "security", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 74 },
2810
+ { name: "devwing-devops-1", display_name: "DevWing DevOps", domain: "devops", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 70 },
2811
+ { name: "devwing-mobile-1", display_name: "DevWing Mobile", domain: "mobile", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 72 },
2812
+ { name: "devwing-data-1", display_name: "DevWing Data", domain: "data", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 68 }
2816
2813
  ];
2817
2814
  var DEMO_PLANS = [
2818
2815
  { name: "Free", price: "$0/mo", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
@@ -2825,6 +2822,65 @@ var DEMO_USAGE = {
2825
2822
  week: { requests: 147, tokens_in: 198340, tokens_out: 102890, cost: 0.7213 },
2826
2823
  month: { requests: 612, tokens_in: 824100, tokens_out: 428750, cost: 3.0012 }
2827
2824
  };
2825
+ function readLine() {
2826
+ return new Promise((resolve) => {
2827
+ try {
2828
+ if (typeof process.stdin.setRawMode === "function") {
2829
+ process.stdin.setRawMode(false);
2830
+ }
2831
+ } catch {
2832
+ }
2833
+ process.stdin.resume();
2834
+ process.stdin.setEncoding("utf8");
2835
+ let buf = "";
2836
+ const onData = (chunk) => {
2837
+ for (const ch of chunk) {
2838
+ if (ch === "\n" || ch === "\r") {
2839
+ cleanup();
2840
+ resolve(buf);
2841
+ return;
2842
+ } else if (ch === "") {
2843
+ cleanup();
2844
+ resolve("");
2845
+ return;
2846
+ } else if (ch === "\x7F" || ch === "\b") {
2847
+ if (buf.length > 0) {
2848
+ buf = buf.slice(0, -1);
2849
+ process.stdout.write("\b \b");
2850
+ }
2851
+ } else if (ch >= " ") {
2852
+ buf += ch;
2853
+ process.stdout.write(ch);
2854
+ }
2855
+ }
2856
+ };
2857
+ const onEnd = () => {
2858
+ cleanup();
2859
+ resolve("");
2860
+ };
2861
+ const cleanup = () => {
2862
+ process.stdin.removeListener("data", onData);
2863
+ process.stdin.removeListener("end", onEnd);
2864
+ process.stdin.pause();
2865
+ };
2866
+ process.stdin.on("data", onData);
2867
+ process.stdin.once("end", onEnd);
2868
+ });
2869
+ }
2870
+ function restoreStdin() {
2871
+ try {
2872
+ if (typeof process.stdin.setRawMode === "function") {
2873
+ try {
2874
+ process.stdin.setRawMode(false);
2875
+ } catch {
2876
+ }
2877
+ }
2878
+ process.stdin.removeAllListeners("keypress");
2879
+ process.stdin.removeAllListeners("data");
2880
+ if (process.stdin.isPaused()) process.stdin.resume();
2881
+ } catch {
2882
+ }
2883
+ }
2828
2884
  var InteractiveSession = class {
2829
2885
  isAuthenticated = false;
2830
2886
  userProfile = null;
@@ -2835,12 +2891,10 @@ var InteractiveSession = class {
2835
2891
  sessionId = null;
2836
2892
  isDemo;
2837
2893
  options;
2838
- rl;
2839
2894
  commands = /* @__PURE__ */ new Map();
2840
- sigintCount = 0;
2841
- sigintTimer = null;
2842
2895
  version;
2843
- isRunningCommand = false;
2896
+ running = true;
2897
+ ctrlCCount = 0;
2844
2898
  constructor(options) {
2845
2899
  this.options = options;
2846
2900
  this.isDemo = isDemoMode();
@@ -2849,109 +2903,48 @@ var InteractiveSession = class {
2849
2903
  if (options.model) this.currentModel = options.model;
2850
2904
  this.registerCommands();
2851
2905
  }
2852
- // ============================================================
2853
- // START
2854
- // ============================================================
2906
+ // ─── START ───────────────────────────────────────────────
2855
2907
  async start() {
2856
2908
  console.clear();
2857
- this.printStartupBanner();
2858
- await this.checkAuthStatus();
2909
+ this.printBanner();
2910
+ await this.checkAuth();
2859
2911
  this.printSystemInfo();
2860
- this.createReadline();
2861
2912
  console.log();
2862
- this.prompt();
2863
- }
2864
- // ============================================================
2865
- // READLINE — created once, never destroyed
2866
- // ============================================================
2867
- createReadline() {
2868
- this.rl = readline.createInterface({
2869
- input: process.stdin,
2870
- output: process.stdout,
2871
- prompt: "",
2872
- terminal: true
2873
- });
2874
- this.rl.on("SIGINT", () => {
2875
- if (this.isRunningCommand) {
2876
- process.stdout.write("\n");
2877
- return;
2878
- }
2879
- this.sigintCount++;
2880
- if (this.sigintCount >= 2) {
2881
- this.exitSession();
2882
- return;
2913
+ while (this.running) {
2914
+ this.printPrompt();
2915
+ const input = await readLine();
2916
+ process.stdout.write("\n");
2917
+ if (input === "") {
2918
+ this.ctrlCCount++;
2919
+ if (this.ctrlCCount >= 2) {
2920
+ this.exit();
2921
+ return;
2922
+ }
2923
+ console.log(chalk7.dim(" Press Ctrl+C again to exit, or type /exit"));
2924
+ setTimeout(() => {
2925
+ this.ctrlCCount = 0;
2926
+ }, 1500);
2927
+ continue;
2883
2928
  }
2884
- process.stdout.write(chalk7.dim("\n Press Ctrl+C again to exit, or type /exit\n"));
2885
- this.sigintTimer = setTimeout(() => {
2886
- this.sigintCount = 0;
2887
- }, 1500);
2888
- this.prompt();
2889
- });
2890
- this.rl.on("line", async (input) => {
2891
- if (this.isRunningCommand) return;
2929
+ this.ctrlCCount = 0;
2892
2930
  const trimmed = input.trim();
2893
- if (!trimmed) {
2894
- this.prompt();
2895
- return;
2896
- }
2897
- this.isRunningCommand = true;
2898
- this.rl.pause();
2931
+ if (!trimmed) continue;
2899
2932
  try {
2900
2933
  if (trimmed.startsWith("/")) {
2901
- await this.handleSlashCommand(trimmed);
2934
+ await this.handleSlash(trimmed);
2902
2935
  } else {
2903
- await this.handleAIPrompt(trimmed);
2936
+ await this.handlePrompt(trimmed);
2904
2937
  }
2905
- } catch (error) {
2906
- logger.error(error.message || "An error occurred");
2938
+ } catch (err) {
2939
+ logger.error(err.message || "An error occurred");
2907
2940
  }
2908
- this.restoreStdin();
2909
- await sleep2(60);
2910
- this.isRunningCommand = false;
2911
- this.rl.resume();
2912
- this.prompt();
2913
- });
2914
- this.rl.on("close", () => {
2915
- if (!this.isRunningCommand) {
2916
- this.exitSession();
2917
- }
2918
- });
2919
- }
2920
- // Pause readline, run fn (which may use inquirer), restore
2921
- async withInquirer(fn) {
2922
- this.rl.pause();
2923
- try {
2924
- return await fn();
2925
- } finally {
2926
- this.restoreStdin();
2927
- await sleep2(80);
2928
- this.rl.resume();
2941
+ restoreStdin();
2942
+ await sleep2(50);
2929
2943
  }
2930
2944
  }
2931
- restoreStdin() {
2932
- try {
2933
- if (typeof process.stdin.setRawMode === "function") {
2934
- try {
2935
- process.stdin.setRawMode(false);
2936
- } catch {
2937
- }
2938
- }
2939
- process.stdin.removeAllListeners("keypress");
2940
- process.stdin.removeAllListeners("data");
2941
- if (process.stdin.isPaused()) process.stdin.resume();
2942
- } catch {
2943
- }
2944
- }
2945
- prompt() {
2946
- const p = this.buildPrompt();
2947
- process.stdout.write(p);
2948
- this.rl.setPrompt(p);
2949
- }
2950
- // ============================================================
2951
- // STARTUP UI — Claude Code style
2952
- // ============================================================
2953
- printStartupBanner() {
2954
- const banner = gradient.pastel.multiline([
2945
+ // ─── BANNER ──────────────────────────────────────────────
2946
+ printBanner() {
2947
+ const art = gradient.pastel.multiline([
2955
2948
  "",
2956
2949
  " \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 ",
2957
2950
  " \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 ",
@@ -2961,102 +2954,81 @@ var InteractiveSession = class {
2961
2954
  " \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 ",
2962
2955
  ""
2963
2956
  ].join("\n"));
2964
- console.log(banner);
2957
+ console.log(art);
2965
2958
  console.log(
2966
- chalk7.dim(" Your AI Wingman in the Terminal") + chalk7.dim(" ".repeat(18)) + chalk7.dim(`v${this.version}`)
2959
+ chalk7.dim(" Your AI Wingman in the Terminal") + " " + chalk7.dim(`v${this.version}`)
2967
2960
  );
2968
2961
  console.log();
2969
2962
  }
2963
+ // ─── SYSTEM INFO PANEL ───────────────────────────────────
2970
2964
  printSystemInfo() {
2971
- const W = 64;
2972
- const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
2973
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
2974
- const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
2975
- const row = (label, value) => {
2976
- const labelPart2 = chalk7.dim("\u2502 ") + chalk7.bold(label.padEnd(11)) + chalk7.dim(" \u2502 ");
2977
- const rawValue = value.replace(/\x1B\[[0-9;]*m/g, "");
2978
- const padLen2 = W - 4 - 11 - 3 - rawValue.length;
2979
- return labelPart2 + value + " ".repeat(Math.max(0, padLen2)) + chalk7.dim(" \u2502");
2965
+ const W = 62;
2966
+ const ln = (s = "") => {
2967
+ const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
2968
+ const pad = W - 4 - raw.length;
2969
+ return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
2980
2970
  };
2971
+ const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
2972
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
2973
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
2974
+ const kv = (k, v) => ln(chalk7.bold(k.padEnd(12)) + v);
2981
2975
  console.log(top);
2982
2976
  if (this.isAuthenticated && this.userProfile) {
2983
2977
  const name = this.userProfile.full_name || this.userProfile.email;
2984
2978
  const plan = (this.userProfile.subscription_plan || "free").toUpperCase();
2985
- const planBadge = plan === "PRO" ? chalk7.bgCyan.black(` ${plan} `) : plan === "ENTERPRISE" ? chalk7.bgMagenta.white(` ${plan} `) : plan === "TEAM" ? chalk7.bgYellow.black(` ${plan} `) : chalk7.bgGray.white(` ${plan} `);
2986
- console.log(row("Logged in", chalk7.green("\u25CF ") + chalk7.white(name) + " " + planBadge));
2979
+ const badge = plan === "PRO" ? chalk7.bgCyan.black(` ${plan} `) : plan === "ENTERPRISE" ? chalk7.bgMagenta.white(` ${plan} `) : plan === "TEAM" ? chalk7.bgYellow.black(` ${plan} `) : chalk7.bgGray.white(` ${plan} `);
2980
+ console.log(ln(chalk7.bold("Auth".padEnd(12)) + chalk7.green("\u25CF ") + chalk7.white(name) + " " + badge));
2987
2981
  } else {
2988
- console.log(row("Auth", chalk7.red("\u25CF ") + chalk7.dim("Not logged in \u2014 type ") + chalk7.cyan("/login")));
2982
+ console.log(ln(chalk7.bold("Auth".padEnd(12)) + chalk7.red("\u25CF ") + chalk7.dim("Not logged in") + " " + chalk7.cyan("/login")));
2989
2983
  }
2990
2984
  console.log(div);
2991
- const modeVal = chalk7.yellow.bold(this.currentMode);
2992
- const modelVal = this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto");
2993
- const labelPart = chalk7.dim("\u2502 ") + chalk7.bold("Mode".padEnd(11)) + chalk7.dim(" \u2502 ");
2994
- const raw1 = this.currentMode;
2995
- const mid = " " + chalk7.bold("Model".padEnd(8)) + " " + modelVal;
2996
- const raw2 = mid.replace(/\x1B\[[0-9;]*m/g, "");
2997
- const padLen = W - 4 - 11 - 3 - raw1.length - raw2.length;
2998
- console.log(labelPart + modeVal + mid + " ".repeat(Math.max(0, padLen)) + chalk7.dim(" \u2502"));
2985
+ console.log(kv("Mode", chalk7.yellow(this.currentMode) + " " + chalk7.bold("Model".padEnd(8)) + (this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto"))));
2999
2986
  console.log(div);
3000
2987
  if (this.isDemo) {
3001
- console.log(row("Project", chalk7.cyan(DEMO_PROJECT2.name)));
2988
+ console.log(kv("Project", chalk7.cyan(DEMO_PROJECT2.name)));
3002
2989
  } else {
3003
2990
  const pid = configManager.getProjectId();
3004
- console.log(row("Project", pid ? chalk7.cyan(pid) : chalk7.dim("none")));
2991
+ console.log(kv("Project", pid ? chalk7.cyan(pid) : chalk7.dim("none")));
3005
2992
  }
3006
2993
  const cwd = process.cwd().replace(process.env.HOME || "", "~");
3007
- console.log(row("Directory", chalk7.dim(cwd)));
2994
+ console.log(kv("Directory", chalk7.dim(cwd)));
3008
2995
  if (this.isDemo) {
3009
2996
  console.log(div);
3010
- console.log(row("Demo", chalk7.yellow("\u25C6 ") + chalk7.yellow("Demo mode") + chalk7.dim(" \u2014 no backend required")));
2997
+ console.log(ln(chalk7.yellow("\u25C6 ") + chalk7.yellow("Demo mode") + chalk7.dim(" \u2014 no backend required")));
3011
2998
  }
3012
- console.log(bottom);
2999
+ console.log(bot);
3013
3000
  console.log();
3014
- console.log(
3015
- chalk7.dim(" Chat freely, or use ") + chalk7.cyan("/help") + chalk7.dim(" to see all commands.")
3016
- );
3001
+ console.log(chalk7.dim(" Type a message to chat, or ") + chalk7.cyan("/help") + chalk7.dim(" for commands."));
3017
3002
  }
3018
- // ============================================================
3019
- // PROMPT LINE — Claude Code style
3020
- // ============================================================
3021
- buildPrompt() {
3022
- const parts = [];
3023
- const sep = chalk7.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
3024
- let nameTag = "";
3003
+ // ─── PROMPT LINE ─────────────────────────────────────────
3004
+ printPrompt() {
3005
+ const sep = chalk7.dim("\n " + "\u2500".repeat(50));
3006
+ const icon = chalk7.bold.cyan("\u2B21");
3007
+ const name = chalk7.bold.white("devwing");
3008
+ const mode = chalk7.dim("[") + chalk7.yellow(this.currentMode) + chalk7.dim("]");
3009
+ let user = "";
3025
3010
  if (this.isAuthenticated && this.userProfile) {
3026
- const first = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0];
3027
- nameTag = chalk7.dim("@") + chalk7.green(first.toLowerCase());
3011
+ const first = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0].toLowerCase();
3012
+ user = " " + chalk7.dim("@") + chalk7.green(first);
3028
3013
  }
3029
- const modeTag = chalk7.dim("[") + chalk7.yellow(this.currentMode) + chalk7.dim("]");
3030
- const demoTag = this.isDemo ? chalk7.dim.yellow(" demo") : "";
3031
- const icon = chalk7.bold.cyan("\u2B21");
3032
- const label = chalk7.bold.white("devwing");
3033
- parts.push(`
3034
- ${sep}`);
3035
- parts.push(` ${icon} ${label} ${modeTag}${nameTag ? " " + nameTag : ""}${demoTag}`);
3036
- parts.push(" " + chalk7.bold.cyan("\u276F") + " ");
3037
- return parts.join("\n");
3038
- }
3039
- // ============================================================
3040
- // AUTH CHECK
3041
- // ============================================================
3042
- async checkAuthStatus() {
3014
+ const demo = this.isDemo ? chalk7.dim.yellow(" demo") : "";
3015
+ process.stdout.write(`${sep}
3016
+ ${icon} ${name} ${mode}${user}${demo}
3017
+ ${chalk7.bold.cyan("\u276F")} `);
3018
+ }
3019
+ // ─── AUTH CHECK ──────────────────────────────────────────
3020
+ async checkAuth() {
3043
3021
  try {
3044
- const apiKey = await configManager.getApiKey();
3022
+ const key = await configManager.getApiKey();
3045
3023
  if (this.isDemo) {
3046
- if (apiKey) {
3047
- this.isAuthenticated = true;
3048
- this.userProfile = { ...DEMO_USER2 };
3049
- } else {
3050
- this.isAuthenticated = false;
3051
- this.userProfile = null;
3052
- }
3024
+ this.isAuthenticated = !!key;
3025
+ this.userProfile = key ? { ...DEMO_USER2 } : null;
3053
3026
  return;
3054
3027
  }
3055
- if (!apiKey) return;
3028
+ if (!key) return;
3056
3029
  try {
3057
- const profile = await apiClient.getProfile();
3030
+ this.userProfile = await apiClient.getProfile();
3058
3031
  this.isAuthenticated = true;
3059
- this.userProfile = profile;
3060
3032
  } catch {
3061
3033
  this.isAuthenticated = true;
3062
3034
  this.userProfile = null;
@@ -3066,10 +3038,8 @@ ${sep}`);
3066
3038
  this.userProfile = null;
3067
3039
  }
3068
3040
  }
3069
- // ============================================================
3070
- // SLASH COMMAND DISPATCH
3071
- // ============================================================
3072
- async handleSlashCommand(input) {
3041
+ // ─── SLASH COMMAND DISPATCH ──────────────────────────────
3042
+ async handleSlash(input) {
3073
3043
  const parts = input.split(/\s+/);
3074
3044
  const cmdName = parts[0].substring(1).toLowerCase();
3075
3045
  const args = parts.slice(1).join(" ").trim();
@@ -3087,19 +3057,17 @@ ${sep}`);
3087
3057
  }
3088
3058
  if (command.requiresAuth && !this.isAuthenticated) {
3089
3059
  console.log();
3090
- logger.warn("You need to be logged in for this command. Type /login first.");
3060
+ logger.warn("You need to be logged in. Type /login first.");
3091
3061
  return;
3092
3062
  }
3093
3063
  console.log();
3094
3064
  await command.handler(args);
3095
3065
  }
3096
- // ============================================================
3097
- // AI PROMPT HANDLER
3098
- // ============================================================
3099
- async handleAIPrompt(input) {
3066
+ // ─── AI PROMPT ───────────────────────────────────────────
3067
+ async handlePrompt(input) {
3100
3068
  if (!this.isAuthenticated) {
3101
3069
  console.log();
3102
- logger.warn("You need to log in to chat with AI. Type /login to authenticate.");
3070
+ logger.warn("You need to log in first. Type /login to authenticate.");
3103
3071
  return;
3104
3072
  }
3105
3073
  console.log();
@@ -3114,12 +3082,12 @@ ${sep}`);
3114
3082
  if (!this.sessionContext) {
3115
3083
  logger.startSpinner("Loading codebase context...");
3116
3084
  try {
3117
- const collector = new ContextCollector(process.cwd());
3118
- this.sessionContext = await collector.collect(input);
3085
+ const col = new ContextCollector(process.cwd());
3086
+ this.sessionContext = await col.collect(input);
3119
3087
  logger.succeedSpinner(`Context loaded \u2014 ${this.sessionContext.files.length} files`);
3120
- } catch (error) {
3088
+ } catch (err) {
3121
3089
  logger.failSpinner("Failed to load context");
3122
- logger.error(error.message);
3090
+ logger.error(err.message);
3123
3091
  return;
3124
3092
  }
3125
3093
  }
@@ -3127,8 +3095,8 @@ ${sep}`);
3127
3095
  logger.info(chalk7.dim("DevWing is thinking..."));
3128
3096
  console.log();
3129
3097
  try {
3130
- const request = {
3131
- prompt: this.buildPromptWithHistory(),
3098
+ const req = {
3099
+ prompt: this.buildHistory(),
3132
3100
  mode: this.currentMode,
3133
3101
  model: this.currentModel || void 0,
3134
3102
  project_id: this.options.project || configManager.getProjectId(),
@@ -3136,149 +3104,130 @@ ${sep}`);
3136
3104
  stream: true,
3137
3105
  max_tokens: 4096
3138
3106
  };
3139
- let assistantResponse = "";
3107
+ let reply = "";
3140
3108
  streamingRenderer.reset();
3141
3109
  streamingRenderer.clearPendingTools();
3142
- for await (const message of apiClient.streamCompletion(request)) {
3143
- await streamingRenderer.processMessage(message);
3144
- if (message.type === "token" && message.content) {
3145
- assistantResponse += message.content;
3146
- }
3147
- if (message.session_id) {
3148
- this.sessionId = message.session_id;
3149
- }
3110
+ for await (const msg of apiClient.streamCompletion(req)) {
3111
+ await streamingRenderer.processMessage(msg);
3112
+ if (msg.type === "token" && msg.content) reply += msg.content;
3113
+ if (msg.session_id) this.sessionId = msg.session_id;
3150
3114
  }
3151
- if (assistantResponse) {
3152
- this.conversationHistory.push({ role: "assistant", content: assistantResponse, timestamp: /* @__PURE__ */ new Date() });
3115
+ if (reply) {
3116
+ this.conversationHistory.push({ role: "assistant", content: reply, timestamp: /* @__PURE__ */ new Date() });
3153
3117
  }
3154
- const pendingTools = streamingRenderer.getPendingToolCalls();
3155
- if (pendingTools.length > 0) {
3118
+ const pending = streamingRenderer.getPendingToolCalls();
3119
+ if (pending.length > 0) {
3156
3120
  logger.newline();
3157
- logger.info("Executing tools...");
3158
- const toolResults = await streamingRenderer.executePendingTools();
3121
+ const results = await streamingRenderer.executePendingTools();
3159
3122
  if (this.sessionId) {
3160
- for await (const message of apiClient.continueCompletion(this.sessionId, toolResults)) {
3161
- await streamingRenderer.processMessage(message);
3123
+ for await (const msg of apiClient.continueCompletion(this.sessionId, results)) {
3124
+ await streamingRenderer.processMessage(msg);
3162
3125
  }
3163
3126
  }
3164
3127
  }
3165
3128
  console.log();
3166
- } catch (error) {
3167
- logger.error(error.message || "AI request failed");
3129
+ } catch (err) {
3130
+ logger.error(err.message || "AI request failed");
3168
3131
  }
3169
3132
  }
3170
- buildPromptWithHistory() {
3171
- const history = this.conversationHistory;
3172
- if (history.length <= 1) return history[history.length - 1]?.content || "";
3173
- const recent = history.slice(-10);
3174
- const context = recent.slice(0, -1).map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n\n");
3133
+ buildHistory() {
3134
+ const h = this.conversationHistory;
3135
+ if (h.length <= 1) return h[h.length - 1]?.content || "";
3136
+ const recent = h.slice(-10);
3137
+ const ctx = recent.slice(0, -1).map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n\n");
3175
3138
  return `Previous conversation:
3176
- ${context}
3139
+ ${ctx}
3177
3140
 
3178
3141
  Current question:
3179
3142
  ${recent[recent.length - 1].content}`;
3180
3143
  }
3181
- // ============================================================
3182
- // EXIT
3183
- // ============================================================
3184
- exitSession() {
3185
- if (this.sigintTimer) clearTimeout(this.sigintTimer);
3186
- this.restoreStdin();
3144
+ // ─── EXIT ────────────────────────────────────────────────
3145
+ exit() {
3146
+ restoreStdin();
3187
3147
  console.log();
3188
- console.log(chalk7.dim(" Goodbye! Happy shipping. \u{1F680}"));
3148
+ console.log(chalk7.dim(" Goodbye! Happy shipping."));
3189
3149
  console.log();
3190
3150
  process.exit(0);
3191
3151
  }
3192
- // ============================================================
3193
- // COMMAND REGISTRATION
3194
- // ============================================================
3152
+ // ─── COMMAND REGISTRATION ────────────────────────────────
3195
3153
  registerCommands() {
3196
- this.addCommand({ name: "help", aliases: ["h", "?"], description: "Show all available commands", category: "session", requiresAuth: false, handler: async () => this.cmdHelp() });
3197
- this.addCommand({ name: "exit", aliases: ["quit", "q"], description: "Exit DevWing", category: "session", requiresAuth: false, handler: async () => this.exitSession() });
3198
- this.addCommand({ name: "clear", aliases: ["cls"], description: "Clear screen and conversation", category: "session", requiresAuth: false, handler: async () => this.cmdClear() });
3199
- this.addCommand({ name: "login", aliases: ["signin"], description: "Authenticate with DevWing", category: "session", requiresAuth: false, handler: async () => this.cmdLogin() });
3200
- this.addCommand({ name: "logout", aliases: ["signout"], description: "Clear credentials and log out", category: "session", requiresAuth: true, handler: async () => this.cmdLogout() });
3201
- this.addCommand({ name: "status", aliases: ["whoami"], description: "Show auth status and profile", category: "session", requiresAuth: false, handler: async () => this.cmdStatus() });
3202
- this.addCommand({ name: "scan", aliases: [], description: "Run security vulnerability scan", category: "tools", requiresAuth: true, handler: async () => this.cmdScan() });
3203
- this.addCommand({ name: "review", aliases: [], description: "Perform code review on recent changes", category: "tools", requiresAuth: true, handler: async () => this.cmdReview() });
3204
- this.addCommand({ name: "explain", aliases: [], description: "Explain code, file, or concept", category: "tools", requiresAuth: true, handler: async (a) => this.cmdExplain(a) });
3205
- this.addCommand({ name: "memory", aliases: ["mem"], description: "Manage project memory (save/show/clear)", category: "tools", requiresAuth: true, handler: async (a) => this.cmdMemory(a) });
3206
- this.addCommand({ name: "mode", aliases: [], description: "Show or set AI mode", category: "config", requiresAuth: false, handler: async (a) => this.cmdMode(a) });
3207
- this.addCommand({ name: "model", aliases: [], description: "Show or set AI model", category: "config", requiresAuth: false, handler: async (a) => this.cmdModel(a) });
3208
- this.addCommand({ name: "models", aliases: [], description: "List all available AI models", category: "config", requiresAuth: false, handler: async () => this.cmdModels() });
3209
- this.addCommand({ name: "usage", aliases: ["stats"], description: "Show token usage statistics", category: "config", requiresAuth: true, handler: async () => this.cmdUsage() });
3210
- this.addCommand({ name: "plans", aliases: ["pricing"], description: "Show available plans and pricing", category: "config", requiresAuth: false, handler: async () => this.cmdPlans() });
3211
- this.addCommand({ name: "context", aliases: ["ctx"], description: "Show loaded codebase context", category: "config", requiresAuth: false, handler: async () => this.cmdContext() });
3212
- this.addCommand({ name: "history", aliases: [], description: "Show conversation history", category: "config", requiresAuth: false, handler: async () => this.cmdHistory() });
3213
- this.addCommand({ name: "config", aliases: ["settings"], description: "Show or set CLI configuration", category: "config", requiresAuth: false, handler: async (a) => this.cmdConfig(a) });
3214
- }
3215
- addCommand(cmd) {
3216
- this.commands.set(cmd.name, cmd);
3217
- }
3218
- // ============================================================
3219
- // COMMAND IMPLEMENTATIONS
3220
- // ============================================================
3154
+ const add = (c) => this.commands.set(c.name, c);
3155
+ add({ name: "help", aliases: ["h", "?"], description: "Show all available commands", category: "session", requiresAuth: false, handler: async () => this.cmdHelp() });
3156
+ add({ name: "exit", aliases: ["quit", "q"], description: "Exit DevWing", category: "session", requiresAuth: false, handler: async () => this.exit() });
3157
+ add({ name: "clear", aliases: ["cls"], description: "Clear screen and reset conversation", category: "session", requiresAuth: false, handler: async () => this.cmdClear() });
3158
+ add({ name: "login", aliases: ["signin"], description: "Authenticate with DevWing", category: "session", requiresAuth: false, handler: async () => this.cmdLogin() });
3159
+ add({ name: "logout", aliases: ["signout"], description: "Clear credentials and log out", category: "session", requiresAuth: true, handler: async () => this.cmdLogout() });
3160
+ add({ name: "status", aliases: ["whoami"], description: "Show auth status and profile", category: "session", requiresAuth: false, handler: async () => this.cmdStatus() });
3161
+ add({ name: "scan", aliases: [], description: "Run security vulnerability scan", category: "tools", requiresAuth: true, handler: async () => this.cmdScan() });
3162
+ add({ name: "review", aliases: [], description: "Perform code review on recent changes", category: "tools", requiresAuth: true, handler: async () => this.cmdReview() });
3163
+ add({ name: "explain", aliases: [], description: "Explain code, file, or concept", category: "tools", requiresAuth: true, handler: async (a) => this.cmdExplain(a) });
3164
+ add({ name: "memory", aliases: ["mem"], description: "Project memory save / show / clear", category: "tools", requiresAuth: true, handler: async (a) => this.cmdMemory(a) });
3165
+ add({ name: "mode", aliases: [], description: "Show or set AI mode", category: "config", requiresAuth: false, handler: async (a) => this.cmdMode(a) });
3166
+ add({ name: "model", aliases: [], description: "Show or set AI model", category: "config", requiresAuth: false, handler: async (a) => this.cmdModel(a) });
3167
+ add({ name: "models", aliases: [], description: "List all available AI models", category: "config", requiresAuth: false, handler: async () => this.cmdModels() });
3168
+ add({ name: "usage", aliases: ["stats"], description: "Show token usage statistics", category: "config", requiresAuth: true, handler: async () => this.cmdUsage() });
3169
+ add({ name: "plans", aliases: ["pricing"], description: "Show available plans and pricing", category: "config", requiresAuth: false, handler: async () => this.cmdPlans() });
3170
+ add({ name: "context", aliases: ["ctx"], description: "Show loaded codebase context", category: "config", requiresAuth: false, handler: async () => this.cmdContext() });
3171
+ add({ name: "history", aliases: [], description: "Show conversation history", category: "config", requiresAuth: false, handler: async () => this.cmdHistory() });
3172
+ add({ name: "config", aliases: ["settings"], description: "Show or set CLI configuration", category: "config", requiresAuth: false, handler: async (a) => this.cmdConfig(a) });
3173
+ }
3174
+ // ─── COMMAND IMPLEMENTATIONS ─────────────────────────────
3221
3175
  async cmdHelp() {
3222
- const W = 64;
3176
+ const W = 62;
3223
3177
  const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3224
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3178
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3225
3179
  const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
3226
- const hRow = (left, right) => {
3227
- const rawLeft = left.replace(/\x1B\[[0-9;]*m/g, "");
3228
- const rawRight = right.replace(/\x1B\[[0-9;]*m/g, "");
3229
- const pad = W - 2 - rawLeft.length - rawRight.length - 2;
3230
- return chalk7.dim("\u2502 ") + left + " ".repeat(Math.max(0, pad)) + right + chalk7.dim(" \u2502");
3231
- };
3232
- const catRow = (label) => {
3233
- const raw = label.replace(/\x1B\[[0-9;]*m/g, "");
3180
+ const ln = (s = "") => {
3181
+ const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
3234
3182
  const pad = W - 4 - raw.length;
3235
- return chalk7.dim("\u2502 ") + label + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3183
+ return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3236
3184
  };
3237
- const categories = [
3185
+ console.log(top);
3186
+ console.log(ln(chalk7.bold.white("Commands")));
3187
+ const cats = [
3238
3188
  { key: "session", label: "Session", color: chalk7.bold.green },
3239
3189
  { key: "tools", label: "AI Tools", color: chalk7.bold.magenta },
3240
3190
  { key: "config", label: "Configuration", color: chalk7.bold.yellow }
3241
3191
  ];
3242
- console.log(top);
3243
- console.log(catRow(chalk7.bold.white(" Commands")));
3244
- for (const cat of categories) {
3192
+ for (const cat of cats) {
3245
3193
  console.log(div);
3246
- console.log(catRow(" " + cat.color(cat.label)));
3194
+ console.log(ln(" " + cat.color(cat.label)));
3247
3195
  for (const cmd of this.commands.values()) {
3248
3196
  if (cmd.category !== cat.key) continue;
3249
- const cmdStr = chalk7.cyan(("/" + cmd.name).padEnd(14));
3197
+ const cmdStr = chalk7.cyan(("/" + cmd.name).padEnd(13));
3250
3198
  const descStr = chalk7.dim(cmd.description);
3251
- const aliasStr = cmd.aliases.length > 0 ? chalk7.dim(` \xB7 ${cmd.aliases.map((a) => "/" + a).join(", ")}`) : "";
3252
- console.log(hRow(" " + cmdStr + " " + descStr, aliasStr));
3199
+ const alias = cmd.aliases.length ? chalk7.dim(" \xB7 " + cmd.aliases.map((a) => "/" + a).join(", ")) : "";
3200
+ console.log(ln(" " + cmdStr + " " + descStr + alias));
3253
3201
  }
3254
3202
  }
3255
3203
  console.log(div);
3256
- console.log(catRow(chalk7.dim(" Just type a message to chat with DevWing AI")));
3257
- console.log(bottom);
3204
+ console.log(ln(chalk7.dim(" Type any message to chat with DevWing AI")));
3205
+ console.log(bot);
3258
3206
  }
3259
3207
  async cmdClear() {
3260
3208
  this.conversationHistory = [];
3261
3209
  this.sessionContext = null;
3262
3210
  this.sessionId = null;
3263
3211
  console.clear();
3264
- this.printStartupBanner();
3212
+ this.printBanner();
3265
3213
  this.printSystemInfo();
3214
+ console.log();
3266
3215
  }
3267
3216
  async cmdLogin() {
3268
3217
  if (this.isDemo) {
3269
- await this.withInquirer(() => demoLoginCommand({ fromSession: true }));
3218
+ await demoLoginCommand({ fromSession: true });
3270
3219
  } else {
3271
- await this.withInquirer(() => loginCommand());
3220
+ await loginCommand();
3272
3221
  }
3273
- await this.checkAuthStatus();
3222
+ await this.checkAuth();
3274
3223
  }
3275
3224
  async cmdLogout() {
3276
3225
  if (this.isDemo) {
3277
- await this.withInquirer(() => demoLogoutCommand());
3226
+ await demoLogoutCommand();
3278
3227
  } else {
3279
- await this.withInquirer(() => logoutCommand());
3228
+ await logoutCommand();
3280
3229
  }
3281
- await this.checkAuthStatus();
3230
+ await this.checkAuth();
3282
3231
  }
3283
3232
  async cmdStatus() {
3284
3233
  if (!this.isAuthenticated) {
@@ -3288,22 +3237,22 @@ ${recent[recent.length - 1].content}`;
3288
3237
  if (this.isDemo) {
3289
3238
  const W = 54;
3290
3239
  const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3291
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3292
- const row = (k, v) => {
3240
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3241
+ const kv = (k, v) => {
3293
3242
  const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
3294
3243
  const pad = W - 4 - k.length - rawV.length;
3295
3244
  return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3296
3245
  };
3297
3246
  console.log(top);
3298
- console.log(row("Email", DEMO_USER2.email));
3299
- console.log(row("Name", DEMO_USER2.full_name));
3300
- console.log(row("Plan", chalk7.bgCyan.black(" PRO ")));
3301
- console.log(row("Verified", chalk7.green("\u2713 Yes")));
3302
- console.log(row("2FA", chalk7.dim("Disabled")));
3303
- console.log(row("Workspace", DEMO_WORKSPACE2.name + chalk7.dim(" (enterprise)")));
3304
- console.log(row("Project", DEMO_PROJECT2.name));
3305
- console.log(row("Tokens today", `${(DEMO_USER2.tokens_used_today).toLocaleString()} / 500,000`));
3306
- console.log(bottom);
3247
+ console.log(kv("Email", DEMO_USER2.email));
3248
+ console.log(kv("Name", DEMO_USER2.full_name));
3249
+ console.log(kv("Plan", chalk7.bgCyan.black(" PRO ")));
3250
+ console.log(kv("Verified", chalk7.green("\u2713 Yes")));
3251
+ console.log(kv("2FA", chalk7.dim("Disabled")));
3252
+ console.log(kv("Workspace", DEMO_WORKSPACE2.name + chalk7.dim(" (enterprise)")));
3253
+ console.log(kv("Project", DEMO_PROJECT2.name));
3254
+ console.log(kv("Tokens today", `${(DEMO_USER2.tokens_used_today).toLocaleString()} / 500,000`));
3255
+ console.log(bot);
3307
3256
  } else {
3308
3257
  await statusCommand();
3309
3258
  }
@@ -3324,8 +3273,7 @@ ${recent[recent.length - 1].content}`;
3324
3273
  }
3325
3274
  async cmdExplain(args) {
3326
3275
  if (!args) {
3327
- logger.error("Usage: /explain <file, function, or concept>");
3328
- logger.info("Example: /explain src/auth/middleware.ts");
3276
+ logger.error("Usage: /explain <file or concept>");
3329
3277
  return;
3330
3278
  }
3331
3279
  if (this.isDemo) {
@@ -3338,7 +3286,6 @@ ${recent[recent.length - 1].content}`;
3338
3286
  const parts = args.split(/\s+/);
3339
3287
  const sub = parts[0]?.toLowerCase();
3340
3288
  if (!sub || !["save", "show", "clear"].includes(sub)) {
3341
- console.log();
3342
3289
  console.log(` ${chalk7.cyan("/memory show")} \u2014 Show all project memories`);
3343
3290
  console.log(` ${chalk7.cyan("/memory save <text>")} \u2014 Save a memory`);
3344
3291
  console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
@@ -3346,59 +3293,44 @@ ${recent[recent.length - 1].content}`;
3346
3293
  }
3347
3294
  const content = parts.slice(1).join(" ").trim();
3348
3295
  if (this.isDemo) {
3349
- await this.withInquirer(
3350
- () => demoMemoryCommand(sub, content || void 0)
3351
- );
3296
+ await demoMemoryCommand(sub, content || void 0);
3352
3297
  } else {
3353
3298
  await memoryCommand(sub, content || void 0, this.options);
3354
3299
  }
3355
3300
  }
3356
3301
  async cmdMode(args) {
3357
- const validModes = ["general", "frontend", "backend", "security", "devops"];
3302
+ const valid = ["general", "frontend", "backend", "security", "devops"];
3358
3303
  if (!args) {
3359
- const W = 58;
3360
- const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3361
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3362
- const row = (icon, name, desc) => {
3363
- const left = ` ${icon} ${name.padEnd(12)} ${chalk7.dim(desc)}`;
3364
- const rawLeft = left.replace(/\x1B\[[0-9;]*m/g, "");
3365
- const pad = W - 2 - rawLeft.length;
3366
- return chalk7.dim("\u2502") + left + " ".repeat(Math.max(0, pad)) + chalk7.dim("\u2502");
3304
+ const descs = {
3305
+ general: "All-purpose coding assistant",
3306
+ frontend: "React, Vue, CSS, TypeScript",
3307
+ backend: "APIs, databases, microservices",
3308
+ security: "OWASP, CVE, penetration testing",
3309
+ devops: "Docker, Kubernetes, CI/CD"
3367
3310
  };
3368
- console.log(top);
3369
- console.log(chalk7.dim("\u2502 ") + chalk7.bold("AI Mode".padEnd(W - 4)) + chalk7.dim(" \u2502"));
3370
- for (const m of validModes) {
3311
+ for (const m of valid) {
3371
3312
  const active = m === this.currentMode;
3372
- const icon = active ? chalk7.green("\u25CF") : chalk7.dim("\u25CB");
3373
- const name = active ? chalk7.yellow.bold(m) : chalk7.white(m);
3374
- const descs = {
3375
- general: "All-purpose coding assistant",
3376
- frontend: "React, Vue, CSS, TypeScript",
3377
- backend: "APIs, databases, microservices",
3378
- security: "OWASP, CVE, penetration testing",
3379
- devops: "Docker, Kubernetes, CI/CD"
3380
- };
3381
- console.log(row(icon, name, descs[m] || ""));
3313
+ const dot = active ? chalk7.green("\u25CF") : chalk7.dim("\u25CB");
3314
+ const label = active ? chalk7.yellow.bold(m.padEnd(10)) : chalk7.white(m.padEnd(10));
3315
+ console.log(` ${dot} ${label} ${chalk7.dim(descs[m])}`);
3382
3316
  }
3383
- console.log(bottom);
3317
+ console.log();
3384
3318
  console.log(chalk7.dim(" Usage: /mode <name>"));
3385
3319
  return;
3386
3320
  }
3387
3321
  const mode = args.toLowerCase();
3388
- if (!validModes.includes(mode)) {
3389
- logger.error(`Invalid mode "${args}". Valid: ${validModes.join(", ")}`);
3322
+ if (!valid.includes(mode)) {
3323
+ logger.error(`Invalid mode "${args}". Valid: ${valid.join(", ")}`);
3390
3324
  return;
3391
3325
  }
3392
3326
  this.currentMode = mode;
3393
3327
  this.options.mode = mode;
3394
- console.log();
3395
3328
  console.log(` ${chalk7.green("\u2713")} Mode \u2192 ${chalk7.yellow.bold(mode)}`);
3396
3329
  }
3397
3330
  async cmdModel(args) {
3398
3331
  if (!args) {
3399
- const current = this.currentModel || "auto";
3400
- console.log();
3401
- console.log(` ${chalk7.bold("Current model:")} ${chalk7.cyan(current)}`);
3332
+ const cur = this.currentModel || "auto";
3333
+ console.log(` Current model: ${chalk7.cyan.bold(cur)}`);
3402
3334
  console.log(chalk7.dim(" /model <name> \xB7 /model auto \xB7 /models to list all"));
3403
3335
  return;
3404
3336
  }
@@ -3410,28 +3342,20 @@ ${recent[recent.length - 1].content}`;
3410
3342
  }
3411
3343
  this.currentModel = args;
3412
3344
  this.options.model = args;
3413
- console.log();
3414
3345
  console.log(` ${chalk7.green("\u2713")} Model \u2192 ${chalk7.cyan.bold(args)}`);
3415
3346
  }
3416
3347
  async cmdModels() {
3417
3348
  if (this.isDemo) {
3418
3349
  const table = new Table3({
3419
- head: [
3420
- chalk7.bold("Model"),
3421
- chalk7.bold("Domain"),
3422
- chalk7.bold("Status"),
3423
- chalk7.bold("Plan"),
3424
- chalk7.bold("Context"),
3425
- chalk7.bold("Speed")
3426
- ],
3427
- style: { head: [], border: ["dim"] }
3350
+ head: [chalk7.bold("Model"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Plan"), chalk7.bold("Context"), chalk7.bold("Speed")],
3351
+ style: { border: ["dim"] }
3428
3352
  });
3429
3353
  for (const m of DEMO_MODELS) {
3430
- const active = m.name === (this.currentModel || "");
3431
- const nameStr = active ? chalk7.cyan.bold(m.display_name + " \u25CF") : m.display_name;
3432
- const statusBadge = m.status === "active" ? chalk7.green("active") : chalk7.yellow("beta");
3433
- const planBadge = m.min_plan === "free" ? chalk7.green("free") : chalk7.cyan("pro");
3434
- table.push([nameStr, chalk7.yellow(m.domain), statusBadge, planBadge, `${(m.context_window / 1024).toFixed(0)}k`, `${m.tokens_per_sec} t/s`]);
3354
+ const cur = m.name === (this.currentModel || "");
3355
+ const nm = cur ? chalk7.cyan.bold(m.display_name + " \u25CF") : m.display_name;
3356
+ const status = m.status === "active" ? chalk7.green("active") : chalk7.yellow("beta");
3357
+ const plan = m.min_plan === "free" ? chalk7.green("free") : chalk7.cyan("pro");
3358
+ table.push([nm, chalk7.yellow(m.domain), status, plan, `${(m.context_window / 1024).toFixed(0)}k`, `${m.tokens_per_sec} t/s`]);
3435
3359
  }
3436
3360
  console.log(table.toString());
3437
3361
  console.log(chalk7.dim(" /model <name> to select \xB7 /model auto for automatic"));
@@ -3452,29 +3376,28 @@ ${recent[recent.length - 1].content}`;
3452
3376
  }
3453
3377
  async cmdUsage() {
3454
3378
  if (this.isDemo) {
3455
- const table = new Table3({
3379
+ const t = new Table3({
3456
3380
  head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")],
3457
- style: { head: [], border: ["dim"] }
3381
+ style: { border: ["dim"] }
3458
3382
  });
3459
- table.push(
3383
+ t.push(
3460
3384
  ["Today", String(DEMO_USAGE.today.requests), DEMO_USAGE.today.tokens_in.toLocaleString(), DEMO_USAGE.today.tokens_out.toLocaleString(), `$${DEMO_USAGE.today.cost.toFixed(4)}`],
3461
3385
  ["This Week", String(DEMO_USAGE.week.requests), DEMO_USAGE.week.tokens_in.toLocaleString(), DEMO_USAGE.week.tokens_out.toLocaleString(), `$${DEMO_USAGE.week.cost.toFixed(4)}`],
3462
3386
  ["This Month", String(DEMO_USAGE.month.requests), DEMO_USAGE.month.tokens_in.toLocaleString(), DEMO_USAGE.month.tokens_out.toLocaleString(), `$${DEMO_USAGE.month.cost.toFixed(4)}`]
3463
3387
  );
3464
- console.log(table.toString());
3388
+ console.log(t.toString());
3465
3389
  const used = DEMO_USER2.tokens_used_today;
3466
3390
  const limit = 5e5;
3467
3391
  const pct = Math.round(used / limit * 100);
3468
- const barW = 32;
3469
- const filled = Math.round(pct / 100 * barW);
3470
- const bar = chalk7.cyan("\u2588".repeat(filled)) + chalk7.dim("\u2591".repeat(barW - filled));
3392
+ const barW = 30;
3393
+ const fill = Math.round(pct / 100 * barW);
3394
+ const bar = chalk7.cyan("\u2588".repeat(fill)) + chalk7.dim("\u2591".repeat(barW - fill));
3471
3395
  console.log();
3472
- console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()} tokens`)}`);
3396
+ console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()}`)}`);
3473
3397
  console.log();
3474
- console.log(chalk7.bold(" By Model"));
3475
3398
  const mt = new Table3({
3476
3399
  head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")],
3477
- style: { head: [], border: ["dim"] }
3400
+ style: { border: ["dim"] }
3478
3401
  });
3479
3402
  mt.push(
3480
3403
  ["devwing-backend-1", "312", "421,000", "$1.4721"],
@@ -3486,10 +3409,10 @@ ${recent[recent.length - 1].content}`;
3486
3409
  console.log(mt.toString());
3487
3410
  } else {
3488
3411
  try {
3489
- const usage = await apiClient.getUsage();
3490
- console.log(` Requests: ${usage.total_requests}`);
3491
- console.log(` Tokens: ${usage.total_tokens_in.toLocaleString()} in, ${usage.total_tokens_out.toLocaleString()} out`);
3492
- console.log(` Cost: $${usage.total_cost_usd.toFixed(4)}`);
3412
+ const u = await apiClient.getUsage();
3413
+ console.log(` Requests: ${u.total_requests}`);
3414
+ console.log(` Tokens: ${u.total_tokens_in.toLocaleString()} in, ${u.total_tokens_out.toLocaleString()} out`);
3415
+ console.log(` Cost: $${u.total_cost_usd.toFixed(4)}`);
3493
3416
  } catch (e) {
3494
3417
  logger.error("Failed to fetch usage: " + e.message);
3495
3418
  }
@@ -3497,71 +3420,60 @@ ${recent[recent.length - 1].content}`;
3497
3420
  }
3498
3421
  async cmdPlans() {
3499
3422
  const table = new Table3({
3500
- head: [
3501
- chalk7.bold("Plan"),
3502
- chalk7.bold("Price"),
3503
- chalk7.bold("Tokens/Day"),
3504
- chalk7.bold("Models"),
3505
- chalk7.bold("Projects"),
3506
- chalk7.bold("Seats")
3507
- ],
3508
- style: { head: [], border: ["dim"] }
3423
+ head: [chalk7.bold("Plan"), chalk7.bold("Price"), chalk7.bold("Tokens/Day"), chalk7.bold("Models"), chalk7.bold("Projects"), chalk7.bold("Seats")],
3424
+ style: { border: ["dim"] }
3509
3425
  });
3510
- const currentPlan = this.userProfile?.subscription_plan?.toLowerCase() || "";
3511
- for (const plan of DEMO_PLANS) {
3512
- const isCurrent = this.isAuthenticated && currentPlan === plan.name.toLowerCase();
3513
- const nameStr = isCurrent ? chalk7.cyan.bold(plan.name) + chalk7.dim(" \u2190") : plan.name;
3514
- table.push([nameStr, plan.price, plan.tokens_day, plan.models, plan.projects, plan.seats]);
3426
+ const cur = this.userProfile?.subscription_plan?.toLowerCase() || "";
3427
+ for (const p of DEMO_PLANS) {
3428
+ const isCur = this.isAuthenticated && cur === p.name.toLowerCase();
3429
+ const nm = isCur ? chalk7.cyan.bold(p.name) + chalk7.dim(" \u2190") : p.name;
3430
+ table.push([nm, p.price, p.tokens_day, p.models, p.projects, p.seats]);
3515
3431
  }
3516
3432
  console.log(table.toString());
3517
3433
  console.log();
3518
3434
  if (this.isAuthenticated) {
3519
- console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3435
+ console.log(chalk7.dim(` Your plan: ${cur.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3520
3436
  } else {
3521
3437
  console.log(chalk7.dim(" Sign up at devwing.ai"));
3522
3438
  }
3523
3439
  }
3524
3440
  async cmdContext() {
3525
3441
  if (this.isDemo) {
3526
- const W = 58;
3442
+ const W = 56;
3527
3443
  const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3528
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3444
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3529
3445
  const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
3530
- const row = (k, v) => {
3531
- const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
3532
- const pad = W - 4 - k.length - rawV.length;
3446
+ const kv = (k, v) => {
3447
+ const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
3448
+ const pad = W - 4 - k.length - rv.length;
3533
3449
  return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3534
3450
  };
3535
- const fileRow = (f) => {
3536
- const pad = W - 4 - 2 - f.length;
3537
- return chalk7.dim("\u2502 ") + chalk7.dim("\xB7") + " " + chalk7.cyan(f) + " ".repeat(Math.max(0, pad)) + chalk7.dim("\u2502");
3451
+ const fl = (f) => {
3452
+ const pad = W - 6 - f.length;
3453
+ return chalk7.dim("\u2502 \xB7 ") + chalk7.cyan(f) + " ".repeat(Math.max(0, pad)) + chalk7.dim("\u2502");
3538
3454
  };
3539
- const textRow = (t) => {
3540
- const pad = W - 4 - t.length;
3541
- return chalk7.dim("\u2502 ") + t + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3455
+ const ln = (s) => {
3456
+ const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
3457
+ const pad = W - 4 - raw.length;
3458
+ return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3542
3459
  };
3543
3460
  console.log(top);
3544
- console.log(row("Directory", chalk7.cyan(process.cwd().replace(process.env.HOME || "", "~"))));
3545
- console.log(row("Git Branch", "main"));
3546
- console.log(row("Files Loaded", "34"));
3547
- console.log(row("Framework", "FastAPI + TypeScript"));
3548
- console.log(row("Shell", process.env.SHELL?.split("/").pop() || "unknown"));
3461
+ console.log(kv("Directory", chalk7.dim(process.cwd().replace(process.env.HOME || "", "~"))));
3462
+ console.log(kv("Git Branch", "main"));
3463
+ console.log(kv("Files", "34 files loaded"));
3464
+ console.log(kv("Framework", "FastAPI + TypeScript"));
3549
3465
  console.log(div);
3550
- console.log(textRow(chalk7.bold("Top files in context")));
3551
- const files = ["src/auth/auth_controller.ts", "src/middleware/jwt.middleware.ts", "src/services/user_service.ts", "src/auth/repositories/user.repository.ts", "package.json", "docker-compose.yml"];
3552
- for (const f of files) console.log(fileRow(f));
3553
- console.log(textRow(chalk7.dim("... and 28 more")));
3554
- console.log(bottom);
3466
+ console.log(ln(chalk7.bold("Top files")));
3467
+ for (const f of ["src/auth/auth_controller.ts", "src/middleware/jwt.middleware.ts", "src/services/user_service.ts", "package.json", "docker-compose.yml"]) {
3468
+ console.log(fl(f));
3469
+ }
3470
+ console.log(ln(chalk7.dim("... and 29 more")));
3471
+ console.log(bot);
3555
3472
  } else if (this.sessionContext) {
3556
- console.log(` Dir: ${chalk7.cyan(this.sessionContext.cwd)} \xB7 Shell: ${this.sessionContext.shell} \xB7 Files: ${this.sessionContext.files.length}`);
3557
- if (this.sessionContext.git_branch) console.log(` Branch: ${this.sessionContext.git_branch}`);
3558
- console.log();
3473
+ console.log(` Dir: ${this.sessionContext.cwd} \xB7 Files: ${this.sessionContext.files.length}`);
3559
3474
  for (const f of this.sessionContext.files.slice(0, 10)) {
3560
3475
  console.log(` ${chalk7.dim("\xB7")} ${chalk7.cyan(f.path)}`);
3561
3476
  }
3562
- if (this.sessionContext.files.length > 10) {
3563
- console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3564
- }
3565
3477
  } else {
3566
3478
  logger.info("No context loaded yet. Send a message to load context.");
3567
3479
  }
@@ -3571,43 +3483,41 @@ ${recent[recent.length - 1].content}`;
3571
3483
  logger.info("No conversation history yet.");
3572
3484
  return;
3573
3485
  }
3574
- console.log();
3575
- for (const msg of this.conversationHistory.slice(-20)) {
3576
- const label = msg.role === "user" ? chalk7.green.bold("You ") : chalk7.cyan.bold("DevWing ");
3577
- const time = chalk7.dim(msg.timestamp.toLocaleTimeString());
3578
- const preview = msg.content.length > 100 ? msg.content.substring(0, 100) + "\u2026" : msg.content;
3486
+ for (const m of this.conversationHistory.slice(-20)) {
3487
+ const label = m.role === "user" ? chalk7.green.bold("You ") : chalk7.cyan.bold("DevWing ");
3488
+ const time = chalk7.dim(m.timestamp.toLocaleTimeString());
3489
+ const preview = m.content.length > 100 ? m.content.slice(0, 100) + "\u2026" : m.content;
3579
3490
  console.log(` ${time} ${label} ${preview}`);
3580
3491
  }
3581
3492
  }
3582
3493
  async cmdConfig(args) {
3583
3494
  if (!args) {
3584
- const config = configManager.getAll();
3585
- const W = 56;
3495
+ const cfg = configManager.getAll();
3496
+ const W = 54;
3586
3497
  const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3587
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3588
- const row = (k, v) => {
3589
- const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
3590
- const pad = W - 4 - k.length - rawV.length;
3498
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3499
+ const kv = (k, v) => {
3500
+ const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
3501
+ const pad = W - 4 - k.length - rv.length;
3591
3502
  return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3592
3503
  };
3593
3504
  console.log(top);
3594
- console.log(row("API URL", chalk7.dim(config.apiUrl || "default")));
3595
- console.log(row("Workspace", chalk7.dim(config.workspaceId || "none")));
3596
- console.log(row("Project", chalk7.dim(config.projectId || "none")));
3597
- console.log(row("Model", chalk7.dim(config.model || "auto")));
3598
- console.log(row("Mode", chalk7.dim(config.mode || "general")));
3599
- console.log(row("Config", chalk7.dim(configManager.getPath())));
3600
- console.log(bottom);
3505
+ console.log(kv("API URL", chalk7.dim(cfg.apiUrl || "default")));
3506
+ console.log(kv("Workspace", chalk7.dim(cfg.workspaceId || "none")));
3507
+ console.log(kv("Project", chalk7.dim(cfg.projectId || "none")));
3508
+ console.log(kv("Model", chalk7.dim(cfg.model || "auto")));
3509
+ console.log(kv("Mode", chalk7.dim(cfg.mode || "general")));
3510
+ console.log(kv("Config", chalk7.dim(configManager.getPath())));
3511
+ console.log(bot);
3601
3512
  console.log(chalk7.dim(" /config set <key> <value> \xB7 /config get <key>"));
3602
3513
  return;
3603
3514
  }
3604
3515
  const parts = args.split(/\s+/);
3605
- const sub = parts[0];
3606
- if (sub === "list") {
3516
+ if (parts[0] === "list") {
3607
3517
  await configCommand("list");
3608
- } else if (sub === "get" && parts[1]) {
3518
+ } else if (parts[0] === "get" && parts[1]) {
3609
3519
  await configCommand("get", parts[1]);
3610
- } else if (sub === "set" && parts[1] && parts[2]) {
3520
+ } else if (parts[0] === "set" && parts[1] && parts[2]) {
3611
3521
  await configCommand("set", parts[1], parts[2]);
3612
3522
  if (parts[1] === "mode") this.currentMode = parts[2];
3613
3523
  if (parts[1] === "model") this.currentModel = parts[2];