@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.
Files changed (37) hide show
  1. package/dist/{apply-patch-D5PDUXUC.js → apply-patch-U6K67CMT.js} +1 -0
  2. package/dist/auth-UAMMP5IJ.js +29 -0
  3. package/dist/{chunk-OSWUX6TC.js → chunk-4HPRBCSY.js} +1 -1
  4. package/dist/{chunk-QKM27RHS.js → chunk-6NKRMZTX.js} +1 -1
  5. package/dist/{chunk-TU465P2P.js → chunk-EPSOOCNB.js} +4 -2
  6. package/dist/{chunk-MMBFNIKE.js → chunk-FZVPGJJW.js} +5 -3
  7. package/dist/chunk-J66N6AFH.js +137 -0
  8. package/dist/{chunk-443G6HCC.js → chunk-JXQ4HZ47.js} +56 -55
  9. package/dist/chunk-KCAR5DOB.js +52 -0
  10. package/dist/chunk-KFQGP6VL.js +33 -0
  11. package/dist/chunk-O6AKZ4OH.js +0 -0
  12. package/dist/{chunk-FIFC4V2R.js → chunk-PPEBWOMJ.js} +91 -7
  13. package/dist/{compression-SQAIQ2UU.js → compression-YJLWEHCC.js} +1 -0
  14. package/dist/config-set-3IWEVZQ4.js +110 -0
  15. package/dist/{edit-JEFEK43H.js → edit-6QYAXVNU.js} +1 -0
  16. package/dist/{git-5T5TSQTX.js → git-DNQ5EELH.js} +1 -0
  17. package/dist/{github-DWRGWX6U.js → github-34T4QQIH.js} +1 -0
  18. package/dist/{glob-BI3P4C7Q.js → glob-XT43LEJ4.js} +1 -0
  19. package/dist/{grep-VZ3I5GNW.js → grep-T2CXYNRI.js} +1 -0
  20. package/dist/index.js +420 -298
  21. package/dist/{lsp-UPY6I3L7.js → lsp-JXQVU7NP.js} +1 -0
  22. package/dist/model-download-3NDKS3VM.js +176 -0
  23. package/dist/{notebook-FXJBTSPA.js → notebook-MFODW345.js} +1 -0
  24. package/dist/{ollama-bench-QQHBIG2D.js → ollama-bench-5V5CCOCQ.js} +6 -2
  25. package/dist/{ollama-launch-2ASVER3S.js → ollama-launch-P5KBK7AJ.js} +6 -2
  26. package/dist/{ollama-usage-2WPCZJJI.js → ollama-usage-3PROM2WC.js} +1 -0
  27. package/dist/{plugins-OG2P75K5.js → plugins-PNGRZLFW.js} +1 -0
  28. package/dist/{read-OVJG2XKW.js → read-B64XE7N3.js} +1 -0
  29. package/dist/{server-7UQKCB2Z.js → server-IGOZHW52.js} +17 -15
  30. package/dist/{session-index-SSGOOZXK.js → session-index-7FWEVP6E.js} +3 -2
  31. package/dist/{shell-4X545EVN.js → shell-BOZTHQUT.js} +1 -0
  32. package/dist/{task-OS3E5F3X.js → task-67G4KLYC.js} +1 -0
  33. package/dist/{tools-7WAWS6V4.js → tools-XWKCW4RN.js} +4 -3
  34. package/dist/{web-fetch-KNIV3Z3W.js → web-fetch-OTNDICGJ.js} +1 -0
  35. package/dist/{write-NNHLOTYK.js → write-ZOSB7I4J.js} +1 -0
  36. package/package.json +1 -1
  37. 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-OSWUX6TC.js";
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-TU465P2P.js";
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-QKM27RHS.js";
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-443G6HCC.js";
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-FIFC4V2R.js";
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 fs2 } from "fs";
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 fs2.stat(abs);
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 fs2.readFile(abs);
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 fs5 } from "fs";
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 fs3 from "fs/promises";
394
- import path2 from "path";
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 = path2.join(homeDir, file);
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 = path2.join(projectRoot, file);
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 fs3.readFile(filePath, "utf-8");
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 fs4 from "fs/promises";
440
- import path3 from "path";
352
+ import fs3 from "fs/promises";
353
+ import path2 from "path";
441
354
  import os3 from "os";
