devwing 0.1.21 → 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
@@ -2787,7 +2787,7 @@ function getVersion() {
2787
2787
  continue;
2788
2788
  }
2789
2789
  }
2790
- return "0.1.21";
2790
+ return "0.1.22";
2791
2791
  }
2792
2792
  var DEMO_USER2 = {
2793
2793
  id: "usr_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
@@ -2802,8 +2802,7 @@ var DEMO_USER2 = {
2802
2802
  tokens_reset_at: "2026-04-10T00:00:00Z"
2803
2803
  };
2804
2804
  var DEMO_WORKSPACE2 = {
2805
- name: "Kano State Government",
2806
- plan: "enterprise"};
2805
+ name: "Kano State Government"};
2807
2806
  var DEMO_PROJECT2 = {
2808
2807
  name: "Tallon SaaS ERP"};
2809
2808
  var DEMO_MODELS = [
@@ -2812,11 +2811,11 @@ var DEMO_MODELS = [
2812
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" },
2813
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" },
2814
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" },
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" }
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" }
2817
2816
  ];
2818
2817
  var DEMO_PLANS = [
2819
- { 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" },
2820
2819
  { name: "Pro", price: "$19/mo", tokens_day: "500,000", models: "All models", projects: "10", seats: "1" },
2821
2820
  { name: "Team", price: "$99/mo", tokens_day: "2,000,000", models: "All + priority", projects: "Unlimited", seats: "5 included" },
2822
2821
  { name: "Enterprise", price: "Custom", tokens_day: "Unlimited", models: "Dedicated", projects: "Unlimited", seats: "Unlimited" }
@@ -2841,8 +2840,7 @@ var InteractiveSession = class {
2841
2840
  sigintCount = 0;
2842
2841
  sigintTimer = null;
2843
2842
  version;
2844
- isBusy = false;
2845
- // prevent readline events during command execution
2843
+ isRunningCommand = false;
2846
2844
  constructor(options) {
2847
2845
  this.options = options;
2848
2846
  this.isDemo = isDemoMode();
@@ -2861,63 +2859,76 @@ var InteractiveSession = class {
2861
2859
  this.printSystemInfo();
2862
2860
  this.createReadline();
2863
2861
  console.log();
2864
- this.rl.prompt();
2862
+ this.prompt();
2865
2863
  }
2866
2864
  // ============================================================
2867
- // READLINE MANAGEMENT
2865
+ // READLINE — created once, never destroyed
2868
2866
  // ============================================================
2869
2867
  createReadline() {
2870
- this.isBusy = false;
2871
2868
  this.rl = readline.createInterface({
2872
2869
  input: process.stdin,
2873
2870
  output: process.stdout,
2874
- prompt: this.buildPrompt(),
2875
- completer: (line) => this.completer(line)
2871
+ prompt: "",
2872
+ terminal: true
2876
2873
  });
2877
2874
  this.rl.on("SIGINT", () => {
2875
+ if (this.isRunningCommand) {
2876
+ process.stdout.write("\n");
2877
+ return;
2878
+ }
2878
2879
  this.sigintCount++;
2879
2880
  if (this.sigintCount >= 2) {
2880
2881
  this.exitSession();
2881
2882
  return;
2882
2883
  }
2883
- 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"));
2884
2885
  this.sigintTimer = setTimeout(() => {
2885
2886
  this.sigintCount = 0;
2886
2887
  }, 1500);
2887
- this.rl.prompt();
2888
+ this.prompt();
2888
2889
  });
2889
- const currentRl = this.rl;
2890
2890
  this.rl.on("line", async (input) => {
2891
- if (this.isBusy) return;
2891
+ if (this.isRunningCommand) return;
2892
2892
  const trimmed = input.trim();
2893
2893
  if (!trimmed) {
2894
- this.rl.prompt();
2894
+ this.prompt();
2895
2895
  return;
2896
2896
  }
2897
- this.isBusy = true;
2897
+ this.isRunningCommand = true;
2898
+ this.rl.pause();
2898
2899
  try {
2899
2900
  if (trimmed.startsWith("/")) {
2900
2901
  await this.handleSlashCommand(trimmed);
2901
- return;
2902
2902
  } else {
2903
2903
  await this.handleAIPrompt(trimmed);
2904
2904
  }
2905
2905
  } catch (error) {
2906
2906
  logger.error(error.message || "An error occurred");
2907
2907
  }
2908
- this.isBusy = false;
2909
- if (this.rl === currentRl) {
2910
- this.rl.setPrompt(this.buildPrompt());
2911
- this.rl.prompt();
2912
- }
2908
+ this.restoreStdin();
2909
+ await sleep2(60);
2910
+ this.isRunningCommand = false;
2911
+ this.rl.resume();
2912
+ this.prompt();
2913
2913
  });
2914
2914
  this.rl.on("close", () => {
2915
- if (!this.isBusy) {
2915
+ if (!this.isRunningCommand) {
2916
2916
  this.exitSession();
2917
2917
  }
2918
2918
  });
2919
2919
  }
2920
- 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() {
2921
2932
  try {
2922
2933
  if (typeof process.stdin.setRawMode === "function") {
2923
2934
  try {
@@ -2927,18 +2938,17 @@ var InteractiveSession = class {
2927
2938
  }
2928
2939
  process.stdin.removeAllListeners("keypress");
2929
2940
  process.stdin.removeAllListeners("data");
2930
- if (process.stdin.isPaused()) {
2931
- process.stdin.resume();
2932
- }
2941
+ if (process.stdin.isPaused()) process.stdin.resume();
2933
2942
  } catch {
2934
2943
  }
2935
2944
  }
2936
- recreateReadline() {
2937
- this.createReadline();
2938
- this.rl.prompt();
2945
+ prompt() {
2946
+ const p = this.buildPrompt();
2947
+ process.stdout.write(p);
2948
+ this.rl.setPrompt(p);
2939
2949
  }
2940
2950
  // ============================================================
2941
- // STARTUP UI
2951
+ // STARTUP UI — Claude Code style
2942
2952
  // ============================================================
2943
2953
  printStartupBanner() {
2944
2954
  const banner = gradient.pastel.multiline([
@@ -2952,78 +2962,79 @@ var InteractiveSession = class {
2952
2962
  ""
2953
2963
  ].join("\n"));
2954
2964
  console.log(banner);
2955
- 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
+ );
2956
2968
  console.log();
2957
2969
  }
2958
2970
  printSystemInfo() {
2959
- const line = chalk7.dim("\u2500".repeat(64));
2960
- 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);
2961
2982
  if (this.isAuthenticated && this.userProfile) {
2962
2983
  const name = this.userProfile.full_name || this.userProfile.email;
2963
2984
  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
- );
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));
2972
2987
  } else {
2973
- console.log(
2974
- ` ${chalk7.bold("Auth")} ${chalk7.red("\u25CF")} Not logged in ${chalk7.dim("\u2014 type /login")}`
2975
- );
2976
- }
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 || "", "~");
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);
2981
3000
  if (this.isDemo) {
2982
- 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)));
2983
3002
  } 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)}`);
3003
+ const pid = configManager.getProjectId();
3004
+ console.log(row("Project", pid ? chalk7.cyan(pid) : chalk7.dim("none")));
2987
3005
  }
2988
- const shell = process.env.SHELL?.split("/").pop() || "unknown";
2989
- 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)));
2990
3008
  if (this.isDemo) {
2991
- 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")));
2992
3011
  }
2993
- console.log(line);
3012
+ console.log(bottom);
2994
3013
  console.log();
2995
- 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
+ );
2996
3017
  }
2997
3018
  // ============================================================
2998
- // PROMPT
3019
+ // PROMPT LINE — Claude Code style
2999
3020
  // ============================================================
3000
3021
  buildPrompt() {
3001
3022
  const parts = [];
3002
- parts.push(chalk7.bold.cyan("devwing"));
3003
- 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 = "";
3004
3025
  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"));
3010
- }
3011
- return "\n" + parts.join(" ") + chalk7.bold(" > ");
3012
- }
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];
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");
3027
3038
  }
3028
3039
  // ============================================================
3029
3040
  // AUTH CHECK
@@ -3070,23 +3081,17 @@ var InteractiveSession = class {
3070
3081
  }
3071
3082
  }
3072
3083
  if (!command) {
3084
+ console.log();
3073
3085
  logger.warn(`Unknown command: /${cmdName}. Type /help for available commands.`);
3074
3086
  return;
3075
3087
  }
3076
3088
  if (command.requiresAuth && !this.isAuthenticated) {
3089
+ console.log();
3077
3090
  logger.warn("You need to be logged in for this command. Type /login first.");
3078
3091
  return;
3079
3092
  }
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();
3093
+ console.log();
3094
+ await command.handler(args);
3090
3095
  }
3091
3096
  // ============================================================
3092
3097
  // AI PROMPT HANDLER
@@ -3099,20 +3104,11 @@ var InteractiveSession = class {
3099
3104
  }
3100
3105
  console.log();
3101
3106
  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();
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
+ );
3116
3112
  return;
3117
3113
  }
3118
3114
  if (!this.sessionContext) {
@@ -3173,9 +3169,7 @@ var InteractiveSession = class {
3173
3169
  }
3174
3170
  buildPromptWithHistory() {
3175
3171
  const history = this.conversationHistory;
3176
- if (history.length <= 1) {
3177
- return history[history.length - 1]?.content || "";
3178
- }
3172
+ if (history.length <= 1) return history[history.length - 1]?.content || "";
3179
3173
  const recent = history.slice(-10);
3180
3174
  const context = recent.slice(0, -1).map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n\n");
3181
3175
  return `Previous conversation:
