devwing 0.1.20 → 0.1.22

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
@@ -2679,6 +2679,20 @@ var TOOL_ICONS = {
2679
2679
  http_request: "\u{1F310}",
2680
2680
  cve_lookup: "\u{1F512}"
2681
2681
  };
2682
+ function resetStdinState() {
2683
+ try {
2684
+ if (typeof process.stdin.setRawMode === "function") {
2685
+ try {
2686
+ process.stdin.setRawMode(false);
2687
+ } catch {
2688
+ }
2689
+ }
2690
+ process.stdin.removeAllListeners("keypress");
2691
+ process.stdin.removeAllListeners("data");
2692
+ if (process.stdin.isPaused()) process.stdin.resume();
2693
+ } catch {
2694
+ }
2695
+ }
2682
2696
  async function showToolCall(tool, args) {
2683
2697
  await sleep(PAUSE_SHORT);
2684
2698
  console.log();
@@ -2697,6 +2711,8 @@ async function showToolCall(tool, args) {
2697
2711
  default: true
2698
2712
  }
2699
2713
  ]);
2714
+ resetStdinState();
2715
+ await sleep(60);
2700
2716
  if (!allowed) {
2701
2717
  console.log(chalk7.dim(" \u2717 Skipped"));
2702
2718
  throw Object.assign(new Error("TOOL_SKIPPED"), { skipped: true });
@@ -2771,7 +2787,7 @@ function getVersion() {
2771
2787
  continue;
2772
2788
  }
2773
2789
  }
2774
- return "0.1.20";
2790
+ return "0.1.22";
2775
2791
  }
2776
2792
  var DEMO_USER2 = {
2777
2793
  id: "usr_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
@@ -2786,8 +2802,7 @@ var DEMO_USER2 = {
2786
2802
  tokens_reset_at: "2026-04-10T00:00:00Z"
2787
2803
  };
2788
2804
  var DEMO_WORKSPACE2 = {
2789
- name: "Kano State Government",
2790
- plan: "enterprise"};
2805
+ name: "Kano State Government"};
2791
2806
  var DEMO_PROJECT2 = {
2792
2807
  name: "Tallon SaaS ERP"};
