@freesyntax/notch-cli 0.4.2 → 0.4.4

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
@@ -3,7 +3,7 @@ import {
3
3
  clearCredentials,
4
4
  loadCredentials,
5
5
  login
6
- } from "./chunk-TJS4W4R5.js";
6
+ } from "./chunk-FIFC4V2R.js";
7
7
  import {
8
8
  autoCompress,
9
9
  estimateTokens
@@ -11,7 +11,7 @@ import {
11
11
 
12
12
  // src/index.ts
13
13
  import { Command } from "commander";
14
- import chalk8 from "chalk";
14
+ import chalk9 from "chalk";
15
15
  import ora from "ora";
16
16
  import * as readline from "readline";
17
17
  import * as nodePath from "path";
@@ -830,20 +830,373 @@ ${text}` };
830
830
  }
831
831
  };
832
832
 
833
+ // src/tools/github.ts
834
+ import { z as z9 } from "zod";
835
+ var GITHUB_API = "https://api.github.com";
836
+ function getToken() {
837
+ return process.env.GITHUB_TOKEN || process.env.GH_TOKEN || null;
838
+ }
839
+ async function ghFetch(path19, opts2 = {}) {
840
+ const token = getToken();
841
+ const headers = {
842
+ "Accept": "application/vnd.github+json",
843
+ "X-GitHub-Api-Version": "2022-11-28",
844
+ ...opts2.headers ?? {}
845
+ };
846
+ if (token) headers["Authorization"] = `Bearer ${token}`;
847
+ return fetch(`${GITHUB_API}${path19}`, { ...opts2, headers });
848
+ }
849
+ var parameters9 = z9.object({
850
+ action: z9.enum([
851
+ "create_pr",
852
+ "list_prs",
853
+ "list_issues",
854
+ "repo_info",
855
+ "create_issue",
856
+ "pr_status"
857
+ ]).describe("The GitHub operation to perform"),
858
+ owner: z9.string().describe("Repository owner (user or org)"),
859
+ repo: z9.string().describe("Repository name"),
860
+ // PR fields
861
+ title: z9.string().optional().describe("PR or issue title"),
862
+ body: z9.string().optional().describe("PR or issue body/description"),
863
+ head: z9.string().optional().describe("PR source branch"),
864
+ base: z9.string().optional().describe("PR target branch (default: main)"),
865
+ // Issue fields
866
+ labels: z9.array(z9.string()).optional().describe("Labels for issue"),
867
+ // Filter
868
+ state: z9.enum(["open", "closed", "all"]).optional().describe("Filter by state"),
869
+ pr_number: z9.number().optional().describe("PR number for status check")
870
+ });
871
+ async function execute(args, _ctx) {
872
+ const token = getToken();
873
+ if (!token) {
874
+ return {
875
+ content: "No GitHub token found. Set GITHUB_TOKEN or GH_TOKEN environment variable.\nYou can create a token at https://github.com/settings/tokens",
876
+ isError: true
877
+ };
878
+ }
879
+ try {
880
+ switch (args.action) {
881
+ case "repo_info": {
882
+ const res = await ghFetch(`/repos/${args.owner}/${args.repo}`);
883
+ if (!res.ok) return { content: `GitHub API error: ${res.status} ${await res.text()}`, isError: true };
884
+ const repo = await res.json();
885
+ return {
886
+ content: [
887
+ `Repository: ${repo.full_name}`,
888
+ `Description: ${repo.description || "None"}`,
889
+ `Stars: ${repo.stargazers_count} | Forks: ${repo.forks_count} | Open issues: ${repo.open_issues_count}`,
890
+ `Default branch: ${repo.default_branch}`,
891
+ `Language: ${repo.language || "Unknown"}`,
892
+ `Private: ${repo.private}`,
893
+ `URL: ${repo.html_url}`
894
+ ].join("\n")
895
+ };
896
+ }
897
+ case "list_prs": {
898
+ const state = args.state ?? "open";
899
+ const res = await ghFetch(`/repos/${args.owner}/${args.repo}/pulls?state=${state}&per_page=15`);
900
+ if (!res.ok) return { content: `GitHub API error: ${res.status}`, isError: true };
901
+ const prs = await res.json();
902
+ if (prs.length === 0) return { content: `No ${state} pull requests.` };
903
+ const lines = prs.map(
904
+ (pr) => `#${pr.number} [${pr.state}] ${pr.title} (${pr.head.ref} \u2192 ${pr.base.ref}) by @${pr.user.login}`
905
+ );
906
+ return { content: lines.join("\n") };
907
+ }
908
+ case "list_issues": {
909
+ const state = args.state ?? "open";
910
+ const res = await ghFetch(`/repos/${args.owner}/${args.repo}/issues?state=${state}&per_page=15`);
911
+ if (!res.ok) return { content: `GitHub API error: ${res.status}`, isError: true };
912
+ const issues = await res.json();
913
+ const realIssues = issues.filter((i) => !i.pull_request);
914
+ if (realIssues.length === 0) return { content: `No ${state} issues.` };
915
+ const lines = realIssues.map((i) => {
916
+ const labels = i.labels.map((l) => l.name).join(", ");
917
+ return `#${i.number} [${i.state}] ${i.title}${labels ? ` [${labels}]` : ""} by @${i.user.login}`;
918
+ });
919
+ return { content: lines.join("\n") };
920
+ }
921
+ case "create_pr": {
922
+ if (!args.title || !args.head) {
923
+ return { content: "title and head branch are required for create_pr", isError: true };
924
+ }
925
+ const res = await ghFetch(`/repos/${args.owner}/${args.repo}/pulls`, {
926
+ method: "POST",
927
+ body: JSON.stringify({
928
+ title: args.title,
929
+ body: args.body ?? "",
930
+ head: args.head,
931
+ base: args.base ?? "main"
932
+ })
933
+ });
934
+ if (!res.ok) {
935
+ const err = await res.json();
936
+ return { content: `Failed to create PR: ${err.message || res.status}`, isError: true };
937
+ }
938
+ const pr = await res.json();
939
+ return { content: `PR #${pr.number} created: ${pr.html_url}` };
940
+ }
941
+ case "create_issue": {
942
+ if (!args.title) {
943
+ return { content: "title is required for create_issue", isError: true };
944
+ }
945
+ const res = await ghFetch(`/repos/${args.owner}/${args.repo}/issues`, {
946
+ method: "POST",
947
+ body: JSON.stringify({
948
+ title: args.title,
949
+ body: args.body ?? "",
950
+ labels: args.labels ?? []
951
+ })
952
+ });
953
+ if (!res.ok) {
954
+ const err = await res.json();
955
+ return { content: `Failed to create issue: ${err.message || res.status}`, isError: true };
956
+ }
957
+ const issue = await res.json();
958
+ return { content: `Issue #${issue.number} created: ${issue.html_url}` };
959
+ }
960
+ case "pr_status": {
961
+ if (!args.pr_number) {
962
+ return { content: "pr_number is required for pr_status", isError: true };
963
+ }
964
+ const res = await ghFetch(`/repos/${args.owner}/${args.repo}/pulls/${args.pr_number}`);
965
+ if (!res.ok) return { content: `GitHub API error: ${res.status}`, isError: true };
966
+ const pr = await res.json();
967
+ const checksRes = await ghFetch(`/repos/${args.owner}/${args.repo}/commits/${pr.head.sha}/check-runs`);
968
+ let checksInfo = "";
969
+ if (checksRes.ok) {
970
+ const checks = await checksRes.json();
971
+ if (checks.check_runs?.length) {
972
+ checksInfo = "\nChecks:\n" + checks.check_runs.map(
973
+ (c) => ` ${c.status === "completed" ? c.conclusion === "success" ? "\u2713" : "\u2717" : "\u25CC"} ${c.name}: ${c.conclusion ?? c.status}`
974
+ ).join("\n");
975
+ }
976
+ }
977
+ return {
978
+ content: [
979
+ `PR #${pr.number}: ${pr.title}`,
980
+ `State: ${pr.state} | Mergeable: ${pr.mergeable ?? "checking..."}`,
981
+ `${pr.head.ref} \u2192 ${pr.base.ref}`,
982
+ `Author: @${pr.user.login}`,
983
+ `+${pr.additions} -${pr.deletions} (${pr.changed_files} files)`,
984
+ `Reviews: ${pr.requested_reviewers?.length ?? 0} requested`,
985
+ checksInfo,
986
+ `URL: ${pr.html_url}`
987
+ ].join("\n")
988
+ };
989
+ }
990
+ default:
991
+ return { content: `Unknown action: ${args.action}`, isError: true };
992
+ }
993
+ } catch (err) {
994
+ return { content: `GitHub error: ${err.message}`, isError: true };
995
+ }
996
+ }
997
+ var githubTool = {
998
+ name: "github",
999
+ description: "GitHub operations \u2014 create PRs, list issues, check PR status, view repo info. Requires GITHUB_TOKEN env var.",
1000
+ parameters: parameters9,
1001
+ execute
1002
+ };
1003
+
1004
+ // src/mcp/client.ts
1005
+ import { spawn } from "child_process";
1006
+ import { z as z10 } from "zod";
1007
+ var MCPClient = class {
1008
+ constructor(config, serverName) {
1009
+ this.config = config;
1010
+ this.serverName = serverName;
1011
+ }
1012
+ process = null;
1013
+ requestId = 0;
1014
+ pendingRequests = /* @__PURE__ */ new Map();
1015
+ buffer = "";
1016
+ serverName;
1017
+ _tools = [];
1018
+ /**
1019
+ * Start the MCP server and initialize the connection.
1020
+ */
1021
+ async connect() {
1022
+ this.process = spawn(this.config.command, this.config.args ?? [], {
1023
+ stdio: ["pipe", "pipe", "pipe"],
1024
+ env: { ...process.env, ...this.config.env },
1025
+ cwd: this.config.cwd
1026
+ });
1027
+ this.process.stdout?.setEncoding("utf-8");
1028
+ this.process.stdout?.on("data", (data) => {
1029
+ this.buffer += data;
1030
+ this.processBuffer();
1031
+ });
1032
+ this.process.on("error", (err) => {
1033
+ for (const [id, pending] of this.pendingRequests) {
1034
+ pending.reject(new Error(`MCP server ${this.serverName} error: ${err.message}`));
1035
+ this.pendingRequests.delete(id);
1036
+ }
1037
+ });
1038
+ this.process.on("exit", (code) => {
1039
+ for (const [id, pending] of this.pendingRequests) {
1040
+ pending.reject(new Error(`MCP server ${this.serverName} exited with code ${code}`));
1041
+ this.pendingRequests.delete(id);
1042
+ }
1043
+ });
1044
+ await this.sendRequest("initialize", {
1045
+ protocolVersion: "2024-11-05",
1046
+ capabilities: {},
1047
+ clientInfo: { name: "notch-cli", version: "0.3.0" }
1048
+ });
1049
+ this.sendNotification("notifications/initialized", {});
1050
+ const result = await this.sendRequest("tools/list", {});
1051
+ this._tools = result.tools ?? [];
1052
+ }
1053
+ /**
1054
+ * Get discovered tools from this server.
1055
+ */
1056
+ get tools() {
1057
+ return this._tools;
1058
+ }
1059
+ /**
1060
+ * Check if the MCP server process is still alive.
1061
+ */
1062
+ get isAlive() {
1063
+ return this.process !== null && this.process.exitCode === null && !this.process.killed;
1064
+ }
1065
+ /**
1066
+ * Call a tool on the MCP server. Auto-reconnects if the server has crashed.
1067
+ */
1068
+ async callTool(name, args) {
1069
+ if (!this.isAlive) {
1070
+ try {
1071
+ await this.connect();
1072
+ } catch (err) {
1073
+ throw new Error(`MCP server ${this.serverName} is down and could not reconnect: ${err.message}`);
1074
+ }
1075
+ }
1076
+ const result = await this.sendRequest("tools/call", { name, arguments: args });
1077
+ return result;
1078
+ }
1079
+ /**
1080
+ * Disconnect from the MCP server.
1081
+ */
1082
+ disconnect() {
1083
+ if (this.process) {
1084
+ this.process.stdin?.end();
1085
+ this.process.kill();
1086
+ this.process = null;
1087
+ }
1088
+ this.pendingRequests.clear();
1089
+ }
1090
+ sendRequest(method, params) {
1091
+ return new Promise((resolve2, reject) => {
1092
+ const id = ++this.requestId;
1093
+ const msg = {
1094
+ jsonrpc: "2.0",
1095
+ id,
1096
+ method,
1097
+ params
1098
+ };
1099
+ this.pendingRequests.set(id, { resolve: resolve2, reject });
1100
+ const data = JSON.stringify(msg);
1101
+ const header = `Content-Length: ${Buffer.byteLength(data)}\r
1102
+ \r
1103
+ `;
1104
+ this.process?.stdin?.write(header + data);
1105
+ setTimeout(() => {
1106
+ if (this.pendingRequests.has(id)) {
1107
+ this.pendingRequests.delete(id);
1108
+ reject(new Error(`MCP request ${method} timed out`));
1109
+ }
1110
+ }, 3e4);
1111
+ });
1112
+ }
1113
+ sendNotification(method, params) {
1114
+ const msg = {
1115
+ jsonrpc: "2.0",
1116
+ method,
1117
+ params
1118
+ };
1119
+ const data = JSON.stringify(msg);
1120
+ const header = `Content-Length: ${Buffer.byteLength(data)}\r
1121
+ \r
1122
+ `;
1123
+ this.process?.stdin?.write(header + data);
1124
+ }
1125
+ processBuffer() {
1126
+ while (this.buffer.length > 0) {
1127
+ const headerEnd = this.buffer.indexOf("\r\n\r\n");
1128
+ if (headerEnd === -1) break;
1129
+ const header = this.buffer.slice(0, headerEnd);
1130
+ const lengthMatch = header.match(/Content-Length:\s*(\d+)/i);
1131
+ if (!lengthMatch) {
1132
+ const nlIdx = this.buffer.indexOf("\n");
1133
+ if (nlIdx === -1) break;
1134
+ const line = this.buffer.slice(0, nlIdx).trim();
1135
+ this.buffer = this.buffer.slice(nlIdx + 1);
1136
+ if (line) this.handleMessage(line);
1137
+ continue;
1138
+ }
1139
+ const contentLength = parseInt(lengthMatch[1], 10);
1140
+ const messageStart = headerEnd + 4;
1141
+ if (this.buffer.length < messageStart + contentLength) break;
1142
+ const body = this.buffer.slice(messageStart, messageStart + contentLength);
1143
+ this.buffer = this.buffer.slice(messageStart + contentLength);
1144
+ this.handleMessage(body);
1145
+ }
1146
+ }
1147
+ handleMessage(raw) {
1148
+ try {
1149
+ const msg = JSON.parse(raw);
1150
+ if (msg.id !== void 0 && this.pendingRequests.has(msg.id)) {
1151
+ const pending = this.pendingRequests.get(msg.id);
1152
+ this.pendingRequests.delete(msg.id);
1153
+ if (msg.error) {
1154
+ pending.reject(new Error(`MCP error: ${msg.error.message}`));
1155
+ } else {
1156
+ pending.resolve(msg.result);
1157
+ }
1158
+ }
1159
+ } catch {
1160
+ }
1161
+ }
1162
+ };
1163
+ function parseMCPConfig(config) {
1164
+ const servers = config?.mcpServers;
1165
+ if (!servers || typeof servers !== "object") return {};
1166
+ const result = {};
1167
+ for (const [name, cfg] of Object.entries(servers)) {
1168
+ const c = cfg;
1169
+ if (c?.command) {
1170
+ result[name] = {
1171
+ command: c.command,
1172
+ args: c.args,
1173
+ env: c.env,
1174
+ cwd: c.cwd
1175
+ };
1176
+ }
1177
+ }
1178
+ return result;
1179
+ }
1180
+
833
1181
  // src/tools/index.ts
