devwing 0.1.22 → 0.1.23

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.23";
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,134 @@ ${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
+ restoreStdin();
3223
+ await sleep2(80);
3224
+ await this.checkAuth();
3274
3225
  }
3275
3226
  async cmdLogout() {
3276
3227
  if (this.isDemo) {
3277
- await this.withInquirer(() => demoLogoutCommand());
3228
+ await demoLogoutCommand();
3278
3229
  } else {
3279
- await this.withInquirer(() => logoutCommand());
3230
+ await logoutCommand();
3280
3231
  }
3281
- await this.checkAuthStatus();
3232
+ restoreStdin();
3233
+ await sleep2(80);
3234
+ await this.checkAuth();
3282
3235
  }
3283
3236
  async cmdStatus() {
3284
3237
  if (!this.isAuthenticated) {
@@ -3288,22 +3241,22 @@ ${recent[recent.length - 1].content}`;
3288
3241
  if (this.isDemo) {
3289
3242
  const W = 54;
3290
3243
  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) => {
3244
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3245
+ const kv = (k, v) => {
3293
3246
  const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
3294
3247
  const pad = W - 4 - k.length - rawV.length;
3295
3248
  return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3296
3249
  };
3297
3250
  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);
3251
+ console.log(kv("Email", DEMO_USER2.email));
3252
+ console.log(kv("Name", DEMO_USER2.full_name));
3253
+ console.log(kv("Plan", chalk7.bgCyan.black(" PRO ")));
3254
+ console.log(kv("Verified", chalk7.green("\u2713 Yes")));
3255
+ console.log(kv("2FA", chalk7.dim("Disabled")));
3256
+ console.log(kv("Workspace", DEMO_WORKSPACE2.name + chalk7.dim(" (enterprise)")));
3257
+ console.log(kv("Project", DEMO_PROJECT2.name));
3258
+ console.log(kv("Tokens today", `${(DEMO_USER2.tokens_used_today).toLocaleString()} / 500,000`));
3259
+ console.log(bot);
3307
3260
  } else {
3308
3261
  await statusCommand();
3309
3262
  }
@@ -3324,8 +3277,7 @@ ${recent[recent.length - 1].content}`;
3324
3277
  }
3325
3278
  async cmdExplain(args) {
3326
3279
  if (!args) {
3327
- logger.error("Usage: /explain <file, function, or concept>");
3328
- logger.info("Example: /explain src/auth/middleware.ts");
3280
+ logger.error("Usage: /explain <file or concept>");
3329
3281
  return;
3330
3282
  }
3331
3283
  if (this.isDemo) {
@@ -3338,7 +3290,6 @@ ${recent[recent.length - 1].content}`;
3338
3290
  const parts = args.split(/\s+/);
3339
3291
  const sub = parts[0]?.toLowerCase();
3340
3292
  if (!sub || !["save", "show", "clear"].includes(sub)) {
3341
- console.log();
3342
3293
  console.log(` ${chalk7.cyan("/memory show")} \u2014 Show all project memories`);
3343
3294
  console.log(` ${chalk7.cyan("/memory save <text>")} \u2014 Save a memory`);
3344
3295
  console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
@@ -3346,59 +3297,46 @@ ${recent[recent.length - 1].content}`;
3346
3297
  }
3347
3298
  const content = parts.slice(1).join(" ").trim();
3348
3299
  if (this.isDemo) {
3349
- await this.withInquirer(
3350
- () => demoMemoryCommand(sub, content || void 0)
3351
- );
3300
+ await demoMemoryCommand(sub, content || void 0);
3301
+ restoreStdin();
3302
+ await sleep2(80);
3352
3303
  } else {
3353
3304
  await memoryCommand(sub, content || void 0, this.options);
3354
3305
  }
3355
3306
  }
3356
3307
  async cmdMode(args) {
3357
- const validModes = ["general", "frontend", "backend", "security", "devops"];
3308
+ const valid = ["general", "frontend", "backend", "security", "devops"];
3358
3309
  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");
3310
+ const descs = {
3311
+ general: "All-purpose coding assistant",
3312
+ frontend: "React, Vue, CSS, TypeScript",
3313
+ backend: "APIs, databases, microservices",
3314
+ security: "OWASP, CVE, penetration testing",
3315
+ devops: "Docker, Kubernetes, CI/CD"
3367
3316
  };
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) {
3317
+ for (const m of valid) {
3371
3318
  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] || ""));