@@ -3189,9 +3183,9 @@ ${recent[recent.length - 1].content}`;
3189
3183
  // ============================================================
3190
3184
  exitSession() {
3191
3185
  if (this.sigintTimer) clearTimeout(this.sigintTimer);
3192
- this.resetStdin();
3186
+ this.restoreStdin();
3193
3187
  console.log();
3194
- console.log(chalk7.dim(" Goodbye! Happy coding."));
3188
+ console.log(chalk7.dim(" Goodbye! Happy shipping. \u{1F680}"));
3195
3189
  console.log();
3196
3190
  process.exit(0);
3197
3191
  }
@@ -3199,150 +3193,24 @@ ${recent[recent.length - 1].content}`;
3199
3193
  // COMMAND REGISTRATION
3200
3194
  // ============================================================
3201
3195
  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
- });
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) });
3346
3214
  }
3347
3215
  addCommand(cmd) {
3348
3216
  this.commands.set(cmd.name, cmd);
@@ -3351,24 +3219,42 @@ ${recent[recent.length - 1].content}`;
3351
3219
  // COMMAND IMPLEMENTATIONS
3352
3220
  // ============================================================
3353
3221
  async cmdHelp() {
3354
- 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
+ };
3355
3237
  const categories = [
3356
3238
  { key: "session", label: "Session", color: chalk7.bold.green },
3357
3239
  { key: "tools", label: "AI Tools", color: chalk7.bold.magenta },
3358
3240
  { key: "config", label: "Configuration", color: chalk7.bold.yellow }
3359
3241
  ];
3360
- const lines = [];
3242
+ console.log(top);
3243
+ console.log(catRow(chalk7.bold.white(" Commands")));
3361
3244
  for (const cat of categories) {
3362
- lines.push(cat.color(` ${cat.label}`));
3245
+ console.log(div);
3246
+ console.log(catRow(" " + cat.color(cat.label)));
3363
3247
  for (const cmd of this.commands.values()) {
3364
3248
  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}`);
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));
3367
3253
  }