834
- var ALL_TOOLS = [
1182
+ var BUILTIN_TOOLS = [
835
1183
  readTool,
836
1184
  writeTool,
837
1185
  editTool,
838
1186
  shellTool,
839
1187
  gitTool,
1188
+ githubTool,
840
1189
  grepTool,
841
1190
  globTool,
842
1191
  webFetchTool
843
1192
  ];
1193
+ var mcpTools = [];
1194
+ function getAllTools() {
1195
+ return [...BUILTIN_TOOLS, ...mcpTools];
1196
+ }
844
1197
  function buildToolMap(ctx) {
845
1198
  const map = {};
846
- for (const t of ALL_TOOLS) {
1199
+ for (const t of getAllTools()) {
847
1200
  map[t.name] = tool({
848
1201
  description: t.description,
849
1202
  parameters: t.parameters,
@@ -888,7 +1241,7 @@ ${paramSummary}`
888
1241
  return map;
889
1242
  }
890
1243
  function describeTools() {
891
- return ALL_TOOLS.map(
1244
+ return getAllTools().map(
892
1245
  (t) => `- **${t.name}**: ${t.description}`
893
1246
  ).join("\n");
894
1247
  }
@@ -2311,36 +2664,49 @@ async function resolveGlob(pattern, cwd) {
2311
2664
  }
2312
2665
  }
2313
2666
 
2667
+ // src/ui/banner.ts
2668
+ import chalk6 from "chalk";
2669
+
2314
2670
  // src/ui/themes.ts
2315
2671
  import chalk5 from "chalk";
2316
2672
  var defaultTheme = {
2317
2673
  name: "Default",
2318
- description: "Classic Notch \u2014 green mantis, blue wordmark",
2319
- brand: chalk5.blueBright,
2320
- mascot: chalk5.greenBright,
2321
- mascotAccent: chalk5.yellowBright,
2322
- tagline: chalk5.cyan,
2323
- prompt: chalk5.blueBright,
2324
- border: chalk5.gray,
2325
- dim: chalk5.gray,
2326
- text: chalk5.white,
2327
- bold: chalk5.white.bold,
2674
+ description: "FreeSyntax \u2014 silver, white, monochrome",
2675
+ brand: chalk5.hex("#D4D4D4"),
2676
+ // silver (banner uses gradient override)
2677
+ mascot: chalk5.hex("#AAAAAA"),
2678
+ // medium gray mantis
2679
+ mascotAccent: chalk5.hex("#FFFFFF"),
2680
+ // white eyes
2681
+ tagline: chalk5.hex("#777777"),
2682
+ // muted gray
2683
+ prompt: chalk5.hex("#CCCCCC"),
2684
+ // silver prompt
2685
+ border: chalk5.hex("#444444"),
2686
+ // dark border
2687
+ dim: chalk5.hex("#666666"),
2688
+ // muted text
2689
+ text: chalk5.hex("#D4D4D4"),
2690
+ // silver body text
2691
+ bold: chalk5.hex("#FFFFFF").bold,
2692
+ // pure white emphasis
2328
2693
  success: chalk5.green,
2329
2694
  warning: chalk5.yellow,
2330
2695
  error: chalk5.red,
2331
- info: chalk5.cyan,
2332
- toolName: chalk5.gray,
2333
- toolArgs: chalk5.gray,
2334
- toolResult: chalk5.gray,
2696
+ info: chalk5.hex("#BBBBBB"),
2697
+ // light gray info
2698
+ toolName: chalk5.hex("#AAAAAA"),
2699
+ toolArgs: chalk5.hex("#777777"),
2700
+ toolResult: chalk5.hex("#555555"),
2335
2701
  diffAdd: chalk5.green,
2336
2702
  diffRemove: chalk5.red,
2337
- diffHeader: chalk5.cyan,
2338
- mdH1: chalk5.white.bold,
2339
- mdH2: chalk5.blueBright,
2340
- mdH3: chalk5.cyan,
2341
- mdCode: chalk5.cyan,
2342
- mdInlineCode: chalk5.bgGray.white,
2343
- mdLink: chalk5.blue.underline,
2703
+ diffHeader: chalk5.hex("#CCCCCC"),
2704
+ mdH1: chalk5.hex("#FFFFFF").bold,
2705
+ mdH2: chalk5.hex("#D4D4D4"),
2706
+ mdH3: chalk5.hex("#AAAAAA"),
2707
+ mdCode: chalk5.hex("#BBBBBB"),
2708
+ mdInlineCode: chalk5.hex("#CCCCCC"),
2709
+ mdLink: chalk5.hex("#D4D4D4").underline,
2344
2710
  meterLow: chalk5.green,
2345
2711
  meterMid: chalk5.yellow,
2346
2712
  meterHigh: chalk5.red
@@ -2790,6 +3156,39 @@ function formatThemeList(activeId) {
2790
3156
  }
2791
3157
 
2792
3158
  // src/ui/banner.ts
3159
+ var NOTCH_LARGE = [
3160
+ "\u2588\u2588\u2584 \u2588 \u2584\u2588\u2588\u2580\u2580\u2588\u2588\u2584 \u2580\u2588\u2588\u2580\u2580\u2580\u2580 \u2584\u2588\u2588\u2580\u2580\u2588\u2588\u2584 \u2588\u2588 \u2588\u2588",
3161
+ "\u2588\u2580\u2588\u2588 \u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2580\u2588\u2588\u2580\u2588",
3162
+ "\u2588 \u2580\u2588\u2584 \u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588 \u2580 \u2588",
3163
+ "\u2588 \u2580\u2588\u2584\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588 \u2588",
3164
+ "\u2588 \u2580\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588 \u2588",
3165
+ "\u2588 \u2588\u2588 \u2580\u2588\u2588\u2584\u2584\u2588\u2588\u2580 \u2588\u2588 \u2580\u2588\u2588\u2584\u2584\u2588\u2588\u2580 \u2588 \u2588",
3166
+ "\u2580 \u2580 \u2580\u2580\u2580\u2580 \u2580\u2580 \u2580\u2580\u2580\u2580 \u2580 \u2580"
3167
+ ];
3168
+ var ROW_COLORS = [
3169
+ "#666666",
3170
+ // dim silver
3171
+ "#888888",
3172
+ // medium gray
3173
+ "#AAAAAA",
3174
+ // silver
3175
+ "#CCCCCC",
3176
+ // light silver
3177
+ "#DDDDDD",
3178
+ // near white
3179
+ "#EEEEEE",
3180
+ // bright
3181
+ "#FFFFFF"
3182
+ // pure white
3183
+ ];
3184
+ function colorBannerLine(line, rowIndex) {
3185
+ const t = theme();
3186
+ if (t.name !== "Default") {
3187
+ return t.brand(line);
3188
+ }
3189
+ const color = ROW_COLORS[rowIndex] ?? "#CCCCCC";
3190
+ return chalk6.hex(color)(line);
3191
+ }
2793
3192
  var MANTIS = [
2794
3193
  " \u2571\u25C9\u25C9\u2572",
2795
3194
  // ╱◉◉╲ antennae + eyes
@@ -2804,11 +3203,6 @@ var MANTIS = [
2804
3203
  " \u2580\u2580 \u2580\u2580"
2805
3204
  // ▀▀ ▀▀ feet
2806
3205
  ];
2807
- var LOGO_INLINE = [
2808
- " \u2588\u2588\u2584 \u2588 \u2584\u2580\u2580\u2584 \u2580\u2588\u2580 \u2584\u2580\u2580\u2584 \u2588 \u2588",
2809
- " \u2588 \u2580\u2584 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2580\u2580\u2588",
2810
- " \u2588 \u2580\u2588\u2588 \u2580\u2584\u2584\u2580 \u2588 \u2580\u2584\u2584\u2580 \u2588 \u2588"
2811
- ];
2812
3206
  function colorMantis(line) {
2813
3207
  const t = theme();
2814
3208
  return line.replace(/[\u2571\u2572]/g, (ch) => t.mascot(ch)).replace(/\u25c9/g, t.mascotAccent("\u25C9")).replace(/[\u2588]/g, (ch) => t.mascot(ch)).replace(/[\u2584\u2580]/g, (ch) => t.mascot(ch)).replace(/\u2590/g, t.mascot("\u2590")).replace(/\u258c/g, t.mascot("\u258C"));
@@ -2817,24 +3211,49 @@ function printBanner(version, modelLabel, modelId, modelSize, project) {
2817
3211
  const t = theme();
2818
3212
  const termWidth = process.stdout.columns || 80;
2819
3213
  console.log("");
2820
- for (const line of MANTIS) {
2821
- console.log(" " + colorMantis(line));
2822
- }
2823
- console.log("");
2824
- for (const line of LOGO_INLINE) {
2825
- console.log(" " + t.brand(line));
3214
+ for (let i = 0; i < NOTCH_LARGE.length; i++) {
3215
+ console.log(" " + colorBannerLine(NOTCH_LARGE[i], i));
2826
3216
  }
2827
3217
  console.log("");
2828
- const divWidth = Math.min(50, termWidth - 4);
3218
+ const mantisStr = MANTIS.map((l) => colorMantis(l));
3219
+ const divWidth = Math.min(54, termWidth - 4);
2829
3220
  const divider = t.border(" " + "\u2500".repeat(divWidth));
2830
3221
  console.log(divider);
2831
- console.log(
2832
- t.dim(" ") + t.bold(modelLabel) + t.dim(" \u2502 v") + t.text(version) + t.dim(" \u2502 ") + t.dim("by ") + t.tagline("Driftrail")
2833
- );
2834
- console.log(t.dim(` ${project}`));
3222
+ const info = [
3223
+ t.dim(" ") + t.bold(modelLabel) + t.dim(" \u2502 v") + t.text(version),
3224
+ t.dim(" ") + t.dim(project),
3225
+ t.dim(" ") + t.dim("by ") + t.tagline("Driftrail")
3226
+ ];
3227
+ for (let i = 0; i < Math.max(mantisStr.length, info.length); i++) {
3228
+ const left = i < mantisStr.length ? " " + mantisStr[i] : "";
3229
+ const right = i < info.length ? info[i] : "";
3230
+ if (left && right) {
3231
+ const rawLeft = " " + MANTIS[i];
3232
+ const pad = Math.max(0, 16 - rawLeft.length);
3233
+ console.log(left + " ".repeat(pad) + right);
3234
+ } else if (left) {
3235
+ console.log(left);
3236
+ } else {
3237
+ console.log(" " + " ".repeat(14) + right);
3238
+ }
3239
+ }
2835
3240
  console.log(divider);
2836
3241
  console.log("");
2837
3242
  }
3243
+ function printWordmark(version) {
3244
+ const t = theme();
3245
+ const termWidth = process.stdout.columns || 80;
3246
+ console.log("");
3247
+ for (let i = 0; i < NOTCH_LARGE.length; i++) {
3248
+ console.log(" " + colorBannerLine(NOTCH_LARGE[i], i));
3249
+ }
3250
+ console.log("");
3251
+ const divWidth = Math.min(54, termWidth - 4);
3252
+ console.log(t.border(" " + "\u2500".repeat(divWidth)));
3253
+ console.log(t.dim(" v" + version) + t.dim(" \u2502 ") + t.tagline("by Driftrail"));
3254
+ console.log(t.border(" " + "\u2500".repeat(divWidth)));
3255
+ console.log("");
3256
+ }
2838
3257
  function printMantis() {
2839
3258
  const t = theme();
2840
3259
  console.log("");
@@ -2877,7 +3296,7 @@ function formatTokens(n) {
2877
3296
  import fs12 from "fs/promises";
2878
3297
  import path13 from "path";
2879
3298
  import os3 from "os";
2880
- import chalk6 from "chalk";
3299
+ import chalk7 from "chalk";
2881
3300
  var CACHE_FILE = path13.join(os3.homedir(), ".notch", "update-check.json");
2882
3301
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
2883
3302
  var PACKAGE_NAME = "notch-cli";
@@ -2921,7 +3340,7 @@ function isNewer(latest, current) {
2921
3340
  return false;
2922
3341
  }
2923
3342
  function formatUpdateMessage(current, latest) {
2924
- return chalk6.yellow(` Update available: ${current} -> ${latest}. Run: npm update -g ${PACKAGE_NAME}
3343
+ return chalk7.yellow(` Update available: ${current} -> ${latest}. Run: npm update -g ${PACKAGE_NAME}
2925
3344
  `);
2926
3345
  }
2927
3346
  async function loadCache() {
@@ -3275,7 +3694,7 @@ async function exportSession(messages, outputPath, meta) {
3275
3694
  // src/init.ts
3276
3695
  import fs16 from "fs/promises";
3277
3696
  import path17 from "path";
3278
- import chalk7 from "chalk";
3697
+ import chalk8 from "chalk";
3279
3698
  var DEFAULT_CONFIG = {
3280
3699
  model: "notch-forge",
3281
3700
  temperature: 0.3,
@@ -3322,15 +3741,15 @@ async function initProject(projectRoot) {
3322
3741
  }
3323
3742
  if (!configExists) {
3324
3743
  await fs16.writeFile(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
3325
- console.log(chalk7.green(` Created ${configPath}`));
3744
+ console.log(chalk8.green(` Created ${configPath}`));
3326
3745
  } else {
3327
- console.log(chalk7.gray(` Skipped ${configPath} (already exists)`));
3746
+ console.log(chalk8.gray(` Skipped ${configPath} (already exists)`));
3328
3747
  }
3329
3748
  if (!instructionsExist) {
3330
3749
  await fs16.writeFile(instructionsPath, DEFAULT_INSTRUCTIONS, "utf-8");
3331
- console.log(chalk7.green(` Created ${instructionsPath}`));
3750
+ console.log(chalk8.green(` Created ${instructionsPath}`));
3332
3751
  } else {
3333
- console.log(chalk7.gray(` Skipped ${instructionsPath} (already exists)`));
3752
+ console.log(chalk8.gray(` Skipped ${instructionsPath} (already exists)`));
3334
3753
  }
3335
3754
  const gitignorePath = path17.join(projectRoot, ".gitignore");
3336
3755
  try {
@@ -3340,13 +3759,13 @@ async function initProject(projectRoot) {
3340
3759
  if (additions.length > 0) {
3341
3760
  const append = "\n# Notch CLI\n" + additions.join("\n") + "\n";
3342
3761
  await fs16.appendFile(gitignorePath, append, "utf-8");
3343
- console.log(chalk7.green(` Updated .gitignore`));
3762
+ console.log(chalk8.green(` Updated .gitignore`));
3344
3763
  }
3345
3764
  } catch {
3346
3765
  }
3347
3766
  console.log("");
3348
- console.log(chalk7.cyan(" Notch initialized! Edit .notch.md to customize behavior."));
3349
- console.log(chalk7.gray(' Run "notch" to start.\n'));
3767
+ console.log(chalk8.cyan(" Notch initialized! Edit .notch.md to customize behavior."));
3768
+ console.log(chalk8.gray(' Run "notch" to start.\n'));
3350
3769
  }
3351
3770
 
3352
3771
  // src/tools/diff-preview.ts
@@ -3465,183 +3884,6 @@ function findSync(oldLines, newLines, oi, ni, lookAhead) {
3465
3884
  return null;
3466
3885
  }
3467
3886
 
3468
- // src/mcp/client.ts
3469
- import { spawn } from "child_process";
3470
- import { z as z9 } from "zod";
3471
- var MCPClient = class {
3472
- constructor(config, serverName) {
3473
- this.config = config;
3474
- this.serverName = serverName;
3475
- }
3476
- process = null;
3477
- requestId = 0;
3478
- pendingRequests = /* @__PURE__ */ new Map();
3479
- buffer = "";
3480
- serverName;
3481
- _tools = [];
3482
- /**
3483
- * Start the MCP server and initialize the connection.
3484
- */
3485
- async connect() {
3486
- this.process = spawn(this.config.command, this.config.args ?? [], {
3487
- stdio: ["pipe", "pipe", "pipe"],
3488
- env: { ...process.env, ...this.config.env },
3489
- cwd: this.config.cwd
3490
- });
3491
- this.process.stdout?.setEncoding("utf-8");
3492
- this.process.stdout?.on("data", (data) => {
3493
- this.buffer += data;
3494
- this.processBuffer();
3495
- });
3496
- this.process.on("error", (err) => {
3497
- for (const [id, pending] of this.pendingRequests) {
3498
- pending.reject(new Error(`MCP server ${this.serverName} error: ${err.message}`));
3499
- this.pendingRequests.delete(id);
3500
- }
3501
- });
3502
- this.process.on("exit", (code) => {
3503
- for (const [id, pending] of this.pendingRequests) {
3504
- pending.reject(new Error(`MCP server ${this.serverName} exited with code ${code}`));
3505
- this.pendingRequests.delete(id);
3506
- }
3507
- });
3508
- await this.sendRequest("initialize", {
3509
- protocolVersion: "2024-11-05",
3510
- capabilities: {},
3511
- clientInfo: { name: "notch-cli", version: "0.3.0" }
3512
- });
3513
- this.sendNotification("notifications/initialized", {});
3514
- const result = await this.sendRequest("tools/list", {});
3515
- this._tools = result.tools ?? [];
3516
- }
3517
- /**
3518
- * Get discovered tools from this server.
3519
- */
3520
- get tools() {
3521
- return this._tools;
3522
- }
3523
- /**
3524
- * Check if the MCP server process is still alive.
3525
- */
3526
- get isAlive() {
3527
- return this.process !== null && this.process.exitCode === null && !this.process.killed;
3528
- }
3529
- /**
3530
- * Call a tool on the MCP server. Auto-reconnects if the server has crashed.
3531
- */
3532
- async callTool(name, args) {
3533
- if (!this.isAlive) {
3534
- try {
3535
- await this.connect();
3536
- } catch (err) {
3537
- throw new Error(`MCP server ${this.serverName} is down and could not reconnect: ${err.message}`);
3538
- }
3539
- }
3540
- const result = await this.sendRequest("tools/call", { name, arguments: args });
3541
- return result;
3542
- }
3543
- /**
3544
- * Disconnect from the MCP server.
3545
- */
3546
- disconnect() {
3547
- if (this.process) {
3548
- this.process.stdin?.end();
3549
- this.process.kill();
3550
- this.process = null;
3551
- }
3552
- this.pendingRequests.clear();
3553
- }
3554
- sendRequest(method, params) {
3555
- return new Promise((resolve2, reject) => {
3556
- const id = ++this.requestId;
3557
- const msg = {
3558
- jsonrpc: "2.0",
3559
- id,
3560
- method,
3561
- params
3562
- };
3563
- this.pendingRequests.set(id, { resolve: resolve2, reject });
3564
- const data = JSON.stringify(msg);
3565
- const header = `Content-Length: ${Buffer.byteLength(data)}\r
3566
- \r
3567
- `;
3568
- this.process?.stdin?.write(header + data);
3569
- setTimeout(() => {
3570
- if (this.pendingRequests.has(id)) {
3571
- this.pendingRequests.delete(id);
3572
- reject(new Error(`MCP request ${method} timed out`));
3573
- }
3574
- }, 3e4);
3575
- });
3576
- }
3577
- sendNotification(method, params) {
3578
- const msg = {
3579
- jsonrpc: "2.0",
3580
- method,
3581
- params
3582
- };
3583
- const data = JSON.stringify(msg);
3584
- const header = `Content-Length: ${Buffer.byteLength(data)}\r
3585
- \r
3586
- `;
3587
- this.process?.stdin?.write(header + data);
3588
- }
3589
- processBuffer() {
3590
- while (this.buffer.length > 0) {
3591
- const headerEnd = this.buffer.indexOf("\r\n\r\n");
3592
- if (headerEnd === -1) break;
3593
- const header = this.buffer.slice(0, headerEnd);
3594
- const lengthMatch = header.match(/Content-Length:\s*(\d+)/i);
3595
- if (!lengthMatch) {
3596
- const nlIdx = this.buffer.indexOf("\n");
3597
- if (nlIdx === -1) break;
3598
- const line = this.buffer.slice(0, nlIdx).trim();
3599
- this.buffer = this.buffer.slice(nlIdx + 1);
3600
- if (line) this.handleMessage(line);
3601
- continue;
3602
- }
3603
- const contentLength = parseInt(lengthMatch[1], 10);
3604
- const messageStart = headerEnd + 4;
3605
- if (this.buffer.length < messageStart + contentLength) break;
3606
- const body = this.buffer.slice(messageStart, messageStart + contentLength);
3607
- this.buffer = this.buffer.slice(messageStart + contentLength);
3608
- this.handleMessage(body);
3609
- }
3610
- }
3611
- handleMessage(raw) {
3612
- try {
3613
- const msg = JSON.parse(raw);
3614
- if (msg.id !== void 0 && this.pendingRequests.has(msg.id)) {
3615
- const pending = this.pendingRequests.get(msg.id);
3616
- this.pendingRequests.delete(msg.id);
3617
- if (msg.error) {
3618
- pending.reject(new Error(`MCP error: ${msg.error.message}`));
3619
- } else {
3620
- pending.resolve(msg.result);
3621
- }
3622
- }
3623
- } catch {
3624
- }
3625
- }
3626
- };
3627
- function parseMCPConfig(config) {
3628
- const servers = config?.mcpServers;
3629
- if (!servers || typeof servers !== "object") return {};
3630
- const result = {};
3631
- for (const [name, cfg] of Object.entries(servers)) {
3632
- const c = cfg;
3633
- if (c?.command) {
3634
- result[name] = {
3635
- command: c.command,
3636
- args: c.args,
3637
- env: c.env,
3638
- cwd: c.cwd
3639
- };
3640
- }
3641
- }
3642
- return result;
3643
- }
3644
-
3645
3887
  // src/ui/completions.ts
3646
3888
  import fs17 from "fs";
3647
3889
  import path18 from "path";
@@ -3749,7 +3991,7 @@ function printModelTable(activeModel) {
3749
3991
  `));
3750
3992
  }
3751
3993
  function printHelp() {
3752
- console.log(chalk8.gray(`
3994
+ console.log(chalk9.gray(`
3753
3995
  Commands:
3754
3996
  /model \u2014 Show available models
3755
3997
  /model <name> \u2014 Switch model (e.g., /model pyre)
@@ -3822,13 +4064,13 @@ async function main() {
3822
4064
  try {
3823
4065
  spinner.stop();
3824
4066
  const creds = await login();
3825
- console.log(chalk8.green(`
4067
+ console.log(chalk9.green(`
3826
4068
  \u2713 Signed in as ${creds.email}`));
3827
- console.log(chalk8.gray(` API key stored in ${(await import("./auth-GTGBXOSH.js")).getCredentialsPath()}
4069
+ console.log(chalk9.gray(` API key stored in ${(await import("./auth-JQX6MHJG.js")).getCredentialsPath()}
3828
4070
  `));
3829
4071
  } catch (err) {
3830
4072
  spinner.stop();
3831
- console.error(chalk8.red(`
4073
+ console.error(chalk9.red(`
3832
4074
  Login failed: ${err.message}
3833
4075
  `));
3834
4076
  process.exit(1);
@@ -3838,10 +4080,10 @@ async function main() {
3838
4080
  if (promptArgs[0] === "logout") {
3839
4081
  const creds = await loadCredentials();
3840
4082
  if (!creds) {
3841
- console.log(chalk8.gray("\n Not signed in.\n"));
4083
+ console.log(chalk9.gray("\n Not signed in.\n"));
3842
4084
  } else {
3843
4085
  await clearCredentials();
3844
- console.log(chalk8.green(`
4086
+ console.log(chalk9.green(`
3845
4087
  \u2713 Signed out (${creds.email})
3846
4088
  `));
3847
4089
  }
@@ -3850,13 +4092,13 @@ async function main() {
3850
4092
  if (promptArgs[0] === "whoami") {
3851
4093
  const creds = await loadCredentials();
3852
4094
  if (!creds) {
3853
- console.log(chalk8.gray("\n Not signed in. Run: notch login\n"));
4095
+ console.log(chalk9.gray("\n Not signed in. Run: notch login\n"));
3854
4096
  } else {
3855
4097
  const keyPreview = `${creds.token.slice(0, 12)}...`;
3856
- console.log(chalk8.gray(`
3857
- Signed in as ${chalk8.white(creds.email)}`));
3858
- console.log(chalk8.gray(` Key: ${keyPreview}`));
3859
- console.log(chalk8.gray(` Since: ${new Date(creds.createdAt).toLocaleDateString()}
4098
+ console.log(chalk9.gray(`
4099
+ Signed in as ${chalk9.white(creds.email)}`));
4100
+ console.log(chalk9.gray(` Key: ${keyPreview}`));
4101
+ console.log(chalk9.gray(` Since: ${new Date(creds.createdAt).toLocaleDateString()}
3860
4102
  `));
3861
4103
  }
3862
4104
  return;
@@ -3880,8 +4122,8 @@ async function main() {
3880
4122
  const config = await loadConfig(configOverrides);
3881
4123
  if (opts.model) {
3882
4124
  if (!isValidModel(opts.model)) {
3883
- console.error(chalk8.red(` Unknown model: ${opts.model}`));
3884
- console.error(chalk8.gray(` Available: ${modelChoices}`));
4125
+ console.error(chalk9.red(` Unknown model: ${opts.model}`));
4126
+ console.error(chalk9.gray(` Available: ${modelChoices}`));
3885
4127
  process.exit(1);
3886
4128
  }
3887
4129
  config.models.chat.model = opts.model;
@@ -3897,9 +4139,7 @@ async function main() {
3897
4139
  model = resolveModel(config.models.chat);
3898
4140
  } catch (err) {
3899
4141
  if (err instanceof MissingApiKeyError) {
3900
- console.log("");
3901
- console.log(" \x1B[1m\x1B[36m\u26A1 Welcome to Notch CLI\x1B[0m");
3902
- console.log("");
4142
+ printWordmark(VERSION);
3903
4143
  console.log(" To get started, you need a Notch API key.");
3904
4144
  console.log("");
3905
4145
  console.log(" \x1B[1mOption 1:\x1B[0m Log in via browser (recommended)");
@@ -3911,7 +4151,7 @@ async function main() {
3911
4151
  console.log(" \x1B[1mOption 3:\x1B[0m Pass it inline");
3912
4152
  console.log(" \x1B[33m$ notch --api-key your-key-here\x1B[0m");
3913
4153
  console.log("");
3914
- console.log(" Get your key at: \x1B[4mhttps://freesyntax.com/settings\x1B[0m");
4154
+ console.log(" Get your key at: \x1B[4mhttps://freesyntax.dev/settings\x1B[0m");
3915
4155
  console.log("");
3916
4156
  process.exit(0);
3917
4157
  }
@@ -3923,11 +4163,11 @@ async function main() {
3923
4163
  if (msg) console.log(msg);
3924
4164
  });
3925
4165
  const hookTrustPrompt = async (commands) => {
3926
- console.warn(chalk8.yellow("\n\u26A0 This project contains hooks in .notch.json that will run shell commands:"));
3927
- commands.forEach((cmd) => console.warn(chalk8.gray(` \u2022 ${cmd}`)));
4166
+ console.warn(chalk9.yellow("\n\u26A0 This project contains hooks in .notch.json that will run shell commands:"));
4167
+ commands.forEach((cmd) => console.warn(chalk9.gray(` \u2022 ${cmd}`)));
3928
4168
  const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
3929
4169
  return new Promise((resolve2) => {
3930
- rl2.question(chalk8.yellow("\nAllow these hooks for this project? [y/N] "), (answer) => {
4170
+ rl2.question(chalk9.yellow("\nAllow these hooks for this project? [y/N] "), (answer) => {
3931
4171
  rl2.close();
3932
4172
  resolve2(answer.trim().toLowerCase() === "y");
3933
4173
  });
@@ -3973,9 +4213,9 @@ ${repoMapStr}` : ""
3973
4213
  const client = new MCPClient(mcpConfig, name);
3974
4214
  await client.connect();
3975
4215
  mcpClients.push(client);
3976
- console.log(chalk8.green(` MCP: Connected to ${name} (${client.tools.length} tools)`));
4216
+ console.log(chalk9.green(` MCP: Connected to ${name} (${client.tools.length} tools)`));
3977
4217
  } catch (err) {
3978
- console.log(chalk8.yellow(` MCP: Could not connect to ${name}: ${err.message}`));
4218
+ console.log(chalk9.yellow(` MCP: Could not connect to ${name}: ${err.message}`));
3979
4219
  }
3980
4220
  }
3981
4221
  } catch {
@@ -3992,7 +4232,7 @@ ${repoMapStr}` : ""
3992
4232
  });
3993
4233
  });
3994
4234
  },
3995
- log: (msg) => console.log(chalk8.gray(` ${msg}`)),
4235
+ log: (msg) => console.log(chalk9.gray(` ${msg}`)),
3996
4236
  checkPermission: config.permissionMode === "trust" ? () => "allow" : (toolName, args) => checkPermission(permissions, toolName, args),
3997
4237
  runHook: async (event, ctx) => {
3998
4238
  if (!config.enableHooks || hookConfig.hooks.length === 0) return;
@@ -4002,7 +4242,7 @@ ${repoMapStr}` : ""
4002
4242
  });
4003
4243
  for (const r of results) {
4004
4244
  if (!r.ok) {
4005
- console.log(chalk8.yellow(` Hook failed (${r.hook.event}): ${r.error}`));
4245
+ console.log(chalk9.yellow(` Hook failed (${r.hook.event}): ${r.error}`));
4006
4246
  }
4007
4247
  }
4008
4248
  }
@@ -4014,10 +4254,10 @@ ${repoMapStr}` : ""
4014
4254
  if (session) {
4015
4255
  messages.push(...session.messages);
4016
4256
  sessionId = session.meta.id;
4017
- console.log(chalk8.green(` Resumed session ${session.meta.id} (${session.meta.turns} turns)
4257
+ console.log(chalk9.green(` Resumed session ${session.meta.id} (${session.meta.turns} turns)
4018
4258
  `));
4019
4259
  } else {
4020
- console.log(chalk8.gray(" No session to resume.\n"));
4260
+ console.log(chalk9.gray(" No session to resume.\n"));
4021
4261
  }
4022
4262
  }
4023
4263
  const pipedInput = await readStdin();
@@ -4038,10 +4278,10 @@ Analyze the above input.`;
4038
4278
  const refContext = formatReferences(references);
4039
4279
  const finalPrompt = refContext + cleanInput;
4040
4280
  messages.push({ role: "user", content: finalPrompt });
4041
- console.log(chalk8.cyan(`> ${oneShot || "(piped input)"}
4281
+ console.log(chalk9.cyan(`> ${oneShot || "(piped input)"}
4042
4282
  `));
4043
4283
  if (references.length > 0) {
4044
- console.log(chalk8.gray(` Injected ${references.length} reference(s)
4284
+ console.log(chalk9.gray(` Injected ${references.length} reference(s)
4045
4285
  `));
4046
4286
  }
4047
4287
  const spinner = ora("Thinking...").start();
@@ -4060,13 +4300,13 @@ Analyze the above input.`;
4060
4300
  onToolCall: (name, args) => {
4061
4301
  if (spinner.isSpinning) spinner.stop();
4062
4302
  const argSummary = Object.entries(args).map(([k, v]) => `${k}=${String(v).slice(0, 60)}`).join(", ");
4063
- console.log(chalk8.gray(`
4303
+ console.log(chalk9.gray(`
4064
4304
  \u2192 ${name}(${argSummary})`));
4065
4305
  },
4066
4306
  onToolResult: (_name, result, isError) => {
4067
4307
  const preview = result.slice(0, 100).replace(/\n/g, " ");
4068
- const icon = isError ? chalk8.red("\u2717") : chalk8.green("\u2713");
4069
- console.log(chalk8.gray(` ${icon} ${preview}${result.length > 100 ? "..." : ""}`));
4308
+ const icon = isError ? chalk9.red("\u2717") : chalk9.green("\u2713");
4309
+ console.log(chalk9.gray(` ${icon} ${preview}${result.length > 100 ? "..." : ""}`));
4070
4310
  }
4071
4311
  })
4072
4312
  );
@@ -4079,7 +4319,7 @@ Analyze the above input.`;
4079
4319
  model: activeModelId
4080
4320
  });
4081
4321
  costTracker.record(activeModelId, response.usage.promptTokens, response.usage.completionTokens);
4082
- console.log(usage.formatLast());
4322
+ console.log(usage.formatLast() + " " + costTracker.formatLastCost());
4083
4323
  }
4084
4324
  } catch (err) {
4085
4325
  spinner.fail(`Error: ${err.message}`);
@@ -4091,7 +4331,7 @@ Analyze the above input.`;
4091
4331
  const savedPlan = await loadPlan(config.projectRoot);
4092
4332
  if (savedPlan) {
4093
4333
  ralphPlan = savedPlan;
4094
- console.log(chalk8.gray(` Ralph plan loaded (${savedPlan.tasks.length} tasks)
4334
+ console.log(chalk9.gray(` Ralph plan loaded (${savedPlan.tasks.length} tasks)
4095
4335
  `));
4096
4336
  }
4097
4337
  } catch {
@@ -4103,7 +4343,7 @@ Analyze the above input.`;
4103
4343
  prompt: theme().prompt("notch> "),
4104
4344
  completer: (line) => completer(line)
4105
4345
  });
4106
- console.log(chalk8.gray(" Type your request, or /help for commands.\n"));
4346
+ console.log(chalk9.gray(" Type your request, or /help for commands.\n"));
4107
4347
  rl.prompt();
4108
4348
  rl.on("line", async (line) => {
4109
4349
  const input = line.trim();
@@ -4119,7 +4359,7 @@ Analyze the above input.`;
4119
4359
  if (messages.length > 0) {
4120
4360
  try {
4121
4361
  const id = await saveSession(config.projectRoot, messages, { model: activeModelId });
4122
- console.log(chalk8.gray(` Session saved: ${id}`));
4362
+ console.log(chalk9.gray(` Session saved: ${id}`));
4123
4363
  } catch {
4124
4364
  }
4125
4365
  }
@@ -4130,12 +4370,12 @@ Analyze the above input.`;
4130
4370
  }
4131
4371
  }
4132
4372
  await toolCtx.runHook?.("session-end", {});
4133
- console.log(chalk8.gray("\n Goodbye!\n"));
4373
+ console.log(chalk9.gray("\n Goodbye!\n"));
4134
4374
  process.exit(0);
4135
4375
  }
4136
4376
  if (input === "/clear") {
4137
4377
  messages.length = 0;
4138
- console.log(chalk8.gray(" Conversation cleared.\n"));
4378
+ console.log(chalk9.gray(" Conversation cleared.\n"));
4139
4379
  rl.prompt();
4140
4380
  return;
4141
4381
  }
@@ -4155,8 +4395,8 @@ Analyze the above input.`;
4155
4395
  newModel = `notch-${newModel}`;
4156
4396
  }
4157
4397
  if (!isValidModel(newModel)) {
4158
- console.log(chalk8.red(` Unknown model: ${newModel}`));
4159
- console.log(chalk8.gray(` Available: ${modelChoices}`));
4398
+ console.log(chalk9.red(` Unknown model: ${newModel}`));
4399
+ console.log(chalk9.gray(` Available: ${modelChoices}`));
4160
4400
  rl.prompt();
4161
4401
  return;
4162
4402
  }
@@ -4164,7 +4404,7 @@ Analyze the above input.`;
4164
4404
  config.models.chat.model = activeModelId;
4165
4405
  model = resolveModel(config.models.chat);
4166
4406
  const switchedInfo = MODEL_CATALOG[activeModelId];
4167
- console.log(chalk8.green(` Switched to ${switchedInfo.label} (${switchedInfo.id})
4407
+ console.log(chalk9.green(` Switched to ${switchedInfo.label} (${switchedInfo.id})
4168
4408
  `));
4169
4409
  rl.prompt();
4170
4410
  return;
@@ -4177,22 +4417,22 @@ Analyze the above input.`;
4177
4417
  statusSpinner.succeed(`${statusInfo.label} (${activeModelId}) is reachable`);
4178
4418
  } else {
4179
4419
  statusSpinner.fail(check.error ?? "API unreachable");
4180
- console.log(chalk8.gray(" Tip: Set NOTCH_API_KEY or use --api-key, and verify your Modal endpoint is running.\n"));
4420
+ console.log(chalk9.gray(" Tip: Set NOTCH_API_KEY or use --api-key, and verify your Modal endpoint is running.\n"));
4181
4421
  }
4182
4422
  rl.prompt();
4183
4423
  return;
4184
4424
  }
4185
4425
  if (input === "/undo") {
4186
4426
  if (checkpoints.undoCount === 0) {
4187
- console.log(chalk8.gray(" Nothing to undo.\n"));
4427
+ console.log(chalk9.gray(" Nothing to undo.\n"));
4188
4428
  rl.prompt();
4189
4429
  return;
4190
4430
  }
4191
4431
  const undone = await checkpoints.undo();
4192
4432
  if (undone) {
4193
4433
  const fileCount = undone.files.length;
4194
- console.log(chalk8.yellow(` Undid "${undone.description}" (${fileCount} file${fileCount !== 1 ? "s" : ""} restored)`));
4195
- console.log(chalk8.gray(` ${checkpoints.undoCount} checkpoint${checkpoints.undoCount !== 1 ? "s" : ""} remaining
4434
+ console.log(chalk9.yellow(` Undid "${undone.description}" (${fileCount} file${fileCount !== 1 ? "s" : ""} restored)`));
4435
+ console.log(chalk9.gray(` ${checkpoints.undoCount} checkpoint${checkpoints.undoCount !== 1 ? "s" : ""} remaining
4196
4436
  `));
4197
4437
  }
4198
4438
  rl.prompt();
@@ -4200,7 +4440,7 @@ Analyze the above input.`;
4200
4440
  }
4201
4441
  if (input === "/usage") {
4202
4442
  if (usage.turnCount === 0) {
4203
- console.log(chalk8.gray(" No usage yet.\n"));
4443
+ console.log(chalk9.gray(" No usage yet.\n"));
4204
4444
  } else {
4205
4445
  console.log(usage.formatSession());
4206
4446
  const currentTokens = estimateTokens(messages);
@@ -4213,7 +4453,7 @@ Analyze the above input.`;
4213
4453
  }
4214
4454
  if (input === "/cost") {
4215
4455
  if (costTracker.totalCost === 0) {
4216
- console.log(chalk8.gray(" No cost data yet.\n"));
4456
+ console.log(chalk9.gray(" No cost data yet.\n"));
4217
4457
  } else {
4218
4458
  console.log(costTracker.formatTotal());
4219
4459
  console.log(costTracker.formatByModel());
@@ -4228,7 +4468,7 @@ Analyze the above input.`;
4228
4468
  const compressed = await autoCompress2(messages, model, MODEL_CATALOG[activeModelId].contextWindow);
4229
4469
  messages.length = 0;
4230
4470
  messages.push(...compressed);
4231
- console.log(chalk8.green(` Compressed: ${before} messages \u2192 ${messages.length} messages`));
4471
+ console.log(chalk9.green(` Compressed: ${before} messages \u2192 ${messages.length} messages`));
4232
4472
  console.log("");
4233
4473
  rl.prompt();
4234
4474
  return;
@@ -4236,10 +4476,10 @@ Analyze the above input.`;
4236
4476
  if (input === "/diff") {
4237
4477
  const diffs = checkpoints.allDiffs();
4238
4478
  if (diffs.length === 0) {
4239
- console.log(chalk8.gray(" No file changes this session.\n"));
4479
+ console.log(chalk9.gray(" No file changes this session.\n"));
4240
4480
  } else {
4241
4481
  for (const df of diffs) {
4242
- console.log(chalk8.cyan(` ${df.path}:`));
4482
+ console.log(chalk9.cyan(` ${df.path}:`));
4243
4483
  console.log(unifiedDiff(df.before, df.after, df.path));
4244
4484
  console.log("");
4245
4485
  }
@@ -4255,10 +4495,10 @@ Analyze the above input.`;
4255
4495
  projectRoot: config.projectRoot,
4256
4496
  outputPath: exportPath
4257
4497
  });
4258
- console.log(chalk8.green(` Exported to ${ePath}
4498
+ console.log(chalk9.green(` Exported to ${ePath}
4259
4499
  `));
4260
4500
  } catch (err) {
4261
- console.log(chalk8.red(` Export failed: ${err.message}
4501
+ console.log(chalk9.red(` Export failed: ${err.message}
4262
4502
  `));
4263
4503
  }
4264
4504
  rl.prompt();
@@ -4268,10 +4508,10 @@ Analyze the above input.`;
4268
4508
  try {
4269
4509
  const id = await saveSession(config.projectRoot, messages, { model: activeModelId });
4270
4510
  sessionId = id;
4271
- console.log(chalk8.green(` Session saved: ${id}
4511
+ console.log(chalk9.green(` Session saved: ${id}
4272
4512
  `));
4273
4513
  } catch (err) {
4274
- console.log(chalk8.red(` Save failed: ${err.message}
4514
+ console.log(chalk9.red(` Save failed: ${err.message}
4275
4515
  `));
4276
4516
  }
4277
4517
  rl.prompt();
@@ -4281,16 +4521,16 @@ Analyze the above input.`;
4281
4521
  try {
4282
4522
  const sessions = await listSessions(config.projectRoot);
4283
4523
  if (sessions.length === 0) {
4284
- console.log(chalk8.gray(" No saved sessions.\n"));
4524
+ console.log(chalk9.gray(" No saved sessions.\n"));
4285
4525
  } else {
4286
- console.log(chalk8.gray("\n Saved sessions:\n"));
4526
+ console.log(chalk9.gray("\n Saved sessions:\n"));
4287
4527
  for (const s of sessions.slice(0, 10)) {
4288
- console.log(chalk8.gray(` ${s.id} ${s.turns} turns ${s.date} ${s.model}`));
4528
+ console.log(chalk9.gray(` ${s.id} ${s.turns} turns ${s.date} ${s.model}`));
4289
4529
  }
4290
4530
  console.log("");
4291
4531
  }
4292
4532
  } catch (err) {
4293
- console.log(chalk8.red(` Error listing sessions: ${err.message}
4533
+ console.log(chalk9.red(` Error listing sessions: ${err.message}
4294
4534
  `));
4295
4535
  }
4296
4536
  rl.prompt();
@@ -4308,18 +4548,18 @@ Analyze the above input.`;
4308
4548
  const savedPlan = await loadPlan(config.projectRoot);
4309
4549
  if (savedPlan) {
4310
4550
  ralphPlan = savedPlan;
4311
- console.log(chalk8.green(` Ralph plan restored (${savedPlan.tasks.length} tasks)
4551
+ console.log(chalk9.green(` Ralph plan restored (${savedPlan.tasks.length} tasks)
4312
4552
  `));
4313
4553
  }
4314
4554
  } catch {
4315
4555
  }
4316
- console.log(chalk8.green(` Resumed session ${session.meta.id} (${session.meta.turns} turns)
4556
+ console.log(chalk9.green(` Resumed session ${session.meta.id} (${session.meta.turns} turns)
4317
4557
  `));
4318
4558
  } else {
4319
- console.log(chalk8.gray(" No session found.\n"));
4559
+ console.log(chalk9.gray(" No session found.\n"));
4320
4560
  }
4321
4561
  } catch (err) {
4322
- console.log(chalk8.red(` Resume failed: ${err.message}
4562
+ console.log(chalk9.red(` Resume failed: ${err.message}
4323
4563
  `));
4324
4564
  }
4325
4565
  rl.prompt();
@@ -4328,7 +4568,7 @@ Analyze the above input.`;
4328
4568
  if (input.startsWith("/search ")) {
4329
4569
  const query = input.replace("/search ", "").trim().toLowerCase();
4330
4570
  if (!query) {
4331
- console.log(chalk8.gray(" Usage: /search <query>\n"));
4571
+ console.log(chalk9.gray(" Usage: /search <query>\n"));
4332
4572
  rl.prompt();
4333
4573
  return;
4334
4574
  }
@@ -4345,14 +4585,14 @@ Analyze the above input.`;
4345
4585
  }
4346
4586
  }
4347
4587
  if (matches.length === 0) {
4348
- console.log(chalk8.gray(` No matches for "${query}"
4588
+ console.log(chalk9.gray(` No matches for "${query}"
4349
4589
  `));
4350
4590
  } else {
4351
- console.log(chalk8.gray(`
4591
+ console.log(chalk9.gray(`
4352
4592
  ${matches.length} match(es) for "${query}":
4353
4593
  `));
4354
4594
  for (const m of matches.slice(0, 10)) {
4355
- console.log(chalk8.gray(` [${m.index}] ${m.role}: ${m.preview}`));
4595
+ console.log(chalk9.gray(` [${m.index}] ${m.role}: ${m.preview}`));
4356
4596
  }
4357
4597
  console.log("");
4358
4598
  }
@@ -4366,7 +4606,7 @@ Analyze the above input.`;
4366
4606
  activePlan = await generatePlan(task, model, systemPrompt);
4367
4607
  planSpinner.succeed("Plan generated");
4368
4608
  console.log(formatPlan(activePlan));
4369
- console.log(chalk8.gray(" Use /plan approve to execute, /plan edit to modify, or /plan cancel to discard.\n"));
4609
+ console.log(chalk9.gray(" Use /plan approve to execute, /plan edit to modify, or /plan cancel to discard.\n"));
4370
4610
  } catch (err) {
4371
4611
  planSpinner.fail(`Plan failed: ${err.message}`);
4372
4612
  }
@@ -4375,11 +4615,11 @@ Analyze the above input.`;
4375
4615
  }
4376
4616
  if (input === "/plan approve") {
4377
4617
  if (!activePlan) {
4378
- console.log(chalk8.gray(" No active plan. Use /plan <task> to create one.\n"));
4618
+ console.log(chalk9.gray(" No active plan. Use /plan <task> to create one.\n"));
4379
4619
  rl.prompt();
4380
4620
  return;
4381
4621
  }
4382
- console.log(chalk8.green(" Executing plan...\n"));
4622
+ console.log(chalk9.green(" Executing plan...\n"));
4383
4623
  while (!isPlanComplete(activePlan)) {
4384
4624
  const stepPrompt = currentStepPrompt(activePlan);
4385
4625
  messages.push({ role: "user", content: stepPrompt });
@@ -4397,10 +4637,10 @@ Analyze the above input.`;
4397
4637
  },
4398
4638
  onToolCall: (name) => {
4399
4639
  if (planStepSpinner.isSpinning) planStepSpinner.stop();
4400
- console.log(chalk8.gray(` \u2192 ${name}`));
4640
+ console.log(chalk9.gray(` \u2192 ${name}`));
4401
4641
  },
4402
4642
  onToolResult: (_name, _result, isError) => {
4403
- console.log(isError ? chalk8.red(" \u2717") : chalk8.green(" \u2713"));
4643
+ console.log(isError ? chalk9.red(" \u2717") : chalk9.green(" \u2713"));
4404
4644
  }
4405
4645
  });
4406
4646
  console.log("\n");
@@ -4413,7 +4653,7 @@ Analyze the above input.`;
4413
4653
  }
4414
4654
  }
4415
4655
  if (activePlan && isPlanComplete(activePlan)) {
4416
- console.log(chalk8.green(" Plan completed!\n"));
4656
+ console.log(chalk9.green(" Plan completed!\n"));
4417
4657
  }
4418
4658
  activePlan = null;
4419
4659
  rl.prompt();
@@ -4421,26 +4661,26 @@ Analyze the above input.`;
4421
4661
  }
4422
4662
  if (input === "/plan edit") {
4423
4663
  if (!activePlan) {
4424
- console.log(chalk8.gray(" No active plan to edit.\n"));
4664
+ console.log(chalk9.gray(" No active plan to edit.\n"));
4425
4665
  rl.prompt();
4426
4666
  return;
4427
4667
  }
4428
4668
  console.log(formatPlan(activePlan));
4429
- console.log(chalk8.gray(" Type a modified plan description and use /plan <new task> to regenerate,"));
4430
- console.log(chalk8.gray(" or /plan approve to proceed with the current plan.\n"));
4669
+ console.log(chalk9.gray(" Type a modified plan description and use /plan <new task> to regenerate,"));
4670
+ console.log(chalk9.gray(" or /plan approve to proceed with the current plan.\n"));
4431
4671
  rl.prompt();
4432
4672
  return;
4433
4673
  }
4434
4674
  if (input === "/plan cancel") {
4435
4675
  activePlan = null;
4436
- console.log(chalk8.gray(" Plan discarded.\n"));
4676
+ console.log(chalk9.gray(" Plan discarded.\n"));
4437
4677
  rl.prompt();
4438
4678
  return;
4439
4679
  }
4440
4680
  if (input.startsWith("/agent ")) {
4441
4681
  const task = input.replace("/agent ", "").trim();
4442
4682
  const agentId = nextSubagentId();
4443
- console.log(chalk8.cyan(` Spawning subagent #${agentId}: ${task}
4683
+ console.log(chalk9.cyan(` Spawning subagent #${agentId}: ${task}
4444
4684
  `));
4445
4685
  spawnSubagent({
4446
4686
  id: agentId,
@@ -4450,14 +4690,14 @@ Analyze the above input.`;
4450
4690
  toolContext: toolCtx,
4451
4691
  contextWindow: MODEL_CATALOG[activeModelId].contextWindow,
4452
4692
  onComplete: (result) => {
4453
- console.log(chalk8.green(`
4693
+ console.log(chalk9.green(`
4454
4694
  Subagent #${agentId} finished:`));
4455
- console.log(chalk8.gray(` ${result.slice(0, 200)}
4695
+ console.log(chalk9.gray(` ${result.slice(0, 200)}
4456
4696
  `));
4457
4697
  rl.prompt();
4458
4698
  },
4459
4699
  onError: (err) => {
4460
- console.log(chalk8.red(`
4700
+ console.log(chalk9.red(`
4461
4701
  Subagent #${agentId} failed: ${err}
4462
4702
  `));
4463
4703
  rl.prompt();
@@ -4470,18 +4710,18 @@ Analyze the above input.`;
4470
4710
  try {
4471
4711
  const memories = await loadMemories(config.projectRoot);
4472
4712
  if (memories.length === 0) {
4473
- console.log(chalk8.gray(" No saved memories.\n"));
4713
+ console.log(chalk9.gray(" No saved memories.\n"));
4474
4714
  } else {
4475
- console.log(chalk8.gray(`
4715
+ console.log(chalk9.gray(`
4476
4716
  ${memories.length} saved memories:
4477
4717
  `));
4478
4718
  for (const m of memories) {
4479
- console.log(chalk8.gray(` [${m.type}] ${m.content.slice(0, 80)}`));
4719
+ console.log(chalk9.gray(` [${m.type}] ${m.content.slice(0, 80)}`));
4480
4720
  }
4481
4721
  console.log("");
4482
4722
  }
4483
4723
  } catch (err) {
4484
- console.log(chalk8.red(` Error: ${err.message}
4724
+ console.log(chalk9.red(` Error: ${err.message}
4485
4725
  `));
4486
4726
  }
4487
4727
  rl.prompt();
@@ -4492,16 +4732,16 @@ Analyze the above input.`;
4492
4732
  try {
4493
4733
  const results = await searchMemories(config.projectRoot, query);
4494
4734
  if (results.length === 0) {
4495
- console.log(chalk8.gray(` No memories matching "${query}"
4735
+ console.log(chalk9.gray(` No memories matching "${query}"
4496
4736
  `));
4497
4737
  } else {
4498
4738
  for (const m of results) {
4499
- console.log(chalk8.gray(` [${m.type}] ${m.content.slice(0, 100)}`));
4739
+ console.log(chalk9.gray(` [${m.type}] ${m.content.slice(0, 100)}`));
4500
4740
  }
4501
4741
  console.log("");
4502
4742
  }
4503
4743
  } catch (err) {
4504
- console.log(chalk8.red(` Error: ${err.message}
4744
+ console.log(chalk9.red(` Error: ${err.message}
4505
4745
  `));
4506
4746
  }
4507
4747
  rl.prompt();
@@ -4513,10 +4753,10 @@ Analyze the above input.`;
4513
4753
  for (const m of memories) {
4514
4754
  await deleteMemory(config.projectRoot, m.id);
4515
4755
  }
4516
- console.log(chalk8.yellow(` Cleared ${memories.length} memories.
4756
+ console.log(chalk9.yellow(` Cleared ${memories.length} memories.
4517
4757
  `));
4518
4758
  } catch (err) {
4519
- console.log(chalk8.red(` Error: ${err.message}
4759
+ console.log(chalk9.red(` Error: ${err.message}
4520
4760
  `));
4521
4761
  }
4522
4762
  rl.prompt();
@@ -4544,27 +4784,27 @@ Analyze the above input.`;
4544
4784
  }
4545
4785
  if (input === "/ralph run") {
4546
4786
  if (!ralphPlan) {
4547
- console.log(chalk8.gray(" No Ralph plan. Use /ralph plan <goal> first.\n"));
4787
+ console.log(chalk9.gray(" No Ralph plan. Use /ralph plan <goal> first.\n"));
4548
4788
  rl.prompt();
4549
4789
  return;
4550
4790
  }
4551
- console.log(chalk8.green(" Ralph is running...\n"));
4791
+ console.log(chalk9.green(" Ralph is running...\n"));
4552
4792
  try {
4553
4793
  ralphPlan = await runRalphLoop(ralphPlan, {
4554
4794
  model,
4555
4795
  systemPrompt,
4556
4796
  toolContext: toolCtx,
4557
4797
  contextWindow: MODEL_CATALOG[activeModelId].contextWindow,
4558
- onTaskStart: (task) => console.log(chalk8.cyan(` \u25B6 Task: ${task.description}`)),
4559
- onTaskComplete: (task) => console.log(chalk8.green(` \u2713 Done: ${task.description}
4798
+ onTaskStart: (task) => console.log(chalk9.cyan(` \u25B6 Task: ${task.description}`)),
4799
+ onTaskComplete: (task) => console.log(chalk9.green(` \u2713 Done: ${task.description}
4560
4800
  `)),
4561
- onTaskFail: (task, err) => console.log(chalk8.red(` \u2717 Failed: ${task.description} (${err})
4801
+ onTaskFail: (task, err) => console.log(chalk9.red(` \u2717 Failed: ${task.description} (${err})
4562
4802
  `))
4563
4803
  });
4564
4804
  await savePlan(config.projectRoot, ralphPlan);
4565
4805
  console.log(formatRalphStatus(ralphPlan));
4566
4806
  } catch (err) {
4567
- console.log(chalk8.red(` Ralph error: ${err.message}
4807
+ console.log(chalk9.red(` Ralph error: ${err.message}
4568
4808
  `));
4569
4809
  }
4570
4810
  rl.prompt();
@@ -4572,7 +4812,7 @@ Analyze the above input.`;
4572
4812
  }
4573
4813
  if (input === "/ralph status") {
4574
4814
  if (!ralphPlan) {
4575
- console.log(chalk8.gray(" No Ralph plan active.\n"));
4815
+ console.log(chalk9.gray(" No Ralph plan active.\n"));
4576
4816
  } else {
4577
4817
  console.log(formatRalphStatus(ralphPlan));
4578
4818
  }
@@ -4583,26 +4823,26 @@ Analyze the above input.`;
4583
4823
  ralphPlan = null;
4584
4824
  await deletePlan(config.projectRoot).catch(() => {
4585
4825
  });
4586
- console.log(chalk8.gray(" Ralph plan cleared.\n"));
4826
+ console.log(chalk9.gray(" Ralph plan cleared.\n"));
4587
4827
  rl.prompt();
4588
4828
  return;
4589
4829
  }
4590
4830
  if (input === "/branch") {
4591
4831
  const branchId = `branch-${branches.size + 1}`;
4592
4832
  branches.set(branchId, [...messages]);
4593
- console.log(chalk8.green(` Forked conversation as "${branchId}" (${messages.length} messages)
4833
+ console.log(chalk9.green(` Forked conversation as "${branchId}" (${messages.length} messages)
4594
4834
  `));
4595
4835
  rl.prompt();
4596
4836
  return;
4597
4837
  }
4598
4838
  if (input === "/branches") {
4599
4839
  if (branches.size === 0) {
4600
- console.log(chalk8.gray(" No conversation branches. Use /branch to fork.\n"));
4840
+ console.log(chalk9.gray(" No conversation branches. Use /branch to fork.\n"));
4601
4841
  } else {
4602
- console.log(chalk8.gray("\n Branches:\n"));
4842
+ console.log(chalk9.gray("\n Branches:\n"));
4603
4843
  for (const [name, msgs] of branches) {
4604
- const marker = name === currentBranch ? chalk8.green(" \u25CF") : " ";
4605
- console.log(chalk8.gray(` ${marker} ${name} (${msgs.length} messages)`));
4844
+ const marker = name === currentBranch ? chalk9.green(" \u25CF") : " ";
4845
+ console.log(chalk9.gray(` ${marker} ${name} (${msgs.length} messages)`));
4606
4846
  }
4607
4847
  console.log("");
4608
4848
  }
@@ -4639,8 +4879,8 @@ Analyze the above input.`;
4639
4879
  return;
4640
4880
  }
4641
4881
  if (input.startsWith("/")) {
4642
- console.log(chalk8.red(` Unknown command: ${input}`));
4643
- console.log(chalk8.gray(" Type /help for available commands.\n"));
4882
+ console.log(chalk9.red(` Unknown command: ${input}`));
4883
+ console.log(chalk9.gray(" Type /help for available commands.\n"));
4644
4884
  rl.prompt();
4645
4885
  return;
4646
4886
  }
@@ -4648,7 +4888,7 @@ Analyze the above input.`;
4648
4888
  const refContext = formatReferences(references);
4649
4889
  const finalPrompt = refContext + cleanInput;
4650
4890
  if (references.length > 0) {
4651
- console.log(chalk8.gray(` Injected ${references.length} reference(s)`));
4891
+ console.log(chalk9.gray(` Injected ${references.length} reference(s)`));
4652
4892
  }
4653
4893
  messages.push({ role: "user", content: finalPrompt });
4654
4894
  const spinner = ora("Thinking...").start();
@@ -4674,16 +4914,16 @@ Analyze the above input.`;
4674
4914
  const val = String(v);
4675
4915
  return `${k}=${val.length > 60 ? val.slice(0, 60) + "..." : val}`;
4676
4916
  }).join(", ");
4677
- console.log(chalk8.gray(`
4917
+ console.log(chalk9.gray(`
4678
4918
  \u2192 ${name}(${argSummary})`));
4679
4919
  },
4680
4920
  onToolResult: (_name, result, isError) => {
4681
4921
  const preview = result.slice(0, 100).replace(/\n/g, " ");
4682
- const icon = isError ? chalk8.red("\u2717") : chalk8.green("\u2713");
4683
- console.log(chalk8.gray(` ${icon} ${preview}${result.length > 100 ? "..." : ""}`));
4922
+ const icon = isError ? chalk9.red("\u2717") : chalk9.green("\u2713");
4923
+ console.log(chalk9.gray(` ${icon} ${preview}${result.length > 100 ? "..." : ""}`));
4684
4924
  },
4685
4925
  onCompress: () => {
4686
- console.log(chalk8.yellow("\n [Context compressed to fit window]\n"));
4926
+ console.log(chalk9.yellow("\n [Context compressed to fit window]\n"));
4687
4927
  }
4688
4928
  })
4689
4929
  );
@@ -4697,7 +4937,7 @@ Analyze the above input.`;
4697
4937
  model: activeModelId
4698
4938
  });
4699
4939
  costTracker.record(activeModelId, response.usage.promptTokens, response.usage.completionTokens);
4700
- console.log(usage.formatLast());
4940
+ console.log(usage.formatLast() + " " + costTracker.formatLastCost());
4701
4941
  const currentTokens = estimateTokens(messages);
4702
4942
  const ctxWindow = MODEL_CATALOG[activeModelId].contextWindow;
4703
4943
  if (currentTokens > ctxWindow * 0.5) {
@@ -4716,7 +4956,7 @@ Analyze the above input.`;
4716
4956
  type: "auto",
4717
4957
  content: memMatch[1]
4718
4958
  });
4719
- console.log(chalk8.gray(" (Saved to memory)\n"));
4959
+ console.log(chalk9.gray(" (Saved to memory)\n"));
4720
4960
  } catch {
4721
4961
  }
4722
4962
  }
@@ -4726,13 +4966,13 @@ Analyze the above input.`;
4726
4966
  checkpoints.discard();
4727
4967
  const msg = err.message?.toLowerCase() ?? "";
4728
4968
  if (msg.includes("fetch") || msg.includes("econnrefused") || msg.includes("network")) {
4729
- console.log(chalk8.gray(" Tip: Check that your Notch endpoint is running. Use /status to verify.\n"));
4969
+ console.log(chalk9.gray(" Tip: Check that your Notch endpoint is running. Use /status to verify.\n"));
4730
4970
  } else if (msg.includes("401") || msg.includes("unauthorized") || msg.includes("api key")) {
4731
- console.log(chalk8.gray(" Tip: Set NOTCH_API_KEY env var or use --api-key flag.\n"));
4971
+ console.log(chalk9.gray(" Tip: Set NOTCH_API_KEY env var or use --api-key flag.\n"));
4732
4972
  } else if (msg.includes("429") || msg.includes("rate limit")) {
4733
- console.log(chalk8.gray(" Tip: Rate limited. Wait a moment and try again.\n"));
4973
+ console.log(chalk9.gray(" Tip: Rate limited. Wait a moment and try again.\n"));
4734
4974
  } else {
4735
- console.log(chalk8.gray(" (The conversation history is preserved. Try again.)\n"));
4975
+ console.log(chalk9.gray(" (The conversation history is preserved. Try again.)\n"));
4736
4976
  }
4737
4977
  }
4738
4978
  rl.prompt();
@@ -4750,13 +4990,13 @@ async function handleRalphSubcommand(args, cliOpts) {
4750
4990
  cwd: config.projectRoot,
4751
4991
  requireConfirm: false,
4752
4992
  confirm: async () => true,
4753
- log: (msg) => console.log(chalk8.gray(` ${msg}`))
4993
+ log: (msg) => console.log(chalk9.gray(` ${msg}`))
4754
4994
  };
4755
4995
  const subcommand = args[0];
4756
4996
  if (subcommand === "plan") {
4757
4997
  const goal = args.slice(1).join(" ");
4758
4998
  if (!goal) {
4759
- console.error(chalk8.red(" Usage: notch ralph plan <goal>"));
4999
+ console.error(chalk9.red(" Usage: notch ralph plan <goal>"));
4760
5000
  process.exit(1);
4761
5001
  }
4762
5002
  const spinner = ora("Ralph is planning...").start();
@@ -4767,7 +5007,7 @@ async function handleRalphSubcommand(args, cliOpts) {
4767
5007
  } else if (subcommand === "run") {
4768
5008
  let plan = await loadPlan(config.projectRoot);
4769
5009
  if (!plan) {
4770
- console.error(chalk8.red(" No plan found. Run: notch ralph plan <goal>"));
5010
+ console.error(chalk9.red(" No plan found. Run: notch ralph plan <goal>"));
4771
5011
  process.exit(1);
4772
5012
  }
4773
5013
  plan = await runRalphLoop(plan, {
@@ -4775,27 +5015,27 @@ async function handleRalphSubcommand(args, cliOpts) {
4775
5015
  systemPrompt,
4776
5016
  toolContext: toolCtx,
4777
5017
  contextWindow: MODEL_CATALOG[config.models.chat.model].contextWindow,
4778
- onTaskStart: (t) => console.log(chalk8.cyan(` \u25B6 ${t.description}`)),
4779
- onTaskComplete: (t) => console.log(chalk8.green(` \u2713 ${t.description}`)),
4780
- onTaskFail: (t, e) => console.log(chalk8.red(` \u2717 ${t.description}: ${e}`))
5018
+ onTaskStart: (t) => console.log(chalk9.cyan(` \u25B6 ${t.description}`)),
5019
+ onTaskComplete: (t) => console.log(chalk9.green(` \u2713 ${t.description}`)),
5020
+ onTaskFail: (t, e) => console.log(chalk9.red(` \u2717 ${t.description}: ${e}`))
4781
5021
  });
4782
5022
  await savePlan(config.projectRoot, plan);
4783
5023
  console.log(formatRalphStatus(plan));
4784
5024
  } else if (subcommand === "status") {
4785
5025
  const plan = await loadPlan(config.projectRoot);
4786
5026
  if (!plan) {
4787
- console.log(chalk8.gray(" No Ralph plan found."));
5027
+ console.log(chalk9.gray(" No Ralph plan found."));
4788
5028
  } else {
4789
5029
  console.log(formatRalphStatus(plan));
4790
5030
  }
4791
5031
  } else {
4792
- console.error(chalk8.red(` Unknown: notch ralph ${subcommand}`));
4793
- console.error(chalk8.gray(" Usage: notch ralph <plan|run|status>"));
5032
+ console.error(chalk9.red(` Unknown: notch ralph ${subcommand}`));
5033
+ console.error(chalk9.gray(" Usage: notch ralph <plan|run|status>"));
4794
5034
  process.exit(1);
4795
5035
  }
4796
5036
  }
4797
5037
  main().catch((err) => {
4798
- console.error(chalk8.red(`
5038
+ console.error(chalk9.red(`
4799
5039
  Fatal: ${err.message}
4800
5040
  `));
4801
5041
  process.exit(1);