3319
+ const dot = active ? chalk7.green("\u25CF") : chalk7.dim("\u25CB");
3320
+ const label = active ? chalk7.yellow.bold(m.padEnd(10)) : chalk7.white(m.padEnd(10));
3321
+ console.log(` ${dot} ${label} ${chalk7.dim(descs[m])}`);
3382
3322
  }
3383
- console.log(bottom);
3323
+ console.log();
3384
3324
  console.log(chalk7.dim(" Usage: /mode <name>"));
3385
3325
  return;
3386
3326
  }
3387
3327
  const mode = args.toLowerCase();
3388
- if (!validModes.includes(mode)) {
3389
- logger.error(`Invalid mode "${args}". Valid: ${validModes.join(", ")}`);
3328
+ if (!valid.includes(mode)) {
3329
+ logger.error(`Invalid mode "${args}". Valid: ${valid.join(", ")}`);
3390
3330
  return;
3391
3331
  }
3392
3332
  this.currentMode = mode;
3393
3333
  this.options.mode = mode;
3394
- console.log();
3395
3334
  console.log(` ${chalk7.green("\u2713")} Mode \u2192 ${chalk7.yellow.bold(mode)}`);
3396
3335
  }
3397
3336
  async cmdModel(args) {
3398
3337
  if (!args) {
3399
- const current = this.currentModel || "auto";
3400
- console.log();
3401
- console.log(` ${chalk7.bold("Current model:")} ${chalk7.cyan(current)}`);
3338
+ const cur = this.currentModel || "auto";
3339
+ console.log(` Current model: ${chalk7.cyan.bold(cur)}`);
3402
3340
  console.log(chalk7.dim(" /model <name> \xB7 /model auto \xB7 /models to list all"));
3403
3341
  return;
3404
3342
  }
@@ -3410,28 +3348,20 @@ ${recent[recent.length - 1].content}`;
3410
3348
  }
3411
3349
  this.currentModel = args;
3412
3350
  this.options.model = args;
3413
- console.log();
3414
3351
  console.log(` ${chalk7.green("\u2713")} Model \u2192 ${chalk7.cyan.bold(args)}`);
3415
3352
  }
3416
3353
  async cmdModels() {
3417
3354
  if (this.isDemo) {
3418
3355
  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"] }
3356
+ head: [chalk7.bold("Model"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Plan"), chalk7.bold("Context"), chalk7.bold("Speed")],
3357
+ style: { border: ["dim"] }
3428
3358
  });
3429
3359
  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`]);
3360
+ const cur = m.name === (this.currentModel || "");
3361
+ const nm = cur ? chalk7.cyan.bold(m.display_name + " \u25CF") : m.display_name;
3362
+ const status = m.status === "active" ? chalk7.green("active") : chalk7.yellow("beta");
3363
+ const plan = m.min_plan === "free" ? chalk7.green("free") : chalk7.cyan("pro");
3364
+ table.push([nm, chalk7.yellow(m.domain), status, plan, `${(m.context_window / 1024).toFixed(0)}k`, `${m.tokens_per_sec} t/s`]);
3435
3365
  }
3436
3366
  console.log(table.toString());
3437
3367
  console.log(chalk7.dim(" /model <name> to select \xB7 /model auto for automatic"));