3368
- lines.push("");
3369
3254
  }
3370
- lines.push(chalk7.dim(" Just type a message to chat with DevWing AI."));
3371
- 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);
3372
3258
  }
3373
3259
  async cmdClear() {
3374
3260
  this.conversationHistory = [];
@@ -3379,53 +3265,50 @@ ${recent[recent.length - 1].content}`;
3379
3265
  this.printSystemInfo();
3380
3266
  }
3381
3267
  async cmdLogin() {
3382
- console.log();
3383
3268
  if (this.isDemo) {
3384
- await demoLoginCommand({ fromSession: true });
3269
+ await this.withInquirer(() => demoLoginCommand({ fromSession: true }));
3385
3270
  } else {
3386
- await loginCommand();
3271
+ await this.withInquirer(() => loginCommand());
3387
3272
  }
3388
3273
  await this.checkAuthStatus();
3389
- this.rl.setPrompt(this.buildPrompt());
3390
3274
  }
3391
3275
  async cmdLogout() {
3392
- console.log();
3393
3276
  if (this.isDemo) {
3394
- await demoLogoutCommand();
3277
+ await this.withInquirer(() => demoLogoutCommand());
3395
3278
  } else {
3396
- await logoutCommand();
3279
+ await this.withInquirer(() => logoutCommand());
3397
3280
  }
3398
3281
  await this.checkAuthStatus();
3399
- this.rl.setPrompt(this.buildPrompt());
3400
3282
  }
3401
3283
  async cmdStatus() {
3402
- console.log();
3403
3284
  if (!this.isAuthenticated) {
3404
3285
  logger.info("Not logged in. Type /login to authenticate.");
3405
3286
  return;
3406
3287
  }
3407
3288
  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());
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);
3423
3307
  } else {
3424
3308
  await statusCommand();
3425
3309
  }
3426
3310
  }
3427
3311
  async cmdScan() {
3428
- console.log();
3429
3312
  if (this.isDemo) {
3430
3313
  await demoPromptCommand("scan auth service for OWASP vulnerabilities", { ...this.options, mode: "security" });
3431
3314
  } else {
@@ -3433,7 +3316,6 @@ ${recent[recent.length - 1].content}`;
3433
3316
  }