2793
2808
  var DEMO_MODELS = [
@@ -2796,11 +2811,11 @@ var DEMO_MODELS = [
2796
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" },
2797
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" },
2798
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" },
2799
- { 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" },
2800
- { 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" }
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" }
2801
2816
  ];
2802
2817
  var DEMO_PLANS = [
2803
- { name: "Free", price: "$0", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
2818
+ { name: "Free", price: "$0/mo", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
2804
2819
  { name: "Pro", price: "$19/mo", tokens_day: "500,000", models: "All models", projects: "10", seats: "1" },
2805
2820
  { name: "Team", price: "$99/mo", tokens_day: "2,000,000", models: "All + priority", projects: "Unlimited", seats: "5 included" },
2806
2821
  { name: "Enterprise", price: "Custom", tokens_day: "Unlimited", models: "Dedicated", projects: "Unlimited", seats: "Unlimited" }
@@ -2825,8 +2840,7 @@ var InteractiveSession = class {
2825
2840
  sigintCount = 0;
2826
2841
  sigintTimer = null;
2827
2842
  version;
2828
- isBusy = false;
2829
- // prevent readline events during command execution
2843
+ isRunningCommand = false;
2830
2844
  constructor(options) {
2831
2845
  this.options = options;
2832
2846
  this.isDemo = isDemoMode();
@@ -2845,63 +2859,76 @@ var InteractiveSession = class {
2845
2859
  this.printSystemInfo();
2846
2860
  this.createReadline();
2847
2861
  console.log();
2848
- this.rl.prompt();
2862
+ this.prompt();
2849
2863
  }
2850
2864
  // ============================================================
2851
- // READLINE MANAGEMENT
2865
+ // READLINE — created once, never destroyed
2852
2866
  // ============================================================
2853
2867
  createReadline() {
2854
- this.isBusy = false;
2855
2868
  this.rl = readline.createInterface({
2856
2869
  input: process.stdin,
2857
2870
  output: process.stdout,
2858
- prompt: this.buildPrompt(),
2859
- completer: (line) => this.completer(line)
2871
+ prompt: "",
2872
+ terminal: true
2860
2873
  });
2861
2874
  this.rl.on("SIGINT", () => {
2875
+ if (this.isRunningCommand) {
2876
+ process.stdout.write("\n");
2877
+ return;
2878
+ }
2862
2879
  this.sigintCount++;
2863
2880
  if (this.sigintCount >= 2) {
2864
2881
  this.exitSession();
2865
2882
  return;
2866
2883
  }
2867
- console.log(chalk7.dim("\n Press Ctrl+C again to exit, or type /exit"));
2884
+ process.stdout.write(chalk7.dim("\n Press Ctrl+C again to exit, or type /exit\n"));
2868
2885
  this.sigintTimer = setTimeout(() => {
2869
2886
  this.sigintCount = 0;
2870
2887
  }, 1500);
2871
- this.rl.prompt();
2888
+ this.prompt();
2872
2889
  });
2873
- const currentRl = this.rl;
2874
2890
  this.rl.on("line", async (input) => {
2875
- if (this.isBusy) return;
2891
+ if (this.isRunningCommand) return;
2876
2892
  const trimmed = input.trim();
2877
2893
  if (!trimmed) {
2878
- this.rl.prompt();
2894
+ this.prompt();
2879
2895
  return;
2880
2896
  }
2881
- this.isBusy = true;
2897
+ this.isRunningCommand = true;
2898
+ this.rl.pause();
2882
2899
  try {
2883
2900
  if (trimmed.startsWith("/")) {
2884
2901
  await this.handleSlashCommand(trimmed);
2885
- return;
2886
2902
  } else {
2887
2903
  await this.handleAIPrompt(trimmed);
2888
2904
  }
2889
2905
  } catch (error) {
2890
2906
  logger.error(error.message || "An error occurred");
2891
2907
  }
2892
- this.isBusy = false;
2893
- if (this.rl === currentRl) {
2894
- this.rl.setPrompt(this.buildPrompt());
2895
- this.rl.prompt();
2896
- }
2908
+ this.restoreStdin();
2909
+ await sleep2(60);
2910
+ this.isRunningCommand = false;
2911
+ this.rl.resume();
2912
+ this.prompt();
2897
2913
  });
2898
2914
  this.rl.on("close", () => {
2899
- if (!this.isBusy) {
2915
+ if (!this.isRunningCommand) {
2900
2916
  this.exitSession();
2901
2917
  }
2902
2918
  });
2903
2919
  }
2904
- resetStdin() {
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();
2929
+ }
2930
+ }
2931
+ restoreStdin() {
2905
2932
  try {
2906
2933
  if (typeof process.stdin.setRawMode === "function") {
2907
2934
  try {
@@ -2911,18 +2938,17 @@ var InteractiveSession = class {
2911
2938
  }
2912
2939
  process.stdin.removeAllListeners("keypress");
2913
2940
  process.stdin.removeAllListeners("data");
2914
- if (process.stdin.isPaused()) {
2915
- process.stdin.resume();
2916
- }
2941
+ if (process.stdin.isPaused()) process.stdin.resume();
2917
2942
  } catch {
2918
2943
  }
2919
2944
  }
2920
- recreateReadline() {
2921
- this.createReadline();
2922
- this.rl.prompt();
2945
+ prompt() {
2946
+ const p = this.buildPrompt();
2947
+ process.stdout.write(p);
2948
+ this.rl.setPrompt(p);
2923
2949
  }
2924
2950
  // ============================================================
2925
- // STARTUP UI
2951
+ // STARTUP UI — Claude Code style
2926
2952
  // ============================================================
2927
2953
  printStartupBanner() {
2928
2954
  const banner = gradient.pastel.multiline([
@@ -2936,78 +2962,79 @@ var InteractiveSession = class {
2936
2962
  ""
2937
2963
  ].join("\n"));
2938
2964
  console.log(banner);
2939
- console.log(chalk7.dim(" Your AI Wingman in the Terminal") + chalk7.dim(` v${this.version}`));
2965
+ console.log(
2966
+ chalk7.dim(" Your AI Wingman in the Terminal") + chalk7.dim(" ".repeat(18)) + chalk7.dim(`v${this.version}`)
2967
+ );
2940
2968
  console.log();
2941
2969
  }
2942
2970
  printSystemInfo() {
2943
- const line = chalk7.dim("\u2500".repeat(64));
2944
- console.log(line);
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");
2980
+ };
2981
+ console.log(top);
2945
2982
  if (this.isAuthenticated && this.userProfile) {
2946
2983
  const name = this.userProfile.full_name || this.userProfile.email;
2947
2984
  const plan = (this.userProfile.subscription_plan || "free").toUpperCase();
2948
- const planColor = plan === "PRO" ? chalk7.cyan : plan === "ENTERPRISE" ? chalk7.magenta : plan === "TEAM" ? chalk7.yellow : chalk7.dim;
2949
- console.log(
2950
- ` ${chalk7.bold("Auth")} ${chalk7.green("\u25CF")} ${name} ${planColor(`(${plan})`)}`
2951
- );
2952
- } else if (this.isAuthenticated) {
2953
- console.log(
2954
- ` ${chalk7.bold("Auth")} ${chalk7.green("\u25CF")} Authenticated ${chalk7.dim("(offline)")}`
2955
- );
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));
2956
2987
  } else {
2957
- console.log(
2958
- ` ${chalk7.bold("Auth")} ${chalk7.red("\u25CF")} Not logged in ${chalk7.dim("\u2014 type /login")}`
2959
- );
2960
- }
2961
- const modeStr = chalk7.yellow(this.currentMode);
2962
- const modelStr = this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto");
2963
- console.log(` ${chalk7.bold("Mode")} ${modeStr} ${chalk7.bold("Model")} ${modelStr}`);
2964
- const cwd = process.cwd().replace(process.env.HOME || "", "~");
2988
+ console.log(row("Auth", chalk7.red("\u25CF ") + chalk7.dim("Not logged in \u2014 type ") + chalk7.cyan("/login")));
2989
+ }
2990
+ 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"));
2999
+ console.log(div);
2965
3000
  if (this.isDemo) {
2966
- console.log(` ${chalk7.bold("Project")} ${chalk7.cyan(DEMO_PROJECT2.name)} ${chalk7.bold("CWD")} ${chalk7.dim(cwd)}`);
3001
+ console.log(row("Project", chalk7.cyan(DEMO_PROJECT2.name)));
2967
3002
  } else {
2968
- const projectId = configManager.getProjectId();
2969
- const projectStr = projectId ? chalk7.cyan(projectId) : chalk7.dim("none");
2970
- console.log(` ${chalk7.bold("Project")} ${projectStr} ${chalk7.bold("CWD")} ${chalk7.dim(cwd)}`);
3003
+ const pid = configManager.getProjectId();
3004
+ console.log(row("Project", pid ? chalk7.cyan(pid) : chalk7.dim("none")));
2971
3005
  }
2972
- const shell = process.env.SHELL?.split("/").pop() || "unknown";
2973
- console.log(` ${chalk7.bold("Shell")} ${shell} ${chalk7.bold("OS")} ${process.platform}`);
3006
+ const cwd = process.cwd().replace(process.env.HOME || "", "~");
3007
+ console.log(row("Directory", chalk7.dim(cwd)));
2974
3008
  if (this.isDemo) {
2975
- console.log(` ${chalk7.bold("Demo")} ${chalk7.yellow("\u25CF")} ${chalk7.yellow("Demo mode active")} ${chalk7.dim("\u2014 no backend required")}`);
3009
+ console.log(div);
3010
+ console.log(row("Demo", chalk7.yellow("\u25C6 ") + chalk7.yellow("Demo mode") + chalk7.dim(" \u2014 no backend required")));
2976
3011
  }
2977
- console.log(line);
3012
+ console.log(bottom);
2978
3013
  console.log();
2979
- console.log(chalk7.dim(" Type a message to chat with AI, or /help for commands."));
3014
+ console.log(
3015
+ chalk7.dim(" Chat freely, or use ") + chalk7.cyan("/help") + chalk7.dim(" to see all commands.")
3016
+ );
2980
3017
  }
2981
3018
  // ============================================================
2982
- // PROMPT
3019
+ // PROMPT LINE — Claude Code style
2983
3020
  // ============================================================
2984
3021
  buildPrompt() {
2985
3022
  const parts = [];
2986
- parts.push(chalk7.bold.cyan("devwing"));
2987
- parts.push(chalk7.dim("[") + chalk7.yellow(this.currentMode) + chalk7.dim("]"));
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 = "";
2988
3025
  if (this.isAuthenticated && this.userProfile) {
2989
- const firstName = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0].toLowerCase();
2990
- parts.push(chalk7.green(firstName));
2991
- }
2992
- if (this.isDemo) {
2993
- parts.push(chalk7.dim.yellow("demo"));
2994
- }
2995
- return "\n" + parts.join(" ") + chalk7.bold(" > ");
2996
- }
2997
- // ============================================================
2998
- // TAB COMPLETION
2999
- // ============================================================
3000
- completer(line) {
3001
- if (!line.startsWith("/")) return [[], line];
3002
- const allNames = [];
3003
- for (const cmd of this.commands.values()) {
3004
- allNames.push("/" + cmd.name);
3005
- for (const alias of cmd.aliases) {
3006
- allNames.push("/" + alias);
3007
- }
3008
- }
3009
- const hits = allNames.filter((n) => n.startsWith(line));
3010
- return [hits.length ? hits : allNames, line];
3026
+ const first = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0];
3027
+ nameTag = chalk7.dim("@") + chalk7.green(first.toLowerCase());
3028
+ }
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");
3011
3038
  }
3012
3039
  // ============================================================
3013
3040
  // AUTH CHECK
@@ -3054,23 +3081,17 @@ var InteractiveSession = class {
3054
3081
  }
3055
3082
  }
3056
3083
  if (!command) {
3084
+ console.log();
3057
3085
  logger.warn(`Unknown command: /${cmdName}. Type /help for available commands.`);
3058
3086
  return;
3059
3087
  }
3060
3088
  if (command.requiresAuth && !this.isAuthenticated) {
3089
+ console.log();
3061
3090
  logger.warn("You need to be logged in for this command. Type /login first.");
3062
3091
  return;
3063
3092
  }
3064
- this.isBusy = true;
3065
- this.rl.close();
3066
- try {
3067
- await command.handler(args);
3068
- } catch (error) {
3069
- logger.error(error.message || "Command failed");
3070
- }
3071
- this.resetStdin();
3072
- await sleep2(80);
3073
- this.recreateReadline();
3093
+ console.log();
3094
+ await command.handler(args);
3074
3095
  }
3075
3096
  // ============================================================
3076
3097
  // AI PROMPT HANDLER
@@ -3083,20 +3104,11 @@ var InteractiveSession = class {
3083
3104
  }
3084
3105
  console.log();
3085
3106
  if (this.isDemo) {
3086
- this.isBusy = true;
3087
- this.rl.close();
3088
- try {
3089
- await demoPromptCommand(input, { ...this.options, mode: this.currentMode });
3090
- this.conversationHistory.push(
3091
- { role: "user", content: input, timestamp: /* @__PURE__ */ new Date() },
3092
- { role: "assistant", content: "[demo response]", timestamp: /* @__PURE__ */ new Date() }
3093
- );
3094
- } catch (error) {
3095
- logger.error(error.message || "Command failed");
3096
- }
3097
- this.resetStdin();
3098
- await sleep2(80);
3099
- this.recreateReadline();
3107
+ await demoPromptCommand(input, { ...this.options, mode: this.currentMode });
3108
+ this.conversationHistory.push(
3109
+ { role: "user", content: input, timestamp: /* @__PURE__ */ new Date() },
3110
+ { role: "assistant", content: "[demo response]", timestamp: /* @__PURE__ */ new Date() }
3111
+ );
3100
3112
  return;
3101
3113
  }
3102
3114
  if (!this.sessionContext) {
@@ -3157,9 +3169,7 @@ var InteractiveSession = class {
3157
3169
  }
3158
3170
  buildPromptWithHistory() {
3159
3171
  const history = this.conversationHistory;
3160
- if (history.length <= 1) {
3161
- return history[history.length - 1]?.content || "";
3162
- }
3172
+ if (history.length <= 1) return history[history.length - 1]?.content || "";
3163
3173
  const recent = history.slice(-10);
3164
3174
  const context = recent.slice(0, -1).map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n\n");
3165
3175
  return `Previous conversation:
@@ -3173,9 +3183,9 @@ ${recent[recent.length - 1].content}`;
3173
3183
  // ============================================================
3174
3184
  exitSession() {
3175
3185
  if (this.sigintTimer) clearTimeout(this.sigintTimer);
3176
- this.resetStdin();
3186
+ this.restoreStdin();
3177
3187
  console.log();
3178
- console.log(chalk7.dim(" Goodbye! Happy coding."));
3188
+ console.log(chalk7.dim(" Goodbye! Happy shipping. \u{1F680}"));
3179
3189
  console.log();
3180
3190
  process.exit(0);
3181
3191
  }
@@ -3183,150 +3193,24 @@ ${recent[recent.length - 1].content}`;
3183
3193
  // COMMAND REGISTRATION
3184
3194
  // ============================================================
3185
3195
  registerCommands() {
3186
- this.addCommand({
3187
- name: "help",
3188
- aliases: ["h", "?"],
3189
- description: "Show all available commands",
3190
- category: "session",
3191
- requiresAuth: false,
3192
- handler: async () => this.cmdHelp()
3193
- });
3194
- this.addCommand({
3195
- name: "exit",
3196
- aliases: ["quit", "q"],
3197
- description: "Exit DevWing",
3198
- category: "session",
3199
- requiresAuth: false,
3200
- handler: async () => this.exitSession()
3201
- });
3202
- this.addCommand({
3203
- name: "clear",
3204
- aliases: ["cls"],
3205
- description: "Clear screen and conversation",
3206
- category: "session",
3207
- requiresAuth: false,
3208
- handler: async () => this.cmdClear()
3209
- });
3210
- this.addCommand({
3211
- name: "login",
3212
- aliases: ["signin"],
3213
- description: "Authenticate with DevWing",
3214
- category: "session",
3215
- requiresAuth: false,
3216
- handler: async () => this.cmdLogin()
3217
- });
3218
- this.addCommand({
3219
- name: "logout",
3220
- aliases: ["signout"],
3221
- description: "Clear credentials and log out",
3222
- category: "session",
3223
- requiresAuth: true,
3224
- handler: async () => this.cmdLogout()
3225
- });
3226
- this.addCommand({
3227
- name: "status",
3228
- aliases: ["whoami"],
3229
- description: "Show auth status and profile",
3230
- category: "session",
3231
- requiresAuth: false,
3232
- handler: async () => this.cmdStatus()
3233
- });
3234
- this.addCommand({
3235
- name: "scan",
3236
- aliases: [],
3237
- description: "Run security vulnerability scan",
3238
- category: "tools",
3239
- requiresAuth: true,
3240
- handler: async () => this.cmdScan()
3241
- });
3242
- this.addCommand({
3243
- name: "review",
3244
- aliases: [],
3245
- description: "Perform code review on recent changes",
3246
- category: "tools",
3247
- requiresAuth: true,
3248
- handler: async () => this.cmdReview()
3249
- });
3250
- this.addCommand({
3251
- name: "explain",
3252
- aliases: [],
3253
- description: "Explain code, file, or concept",
3254
- category: "tools",
3255
- requiresAuth: true,
3256
- handler: async (args) => this.cmdExplain(args)
3257
- });
3258
- this.addCommand({
3259
- name: "memory",
3260
- aliases: ["mem"],
3261
- description: "Manage project memory (save/show/clear)",
3262
- category: "tools",
3263
- requiresAuth: true,
3264
- handler: async (args) => this.cmdMemory(args)
3265
- });
3266
- this.addCommand({
3267
- name: "mode",
3268
- aliases: [],
3269
- description: "Show or set AI mode",
3270
- category: "config",
3271
- requiresAuth: false,
3272
- handler: async (args) => this.cmdMode(args)
3273
- });
3274
- this.addCommand({
3275
- name: "model",
3276
- aliases: [],
3277
- description: "Show or set AI model",
3278
- category: "config",
3279
- requiresAuth: false,
3280
- handler: async (args) => this.cmdModel(args)
3281
- });
3282
- this.addCommand({
3283
- name: "models",
3284
- aliases: [],
3285
- description: "List all available AI models",
3286
- category: "config",
3287
- requiresAuth: false,
3288
- handler: async () => this.cmdModels()
3289
- });
3290
- this.addCommand({
3291
- name: "usage",
3292
- aliases: ["stats"],
3293
- description: "Show token usage statistics",
3294
- category: "config",
3295
- requiresAuth: true,
3296
- handler: async () => this.cmdUsage()
3297
- });
3298
- this.addCommand({
3299
- name: "plans",
3300
- aliases: ["pricing"],
3301
- description: "Show available plans and pricing",
3302
- category: "config",
3303
- requiresAuth: false,
3304
- handler: async () => this.cmdPlans()
3305
- });
3306
- this.addCommand({
3307
- name: "context",
3308
- aliases: ["ctx"],
3309
- description: "Show loaded codebase context",
3310
- category: "config",
3311
- requiresAuth: false,
3312
- handler: async () => this.cmdContext()
3313
- });
3314
- this.addCommand({
3315
- name: "history",
3316
- aliases: [],
3317
- description: "Show conversation history",
3318
- category: "config",
3319
- requiresAuth: false,
3320
- handler: async () => this.cmdHistory()
3321
- });
3322
- this.addCommand({
3323
- name: "config",
3324
- aliases: ["settings"],
3325
- description: "Show or set CLI configuration",
3326
- category: "config",
3327
- requiresAuth: false,
3328
- handler: async (args) => this.cmdConfig(args)
3329
- });
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) });
3330
3214
  }
3331
3215
  addCommand(cmd) {
3332
3216
  this.commands.set(cmd.name, cmd);
@@ -3335,24 +3219,42 @@ ${recent[recent.length - 1].content}`;
3335
3219
  // COMMAND IMPLEMENTATIONS
3336
3220
  // ============================================================
3337
3221
  async cmdHelp() {
3338
- console.log();
3222
+ const W = 64;
3223
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3224
+ const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3225
+ 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, "");
3234
+ const pad = W - 4 - raw.length;
3235
+ return chalk7.dim("\u2502 ") + label + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
3236
+ };
3339
3237
  const categories = [
3340
3238
  { key: "session", label: "Session", color: chalk7.bold.green },
3341
3239
  { key: "tools", label: "AI Tools", color: chalk7.bold.magenta },
3342
3240
  { key: "config", label: "Configuration", color: chalk7.bold.yellow }
3343
3241
  ];
3344
- const lines = [];
3242
+ console.log(top);
3243
+ console.log(catRow(chalk7.bold.white(" Commands")));
3345
3244
  for (const cat of categories) {
3346
- lines.push(cat.color(` ${cat.label}`));
3245
+ console.log(div);
3246
+ console.log(catRow(" " + cat.color(cat.label)));
3347
3247
  for (const cmd of this.commands.values()) {
3348
3248
  if (cmd.category !== cat.key) continue;
3349
- const aliasStr = cmd.aliases.length > 0 ? chalk7.dim(` (${cmd.aliases.map((a) => "/" + a).join(", ")})`) : "";
3350
- lines.push(` ${chalk7.cyan("/" + cmd.name.padEnd(12))} ${cmd.description}${aliasStr}`);
3249
+ const cmdStr = chalk7.cyan(("/" + cmd.name).padEnd(14));
3250
+ 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));
3351
3253
  }
3352
- lines.push("");
3353
3254
  }
3354
- lines.push(chalk7.dim(" Just type a message to chat with DevWing AI."));
3355
- logger.box(lines.join("\n"), { title: "Commands", color: "cyan" });
3255
+ console.log(div);
3256
+ console.log(catRow(chalk7.dim(" Just type a message to chat with DevWing AI")));
3257
+ console.log(bottom);
3356
3258
  }
3357
3259
  async cmdClear() {
3358
3260
  this.conversationHistory = [];
@@ -3363,53 +3265,50 @@ ${recent[recent.length - 1].content}`;
3363
3265
  this.printSystemInfo();
3364
3266
  }
3365
3267
  async cmdLogin() {
3366
- console.log();
3367
3268
  if (this.isDemo) {
3368
- await demoLoginCommand({ fromSession: true });
3269
+ await this.withInquirer(() => demoLoginCommand({ fromSession: true }));
3369
3270
  } else {
3370
- await loginCommand();
3271
+ await this.withInquirer(() => loginCommand());
3371
3272
  }
3372
3273
  await this.checkAuthStatus();
3373
- this.rl.setPrompt(this.buildPrompt());
3374
3274
  }
3375
3275
  async cmdLogout() {
3376
- console.log();
3377
3276
  if (this.isDemo) {
3378
- await demoLogoutCommand();
3277
+ await this.withInquirer(() => demoLogoutCommand());
3379
3278
  } else {
3380
- await logoutCommand();
3279
+ await this.withInquirer(() => logoutCommand());
3381
3280
  }
3382
3281
  await this.checkAuthStatus();
3383
- this.rl.setPrompt(this.buildPrompt());
3384
3282
  }
3385
3283
  async cmdStatus() {
3386
- console.log();
3387
3284
  if (!this.isAuthenticated) {
3388
3285
  logger.info("Not logged in. Type /login to authenticate.");
3389
3286
  return;
3390
3287
  }
3391
3288
  if (this.isDemo) {
3392
- const table = new Table3({
3393
- chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3394
- });
3395
- table.push(
3396
- [chalk7.bold("Email"), DEMO_USER2.email],
3397
- [chalk7.bold("Name"), DEMO_USER2.full_name],
3398
- [chalk7.bold("Plan"), chalk7.cyan(DEMO_USER2.subscription_plan.toUpperCase())],
3399
- [chalk7.bold("Verified"), chalk7.green("Yes")],
3400
- [chalk7.bold("2FA"), chalk7.dim("Disabled")],
3401
- [chalk7.bold("Workspace"), `${DEMO_WORKSPACE2.name} (${DEMO_WORKSPACE2.plan})`],
3402
- [chalk7.bold("Project"), DEMO_PROJECT2.name],
3403
- [chalk7.bold("Tokens Today"), `${DEMO_USER2.tokens_used_today?.toLocaleString()} / 500,000`],
3404
- [chalk7.bold("Member Since"), new Date(DEMO_USER2.created_at).toLocaleDateString()]
3405
- );
3406
- console.log(table.toString());
3289
+ const W = 54;
3290
+ 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) => {
3293
+ const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
3294
+ const pad = W - 4 - k.length - rawV.length;
3295
+ return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3296
+ };
3297
+ 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);
3407
3307
  } else {
3408
3308
  await statusCommand();
3409
3309
  }
3410
3310
  }
3411
3311
  async cmdScan() {
3412
- console.log();
3413
3312
  if (this.isDemo) {
3414
3313
  await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...this.options, mode: "security" });
3415
3314
  } else {
@@ -3417,7 +3316,6 @@ ${recent[recent.length - 1].content}`;
3417
3316
  }
3418
3317
  }