@@ -3452,29 +3382,28 @@ ${recent[recent.length - 1].content}`;
3452
3382
  }
3453
3383
  async cmdUsage() {
3454
3384
  if (this.isDemo) {
3455
- const table = new Table3({
3385
+ const t = new Table3({
3456
3386
  head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")],
3457
- style: { head: [], border: ["dim"] }
3387
+ style: { border: ["dim"] }
3458
3388
  });
3459
- table.push(
3389
+ t.push(
3460
3390
  ["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
3391
  ["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
3392
  ["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
3393
  );
3464
- console.log(table.toString());
3394
+ console.log(t.toString());
3465
3395
  const used = DEMO_USER2.tokens_used_today;
3466
3396
  const limit = 5e5;
3467
3397
  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));
3398
+ const barW = 30;
3399
+ const fill = Math.round(pct / 100 * barW);
3400
+ const bar = chalk7.cyan("\u2588".repeat(fill)) + chalk7.dim("\u2591".repeat(barW - fill));
3471
3401
  console.log();
3472
- console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()} tokens`)}`);
3402
+ console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()}`)}`);
3473
3403
  console.log();
3474
- console.log(chalk7.bold(" By Model"));
3475
3404
  const mt = new Table3({
3476
3405
  head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")],
3477
- style: { head: [], border: ["dim"] }
3406
+ style: { border: ["dim"] }
3478
3407
  });
3479
3408
  mt.push(
3480
3409
  ["devwing-backend-1", "312", "421,000", "$1.4721"],
@@ -3486,10 +3415,10 @@ ${recent[recent.length - 1].content}`;
3486
3415
  console.log(mt.toString());
3487
3416
  } else {
3488
3417
  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)}`);
3418
+ const u = await apiClient.getUsage();
3419
+ console.log(` Requests: ${u.total_requests}`);
3420
+ console.log(` Tokens: ${u.total_tokens_in.toLocaleString()} in, ${u.total_tokens_out.toLocaleString()} out`);
3421
+ console.log(` Cost: $${u.total_cost_usd.toFixed(4)}`);
3493
3422
  } catch (e) {
3494
3423
  logger.error("Failed to fetch usage: " + e.message);
3495
3424
  }
@@ -3497,71 +3426,60 @@ ${recent[recent.length - 1].content}`;
3497
3426
  }
3498
3427
  async cmdPlans() {
3499
3428
  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"] }
3429
+ head: [chalk7.bold("Plan"), chalk7.bold("Price"), chalk7.bold("Tokens/Day"), chalk7.bold("Models"), chalk7.bold("Projects"), chalk7.bold("Seats")],
3430
+ style: { border: ["dim"] }
3509
3431
  });
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]);
3432
+ const cur = this.userProfile?.subscription_plan?.toLowerCase() || "";
3433
+ for (const p of DEMO_PLANS) {
3434
+ const isCur = this.isAuthenticated && cur === p.name.toLowerCase();
3435
+ const nm = isCur ? chalk7.cyan.bold(p.name) + chalk7.dim(" \u2190") : p.name;
3436
+ table.push([nm, p.price, p.tokens_day, p.models, p.projects, p.seats]);
3515
3437
  }
3516
3438
  console.log(table.toString());
3517
3439
  console.log();