3434
3317
  }
3435
3318
  async cmdReview() {
3436
- console.log();
3437
3319
  if (this.isDemo) {
3438
3320
  await demoReviewCommand();
3439
3321
  } else {
@@ -3446,7 +3328,6 @@ ${recent[recent.length - 1].content}`;
3446
3328
  logger.info("Example: /explain src/auth/middleware.ts");
3447
3329
  return;
3448
3330
  }
3449
- console.log();
3450
3331
  if (this.isDemo) {
3451
3332
  await demoExplainCommand(args);
3452
3333
  } else {
@@ -3463,10 +3344,11 @@ ${recent[recent.length - 1].content}`;
3463
3344
  console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
3464
3345
  return;
3465
3346
  }
3466
- console.log();
3467
3347
  const content = parts.slice(1).join(" ").trim();
3468
3348
  if (this.isDemo) {
3469
- await demoMemoryCommand(sub, content || void 0);
3349
+ await this.withInquirer(
3350
+ () => demoMemoryCommand(sub, content || void 0)
3351
+ );
3470
3352
  } else {
3471
3353
  await memoryCommand(sub, content || void 0, this.options);
3472
3354
  }
@@ -3474,17 +3356,32 @@ ${recent[recent.length - 1].content}`;
3474
3356
  async cmdMode(args) {
3475
3357
  const validModes = ["general", "frontend", "backend", "security", "devops"];
3476
3358
  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)}`);
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] || ""));
3485
3382
  }
3486
- console.log();
3487
- console.log(chalk7.dim(` Usage: /mode <name>`));
3383
+ console.log(bottom);
3384
+ console.log(chalk7.dim(" Usage: /mode <name>"));
3488
3385
  return;
3489
3386
  }
3490
3387
  const mode = args.toLowerCase();