3419
3318
  async cmdReview() {
3420
- console.log();
3421
3319
  if (this.isDemo) {
3422
3320
  await demoReviewCommand();
3423
3321
  } else {
@@ -3430,7 +3328,6 @@ ${recent[recent.length - 1].content}`;
3430
3328
  logger.info("Example: /explain src/auth/middleware.ts");
3431
3329
  return;
3432
3330
  }
3433
- console.log();
3434
3331
  if (this.isDemo) {
3435
3332
  await demoExplainCommand(args);
3436
3333
  } else {
@@ -3447,10 +3344,11 @@ ${recent[recent.length - 1].content}`;
3447
3344
  console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
3448
3345
  return;
3449
3346
  }
3450
- console.log();
3451
3347
  const content = parts.slice(1).join(" ").trim();
3452
3348
  if (this.isDemo) {
3453
- await demoMemoryCommand(sub, content || void 0);
3349
+ await this.withInquirer(
3350
+ () => demoMemoryCommand(sub, content || void 0)
3351
+ );
3454
3352
  } else {
3455
3353
  await memoryCommand(sub, content || void 0, this.options);
3456
3354
  }
@@ -3458,17 +3356,32 @@ ${recent[recent.length - 1].content}`;
3458
3356
  async cmdMode(args) {
3459
3357
  const validModes = ["general", "frontend", "backend", "security", "devops"];
3460
3358
  if (!args) {
3461
- console.log();
3462
- console.log(` Current mode: ${chalk7.yellow.bold(this.currentMode)}`);
3463
- console.log();
3464
- console.log(" Available modes:");
3465
- for (const mode2 of validModes) {
3466
- const indicator = mode2 === this.currentMode ? chalk7.green(" \u25CF") : chalk7.dim(" \u25CB");
3467
- 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";
3468
- console.log(` ${indicator} ${chalk7.cyan(mode2.padEnd(12))} ${chalk7.dim(desc)}`);
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");
3367
+ };
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) {
3371
+ 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] || ""));
3469
3382
  }
3470
- console.log();
3471
- console.log(chalk7.dim(` Usage: /mode <name>`));
3383
+ console.log(bottom);
3384
+ console.log(chalk7.dim(" Usage: /mode <name>"));
3472
3385
  return;
3473
3386
  }
3474
3387
  const mode = args.toLowerCase();
@@ -3478,124 +3391,111 @@ ${recent[recent.length - 1].content}`;
3478
3391
  }
