devwing 0.1.21 → 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.21";
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,22 +2800,19 @@ 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
- plan: "enterprise"};
2807
- var DEMO_PROJECT2 = {
2808
- name: "Tallon SaaS ERP"};
2803
+ var DEMO_WORKSPACE2 = { name: "Kano State Government"};
2804
+ var DEMO_PROJECT2 = { name: "Tallon SaaS ERP"};
2809
2805
  var DEMO_MODELS = [
2810
- { 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" },
2811
- { 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" },
2812
- { 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" },
2813
- { 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" },
2814
- { 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" },
2815
- { name: "devwing-mobile-1", display_name: "DevWing Mobile", domain: "general", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 72, version: "0.8" },
2816
- { name: "devwing-data-1", display_name: "DevWing Data", domain: "general", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 68, version: "0.7" }
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 }
2817
2813
  ];
2818
2814
  var DEMO_PLANS = [
2819
- { name: "Free", price: "$0", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
2815
+ { name: "Free", price: "$0/mo", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
2820
2816
  { name: "Pro", price: "$19/mo", tokens_day: "500,000", models: "All models", projects: "10", seats: "1" },
2821
2817
  { name: "Team", price: "$99/mo", tokens_day: "2,000,000", models: "All + priority", projects: "Unlimited", seats: "5 included" },
2822
2818
  { name: "Enterprise", price: "Custom", tokens_day: "Unlimited", models: "Dedicated", projects: "Unlimited", seats: "Unlimited" }
@@ -2826,6 +2822,65 @@ var DEMO_USAGE = {
2826
2822
  week: { requests: 147, tokens_in: 198340, tokens_out: 102890, cost: 0.7213 },
2827
2823
  month: { requests: 612, tokens_in: 824100, tokens_out: 428750, cost: 3.0012 }
2828
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
+ }
2829
2884
  var InteractiveSession = class {
2830
2885
  isAuthenticated = false;
2831
2886
  userProfile = null;
@@ -2836,13 +2891,10 @@ var InteractiveSession = class {
2836
2891
  sessionId = null;
2837
2892
  isDemo;
2838
2893
  options;
2839
- rl;
2840
2894
  commands = /* @__PURE__ */ new Map();
2841
- sigintCount = 0;
2842
- sigintTimer = null;
2843
2895
  version;
2844
- isBusy = false;
2845
- // prevent readline events during command execution
2896
+ running = true;
2897
+ ctrlCCount = 0;
2846
2898
  constructor(options) {
2847
2899
  this.options = options;
2848
2900
  this.isDemo = isDemoMode();
@@ -2851,97 +2903,48 @@ var InteractiveSession = class {
2851
2903
  if (options.model) this.currentModel = options.model;
2852
2904
  this.registerCommands();
2853
2905
  }
2854
- // ============================================================
2855
- // START
2856
- // ============================================================
2906
+ // ─── START ───────────────────────────────────────────────
2857
2907
  async start() {
2858
2908
  console.clear();
2859
- this.printStartupBanner();
2860
- await this.checkAuthStatus();
2909
+ this.printBanner();
2910
+ await this.checkAuth();
2861
2911
  this.printSystemInfo();
2862
- this.createReadline();
2863
2912
  console.log();
2864
- this.rl.prompt();
2865
- }
2866
- // ============================================================
2867
- // READLINE MANAGEMENT
2868
- // ============================================================
2869
- createReadline() {
2870
- this.isBusy = false;
2871
- this.rl = readline.createInterface({
2872
- input: process.stdin,
2873
- output: process.stdout,
2874
- prompt: this.buildPrompt(),
2875
- completer: (line) => this.completer(line)
2876
- });
2877
- this.rl.on("SIGINT", () => {
2878
- this.sigintCount++;
2879
- if (this.sigintCount >= 2) {
2880
- this.exitSession();
2881
- 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;
2882
2928
  }
2883
- console.log(chalk7.dim("\n Press Ctrl+C again to exit, or type /exit"));
2884
- this.sigintTimer = setTimeout(() => {
2885
- this.sigintCount = 0;
2886
- }, 1500);
2887
- this.rl.prompt();
2888
- });
2889
- const currentRl = this.rl;
2890
- this.rl.on("line", async (input) => {
2891
- if (this.isBusy) return;
2929
+ this.ctrlCCount = 0;
2892
2930
  const trimmed = input.trim();
2893
- if (!trimmed) {
2894
- this.rl.prompt();
2895
- return;
2896
- }
2897
- this.isBusy = true;
2931
+ if (!trimmed) continue;
2898
2932
  try {
2899
2933
  if (trimmed.startsWith("/")) {
2900
- await this.handleSlashCommand(trimmed);
2901
- return;
2934
+ await this.handleSlash(trimmed);
2902
2935
  } else {
2903
- await this.handleAIPrompt(trimmed);
2904
- }
2905
- } catch (error) {
2906
- logger.error(error.message || "An error occurred");
2907
- }
2908
- this.isBusy = false;
2909
- if (this.rl === currentRl) {
2910
- this.rl.setPrompt(this.buildPrompt());
2911
- this.rl.prompt();
2912
- }
2913
- });
2914
- this.rl.on("close", () => {
2915
- if (!this.isBusy) {
2916
- this.exitSession();
2917
- }
2918
- });
2919
- }
2920
- resetStdin() {
2921
- try {
2922
- if (typeof process.stdin.setRawMode === "function") {
2923
- try {
2924
- process.stdin.setRawMode(false);
2925
- } catch {
2936
+ await this.handlePrompt(trimmed);
2926
2937
  }
2938
+ } catch (err) {
2939
+ logger.error(err.message || "An error occurred");
2927
2940
  }
2928
- process.stdin.removeAllListeners("keypress");
2929
- process.stdin.removeAllListeners("data");
2930
- if (process.stdin.isPaused()) {
2931
- process.stdin.resume();
2932
- }
2933
- } catch {
2941
+ restoreStdin();
2942
+ await sleep2(50);
2934
2943
  }
2935
2944
  }
2936
- recreateReadline() {
2937
- this.createReadline();
2938
- this.rl.prompt();
2939
- }
2940
- // ============================================================
2941
- // STARTUP UI
2942
- // ============================================================
2943
- printStartupBanner() {
2944
- const banner = gradient.pastel.multiline([
2945
+ // ─── BANNER ──────────────────────────────────────────────
2946
+ printBanner() {
2947
+ const art = gradient.pastel.multiline([
2945
2948
  "",
2946
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 ",
2947
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 ",
@@ -2951,101 +2954,81 @@ var InteractiveSession = class {
2951
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 ",
2952
2955
  ""
2953
2956
  ].join("\n"));
2954
- console.log(banner);
2955
- console.log(chalk7.dim(" Your AI Wingman in the Terminal") + chalk7.dim(` v${this.version}`));
2957
+ console.log(art);
2958
+ console.log(
2959
+ chalk7.dim(" Your AI Wingman in the Terminal") + " " + chalk7.dim(`v${this.version}`)
2960
+ );
2956
2961
  console.log();
2957
2962
  }
2963
+ // ─── SYSTEM INFO PANEL ───────────────────────────────────
2958
2964
  printSystemInfo() {
2959
- const line = chalk7.dim("\u2500".repeat(64));
2960
- console.log(line);
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");
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);
2975
+ console.log(top);
2961
2976
  if (this.isAuthenticated && this.userProfile) {
2962
2977
  const name = this.userProfile.full_name || this.userProfile.email;
2963
2978
  const plan = (this.userProfile.subscription_plan || "free").toUpperCase();
2964
- const planColor = plan === "PRO" ? chalk7.cyan : plan === "ENTERPRISE" ? chalk7.magenta : plan === "TEAM" ? chalk7.yellow : chalk7.dim;
2965
- console.log(
2966
- ` ${chalk7.bold("Auth")} ${chalk7.green("\u25CF")} ${name} ${planColor(`(${plan})`)}`
2967
- );
2968
- } else if (this.isAuthenticated) {
2969
- console.log(
2970
- ` ${chalk7.bold("Auth")} ${chalk7.green("\u25CF")} Authenticated ${chalk7.dim("(offline)")}`
2971
- );
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));
2972
2981
  } else {
2973
- console.log(
2974
- ` ${chalk7.bold("Auth")} ${chalk7.red("\u25CF")} Not logged in ${chalk7.dim("\u2014 type /login")}`
2975
- );
2982
+ console.log(ln(chalk7.bold("Auth".padEnd(12)) + chalk7.red("\u25CF ") + chalk7.dim("Not logged in") + " " + chalk7.cyan("/login")));
2976
2983
  }
2977
- const modeStr = chalk7.yellow(this.currentMode);
2978
- const modelStr = this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto");
2979
- console.log(` ${chalk7.bold("Mode")} ${modeStr} ${chalk7.bold("Model")} ${modelStr}`);
2980
- const cwd = process.cwd().replace(process.env.HOME || "", "~");
2984
+ console.log(div);
2985
+ console.log(kv("Mode", chalk7.yellow(this.currentMode) + " " + chalk7.bold("Model".padEnd(8)) + (this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto"))));
2986
+ console.log(div);
2981
2987
  if (this.isDemo) {
2982
- console.log(` ${chalk7.bold("Project")} ${chalk7.cyan(DEMO_PROJECT2.name)} ${chalk7.bold("CWD")} ${chalk7.dim(cwd)}`);
2988
+ console.log(kv("Project", chalk7.cyan(DEMO_PROJECT2.name)));
2983
2989
  } else {
2984
- const projectId = configManager.getProjectId();
2985
- const projectStr = projectId ? chalk7.cyan(projectId) : chalk7.dim("none");
2986
- console.log(` ${chalk7.bold("Project")} ${projectStr} ${chalk7.bold("CWD")} ${chalk7.dim(cwd)}`);
2990
+ const pid = configManager.getProjectId();
2991
+ console.log(kv("Project", pid ? chalk7.cyan(pid) : chalk7.dim("none")));
2987
2992
  }
2988
- const shell = process.env.SHELL?.split("/").pop() || "unknown";
2989
- console.log(` ${chalk7.bold("Shell")} ${shell} ${chalk7.bold("OS")} ${process.platform}`);
2993
+ const cwd = process.cwd().replace(process.env.HOME || "", "~");
2994
+ console.log(kv("Directory", chalk7.dim(cwd)));
2990
2995
  if (this.isDemo) {
2991
- console.log(` ${chalk7.bold("Demo")} ${chalk7.yellow("\u25CF")} ${chalk7.yellow("Demo mode active")} ${chalk7.dim("\u2014 no backend required")}`);
2996
+ console.log(div);
2997
+ console.log(ln(chalk7.yellow("\u25C6 ") + chalk7.yellow("Demo mode") + chalk7.dim(" \u2014 no backend required")));
2992
2998
  }
2993
- console.log(line);
2999
+ console.log(bot);
2994
3000
  console.log();
2995
- console.log(chalk7.dim(" Type a message to chat with AI, or /help for commands."));
2996
- }
2997
- // ============================================================
2998
- // PROMPT
2999
- // ============================================================
3000
- buildPrompt() {
3001
- const parts = [];
3002
- parts.push(chalk7.bold.cyan("devwing"));
3003
- parts.push(chalk7.dim("[") + chalk7.yellow(this.currentMode) + chalk7.dim("]"));
3001
+ console.log(chalk7.dim(" Type a message to chat, or ") + chalk7.cyan("/help") + chalk7.dim(" for commands."));
3002
+ }
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 = "";
3004
3010
  if (this.isAuthenticated && this.userProfile) {
3005
- const firstName = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0].toLowerCase();
3006
- parts.push(chalk7.green(firstName));
3007
- }
3008
- if (this.isDemo) {
3009
- parts.push(chalk7.dim.yellow("demo"));
3011
+ const first = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0].toLowerCase();
3012
+ user = " " + chalk7.dim("@") + chalk7.green(first);
3010
3013
  }
3011
- return "\n" + parts.join(" ") + chalk7.bold(" > ");
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")} `);
3012
3018
  }
3013
- // ============================================================
3014
- // TAB COMPLETION
3015
- // ============================================================
3016
- completer(line) {
3017
- if (!line.startsWith("/")) return [[], line];
3018
- const allNames = [];
3019
- for (const cmd of this.commands.values()) {
3020
- allNames.push("/" + cmd.name);
3021
- for (const alias of cmd.aliases) {
3022
- allNames.push("/" + alias);
3023
- }
3024
- }
3025
- const hits = allNames.filter((n) => n.startsWith(line));
3026
- return [hits.length ? hits : allNames, line];
3027
- }
3028
- // ============================================================
3029
- // AUTH CHECK
3030
- // ============================================================
3031
- async checkAuthStatus() {
3019
+ // ─── AUTH CHECK ──────────────────────────────────────────
3020
+ async checkAuth() {
3032
3021
  try {
3033
- const apiKey = await configManager.getApiKey();
3022
+ const key = await configManager.getApiKey();
3034
3023
  if (this.isDemo) {
3035
- if (apiKey) {
3036
- this.isAuthenticated = true;
3037
- this.userProfile = { ...DEMO_USER2 };
3038
- } else {
3039
- this.isAuthenticated = false;
3040
- this.userProfile = null;
3041
- }
3024
+ this.isAuthenticated = !!key;
3025
+ this.userProfile = key ? { ...DEMO_USER2 } : null;
3042
3026
  return;
3043
3027
  }
3044
- if (!apiKey) return;
3028
+ if (!key) return;
3045
3029
  try {
3046
- const profile = await apiClient.getProfile();
3030
+ this.userProfile = await apiClient.getProfile();
3047
3031
  this.isAuthenticated = true;
3048
- this.userProfile = profile;
3049
3032
  } catch {
3050
3033
  this.isAuthenticated = true;
3051
3034
  this.userProfile = null;
@@ -3055,10 +3038,8 @@ var InteractiveSession = class {
3055
3038
  this.userProfile = null;
3056
3039
  }
3057
3040
  }
3058
- // ============================================================
3059
- // SLASH COMMAND DISPATCH
3060
- // ============================================================
3061
- async handleSlashCommand(input) {
3041
+ // ─── SLASH COMMAND DISPATCH ──────────────────────────────
3042
+ async handleSlash(input) {
3062
3043
  const parts = input.split(/\s+/);
3063
3044
  const cmdName = parts[0].substring(1).toLowerCase();
3064
3045
  const args = parts.slice(1).join(" ").trim();
@@ -3070,60 +3051,43 @@ var InteractiveSession = class {
3070
3051
  }
3071
3052
  }
3072
3053
  if (!command) {
3054
+ console.log();
3073
3055
  logger.warn(`Unknown command: /${cmdName}. Type /help for available commands.`);
3074
3056
  return;
3075
3057
  }
3076
3058
  if (command.requiresAuth && !this.isAuthenticated) {
3077
- logger.warn("You need to be logged in for this command. Type /login first.");
3059
+ console.log();
3060
+ logger.warn("You need to be logged in. Type /login first.");
3078
3061
  return;
3079
3062
  }
3080
- this.isBusy = true;
3081
- this.rl.close();
3082
- try {
3083
- await command.handler(args);
3084
- } catch (error) {
3085
- logger.error(error.message || "Command failed");
3086
- }
3087
- this.resetStdin();
3088
- await sleep2(80);
3089
- this.recreateReadline();
3063
+ console.log();
3064
+ await command.handler(args);
3090
3065
  }
3091
- // ============================================================
3092
- // AI PROMPT HANDLER
3093
- // ============================================================
3094
- async handleAIPrompt(input) {
3066
+ // ─── AI PROMPT ───────────────────────────────────────────
3067
+ async handlePrompt(input) {
3095
3068
  if (!this.isAuthenticated) {
3096
3069
  console.log();
3097
- 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.");
3098
3071
  return;
3099
3072
  }
3100
3073
  console.log();
3101
3074
  if (this.isDemo) {
3102
- this.isBusy = true;
3103
- this.rl.close();
3104
- try {
3105
- await demoPromptCommand(input, { ...this.options, mode: this.currentMode });
3106
- this.conversationHistory.push(
3107
- { role: "user", content: input, timestamp: /* @__PURE__ */ new Date() },
3108
- { role: "assistant", content: "[demo response]", timestamp: /* @__PURE__ */ new Date() }
3109
- );
3110
- } catch (error) {
3111
- logger.error(error.message || "Command failed");
3112
- }
3113
- this.resetStdin();
3114
- await sleep2(80);
3115
- this.recreateReadline();
3075
+ await demoPromptCommand(input, { ...this.options, mode: this.currentMode });
3076
+ this.conversationHistory.push(
3077
+ { role: "user", content: input, timestamp: /* @__PURE__ */ new Date() },
3078
+ { role: "assistant", content: "[demo response]", timestamp: /* @__PURE__ */ new Date() }
3079
+ );
3116
3080
  return;
3117
3081
  }
3118
3082
  if (!this.sessionContext) {
3119
3083
  logger.startSpinner("Loading codebase context...");
3120
3084
  try {
3121
- const collector = new ContextCollector(process.cwd());
3122
- this.sessionContext = await collector.collect(input);
3085
+ const col = new ContextCollector(process.cwd());
3086
+ this.sessionContext = await col.collect(input);
3123
3087
  logger.succeedSpinner(`Context loaded \u2014 ${this.sessionContext.files.length} files`);
3124
- } catch (error) {
3088
+ } catch (err) {
3125
3089
  logger.failSpinner("Failed to load context");
3126
- logger.error(error.message);
3090
+ logger.error(err.message);
3127
3091
  return;
3128
3092
  }
3129
3093
  }
@@ -3131,8 +3095,8 @@ var InteractiveSession = class {
3131
3095
  logger.info(chalk7.dim("DevWing is thinking..."));
3132
3096
  console.log();
3133
3097
  try {
3134
- const request = {
3135
- prompt: this.buildPromptWithHistory(),
3098
+ const req = {
3099
+ prompt: this.buildHistory(),
3136
3100
  mode: this.currentMode,
3137
3101
  model: this.currentModel || void 0,
3138
3102
  project_id: this.options.project || configManager.getProjectId(),
@@ -3140,292 +3104,164 @@ var InteractiveSession = class {
3140
3104
  stream: true,
3141
3105
  max_tokens: 4096
3142
3106
  };
3143
- let assistantResponse = "";
3107
+ let reply = "";
3144
3108
  streamingRenderer.reset();
3145
3109
  streamingRenderer.clearPendingTools();
3146
- for await (const message of apiClient.streamCompletion(request)) {
3147
- await streamingRenderer.processMessage(message);
3148
- if (message.type === "token" && message.content) {
3149
- assistantResponse += message.content;
3150
- }
3151
- if (message.session_id) {
3152
- this.sessionId = message.session_id;
3153
- }
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;
3154
3114
  }
3155
- if (assistantResponse) {
3156
- 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() });
3157
3117
  }
3158
- const pendingTools = streamingRenderer.getPendingToolCalls();
3159
- if (pendingTools.length > 0) {
3118
+ const pending = streamingRenderer.getPendingToolCalls();
3119
+ if (pending.length > 0) {
3160
3120
  logger.newline();
3161
- logger.info("Executing tools...");
3162
- const toolResults = await streamingRenderer.executePendingTools();
3121
+ const results = await streamingRenderer.executePendingTools();
3163
3122
  if (this.sessionId) {
3164
- for await (const message of apiClient.continueCompletion(this.sessionId, toolResults)) {
3165
- await streamingRenderer.processMessage(message);
3123
+ for await (const msg of apiClient.continueCompletion(this.sessionId, results)) {
3124
+ await streamingRenderer.processMessage(msg);
3166
3125
  }
3167
3126
  }
3168
3127
  }
3169
3128
  console.log();
3170
- } catch (error) {
3171
- logger.error(error.message || "AI request failed");
3129
+ } catch (err) {
3130
+ logger.error(err.message || "AI request failed");
3172
3131
  }
3173
3132
  }
3174
- buildPromptWithHistory() {
3175
- const history = this.conversationHistory;
3176
- if (history.length <= 1) {
3177
- return history[history.length - 1]?.content || "";
3178
- }
3179
- const recent = history.slice(-10);
3180
- 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");
3181
3138
  return `Previous conversation:
3182
- ${context}
3139
+ ${ctx}
3183
3140
 
3184
3141
  Current question:
3185
3142
  ${recent[recent.length - 1].content}`;
3186
3143
  }
3187
- // ============================================================
3188
- // EXIT
3189
- // ============================================================
3190
- exitSession() {
3191
- if (this.sigintTimer) clearTimeout(this.sigintTimer);
3192
- this.resetStdin();
3144
+ // ─── EXIT ────────────────────────────────────────────────
3145
+ exit() {
3146
+ restoreStdin();
3193
3147
  console.log();
3194
- console.log(chalk7.dim(" Goodbye! Happy coding."));
3148
+ console.log(chalk7.dim(" Goodbye! Happy shipping."));
3195
3149
  console.log();
3196
3150
  process.exit(0);
3197
3151
  }
3198
- // ============================================================
3199
- // COMMAND REGISTRATION
3200
- // ============================================================
3152
+ // ─── COMMAND REGISTRATION ────────────────────────────────
3201
3153
  registerCommands() {
3202
- this.addCommand({
3203
- name: "help",
3204
- aliases: ["h", "?"],
3205
- description: "Show all available commands",
3206
- category: "session",
3207
- requiresAuth: false,
3208
- handler: async () => this.cmdHelp()
3209
- });
3210
- this.addCommand({
3211
- name: "exit",
3212
- aliases: ["quit", "q"],
3213
- description: "Exit DevWing",
3214
- category: "session",
3215
- requiresAuth: false,
3216
- handler: async () => this.exitSession()
3217
- });
3218
- this.addCommand({
3219
- name: "clear",
3220
- aliases: ["cls"],
3221
- description: "Clear screen and conversation",
3222
- category: "session",
3223
- requiresAuth: false,
3224
- handler: async () => this.cmdClear()
3225
- });
3226
- this.addCommand({
3227
- name: "login",
3228
- aliases: ["signin"],
3229
- description: "Authenticate with DevWing",
3230
- category: "session",
3231
- requiresAuth: false,
3232
- handler: async () => this.cmdLogin()
3233
- });
3234
- this.addCommand({
3235
- name: "logout",
3236
- aliases: ["signout"],
3237
- description: "Clear credentials and log out",
3238
- category: "session",
3239
- requiresAuth: true,
3240
- handler: async () => this.cmdLogout()
3241
- });
3242
- this.addCommand({
3243
- name: "status",
3244
- aliases: ["whoami"],
3245
- description: "Show auth status and profile",
3246
- category: "session",
3247
- requiresAuth: false,
3248
- handler: async () => this.cmdStatus()
3249
- });
3250
- this.addCommand({
3251
- name: "scan",
3252
- aliases: [],
3253
- description: "Run security vulnerability scan",
3254
- category: "tools",
3255
- requiresAuth: true,
3256
- handler: async () => this.cmdScan()
3257
- });
3258
- this.addCommand({
3259
- name: "review",
3260
- aliases: [],
3261
- description: "Perform code review on recent changes",
3262
- category: "tools",
3263
- requiresAuth: true,
3264
- handler: async () => this.cmdReview()
3265
- });
3266
- this.addCommand({
3267
- name: "explain",
3268
- aliases: [],
3269
- description: "Explain code, file, or concept",
3270
- category: "tools",
3271
- requiresAuth: true,
3272
- handler: async (args) => this.cmdExplain(args)
3273
- });
3274
- this.addCommand({
3275
- name: "memory",
3276
- aliases: ["mem"],
3277
- description: "Manage project memory (save/show/clear)",
3278
- category: "tools",
3279
- requiresAuth: true,
3280
- handler: async (args) => this.cmdMemory(args)
3281
- });
3282
- this.addCommand({
3283
- name: "mode",
3284
- aliases: [],
3285
- description: "Show or set AI mode",
3286
- category: "config",
3287
- requiresAuth: false,
3288
- handler: async (args) => this.cmdMode(args)
3289
- });
3290
- this.addCommand({
3291
- name: "model",
3292
- aliases: [],
3293
- description: "Show or set AI model",
3294
- category: "config",
3295
- requiresAuth: false,
3296
- handler: async (args) => this.cmdModel(args)
3297
- });
3298
- this.addCommand({
3299
- name: "models",
3300
- aliases: [],
3301
- description: "List all available AI models",
3302
- category: "config",
3303
- requiresAuth: false,
3304
- handler: async () => this.cmdModels()
3305
- });
3306
- this.addCommand({
3307
- name: "usage",
3308
- aliases: ["stats"],
3309
- description: "Show token usage statistics",
3310
- category: "config",
3311
- requiresAuth: true,
3312
- handler: async () => this.cmdUsage()
3313
- });
3314
- this.addCommand({
3315
- name: "plans",
3316
- aliases: ["pricing"],
3317
- description: "Show available plans and pricing",
3318
- category: "config",
3319
- requiresAuth: false,
3320
- handler: async () => this.cmdPlans()
3321
- });
3322
- this.addCommand({
3323
- name: "context",
3324
- aliases: ["ctx"],
3325
- description: "Show loaded codebase context",
3326
- category: "config",
3327
- requiresAuth: false,
3328
- handler: async () => this.cmdContext()
3329
- });
3330
- this.addCommand({
3331
- name: "history",
3332
- aliases: [],
3333
- description: "Show conversation history",
3334
- category: "config",
3335
- requiresAuth: false,
3336
- handler: async () => this.cmdHistory()
3337
- });
3338
- this.addCommand({
3339
- name: "config",
3340
- aliases: ["settings"],
3341
- description: "Show or set CLI configuration",
3342
- category: "config",
3343
- requiresAuth: false,
3344
- handler: async (args) => this.cmdConfig(args)
3345
- });
3346
- }
3347
- addCommand(cmd) {
3348
- this.commands.set(cmd.name, cmd);
3349
- }
3350
- // ============================================================
3351
- // COMMAND IMPLEMENTATIONS
3352
- // ============================================================
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 ─────────────────────────────
3353
3175
  async cmdHelp() {
3354
- console.log();
3355
- const categories = [
3176
+ const W = 62;
3177
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3178
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3179
+ const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
3180
+ const ln = (s = "") => {
3181
+ const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
3182
+ const pad = W - 4 - raw.length;
3183
+ return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3184
+ };
3185
+ console.log(top);
3186
+ console.log(ln(chalk7.bold.white("Commands")));
3187
+ const cats = [
3356
3188
  { key: "session", label: "Session", color: chalk7.bold.green },
3357
3189
  { key: "tools", label: "AI Tools", color: chalk7.bold.magenta },
3358
3190
  { key: "config", label: "Configuration", color: chalk7.bold.yellow }
3359
3191
  ];
3360
- const lines = [];
3361
- for (const cat of categories) {
3362
- lines.push(cat.color(` ${cat.label}`));
3192
+ for (const cat of cats) {
3193
+ console.log(div);
3194
+ console.log(ln(" " + cat.color(cat.label)));
3363
3195
  for (const cmd of this.commands.values()) {
3364
3196
  if (cmd.category !== cat.key) continue;
3365
- const aliasStr = cmd.aliases.length > 0 ? chalk7.dim(` (${cmd.aliases.map((a) => "/" + a).join(", ")})`) : "";
3366
- lines.push(` ${chalk7.cyan("/" + cmd.name.padEnd(12))} ${cmd.description}${aliasStr}`);
3197
+ const cmdStr = chalk7.cyan(("/" + cmd.name).padEnd(13));
3198
+ const descStr = chalk7.dim(cmd.description);
3199
+ const alias = cmd.aliases.length ? chalk7.dim(" \xB7 " + cmd.aliases.map((a) => "/" + a).join(", ")) : "";
3200
+ console.log(ln(" " + cmdStr + " " + descStr + alias));
3367
3201
  }
3368
- lines.push("");
3369
3202
  }
3370
- lines.push(chalk7.dim(" Just type a message to chat with DevWing AI."));
3371
- logger.box(lines.join("\n"), { title: "Commands", color: "cyan" });
3203
+ console.log(div);
3204
+ console.log(ln(chalk7.dim(" Type any message to chat with DevWing AI")));
3205
+ console.log(bot);
3372
3206
  }
3373
3207
  async cmdClear() {
3374
3208
  this.conversationHistory = [];
3375
3209
  this.sessionContext = null;
3376
3210
  this.sessionId = null;
3377
3211
  console.clear();
3378
- this.printStartupBanner();
3212
+ this.printBanner();
3379
3213
  this.printSystemInfo();
3214
+ console.log();
3380
3215
  }
3381
3216
  async cmdLogin() {
3382
- console.log();
3383
3217
  if (this.isDemo) {
3384
3218
  await demoLoginCommand({ fromSession: true });
3385
3219
  } else {
3386
3220
  await loginCommand();
3387
3221
  }
3388
- await this.checkAuthStatus();
3389
- this.rl.setPrompt(this.buildPrompt());
3222
+ restoreStdin();
3223
+ await sleep2(80);
3224
+ await this.checkAuth();
3390
3225
  }
3391
3226
  async cmdLogout() {
3392
- console.log();
3393
3227
  if (this.isDemo) {
3394
3228
  await demoLogoutCommand();
3395
3229
  } else {
3396
3230
  await logoutCommand();
3397
3231
  }
3398
- await this.checkAuthStatus();
3399
- this.rl.setPrompt(this.buildPrompt());
3232
+ restoreStdin();
3233
+ await sleep2(80);
3234
+ await this.checkAuth();
3400
3235
  }
3401
3236
  async cmdStatus() {
3402
- console.log();
3403
3237
  if (!this.isAuthenticated) {
3404
3238
  logger.info("Not logged in. Type /login to authenticate.");
3405
3239
  return;
3406
3240
  }
3407
3241
  if (this.isDemo) {
3408
- const table = new Table3({
3409
- chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3410
- });
3411
- table.push(
3412
- [chalk7.bold("Email"), DEMO_USER2.email],
3413
- [chalk7.bold("Name"), DEMO_USER2.full_name],
3414
- [chalk7.bold("Plan"), chalk7.cyan(DEMO_USER2.subscription_plan.toUpperCase())],
3415
- [chalk7.bold("Verified"), chalk7.green("Yes")],
3416
- [chalk7.bold("2FA"), chalk7.dim("Disabled")],
3417
- [chalk7.bold("Workspace"), `${DEMO_WORKSPACE2.name} (${DEMO_WORKSPACE2.plan})`],
3418
- [chalk7.bold("Project"), DEMO_PROJECT2.name],
3419
- [chalk7.bold("Tokens Today"), `${DEMO_USER2.tokens_used_today?.toLocaleString()} / 500,000`],
3420
- [chalk7.bold("Member Since"), new Date(DEMO_USER2.created_at).toLocaleDateString()]
3421
- );
3422
- console.log(table.toString());
3242
+ const W = 54;
3243
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3244
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3245
+ const kv = (k, v) => {
3246
+ const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
3247
+ const pad = W - 4 - k.length - rawV.length;
3248
+ return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3249
+ };
3250
+ console.log(top);
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);
3423
3260
  } else {
3424
3261
  await statusCommand();
3425
3262
  }
3426
3263
  }
3427
3264
  async cmdScan() {
3428
- console.log();
3429
3265
  if (this.isDemo) {
3430
3266
  await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...this.options, mode: "security" });
3431
3267
  } else {
@@ -3433,7 +3269,6 @@ ${recent[recent.length - 1].content}`;
3433
3269
  }
3434
3270
  }
3435
3271
  async cmdReview() {
3436
- console.log();
3437
3272
  if (this.isDemo) {
3438
3273
  await demoReviewCommand();
3439
3274
  } else {
@@ -3442,11 +3277,9 @@ ${recent[recent.length - 1].content}`;
3442
3277
  }
3443
3278
  async cmdExplain(args) {
3444
3279
  if (!args) {
3445
- logger.error("Usage: /explain <file, function, or concept>");
3446
- logger.info("Example: /explain src/auth/middleware.ts");
3280
+ logger.error("Usage: /explain <file or concept>");
3447
3281
  return;
3448
3282
  }
3449
- console.log();
3450
3283
  if (this.isDemo) {
3451
3284
  await demoExplainCommand(args);
3452
3285
  } else {
@@ -3457,275 +3290,240 @@ ${recent[recent.length - 1].content}`;
3457
3290
  const parts = args.split(/\s+/);
3458
3291
  const sub = parts[0]?.toLowerCase();
3459
3292
  if (!sub || !["save", "show", "clear"].includes(sub)) {
3460
- console.log();
3461
3293
  console.log(` ${chalk7.cyan("/memory show")} \u2014 Show all project memories`);
3462
3294
  console.log(` ${chalk7.cyan("/memory save <text>")} \u2014 Save a memory`);
3463
3295
  console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
3464
3296
  return;
3465
3297
  }
3466
- console.log();
3467
3298
  const content = parts.slice(1).join(" ").trim();
3468
3299
  if (this.isDemo) {
3469
3300
  await demoMemoryCommand(sub, content || void 0);
3301
+ restoreStdin();
3302
+ await sleep2(80);
3470
3303
  } else {
3471
3304
  await memoryCommand(sub, content || void 0, this.options);
3472
3305
  }
3473
3306
  }
3474
3307
  async cmdMode(args) {
3475
- const validModes = ["general", "frontend", "backend", "security", "devops"];
3308
+ const valid = ["general", "frontend", "backend", "security", "devops"];
3476
3309
  if (!args) {
3477
- console.log();
3478
- console.log(` Current mode: ${chalk7.yellow.bold(this.currentMode)}`);
3479
- console.log();
3480
- console.log(" Available modes:");
3481
- for (const mode2 of validModes) {
3482
- const indicator = mode2 === this.currentMode ? chalk7.green(" \u25CF") : chalk7.dim(" \u25CB");
3483
- const desc = mode2 === "general" ? "All-purpose coding assistant" : mode2 === "frontend" ? "React, Vue, CSS, TypeScript, accessibility" : mode2 === "backend" ? "APIs, databases, microservices, system design" : mode2 === "security" ? "OWASP, CVE, penetration testing, compliance" : "Docker, Kubernetes, CI/CD, infrastructure";
3484
- console.log(` ${indicator} ${chalk7.cyan(mode2.padEnd(12))} ${chalk7.dim(desc)}`);
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"
3316
+ };
3317
+ for (const m of valid) {
3318
+ const active = m === this.currentMode;
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])}`);
3485
3322
  }
3486
3323
  console.log();
3487
- console.log(chalk7.dim(` Usage: /mode <name>`));
3324
+ console.log(chalk7.dim(" Usage: /mode <name>"));
3488
3325
  return;
3489
3326
  }
3490
3327
  const mode = args.toLowerCase();
3491
- if (!validModes.includes(mode)) {
3492
- logger.error(`Invalid mode "${args}". Valid: ${validModes.join(", ")}`);
3328
+ if (!valid.includes(mode)) {
3329
+ logger.error(`Invalid mode "${args}". Valid: ${valid.join(", ")}`);
3493
3330
  return;
3494
3331
  }
3495
3332
  this.currentMode = mode;
3496
3333
  this.options.mode = mode;
3497
- logger.success(`Mode set to ${chalk7.yellow.bold(mode)}`);
3334
+ console.log(` ${chalk7.green("\u2713")} Mode \u2192 ${chalk7.yellow.bold(mode)}`);
3498
3335
  }
3499
3336
  async cmdModel(args) {
3500
3337
  if (!args) {
3501
- console.log();
3502
- const current = this.currentModel || "auto (selected by mode)";
3503
- console.log(` Current model: ${chalk7.cyan.bold(current)}`);
3504
- console.log(chalk7.dim(` Usage: /model <name> | /model auto | /models to list all`));
3338
+ const cur = this.currentModel || "auto";
3339
+ console.log(` Current model: ${chalk7.cyan.bold(cur)}`);
3340
+ console.log(chalk7.dim(" /model <name> \xB7 /model auto \xB7 /models to list all"));
3505
3341
  return;
3506
3342
  }
3507
3343
  if (args === "auto") {
3508
3344
  this.currentModel = null;
3509
3345
  this.options.model = void 0;
3510
- logger.success("Model set to auto (selected by mode)");
3346
+ logger.success("Model \u2192 auto");
3511
3347
  return;
3512
3348
  }
3513
3349
  this.currentModel = args;
3514
3350
  this.options.model = args;
3515
- logger.success(`Model set to ${chalk7.cyan.bold(args)}`);
3351
+ console.log(` ${chalk7.green("\u2713")} Model \u2192 ${chalk7.cyan.bold(args)}`);
3516
3352
  }
3517
3353
  async cmdModels() {
3518
- console.log();
3519
3354
  if (this.isDemo) {
3520
3355
  const table = new Table3({
3521
- head: [
3522
- chalk7.bold("Model"),
3523
- chalk7.bold("Domain"),
3524
- chalk7.bold("Status"),
3525
- chalk7.bold("Min Plan"),
3526
- chalk7.bold("Context"),
3527
- chalk7.bold("Speed")
3528
- ],
3529
- colWidths: [24, 12, 10, 10, 10, 12]
3356
+ head: [chalk7.bold("Model"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Plan"), chalk7.bold("Context"), chalk7.bold("Speed")],
3357
+ style: { border: ["dim"] }
3530
3358
  });
3531
3359
  for (const m of DEMO_MODELS) {
3532
- const statusColor = m.status === "active" ? chalk7.green : chalk7.yellow;
3533
- const planColor = m.min_plan === "free" ? chalk7.green : chalk7.cyan;
3534
- table.push([
3535
- chalk7.bold(m.display_name),
3536
- chalk7.yellow(m.domain),
3537
- statusColor(m.status),
3538
- planColor(m.min_plan),
3539
- `${(m.context_window / 1024).toFixed(0)}k`,
3540
- `${m.tokens_per_sec} tok/s`
3541
- ]);
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`]);
3542
3365
  }
3543
3366
  console.log(table.toString());
3544
- console.log();
3545
- console.log(chalk7.dim(` Set a model: /model devwing-backend-1 | /model auto`));
3367
+ console.log(chalk7.dim(" /model <name> to select \xB7 /model auto for automatic"));
3546
3368
  } else {
3547
3369
  try {
3548
3370
  const models = await apiClient.getModels();
3549
- if (models.length === 0) {
3371
+ if (!models.length) {
3550
3372
  logger.info("No models available");
3551
3373
  return;
3552
3374
  }
3553
- const table = new Table3({
3554
- head: [chalk7.bold("Name"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Min Plan")]
3555
- });
3556
- for (const m of models) {
3557
- table.push([m.display_name || m.name, m.domain, m.status, m.min_plan]);
3558
- }
3375
+ const table = new Table3({ head: [chalk7.bold("Name"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Plan")] });
3376
+ for (const m of models) table.push([m.display_name || m.name, m.domain, m.status, m.min_plan]);
3559
3377
  console.log(table.toString());
3560
- } catch (error) {
3561
- logger.error("Failed to fetch models: " + error.message);
3378
+ } catch (e) {
3379
+ logger.error("Failed to fetch models: " + e.message);
3562
3380
  }
3563
3381
  }
3564
3382
  }
3565
3383
  async cmdUsage() {
3566
- console.log();
3567
3384
  if (this.isDemo) {
3568
- console.log(chalk7.bold(" Token Usage"));
3569
- console.log();
3570
- const table = new Table3({
3571
- head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")]
3385
+ const t = new Table3({
3386
+ head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")],
3387
+ style: { border: ["dim"] }
3572
3388
  });
3573
- table.push(
3574
- ["Today", DEMO_USAGE.today.requests.toString(), DEMO_USAGE.today.tokens_in.toLocaleString(), DEMO_USAGE.today.tokens_out.toLocaleString(), `$${DEMO_USAGE.today.cost.toFixed(4)}`],
3575
- ["This Week", DEMO_USAGE.week.requests.toString(), DEMO_USAGE.week.tokens_in.toLocaleString(), DEMO_USAGE.week.tokens_out.toLocaleString(), `$${DEMO_USAGE.week.cost.toFixed(4)}`],
3576
- ["This Month", DEMO_USAGE.month.requests.toString(), DEMO_USAGE.month.tokens_in.toLocaleString(), DEMO_USAGE.month.tokens_out.toLocaleString(), `$${DEMO_USAGE.month.cost.toFixed(4)}`]
3389
+ t.push(
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)}`],
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)}`],
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)}`]
3577
3393
  );
3578
- console.log(table.toString());
3579
- console.log();
3394
+ console.log(t.toString());
3580
3395
  const used = DEMO_USER2.tokens_used_today;
3581
3396
  const limit = 5e5;
3582
3397
  const pct = Math.round(used / limit * 100);
3583
- const barWidth = 30;
3584
- const filled = Math.round(pct / 100 * barWidth);
3585
- const bar = chalk7.cyan("\u2588".repeat(filled)) + chalk7.dim("\u2591".repeat(barWidth - filled));
3586
- console.log(` Daily Quota: ${bar} ${pct}% (${used.toLocaleString()} / ${limit.toLocaleString()})`);
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));
3587
3401
  console.log();
3588
- console.log(chalk7.bold(" Usage by Model"));
3402
+ console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()}`)}`);
3589
3403
  console.log();
3590
- const modelTable = new Table3({
3591
- head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")]
3404
+ const mt = new Table3({
3405
+ head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")],
3406
+ style: { border: ["dim"] }
3592
3407
  });
3593
- modelTable.push(
3408
+ mt.push(
3594
3409
  ["devwing-backend-1", "312", "421,000", "$1.4721"],
3595
3410
  ["devwing-security-1", "145", "198,500", "$0.6948"],
3596
3411
  ["devwing-general-1", "98", "132,200", "$0.4627"],
3597
3412
  ["devwing-frontend-1", "42", "56,800", "$0.1988"],
3598
3413
  ["devwing-devops-1", "15", "15,600", "$0.1728"]
3599
3414
  );
3600
- console.log(modelTable.toString());
3415
+ console.log(mt.toString());
3601
3416
  } else {
3602
3417
  try {
3603
- const usage = await apiClient.getUsage();
3604
- console.log(` Total Requests: ${usage.total_requests}`);
3605
- console.log(` Tokens In: ${usage.total_tokens_in.toLocaleString()}`);
3606
- console.log(` Tokens Out: ${usage.total_tokens_out.toLocaleString()}`);
3607
- console.log(` Cost: $${usage.total_cost_usd.toFixed(4)}`);
3608
- } catch (error) {
3609
- logger.error("Failed to fetch usage: " + error.message);
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)}`);
3422
+ } catch (e) {
3423
+ logger.error("Failed to fetch usage: " + e.message);
3610
3424
  }
3611
3425
  }
3612
3426
  }
3613
3427
  async cmdPlans() {
3614
- console.log();
3615
3428
  const table = new Table3({
3616
- head: [
3617
- chalk7.bold("Plan"),
3618
- chalk7.bold("Price"),
3619
- chalk7.bold("Tokens/Day"),
3620
- chalk7.bold("Models"),
3621
- chalk7.bold("Projects"),
3622
- chalk7.bold("Seats")
3623
- ],
3624
- colWidths: [14, 12, 14, 16, 12, 14]
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"] }
3625
3431
  });
3626
- for (const plan of DEMO_PLANS) {
3627
- const isCurrentPlan = this.isAuthenticated && this.userProfile?.subscription_plan?.toLowerCase() === plan.name.toLowerCase();
3628
- const nameStr = isCurrentPlan ? chalk7.cyan.bold(plan.name + " \u25CF") : plan.name;
3629
- 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]);
3630
3437
  }
3631
3438
  console.log(table.toString());
3632
3439
  console.log();
3633
3440
  if (this.isAuthenticated) {
3634
- const currentPlan = this.userProfile?.subscription_plan || "free";
3635
- console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()}. Upgrade at https://devwing.ai/dashboard/billing`));
3441
+ console.log(chalk7.dim(` Your plan: ${cur.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3636
3442
  } else {
3637
- console.log(chalk7.dim(" Sign up at https://devwing.ai to get started."));
3443
+ console.log(chalk7.dim(" Sign up at devwing.ai"));
3638
3444
  }
3639
3445
  }
3640
3446
  async cmdContext() {
3641
- console.log();
3642
3447
  if (this.isDemo) {
3643
- const table = new Table3({
3644
- chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3645
- });
3646
- table.push(
3647
- [chalk7.bold("Working Dir"), chalk7.cyan(process.cwd().replace(process.env.HOME || "", "~"))],
3648
- [chalk7.bold("Git Branch"), "main"],
3649
- [chalk7.bold("Files Loaded"), "34"],
3650
- [chalk7.bold("Framework"), "FastAPI + TypeScript"],
3651
- [chalk7.bold("Shell"), process.env.SHELL?.split("/").pop() || "unknown"],
3652
- [chalk7.bold("OS"), process.platform]
3653
- );
3654
- console.log(table.toString());
3655
- console.log();
3656
- console.log(chalk7.bold(" Top files in context:"));
3657
- const files = [
3658
- "src/auth/auth_controller.ts",
3659
- "src/middleware/jwt.middleware.ts",
3660
- "src/services/user_service.ts",
3661
- "src/auth/repositories/user.repository.ts",
3662
- "package.json",
3663
- "tsconfig.json",
3664
- "docker-compose.yml"
3665
- ];
3666
- for (const f of files) {
3667
- console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f)}`);
3668
- }
3669
- console.log(chalk7.dim(" ... and 27 more"));
3448
+ const W = 56;
3449
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3450
+ const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3451
+ const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
3452
+ const kv = (k, v) => {
3453
+ const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
3454
+ const pad = W - 4 - k.length - rv.length;
3455
+ return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3456
+ };
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");
3460
+ };
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");
3465
+ };
3466
+ console.log(top);
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"));
3471
+ console.log(div);
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);
3670
3478
  } else if (this.sessionContext) {
3671
- console.log(` Working Dir: ${chalk7.cyan(this.sessionContext.cwd)}`);
3672
- console.log(` Shell: ${this.sessionContext.shell} | OS: ${this.sessionContext.os}`);
3673
- console.log(` Files: ${this.sessionContext.files.length}`);
3674
- if (this.sessionContext.git_branch) {
3675
- console.log(` Git Branch: ${this.sessionContext.git_branch}`);
3676
- }
3677
- console.log();
3678
- console.log(chalk7.bold(" Files in context:"));
3479
+ console.log(` Dir: ${this.sessionContext.cwd} \xB7 Files: ${this.sessionContext.files.length}`);
3679
3480
  for (const f of this.sessionContext.files.slice(0, 10)) {
3680
- console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f.path)}`);
3681
- }
3682
- if (this.sessionContext.files.length > 10) {
3683
- console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3481
+ console.log(` ${chalk7.dim("\xB7")} ${chalk7.cyan(f.path)}`);
3684
3482
  }
3685
3483
  } else {
3686
3484
  logger.info("No context loaded yet. Send a message to load context.");
3687
3485
  }
3688
3486
  }
3689
3487
  async cmdHistory() {
3690
- console.log();
3691
- if (this.conversationHistory.length === 0) {
3488
+ if (!this.conversationHistory.length) {
3692
3489
  logger.info("No conversation history yet.");
3693
3490
  return;
3694
3491
  }
3695
- for (const msg of this.conversationHistory.slice(-20)) {
3696
- const label = msg.role === "user" ? chalk7.green.bold("You") : chalk7.cyan.bold("DevWing");
3697
- const time = chalk7.dim(msg.timestamp.toLocaleTimeString());
3698
- const preview = msg.content.length > 120 ? msg.content.substring(0, 120) + "..." : msg.content;
3699
- console.log(` ${time} ${label}: ${preview}`);
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;
3496
+ console.log(` ${time} ${label} ${preview}`);
3700
3497
  }
3701
3498
  }
3702
3499
  async cmdConfig(args) {
3703
3500
  if (!args) {
3704
- console.log();
3705
- const config = configManager.getAll();
3706
- const table = new Table3({
3707
- chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3708
- });
3709
- table.push(
3710
- [chalk7.bold("API URL"), config.apiUrl || chalk7.dim("default")],
3711
- [chalk7.bold("Workspace"), config.workspaceId || chalk7.dim("none")],
3712
- [chalk7.bold("Project"), config.projectId || chalk7.dim("none")],
3713
- [chalk7.bold("Model"), config.model || chalk7.dim("auto")],
3714
- [chalk7.bold("Mode"), config.mode || chalk7.dim("general")],
3715
- [chalk7.bold("Config Path"), configManager.getPath()]
3716
- );
3717
- console.log(table.toString());
3718
- console.log();
3719
- console.log(chalk7.dim(" Usage: /config set <key> <value> | /config get <key>"));
3501
+ const cfg = configManager.getAll();
3502
+ const W = 54;
3503
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
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;
3508
+ return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3509
+ };
3510
+ console.log(top);
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);
3518
+ console.log(chalk7.dim(" /config set <key> <value> \xB7 /config get <key>"));
3720
3519
  return;
3721
3520
  }
3722
3521
  const parts = args.split(/\s+/);
3723
- const sub = parts[0];
3724
- if (sub === "list") {
3522
+ if (parts[0] === "list") {
3725
3523
  await configCommand("list");
3726
- } else if (sub === "get" && parts[1]) {
3524
+ } else if (parts[0] === "get" && parts[1]) {
3727
3525
  await configCommand("get", parts[1]);
3728
- } else if (sub === "set" && parts[1] && parts[2]) {
3526
+ } else if (parts[0] === "set" && parts[1] && parts[2]) {
3729
3527
  await configCommand("set", parts[1], parts[2]);
3730
3528
  if (parts[1] === "mode") this.currentMode = parts[2];
3731
3529
  if (parts[1] === "model") this.currentModel = parts[2];