@@ -3494,124 +3391,111 @@ ${recent[recent.length - 1].content}`;
3494
3391
  }
3495
3392
  this.currentMode = mode;
3496
3393
  this.options.mode = mode;
3497
- logger.success(`Mode set to ${chalk7.yellow.bold(mode)}`);
3394
+ console.log();
3395
+ console.log(` ${chalk7.green("\u2713")} Mode \u2192 ${chalk7.yellow.bold(mode)}`);
3498
3396
  }
3499
3397
  async cmdModel(args) {
3500
3398
  if (!args) {
3399
+ const current = this.currentModel || "auto";
3501
3400
  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`));
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"));
3505
3403
  return;
3506
3404
  }
3507
3405
  if (args === "auto") {
3508
3406
  this.currentModel = null;
3509
3407
  this.options.model = void 0;
3510
- logger.success("Model set to auto (selected by mode)");
3408
+ logger.success("Model \u2192 auto");
3511
3409
  return;
3512
3410
  }
3513
3411
  this.currentModel = args;
3514
3412
  this.options.model = args;
3515
- logger.success(`Model set to ${chalk7.cyan.bold(args)}`);
3413
+ console.log();
3414
+ console.log(` ${chalk7.green("\u2713")} Model \u2192 ${chalk7.cyan.bold(args)}`);
3516
3415
  }
3517
3416
  async cmdModels() {
3518
- console.log();
3519
3417
  if (this.isDemo) {
3520
3418
  const table = new Table3({
3521
3419
  head: [
3522
3420
  chalk7.bold("Model"),
3523
3421
  chalk7.bold("Domain"),
3524
3422
  chalk7.bold("Status"),
3525
- chalk7.bold("Min Plan"),
3423
+ chalk7.bold("Plan"),
3526
3424
  chalk7.bold("Context"),
3527
3425
  chalk7.bold("Speed")
3528
3426
  ],
3529
- colWidths: [24, 12, 10, 10, 10, 12]
3427
+ style: { head: [], border: ["dim"] }
3530
3428
  });
3531
3429
  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
- ]);
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`]);
3542
3435
  }
3543
3436
  console.log(table.toString());
3544
- console.log();
3545
- 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"));
3546
3438
  } else {
3547
3439
  try {
3548
3440
  const models = await apiClient.getModels();
3549
- if (models.length === 0) {
3441
+ if (!models.length) {
3550
3442
  logger.info("No models available");
3551
3443
  return;
3552
3444
  }
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
- }
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]);
3559
3447
  console.log(table.toString());
3560
- } catch (error) {
3561
- logger.error("Failed to fetch models: " + error.message);
3448
+ } catch (e) {
3449
+ logger.error("Failed to fetch models: " + e.message);
3562
3450
  }
3563
3451
  }
3564
3452
  }
3565
3453
  async cmdUsage() {
3566
- console.log();
3567
3454
  if (this.isDemo) {
3568
- console.log(chalk7.bold(" Token Usage"));
3569
- console.log();
3570
3455
  const table = new Table3({
3571
- 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"] }
3572
3458
  });
3573
3459
  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)}`]
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)}`]
3577
3463
  );
3578
3464
  console.log(table.toString());
3579
- console.log();
3580
3465
  const used = DEMO_USER2.tokens_used_today;
3581
3466
  const limit = 5e5;
3582
3467
  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()})`);
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));
3587
3471
  console.log();
3588
- console.log(chalk7.bold(" Usage by Model"));
3472
+ console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()} tokens`)}`);
3589
3473
  console.log();
3590
- const modelTable = new Table3({
3591
- 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"] }
3592
3478
  });