3479
3392
  this.currentMode = mode;
3480
3393
  this.options.mode = mode;
3481
- logger.success(`Mode set to ${chalk7.yellow.bold(mode)}`);
3394
+ console.log();
3395
+ console.log(` ${chalk7.green("\u2713")} Mode \u2192 ${chalk7.yellow.bold(mode)}`);
3482
3396
  }
3483
3397
  async cmdModel(args) {
3484
3398
  if (!args) {
3399
+ const current = this.currentModel || "auto";
3485
3400
  console.log();
3486
- const current = this.currentModel || "auto (selected by mode)";
3487
- console.log(` Current model: ${chalk7.cyan.bold(current)}`);
3488
- console.log(chalk7.dim(` Usage: /model <name> | /model auto | /models to list all`));
3401
+ console.log(` ${chalk7.bold("Current model:")} ${chalk7.cyan(current)}`);
3402
+ console.log(chalk7.dim(" /model <name> \xB7 /model auto \xB7 /models to list all"));
3489
3403
  return;
3490
3404
  }
3491
3405
  if (args === "auto") {
3492
3406
  this.currentModel = null;
3493
3407
  this.options.model = void 0;
3494
- logger.success("Model set to auto (selected by mode)");
3408
+ logger.success("Model \u2192 auto");
3495
3409
  return;
3496
3410
  }
3497
3411
  this.currentModel = args;
3498
3412
  this.options.model = args;
3499
- logger.success(`Model set to ${chalk7.cyan.bold(args)}`);
3413
+ console.log();
3414
+ console.log(` ${chalk7.green("\u2713")} Model \u2192 ${chalk7.cyan.bold(args)}`);
3500
3415
  }
