@freesyntax/notch-cli 0.4.1 → 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-
|
|
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
|
|
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";
|
|
@@ -22,6 +22,12 @@ import path from "path";
|
|
|
22
22
|
|
|
23
23
|
// src/providers/registry.ts
|
|
24
24
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
25
|
+
var MissingApiKeyError = class extends Error {
|
|
26
|
+
constructor() {
|
|
27
|
+
super("NOTCH_API_KEY is not set");
|
|
28
|
+
this.name = "MissingApiKeyError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
25
31
|
var MODEL_CATALOG = {
|
|
26
32
|
"notch-cinder": {
|
|
27
33
|
id: "notch-cinder",
|
|
@@ -74,9 +80,7 @@ function resolveModel(config) {
|
|
|
74
80
|
const baseUrl = config.baseUrl ?? process.env.NOTCH_BASE_URL ?? info.baseUrl;
|
|
75
81
|
const apiKey = config.apiKey ?? process.env.NOTCH_API_KEY;
|
|
76
82
|
if (!apiKey) {
|
|
77
|
-
throw new
|
|
78
|
-
"NOTCH_API_KEY is not set. Set it via the NOTCH_API_KEY environment variable or --api-key flag."
|
|
79
|
-
);
|
|
83
|
+
throw new MissingApiKeyError();
|
|
80
84
|
}
|
|
81
85
|
const provider = createOpenAI({
|
|
82
86
|
apiKey,
|
|
@@ -826,20 +830,373 @@ ${text}` };
|
|
|
826
830
|
}
|
|
827
831
|
};
|
|
828
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
|
+
|
|
829
1181
|
// src/tools/index.ts
|
|
830
|
-
var
|
|
1182
|
+
var BUILTIN_TOOLS = [
|
|
831
1183
|
readTool,
|
|
832
1184
|
writeTool,
|
|
833
1185
|
editTool,
|
|
834
1186
|
shellTool,
|
|
835
1187
|
gitTool,
|
|
1188
|
+
githubTool,
|
|
836
1189
|
grepTool,
|
|
837
1190
|
globTool,
|
|
838
1191
|
webFetchTool
|
|
839
1192
|
];
|
|
1193
|
+
var mcpTools = [];
|
|
1194
|
+
function getAllTools() {
|
|
1195
|
+
return [...BUILTIN_TOOLS, ...mcpTools];
|
|
1196
|
+
}
|
|
840
1197
|
function buildToolMap(ctx) {
|
|
841
1198
|
const map = {};
|
|
842
|
-
for (const t of
|
|
1199
|
+
for (const t of getAllTools()) {
|
|
843
1200
|
map[t.name] = tool({
|
|
844
1201
|
description: t.description,
|
|
845
1202
|
parameters: t.parameters,
|
|
@@ -884,7 +1241,7 @@ ${paramSummary}`
|
|
|
884
1241
|
return map;
|
|
885
1242
|
}
|
|
886
1243
|
function describeTools() {
|
|
887
|
-
return
|
|
1244
|
+
return getAllTools().map(
|
|
888
1245
|
(t) => `- **${t.name}**: ${t.description}`
|
|
889
1246
|
).join("\n");
|
|
890
1247
|
}
|
|
@@ -2307,36 +2664,49 @@ async function resolveGlob(pattern, cwd) {
|
|
|
2307
2664
|
}
|
|
2308
2665
|
}
|
|
2309
2666
|
|
|
2667
|
+
// src/ui/banner.ts
|
|
2668
|
+
import chalk6 from "chalk";
|
|
2669
|
+
|
|
2310
2670
|
// src/ui/themes.ts
|
|
2311
2671
|
import chalk5 from "chalk";
|
|
2312
2672
|
var defaultTheme = {
|
|
2313
2673
|
name: "Default",
|
|
2314
|
-
description: "
|
|
2315
|
-
brand: chalk5.
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
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
|
|
2324
2693
|
success: chalk5.green,
|
|
2325
2694
|
warning: chalk5.yellow,
|
|
2326
2695
|
error: chalk5.red,
|
|
2327
|
-
info: chalk5.
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2696
|
+
info: chalk5.hex("#BBBBBB"),
|
|
2697
|
+
// light gray info
|
|
2698
|
+
toolName: chalk5.hex("#AAAAAA"),
|
|
2699
|
+
toolArgs: chalk5.hex("#777777"),
|
|
2700
|
+
toolResult: chalk5.hex("#555555"),
|
|
2331
2701
|
diffAdd: chalk5.green,
|
|
2332
2702
|
diffRemove: chalk5.red,
|
|
2333
|
-
diffHeader: chalk5.
|
|
2334
|
-
mdH1: chalk5.
|
|
2335
|
-
mdH2: chalk5.
|
|
2336
|
-
mdH3: chalk5.
|
|
2337
|
-
mdCode: chalk5.
|
|
2338
|
-
mdInlineCode: chalk5.
|
|
2339
|
-
mdLink: chalk5.
|
|
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,
|
|
2340
2710
|
meterLow: chalk5.green,
|
|
2341
2711
|
meterMid: chalk5.yellow,
|
|
2342
2712
|
meterHigh: chalk5.red
|
|
@@ -2786,59 +3156,104 @@ function formatThemeList(activeId) {
|
|
|
2786
3156
|
}
|
|
2787
3157
|
|
|
2788
3158
|
// src/ui/banner.ts
|
|
2789
|
-
var
|
|
2790
|
-
" \
|
|
2791
|
-
|
|
2792
|
-
"
|
|
2793
|
-
|
|
2794
|
-
" \
|
|
2795
|
-
|
|
2796
|
-
"
|
|
2797
|
-
// forelegs out
|
|
2798
|
-
" \u2580\u2588\u2584\u2588\u2588\u2584\u2588\u2580",
|
|
2799
|
-
// forelegs folding (prayer)
|
|
2800
|
-
" \u2590\u2588\u2588\u258C",
|
|
2801
|
-
// thorax
|
|
2802
|
-
" \u2590\u2588\u2588\u258C",
|
|
2803
|
-
// abdomen
|
|
2804
|
-
" \u2590\u2588\u2588\u258C",
|
|
2805
|
-
// abdomen
|
|
2806
|
-
" \u2584\u2580 \u2580\u2584",
|
|
2807
|
-
// legs splay
|
|
2808
|
-
" \u2580 \u2580"
|
|
2809
|
-
// feet
|
|
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"
|
|
2810
3167
|
];
|
|
2811
|
-
var
|
|
2812
|
-
"
|
|
2813
|
-
|
|
2814
|
-
"
|
|
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
|
+
}
|
|
3192
|
+
var MANTIS = [
|
|
3193
|
+
" \u2571\u25C9\u25C9\u2572",
|
|
3194
|
+
// ╱◉◉╲ antennae + eyes
|
|
3195
|
+
" \u2588\u2588\u2588",
|
|
3196
|
+
// ███ head
|
|
3197
|
+
" \u2590\u2588\u258C",
|
|
3198
|
+
// ▐█▌ neck
|
|
3199
|
+
" \u2584\u2580\u2590\u2588\u258C\u2580\u2584",
|
|
3200
|
+
// ▄▀▐█▌▀▄ prayer arms
|
|
3201
|
+
" \u2590\u2588\u258C",
|
|
3202
|
+
// ▐█▌ abdomen
|
|
3203
|
+
" \u2580\u2580 \u2580\u2580"
|
|
3204
|
+
// ▀▀ ▀▀ feet
|
|
2815
3205
|
];
|
|
2816
3206
|
function colorMantis(line) {
|
|
2817
3207
|
const t = theme();
|
|
2818
|
-
return line.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"));
|
|
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"));
|
|
2819
3209
|
}
|
|
2820
3210
|
function printBanner(version, modelLabel, modelId, modelSize, project) {
|
|
2821
3211
|
const t = theme();
|
|
2822
3212
|
const termWidth = process.stdout.columns || 80;
|
|
2823
3213
|
console.log("");
|
|
2824
|
-
for (
|
|
2825
|
-
console.log(" " +
|
|
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
|
-
|
|
2829
|
-
|
|
2830
|
-
}
|
|
2831
|
-
console.log("");
|
|
2832
|
-
const divWidth = Math.min(50, termWidth - 4);
|
|
3218
|
+
const mantisStr = MANTIS.map((l) => colorMantis(l));
|
|
3219
|
+
const divWidth = Math.min(54, termWidth - 4);
|
|
2833
3220
|
const divider = t.border(" " + "\u2500".repeat(divWidth));
|
|
2834
3221
|
console.log(divider);
|
|
2835
|
-
|
|
2836
|
-
t.dim(" ") + t.bold(modelLabel) + t.dim(" \u2502 v") + t.text(version)
|
|
2837
|
-
)
|
|
2838
|
-
|
|
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
|
+
}
|
|
2839
3240
|
console.log(divider);
|
|
2840
3241
|
console.log("");
|
|
2841
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
|
+
}
|
|
2842
3257
|
function printMantis() {
|
|
2843
3258
|
const t = theme();
|
|
2844
3259
|
console.log("");
|
|
@@ -2881,7 +3296,7 @@ function formatTokens(n) {
|
|
|
2881
3296
|
import fs12 from "fs/promises";
|
|
2882
3297
|
import path13 from "path";
|
|
2883
3298
|
import os3 from "os";
|
|
2884
|
-
import
|
|
3299
|
+
import chalk7 from "chalk";
|
|
2885
3300
|
var CACHE_FILE = path13.join(os3.homedir(), ".notch", "update-check.json");
|
|
2886
3301
|
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
2887
3302
|
var PACKAGE_NAME = "notch-cli";
|
|
@@ -2925,7 +3340,7 @@ function isNewer(latest, current) {
|
|
|
2925
3340
|
return false;
|
|
2926
3341
|
}
|
|
2927
3342
|
function formatUpdateMessage(current, latest) {
|
|
2928
|
-
return
|
|
3343
|
+
return chalk7.yellow(` Update available: ${current} -> ${latest}. Run: npm update -g ${PACKAGE_NAME}
|
|
2929
3344
|
`);
|
|
2930
3345
|
}
|
|
2931
3346
|
async function loadCache() {
|
|
@@ -3279,7 +3694,7 @@ async function exportSession(messages, outputPath, meta) {
|
|
|
3279
3694
|
// src/init.ts
|
|
3280
3695
|
import fs16 from "fs/promises";
|
|
3281
3696
|
import path17 from "path";
|
|
3282
|
-
import
|
|
3697
|
+
import chalk8 from "chalk";
|
|
3283
3698
|
var DEFAULT_CONFIG = {
|
|
3284
3699
|
model: "notch-forge",
|
|
3285
3700
|
temperature: 0.3,
|
|
@@ -3326,15 +3741,15 @@ async function initProject(projectRoot) {
|
|
|
3326
3741
|
}
|
|
3327
3742
|
if (!configExists) {
|
|
3328
3743
|
await fs16.writeFile(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
|
|
3329
|
-
console.log(
|
|
3744
|
+
console.log(chalk8.green(` Created ${configPath}`));
|
|
3330
3745
|
} else {
|
|
3331
|
-
console.log(
|
|
3746
|
+
console.log(chalk8.gray(` Skipped ${configPath} (already exists)`));
|
|
3332
3747
|
}
|
|
3333
3748
|
if (!instructionsExist) {
|
|
3334
3749
|
await fs16.writeFile(instructionsPath, DEFAULT_INSTRUCTIONS, "utf-8");
|
|
3335
|
-
console.log(
|
|
3750
|
+
console.log(chalk8.green(` Created ${instructionsPath}`));
|
|
3336
3751
|
} else {
|
|
3337
|
-
console.log(
|
|
3752
|
+
console.log(chalk8.gray(` Skipped ${instructionsPath} (already exists)`));
|
|
3338
3753
|
}
|
|
3339
3754
|
const gitignorePath = path17.join(projectRoot, ".gitignore");
|
|
3340
3755
|
try {
|
|
@@ -3344,13 +3759,13 @@ async function initProject(projectRoot) {
|
|
|
3344
3759
|
if (additions.length > 0) {
|
|
3345
3760
|
const append = "\n# Notch CLI\n" + additions.join("\n") + "\n";
|
|
3346
3761
|
await fs16.appendFile(gitignorePath, append, "utf-8");
|
|
3347
|
-
console.log(
|
|
3762
|
+
console.log(chalk8.green(` Updated .gitignore`));
|
|
3348
3763
|
}
|
|
3349
3764
|
} catch {
|
|
3350
3765
|
}
|
|
3351
3766
|
console.log("");
|
|
3352
|
-
console.log(
|
|
3353
|
-
console.log(
|
|
3767
|
+
console.log(chalk8.cyan(" Notch initialized! Edit .notch.md to customize behavior."));
|
|
3768
|
+
console.log(chalk8.gray(' Run "notch" to start.\n'));
|
|
3354
3769
|
}
|
|
3355
3770
|
|
|
3356
3771
|
// src/tools/diff-preview.ts
|
|
@@ -3469,183 +3884,6 @@ function findSync(oldLines, newLines, oi, ni, lookAhead) {
|
|
|
3469
3884
|
return null;
|
|
3470
3885
|
}
|
|
3471
3886
|
|
|
3472
|
-
// src/mcp/client.ts
|
|
3473
|
-
import { spawn } from "child_process";
|
|
3474
|
-
import { z as z9 } from "zod";
|
|
3475
|
-
var MCPClient = class {
|
|
3476
|
-
constructor(config, serverName) {
|
|
3477
|
-
this.config = config;
|
|
3478
|
-
this.serverName = serverName;
|
|
3479
|
-
}
|
|
3480
|
-
process = null;
|
|
3481
|
-
requestId = 0;
|
|
3482
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
3483
|
-
buffer = "";
|
|
3484
|
-
serverName;
|
|
3485
|
-
_tools = [];
|
|
3486
|
-
/**
|
|
3487
|
-
* Start the MCP server and initialize the connection.
|
|
3488
|
-
*/
|
|
3489
|
-
async connect() {
|
|
3490
|
-
this.process = spawn(this.config.command, this.config.args ?? [], {
|
|
3491
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
3492
|
-
env: { ...process.env, ...this.config.env },
|
|
3493
|
-
cwd: this.config.cwd
|
|
3494
|
-
});
|
|
3495
|
-
this.process.stdout?.setEncoding("utf-8");
|
|
3496
|
-
this.process.stdout?.on("data", (data) => {
|
|
3497
|
-
this.buffer += data;
|
|
3498
|
-
this.processBuffer();
|
|
3499
|
-
});
|
|
3500
|
-
this.process.on("error", (err) => {
|
|
3501
|
-
for (const [id, pending] of this.pendingRequests) {
|
|
3502
|
-
pending.reject(new Error(`MCP server ${this.serverName} error: ${err.message}`));
|
|
3503
|
-
this.pendingRequests.delete(id);
|
|
3504
|
-
}
|
|
3505
|
-
});
|
|
3506
|
-
this.process.on("exit", (code) => {
|
|
3507
|
-
for (const [id, pending] of this.pendingRequests) {
|
|
3508
|
-
pending.reject(new Error(`MCP server ${this.serverName} exited with code ${code}`));
|
|
3509
|
-
this.pendingRequests.delete(id);
|
|
3510
|
-
}
|
|
3511
|
-
});
|
|
3512
|
-
await this.sendRequest("initialize", {
|
|
3513
|
-
protocolVersion: "2024-11-05",
|
|
3514
|
-
capabilities: {},
|
|
3515
|
-
clientInfo: { name: "notch-cli", version: "0.3.0" }
|
|
3516
|
-
});
|
|
3517
|
-
this.sendNotification("notifications/initialized", {});
|
|
3518
|
-
const result = await this.sendRequest("tools/list", {});
|
|
3519
|
-
this._tools = result.tools ?? [];
|
|
3520
|
-
}
|
|
3521
|
-
/**
|
|
3522
|
-
* Get discovered tools from this server.
|
|
3523
|
-
*/
|
|
3524
|
-
get tools() {
|
|
3525
|
-
return this._tools;
|
|
3526
|
-
}
|
|
3527
|
-
/**
|
|
3528
|
-
* Check if the MCP server process is still alive.
|
|
3529
|
-
*/
|
|
3530
|
-
get isAlive() {
|
|
3531
|
-
return this.process !== null && this.process.exitCode === null && !this.process.killed;
|
|
3532
|
-
}
|
|
3533
|
-
/**
|
|
3534
|
-
* Call a tool on the MCP server. Auto-reconnects if the server has crashed.
|
|
3535
|
-
*/
|
|
3536
|
-
async callTool(name, args) {
|
|
3537
|
-
if (!this.isAlive) {
|
|
3538
|
-
try {
|
|
3539
|
-
await this.connect();
|
|
3540
|
-
} catch (err) {
|
|
3541
|
-
throw new Error(`MCP server ${this.serverName} is down and could not reconnect: ${err.message}`);
|
|
3542
|
-
}
|
|
3543
|
-
}
|
|
3544
|
-
const result = await this.sendRequest("tools/call", { name, arguments: args });
|
|
3545
|
-
return result;
|
|
3546
|
-
}
|
|
3547
|
-
/**
|
|
3548
|
-
* Disconnect from the MCP server.
|
|
3549
|
-
*/
|
|
3550
|
-
disconnect() {
|
|
3551
|
-
if (this.process) {
|
|
3552
|
-
this.process.stdin?.end();
|
|
3553
|
-
this.process.kill();
|
|
3554
|
-
this.process = null;
|
|
3555
|
-
}
|
|
3556
|
-
this.pendingRequests.clear();
|
|
3557
|
-
}
|
|
3558
|
-
sendRequest(method, params) {
|
|
3559
|
-
return new Promise((resolve2, reject) => {
|
|
3560
|
-
const id = ++this.requestId;
|
|
3561
|
-
const msg = {
|
|
3562
|
-
jsonrpc: "2.0",
|
|
3563
|
-
id,
|
|
3564
|
-
method,
|
|
3565
|
-
params
|
|
3566
|
-
};
|
|
3567
|
-
this.pendingRequests.set(id, { resolve: resolve2, reject });
|
|
3568
|
-
const data = JSON.stringify(msg);
|
|
3569
|
-
const header = `Content-Length: ${Buffer.byteLength(data)}\r
|
|
3570
|
-
\r
|
|
3571
|
-
`;
|
|
3572
|
-
this.process?.stdin?.write(header + data);
|
|
3573
|
-
setTimeout(() => {
|
|
3574
|
-
if (this.pendingRequests.has(id)) {
|
|
3575
|
-
this.pendingRequests.delete(id);
|
|
3576
|
-
reject(new Error(`MCP request ${method} timed out`));
|
|
3577
|
-
}
|
|
3578
|
-
}, 3e4);
|
|
3579
|
-
});
|
|
3580
|
-
}
|
|
3581
|
-
sendNotification(method, params) {
|
|
3582
|
-
const msg = {
|
|
3583
|
-
jsonrpc: "2.0",
|
|
3584
|
-
method,
|
|
3585
|
-
params
|
|
3586
|
-
};
|
|
3587
|
-
const data = JSON.stringify(msg);
|
|
3588
|
-
const header = `Content-Length: ${Buffer.byteLength(data)}\r
|
|
3589
|
-
\r
|
|
3590
|
-
`;
|
|
3591
|
-
this.process?.stdin?.write(header + data);
|
|
3592
|
-
}
|
|
3593
|
-
processBuffer() {
|
|
3594
|
-
while (this.buffer.length > 0) {
|
|
3595
|
-
const headerEnd = this.buffer.indexOf("\r\n\r\n");
|
|
3596
|
-
if (headerEnd === -1) break;
|
|
3597
|
-
const header = this.buffer.slice(0, headerEnd);
|
|
3598
|
-
const lengthMatch = header.match(/Content-Length:\s*(\d+)/i);
|
|
3599
|
-
if (!lengthMatch) {
|
|
3600
|
-
const nlIdx = this.buffer.indexOf("\n");
|
|
3601
|
-
if (nlIdx === -1) break;
|
|
3602
|
-
const line = this.buffer.slice(0, nlIdx).trim();
|
|
3603
|
-
this.buffer = this.buffer.slice(nlIdx + 1);
|
|
3604
|
-
if (line) this.handleMessage(line);
|
|
3605
|
-
continue;
|
|
3606
|
-
}
|
|
3607
|
-
const contentLength = parseInt(lengthMatch[1], 10);
|
|
3608
|
-
const messageStart = headerEnd + 4;
|
|
3609
|
-
if (this.buffer.length < messageStart + contentLength) break;
|
|
3610
|
-
const body = this.buffer.slice(messageStart, messageStart + contentLength);
|
|
3611
|
-
this.buffer = this.buffer.slice(messageStart + contentLength);
|
|
3612
|
-
this.handleMessage(body);
|
|
3613
|
-
}
|
|
3614
|
-
}
|
|
3615
|
-
handleMessage(raw) {
|
|
3616
|
-
try {
|
|
3617
|
-
const msg = JSON.parse(raw);
|
|
3618
|
-
if (msg.id !== void 0 && this.pendingRequests.has(msg.id)) {
|
|
3619
|
-
const pending = this.pendingRequests.get(msg.id);
|
|
3620
|
-
this.pendingRequests.delete(msg.id);
|
|
3621
|
-
if (msg.error) {
|
|
3622
|
-
pending.reject(new Error(`MCP error: ${msg.error.message}`));
|
|
3623
|
-
} else {
|
|
3624
|
-
pending.resolve(msg.result);
|
|
3625
|
-
}
|
|
3626
|
-
}
|
|
3627
|
-
} catch {
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
};
|
|
3631
|
-
function parseMCPConfig(config) {
|
|
3632
|
-
const servers = config?.mcpServers;
|
|
3633
|
-
if (!servers || typeof servers !== "object") return {};
|
|
3634
|
-
const result = {};
|
|
3635
|
-
for (const [name, cfg] of Object.entries(servers)) {
|
|
3636
|
-
const c = cfg;
|
|
3637
|
-
if (c?.command) {
|
|
3638
|
-
result[name] = {
|
|
3639
|
-
command: c.command,
|
|
3640
|
-
args: c.args,
|
|
3641
|
-
env: c.env,
|
|
3642
|
-
cwd: c.cwd
|
|
3643
|
-
};
|
|
3644
|
-
}
|
|
3645
|
-
}
|
|
3646
|
-
return result;
|
|
3647
|
-
}
|
|
3648
|
-
|
|
3649
3887
|
// src/ui/completions.ts
|
|
3650
3888
|
import fs17 from "fs";
|
|
3651
3889
|
import path18 from "path";
|
|
@@ -3753,7 +3991,7 @@ function printModelTable(activeModel) {
|
|
|
3753
3991
|
`));
|
|
3754
3992
|
}
|
|
3755
3993
|
function printHelp() {
|
|
3756
|
-
console.log(
|
|
3994
|
+
console.log(chalk9.gray(`
|
|
3757
3995
|
Commands:
|
|
3758
3996
|
/model \u2014 Show available models
|
|
3759
3997
|
/model <name> \u2014 Switch model (e.g., /model pyre)
|
|
@@ -3826,13 +4064,13 @@ async function main() {
|
|
|
3826
4064
|
try {
|
|
3827
4065
|
spinner.stop();
|
|
3828
4066
|
const creds = await login();
|
|
3829
|
-
console.log(
|
|
4067
|
+
console.log(chalk9.green(`
|
|
3830
4068
|
\u2713 Signed in as ${creds.email}`));
|
|
3831
|
-
console.log(
|
|
4069
|
+
console.log(chalk9.gray(` API key stored in ${(await import("./auth-JQX6MHJG.js")).getCredentialsPath()}
|
|
3832
4070
|
`));
|
|
3833
4071
|
} catch (err) {
|
|
3834
4072
|
spinner.stop();
|
|
3835
|
-
console.error(
|
|
4073
|
+
console.error(chalk9.red(`
|
|
3836
4074
|
Login failed: ${err.message}
|
|
3837
4075
|
`));
|
|
3838
4076
|
process.exit(1);
|
|
@@ -3842,10 +4080,10 @@ async function main() {
|
|
|
3842
4080
|
if (promptArgs[0] === "logout") {
|
|
3843
4081
|
const creds = await loadCredentials();
|
|
3844
4082
|
if (!creds) {
|
|
3845
|
-
console.log(
|
|
4083
|
+
console.log(chalk9.gray("\n Not signed in.\n"));
|
|
3846
4084
|
} else {
|
|
3847
4085
|
await clearCredentials();
|
|
3848
|
-
console.log(
|
|
4086
|
+
console.log(chalk9.green(`
|
|
3849
4087
|
\u2713 Signed out (${creds.email})
|
|
3850
4088
|
`));
|
|
3851
4089
|
}
|
|
@@ -3854,13 +4092,13 @@ async function main() {
|
|
|
3854
4092
|
if (promptArgs[0] === "whoami") {
|
|
3855
4093
|
const creds = await loadCredentials();
|
|
3856
4094
|
if (!creds) {
|
|
3857
|
-
console.log(
|
|
4095
|
+
console.log(chalk9.gray("\n Not signed in. Run: notch login\n"));
|
|
3858
4096
|
} else {
|
|
3859
4097
|
const keyPreview = `${creds.token.slice(0, 12)}...`;
|
|
3860
|
-
console.log(
|
|
3861
|
-
Signed in as ${
|
|
3862
|
-
console.log(
|
|
3863
|
-
console.log(
|
|
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()}
|
|
3864
4102
|
`));
|
|
3865
4103
|
}
|
|
3866
4104
|
return;
|
|
@@ -3884,8 +4122,8 @@ async function main() {
|
|
|
3884
4122
|
const config = await loadConfig(configOverrides);
|
|
3885
4123
|
if (opts.model) {
|
|
3886
4124
|
if (!isValidModel(opts.model)) {
|
|
3887
|
-
console.error(
|
|
3888
|
-
console.error(
|
|
4125
|
+
console.error(chalk9.red(` Unknown model: ${opts.model}`));
|
|
4126
|
+
console.error(chalk9.gray(` Available: ${modelChoices}`));
|
|
3889
4127
|
process.exit(1);
|
|
3890
4128
|
}
|
|
3891
4129
|
config.models.chat.model = opts.model;
|
|
@@ -3896,18 +4134,40 @@ async function main() {
|
|
|
3896
4134
|
setTheme(config.theme);
|
|
3897
4135
|
}
|
|
3898
4136
|
let activeModelId = config.models.chat.model;
|
|
3899
|
-
let model
|
|
4137
|
+
let model;
|
|
4138
|
+
try {
|
|
4139
|
+
model = resolveModel(config.models.chat);
|
|
4140
|
+
} catch (err) {
|
|
4141
|
+
if (err instanceof MissingApiKeyError) {
|
|
4142
|
+
printWordmark(VERSION);
|
|
4143
|
+
console.log(" To get started, you need a Notch API key.");
|
|
4144
|
+
console.log("");
|
|
4145
|
+
console.log(" \x1B[1mOption 1:\x1B[0m Log in via browser (recommended)");
|
|
4146
|
+
console.log(" \x1B[33m$ notch login\x1B[0m");
|
|
4147
|
+
console.log("");
|
|
4148
|
+
console.log(" \x1B[1mOption 2:\x1B[0m Set your API key directly");
|
|
4149
|
+
console.log(" \x1B[33m$ export NOTCH_API_KEY=your-key-here\x1B[0m");
|
|
4150
|
+
console.log("");
|
|
4151
|
+
console.log(" \x1B[1mOption 3:\x1B[0m Pass it inline");
|
|
4152
|
+
console.log(" \x1B[33m$ notch --api-key your-key-here\x1B[0m");
|
|
4153
|
+
console.log("");
|
|
4154
|
+
console.log(" Get your key at: \x1B[4mhttps://freesyntax.dev/settings\x1B[0m");
|
|
4155
|
+
console.log("");
|
|
4156
|
+
process.exit(0);
|
|
4157
|
+
}
|
|
4158
|
+
throw err;
|
|
4159
|
+
}
|
|
3900
4160
|
const info = MODEL_CATALOG[activeModelId];
|
|
3901
4161
|
printBanner(VERSION, info.label, info.id, info.size, config.projectRoot);
|
|
3902
4162
|
checkForUpdates(VERSION).then((msg) => {
|
|
3903
4163
|
if (msg) console.log(msg);
|
|
3904
4164
|
});
|
|
3905
4165
|
const hookTrustPrompt = async (commands) => {
|
|
3906
|
-
console.warn(
|
|
3907
|
-
commands.forEach((cmd) => console.warn(
|
|
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}`)));
|
|
3908
4168
|
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3909
4169
|
return new Promise((resolve2) => {
|
|
3910
|
-
rl2.question(
|
|
4170
|
+
rl2.question(chalk9.yellow("\nAllow these hooks for this project? [y/N] "), (answer) => {
|
|
3911
4171
|
rl2.close();
|
|
3912
4172
|
resolve2(answer.trim().toLowerCase() === "y");
|
|
3913
4173
|
});
|
|
@@ -3953,9 +4213,9 @@ ${repoMapStr}` : ""
|
|
|
3953
4213
|
const client = new MCPClient(mcpConfig, name);
|
|
3954
4214
|
await client.connect();
|
|
3955
4215
|
mcpClients.push(client);
|
|
3956
|
-
console.log(
|
|
4216
|
+
console.log(chalk9.green(` MCP: Connected to ${name} (${client.tools.length} tools)`));
|
|
3957
4217
|
} catch (err) {
|
|
3958
|
-
console.log(
|
|
4218
|
+
console.log(chalk9.yellow(` MCP: Could not connect to ${name}: ${err.message}`));
|
|
3959
4219
|
}
|
|
3960
4220
|
}
|
|
3961
4221
|
} catch {
|
|
@@ -3972,7 +4232,7 @@ ${repoMapStr}` : ""
|
|
|
3972
4232
|
});
|
|
3973
4233
|
});
|
|
3974
4234
|
},
|
|
3975
|
-
log: (msg) => console.log(
|
|
4235
|
+
log: (msg) => console.log(chalk9.gray(` ${msg}`)),
|
|
3976
4236
|
checkPermission: config.permissionMode === "trust" ? () => "allow" : (toolName, args) => checkPermission(permissions, toolName, args),
|
|
3977
4237
|
runHook: async (event, ctx) => {
|
|
3978
4238
|
if (!config.enableHooks || hookConfig.hooks.length === 0) return;
|
|
@@ -3982,7 +4242,7 @@ ${repoMapStr}` : ""
|
|
|
3982
4242
|
});
|
|
3983
4243
|
for (const r of results) {
|
|
3984
4244
|
if (!r.ok) {
|
|
3985
|
-
console.log(
|
|
4245
|
+
console.log(chalk9.yellow(` Hook failed (${r.hook.event}): ${r.error}`));
|
|
3986
4246
|
}
|
|
3987
4247
|
}
|
|
3988
4248
|
}
|
|
@@ -3994,10 +4254,10 @@ ${repoMapStr}` : ""
|
|
|
3994
4254
|
if (session) {
|
|
3995
4255
|
messages.push(...session.messages);
|
|
3996
4256
|
sessionId = session.meta.id;
|
|
3997
|
-
console.log(
|
|
4257
|
+
console.log(chalk9.green(` Resumed session ${session.meta.id} (${session.meta.turns} turns)
|
|
3998
4258
|
`));
|
|
3999
4259
|
} else {
|
|
4000
|
-
console.log(
|
|
4260
|
+
console.log(chalk9.gray(" No session to resume.\n"));
|
|
4001
4261
|
}
|
|
4002
4262
|
}
|
|
4003
4263
|
const pipedInput = await readStdin();
|
|
@@ -4018,10 +4278,10 @@ Analyze the above input.`;
|
|
|
4018
4278
|
const refContext = formatReferences(references);
|
|
4019
4279
|
const finalPrompt = refContext + cleanInput;
|
|
4020
4280
|
messages.push({ role: "user", content: finalPrompt });
|
|
4021
|
-
console.log(
|
|
4281
|
+
console.log(chalk9.cyan(`> ${oneShot || "(piped input)"}
|
|
4022
4282
|
`));
|
|
4023
4283
|
if (references.length > 0) {
|
|
4024
|
-
console.log(
|
|
4284
|
+
console.log(chalk9.gray(` Injected ${references.length} reference(s)
|
|
4025
4285
|
`));
|
|
4026
4286
|
}
|
|
4027
4287
|
const spinner = ora("Thinking...").start();
|
|
@@ -4040,13 +4300,13 @@ Analyze the above input.`;
|
|
|
4040
4300
|
onToolCall: (name, args) => {
|
|
4041
4301
|
if (spinner.isSpinning) spinner.stop();
|
|
4042
4302
|
const argSummary = Object.entries(args).map(([k, v]) => `${k}=${String(v).slice(0, 60)}`).join(", ");
|
|
4043
|
-
console.log(
|
|
4303
|
+
console.log(chalk9.gray(`
|
|
4044
4304
|
\u2192 ${name}(${argSummary})`));
|
|
4045
4305
|
},
|
|
4046
4306
|
onToolResult: (_name, result, isError) => {
|
|
4047
4307
|
const preview = result.slice(0, 100).replace(/\n/g, " ");
|
|
4048
|
-
const icon = isError ?
|
|
4049
|
-
console.log(
|
|
4308
|
+
const icon = isError ? chalk9.red("\u2717") : chalk9.green("\u2713");
|
|
4309
|
+
console.log(chalk9.gray(` ${icon} ${preview}${result.length > 100 ? "..." : ""}`));
|
|
4050
4310
|
}
|
|
4051
4311
|
})
|
|
4052
4312
|
);
|
|
@@ -4059,7 +4319,7 @@ Analyze the above input.`;
|
|
|
4059
4319
|
model: activeModelId
|
|
4060
4320
|
});
|
|
4061
4321
|
costTracker.record(activeModelId, response.usage.promptTokens, response.usage.completionTokens);
|
|
4062
|
-
console.log(usage.formatLast());
|
|
4322
|
+
console.log(usage.formatLast() + " " + costTracker.formatLastCost());
|
|
4063
4323
|
}
|
|
4064
4324
|
} catch (err) {
|
|
4065
4325
|
spinner.fail(`Error: ${err.message}`);
|
|
@@ -4071,7 +4331,7 @@ Analyze the above input.`;
|
|
|
4071
4331
|
const savedPlan = await loadPlan(config.projectRoot);
|
|
4072
4332
|
if (savedPlan) {
|
|
4073
4333
|
ralphPlan = savedPlan;
|
|
4074
|
-
console.log(
|
|
4334
|
+
console.log(chalk9.gray(` Ralph plan loaded (${savedPlan.tasks.length} tasks)
|
|
4075
4335
|
`));
|
|
4076
4336
|
}
|
|
4077
4337
|
} catch {
|
|
@@ -4083,7 +4343,7 @@ Analyze the above input.`;
|
|
|
4083
4343
|
prompt: theme().prompt("notch> "),
|
|
4084
4344
|
completer: (line) => completer(line)
|
|
4085
4345
|
});
|
|
4086
|
-
console.log(
|
|
4346
|
+
console.log(chalk9.gray(" Type your request, or /help for commands.\n"));
|
|
4087
4347
|
rl.prompt();
|
|
4088
4348
|
rl.on("line", async (line) => {
|
|
4089
4349
|
const input = line.trim();
|
|
@@ -4099,7 +4359,7 @@ Analyze the above input.`;
|
|
|
4099
4359
|
if (messages.length > 0) {
|
|
4100
4360
|
try {
|
|
4101
4361
|
const id = await saveSession(config.projectRoot, messages, { model: activeModelId });
|
|
4102
|
-
console.log(
|
|
4362
|
+
console.log(chalk9.gray(` Session saved: ${id}`));
|
|
4103
4363
|
} catch {
|
|
4104
4364
|
}
|
|
4105
4365
|
}
|
|
@@ -4110,12 +4370,12 @@ Analyze the above input.`;
|
|
|
4110
4370
|
}
|
|
4111
4371
|
}
|
|
4112
4372
|
await toolCtx.runHook?.("session-end", {});
|
|
4113
|
-
console.log(
|
|
4373
|
+
console.log(chalk9.gray("\n Goodbye!\n"));
|
|
4114
4374
|
process.exit(0);
|
|
4115
4375
|
}
|
|
4116
4376
|
if (input === "/clear") {
|
|
4117
4377
|
messages.length = 0;
|
|
4118
|
-
console.log(
|
|
4378
|
+
console.log(chalk9.gray(" Conversation cleared.\n"));
|
|
4119
4379
|
rl.prompt();
|
|
4120
4380
|
return;
|
|
4121
4381
|
}
|
|
@@ -4135,8 +4395,8 @@ Analyze the above input.`;
|
|
|
4135
4395
|
newModel = `notch-${newModel}`;
|
|
4136
4396
|
}
|
|
4137
4397
|
if (!isValidModel(newModel)) {
|
|
4138
|
-
console.log(
|
|
4139
|
-
console.log(
|
|
4398
|
+
console.log(chalk9.red(` Unknown model: ${newModel}`));
|
|
4399
|
+
console.log(chalk9.gray(` Available: ${modelChoices}`));
|
|
4140
4400
|
rl.prompt();
|
|
4141
4401
|
return;
|
|
4142
4402
|
}
|
|
@@ -4144,7 +4404,7 @@ Analyze the above input.`;
|
|
|
4144
4404
|
config.models.chat.model = activeModelId;
|
|
4145
4405
|
model = resolveModel(config.models.chat);
|
|
4146
4406
|
const switchedInfo = MODEL_CATALOG[activeModelId];
|
|
4147
|
-
console.log(
|
|
4407
|
+
console.log(chalk9.green(` Switched to ${switchedInfo.label} (${switchedInfo.id})
|
|
4148
4408
|
`));
|
|
4149
4409
|
rl.prompt();
|
|
4150
4410
|
return;
|
|
@@ -4157,22 +4417,22 @@ Analyze the above input.`;
|
|
|
4157
4417
|
statusSpinner.succeed(`${statusInfo.label} (${activeModelId}) is reachable`);
|
|
4158
4418
|
} else {
|
|
4159
4419
|
statusSpinner.fail(check.error ?? "API unreachable");
|
|
4160
|
-
console.log(
|
|
4420
|
+
console.log(chalk9.gray(" Tip: Set NOTCH_API_KEY or use --api-key, and verify your Modal endpoint is running.\n"));
|
|
4161
4421
|
}
|
|
4162
4422
|
rl.prompt();
|
|
4163
4423
|
return;
|
|
4164
4424
|
}
|
|
4165
4425
|
if (input === "/undo") {
|
|
4166
4426
|
if (checkpoints.undoCount === 0) {
|
|
4167
|
-
console.log(
|
|
4427
|
+
console.log(chalk9.gray(" Nothing to undo.\n"));
|
|
4168
4428
|
rl.prompt();
|
|
4169
4429
|
return;
|
|
4170
4430
|
}
|
|
4171
4431
|
const undone = await checkpoints.undo();
|
|
4172
4432
|
if (undone) {
|
|
4173
4433
|
const fileCount = undone.files.length;
|
|
4174
|
-
console.log(
|
|
4175
|
-
console.log(
|
|
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
|
|
4176
4436
|
`));
|
|
4177
4437
|
}
|
|
4178
4438
|
rl.prompt();
|
|
@@ -4180,7 +4440,7 @@ Analyze the above input.`;
|
|
|
4180
4440
|
}
|
|
4181
4441
|
if (input === "/usage") {
|
|
4182
4442
|
if (usage.turnCount === 0) {
|
|
4183
|
-
console.log(
|
|
4443
|
+
console.log(chalk9.gray(" No usage yet.\n"));
|
|
4184
4444
|
} else {
|
|
4185
4445
|
console.log(usage.formatSession());
|
|
4186
4446
|
const currentTokens = estimateTokens(messages);
|
|
@@ -4193,7 +4453,7 @@ Analyze the above input.`;
|
|
|
4193
4453
|
}
|
|
4194
4454
|
if (input === "/cost") {
|
|
4195
4455
|
if (costTracker.totalCost === 0) {
|
|
4196
|
-
console.log(
|
|
4456
|
+
console.log(chalk9.gray(" No cost data yet.\n"));
|
|
4197
4457
|
} else {
|
|
4198
4458
|
console.log(costTracker.formatTotal());
|
|
4199
4459
|
console.log(costTracker.formatByModel());
|
|
@@ -4208,7 +4468,7 @@ Analyze the above input.`;
|
|
|
4208
4468
|
const compressed = await autoCompress2(messages, model, MODEL_CATALOG[activeModelId].contextWindow);
|
|
4209
4469
|
messages.length = 0;
|
|
4210
4470
|
messages.push(...compressed);
|
|
4211
|
-
console.log(
|
|
4471
|
+
console.log(chalk9.green(` Compressed: ${before} messages \u2192 ${messages.length} messages`));
|
|
4212
4472
|
console.log("");
|
|
4213
4473
|
rl.prompt();
|
|
4214
4474
|
return;
|
|
@@ -4216,10 +4476,10 @@ Analyze the above input.`;
|
|
|
4216
4476
|
if (input === "/diff") {
|
|
4217
4477
|
const diffs = checkpoints.allDiffs();
|
|
4218
4478
|
if (diffs.length === 0) {
|
|
4219
|
-
console.log(
|
|
4479
|
+
console.log(chalk9.gray(" No file changes this session.\n"));
|
|
4220
4480
|
} else {
|
|
4221
4481
|
for (const df of diffs) {
|
|
4222
|
-
console.log(
|
|
4482
|
+
console.log(chalk9.cyan(` ${df.path}:`));
|
|
4223
4483
|
console.log(unifiedDiff(df.before, df.after, df.path));
|
|
4224
4484
|
console.log("");
|
|
4225
4485
|
}
|
|
@@ -4235,10 +4495,10 @@ Analyze the above input.`;
|
|
|
4235
4495
|
projectRoot: config.projectRoot,
|
|
4236
4496
|
outputPath: exportPath
|
|
4237
4497
|
});
|
|
4238
|
-
console.log(
|
|
4498
|
+
console.log(chalk9.green(` Exported to ${ePath}
|
|
4239
4499
|
`));
|
|
4240
4500
|
} catch (err) {
|
|
4241
|
-
console.log(
|
|
4501
|
+
console.log(chalk9.red(` Export failed: ${err.message}
|
|
4242
4502
|
`));
|
|
4243
4503
|
}
|
|
4244
4504
|
rl.prompt();
|
|
@@ -4248,10 +4508,10 @@ Analyze the above input.`;
|
|
|
4248
4508
|
try {
|
|
4249
4509
|
const id = await saveSession(config.projectRoot, messages, { model: activeModelId });
|
|
4250
4510
|
sessionId = id;
|
|
4251
|
-
console.log(
|
|
4511
|
+
console.log(chalk9.green(` Session saved: ${id}
|
|
4252
4512
|
`));
|
|
4253
4513
|
} catch (err) {
|
|
4254
|
-
console.log(
|
|
4514
|
+
console.log(chalk9.red(` Save failed: ${err.message}
|
|
4255
4515
|
`));
|
|
4256
4516
|
}
|
|
4257
4517
|
rl.prompt();
|
|
@@ -4261,16 +4521,16 @@ Analyze the above input.`;
|
|
|
4261
4521
|
try {
|
|
4262
4522
|
const sessions = await listSessions(config.projectRoot);
|
|
4263
4523
|
if (sessions.length === 0) {
|
|
4264
|
-
console.log(
|
|
4524
|
+
console.log(chalk9.gray(" No saved sessions.\n"));
|
|
4265
4525
|
} else {
|
|
4266
|
-
console.log(
|
|
4526
|
+
console.log(chalk9.gray("\n Saved sessions:\n"));
|
|
4267
4527
|
for (const s of sessions.slice(0, 10)) {
|
|
4268
|
-
console.log(
|
|
4528
|
+
console.log(chalk9.gray(` ${s.id} ${s.turns} turns ${s.date} ${s.model}`));
|
|
4269
4529
|
}
|
|
4270
4530
|
console.log("");
|
|
4271
4531
|
}
|
|
4272
4532
|
} catch (err) {
|
|
4273
|
-
console.log(
|
|
4533
|
+
console.log(chalk9.red(` Error listing sessions: ${err.message}
|
|
4274
4534
|
`));
|
|
4275
4535
|
}
|
|
4276
4536
|
rl.prompt();
|
|
@@ -4288,18 +4548,18 @@ Analyze the above input.`;
|
|
|
4288
4548
|
const savedPlan = await loadPlan(config.projectRoot);
|
|
4289
4549
|
if (savedPlan) {
|
|
4290
4550
|
ralphPlan = savedPlan;
|
|
4291
|
-
console.log(
|
|
4551
|
+
console.log(chalk9.green(` Ralph plan restored (${savedPlan.tasks.length} tasks)
|
|
4292
4552
|
`));
|
|
4293
4553
|
}
|
|
4294
4554
|
} catch {
|
|
4295
4555
|
}
|
|
4296
|
-
console.log(
|
|
4556
|
+
console.log(chalk9.green(` Resumed session ${session.meta.id} (${session.meta.turns} turns)
|
|
4297
4557
|
`));
|
|
4298
4558
|
} else {
|
|
4299
|
-
console.log(
|
|
4559
|
+
console.log(chalk9.gray(" No session found.\n"));
|
|
4300
4560
|
}
|
|
4301
4561
|
} catch (err) {
|
|
4302
|
-
console.log(
|
|
4562
|
+
console.log(chalk9.red(` Resume failed: ${err.message}
|
|
4303
4563
|
`));
|
|
4304
4564
|
}
|
|
4305
4565
|
rl.prompt();
|
|
@@ -4308,7 +4568,7 @@ Analyze the above input.`;
|
|
|
4308
4568
|
if (input.startsWith("/search ")) {
|
|
4309
4569
|
const query = input.replace("/search ", "").trim().toLowerCase();
|
|
4310
4570
|
if (!query) {
|
|
4311
|
-
console.log(
|
|
4571
|
+
console.log(chalk9.gray(" Usage: /search <query>\n"));
|
|
4312
4572
|
rl.prompt();
|
|
4313
4573
|
return;
|
|
4314
4574
|
}
|
|
@@ -4325,14 +4585,14 @@ Analyze the above input.`;
|
|
|
4325
4585
|
}
|
|
4326
4586
|
}
|
|
4327
4587
|
if (matches.length === 0) {
|
|
4328
|
-
console.log(
|
|
4588
|
+
console.log(chalk9.gray(` No matches for "${query}"
|
|
4329
4589
|
`));
|
|
4330
4590
|
} else {
|
|
4331
|
-
console.log(
|
|
4591
|
+
console.log(chalk9.gray(`
|
|
4332
4592
|
${matches.length} match(es) for "${query}":
|
|
4333
4593
|
`));
|
|
4334
4594
|
for (const m of matches.slice(0, 10)) {
|
|
4335
|
-
console.log(
|
|
4595
|
+
console.log(chalk9.gray(` [${m.index}] ${m.role}: ${m.preview}`));
|
|
4336
4596
|
}
|
|
4337
4597
|
console.log("");
|
|
4338
4598
|
}
|
|
@@ -4346,7 +4606,7 @@ Analyze the above input.`;
|
|
|
4346
4606
|
activePlan = await generatePlan(task, model, systemPrompt);
|
|
4347
4607
|
planSpinner.succeed("Plan generated");
|
|
4348
4608
|
console.log(formatPlan(activePlan));
|
|
4349
|
-
console.log(
|
|
4609
|
+
console.log(chalk9.gray(" Use /plan approve to execute, /plan edit to modify, or /plan cancel to discard.\n"));
|
|
4350
4610
|
} catch (err) {
|
|
4351
4611
|
planSpinner.fail(`Plan failed: ${err.message}`);
|
|
4352
4612
|
}
|
|
@@ -4355,11 +4615,11 @@ Analyze the above input.`;
|
|
|
4355
4615
|
}
|
|
4356
4616
|
if (input === "/plan approve") {
|
|
4357
4617
|
if (!activePlan) {
|
|
4358
|
-
console.log(
|
|
4618
|
+
console.log(chalk9.gray(" No active plan. Use /plan <task> to create one.\n"));
|
|
4359
4619
|
rl.prompt();
|
|
4360
4620
|
return;
|
|
4361
4621
|
}
|
|
4362
|
-
console.log(
|
|
4622
|
+
console.log(chalk9.green(" Executing plan...\n"));
|
|
4363
4623
|
while (!isPlanComplete(activePlan)) {
|
|
4364
4624
|
const stepPrompt = currentStepPrompt(activePlan);
|
|
4365
4625
|
messages.push({ role: "user", content: stepPrompt });
|
|
@@ -4377,10 +4637,10 @@ Analyze the above input.`;
|
|
|
4377
4637
|
},
|
|
4378
4638
|
onToolCall: (name) => {
|
|
4379
4639
|
if (planStepSpinner.isSpinning) planStepSpinner.stop();
|
|
4380
|
-
console.log(
|
|
4640
|
+
console.log(chalk9.gray(` \u2192 ${name}`));
|
|
4381
4641
|
},
|
|
4382
4642
|
onToolResult: (_name, _result, isError) => {
|
|
4383
|
-
console.log(isError ?
|
|
4643
|
+
console.log(isError ? chalk9.red(" \u2717") : chalk9.green(" \u2713"));
|
|
4384
4644
|
}
|
|
4385
4645
|
});
|
|
4386
4646
|
console.log("\n");
|
|
@@ -4393,7 +4653,7 @@ Analyze the above input.`;
|
|
|
4393
4653
|
}
|
|
4394
4654
|
}
|
|
4395
4655
|
if (activePlan && isPlanComplete(activePlan)) {
|
|
4396
|
-
console.log(
|
|
4656
|
+
console.log(chalk9.green(" Plan completed!\n"));
|
|
4397
4657
|
}
|
|
4398
4658
|
activePlan = null;
|
|
4399
4659
|
rl.prompt();
|
|
@@ -4401,26 +4661,26 @@ Analyze the above input.`;
|
|
|
4401
4661
|
}
|
|
4402
4662
|
if (input === "/plan edit") {
|
|
4403
4663
|
if (!activePlan) {
|
|
4404
|
-
console.log(
|
|
4664
|
+
console.log(chalk9.gray(" No active plan to edit.\n"));
|
|
4405
4665
|
rl.prompt();
|
|
4406
4666
|
return;
|
|
4407
4667
|
}
|
|
4408
4668
|
console.log(formatPlan(activePlan));
|
|
4409
|
-
console.log(
|
|
4410
|
-
console.log(
|
|
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"));
|
|
4411
4671
|
rl.prompt();
|
|
4412
4672
|
return;
|
|
4413
4673
|
}
|
|
4414
4674
|
if (input === "/plan cancel") {
|
|
4415
4675
|
activePlan = null;
|
|
4416
|
-
console.log(
|
|
4676
|
+
console.log(chalk9.gray(" Plan discarded.\n"));
|
|
4417
4677
|
rl.prompt();
|
|
4418
4678
|
return;
|
|
4419
4679
|
}
|
|
4420
4680
|
if (input.startsWith("/agent ")) {
|
|
4421
4681
|
const task = input.replace("/agent ", "").trim();
|
|
4422
4682
|
const agentId = nextSubagentId();
|
|
4423
|
-
console.log(
|
|
4683
|
+
console.log(chalk9.cyan(` Spawning subagent #${agentId}: ${task}
|
|
4424
4684
|
`));
|
|
4425
4685
|
spawnSubagent({
|
|
4426
4686
|
id: agentId,
|
|
@@ -4430,14 +4690,14 @@ Analyze the above input.`;
|
|
|
4430
4690
|
toolContext: toolCtx,
|
|
4431
4691
|
contextWindow: MODEL_CATALOG[activeModelId].contextWindow,
|
|
4432
4692
|
onComplete: (result) => {
|
|
4433
|
-
console.log(
|
|
4693
|
+
console.log(chalk9.green(`
|
|
4434
4694
|
Subagent #${agentId} finished:`));
|
|
4435
|
-
console.log(
|
|
4695
|
+
console.log(chalk9.gray(` ${result.slice(0, 200)}
|
|
4436
4696
|
`));
|
|
4437
4697
|
rl.prompt();
|
|
4438
4698
|
},
|
|
4439
4699
|
onError: (err) => {
|
|
4440
|
-
console.log(
|
|
4700
|
+
console.log(chalk9.red(`
|
|
4441
4701
|
Subagent #${agentId} failed: ${err}
|
|
4442
4702
|
`));
|
|
4443
4703
|
rl.prompt();
|
|
@@ -4450,18 +4710,18 @@ Analyze the above input.`;
|
|
|
4450
4710
|
try {
|
|
4451
4711
|
const memories = await loadMemories(config.projectRoot);
|
|
4452
4712
|
if (memories.length === 0) {
|
|
4453
|
-
console.log(
|
|
4713
|
+
console.log(chalk9.gray(" No saved memories.\n"));
|
|
4454
4714
|
} else {
|
|
4455
|
-
console.log(
|
|
4715
|
+
console.log(chalk9.gray(`
|
|
4456
4716
|
${memories.length} saved memories:
|
|
4457
4717
|
`));
|
|
4458
4718
|
for (const m of memories) {
|
|
4459
|
-
console.log(
|
|
4719
|
+
console.log(chalk9.gray(` [${m.type}] ${m.content.slice(0, 80)}`));
|
|
4460
4720
|
}
|
|
4461
4721
|
console.log("");
|
|
4462
4722
|
}
|
|
4463
4723
|
} catch (err) {
|
|
4464
|
-
console.log(
|
|
4724
|
+
console.log(chalk9.red(` Error: ${err.message}
|
|
4465
4725
|
`));
|
|
4466
4726
|
}
|
|
4467
4727
|
rl.prompt();
|
|
@@ -4472,16 +4732,16 @@ Analyze the above input.`;
|
|
|
4472
4732
|
try {
|
|
4473
4733
|
const results = await searchMemories(config.projectRoot, query);
|
|
4474
4734
|
if (results.length === 0) {
|
|
4475
|
-
console.log(
|
|
4735
|
+
console.log(chalk9.gray(` No memories matching "${query}"
|
|
4476
4736
|
`));
|
|
4477
4737
|
} else {
|
|
4478
4738
|
for (const m of results) {
|
|
4479
|
-
console.log(
|
|
4739
|
+
console.log(chalk9.gray(` [${m.type}] ${m.content.slice(0, 100)}`));
|
|
4480
4740
|
}
|
|
4481
4741
|
console.log("");
|
|
4482
4742
|
}
|
|
4483
4743
|
} catch (err) {
|
|
4484
|
-
console.log(
|
|
4744
|
+
console.log(chalk9.red(` Error: ${err.message}
|
|
4485
4745
|
`));
|
|
4486
4746
|
}
|
|
4487
4747
|
rl.prompt();
|
|
@@ -4493,10 +4753,10 @@ Analyze the above input.`;
|
|
|
4493
4753
|
for (const m of memories) {
|
|
4494
4754
|
await deleteMemory(config.projectRoot, m.id);
|
|
4495
4755
|
}
|
|
4496
|
-
console.log(
|
|
4756
|
+
console.log(chalk9.yellow(` Cleared ${memories.length} memories.
|
|
4497
4757
|
`));
|
|
4498
4758
|
} catch (err) {
|
|
4499
|
-
console.log(
|
|
4759
|
+
console.log(chalk9.red(` Error: ${err.message}
|
|
4500
4760
|
`));
|
|
4501
4761
|
}
|
|
4502
4762
|
rl.prompt();
|
|
@@ -4524,27 +4784,27 @@ Analyze the above input.`;
|
|
|
4524
4784
|
}
|
|
4525
4785
|
if (input === "/ralph run") {
|
|
4526
4786
|
if (!ralphPlan) {
|
|
4527
|
-
console.log(
|
|
4787
|
+
console.log(chalk9.gray(" No Ralph plan. Use /ralph plan <goal> first.\n"));
|
|
4528
4788
|
rl.prompt();
|
|
4529
4789
|
return;
|
|
4530
4790
|
}
|
|
4531
|
-
console.log(
|
|
4791
|
+
console.log(chalk9.green(" Ralph is running...\n"));
|
|
4532
4792
|
try {
|
|
4533
4793
|
ralphPlan = await runRalphLoop(ralphPlan, {
|
|
4534
4794
|
model,
|
|
4535
4795
|
systemPrompt,
|
|
4536
4796
|
toolContext: toolCtx,
|
|
4537
4797
|
contextWindow: MODEL_CATALOG[activeModelId].contextWindow,
|
|
4538
|
-
onTaskStart: (task) => console.log(
|
|
4539
|
-
onTaskComplete: (task) => console.log(
|
|
4798
|
+
onTaskStart: (task) => console.log(chalk9.cyan(` \u25B6 Task: ${task.description}`)),
|
|
4799
|
+
onTaskComplete: (task) => console.log(chalk9.green(` \u2713 Done: ${task.description}
|
|
4540
4800
|
`)),
|
|
4541
|
-
onTaskFail: (task, err) => console.log(
|
|
4801
|
+
onTaskFail: (task, err) => console.log(chalk9.red(` \u2717 Failed: ${task.description} (${err})
|
|
4542
4802
|
`))
|
|
4543
4803
|
});
|
|
4544
4804
|
await savePlan(config.projectRoot, ralphPlan);
|
|
4545
4805
|
console.log(formatRalphStatus(ralphPlan));
|
|
4546
4806
|
} catch (err) {
|
|
4547
|
-
console.log(
|
|
4807
|
+
console.log(chalk9.red(` Ralph error: ${err.message}
|
|
4548
4808
|
`));
|
|
4549
4809
|
}
|
|
4550
4810
|
rl.prompt();
|
|
@@ -4552,7 +4812,7 @@ Analyze the above input.`;
|
|
|
4552
4812
|
}
|
|
4553
4813
|
if (input === "/ralph status") {
|
|
4554
4814
|
if (!ralphPlan) {
|
|
4555
|
-
console.log(
|
|
4815
|
+
console.log(chalk9.gray(" No Ralph plan active.\n"));
|
|
4556
4816
|
} else {
|
|
4557
4817
|
console.log(formatRalphStatus(ralphPlan));
|
|
4558
4818
|
}
|
|
@@ -4563,26 +4823,26 @@ Analyze the above input.`;
|
|
|
4563
4823
|
ralphPlan = null;
|
|
4564
4824
|
await deletePlan(config.projectRoot).catch(() => {
|
|
4565
4825
|
});
|
|
4566
|
-
console.log(
|
|
4826
|
+
console.log(chalk9.gray(" Ralph plan cleared.\n"));
|
|
4567
4827
|
rl.prompt();
|
|
4568
4828
|
return;
|
|
4569
4829
|
}
|
|
4570
4830
|
if (input === "/branch") {
|
|
4571
4831
|
const branchId = `branch-${branches.size + 1}`;
|
|
4572
4832
|
branches.set(branchId, [...messages]);
|
|
4573
|
-
console.log(
|
|
4833
|
+
console.log(chalk9.green(` Forked conversation as "${branchId}" (${messages.length} messages)
|
|
4574
4834
|
`));
|
|
4575
4835
|
rl.prompt();
|
|
4576
4836
|
return;
|
|
4577
4837
|
}
|
|
4578
4838
|
if (input === "/branches") {
|
|
4579
4839
|
if (branches.size === 0) {
|
|
4580
|
-
console.log(
|
|
4840
|
+
console.log(chalk9.gray(" No conversation branches. Use /branch to fork.\n"));
|
|
4581
4841
|
} else {
|
|
4582
|
-
console.log(
|
|
4842
|
+
console.log(chalk9.gray("\n Branches:\n"));
|
|
4583
4843
|
for (const [name, msgs] of branches) {
|
|
4584
|
-
const marker = name === currentBranch ?
|
|
4585
|
-
console.log(
|
|
4844
|
+
const marker = name === currentBranch ? chalk9.green(" \u25CF") : " ";
|
|
4845
|
+
console.log(chalk9.gray(` ${marker} ${name} (${msgs.length} messages)`));
|
|
4586
4846
|
}
|
|
4587
4847
|
console.log("");
|
|
4588
4848
|
}
|
|
@@ -4619,8 +4879,8 @@ Analyze the above input.`;
|
|
|
4619
4879
|
return;
|
|
4620
4880
|
}
|
|
4621
4881
|
if (input.startsWith("/")) {
|
|
4622
|
-
console.log(
|
|
4623
|
-
console.log(
|
|
4882
|
+
console.log(chalk9.red(` Unknown command: ${input}`));
|
|
4883
|
+
console.log(chalk9.gray(" Type /help for available commands.\n"));
|
|
4624
4884
|
rl.prompt();
|
|
4625
4885
|
return;
|
|
4626
4886
|
}
|
|
@@ -4628,7 +4888,7 @@ Analyze the above input.`;
|
|
|
4628
4888
|
const refContext = formatReferences(references);
|
|
4629
4889
|
const finalPrompt = refContext + cleanInput;
|
|
4630
4890
|
if (references.length > 0) {
|
|
4631
|
-
console.log(
|
|
4891
|
+
console.log(chalk9.gray(` Injected ${references.length} reference(s)`));
|
|
4632
4892
|
}
|
|
4633
4893
|
messages.push({ role: "user", content: finalPrompt });
|
|
4634
4894
|
const spinner = ora("Thinking...").start();
|
|
@@ -4654,16 +4914,16 @@ Analyze the above input.`;
|
|
|
4654
4914
|
const val = String(v);
|
|
4655
4915
|
return `${k}=${val.length > 60 ? val.slice(0, 60) + "..." : val}`;
|
|
4656
4916
|
}).join(", ");
|
|
4657
|
-
console.log(
|
|
4917
|
+
console.log(chalk9.gray(`
|
|
4658
4918
|
\u2192 ${name}(${argSummary})`));
|
|
4659
4919
|
},
|
|
4660
4920
|
onToolResult: (_name, result, isError) => {
|
|
4661
4921
|
const preview = result.slice(0, 100).replace(/\n/g, " ");
|
|
4662
|
-
const icon = isError ?
|
|
4663
|
-
console.log(
|
|
4922
|
+
const icon = isError ? chalk9.red("\u2717") : chalk9.green("\u2713");
|
|
4923
|
+
console.log(chalk9.gray(` ${icon} ${preview}${result.length > 100 ? "..." : ""}`));
|
|
4664
4924
|
},
|
|
4665
4925
|
onCompress: () => {
|
|
4666
|
-
console.log(
|
|
4926
|
+
console.log(chalk9.yellow("\n [Context compressed to fit window]\n"));
|
|
4667
4927
|
}
|
|
4668
4928
|
})
|
|
4669
4929
|
);
|
|
@@ -4677,7 +4937,7 @@ Analyze the above input.`;
|
|
|
4677
4937
|
model: activeModelId
|
|
4678
4938
|
});
|
|
4679
4939
|
costTracker.record(activeModelId, response.usage.promptTokens, response.usage.completionTokens);
|
|
4680
|
-
console.log(usage.formatLast());
|
|
4940
|
+
console.log(usage.formatLast() + " " + costTracker.formatLastCost());
|
|
4681
4941
|
const currentTokens = estimateTokens(messages);
|
|
4682
4942
|
const ctxWindow = MODEL_CATALOG[activeModelId].contextWindow;
|
|
4683
4943
|
if (currentTokens > ctxWindow * 0.5) {
|
|
@@ -4696,7 +4956,7 @@ Analyze the above input.`;
|
|
|
4696
4956
|
type: "auto",
|
|
4697
4957
|
content: memMatch[1]
|
|
4698
4958
|
});
|
|
4699
|
-
console.log(
|
|
4959
|
+
console.log(chalk9.gray(" (Saved to memory)\n"));
|
|
4700
4960
|
} catch {
|
|
4701
4961
|
}
|
|
4702
4962
|
}
|
|
@@ -4706,13 +4966,13 @@ Analyze the above input.`;
|
|
|
4706
4966
|
checkpoints.discard();
|
|
4707
4967
|
const msg = err.message?.toLowerCase() ?? "";
|
|
4708
4968
|
if (msg.includes("fetch") || msg.includes("econnrefused") || msg.includes("network")) {
|
|
4709
|
-
console.log(
|
|
4969
|
+
console.log(chalk9.gray(" Tip: Check that your Notch endpoint is running. Use /status to verify.\n"));
|
|
4710
4970
|
} else if (msg.includes("401") || msg.includes("unauthorized") || msg.includes("api key")) {
|
|
4711
|
-
console.log(
|
|
4971
|
+
console.log(chalk9.gray(" Tip: Set NOTCH_API_KEY env var or use --api-key flag.\n"));
|
|
4712
4972
|
} else if (msg.includes("429") || msg.includes("rate limit")) {
|
|
4713
|
-
console.log(
|
|
4973
|
+
console.log(chalk9.gray(" Tip: Rate limited. Wait a moment and try again.\n"));
|
|
4714
4974
|
} else {
|
|
4715
|
-
console.log(
|
|
4975
|
+
console.log(chalk9.gray(" (The conversation history is preserved. Try again.)\n"));
|
|
4716
4976
|
}
|
|
4717
4977
|
}
|
|
4718
4978
|
rl.prompt();
|
|
@@ -4730,13 +4990,13 @@ async function handleRalphSubcommand(args, cliOpts) {
|
|
|
4730
4990
|
cwd: config.projectRoot,
|
|
4731
4991
|
requireConfirm: false,
|
|
4732
4992
|
confirm: async () => true,
|
|
4733
|
-
log: (msg) => console.log(
|
|
4993
|
+
log: (msg) => console.log(chalk9.gray(` ${msg}`))
|
|
4734
4994
|
};
|
|
4735
4995
|
const subcommand = args[0];
|
|
4736
4996
|
if (subcommand === "plan") {
|
|
4737
4997
|
const goal = args.slice(1).join(" ");
|
|
4738
4998
|
if (!goal) {
|
|
4739
|
-
console.error(
|
|
4999
|
+
console.error(chalk9.red(" Usage: notch ralph plan <goal>"));
|
|
4740
5000
|
process.exit(1);
|
|
4741
5001
|
}
|
|
4742
5002
|
const spinner = ora("Ralph is planning...").start();
|
|
@@ -4747,7 +5007,7 @@ async function handleRalphSubcommand(args, cliOpts) {
|
|
|
4747
5007
|
} else if (subcommand === "run") {
|
|
4748
5008
|
let plan = await loadPlan(config.projectRoot);
|
|
4749
5009
|
if (!plan) {
|
|
4750
|
-
console.error(
|
|
5010
|
+
console.error(chalk9.red(" No plan found. Run: notch ralph plan <goal>"));
|
|
4751
5011
|
process.exit(1);
|
|
4752
5012
|
}
|
|
4753
5013
|
plan = await runRalphLoop(plan, {
|
|
@@ -4755,27 +5015,27 @@ async function handleRalphSubcommand(args, cliOpts) {
|
|
|
4755
5015
|
systemPrompt,
|
|
4756
5016
|
toolContext: toolCtx,
|
|
4757
5017
|
contextWindow: MODEL_CATALOG[config.models.chat.model].contextWindow,
|
|
4758
|
-
onTaskStart: (t) => console.log(
|
|
4759
|
-
onTaskComplete: (t) => console.log(
|
|
4760
|
-
onTaskFail: (t, e) => console.log(
|
|
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}`))
|
|
4761
5021
|
});
|
|
4762
5022
|
await savePlan(config.projectRoot, plan);
|
|
4763
5023
|
console.log(formatRalphStatus(plan));
|
|
4764
5024
|
} else if (subcommand === "status") {
|
|
4765
5025
|
const plan = await loadPlan(config.projectRoot);
|
|
4766
5026
|
if (!plan) {
|
|
4767
|
-
console.log(
|
|
5027
|
+
console.log(chalk9.gray(" No Ralph plan found."));
|
|
4768
5028
|
} else {
|
|
4769
5029
|
console.log(formatRalphStatus(plan));
|
|
4770
5030
|
}
|
|
4771
5031
|
} else {
|
|
4772
|
-
console.error(
|
|
4773
|
-
console.error(
|
|
5032
|
+
console.error(chalk9.red(` Unknown: notch ralph ${subcommand}`));
|
|
5033
|
+
console.error(chalk9.gray(" Usage: notch ralph <plan|run|status>"));
|
|
4774
5034
|
process.exit(1);
|
|
4775
5035
|
}
|
|
4776
5036
|
}
|
|
4777
5037
|
main().catch((err) => {
|
|
4778
|
-
console.error(
|
|
5038
|
+
console.error(chalk9.red(`
|
|
4779
5039
|
Fatal: ${err.message}
|
|
4780
5040
|
`));
|
|
4781
5041
|
process.exit(1);
|