442
- var MEMORY_DIR = path3.join(os3.homedir(), ".notch", "memory");
443
- var INDEX_FILE = path3.join(MEMORY_DIR, "MEMORY.md");
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 fs4.mkdir(MEMORY_DIR, { recursive: true });
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 = path3.join(MEMORY_DIR, filename);
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 fs4.writeFile(filePath, fileContent, "utf-8");
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 fs4.readdir(MEMORY_DIR);
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 fs4.readFile(path3.join(MEMORY_DIR, file), "utf-8");
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 fs4.unlink(path3.join(MEMORY_DIR, filename));
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 fs4.writeFile(INDEX_FILE, lines.join("\n"), "utf-8");
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 fs5.readdir(projectRoot);
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 fs6 from "fs/promises";
1013
- import path4 from "path";
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 fs6.readFile(filePath, "utf-8");
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 fs6.readFile(filePath, "utf-8");
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 fs6.unlink(snap.path);
969
+ await fs5.unlink(snap.path);
1057
970
  } catch {
1058
971
  }
1059
972
  } else {
1060
- await fs6.mkdir(path4.dirname(snap.path), { recursive: true });
1061
- await fs6.writeFile(snap.path, snap.before, "utf-8");
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(([path21, { before, after }]) => ({
1094
- path: path21,
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 fs8 from "fs/promises";
1493
- import path6 from "path";
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 fs7 from "fs/promises";
1499
- import path5 from "path";
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 = path5.extname(filePath).slice(1);
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 = path5.resolve(root, file);
1523
+ const fullPath = path4.resolve(root, file);
1611
1524
  try {
1612
- const content = await fs7.readFile(fullPath, "utf-8");
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 fs8.writeFile(path6.join(cwd, PLAN_FILE), JSON.stringify(plan, null, 2), "utf-8");
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 fs8.readFile(path6.join(cwd, PLAN_FILE), "utf-8");
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 fs8.unlink(path6.join(cwd, PLAN_FILE));
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 fs9 from "fs/promises";
1910
- import path7 from "path";
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 = path7.isAbsolute(ref) ? ref : path7.resolve(cwd, ref);
1866
+ const filePath = path6.isAbsolute(ref) ? ref : path6.resolve(cwd, ref);
1954
1867
  try {
1955
- const content = await fs9.readFile(filePath, "utf-8");
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 fs10 from "fs/promises";
2681
- import path8 from "path";
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 = path8.join(os4.homedir(), ".notch");
2688
- var CACHE_FILE = path8.join(NOTCH_DIR, "update-check.json");
2689
- var LOG_FILE = path8.join(NOTCH_DIR, "update-log.txt");
2690
- var CONFIG_FILE = path8.join(NOTCH_DIR, "config.json");
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 fs10.readFile(CONFIG_FILE, "utf-8");
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 fs10.readFile(CACHE_FILE, "utf-8");
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 fs10.realpath(entry);
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 = path8.parse(dir).root;
2783
+ const root = path7.parse(dir).root;
2871
2784
  while (dir !== root) {
2872
- const localNM = path8.join(dir, "node_modules");
2785
+ const localNM = path7.join(dir, "node_modules");
2873
2786
  if (pathIsInside(entryReal, localNM)) return "local";
2874
- const parent = path8.dirname(dir);
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 = path8.relative(path8.resolve(parent), path8.resolve(child));
2885
- return !!rel && !rel.startsWith("..") && !path8.isAbsolute(rel);
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 fs10.mkdir(NOTCH_DIR, { recursive: true });
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 fs10.readFile(LOG_FILE, "utf-8");
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 fs10.writeFile(tmp, contents, "utf-8");
2880
+ await fs9.writeFile(tmp, contents, "utf-8");
2968
2881
  try {
2969
- await fs10.rename(tmp, file);
2882
+ await fs9.rename(tmp, file);
2970
2883
  } catch (err) {
2971
2884
  try {
2972
- await fs10.copyFile(tmp, file);
2973
- await fs10.unlink(tmp).catch(() => {
2885
+ await fs9.copyFile(tmp, file);
2886
+ await fs9.unlink(tmp).catch(() => {
2974
2887
  });
2975
2888
  } catch {
2976
- await fs10.unlink(tmp).catch(() => {
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 path9 from "path";
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 ? path9.dirname(entry) : fileURLToPath(new URL(".", import.meta.url));
3005
- const root = path9.parse(dir).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 = path9.join(dir, "package.json");
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 = path9.dirname(dir);
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 fs11 from "fs/promises";
3138
- import path11 from "path";
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 path10 = {
4112
+ var path9 = {
4200
4113
  win32: { sep: "\\" },
4201
4114
  posix: { sep: "/" }
4202
4115
  };
4203
- var sep = defaultPlatform === "win32" ? path10.win32.sep : path10.posix.sep;
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 = path11.join(projectRoot, ".notch.json");
5001
- const globalPath = path11.join(os5.homedir(), ".notch", "permissions.json");
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 fs11.readFile(globalPath, "utf-8");
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 fs11.readFile(projectPath, "utf-8");
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 fs12 from "fs/promises";
5065
+ import fs11 from "fs/promises";
5153
5066
  import { watch } from "fs";
5154
- import path12 from "path";
5067
+ import path11 from "path";
5155
5068
  import os6 from "os";
5156
5069
  import crypto from "crypto";
5157
- var TRUST_STORE_PATH = path12.join(os6.homedir(), ".notch", "trusted-projects.json");
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 = path12.resolve(projectRoot);
5073
+ const key = path11.resolve(projectRoot);
5161
5074
  try {
5162
- const store = JSON.parse(await fs12.readFile(TRUST_STORE_PATH, "utf-8"));
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 = path12.resolve(projectRoot);
5083
+ const key = path11.resolve(projectRoot);
5171
5084
  let store = {};
5172
5085
  try {
5173
- store = JSON.parse(await fs12.readFile(TRUST_STORE_PATH, "utf-8"));
5086
+ store = JSON.parse(await fs11.readFile(TRUST_STORE_PATH, "utf-8"));
5174
5087
  } catch {
5175
5088
  }
5176
5089
  store[key] = fingerprint;
5177
- await fs12.mkdir(path12.dirname(TRUST_STORE_PATH), { recursive: true });
5178
- await fs12.writeFile(TRUST_STORE_PATH, JSON.stringify(store, null, 2));
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 = path12.join(os6.homedir(), ".notch", "hooks.json");
5095
+ const globalPath = path11.join(os6.homedir(), ".notch", "hooks.json");
5183
5096
  try {
5184
- const raw = await fs12.readFile(globalPath, "utf-8");
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 = path12.join(projectRoot, ".notch.json");
5104
+ const projectPath = path11.join(projectRoot, ".notch.json");
5192
5105
  try {
5193
- const raw = await fs12.readFile(projectPath, "utf-8");
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 = path12.join(projectRoot, filename);
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 fs13 from "fs/promises";
5309
- import path13 from "path";
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 = path13.join(os7.homedir(), ".notch", "sessions");
5232
+ var SESSION_DIR = path12.join(os7.homedir(), ".notch", "sessions");
5320
5233
  var MAX_SESSIONS = 20;
5321
5234
  async function ensureDir2() {
5322
- await fs13.mkdir(SESSION_DIR, { recursive: true });
5235
+ await fs12.mkdir(SESSION_DIR, { recursive: true });
5323
5236
  }
5324
5237
  function sessionPath(id) {
5325
- return path13.join(SESSION_DIR, `${id}.json`);
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 fs13.writeFile(sessionPath(id), JSON.stringify(session, null, 2), "utf-8");
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 fs13.readFile(sessionPath(id), "utf-8");
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 fs13.readdir(SESSION_DIR);
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 fs13.readFile(path13.join(SESSION_DIR, file), "utf-8");
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 fs13.unlink(sessionPath(id));
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 fs13.writeFile(outputPath, lines.join("\n"), "utf-8");
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 fs14 from "fs/promises";
5447
- import path14 from "path";
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 fs14.access(p);
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 ${path14.relative(ctx.projectRoot, p)} (already exists)`));
5372
+ ctx.log(chalk8.gray(` Skipped ${path13.relative(ctx.projectRoot, p)} (already exists)`));
5460
5373
  return false;
5461
5374
  }
5462
- await fs14.mkdir(path14.dirname(p), { recursive: true });
5463
- await fs14.writeFile(p, content, "utf-8");
5464
- ctx.log(chalk8.green(` Created ${path14.relative(ctx.projectRoot, p)}`));
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 fs14.readFile(p, "utf-8");
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 fs14.writeFile(p, JSON.stringify(next, null, 2) + "\n", "utf-8");
5479
- ctx.log(chalk8.green(` Patched ${path14.relative(ctx.projectRoot, p)}`));
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 fs14.readFile(p, "utf-8");
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 fs14.appendFile(p, append, "utf-8");
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
- path14.join(ctx.projectRoot, ".notch.json"),
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
- path14.join(ctx.projectRoot, ".notch.md"),
5457
+ path13.join(ctx.projectRoot, ".notch.md"),
5545
5458
  DEFAULT_INSTRUCTIONS,
5546
5459
  ctx
5547
5460
  );
5548
5461
  await ensureGitignoreEntries(
5549
- path14.join(ctx.projectRoot, ".gitignore"),
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
- path14.join(ctx.projectRoot, ".notch.json"),
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
- path14.join(ctx.projectRoot, ".notch.json"),
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 = path14.join(ctx.projectRoot, ".notch", "agents");
5600
- await fs14.mkdir(dir, { recursive: true });
5512
+ const dir = path13.join(ctx.projectRoot, ".notch", "agents");
5513
+ await fs13.mkdir(dir, { recursive: true });
5601
5514
  await writeIfMissing(
5602
- path14.join(dir, "README.md"),
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
- path14.join(ctx.projectRoot, ".notch.json"),
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
- path14.join(ctx.projectRoot, ".notch.json"),
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
- path14.join(ctx.projectRoot, "AGENTS.md"),
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 fs15 from "fs/promises";
5808
+ import fs14 from "fs/promises";
5893
5809
  async function loadOutputSchema(filePath) {
5894
- const raw = await fs15.readFile(filePath, "utf-8");
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 fs16 from "fs/promises";
6112
- import path15 from "path";
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 fs16.access(path15.join(cwd, ".notch.json"));
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 = path15.join(os8.homedir(), ".notch");
6075
+ const notchDir = path14.join(os8.homedir(), ".notch");
6160
6076
  try {
6161
- await fs16.access(notchDir);
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 fs16.access(path15.join(cwd, ".notch.md"));
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 fs16.readFile(path15.join(cwd, ".notch.json"), "utf-8").catch(() => "{}");
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 = path15.join(notchDir, "sessions");
6195
- const entries = await fs16.readdir(sessionsDir).catch(() => []);
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 fs17 from "fs/promises";
6598
- import path16 from "path";
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 = path16.join(os9.homedir(), ".notch", "plugins");
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 fs17.mkdir(GLOBAL_PLUGINS_DIR, { recursive: true });
6638
- const pluginDir = path16.join(GLOBAL_PLUGINS_DIR, path16.basename(target).replace(/\.git$/, ""));
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 fs17.mkdir(pluginDir, { recursive: true });
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 fs17.access(path16.join(pluginDir, "package.json")).then(() => true).catch(() => false);
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 = path16.join(GLOBAL_PLUGINS_DIR, target);
6605
+ const pluginDir = path15.join(GLOBAL_PLUGINS_DIR, target);
6690
6606
  try {
6691
- await fs17.access(pluginDir);
6692
- await fs17.rm(pluginDir, { recursive: true, force: true });
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 fs18 from "fs";
7220
+ import fs17 from "fs";
7305
7221
  import fsp3 from "fs/promises";
7306
7222
  import os10 from "os";
7307
- import path17 from "path";
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 path17.join(os10.tmpdir(), "notch-skills", `${skill.id}-${sha8}`);
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 = path17.dirname(target);
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 = path17.normalize(relPath);
7285
+ const normalized = path16.normalize(relPath);
7370
7286
  const parts = normalized.split(/[\\/]/);
7371
- if (path17.isAbsolute(normalized) || parts.includes("..")) {
7287
+ if (path16.isAbsolute(normalized) || parts.includes("..")) {
7372
7288
  throw new Error(`skill file path escapes skill dir: ${relPath}`);
7373
7289
  }
7374
- return path17.join(baseDir, normalized);
7290
+ return path16.join(baseDir, normalized);
7375
7291
  }
7376
7292
  function cleanupSkills() {
7377
7293
  for (const dir of extractedDirs) {
7378
7294
  try {
7379
- fs18.rmSync(dir, { recursive: true, force: true });
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 fs19 from "fs";
7779
- import path18 from "path";
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("/") ? path18.resolve(cwd, path18.dirname(partial)) : cwd;
7873
- const prefix = partial.includes("/") ? path18.basename(partial) : partial;
7874
- const entries = fs19.readdirSync(dir, { withFileTypes: true });
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("/") ? path18.dirname(partial) + "/" + entry.name : entry.name;
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 fs20 from "fs/promises";
8162
- import path19 from "path";
8082
+ import fs19 from "fs/promises";
8083
+ import path18 from "path";
8163
8084
  import os11 from "os";
8164
- var NOTCH_DIR2 = path19.join(os11.homedir(), ".notch");
8165
- var SESSION_DIR2 = path19.join(NOTCH_DIR2, "sessions");
8166
- var STATE_FILE = path19.join(NOTCH_DIR2, "dream-state.json");
8167
- var LOCK_FILE = path19.join(NOTCH_DIR2, ".dream.lock");
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 fs20.mkdir(NOTCH_DIR2, { recursive: true });
8093
+ await fs19.mkdir(NOTCH_DIR2, { recursive: true });
8173
8094
  }
8174
8095
  async function readState() {
8175
8096
  try {
8176
- const raw = await fs20.readFile(STATE_FILE, "utf-8");
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 fs20.writeFile(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
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 fs20.readdir(SESSION_DIR2);
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 fs20.stat(path19.join(SESSION_DIR2, name));
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 fs20.open(LOCK_FILE, "wx");
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 fs20.unlink(LOCK_FILE);
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 path20 from "path";
8186
+ import path19 from "path";
8266
8187
  import os12 from "os";
8267
- var MEMORY_DIR2 = path20.join(os12.homedir(), ".notch", "memory");
8268
- var SESSION_DIR3 = path20.join(os12.homedir(), ".notch", "sessions");
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 fs21 from "fs/promises";
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-2ASVER3S.js");
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 fs21.readFile(p, "utf-8");
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 fs21.writeFile(p, JSON.stringify(current, null, 2) + "\n", "utf-8");
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 keyPresent = p.apiKeyEnv ? process.env[p.apiKeyEnv] ? t.success("\u2713") : t.dim("\u2717") : t.dim("\u2013");
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-JQX6MHJG.js")).getCredentialsPath()}
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-7UQKCB2Z.js");
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? [y/N] "), (answer) => {
8957
+ rl2.question(chalk30.yellow("\nAllow these hooks for this project? [Y/n] "), (answer) => {
8964
8958
  rl2.close();
8965
- resolve3(answer.trim().toLowerCase() === "y");
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-2WPCZJJI.js");
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 fs21.readFile(nodePath2.resolve(config.projectRoot, ".notch.json"), "utf-8").catch(() => "{}");
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: config.permissionMode !== "trust" && !config.autoConfirm,
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} (y/N) `, (answer) => {
9085
+ rl2.question(`${message} (Y/n) `, (answer) => {
9089
9086
  rl2.close();
9090
- resolve3(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
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 fs21.writeFile(opts.outputLastMessage, response.text, "utf-8");
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-SQAIQ2UU.js");
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;