3501
3416
  async cmdModels() {
3502
- console.log();
3503
3417
  if (this.isDemo) {
3504
3418
  const table = new Table3({
3505
3419
  head: [
3506
3420
  chalk7.bold("Model"),
3507
3421
  chalk7.bold("Domain"),
3508
3422
  chalk7.bold("Status"),
3509
- chalk7.bold("Min Plan"),
3423
+ chalk7.bold("Plan"),
3510
3424
  chalk7.bold("Context"),
3511
3425
  chalk7.bold("Speed")
3512
3426
  ],
3513
- colWidths: [24, 12, 10, 10, 10, 12]
3427
+ style: { head: [], border: ["dim"] }
3514
3428
  });
3515
3429
  for (const m of DEMO_MODELS) {
3516
- const statusColor = m.status === "active" ? chalk7.green : chalk7.yellow;
3517
- const planColor = m.min_plan === "free" ? chalk7.green : chalk7.cyan;
3518
- table.push([
3519
- chalk7.bold(m.display_name),
3520
- chalk7.yellow(m.domain),
3521
- statusColor(m.status),
3522
- planColor(m.min_plan),
3523
- `${(m.context_window / 1024).toFixed(0)}k`,
3524
- `${m.tokens_per_sec} tok/s`
3525
- ]);
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`]);
3526
3435
  }
3527
3436
  console.log(table.toString());
3528
- console.log();
3529
- console.log(chalk7.dim(` Set a model: /model devwing-backend-1 | /model auto`));
3437
+ console.log(chalk7.dim(" /model <name> to select \xB7 /model auto for automatic"));
3530
3438
  } else {
3531
3439
  try {
3532
3440
  const models = await apiClient.getModels();
3533
- if (models.length === 0) {
3441
+ if (!models.length) {
3534
3442
  logger.info("No models available");
3535
3443
  return;
3536
3444
  }
3537
- const table = new Table3({
3538
- head: [chalk7.bold("Name"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Min Plan")]
3539
- });
3540
- for (const m of models) {
3541
- table.push([m.display_name || m.name, m.domain, m.status, m.min_plan]);
3542
- }
3445
+ const table = new Table3({ head: [chalk7.bold("Name"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Plan")] });
3446
+ for (const m of models) table.push([m.display_name || m.name, m.domain, m.status, m.min_plan]);
3543
3447
  console.log(table.toString());
3544
- } catch (error) {
3545
- logger.error("Failed to fetch models: " + error.message);
3448
+ } catch (e) {
3449
+ logger.error("Failed to fetch models: " + e.message);
3546
3450
  }
3547
3451
  }
3548
3452
  }
3549
3453
  async cmdUsage() {
3550
- console.log();
3551
3454
  if (this.isDemo) {
3552
- console.log(chalk7.bold(" Token Usage"));
3553
- console.log();
3554
3455
  const table = new Table3({
3555
- head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")]
3456
+ head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")],
3457
+ style: { head: [], border: ["dim"] }
3556
3458
  });
3557
3459
  table.push(
3558
- ["Today", DEMO_USAGE.today.requests.toString(), DEMO_USAGE.today.tokens_in.toLocaleString(), DEMO_USAGE.today.tokens_out.toLocaleString(), `$${DEMO_USAGE.today.cost.toFixed(4)}`],
3559
- ["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)}`],
3560
- ["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)}`]
3460
+ ["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
+ ["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
+ ["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)}`]
3561
3463
  );
3562
3464
  console.log(table.toString());
3563
- console.log();
3564
3465
  const used = DEMO_USER2.tokens_used_today;
3565
3466
  const limit = 5e5;
3566
3467
  const pct = Math.round(used / limit * 100);
3567
- const barWidth = 30;
3568
- const filled = Math.round(pct / 100 * barWidth);
3569
- const bar = chalk7.cyan("\u2588".repeat(filled)) + chalk7.dim("\u2591".repeat(barWidth - filled));
3570
- console.log(` Daily Quota: ${bar} ${pct}% (${used.toLocaleString()} / ${limit.toLocaleString()})`);
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));
3571
3471
  console.log();
3572
- console.log(chalk7.bold(" Usage by Model"));
3472
+ console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()} tokens`)}`);
3573
3473
  console.log();
3574
- const modelTable = new Table3({
3575
- head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")]
3474
+ console.log(chalk7.bold(" By Model"));
3475
+ const mt = new Table3({
3476
+ head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")],
3477
+ style: { head: [], border: ["dim"] }
3576
3478
  });
3577
- modelTable.push(
3479
+ mt.push(
3578
3480
  ["devwing-backend-1", "312", "421,000", "$1.4721"],
3579
3481
  ["devwing-security-1", "145", "198,500", "$0.6948"],
3580
3482
  ["devwing-general-1", "98", "132,200", "$0.4627"],
3581
3483
  ["devwing-frontend-1", "42", "56,800", "$0.1988"],
3582
3484
  ["devwing-devops-1", "15", "15,600", "$0.1728"]
3583
3485
  );
3584
- console.log(modelTable.toString());
3486
+ console.log(mt.toString());
3585
3487
  } else {
3586
3488
  try {
3587
3489
  const usage = await apiClient.getUsage();
3588
- console.log(` Total Requests: ${usage.total_requests}`);
3589
- console.log(` Tokens In: ${usage.total_tokens_in.toLocaleString()}`);
3590
- console.log(` Tokens Out: ${usage.total_tokens_out.toLocaleString()}`);
3490
+ console.log(` Requests: ${usage.total_requests}`);
3491
+ console.log(` Tokens: ${usage.total_tokens_in.toLocaleString()} in, ${usage.total_tokens_out.toLocaleString()} out`);
3591
3492
  console.log(` Cost: $${usage.total_cost_usd.toFixed(4)}`);
3592
- } catch (error) {
3593
- logger.error("Failed to fetch usage: " + error.message);
3493
+ } catch (e) {
3494
+ logger.error("Failed to fetch usage: " + e.message);
3594
3495
  }
3595
3496
  }
3596
3497
  }
3597
3498
  async cmdPlans() {
3598
- console.log();
3599
3499
  const table = new Table3({
3600
3500
  head: [
3601
3501
  chalk7.bold("Plan"),
@@ -3605,102 +3505,100 @@ ${recent[recent.length - 1].content}`;
3605
3505
  chalk7.bold("Projects"),
3606
3506
  chalk7.bold("Seats")
3607
3507
  ],
3608
- colWidths: [14, 12, 14, 16, 12, 14]
3508
+ style: { head: [], border: ["dim"] }
3609
3509
  });
3510
+ const currentPlan = this.userProfile?.subscription_plan?.toLowerCase() || "";
3610
3511
  for (const plan of DEMO_PLANS) {
3611
- const isCurrentPlan = this.isAuthenticated && this.userProfile?.subscription_plan?.toLowerCase() === plan.name.toLowerCase();
3612
- const nameStr = isCurrentPlan ? chalk7.cyan.bold(plan.name + " \u25CF") : plan.name;
3512
+ const isCurrent = this.isAuthenticated && currentPlan === plan.name.toLowerCase();
3513
+ const nameStr = isCurrent ? chalk7.cyan.bold(plan.name) + chalk7.dim(" \u2190") : plan.name;
3613
3514
  table.push([nameStr, plan.price, plan.tokens_day, plan.models, plan.projects, plan.seats]);
3614
3515
  }
3615
3516
  console.log(table.toString());
3616
3517
  console.log();
3617
3518
  if (this.isAuthenticated) {
3618
- const currentPlan = this.userProfile?.subscription_plan || "free";
3619
- console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()}. Upgrade at https://devwing.ai/dashboard/billing`));
3519
+ console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3620
3520
  } else {
3621
- console.log(chalk7.dim(" Sign up at https://devwing.ai to get started."));
3521
+ console.log(chalk7.dim(" Sign up at devwing.ai"));
3622
3522
  }
3623
3523
  }
3624
3524
  async cmdContext() {
3625
- console.log();
3626
3525
  if (this.isDemo) {
3627
- const table = new Table3({
3628
- chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3629
- });
3630
- table.push(
3631
- [chalk7.bold("Working Dir"), chalk7.cyan(process.cwd().replace(process.env.HOME || "", "~"))],
3632
- [chalk7.bold("Git Branch"), "main"],
3633
- [chalk7.bold("Files Loaded"), "34"],
3634
- [chalk7.bold("Framework"), "FastAPI + TypeScript"],
3635
- [chalk7.bold("Shell"), process.env.SHELL?.split("/").pop() || "unknown"],
3636
- [chalk7.bold("OS"), process.platform]
3637
- );
3638
- console.log(table.toString());
3639
- console.log();
3640
- console.log(chalk7.bold(" Top files in context:"));
3641
- const files = [
3642
- "src/auth/auth_controller.ts",
3643
- "src/middleware/jwt.middleware.ts",
3644
- "src/services/user_service.ts",
3645
- "src/auth/repositories/user.repository.ts",
3646
- "package.json",
3647
- "tsconfig.json",
3648
- "docker-compose.yml"
3649
- ];
3650
- for (const f of files) {
3651
- console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f)}`);
3652
- }
3653
- console.log(chalk7.dim(" ... and 27 more"));
3526
+ const W = 58;
3527
+ const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
3528
+ const bottom = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
3529
+ 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;
3533
+ return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3534
+ };
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");
3538
+ };
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");
3542
+ };
3543
+ 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"));
3549
+ 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);
3654
3555
  } else if (this.sessionContext) {
3655
- console.log(` Working Dir: ${chalk7.cyan(this.sessionContext.cwd)}`);
3656
- console.log(` Shell: ${this.sessionContext.shell} | OS: ${this.sessionContext.os}`);
3657
- console.log(` Files: ${this.sessionContext.files.length}`);
3658
- if (this.sessionContext.git_branch) {
3659
- console.log(` Git Branch: ${this.sessionContext.git_branch}`);
3660
- }
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}`);
3661
3558
  console.log();
3662
- console.log(chalk7.bold(" Files in context:"));
3663
3559
  for (const f of this.sessionContext.files.slice(0, 10)) {
3664
- console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f.path)}`);
3560
+ console.log(` ${chalk7.dim("\xB7")} ${chalk7.cyan(f.path)}`);
3665
3561
  }
3666
3562
  if (this.sessionContext.files.length > 10) {
3667
- console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3563
+ console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3668
3564
  }
3669
3565
  } else {
3670
3566
  logger.info("No context loaded yet. Send a message to load context.");
3671
3567
  }
3672
3568
  }
3673
3569
  async cmdHistory() {
3674
- console.log();
3675
- if (this.conversationHistory.length === 0) {
3570
+ if (!this.conversationHistory.length) {
3676
3571
  logger.info("No conversation history yet.");
3677
3572
  return;
3678
3573
  }
3574
+ console.log();
3679
3575
  for (const msg of this.conversationHistory.slice(-20)) {
3680
- const label = msg.role === "user" ? chalk7.green.bold("You") : chalk7.cyan.bold("DevWing");
3576
+ const label = msg.role === "user" ? chalk7.green.bold("You ") : chalk7.cyan.bold("DevWing ");
3681
3577
  const time = chalk7.dim(msg.timestamp.toLocaleTimeString());
3682
- const preview = msg.content.length > 120 ? msg.content.substring(0, 120) + "..." : msg.content;
3683
- console.log(` ${time} ${label}: ${preview}`);
3578
+ const preview = msg.content.length > 100 ? msg.content.substring(0, 100) + "\u2026" : msg.content;
3579
+ console.log(` ${time} ${label} ${preview}`);
3684
3580
  }
3685
3581
  }
3686
3582
  async cmdConfig(args) {
3687
3583
  if (!args) {
3688
- console.log();
3689
3584
  const config = configManager.getAll();
3690
- const table = new Table3({
3691
- chars: { "mid": "", "left-mid": "", "mid-mid": "", "right-mid": "" }
3692
- });
3693
- table.push(
3694
- [chalk7.bold("API URL"), config.apiUrl || chalk7.dim("default")],
3695
- [chalk7.bold("Workspace"), config.workspaceId || chalk7.dim("none")],
3696
- [chalk7.bold("Project"), config.projectId || chalk7.dim("none")],
3697
- [chalk7.bold("Model"), config.model || chalk7.dim("auto")],
3698
- [chalk7.bold("Mode"), config.mode || chalk7.dim("general")],
3699
- [chalk7.bold("Config Path"), configManager.getPath()]
3700
- );
3701
- console.log(table.toString());
3702
- console.log();
3703
- console.log(chalk7.dim(" Usage: /config set <key> <value> | /config get <key>"));
3585
+ const W = 56;
3586
+ 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;
3591
+ return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
3592
+ };
3593
+ 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);
3601
+ console.log(chalk7.dim(" /config set <key> <value> \xB7 /config get <key>"));
3704
3602
  return;
3705
3603
  }
3706
3604
  const parts = args.split(/\s+/);