@freesyntax/notch-cli 0.5.21 → 0.5.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{apply-patch-D5PDUXUC.js → apply-patch-U6K67CMT.js} +1 -0
- package/dist/auth-UAMMP5IJ.js +29 -0
- package/dist/{chunk-OSWUX6TC.js → chunk-4HPRBCSY.js} +1 -1
- package/dist/{chunk-QKM27RHS.js → chunk-6NKRMZTX.js} +1 -1
- package/dist/{chunk-TU465P2P.js → chunk-EPSOOCNB.js} +4 -2
- package/dist/{chunk-MMBFNIKE.js → chunk-FZVPGJJW.js} +5 -3
- package/dist/chunk-J66N6AFH.js +137 -0
- package/dist/{chunk-443G6HCC.js → chunk-JXQ4HZ47.js} +56 -55
- package/dist/chunk-KCAR5DOB.js +52 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-O6AKZ4OH.js +0 -0
- package/dist/{chunk-FIFC4V2R.js → chunk-PPEBWOMJ.js} +91 -7
- package/dist/{compression-SQAIQ2UU.js → compression-YJLWEHCC.js} +1 -0
- package/dist/config-set-3IWEVZQ4.js +110 -0
- package/dist/{edit-JEFEK43H.js → edit-6QYAXVNU.js} +1 -0
- package/dist/{git-5T5TSQTX.js → git-DNQ5EELH.js} +1 -0
- package/dist/{github-DWRGWX6U.js → github-34T4QQIH.js} +1 -0
- package/dist/{glob-BI3P4C7Q.js → glob-XT43LEJ4.js} +1 -0
- package/dist/{grep-VZ3I5GNW.js → grep-T2CXYNRI.js} +1 -0
- package/dist/index.js +420 -298
- package/dist/{lsp-UPY6I3L7.js → lsp-JXQVU7NP.js} +1 -0
- package/dist/model-download-3NDKS3VM.js +176 -0
- package/dist/{notebook-FXJBTSPA.js → notebook-MFODW345.js} +1 -0
- package/dist/{ollama-bench-QQHBIG2D.js → ollama-bench-5V5CCOCQ.js} +6 -2
- package/dist/{ollama-launch-2ASVER3S.js → ollama-launch-P5KBK7AJ.js} +6 -2
- package/dist/{ollama-usage-2WPCZJJI.js → ollama-usage-3PROM2WC.js} +1 -0
- package/dist/{plugins-OG2P75K5.js → plugins-PNGRZLFW.js} +1 -0
- package/dist/{read-OVJG2XKW.js → read-B64XE7N3.js} +1 -0
- package/dist/{server-7UQKCB2Z.js → server-IGOZHW52.js} +17 -15
- package/dist/{session-index-SSGOOZXK.js → session-index-7FWEVP6E.js} +3 -2
- package/dist/{shell-4X545EVN.js → shell-BOZTHQUT.js} +1 -0
- package/dist/{task-OS3E5F3X.js → task-67G4KLYC.js} +1 -0
- package/dist/{tools-7WAWS6V4.js → tools-XWKCW4RN.js} +4 -3
- package/dist/{web-fetch-KNIV3Z3W.js → web-fetch-OTNDICGJ.js} +1 -0
- package/dist/{write-NNHLOTYK.js → write-ZOSB7I4J.js} +1 -0
- package/package.json +1 -1
- package/dist/auth-JQX6MHJG.js +0 -16
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-4HPRBCSY.js";
|
|
3
3
|
import {
|
|
4
4
|
MCPClient,
|
|
5
5
|
buildToolMap,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
pollPendingAgents,
|
|
12
12
|
setCurrentSurface,
|
|
13
13
|
spawnSubagent
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EPSOOCNB.js";
|
|
15
15
|
import {
|
|
16
16
|
Rollout,
|
|
17
17
|
generateSessionId,
|
|
@@ -20,11 +20,17 @@ import {
|
|
|
20
20
|
readIndex,
|
|
21
21
|
readRollout,
|
|
22
22
|
rebuildMessagesFromRollout
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-6NKRMZTX.js";
|
|
24
24
|
import {
|
|
25
25
|
autoCompress,
|
|
26
26
|
estimateTokens
|
|
27
27
|
} from "./chunk-PKZKVOAN.js";
|
|
28
|
+
import "./chunk-O6AKZ4OH.js";
|
|
29
|
+
import {
|
|
30
|
+
loadConfig,
|
|
31
|
+
persistConfigPatch
|
|
32
|
+
} from "./chunk-J66N6AFH.js";
|
|
33
|
+
import "./chunk-KCAR5DOB.js";
|
|
28
34
|
import {
|
|
29
35
|
ByokMissingApiKeyError,
|
|
30
36
|
ByokMissingBaseUrlError,
|
|
@@ -37,10 +43,9 @@ import {
|
|
|
37
43
|
listByokProviders,
|
|
38
44
|
modelSupportsImages,
|
|
39
45
|
parseByokRef,
|
|
40
|
-
readOllamaCreds,
|
|
41
46
|
resolveModel,
|
|
42
47
|
validateConfig
|
|
43
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-JXQ4HZ47.js";
|
|
44
49
|
import "./chunk-6CZCFY6H.js";
|
|
45
50
|
import "./chunk-6U3ZAGYA.js";
|
|
46
51
|
import "./chunk-FFB7GK3Y.js";
|
|
@@ -57,10 +62,12 @@ import {
|
|
|
57
62
|
registerCommand
|
|
58
63
|
} from "./chunk-3QUV4JEX.js";
|
|
59
64
|
import {
|
|
65
|
+
auth_exports,
|
|
60
66
|
clearCredentials,
|
|
67
|
+
init_auth,
|
|
61
68
|
loadCredentials,
|
|
62
69
|
login
|
|
63
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-PPEBWOMJ.js";
|
|
64
71
|
import "./chunk-CQMAVWLJ.js";
|
|
65
72
|
import "./chunk-O3WZW7GS.js";
|
|
66
73
|
import "./chunk-YAYPQTOU.js";
|
|
@@ -69,6 +76,9 @@ import {
|
|
|
69
76
|
} from "./chunk-C4CPDDMN.js";
|
|
70
77
|
import "./chunk-W4FAGQFL.js";
|
|
71
78
|
import "./chunk-FAULT7VE.js";
|
|
79
|
+
import {
|
|
80
|
+
__toCommonJS
|
|
81
|
+
} from "./chunk-KFQGP6VL.js";
|
|
72
82
|
|
|
73
83
|
// src/index.ts
|
|
74
84
|
import { Command } from "commander";
|
|
@@ -77,105 +87,8 @@ import ora7 from "ora";
|
|
|
77
87
|
import * as readline from "readline";
|
|
78
88
|
import * as nodePath2 from "path";
|
|
79
89
|
|
|
80
|
-
// src/config.ts
|
|
81
|
-
import fs from "fs/promises";
|
|
82
|
-
import path from "path";
|
|
83
|
-
var DEFAULT_MODEL = {
|
|
84
|
-
model: "notch-pyre",
|
|
85
|
-
temperature: 0.3
|
|
86
|
-
};
|
|
87
|
-
var DEFAULTS = {
|
|
88
|
-
models: { chat: DEFAULT_MODEL },
|
|
89
|
-
projectRoot: process.cwd(),
|
|
90
|
-
autoConfirm: false,
|
|
91
|
-
maxIterations: 25,
|
|
92
|
-
useRepoMap: true,
|
|
93
|
-
renderMarkdown: true,
|
|
94
|
-
enableMemory: true,
|
|
95
|
-
enableHooks: true,
|
|
96
|
-
permissionMode: "auto",
|
|
97
|
-
theme: "default"
|
|
98
|
-
};
|
|
99
|
-
async function loadConfig(overrides = {}) {
|
|
100
|
-
const config = { ...DEFAULTS, models: { chat: { ...DEFAULT_MODEL } } };
|
|
101
|
-
const configPath = path.resolve(config.projectRoot, ".notch.json");
|
|
102
|
-
try {
|
|
103
|
-
const raw = await fs.readFile(configPath, "utf-8");
|
|
104
|
-
const fileConfig = JSON.parse(raw);
|
|
105
|
-
if (fileConfig.model && (isValidModel(fileConfig.model) || isByokRef(fileConfig.model))) {
|
|
106
|
-
config.models.chat.model = fileConfig.model;
|
|
107
|
-
}
|
|
108
|
-
if (fileConfig.baseUrl) config.models.chat.baseUrl = fileConfig.baseUrl;
|
|
109
|
-
if (fileConfig.apiKey) config.models.chat.apiKey = fileConfig.apiKey;
|
|
110
|
-
if (fileConfig.byok && typeof fileConfig.byok === "object") {
|
|
111
|
-
const byok = fileConfig.byok;
|
|
112
|
-
config.byok = { ...byok };
|
|
113
|
-
if (byok.provider && findByokProvider(byok.provider === "custom" ? "__custom__" : byok.provider)) {
|
|
114
|
-
config.models.chat.byokProvider = byok.provider === "custom" ? "__custom__" : byok.provider;
|
|
115
|
-
if (byok.model) config.models.chat.model = byok.model;
|
|
116
|
-
if (byok.baseUrl) config.models.chat.baseUrl = byok.baseUrl;
|
|
117
|
-
if (byok.headers) {
|
|
118
|
-
config.models.chat.byokHeaders = { ...config.models.chat.byokHeaders, ...byok.headers };
|
|
119
|
-
}
|
|
120
|
-
if (byok.apiShape === "openai" || byok.apiShape === "anthropic") {
|
|
121
|
-
config.models.chat.byokApiShape = byok.apiShape;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (fileConfig.hybrid && typeof fileConfig.hybrid === "object") {
|
|
126
|
-
const hybrid = fileConfig.hybrid;
|
|
127
|
-
config.hybrid = hybrid;
|
|
128
|
-
}
|
|
129
|
-
if (fileConfig.maxIterations) config.maxIterations = fileConfig.maxIterations;
|
|
130
|
-
if (fileConfig.useRepoMap !== void 0) config.useRepoMap = fileConfig.useRepoMap;
|
|
131
|
-
if (fileConfig.temperature !== void 0) config.models.chat.temperature = fileConfig.temperature;
|
|
132
|
-
if (fileConfig.renderMarkdown !== void 0) config.renderMarkdown = fileConfig.renderMarkdown;
|
|
133
|
-
if (fileConfig.enableMemory !== void 0) config.enableMemory = fileConfig.enableMemory;
|
|
134
|
-
if (fileConfig.enableHooks !== void 0) config.enableHooks = fileConfig.enableHooks;
|
|
135
|
-
if (fileConfig.permissionMode) config.permissionMode = fileConfig.permissionMode;
|
|
136
|
-
if (fileConfig.shellTimeout) config.shellTimeout = fileConfig.shellTimeout;
|
|
137
|
-
if (fileConfig.theme) config.theme = fileConfig.theme;
|
|
138
|
-
} catch {
|
|
139
|
-
}
|
|
140
|
-
const activeProviderId = config.models.chat.byokProvider ?? (typeof config.models.chat.model === "string" && isByokRef(config.models.chat.model) ? config.models.chat.model.split(":", 1)[0] : void 0);
|
|
141
|
-
const isOllamaProvider = activeProviderId === "ollama" || activeProviderId === "ollama-cloud" || activeProviderId === "ollama-anthropic";
|
|
142
|
-
if (isOllamaProvider) {
|
|
143
|
-
if (!config.models.chat.apiKey && !process.env.OLLAMA_API_KEY) {
|
|
144
|
-
const ollamaCreds = await readOllamaCreds();
|
|
145
|
-
if (ollamaCreds?.apiKey) {
|
|
146
|
-
config.models.chat.apiKey = ollamaCreds.apiKey;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
const creds = await loadCredentials();
|
|
151
|
-
if (creds?.token) {
|
|
152
|
-
config.models.chat.apiKey = creds.token;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (process.env.NOTCH_MODEL) {
|
|
156
|
-
const envModel = process.env.NOTCH_MODEL;
|
|
157
|
-
if (isValidModel(envModel)) {
|
|
158
|
-
config.models.chat.model = envModel;
|
|
159
|
-
} else if (isByokRef(envModel)) {
|
|
160
|
-
config.models.chat.model = envModel;
|
|
161
|
-
config.models.chat.byokProvider = void 0;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (process.env.NOTCH_BASE_URL) {
|
|
165
|
-
config.models.chat.baseUrl = process.env.NOTCH_BASE_URL;
|
|
166
|
-
}
|
|
167
|
-
if (process.env.NOTCH_API_KEY) {
|
|
168
|
-
config.models.chat.apiKey = process.env.NOTCH_API_KEY;
|
|
169
|
-
}
|
|
170
|
-
if (config.models.chat.temperature !== void 0) {
|
|
171
|
-
config.models.chat.temperature = Math.max(0, Math.min(2, config.models.chat.temperature));
|
|
172
|
-
}
|
|
173
|
-
config.maxIterations = Math.max(1, Math.min(100, config.maxIterations));
|
|
174
|
-
return { ...config, ...overrides };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
90
|
// src/ui/image-input.ts
|
|
178
|
-
import { promises as
|
|
91
|
+
import { promises as fs } from "fs";
|
|
179
92
|
import * as nodePath from "path";
|
|
180
93
|
import * as os from "os";
|
|
181
94
|
function isImageLoadError(r) {
|
|
@@ -292,7 +205,7 @@ async function loadFromFile(spec, cwd) {
|
|
|
292
205
|
const abs = expandUserPath(spec, cwd);
|
|
293
206
|
let stat;
|
|
294
207
|
try {
|
|
295
|
-
stat = await
|
|
208
|
+
stat = await fs.stat(abs);
|
|
296
209
|
} catch (err) {
|
|
297
210
|
return { error: `Image file not found: ${abs} (${err?.code ?? err?.message ?? "unknown error"})` };
|
|
298
211
|
}
|
|
@@ -309,7 +222,7 @@ async function loadFromFile(spec, cwd) {
|
|
|
309
222
|
}
|
|
310
223
|
let buf;
|
|
311
224
|
try {
|
|
312
|
-
buf = await
|
|
225
|
+
buf = await fs.readFile(abs);
|
|
313
226
|
} catch (err) {
|
|
314
227
|
return { error: `Failed to read ${abs}: ${err?.message ?? String(err)}` };
|
|
315
228
|
}
|
|
@@ -386,19 +299,19 @@ function formatAttachmentStatus(att) {
|
|
|
386
299
|
|
|
387
300
|
// src/agent/loop.ts
|
|
388
301
|
import { streamText } from "ai";
|
|
389
|
-
import { promises as
|
|
302
|
+
import { promises as fs4 } from "fs";
|
|
390
303
|
import { execSync } from "child_process";
|
|
391
304
|
|
|
392
305
|
// src/context/project-instructions.ts
|
|
393
|
-
import
|
|
394
|
-
import
|
|
306
|
+
import fs2 from "fs/promises";
|
|
307
|
+
import path from "path";
|
|
395
308
|
import os2 from "os";
|
|
396
309
|
var INSTRUCTION_FILES = [".notch.md", "NOTCH.md", ".notch/instructions.md"];
|
|
397
310
|
async function loadProjectInstructions(projectRoot) {
|
|
398
311
|
const sources = [];
|
|
399
312
|
const homeDir = os2.homedir();
|
|
400
313
|
for (const file of INSTRUCTION_FILES) {
|
|
401
|
-
const globalPath =
|
|
314
|
+
const globalPath = path.join(homeDir, file);
|
|
402
315
|
const content = await safeRead(globalPath);
|
|
403
316
|
if (content) {
|
|
404
317
|
sources.push({ path: globalPath, content, scope: "global" });
|
|
@@ -406,7 +319,7 @@ async function loadProjectInstructions(projectRoot) {
|
|
|
406
319
|
}
|
|
407
320
|
}
|
|
408
321
|
for (const file of INSTRUCTION_FILES) {
|
|
409
|
-
const projectPath =
|
|
322
|
+
const projectPath = path.join(projectRoot, file);
|
|
410
323
|
const content = await safeRead(projectPath);
|
|
411
324
|
if (content) {
|
|
412
325
|
sources.push({ path: projectPath, content, scope: "project" });
|
|
@@ -428,7 +341,7 @@ ${sections.join("\n\n")}`;
|
|
|
428
341
|
}
|
|
429
342
|
async function safeRead(filePath) {
|
|
430
343
|
try {
|
|
431
|
-
const content = await
|
|
344
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
432
345
|
return content.trim() || null;
|
|
433
346
|
} catch {
|
|
434
347
|
return null;
|
|
@@ -436,19 +349,19 @@ async function safeRead(filePath) {
|
|
|
436
349
|
}
|
|
437
350
|
|
|
438
351
|
// src/memory/store.ts
|
|
439
|
-
import
|
|
440
|
-
import
|
|
352
|
+
import fs3 from "fs/promises";
|
|
353
|
+
import path2 from "path";
|
|
441
354
|
import os3 from "os";
|
|
442
|
-
var MEMORY_DIR =
|
|
443
|
-
var INDEX_FILE =
|
|
355
|
+
var MEMORY_DIR = path2.join(os3.homedir(), ".notch", "memory");
|
|
356
|
+
var INDEX_FILE = path2.join(MEMORY_DIR, "MEMORY.md");
|
|
444
357
|
async function ensureDir() {
|
|
445
|
-
await
|
|
358
|
+
await fs3.mkdir(MEMORY_DIR, { recursive: true });
|
|
446
359
|
}
|
|
447
360
|
async function saveMemory(memory) {
|
|
448
361
|
await ensureDir();
|
|
449
362
|
const slug = memory.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
450
363
|
const filename = `${memory.type}_${slug}.md`;
|
|
451
|
-
const filePath =
|
|
364
|
+
const filePath = path2.join(MEMORY_DIR, filename);
|
|
452
365
|
const fileContent = [
|
|
453
366
|
"---",
|
|
454
367
|
`name: ${memory.name}`,
|
|
@@ -459,18 +372,18 @@ async function saveMemory(memory) {
|
|
|
459
372
|
"",
|
|
460
373
|
memory.content
|
|
461
374
|
].join("\n");
|
|
462
|
-
await
|
|
375
|
+
await fs3.writeFile(filePath, fileContent, "utf-8");
|
|
463
376
|
await updateIndex();
|
|
464
377
|
return filename;
|
|
465
378
|
}
|
|
466
379
|
async function loadMemories() {
|
|
467
380
|
await ensureDir();
|
|
468
|
-
const files = await
|
|
381
|
+
const files = await fs3.readdir(MEMORY_DIR);
|
|
469
382
|
const memories = [];
|
|
470
383
|
for (const file of files) {
|
|
471
384
|
if (!file.endsWith(".md") || file === "MEMORY.md") continue;
|
|
472
385
|
try {
|
|
473
|
-
const content = await
|
|
386
|
+
const content = await fs3.readFile(path2.join(MEMORY_DIR, file), "utf-8");
|
|
474
387
|
const memory = parseMemoryFile(content, file);
|
|
475
388
|
if (memory) memories.push(memory);
|
|
476
389
|
} catch {
|
|
@@ -480,7 +393,7 @@ async function loadMemories() {
|
|
|
480
393
|
}
|
|
481
394
|
async function deleteMemory(filename) {
|
|
482
395
|
try {
|
|
483
|
-
await
|
|
396
|
+
await fs3.unlink(path2.join(MEMORY_DIR, filename));
|
|
484
397
|
await updateIndex();
|
|
485
398
|
return true;
|
|
486
399
|
} catch {
|
|
@@ -556,7 +469,7 @@ async function updateIndex() {
|
|
|
556
469
|
}
|
|
557
470
|
lines.push("");
|
|
558
471
|
}
|
|
559
|
-
await
|
|
472
|
+
await fs3.writeFile(INDEX_FILE, lines.join("\n"), "utf-8");
|
|
560
473
|
}
|
|
561
474
|
|
|
562
475
|
// src/agent/prompt-sections.ts
|
|
@@ -954,7 +867,7 @@ ${describeTools()}`),
|
|
|
954
867
|
lines.push(`- Platform: ${process.platform}`);
|
|
955
868
|
lines.push(`- Current date (UTC): ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`);
|
|
956
869
|
try {
|
|
957
|
-
const entries = await
|
|
870
|
+
const entries = await fs4.readdir(projectRoot);
|
|
958
871
|
const preview = entries.filter((e) => !e.startsWith(".")).slice(0, 30).join(", ");
|
|
959
872
|
if (preview) lines.push(`- Top-level entries: ${preview}`);
|
|
960
873
|
} catch {
|
|
@@ -1009,8 +922,8 @@ ${memoryStr}`, {
|
|
|
1009
922
|
}
|
|
1010
923
|
|
|
1011
924
|
// src/agent/checkpoints.ts
|
|
1012
|
-
import
|
|
1013
|
-
import
|
|
925
|
+
import fs5 from "fs/promises";
|
|
926
|
+
import path3 from "path";
|
|
1014
927
|
var CheckpointManager = class {
|
|
1015
928
|
checkpoints = [];
|
|
1016
929
|
nextId = 1;
|
|
@@ -1019,7 +932,7 @@ var CheckpointManager = class {
|
|
|
1019
932
|
async recordBefore(filePath) {
|
|
1020
933
|
if (this.pendingFiles.has(filePath)) return;
|
|
1021
934
|
try {
|
|
1022
|
-
const content = await
|
|
935
|
+
const content = await fs5.readFile(filePath, "utf-8");
|
|
1023
936
|
this.pendingFiles.set(filePath, content);
|
|
1024
937
|
} catch {
|
|
1025
938
|
this.pendingFiles.set(filePath, null);
|
|
@@ -1031,7 +944,7 @@ var CheckpointManager = class {
|
|
|
1031
944
|
for (const [filePath, before] of this.pendingFiles) {
|
|
1032
945
|
let after = null;
|
|
1033
946
|
try {
|
|
1034
|
-
after = await
|
|
947
|
+
after = await fs5.readFile(filePath, "utf-8");
|
|
1035
948
|
} catch {
|
|
1036
949
|
}
|
|
1037
950
|
files.push({ path: filePath, before, after });
|
|
@@ -1053,12 +966,12 @@ var CheckpointManager = class {
|
|
|
1053
966
|
for (const snap of checkpoint.files) {
|
|
1054
967
|
if (snap.before === null) {
|
|
1055
968
|
try {
|
|
1056
|
-
await
|
|
969
|
+
await fs5.unlink(snap.path);
|
|
1057
970
|
} catch {
|
|
1058
971
|
}
|
|
1059
972
|
} else {
|
|
1060
|
-
await
|
|
1061
|
-
await
|
|
973
|
+
await fs5.mkdir(path3.dirname(snap.path), { recursive: true });
|
|
974
|
+
await fs5.writeFile(snap.path, snap.before, "utf-8");
|
|
1062
975
|
}
|
|
1063
976
|
}
|
|
1064
977
|
return checkpoint;
|
|
@@ -1090,8 +1003,8 @@ var CheckpointManager = class {
|
|
|
1090
1003
|
}
|
|
1091
1004
|
}
|
|
1092
1005
|
}
|
|
1093
|
-
return Array.from(fileMap.entries()).map(([
|
|
1094
|
-
path:
|
|
1006
|
+
return Array.from(fileMap.entries()).map(([path20, { before, after }]) => ({
|
|
1007
|
+
path: path20,
|
|
1095
1008
|
before,
|
|
1096
1009
|
after
|
|
1097
1010
|
}));
|
|
@@ -1489,14 +1402,14 @@ var CostTracker = class {
|
|
|
1489
1402
|
};
|
|
1490
1403
|
|
|
1491
1404
|
// src/agent/ralph.ts
|
|
1492
|
-
import
|
|
1493
|
-
import
|
|
1405
|
+
import fs7 from "fs/promises";
|
|
1406
|
+
import path5 from "path";
|
|
1494
1407
|
import chalk4 from "chalk";
|
|
1495
1408
|
import { generateText as generateText2, streamText as streamText2 } from "ai";
|
|
1496
1409
|
|
|
1497
1410
|
// src/context/repo-map.ts
|
|
1498
|
-
import
|
|
1499
|
-
import
|
|
1411
|
+
import fs6 from "fs/promises";
|
|
1412
|
+
import path4 from "path";
|
|
1500
1413
|
import { glob } from "glob";
|
|
1501
1414
|
var PATTERNS = {
|
|
1502
1415
|
ts: [
|
|
@@ -1527,7 +1440,7 @@ var PATTERNS = {
|
|
|
1527
1440
|
};
|
|
1528
1441
|
var IMPORT_PATTERN = /(?:import|from)\s+['"]([^'"]+)['"]/g;
|
|
1529
1442
|
function getPatterns(filePath) {
|
|
1530
|
-
const ext2 =
|
|
1443
|
+
const ext2 = path4.extname(filePath).slice(1);
|
|
1531
1444
|
if (["ts", "tsx", "mts", "cts"].includes(ext2)) return PATTERNS.ts;
|
|
1532
1445
|
if (["js", "jsx", "mjs", "cjs"].includes(ext2)) return PATTERNS.js;
|
|
1533
1446
|
if (ext2 === "py") return PATTERNS.py;
|
|
@@ -1607,9 +1520,9 @@ async function buildRepoMap(root) {
|
|
|
1607
1520
|
const entries = [];
|
|
1608
1521
|
files.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
1609
1522
|
for (const file of files.slice(0, 300)) {
|
|
1610
|
-
const fullPath =
|
|
1523
|
+
const fullPath = path4.resolve(root, file);
|
|
1611
1524
|
try {
|
|
1612
|
-
const content = await
|
|
1525
|
+
const content = await fs6.readFile(fullPath, "utf-8");
|
|
1613
1526
|
const lines = content.split("\n").length;
|
|
1614
1527
|
const patterns = getPatterns(file);
|
|
1615
1528
|
const symbols = extractSymbols(content, patterns);
|
|
@@ -1711,11 +1624,11 @@ ${repoContext || "(empty project)"}`;
|
|
|
1711
1624
|
async function savePlan(plan, cwd) {
|
|
1712
1625
|
plan.updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
1713
1626
|
plan.completedCount = plan.tasks.filter((t) => t.status === "done").length;
|
|
1714
|
-
await
|
|
1627
|
+
await fs7.writeFile(path5.join(cwd, PLAN_FILE), JSON.stringify(plan, null, 2), "utf-8");
|
|
1715
1628
|
}
|
|
1716
1629
|
async function loadPlan(cwd) {
|
|
1717
1630
|
try {
|
|
1718
|
-
const raw = await
|
|
1631
|
+
const raw = await fs7.readFile(path5.join(cwd, PLAN_FILE), "utf-8");
|
|
1719
1632
|
return JSON.parse(raw);
|
|
1720
1633
|
} catch {
|
|
1721
1634
|
return null;
|
|
@@ -1723,7 +1636,7 @@ async function loadPlan(cwd) {
|
|
|
1723
1636
|
}
|
|
1724
1637
|
async function deletePlan(cwd) {
|
|
1725
1638
|
try {
|
|
1726
|
-
await
|
|
1639
|
+
await fs7.unlink(path5.join(cwd, PLAN_FILE));
|
|
1727
1640
|
} catch {
|
|
1728
1641
|
}
|
|
1729
1642
|
}
|
|
@@ -1906,8 +1819,8 @@ function formatRalphStatus(plan) {
|
|
|
1906
1819
|
}
|
|
1907
1820
|
|
|
1908
1821
|
// src/context/references.ts
|
|
1909
|
-
import
|
|
1910
|
-
import
|
|
1822
|
+
import fs8 from "fs/promises";
|
|
1823
|
+
import path6 from "path";
|
|
1911
1824
|
import { glob as glob2 } from "glob";
|
|
1912
1825
|
async function resolveReferences(input, cwd) {
|
|
1913
1826
|
const references = [];
|
|
@@ -1950,9 +1863,9 @@ ${truncated}
|
|
|
1950
1863
|
return sections.join("\n\n") + "\n\n";
|
|
1951
1864
|
}
|
|
1952
1865
|
async function resolveFile(ref, cwd) {
|
|
1953
|
-
const filePath =
|
|
1866
|
+
const filePath = path6.isAbsolute(ref) ? ref : path6.resolve(cwd, ref);
|
|
1954
1867
|
try {
|
|
1955
|
-
const content = await
|
|
1868
|
+
const content = await fs8.readFile(filePath, "utf-8");
|
|
1956
1869
|
const lines = content.split("\n");
|
|
1957
1870
|
const numbered = lines.map((line, i) => `${String(i + 1).padStart(4)} | ${line}`).join("\n");
|
|
1958
1871
|
return {
|
|
@@ -2677,17 +2590,17 @@ function formatTokens(n) {
|
|
|
2677
2590
|
}
|
|
2678
2591
|
|
|
2679
2592
|
// src/ui/update-checker.ts
|
|
2680
|
-
import
|
|
2681
|
-
import
|
|
2593
|
+
import fs9 from "fs/promises";
|
|
2594
|
+
import path7 from "path";
|
|
2682
2595
|
import os4 from "os";
|
|
2683
2596
|
import { execSync as execSync2, spawnSync } from "child_process";
|
|
2684
2597
|
var PACKAGE_NAME = "@freesyntax/notch-cli";
|
|
2685
2598
|
var CHECK_INTERVAL_HOURS = 4;
|
|
2686
2599
|
var CHECK_INTERVAL_MS = CHECK_INTERVAL_HOURS * 60 * 60 * 1e3;
|
|
2687
|
-
var NOTCH_DIR =
|
|
2688
|
-
var CACHE_FILE =
|
|
2689
|
-
var LOG_FILE =
|
|
2690
|
-
var CONFIG_FILE =
|
|
2600
|
+
var NOTCH_DIR = path7.join(os4.homedir(), ".notch");
|
|
2601
|
+
var CACHE_FILE = path7.join(NOTCH_DIR, "update-check.json");
|
|
2602
|
+
var LOG_FILE = path7.join(NOTCH_DIR, "update-log.txt");
|
|
2603
|
+
var CONFIG_FILE = path7.join(NOTCH_DIR, "config.json");
|
|
2691
2604
|
var MAX_LOG_LINES = 500;
|
|
2692
2605
|
var REGISTRY_BASE = "https://registry.npmjs.org";
|
|
2693
2606
|
var FETCH_TIMEOUT_MS2 = 5e3;
|
|
@@ -2822,7 +2735,7 @@ async function saveChannel(channel) {
|
|
|
2822
2735
|
}
|
|
2823
2736
|
async function readUserConfig() {
|
|
2824
2737
|
try {
|
|
2825
|
-
const raw = await
|
|
2738
|
+
const raw = await fs9.readFile(CONFIG_FILE, "utf-8");
|
|
2826
2739
|
const parsed = JSON.parse(raw);
|
|
2827
2740
|
return typeof parsed === "object" && parsed ? parsed : null;
|
|
2828
2741
|
} catch {
|
|
@@ -2831,7 +2744,7 @@ async function readUserConfig() {
|
|
|
2831
2744
|
}
|
|
2832
2745
|
async function loadCache() {
|
|
2833
2746
|
try {
|
|
2834
|
-
const raw = await
|
|
2747
|
+
const raw = await fs9.readFile(CACHE_FILE, "utf-8");
|
|
2835
2748
|
const parsed = JSON.parse(raw);
|
|
2836
2749
|
if (parsed && typeof parsed === "object" && typeof parsed.lastCheck === "number" && (parsed.latestVersion === null || typeof parsed.latestVersion === "string")) {
|
|
2837
2750
|
const channel = isValidChannel(parsed.channel) ? parsed.channel : "latest";
|
|
@@ -2858,7 +2771,7 @@ async function detectInstallLocation() {
|
|
|
2858
2771
|
if (!entry) return "unknown";
|
|
2859
2772
|
let entryReal;
|
|
2860
2773
|
try {
|
|
2861
|
-
entryReal = await
|
|
2774
|
+
entryReal = await fs9.realpath(entry);
|
|
2862
2775
|
} catch {
|
|
2863
2776
|
entryReal = entry;
|
|
2864
2777
|
}
|
|
@@ -2867,11 +2780,11 @@ async function detectInstallLocation() {
|
|
|
2867
2780
|
return "global";
|
|
2868
2781
|
}
|
|
2869
2782
|
let dir = process.cwd();
|
|
2870
|
-
const root =
|
|
2783
|
+
const root = path7.parse(dir).root;
|
|
2871
2784
|
while (dir !== root) {
|
|
2872
|
-
const localNM =
|
|
2785
|
+
const localNM = path7.join(dir, "node_modules");
|
|
2873
2786
|
if (pathIsInside(entryReal, localNM)) return "local";
|
|
2874
|
-
const parent =
|
|
2787
|
+
const parent = path7.dirname(dir);
|
|
2875
2788
|
if (parent === dir) break;
|
|
2876
2789
|
dir = parent;
|
|
2877
2790
|
}
|
|
@@ -2881,8 +2794,8 @@ async function detectInstallLocation() {
|
|
|
2881
2794
|
}
|
|
2882
2795
|
function pathIsInside(child, parent) {
|
|
2883
2796
|
if (!child || !parent) return false;
|
|
2884
|
-
const rel =
|
|
2885
|
-
return !!rel && !rel.startsWith("..") && !
|
|
2797
|
+
const rel = path7.relative(path7.resolve(parent), path7.resolve(child));
|
|
2798
|
+
return !!rel && !rel.startsWith("..") && !path7.isAbsolute(rel);
|
|
2886
2799
|
}
|
|
2887
2800
|
async function tryExec(cmd) {
|
|
2888
2801
|
try {
|
|
@@ -2940,7 +2853,7 @@ function splitPre(v) {
|
|
|
2940
2853
|
return [v.slice(0, i), v.slice(i + 1)];
|
|
2941
2854
|
}
|
|
2942
2855
|
async function ensureNotchDir() {
|
|
2943
|
-
await
|
|
2856
|
+
await fs9.mkdir(NOTCH_DIR, { recursive: true });
|
|
2944
2857
|
}
|
|
2945
2858
|
async function appendLog(message) {
|
|
2946
2859
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}`;
|
|
@@ -2951,7 +2864,7 @@ async function appendLogRaw(line) {
|
|
|
2951
2864
|
await ensureNotchDir();
|
|
2952
2865
|
let existing = "";
|
|
2953
2866
|
try {
|
|
2954
|
-
existing = await
|
|
2867
|
+
existing = await fs9.readFile(LOG_FILE, "utf-8");
|
|
2955
2868
|
} catch {
|
|
2956
2869
|
existing = "";
|
|
2957
2870
|
}
|
|
@@ -2964,16 +2877,16 @@ async function appendLogRaw(line) {
|
|
|
2964
2877
|
}
|
|
2965
2878
|
async function atomicWrite(file, contents) {
|
|
2966
2879
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
2967
|
-
await
|
|
2880
|
+
await fs9.writeFile(tmp, contents, "utf-8");
|
|
2968
2881
|
try {
|
|
2969
|
-
await
|
|
2882
|
+
await fs9.rename(tmp, file);
|
|
2970
2883
|
} catch (err) {
|
|
2971
2884
|
try {
|
|
2972
|
-
await
|
|
2973
|
-
await
|
|
2885
|
+
await fs9.copyFile(tmp, file);
|
|
2886
|
+
await fs9.unlink(tmp).catch(() => {
|
|
2974
2887
|
});
|
|
2975
2888
|
} catch {
|
|
2976
|
-
await
|
|
2889
|
+
await fs9.unlink(tmp).catch(() => {
|
|
2977
2890
|
});
|
|
2978
2891
|
throw err;
|
|
2979
2892
|
}
|
|
@@ -2995,16 +2908,16 @@ function indent(block, spaces = 2) {
|
|
|
2995
2908
|
// src/commands/update.ts
|
|
2996
2909
|
import chalk7 from "chalk";
|
|
2997
2910
|
import { createRequire } from "module";
|
|
2998
|
-
import
|
|
2911
|
+
import path8 from "path";
|
|
2999
2912
|
import { fileURLToPath } from "url";
|
|
3000
2913
|
var _require = createRequire(import.meta.url);
|
|
3001
2914
|
function resolveVersion() {
|
|
3002
2915
|
try {
|
|
3003
2916
|
const entry = process.argv[1];
|
|
3004
|
-
let dir = entry ?
|
|
3005
|
-
const root =
|
|
2917
|
+
let dir = entry ? path8.dirname(entry) : fileURLToPath(new URL(".", import.meta.url));
|
|
2918
|
+
const root = path8.parse(dir).root;
|
|
3006
2919
|
while (dir && dir !== root) {
|
|
3007
|
-
const candidate =
|
|
2920
|
+
const candidate = path8.join(dir, "package.json");
|
|
3008
2921
|
try {
|
|
3009
2922
|
const pkg = _require(candidate);
|
|
3010
2923
|
if (pkg && pkg.name === PACKAGE_NAME && typeof pkg.version === "string") {
|
|
@@ -3012,7 +2925,7 @@ function resolveVersion() {
|
|
|
3012
2925
|
}
|
|
3013
2926
|
} catch {
|
|
3014
2927
|
}
|
|
3015
|
-
const parent =
|
|
2928
|
+
const parent = path8.dirname(dir);
|
|
3016
2929
|
if (parent === dir) break;
|
|
3017
2930
|
dir = parent;
|
|
3018
2931
|
}
|
|
@@ -3134,8 +3047,8 @@ async function runUpdateCli(argv) {
|
|
|
3134
3047
|
}
|
|
3135
3048
|
|
|
3136
3049
|
// src/permissions/index.ts
|
|
3137
|
-
import
|
|
3138
|
-
import
|
|
3050
|
+
import fs10 from "fs/promises";
|
|
3051
|
+
import path10 from "path";
|
|
3139
3052
|
import os5 from "os";
|
|
3140
3053
|
|
|
3141
3054
|
// node_modules/balanced-match/dist/esm/index.js
|
|
@@ -4196,11 +4109,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
4196
4109
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
4197
4110
|
};
|
|
4198
4111
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
4199
|
-
var
|
|
4112
|
+
var path9 = {
|
|
4200
4113
|
win32: { sep: "\\" },
|
|
4201
4114
|
posix: { sep: "/" }
|
|
4202
4115
|
};
|
|
4203
|
-
var sep = defaultPlatform === "win32" ?
|
|
4116
|
+
var sep = defaultPlatform === "win32" ? path9.win32.sep : path9.posix.sep;
|
|
4204
4117
|
minimatch.sep = sep;
|
|
4205
4118
|
var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
|
|
4206
4119
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -4997,11 +4910,11 @@ var DEFAULT_PERMISSIONS = {
|
|
|
4997
4910
|
denyWriteGlobs: [...DEFAULT_DENY_WRITE_GLOBS]
|
|
4998
4911
|
};
|
|
4999
4912
|
async function loadPermissions(projectRoot) {
|
|
5000
|
-
const projectPath =
|
|
5001
|
-
const globalPath =
|
|
4913
|
+
const projectPath = path10.join(projectRoot, ".notch.json");
|
|
4914
|
+
const globalPath = path10.join(os5.homedir(), ".notch", "permissions.json");
|
|
5002
4915
|
let config = { ...DEFAULT_PERMISSIONS };
|
|
5003
4916
|
try {
|
|
5004
|
-
const raw = await
|
|
4917
|
+
const raw = await fs10.readFile(globalPath, "utf-8");
|
|
5005
4918
|
const parsed = JSON.parse(raw);
|
|
5006
4919
|
if (parsed.permissions) {
|
|
5007
4920
|
config = mergePermissions(config, parsed.permissions);
|
|
@@ -5009,7 +4922,7 @@ async function loadPermissions(projectRoot) {
|
|
|
5009
4922
|
} catch {
|
|
5010
4923
|
}
|
|
5011
4924
|
try {
|
|
5012
|
-
const raw = await
|
|
4925
|
+
const raw = await fs10.readFile(projectPath, "utf-8");
|
|
5013
4926
|
const parsed = JSON.parse(raw);
|
|
5014
4927
|
if (parsed.permissions) {
|
|
5015
4928
|
config = mergePermissions(config, parsed.permissions);
|
|
@@ -5149,17 +5062,17 @@ function mergePermissions(base, override) {
|
|
|
5149
5062
|
|
|
5150
5063
|
// src/hooks/index.ts
|
|
5151
5064
|
import { execSync as execSync3 } from "child_process";
|
|
5152
|
-
import
|
|
5065
|
+
import fs11 from "fs/promises";
|
|
5153
5066
|
import { watch } from "fs";
|
|
5154
|
-
import
|
|
5067
|
+
import path11 from "path";
|
|
5155
5068
|
import os6 from "os";
|
|
5156
5069
|
import crypto from "crypto";
|
|
5157
|
-
var TRUST_STORE_PATH =
|
|
5070
|
+
var TRUST_STORE_PATH = path11.join(os6.homedir(), ".notch", "trusted-projects.json");
|
|
5158
5071
|
async function isTrustedProject(projectRoot, raw) {
|
|
5159
5072
|
const fingerprint = crypto.createHash("sha256").update(raw).digest("hex");
|
|
5160
|
-
const key =
|
|
5073
|
+
const key = path11.resolve(projectRoot);
|
|
5161
5074
|
try {
|
|
5162
|
-
const store = JSON.parse(await
|
|
5075
|
+
const store = JSON.parse(await fs11.readFile(TRUST_STORE_PATH, "utf-8"));
|
|
5163
5076
|
return store[key] === fingerprint;
|
|
5164
5077
|
} catch {
|
|
5165
5078
|
return false;
|
|
@@ -5167,30 +5080,30 @@ async function isTrustedProject(projectRoot, raw) {
|
|
|
5167
5080
|
}
|
|
5168
5081
|
async function trustProject(projectRoot, raw) {
|
|
5169
5082
|
const fingerprint = crypto.createHash("sha256").update(raw).digest("hex");
|
|
5170
|
-
const key =
|
|
5083
|
+
const key = path11.resolve(projectRoot);
|
|
5171
5084
|
let store = {};
|
|
5172
5085
|
try {
|
|
5173
|
-
store = JSON.parse(await
|
|
5086
|
+
store = JSON.parse(await fs11.readFile(TRUST_STORE_PATH, "utf-8"));
|
|
5174
5087
|
} catch {
|
|
5175
5088
|
}
|
|
5176
5089
|
store[key] = fingerprint;
|
|
5177
|
-
await
|
|
5178
|
-
await
|
|
5090
|
+
await fs11.mkdir(path11.dirname(TRUST_STORE_PATH), { recursive: true });
|
|
5091
|
+
await fs11.writeFile(TRUST_STORE_PATH, JSON.stringify(store, null, 2));
|
|
5179
5092
|
}
|
|
5180
5093
|
async function loadHooks(projectRoot, promptTrust) {
|
|
5181
5094
|
const hooks = [];
|
|
5182
|
-
const globalPath =
|
|
5095
|
+
const globalPath = path11.join(os6.homedir(), ".notch", "hooks.json");
|
|
5183
5096
|
try {
|
|
5184
|
-
const raw = await
|
|
5097
|
+
const raw = await fs11.readFile(globalPath, "utf-8");
|
|
5185
5098
|
const parsed = JSON.parse(raw);
|
|
5186
5099
|
if (Array.isArray(parsed.hooks)) {
|
|
5187
5100
|
hooks.push(...parsed.hooks);
|
|
5188
5101
|
}
|
|
5189
5102
|
} catch {
|
|
5190
5103
|
}
|
|
5191
|
-
const projectPath =
|
|
5104
|
+
const projectPath = path11.join(projectRoot, ".notch.json");
|
|
5192
5105
|
try {
|
|
5193
|
-
const raw = await
|
|
5106
|
+
const raw = await fs11.readFile(projectPath, "utf-8");
|
|
5194
5107
|
const parsed = JSON.parse(raw);
|
|
5195
5108
|
if (Array.isArray(parsed.hooks) && parsed.hooks.length > 0) {
|
|
5196
5109
|
const alreadyTrusted = await isTrustedProject(projectRoot, raw);
|
|
@@ -5285,7 +5198,7 @@ function startFileWatcher(projectRoot, hookConfig, onHookResult) {
|
|
|
5285
5198
|
if (existing) clearTimeout(existing);
|
|
5286
5199
|
pending.set(filename, setTimeout(async () => {
|
|
5287
5200
|
pending.delete(filename);
|
|
5288
|
-
const filePath =
|
|
5201
|
+
const filePath = path11.join(projectRoot, filename);
|
|
5289
5202
|
const context = { cwd: projectRoot, file: filePath };
|
|
5290
5203
|
const { results } = await runHooks(hookConfig, "file-changed", context);
|
|
5291
5204
|
onHookResult?.("file-changed", results);
|
|
@@ -5305,8 +5218,8 @@ function startFileWatcher(projectRoot, hookConfig, onHookResult) {
|
|
|
5305
5218
|
}
|
|
5306
5219
|
|
|
5307
5220
|
// src/session/index.ts
|
|
5308
|
-
import
|
|
5309
|
-
import
|
|
5221
|
+
import fs12 from "fs/promises";
|
|
5222
|
+
import path12 from "path";
|
|
5310
5223
|
import os7 from "os";
|
|
5311
5224
|
|
|
5312
5225
|
// src/session/fork.ts
|
|
@@ -5316,13 +5229,13 @@ import fsp from "fs/promises";
|
|
|
5316
5229
|
import fsp2 from "fs/promises";
|
|
5317
5230
|
|
|
5318
5231
|
// src/session/index.ts
|
|
5319
|
-
var SESSION_DIR =
|
|
5232
|
+
var SESSION_DIR = path12.join(os7.homedir(), ".notch", "sessions");
|
|
5320
5233
|
var MAX_SESSIONS = 20;
|
|
5321
5234
|
async function ensureDir2() {
|
|
5322
|
-
await
|
|
5235
|
+
await fs12.mkdir(SESSION_DIR, { recursive: true });
|
|
5323
5236
|
}
|
|
5324
5237
|
function sessionPath(id) {
|
|
5325
|
-
return
|
|
5238
|
+
return path12.join(SESSION_DIR, `${id}.json`);
|
|
5326
5239
|
}
|
|
5327
5240
|
function generateId() {
|
|
5328
5241
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -5349,13 +5262,13 @@ async function saveSession(messages, project, model, existingId) {
|
|
|
5349
5262
|
},
|
|
5350
5263
|
messages
|
|
5351
5264
|
};
|
|
5352
|
-
await
|
|
5265
|
+
await fs12.writeFile(sessionPath(id), JSON.stringify(session, null, 2), "utf-8");
|
|
5353
5266
|
await pruneOldSessions();
|
|
5354
5267
|
return id;
|
|
5355
5268
|
}
|
|
5356
5269
|
async function loadSession(id) {
|
|
5357
5270
|
try {
|
|
5358
|
-
const raw = await
|
|
5271
|
+
const raw = await fs12.readFile(sessionPath(id), "utf-8");
|
|
5359
5272
|
return JSON.parse(raw);
|
|
5360
5273
|
} catch {
|
|
5361
5274
|
return null;
|
|
@@ -5363,12 +5276,12 @@ async function loadSession(id) {
|
|
|
5363
5276
|
}
|
|
5364
5277
|
async function listSessions() {
|
|
5365
5278
|
await ensureDir2();
|
|
5366
|
-
const files = await
|
|
5279
|
+
const files = await fs12.readdir(SESSION_DIR);
|
|
5367
5280
|
const sessions = [];
|
|
5368
5281
|
for (const file of files) {
|
|
5369
5282
|
if (!file.endsWith(".json")) continue;
|
|
5370
5283
|
try {
|
|
5371
|
-
const raw = await
|
|
5284
|
+
const raw = await fs12.readFile(path12.join(SESSION_DIR, file), "utf-8");
|
|
5372
5285
|
const session = JSON.parse(raw);
|
|
5373
5286
|
sessions.push(session.meta);
|
|
5374
5287
|
} catch {
|
|
@@ -5384,7 +5297,7 @@ async function loadLastSession(project) {
|
|
|
5384
5297
|
}
|
|
5385
5298
|
async function deleteSession(id) {
|
|
5386
5299
|
try {
|
|
5387
|
-
await
|
|
5300
|
+
await fs12.unlink(sessionPath(id));
|
|
5388
5301
|
return true;
|
|
5389
5302
|
} catch {
|
|
5390
5303
|
return false;
|
|
@@ -5438,17 +5351,17 @@ async function exportSession(messages, outputPath, meta) {
|
|
|
5438
5351
|
lines.push("");
|
|
5439
5352
|
}
|
|
5440
5353
|
}
|
|
5441
|
-
await
|
|
5354
|
+
await fs12.writeFile(outputPath, lines.join("\n"), "utf-8");
|
|
5442
5355
|
return outputPath;
|
|
5443
5356
|
}
|
|
5444
5357
|
|
|
5445
5358
|
// src/init.ts
|
|
5446
|
-
import
|
|
5447
|
-
import
|
|
5359
|
+
import fs13 from "fs/promises";
|
|
5360
|
+
import path13 from "path";
|
|
5448
5361
|
import chalk8 from "chalk";
|
|
5449
5362
|
async function fileExists(p) {
|
|
5450
5363
|
try {
|
|
5451
|
-
await
|
|
5364
|
+
await fs13.access(p);
|
|
5452
5365
|
return true;
|
|
5453
5366
|
} catch {
|
|
5454
5367
|
return false;
|
|
@@ -5456,18 +5369,18 @@ async function fileExists(p) {
|
|
|
5456
5369
|
}
|
|
5457
5370
|
async function writeIfMissing(p, content, ctx) {
|
|
5458
5371
|
if (await fileExists(p)) {
|
|
5459
|
-
ctx.log(chalk8.gray(` Skipped ${
|
|
5372
|
+
ctx.log(chalk8.gray(` Skipped ${path13.relative(ctx.projectRoot, p)} (already exists)`));
|
|
5460
5373
|
return false;
|
|
5461
5374
|
}
|
|
5462
|
-
await
|
|
5463
|
-
await
|
|
5464
|
-
ctx.log(chalk8.green(` Created ${
|
|
5375
|
+
await fs13.mkdir(path13.dirname(p), { recursive: true });
|
|
5376
|
+
await fs13.writeFile(p, content, "utf-8");
|
|
5377
|
+
ctx.log(chalk8.green(` Created ${path13.relative(ctx.projectRoot, p)}`));
|
|
5465
5378
|
return true;
|
|
5466
5379
|
}
|
|
5467
5380
|
async function patchJson(p, patch, ctx) {
|
|
5468
5381
|
let current = {};
|
|
5469
5382
|
if (await fileExists(p)) {
|
|
5470
|
-
const raw = await
|
|
5383
|
+
const raw = await fs13.readFile(p, "utf-8");
|
|
5471
5384
|
try {
|
|
5472
5385
|
current = JSON.parse(raw);
|
|
5473
5386
|
} catch {
|
|
@@ -5475,18 +5388,18 @@ async function patchJson(p, patch, ctx) {
|
|
|
5475
5388
|
}
|
|
5476
5389
|
}
|
|
5477
5390
|
const next = patch(current);
|
|
5478
|
-
await
|
|
5479
|
-
ctx.log(chalk8.green(` Patched ${
|
|
5391
|
+
await fs13.writeFile(p, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
5392
|
+
ctx.log(chalk8.green(` Patched ${path13.relative(ctx.projectRoot, p)}`));
|
|
5480
5393
|
}
|
|
5481
5394
|
async function ensureGitignoreEntries(p, entries, sectionTitle, ctx) {
|
|
5482
5395
|
if (!await fileExists(p)) return;
|
|
5483
|
-
const current = await
|
|
5396
|
+
const current = await fs13.readFile(p, "utf-8");
|
|
5484
5397
|
const missing = entries.filter((e) => !current.includes(e));
|
|
5485
5398
|
if (missing.length === 0) return;
|
|
5486
5399
|
const append = `
|
|
5487
5400
|
# ${sectionTitle}
|
|
5488
5401
|
` + missing.join("\n") + "\n";
|
|
5489
|
-
await
|
|
5402
|
+
await fs13.appendFile(p, append, "utf-8");
|
|
5490
5403
|
ctx.log(chalk8.green(` Updated .gitignore (+${missing.length})`));
|
|
5491
5404
|
}
|
|
5492
5405
|
var DEFAULT_CONFIG = {
|
|
@@ -5536,17 +5449,17 @@ var baseInstaller = {
|
|
|
5536
5449
|
required: true,
|
|
5537
5450
|
async run(ctx) {
|
|
5538
5451
|
await writeIfMissing(
|
|
5539
|
-
|
|
5452
|
+
path13.join(ctx.projectRoot, ".notch.json"),
|
|
5540
5453
|
JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
|
|
5541
5454
|
ctx
|
|
5542
5455
|
);
|
|
5543
5456
|
await writeIfMissing(
|
|
5544
|
-
|
|
5457
|
+
path13.join(ctx.projectRoot, ".notch.md"),
|
|
5545
5458
|
DEFAULT_INSTRUCTIONS,
|
|
5546
5459
|
ctx
|
|
5547
5460
|
);
|
|
5548
5461
|
await ensureGitignoreEntries(
|
|
5549
|
-
|
|
5462
|
+
path13.join(ctx.projectRoot, ".gitignore"),
|
|
5550
5463
|
[".notch.json"],
|
|
5551
5464
|
"Notch CLI",
|
|
5552
5465
|
ctx
|
|
@@ -5559,7 +5472,7 @@ var mcpInstaller = {
|
|
|
5559
5472
|
description: "Add the mcpServers block to .notch.json (example: github)",
|
|
5560
5473
|
async run(ctx) {
|
|
5561
5474
|
await patchJson(
|
|
5562
|
-
|
|
5475
|
+
path13.join(ctx.projectRoot, ".notch.json"),
|
|
5563
5476
|
(cur) => ({
|
|
5564
5477
|
...cur,
|
|
5565
5478
|
mcpServers: cur.mcpServers ?? {
|
|
@@ -5580,7 +5493,7 @@ var hooksInstaller = {
|
|
|
5580
5493
|
description: "Add hooks array scaffold to .notch.json",
|
|
5581
5494
|
async run(ctx) {
|
|
5582
5495
|
await patchJson(
|
|
5583
|
-
|
|
5496
|
+
path13.join(ctx.projectRoot, ".notch.json"),
|
|
5584
5497
|
(cur) => ({
|
|
5585
5498
|
...cur,
|
|
5586
5499
|
hooks: cur.hooks ?? [
|
|
@@ -5596,10 +5509,10 @@ var vestaInstaller = {
|
|
|
5596
5509
|
label: "Vesta agents",
|
|
5597
5510
|
description: "Scaffold .notch/agents/ directory for Vesta agent configs",
|
|
5598
5511
|
async run(ctx) {
|
|
5599
|
-
const dir =
|
|
5600
|
-
await
|
|
5512
|
+
const dir = path13.join(ctx.projectRoot, ".notch", "agents");
|
|
5513
|
+
await fs13.mkdir(dir, { recursive: true });
|
|
5601
5514
|
await writeIfMissing(
|
|
5602
|
-
|
|
5515
|
+
path13.join(dir, "README.md"),
|
|
5603
5516
|
`# Vesta Agents
|
|
5604
5517
|
|
|
5605
5518
|
Drop agent JSON/TOML configs in this directory. Notch will load them at startup.
|
|
@@ -5614,7 +5527,7 @@ var oneSecInstaller = {
|
|
|
5614
5527
|
description: "Enable 1-SEC opt-in and add the block to .notch.json",
|
|
5615
5528
|
async run(ctx) {
|
|
5616
5529
|
await patchJson(
|
|
5617
|
-
|
|
5530
|
+
path13.join(ctx.projectRoot, ".notch.json"),
|
|
5618
5531
|
(cur) => ({
|
|
5619
5532
|
...cur,
|
|
5620
5533
|
security: cur.security ?? {
|
|
@@ -5633,7 +5546,7 @@ var telemetryInstaller = {
|
|
|
5633
5546
|
description: "Enable opt-in anonymous usage stats",
|
|
5634
5547
|
async run(ctx) {
|
|
5635
5548
|
await patchJson(
|
|
5636
|
-
|
|
5549
|
+
path13.join(ctx.projectRoot, ".notch.json"),
|
|
5637
5550
|
(cur) => ({ ...cur, telemetry: { enabled: true, anonymous: true } }),
|
|
5638
5551
|
ctx
|
|
5639
5552
|
);
|
|
@@ -5645,7 +5558,7 @@ var agentsMdInstaller = {
|
|
|
5645
5558
|
description: "Industry-standard agent instructions file (shared with Codex, Cursor, etc.)",
|
|
5646
5559
|
async run(ctx) {
|
|
5647
5560
|
await writeIfMissing(
|
|
5648
|
-
|
|
5561
|
+
path13.join(ctx.projectRoot, "AGENTS.md"),
|
|
5649
5562
|
`# Agent Instructions
|
|
5650
5563
|
|
|
5651
5564
|
Shared instructions for AI coding agents working in this repo.
|
|
@@ -5753,6 +5666,9 @@ async function initProject(projectRoot, opts2 = {}) {
|
|
|
5753
5666
|
`));
|
|
5754
5667
|
}
|
|
5755
5668
|
|
|
5669
|
+
// src/index.ts
|
|
5670
|
+
init_auth();
|
|
5671
|
+
|
|
5756
5672
|
// src/tools/diff-preview.ts
|
|
5757
5673
|
function unifiedDiff(oldContent, newContent, filePath) {
|
|
5758
5674
|
const t = theme();
|
|
@@ -5889,9 +5805,9 @@ var JsonEmitter = class {
|
|
|
5889
5805
|
};
|
|
5890
5806
|
|
|
5891
5807
|
// src/ui/output-schema.ts
|
|
5892
|
-
import
|
|
5808
|
+
import fs14 from "fs/promises";
|
|
5893
5809
|
async function loadOutputSchema(filePath) {
|
|
5894
|
-
const raw = await
|
|
5810
|
+
const raw = await fs14.readFile(filePath, "utf-8");
|
|
5895
5811
|
const schema = JSON.parse(raw);
|
|
5896
5812
|
return { schema, raw };
|
|
5897
5813
|
}
|
|
@@ -6108,8 +6024,8 @@ function isCoordinatorModeEnv() {
|
|
|
6108
6024
|
|
|
6109
6025
|
// src/commands/doctor.ts
|
|
6110
6026
|
import { execSync as execSync4 } from "child_process";
|
|
6111
|
-
import
|
|
6112
|
-
import
|
|
6027
|
+
import fs15 from "fs/promises";
|
|
6028
|
+
import path14 from "path";
|
|
6113
6029
|
import os8 from "os";
|
|
6114
6030
|
import chalk9 from "chalk";
|
|
6115
6031
|
async function runDiagnostics(cwd) {
|
|
@@ -6151,26 +6067,26 @@ async function runDiagnostics(cwd) {
|
|
|
6151
6067
|
results.push({ name: "Config", status: "fail", message: `Could not load: ${err.message}` });
|
|
6152
6068
|
}
|
|
6153
6069
|
try {
|
|
6154
|
-
await
|
|
6070
|
+
await fs15.access(path14.join(cwd, ".notch.json"));
|
|
6155
6071
|
results.push({ name: ".notch.json", status: "ok", message: "Found" });
|
|
6156
6072
|
} catch {
|
|
6157
6073
|
results.push({ name: ".notch.json", status: "warn", message: "Not found. Run: notch init" });
|
|
6158
6074
|
}
|
|
6159
|
-
const notchDir =
|
|
6075
|
+
const notchDir = path14.join(os8.homedir(), ".notch");
|
|
6160
6076
|
try {
|
|
6161
|
-
await
|
|
6077
|
+
await fs15.access(notchDir);
|
|
6162
6078
|
results.push({ name: "~/.notch/", status: "ok", message: "Exists" });
|
|
6163
6079
|
} catch {
|
|
6164
6080
|
results.push({ name: "~/.notch/", status: "warn", message: "Not found (will be created on first use)" });
|
|
6165
6081
|
}
|
|
6166
6082
|
try {
|
|
6167
|
-
await
|
|
6083
|
+
await fs15.access(path14.join(cwd, ".notch.md"));
|
|
6168
6084
|
results.push({ name: ".notch.md", status: "ok", message: "Found" });
|
|
6169
6085
|
} catch {
|
|
6170
6086
|
results.push({ name: ".notch.md", status: "warn", message: "Not found. Run: notch init" });
|
|
6171
6087
|
}
|
|
6172
6088
|
try {
|
|
6173
|
-
const configRaw = await
|
|
6089
|
+
const configRaw = await fs15.readFile(path14.join(cwd, ".notch.json"), "utf-8").catch(() => "{}");
|
|
6174
6090
|
const mcpConfigs = parseMCPConfig(JSON.parse(configRaw));
|
|
6175
6091
|
const serverNames = Object.keys(mcpConfigs);
|
|
6176
6092
|
if (serverNames.length === 0) {
|
|
@@ -6191,8 +6107,8 @@ async function runDiagnostics(cwd) {
|
|
|
6191
6107
|
results.push({ name: "MCP Servers", status: "ok", message: "No config to check" });
|
|
6192
6108
|
}
|
|
6193
6109
|
try {
|
|
6194
|
-
const sessionsDir =
|
|
6195
|
-
const entries = await
|
|
6110
|
+
const sessionsDir = path14.join(notchDir, "sessions");
|
|
6111
|
+
const entries = await fs15.readdir(sessionsDir).catch(() => []);
|
|
6196
6112
|
results.push({ name: "Sessions", status: "ok", message: `${entries.length} saved` });
|
|
6197
6113
|
} catch {
|
|
6198
6114
|
results.push({ name: "Sessions", status: "ok", message: "0 saved" });
|
|
@@ -6594,11 +6510,11 @@ Read the file first, then make the change. Only modify this one file.`
|
|
|
6594
6510
|
|
|
6595
6511
|
// src/commands/plugin.ts
|
|
6596
6512
|
import { execSync as execSync7, execFileSync as execFileSync2 } from "child_process";
|
|
6597
|
-
import
|
|
6598
|
-
import
|
|
6513
|
+
import fs16 from "fs/promises";
|
|
6514
|
+
import path15 from "path";
|
|
6599
6515
|
import os9 from "os";
|
|
6600
6516
|
import chalk15 from "chalk";
|
|
6601
|
-
var GLOBAL_PLUGINS_DIR =
|
|
6517
|
+
var GLOBAL_PLUGINS_DIR = path15.join(os9.homedir(), ".notch", "plugins");
|
|
6602
6518
|
registerCommand("/plugin", async (args, ctx) => {
|
|
6603
6519
|
const parts = args.split(/\s+/);
|
|
6604
6520
|
const subcommand = parts[0] || "list";
|
|
@@ -6634,8 +6550,8 @@ registerCommand("/plugin", async (args, ctx) => {
|
|
|
6634
6550
|
console.log(chalk15.gray(" Usage: /plugin install <npm-package-or-git-url>\n"));
|
|
6635
6551
|
return;
|
|
6636
6552
|
}
|
|
6637
|
-
await
|
|
6638
|
-
const pluginDir =
|
|
6553
|
+
await fs16.mkdir(GLOBAL_PLUGINS_DIR, { recursive: true });
|
|
6554
|
+
const pluginDir = path15.join(GLOBAL_PLUGINS_DIR, path15.basename(target).replace(/\.git$/, ""));
|
|
6639
6555
|
console.log(chalk15.gray(` Installing ${target}...`));
|
|
6640
6556
|
try {
|
|
6641
6557
|
if (target.includes("/") && !target.startsWith("@")) {
|
|
@@ -6645,7 +6561,7 @@ registerCommand("/plugin", async (args, ctx) => {
|
|
|
6645
6561
|
stdio: "pipe"
|
|
6646
6562
|
});
|
|
6647
6563
|
} else {
|
|
6648
|
-
await
|
|
6564
|
+
await fs16.mkdir(pluginDir, { recursive: true });
|
|
6649
6565
|
execFileSync2("npm", ["init", "-y"], {
|
|
6650
6566
|
cwd: pluginDir,
|
|
6651
6567
|
encoding: "utf-8",
|
|
@@ -6660,7 +6576,7 @@ registerCommand("/plugin", async (args, ctx) => {
|
|
|
6660
6576
|
});
|
|
6661
6577
|
}
|
|
6662
6578
|
try {
|
|
6663
|
-
const pkgExists = await
|
|
6579
|
+
const pkgExists = await fs16.access(path15.join(pluginDir, "package.json")).then(() => true).catch(() => false);
|
|
6664
6580
|
if (pkgExists) {
|
|
6665
6581
|
execSync7("npm install --production", {
|
|
6666
6582
|
cwd: pluginDir,
|
|
@@ -6686,10 +6602,10 @@ registerCommand("/plugin", async (args, ctx) => {
|
|
|
6686
6602
|
console.log(chalk15.gray(" Usage: /plugin remove <plugin-name>\n"));
|
|
6687
6603
|
return;
|
|
6688
6604
|
}
|
|
6689
|
-
const pluginDir =
|
|
6605
|
+
const pluginDir = path15.join(GLOBAL_PLUGINS_DIR, target);
|
|
6690
6606
|
try {
|
|
6691
|
-
await
|
|
6692
|
-
await
|
|
6607
|
+
await fs16.access(pluginDir);
|
|
6608
|
+
await fs16.rm(pluginDir, { recursive: true, force: true });
|
|
6693
6609
|
console.log(chalk15.green(` \u2713 Removed ${target}`));
|
|
6694
6610
|
console.log(chalk15.gray(" Restart notch to apply.\n"));
|
|
6695
6611
|
} catch {
|
|
@@ -7301,10 +7217,10 @@ import ora4 from "ora";
|
|
|
7301
7217
|
|
|
7302
7218
|
// src/skills/registry.ts
|
|
7303
7219
|
import { createHash } from "crypto";
|
|
7304
|
-
import
|
|
7220
|
+
import fs17 from "fs";
|
|
7305
7221
|
import fsp3 from "fs/promises";
|
|
7306
7222
|
import os10 from "os";
|
|
7307
|
-
import
|
|
7223
|
+
import path16 from "path";
|
|
7308
7224
|
var registry = /* @__PURE__ */ new Map();
|
|
7309
7225
|
var loadPromises = /* @__PURE__ */ new Map();
|
|
7310
7226
|
var extractedDirs = /* @__PURE__ */ new Set();
|
|
@@ -7342,14 +7258,14 @@ async function performLoad(skill) {
|
|
|
7342
7258
|
function getExtractDir(skill) {
|
|
7343
7259
|
const filesJson = skill.files ? JSON.stringify(skill.files) : "";
|
|
7344
7260
|
const sha8 = createHash("sha256").update(filesJson).digest("hex").slice(0, 8);
|
|
7345
|
-
return
|
|
7261
|
+
return path16.join(os10.tmpdir(), "notch-skills", `${skill.id}-${sha8}`);
|
|
7346
7262
|
}
|
|
7347
7263
|
async function extractFiles(dir, files) {
|
|
7348
7264
|
await fsp3.mkdir(dir, { recursive: true, mode: 448 });
|
|
7349
7265
|
const byParent = /* @__PURE__ */ new Map();
|
|
7350
7266
|
for (const [relPath, content] of Object.entries(files)) {
|
|
7351
7267
|
const target = resolveSafePath(dir, relPath);
|
|
7352
|
-
const parent =
|
|
7268
|
+
const parent = path16.dirname(target);
|
|
7353
7269
|
const group = byParent.get(parent);
|
|
7354
7270
|
if (group) group.push([target, content]);
|
|
7355
7271
|
else byParent.set(parent, [[target, content]]);
|
|
@@ -7366,17 +7282,17 @@ async function extractFiles(dir, files) {
|
|
|
7366
7282
|
);
|
|
7367
7283
|
}
|
|
7368
7284
|
function resolveSafePath(baseDir, relPath) {
|
|
7369
|
-
const normalized =
|
|
7285
|
+
const normalized = path16.normalize(relPath);
|
|
7370
7286
|
const parts = normalized.split(/[\\/]/);
|
|
7371
|
-
if (
|
|
7287
|
+
if (path16.isAbsolute(normalized) || parts.includes("..")) {
|
|
7372
7288
|
throw new Error(`skill file path escapes skill dir: ${relPath}`);
|
|
7373
7289
|
}
|
|
7374
|
-
return
|
|
7290
|
+
return path16.join(baseDir, normalized);
|
|
7375
7291
|
}
|
|
7376
7292
|
function cleanupSkills() {
|
|
7377
7293
|
for (const dir of extractedDirs) {
|
|
7378
7294
|
try {
|
|
7379
|
-
|
|
7295
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
7380
7296
|
} catch {
|
|
7381
7297
|
}
|
|
7382
7298
|
}
|
|
@@ -7562,6 +7478,7 @@ registerCommand("/microcompact", async (args, ctx) => {
|
|
|
7562
7478
|
import { exec, execSync as execSync11 } from "child_process";
|
|
7563
7479
|
import chalk27 from "chalk";
|
|
7564
7480
|
import ora5 from "ora";
|
|
7481
|
+
init_auth();
|
|
7565
7482
|
var PLATFORM_URL = "https://freesyntax.dev";
|
|
7566
7483
|
function openBrowser(url) {
|
|
7567
7484
|
let cmd;
|
|
@@ -7693,6 +7610,7 @@ registerCommand("/cloud", async (args, ctx) => {
|
|
|
7693
7610
|
import { exec as exec2 } from "child_process";
|
|
7694
7611
|
import chalk28 from "chalk";
|
|
7695
7612
|
import ora6 from "ora";
|
|
7613
|
+
init_auth();
|
|
7696
7614
|
var PLATFORM_URL2 = "https://freesyntax.dev";
|
|
7697
7615
|
function openBrowser2(url) {
|
|
7698
7616
|
let cmd;
|
|
@@ -7775,8 +7693,8 @@ registerCommand("/agent-builder", async (args, _ctx) => {
|
|
|
7775
7693
|
});
|
|
7776
7694
|
|
|
7777
7695
|
// src/ui/completions.ts
|
|
7778
|
-
import
|
|
7779
|
-
import
|
|
7696
|
+
import fs18 from "fs";
|
|
7697
|
+
import path17 from "path";
|
|
7780
7698
|
var COMMANDS = [
|
|
7781
7699
|
"/help",
|
|
7782
7700
|
"/quit",
|
|
@@ -7869,15 +7787,15 @@ function buildCompleter(cwd) {
|
|
|
7869
7787
|
}
|
|
7870
7788
|
function completeFilePath(partial, cwd) {
|
|
7871
7789
|
try {
|
|
7872
|
-
const dir = partial.includes("/") ?
|
|
7873
|
-
const prefix = partial.includes("/") ?
|
|
7874
|
-
const entries =
|
|
7790
|
+
const dir = partial.includes("/") ? path17.resolve(cwd, path17.dirname(partial)) : cwd;
|
|
7791
|
+
const prefix = partial.includes("/") ? path17.basename(partial) : partial;
|
|
7792
|
+
const entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
7875
7793
|
const matches = [];
|
|
7876
7794
|
for (const entry of entries) {
|
|
7877
7795
|
if (entry.name.startsWith(".")) continue;
|
|
7878
7796
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
7879
7797
|
if (entry.name.startsWith(prefix)) {
|
|
7880
|
-
const relative = partial.includes("/") ?
|
|
7798
|
+
const relative = partial.includes("/") ? path17.dirname(partial) + "/" + entry.name : entry.name;
|
|
7881
7799
|
if (entry.isDirectory()) {
|
|
7882
7800
|
matches.push(relative + "/");
|
|
7883
7801
|
} else {
|
|
@@ -7900,7 +7818,10 @@ var SLASH_COMMANDS = [
|
|
|
7900
7818
|
{ name: "/quit", description: "Exit Notch", category: "Core" },
|
|
7901
7819
|
// Model & Status
|
|
7902
7820
|
{ name: "/model", description: "Switch or list models", category: "Model" },
|
|
7821
|
+
{ name: "/model download", description: "Download a Notch model locally", category: "Model" },
|
|
7822
|
+
{ name: "/downloads", description: "Show local model download progress", category: "Model" },
|
|
7903
7823
|
{ name: "/status", description: "Check API endpoint health", category: "Model" },
|
|
7824
|
+
{ name: "/sync-keys", description: "Pull BYOK keys from freesyntax.dev", category: "Model" },
|
|
7904
7825
|
// Session
|
|
7905
7826
|
{ name: "/save", description: "Save current session", category: "Session" },
|
|
7906
7827
|
{ name: "/sessions", description: "List saved sessions", category: "Session" },
|
|
@@ -8158,22 +8079,22 @@ function rewritePromptLine(rl) {
|
|
|
8158
8079
|
}
|
|
8159
8080
|
|
|
8160
8081
|
// src/services/autoDream/gate.ts
|
|
8161
|
-
import
|
|
8162
|
-
import
|
|
8082
|
+
import fs19 from "fs/promises";
|
|
8083
|
+
import path18 from "path";
|
|
8163
8084
|
import os11 from "os";
|
|
8164
|
-
var NOTCH_DIR2 =
|
|
8165
|
-
var SESSION_DIR2 =
|
|
8166
|
-
var STATE_FILE =
|
|
8167
|
-
var LOCK_FILE =
|
|
8085
|
+
var NOTCH_DIR2 = path18.join(os11.homedir(), ".notch");
|
|
8086
|
+
var SESSION_DIR2 = path18.join(NOTCH_DIR2, "sessions");
|
|
8087
|
+
var STATE_FILE = path18.join(NOTCH_DIR2, "dream-state.json");
|
|
8088
|
+
var LOCK_FILE = path18.join(NOTCH_DIR2, ".dream.lock");
|
|
8168
8089
|
var DEFAULT_MIN_HOURS = 24;
|
|
8169
8090
|
var DEFAULT_MIN_SESSIONS = 5;
|
|
8170
8091
|
var MS_PER_HOUR = 36e5;
|
|
8171
8092
|
async function ensureNotchDir2() {
|
|
8172
|
-
await
|
|
8093
|
+
await fs19.mkdir(NOTCH_DIR2, { recursive: true });
|
|
8173
8094
|
}
|
|
8174
8095
|
async function readState() {
|
|
8175
8096
|
try {
|
|
8176
|
-
const raw = await
|
|
8097
|
+
const raw = await fs19.readFile(STATE_FILE, "utf-8");
|
|
8177
8098
|
const parsed = JSON.parse(raw);
|
|
8178
8099
|
if (typeof parsed?.lastRunAt !== "number" || !Number.isFinite(parsed.lastRunAt)) {
|
|
8179
8100
|
return null;
|
|
@@ -8185,7 +8106,7 @@ async function readState() {
|
|
|
8185
8106
|
}
|
|
8186
8107
|
async function writeState(state) {
|
|
8187
8108
|
await ensureNotchDir2();
|
|
8188
|
-
await
|
|
8109
|
+
await fs19.writeFile(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
|
|
8189
8110
|
}
|
|
8190
8111
|
async function shouldDream(_cwd, opts2 = {}) {
|
|
8191
8112
|
const minHours = opts2.minHours ?? DEFAULT_MIN_HOURS;
|
|
@@ -8201,7 +8122,7 @@ async function shouldDream(_cwd, opts2 = {}) {
|
|
|
8201
8122
|
}
|
|
8202
8123
|
let sessionFiles = [];
|
|
8203
8124
|
try {
|
|
8204
|
-
sessionFiles = await
|
|
8125
|
+
sessionFiles = await fs19.readdir(SESSION_DIR2);
|
|
8205
8126
|
} catch {
|
|
8206
8127
|
return { should: false, reason: "session-gate: no session directory yet" };
|
|
8207
8128
|
}
|
|
@@ -8209,7 +8130,7 @@ async function shouldDream(_cwd, opts2 = {}) {
|
|
|
8209
8130
|
for (const name of sessionFiles) {
|
|
8210
8131
|
if (!name.endsWith(".json") && !name.endsWith(".jsonl")) continue;
|
|
8211
8132
|
try {
|
|
8212
|
-
const stat = await
|
|
8133
|
+
const stat = await fs19.stat(path18.join(SESSION_DIR2, name));
|
|
8213
8134
|
if (stat.mtimeMs > lastAt) touchedCount++;
|
|
8214
8135
|
} catch {
|
|
8215
8136
|
}
|
|
@@ -8222,7 +8143,7 @@ async function shouldDream(_cwd, opts2 = {}) {
|
|
|
8222
8143
|
}
|
|
8223
8144
|
try {
|
|
8224
8145
|
await ensureNotchDir2();
|
|
8225
|
-
const handle = await
|
|
8146
|
+
const handle = await fs19.open(LOCK_FILE, "wx");
|
|
8226
8147
|
try {
|
|
8227
8148
|
await handle.writeFile(String(process.pid), "utf-8");
|
|
8228
8149
|
} finally {
|
|
@@ -8245,7 +8166,7 @@ async function shouldDream(_cwd, opts2 = {}) {
|
|
|
8245
8166
|
}
|
|
8246
8167
|
async function releaseDreamLock() {
|
|
8247
8168
|
try {
|
|
8248
|
-
await
|
|
8169
|
+
await fs19.unlink(LOCK_FILE);
|
|
8249
8170
|
} catch {
|
|
8250
8171
|
}
|
|
8251
8172
|
}
|
|
@@ -8262,10 +8183,10 @@ async function recordDreamRun() {
|
|
|
8262
8183
|
}
|
|
8263
8184
|
|
|
8264
8185
|
// src/services/autoDream/consolidationPrompt.ts
|
|
8265
|
-
import
|
|
8186
|
+
import path19 from "path";
|
|
8266
8187
|
import os12 from "os";
|
|
8267
|
-
var MEMORY_DIR2 =
|
|
8268
|
-
var SESSION_DIR3 =
|
|
8188
|
+
var MEMORY_DIR2 = path19.join(os12.homedir(), ".notch", "memory");
|
|
8189
|
+
var SESSION_DIR3 = path19.join(os12.homedir(), ".notch", "sessions");
|
|
8269
8190
|
var INDEX_FILE2 = "MEMORY.md";
|
|
8270
8191
|
var MAX_INDEX_LINES = 200;
|
|
8271
8192
|
var MAX_INDEX_BYTES = 25 * 1024;
|
|
@@ -8427,7 +8348,7 @@ function isDisabled() {
|
|
|
8427
8348
|
}
|
|
8428
8349
|
|
|
8429
8350
|
// src/index.ts
|
|
8430
|
-
import
|
|
8351
|
+
import fs20 from "fs/promises";
|
|
8431
8352
|
import { createRequire as createRequire2 } from "module";
|
|
8432
8353
|
var _require2 = createRequire2(import.meta.url);
|
|
8433
8354
|
var VERSION = _require2("../package.json").version;
|
|
@@ -8437,10 +8358,15 @@ if (process.argv[2] === "update") {
|
|
|
8437
8358
|
process.exit(process.exitCode ?? 0);
|
|
8438
8359
|
}
|
|
8439
8360
|
if (process.argv[2] === "ollama") {
|
|
8440
|
-
const { runOllamaCli } = await import("./ollama-launch-
|
|
8361
|
+
const { runOllamaCli } = await import("./ollama-launch-P5KBK7AJ.js");
|
|
8441
8362
|
const code = await runOllamaCli(process.argv.slice(3), process.cwd());
|
|
8442
8363
|
process.exit(code);
|
|
8443
8364
|
}
|
|
8365
|
+
if (process.argv[2] === "config") {
|
|
8366
|
+
const { runConfigCli } = await import("./config-set-3IWEVZQ4.js");
|
|
8367
|
+
const code = await runConfigCli(process.argv.slice(3), process.cwd());
|
|
8368
|
+
process.exit(code);
|
|
8369
|
+
}
|
|
8444
8370
|
var program = new Command().name("notch").description("Notch CLI \u2014 AI-powered coding assistant by Driftrail").version(VERSION).argument("[prompt...]", "One-shot prompt (runs once and exits)").option(`-m, --model <model>`, `Notch model (${modelChoices}) or BYOK ref like openrouter:anthropic/claude-sonnet-4-6`).option("--base-url <url>", "Override the backend base URL (Notch or BYOK)").option("--api-key <key>", "API key for the backend (prefer the env var: NOTCH_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / OPENROUTER_API_KEY / ...)").option("--provider <id>", "BYOK provider id (openai, anthropic, openrouter, together, fireworks, groq, ollama, lmstudio, vllm, custom). Run --list-providers to see them all.").option("--list-providers", "List built-in BYOK providers and their API-key env vars, then exit").option("--no-repo-map", "Disable automatic repository mapping").option("--no-markdown", "Disable markdown rendering in output").option("--max-iterations <n>", "Max tool-call rounds per turn", "25").option("-y, --yes", "Auto-confirm destructive actions").option("--trust", "Trust mode \u2014 auto-allow all tool calls").option("--theme <theme>", `UI color theme (${THEME_IDS.join(", ")})`).option("--resume", "Resume the last session for this project").option("--session <id>", "Resume a specific session by ID").option("--cwd <dir>", "Set working directory").option("--json", "Emit JSONL event stream on stdout (headless/CI mode)").option("--output-schema <file>", "Path to JSON Schema constraining the final structured output").option("--output-last-message <file>", "Write the final assistant message to this file on exit").option("--guardian", "Enable Guardian: independent Solace-Lite risk scoring before every prompt-level tool call").option("--coordinator", "Coordinator mode: top-level agent can only spawn/continue/stop workers (plus read/grep/glob). All real work is delegated.").option("--no-auto-dream", "Disable the background memory-consolidation daemon (default: enabled in REPL)").option("--no-update", "Disable the background update check on launch (equivalent to NOTCH_AUTO_UPDATE=0)").option("--update-channel <name>", "npm dist-tag to follow for updates (latest | next | beta)").option(
|
|
8445
8371
|
"--image <path>",
|
|
8446
8372
|
"Attach an image (file path, URL, or data URL). Repeatable.",
|
|
@@ -8483,13 +8409,13 @@ async function persistByokChoice(projectRoot, providerId, defaultModel) {
|
|
|
8483
8409
|
const p = nodePath2.resolve(projectRoot, ".notch.json");
|
|
8484
8410
|
let current = {};
|
|
8485
8411
|
try {
|
|
8486
|
-
const raw = await
|
|
8412
|
+
const raw = await fs20.readFile(p, "utf-8");
|
|
8487
8413
|
current = JSON.parse(raw);
|
|
8488
8414
|
} catch {
|
|
8489
8415
|
}
|
|
8490
8416
|
const idToPersist = providerId === "__custom__" ? "custom" : providerId;
|
|
8491
8417
|
current.byok = { provider: idToPersist, model: defaultModel };
|
|
8492
|
-
await
|
|
8418
|
+
await fs20.writeFile(p, JSON.stringify(current, null, 2) + "\n", "utf-8");
|
|
8493
8419
|
} catch {
|
|
8494
8420
|
}
|
|
8495
8421
|
}
|
|
@@ -8549,7 +8475,18 @@ function interactiveModelPicker(activeModel) {
|
|
|
8549
8475
|
const p = row.provider;
|
|
8550
8476
|
const isCurrent = typeof activeModel === "string" && isByokRef(activeModel) ? parseByokRef(activeModel).provider === p.id : false;
|
|
8551
8477
|
const dot = isCurrent ? t.success("\u25CF") : " ";
|
|
8552
|
-
const
|
|
8478
|
+
const envHit = p.apiKeyEnv ? Boolean(process.env[p.apiKeyEnv]) : false;
|
|
8479
|
+
let syncHit = false;
|
|
8480
|
+
if (!envHit) {
|
|
8481
|
+
try {
|
|
8482
|
+
const { loadSyncedByokKeysSync } = (init_auth(), __toCommonJS(auth_exports));
|
|
8483
|
+
const synced = loadSyncedByokKeysSync();
|
|
8484
|
+
const fromSync = synced?.keys[p.id];
|
|
8485
|
+
syncHit = typeof fromSync === "string" && fromSync.length > 0;
|
|
8486
|
+
} catch {
|
|
8487
|
+
}
|
|
8488
|
+
}
|
|
8489
|
+
const keyPresent = p.apiKeyEnv ? envHit ? t.success("\u2713") : syncHit ? t.brand("\u2713") : t.dim("\u2717") : t.dim("\u2013");
|
|
8553
8490
|
const label = isSelected ? t.bold(p.label) : t.dim(p.label);
|
|
8554
8491
|
const envDisplay = t.dim((p.apiKeyEnv || "local").padEnd(22));
|
|
8555
8492
|
const defModel = t.dim(p.defaultModel ? p.defaultModel.slice(0, 34) : "\u2014");
|
|
@@ -8608,6 +8545,9 @@ function printHelp() {
|
|
|
8608
8545
|
Commands:
|
|
8609
8546
|
/model \u2014 Show available models (Notch + BYOK)
|
|
8610
8547
|
/model <name> \u2014 Switch model: /model pyre OR /model openrouter:anthropic/claude-sonnet-4-6
|
|
8548
|
+
/model download <name> \u2014 Pull a Notch model's local weights in the background (HF Hub)
|
|
8549
|
+
/downloads \u2014 Show local download progress for this session
|
|
8550
|
+
/sync-keys \u2014 Pull BYOK keys you added on freesyntax.dev
|
|
8611
8551
|
/providers \u2014 List built-in BYOK providers and whether their keys are set
|
|
8612
8552
|
/status \u2014 Check backend health (Notch API or BYOK endpoint)
|
|
8613
8553
|
/undo \u2014 Undo last file changes
|
|
@@ -8632,6 +8572,7 @@ function printHelp() {
|
|
|
8632
8572
|
/memory search <q> \u2014 Search memories
|
|
8633
8573
|
/memory clear \u2014 Delete all memories
|
|
8634
8574
|
/permissions \u2014 Show current permission config
|
|
8575
|
+
/yolo \u2014 Toggle YOLO mode: auto-allow all tool calls (persists)
|
|
8635
8576
|
|
|
8636
8577
|
Ralph Wiggum Mode (autonomous):
|
|
8637
8578
|
/ralph plan <goal> \u2014 Generate task plan for a goal
|
|
@@ -8708,12 +8649,62 @@ async function main() {
|
|
|
8708
8649
|
const creds = await login();
|
|
8709
8650
|
console.log(chalk30.green(`
|
|
8710
8651
|
\u2713 Signed in as ${creds.email}`));
|
|
8711
|
-
console.log(chalk30.gray(` API key stored in ${(await import("./auth-
|
|
8712
|
-
|
|
8652
|
+
console.log(chalk30.gray(` API key stored in ${(await import("./auth-UAMMP5IJ.js")).getCredentialsPath()}`));
|
|
8653
|
+
try {
|
|
8654
|
+
const { syncByokKeys } = await import("./auth-UAMMP5IJ.js");
|
|
8655
|
+
const synced = await syncByokKeys(creds.token);
|
|
8656
|
+
const providerCount = Object.keys(synced.keys).length;
|
|
8657
|
+
if (providerCount > 0) {
|
|
8658
|
+
console.log(chalk30.gray(
|
|
8659
|
+
` Synced ${providerCount} BYOK provider key(s) from freesyntax.dev \u2192 ${(await import("./auth-UAMMP5IJ.js")).getByokSyncPath()}`
|
|
8660
|
+
));
|
|
8661
|
+
} else {
|
|
8662
|
+
console.log(chalk30.gray(
|
|
8663
|
+
` No BYOK keys on your profile yet \u2014 add them at freesyntax.dev/settings/keys then run ${chalk30.white("notch sync-keys")}.`
|
|
8664
|
+
));
|
|
8665
|
+
}
|
|
8666
|
+
} catch (syncErr) {
|
|
8667
|
+
console.log(chalk30.yellow(
|
|
8668
|
+
` (BYOK sync skipped: ${syncErr.message.slice(0, 120)})`
|
|
8669
|
+
));
|
|
8670
|
+
}
|
|
8671
|
+
console.log("");
|
|
8713
8672
|
} catch (err) {
|
|
8714
8673
|
spinner.stop();
|
|
8715
8674
|
console.error(chalk30.red(`
|
|
8716
8675
|
Login failed: ${err.message}
|
|
8676
|
+
`));
|
|
8677
|
+
process.exit(1);
|
|
8678
|
+
}
|
|
8679
|
+
return;
|
|
8680
|
+
}
|
|
8681
|
+
if (promptArgs[0] === "sync-keys") {
|
|
8682
|
+
const creds = await loadCredentials();
|
|
8683
|
+
if (!creds) {
|
|
8684
|
+
console.log(chalk30.gray("\n Not signed in. Run: notch login\n"));
|
|
8685
|
+
return;
|
|
8686
|
+
}
|
|
8687
|
+
const spinner = ora7("Pulling BYOK keys from freesyntax.dev...").start();
|
|
8688
|
+
try {
|
|
8689
|
+
const { syncByokKeys, getByokSyncPath } = await import("./auth-UAMMP5IJ.js");
|
|
8690
|
+
const synced = await syncByokKeys(creds.token);
|
|
8691
|
+
spinner.stop();
|
|
8692
|
+
const providers = Object.keys(synced.keys);
|
|
8693
|
+
if (providers.length === 0) {
|
|
8694
|
+
console.log(chalk30.gray(`
|
|
8695
|
+
No BYOK keys on your profile yet.`));
|
|
8696
|
+
console.log(chalk30.gray(` Add them at ${chalk30.white("https://freesyntax.dev/settings/keys")} then rerun.
|
|
8697
|
+
`));
|
|
8698
|
+
} else {
|
|
8699
|
+
console.log(chalk30.green(`
|
|
8700
|
+
\u2713 Synced ${providers.length} provider key(s): ${providers.join(", ")}`));
|
|
8701
|
+
console.log(chalk30.gray(` Cached at ${getByokSyncPath()}
|
|
8702
|
+
`));
|
|
8703
|
+
}
|
|
8704
|
+
} catch (err) {
|
|
8705
|
+
spinner.stop();
|
|
8706
|
+
console.error(chalk30.red(`
|
|
8707
|
+
Key sync failed: ${err.message}
|
|
8717
8708
|
`));
|
|
8718
8709
|
process.exit(1);
|
|
8719
8710
|
}
|
|
@@ -8746,7 +8737,7 @@ async function main() {
|
|
|
8746
8737
|
return;
|
|
8747
8738
|
}
|
|
8748
8739
|
if (promptArgs[0] === "mcp-serve" || promptArgs[0] === "mcp-server") {
|
|
8749
|
-
const { runMcpServer } = await import("./server-
|
|
8740
|
+
const { runMcpServer } = await import("./server-IGOZHW52.js");
|
|
8750
8741
|
await runMcpServer({
|
|
8751
8742
|
cwd: opts.cwd ?? process.cwd(),
|
|
8752
8743
|
version: VERSION,
|
|
@@ -8955,14 +8946,18 @@ async function main() {
|
|
|
8955
8946
|
}).catch(() => {
|
|
8956
8947
|
});
|
|
8957
8948
|
}
|
|
8949
|
+
const startupPermissive = config.permissionMode === "trust" || !!config.autoConfirm || !!opts.yes;
|
|
8958
8950
|
const hookTrustPrompt = async (commands) => {
|
|
8951
|
+
if (startupPermissive) return true;
|
|
8952
|
+
if (!process.stdin.isTTY) return true;
|
|
8959
8953
|
console.warn(chalk30.yellow("\n\u26A0 This project contains hooks in .notch.json that will run shell commands:"));
|
|
8960
8954
|
commands.forEach((cmd) => console.warn(chalk30.gray(` \u2022 ${cmd}`)));
|
|
8961
8955
|
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8962
8956
|
return new Promise((resolve3) => {
|
|
8963
|
-
rl2.question(chalk30.yellow("\nAllow these hooks for this project? [
|
|
8957
|
+
rl2.question(chalk30.yellow("\nAllow these hooks for this project? [Y/n] "), (answer) => {
|
|
8964
8958
|
rl2.close();
|
|
8965
|
-
|
|
8959
|
+
const norm = answer.trim().toLowerCase();
|
|
8960
|
+
resolve3(norm !== "n" && norm !== "no");
|
|
8966
8961
|
});
|
|
8967
8962
|
});
|
|
8968
8963
|
};
|
|
@@ -9026,7 +9021,7 @@ ${repoMapStr}` : "",
|
|
|
9026
9021
|
const permissionSessionId = `s_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
9027
9022
|
const checkpoints = new CheckpointManager();
|
|
9028
9023
|
const usage2 = new UsageTracker();
|
|
9029
|
-
const { OllamaCloudUsageTracker } = await import("./ollama-usage-
|
|
9024
|
+
const { OllamaCloudUsageTracker } = await import("./ollama-usage-3PROM2WC.js");
|
|
9030
9025
|
const cloudUsage = new OllamaCloudUsageTracker();
|
|
9031
9026
|
let sessionId;
|
|
9032
9027
|
let activePlan = null;
|
|
@@ -9036,7 +9031,7 @@ ${repoMapStr}` : "",
|
|
|
9036
9031
|
const costTracker = new CostTracker();
|
|
9037
9032
|
const mcpClients = [];
|
|
9038
9033
|
try {
|
|
9039
|
-
const configRaw = await
|
|
9034
|
+
const configRaw = await fs20.readFile(nodePath2.resolve(config.projectRoot, ".notch.json"), "utf-8").catch(() => "{}");
|
|
9040
9035
|
const mcpConfigs = parseMCPConfig(JSON.parse(configRaw));
|
|
9041
9036
|
for (const [name, mcpConfig] of Object.entries(mcpConfigs)) {
|
|
9042
9037
|
try {
|
|
@@ -9079,15 +9074,18 @@ ${repoMapStr}` : "",
|
|
|
9079
9074
|
guardianModel = void 0;
|
|
9080
9075
|
}
|
|
9081
9076
|
}
|
|
9077
|
+
const isPermissive = () => config.permissionMode === "trust" || config.autoConfirm;
|
|
9082
9078
|
const toolCtx = {
|
|
9083
9079
|
cwd: config.projectRoot,
|
|
9084
|
-
requireConfirm:
|
|
9080
|
+
requireConfirm: !isPermissive(),
|
|
9085
9081
|
confirm: async (message) => {
|
|
9082
|
+
if (isPermissive()) return true;
|
|
9086
9083
|
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
9087
9084
|
return new Promise((resolve3) => {
|
|
9088
|
-
rl2.question(`${message} (
|
|
9085
|
+
rl2.question(`${message} (Y/n) `, (answer) => {
|
|
9089
9086
|
rl2.close();
|
|
9090
|
-
|
|
9087
|
+
const norm = answer.trim().toLowerCase();
|
|
9088
|
+
resolve3(norm !== "n" && norm !== "no");
|
|
9091
9089
|
});
|
|
9092
9090
|
});
|
|
9093
9091
|
},
|
|
@@ -9305,7 +9303,7 @@ Analyze the above input.`;
|
|
|
9305
9303
|
}
|
|
9306
9304
|
if (opts.outputLastMessage) {
|
|
9307
9305
|
try {
|
|
9308
|
-
await
|
|
9306
|
+
await fs20.writeFile(opts.outputLastMessage, response.text, "utf-8");
|
|
9309
9307
|
} catch (err) {
|
|
9310
9308
|
if (jsonMode) events.emit({ type: "error", message: `Failed to write --output-last-message: ${err.message}` });
|
|
9311
9309
|
}
|
|
@@ -9435,8 +9433,19 @@ Analyze the above input.`;
|
|
|
9435
9433
|
try {
|
|
9436
9434
|
model = resolveModel(config.models.chat);
|
|
9437
9435
|
const switchedInfo = MODEL_CATALOG[picked.id];
|
|
9438
|
-
console.log(chalk30.green(` \u2713 Switched to ${switchedInfo.label} (${switchedInfo.id})
|
|
9436
|
+
console.log(chalk30.green(` \u2713 Switched to ${switchedInfo.label} (${switchedInfo.id})`));
|
|
9437
|
+
const shortName = picked.id.replace("notch-", "");
|
|
9438
|
+
const hw = switchedInfo.hardware;
|
|
9439
|
+
console.log(chalk30.gray(
|
|
9440
|
+
` Hosted inference is live. To run locally you need ~${hw.vramGb}GB VRAM (${switchedInfo.hardware.recommendedGpu}) + ${hw.diskGb}GB disk for Q4 weights.`
|
|
9441
|
+
));
|
|
9442
|
+
if (switchedInfo.hfRepo) {
|
|
9443
|
+
console.log(chalk30.gray(` Run ${chalk30.white(`/model download ${shortName}`)} to pull them in the background.
|
|
9444
|
+
`));
|
|
9445
|
+
} else {
|
|
9446
|
+
console.log(chalk30.gray(` Local weights aren't published yet \u2014 sticking with the hosted API.
|
|
9439
9447
|
`));
|
|
9448
|
+
}
|
|
9440
9449
|
} catch (e) {
|
|
9441
9450
|
console.log(chalk30.red(` Failed to switch: ${e.message}
|
|
9442
9451
|
`));
|
|
@@ -9516,6 +9525,96 @@ Analyze the above input.`;
|
|
|
9516
9525
|
rl.prompt();
|
|
9517
9526
|
return;
|
|
9518
9527
|
}
|
|
9528
|
+
if (input.startsWith("/model download")) {
|
|
9529
|
+
const arg = input.replace("/model download", "").trim();
|
|
9530
|
+
if (!arg) {
|
|
9531
|
+
console.log(chalk30.red(" Usage: /model download <pyre|ignis|solace|solace-lite>"));
|
|
9532
|
+
rl.prompt();
|
|
9533
|
+
return;
|
|
9534
|
+
}
|
|
9535
|
+
const normalised = arg.startsWith("notch-") ? arg : `notch-${arg}`;
|
|
9536
|
+
if (!isValidModel(normalised)) {
|
|
9537
|
+
console.log(chalk30.red(` Unknown model: ${arg}`));
|
|
9538
|
+
console.log(chalk30.gray(` Notch models: ${modelChoices}`));
|
|
9539
|
+
rl.prompt();
|
|
9540
|
+
return;
|
|
9541
|
+
}
|
|
9542
|
+
const info2 = MODEL_CATALOG[normalised];
|
|
9543
|
+
const { probeSystemCapabilities, evaluateHardware, renderVerdict, startModelDownload } = await import("./model-download-3NDKS3VM.js");
|
|
9544
|
+
const caps = await probeSystemCapabilities();
|
|
9545
|
+
const verdict = evaluateHardware(info2, caps);
|
|
9546
|
+
console.log(`
|
|
9547
|
+
${renderVerdict(info2, verdict)}
|
|
9548
|
+
`);
|
|
9549
|
+
if (!info2.hfRepo) {
|
|
9550
|
+
console.log(chalk30.yellow(
|
|
9551
|
+
` ${info2.label} isn't published to Hugging Face yet \u2014 the Notch team is still preparing merged weights for public download. Use the hosted API in the meantime (run ${chalk30.white("notch login")} if you haven't already).
|
|
9552
|
+
`
|
|
9553
|
+
));
|
|
9554
|
+
rl.prompt();
|
|
9555
|
+
return;
|
|
9556
|
+
}
|
|
9557
|
+
try {
|
|
9558
|
+
const handle = await startModelDownload(normalised);
|
|
9559
|
+
console.log(chalk30.green(` \u2193 Downloading ${info2.label} in background (pid ${handle.pid}).`));
|
|
9560
|
+
console.log(chalk30.gray(` Cache: ${handle.cacheDir}`));
|
|
9561
|
+
console.log(chalk30.gray(` Poll status with ${chalk30.white("/downloads")} \u2014 the pull keeps running even if you exit the REPL (${info2.hardware.diskGb} GB transfer).
|
|
9562
|
+
`));
|
|
9563
|
+
} catch (err) {
|
|
9564
|
+
console.log(chalk30.red(` Download failed to start: ${err.message}
|
|
9565
|
+
`));
|
|
9566
|
+
}
|
|
9567
|
+
rl.prompt();
|
|
9568
|
+
return;
|
|
9569
|
+
}
|
|
9570
|
+
if (input === "/downloads") {
|
|
9571
|
+
const { listDownloads } = await import("./model-download-3NDKS3VM.js");
|
|
9572
|
+
const active = listDownloads();
|
|
9573
|
+
if (active.length === 0) {
|
|
9574
|
+
console.log(chalk30.gray(" No downloads started this session. Try: /model download pyre\n"));
|
|
9575
|
+
} else {
|
|
9576
|
+
console.log(chalk30.gray("\n Model State Last line"));
|
|
9577
|
+
for (const h of active) {
|
|
9578
|
+
const state = h.result == null ? chalk30.yellow("running") : h.result.code === 0 ? chalk30.green("done") : chalk30.red("error");
|
|
9579
|
+
const info2 = MODEL_CATALOG[h.modelId];
|
|
9580
|
+
const label = (info2?.label ?? h.modelId).padEnd(18);
|
|
9581
|
+
const tail2 = (h.lastLine || "").slice(0, 80);
|
|
9582
|
+
console.log(` ${label} ${state.padEnd(20)} ${chalk30.gray(tail2)}`);
|
|
9583
|
+
}
|
|
9584
|
+
console.log("");
|
|
9585
|
+
}
|
|
9586
|
+
rl.prompt();
|
|
9587
|
+
return;
|
|
9588
|
+
}
|
|
9589
|
+
if (input === "/sync-keys") {
|
|
9590
|
+
const { loadCredentials: loadCredentials2, syncByokKeys, getByokSyncPath } = await import("./auth-UAMMP5IJ.js");
|
|
9591
|
+
const creds = await loadCredentials2();
|
|
9592
|
+
if (!creds) {
|
|
9593
|
+
console.log(chalk30.gray(" Not signed in. Run: notch login\n"));
|
|
9594
|
+
rl.prompt();
|
|
9595
|
+
return;
|
|
9596
|
+
}
|
|
9597
|
+
const spinner2 = ora7("Pulling BYOK keys from freesyntax.dev...").start();
|
|
9598
|
+
try {
|
|
9599
|
+
const synced = await syncByokKeys(creds.token);
|
|
9600
|
+
spinner2.stop();
|
|
9601
|
+
const providers = Object.keys(synced.keys);
|
|
9602
|
+
if (providers.length === 0) {
|
|
9603
|
+
console.log(chalk30.gray(` No BYOK keys on your profile yet. Add them at https://freesyntax.dev/settings/keys.
|
|
9604
|
+
`));
|
|
9605
|
+
} else {
|
|
9606
|
+
console.log(chalk30.green(` \u2713 Synced ${providers.length} key(s): ${providers.join(", ")}`));
|
|
9607
|
+
console.log(chalk30.gray(` Cached at ${getByokSyncPath()}
|
|
9608
|
+
`));
|
|
9609
|
+
}
|
|
9610
|
+
} catch (err) {
|
|
9611
|
+
spinner2.stop();
|
|
9612
|
+
console.log(chalk30.red(` Sync failed: ${err.message}
|
|
9613
|
+
`));
|
|
9614
|
+
}
|
|
9615
|
+
rl.prompt();
|
|
9616
|
+
return;
|
|
9617
|
+
}
|
|
9519
9618
|
if (input === "/providers") {
|
|
9520
9619
|
printByokProviderList();
|
|
9521
9620
|
rl.prompt();
|
|
@@ -9586,7 +9685,7 @@ Analyze the above input.`;
|
|
|
9586
9685
|
return;
|
|
9587
9686
|
}
|
|
9588
9687
|
if (input === "/compact") {
|
|
9589
|
-
const { autoCompress: autoCompress2 } = await import("./compression-
|
|
9688
|
+
const { autoCompress: autoCompress2 } = await import("./compression-YJLWEHCC.js");
|
|
9590
9689
|
const before = messages.length;
|
|
9591
9690
|
const compressed = await autoCompress2(messages, model, activeContextWindow(config.models.chat));
|
|
9592
9691
|
messages.length = 0;
|
|
@@ -9889,6 +9988,29 @@ Analyze the above input.`;
|
|
|
9889
9988
|
}
|
|
9890
9989
|
if (input === "/permissions") {
|
|
9891
9990
|
console.log(formatPermissions(permissions));
|
|
9991
|
+
const mode = config.permissionMode === "trust" ? "trust (YOLO)" : config.permissionMode;
|
|
9992
|
+
console.log(chalk30.gray(` Mode: ${mode}${config.autoConfirm ? " (autoConfirm)" : ""}`));
|
|
9993
|
+
console.log(chalk30.gray(" Toggle YOLO with /yolo (persists to .notch.json)."));
|
|
9994
|
+
console.log("");
|
|
9995
|
+
rl.prompt();
|
|
9996
|
+
return;
|
|
9997
|
+
}
|
|
9998
|
+
if (input === "/yolo" || input === "/yes" || input === "/trust" || input === "/strict" || input === "/autoaccept" || input === "/auto") {
|
|
9999
|
+
const turnOn = input === "/yolo" ? config.permissionMode !== "trust" : input === "/yes" || input === "/trust" || input === "/autoaccept" || input === "/auto";
|
|
10000
|
+
config.permissionMode = turnOn ? "trust" : "auto";
|
|
10001
|
+
config.autoConfirm = turnOn;
|
|
10002
|
+
toolCtx.checkPermission = turnOn ? () => "allow" : (toolName, args) => checkPermission(permissions, toolName, args);
|
|
10003
|
+
toolCtx.requireConfirm = !turnOn;
|
|
10004
|
+
toolCtx.autoConfirm = turnOn;
|
|
10005
|
+
try {
|
|
10006
|
+
await persistConfigPatch(config.projectRoot, {
|
|
10007
|
+
permissionMode: config.permissionMode,
|
|
10008
|
+
autoConfirm: config.autoConfirm || void 0
|
|
10009
|
+
});
|
|
10010
|
+
console.log(turnOn ? chalk30.red(` \u{1F525} YOLO mode ON \u2014 all tool calls auto-allowed, no prompts. Saved to .notch.json.`) : chalk30.cyan(` Strict mode ON \u2014 prompts restored. Saved to .notch.json.`));
|
|
10011
|
+
} catch (err) {
|
|
10012
|
+
console.log(chalk30.yellow(` Mode set for this session but persist failed: ${err?.message ?? err}`));
|
|
10013
|
+
}
|
|
9892
10014
|
console.log("");
|
|
9893
10015
|
rl.prompt();
|
|
9894
10016
|
return;
|