3518
3440
  if (this.isAuthenticated) {
3519
- console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3441
+ console.log(chalk7.dim(` Your plan: ${cur.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3520
3442
  } else {
3521
3443
  console.log(chalk7.dim(" Sign up at devwing.ai"));
3522
3444
  }
3523
3445
  }
3524
3446
  async cmdContext() {
3525
3447
  if (this.isDemo) {
3526
- const W = 58;
3448
+ const W = 56;
3527
3449
  const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3528
- const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3450
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3529
3451
  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;
3452
+ const kv = (k, v) => {
3453
+ const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
3454
+ const pad = W - 4 - k.length - rv.length;
3533
3455
  return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3534
3456
  };
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");
3457
+ const fl = (f) => {
3458
+ const pad = W - 6 - f.length;
3459
+ return chalk7.dim("\u2502 \xB7 ") + chalk7.cyan(f) + " ".repeat(Math.max(0, pad)) + chalk7.dim("\u2502");
3538
3460
  };
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");
3461
+ const ln = (s) => {
3462
+ const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
3463
+ const pad = W - 4 - raw.length;
3464
+ return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3542
3465
  };
3543
3466
  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"));
3467
+ console.log(kv("Directory", chalk7.dim(process.cwd().replace(process.env.HOME || "", "~"))));
3468
+ console.log(kv("Git Branch", "main"));
3469
+ console.log(kv("Files", "34 files loaded"));
3470
+ console.log(kv("Framework", "FastAPI + TypeScript"));
3549
3471
  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);
3472
+ console.log(ln(chalk7.bold("Top files")));
3473
+ for (const f of ["src/auth/auth_controller.ts", "src/middleware/jwt.middleware.ts", "src/services/user_service.ts", "package.json", "docker-compose.yml"]) {
3474
+ console.log(fl(f));
3475
+ }
3476
+ console.log(ln(chalk7.dim("... and 29 more")));
3477
+ console.log(bot);
3555
3478
  } 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();
3479
+ console.log(` Dir: ${this.sessionContext.cwd} \xB7 Files: ${this.sessionContext.files.length}`);
3559
3480
  for (const f of this.sessionContext.files.slice(0, 10)) {
3560
3481
  console.log(` ${chalk7.dim("\xB7")} ${chalk7.cyan(f.path)}`);
3561
3482
  }
3562
- if (this.sessionContext.files.length > 10) {
3563
- console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3564
- }
3565
3483
  } else {
3566
3484
  logger.info("No context loaded yet. Send a message to load context.");
3567
3485
  }
@@ -3571,43 +3489,41 @@ ${recent[recent.length - 1].content}`;
3571
3489
  logger.info("No conversation history yet.");
3572
3490
  return;
3573
3491
  }
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;
3492
+ for (const m of this.conversationHistory.slice(-20)) {
3493
+ const label = m.role === "user" ? chalk7.green.bold("You ") : chalk7.cyan.bold("DevWing ");
3494
+ const time = chalk7.dim(m.timestamp.toLocaleTimeString());
3495
+ const preview = m.content.length > 100 ? m.content.slice(0, 100) + "\u2026" : m.content;
3579
3496
  console.log(` ${time} ${label} ${preview}`);
3580
3497
  }
3581
3498
  }
3582
3499
  async cmdConfig(args) {
3583
3500
  if (!args) {
3584
- const config = configManager.getAll();
3585
- const W = 56;
3501
+ const cfg = configManager.getAll();
3502
+ const W = 54;
3586
3503
  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;
3504
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3505
+ const kv = (k, v) => {
3506
+ const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
3507
+ const pad = W - 4 - k.length - rv.length;
3591
3508
  return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3592
3509
  };
3593
3510
  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);
3511
+ console.log(kv("API URL", chalk7.dim(cfg.apiUrl || "default")));
3512
+ console.log(kv("Workspace", chalk7.dim(cfg.workspaceId || "none")));
3513
+ console.log(kv("Project", chalk7.dim(cfg.projectId || "none")));
3514
+ console.log(kv("Model", chalk7.dim(cfg.model || "auto")));
3515
+ console.log(kv("Mode", chalk7.dim(cfg.mode || "general")));
3516
+ console.log(kv("Config", chalk7.dim(configManager.getPath())));
3517
+ console.log(bot);
3601
3518
  console.log(chalk7.dim(" /config set <key> <value> \xB7 /config get <key>"));
3602
3519
  return;
3603
3520
  }
3604
3521
  const parts = args.split(/\s+/);
3605
- const sub = parts[0];
3606
- if (sub === "list") {
3522
+ if (parts[0] === "list") {
3607
3523
  await configCommand("list");
3608
- } else if (sub === "get" && parts[1]) {
3524
+ } else if (parts[0] === "get" && parts[1]) {
3609
3525
  await configCommand("get", parts[1]);
3610
- } else if (sub === "set" && parts[1] && parts[2]) {
3526
+ } else if (parts[0] === "set" && parts[1] && parts[2]) {
3611
3527
  await configCommand("set", parts[1], parts[2]);
3612
3528
  if (parts[1] === "mode") this.currentMode = parts[2];
3613
3529
  if (parts[1] === "model") this.currentModel = parts[2];