3593
- modelTable.push(
3479
+ mt.push(
3594
3480
  ["devwing-backend-1", "312", "421,000", "$1.4721"],
3595
3481
  ["devwing-security-1", "145", "198,500", "$0.6948"],
3596
3482
  ["devwing-general-1", "98", "132,200", "$0.4627"],
3597
3483
  ["devwing-frontend-1", "42", "56,800", "$0.1988"],
3598
3484
  ["devwing-devops-1", "15", "15,600", "$0.1728"]
3599
3485
  );
3600
- console.log(modelTable.toString());
3486
+ console.log(mt.toString());
3601
3487
  } else {
3602
3488
  try {
3603
3489
  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()}`);
3490
+ console.log(` Requests: ${usage.total_requests}`);
3491
+ console.log(` Tokens: ${usage.total_tokens_in.toLocaleString()} in, ${usage.total_tokens_out.toLocaleString()} out`);
3607
3492
  console.log(` Cost: $${usage.total_cost_usd.toFixed(4)}`);
3608
- } catch (error) {
3609
- logger.error("Failed to fetch usage: " + error.message);
3493
+ } catch (e) {
3494
+ logger.error("Failed to fetch usage: " + e.message);
3610
3495
  }
3611
3496
  }
3612
3497
  }
3613
3498
  async cmdPlans() {
3614
- console.log();
3615
3499
  const table = new Table3({
3616
3500
  head: [
3617
3501
  chalk7.bold("Plan"),
@@ -3621,102 +3505,100 @@ ${recent[recent.length - 1].content}`;
3621
3505
  chalk7.bold("Projects"),
3622
3506
  chalk7.bold("Seats")
3623
3507
  ],
3624
- colWidths: [14, 12, 14, 16, 12, 14]
3508
+ style: { head: [], border: ["dim"] }
3625
3509
  });
3510
+ const currentPlan = this.userProfile?.subscription_plan?.toLowerCase() || "";
3626
3511
  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;
3512
+ const isCurrent = this.isAuthenticated && currentPlan === plan.name.toLowerCase();
3513
+ const nameStr = isCurrent ? chalk7.cyan.bold(plan.name) + chalk7.dim(" \u2190") : plan.name;
3629
3514
  table.push([nameStr, plan.price, plan.tokens_day, plan.models, plan.projects, plan.seats]);
3630
3515
  }
3631
3516
  console.log(table.toString());
3632
3517
  console.log();
3633
3518
  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`));
3519
+ console.log(chalk7.dim(` Your plan: ${currentPlan.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
3636
3520
  } else {
3637
- console.log(chalk7.dim(" Sign up at https://devwing.ai to get started."));
3521
+ console.log(chalk7.dim(" Sign up at devwing.ai"));
3638
3522
  }
3639
3523
  }
3640
3524
  async cmdContext() {
3641
- console.log();
3642
3525
  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"));
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);
3670
3555
  } 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
- }
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}`);
3677
3558
  console.log();
3678
- console.log(chalk7.bold(" Files in context:"));
3679
3559
  for (const f of this.sessionContext.files.slice(0, 10)) {
3680
- console.log(` ${chalk7.dim("\u2022")} ${chalk7.cyan(f.path)}`);
3560
+ console.log(` ${chalk7.dim("\xB7")} ${chalk7.cyan(f.path)}`);
3681
3561
  }
3682
3562
  if (this.sessionContext.files.length > 10) {
3683
- console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3563
+ console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
3684
3564
  }
3685
3565
  } else {
3686
3566
  logger.info("No context loaded yet. Send a message to load context.");
3687
3567
  }
3688
3568
  }
3689
3569
  async cmdHistory() {
3690
- console.log();
3691
- if (this.conversationHistory.length === 0) {
3570
+ if (!this.conversationHistory.length) {
3692
3571
  logger.info("No conversation history yet.");
3693
3572
  return;
3694
3573
  }
3574
+ console.log();
3695
3575
  for (const msg of this.conversationHistory.slice(-20)) {
3696
- 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 ");
3697
3577
  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}`);
3578
+ const preview = msg.content.length > 100 ? msg.content.substring(0, 100) + "\u2026" : msg.content;
3579
+ console.log(` ${time} ${label} ${preview}`);
3700
3580
  }
3701
3581
  }
3702
3582
  async cmdConfig(args) {
3703
3583
  if (!args) {
3704
- console.log();
3705
3584
  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>"));
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>"));
3720
3602
  return;
3721
3603
  }
3722
3604
  const parts = args.split(/\s+/);