@freesyntax/notch-cli 0.4.0 → 0.4.2

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 (2) hide show
  1. package/dist/index.js +74 -36
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22,6 +22,12 @@ import path from "path";
22
22
 
23
23
  // src/providers/registry.ts
24
24
  import { createOpenAI } from "@ai-sdk/openai";
25
+ var MissingApiKeyError = class extends Error {
26
+ constructor() {
27
+ super("NOTCH_API_KEY is not set");
28
+ this.name = "MissingApiKeyError";
29
+ }
30
+ };
25
31
  var MODEL_CATALOG = {
26
32
  "notch-cinder": {
27
33
  id: "notch-cinder",
@@ -74,9 +80,7 @@ function resolveModel(config) {
74
80
  const baseUrl = config.baseUrl ?? process.env.NOTCH_BASE_URL ?? info.baseUrl;
75
81
  const apiKey = config.apiKey ?? process.env.NOTCH_API_KEY;
76
82
  if (!apiKey) {
77
- throw new Error(
78
- "NOTCH_API_KEY is not set. Set it via the NOTCH_API_KEY environment variable or --api-key flag."
79
- );
83
+ throw new MissingApiKeyError();
80
84
  }
81
85
  const provider = createOpenAI({
82
86
  apiKey,
@@ -1186,8 +1190,23 @@ async function runAgentLoop(messages, config) {
1186
1190
  }
1187
1191
  };
1188
1192
  }
1189
- async function buildSystemPrompt(projectRoot) {
1190
- const parts = [
1193
+ var CINDER_SYSTEM_PROMPT_PARTS = [
1194
+ "You are Notch, an expert AI coding assistant. Built by Driftrail.",
1195
+ "Be concise. Think before acting. Do exactly what the user asks, nothing more.",
1196
+ "",
1197
+ "## Tools available",
1198
+ "Read, Write, Edit, Shell, Git, Glob, Grep, WebFetch.",
1199
+ "",
1200
+ "## Core rules",
1201
+ "- Read files before editing them. Never guess at file content.",
1202
+ "- Use exact string matching for edits.",
1203
+ "- If a task is complex, break it into steps.",
1204
+ "- If a command fails, read the error, fix it, and retry. Do not apologize \u2014 just fix it.",
1205
+ "- Keep responses short. No lengthy explanations unless asked."
1206
+ ];
1207
+ async function buildSystemPrompt(projectRoot, modelId) {
1208
+ const isCinder = (modelId ?? "").toLowerCase().includes("cinder");
1209
+ const parts = isCinder ? [...CINDER_SYSTEM_PROMPT_PARTS] : [
1191
1210
  "You are Notch, an expert AI coding assistant built by Driftrail.",
1192
1211
  "You help developers write, debug, refactor, and understand code.",
1193
1212
  "You have access to tools for reading/writing files, running shell commands, searching code, and git operations.",
@@ -1214,7 +1233,9 @@ async function buildSystemPrompt(projectRoot) {
1214
1233
  }
1215
1234
  } catch {
1216
1235
  }
1217
- parts.push("", "## Available Tools", describeTools());
1236
+ if (!isCinder) {
1237
+ parts.push("", "## Available Tools", describeTools());
1238
+ }
1218
1239
  return parts.join("\n");
1219
1240
  }
1220
1241
 
@@ -2770,26 +2791,18 @@ function formatThemeList(activeId) {
2770
2791
 
2771
2792
  // src/ui/banner.ts
2772
2793
  var MANTIS = [
2773
- " \u25C9\u2588\u2580\u2580\u2580\u2580\u2588\u25C9",
2774
- // head + eyes
2775
- " \u2580\u2588\u2584\u2584\u2588\u2580",
2776
- // jaw
2777
- " \u2590\u258C",
2778
- // thin neck
2779
- " \u2588\u2580 \u2590\u258C \u2580\u2588",
2780
- // forelegs out
2781
- " \u2580\u2588\u2584\u2588\u2588\u2584\u2588\u2580",
2782
- // forelegs folding (prayer)
2783
- " \u2590\u2588\u2588\u258C",
2784
- // thorax
2785
- " \u2590\u2588\u2588\u258C",
2786
- // abdomen
2787
- " \u2590\u2588\u2588\u258C",
2788
- // abdomen
2789
- " \u2584\u2580 \u2580\u2584",
2790
- // legs splay
2791
- " \u2580 \u2580"
2792
- // feet
2794
+ " \u2571\u25C9\u25C9\u2572",
2795
+ // ╱◉◉╲ antennae + eyes
2796
+ " \u2588\u2588\u2588",
2797
+ // ███ head
2798
+ " \u2590\u2588\u258C",
2799
+ // ▐█▌ neck
2800
+ " \u2584\u2580\u2590\u2588\u258C\u2580\u2584",
2801
+ // ▄▀▐█▌▀▄ prayer arms
2802
+ " \u2590\u2588\u258C",
2803
+ // ▐█▌ abdomen
2804
+ " \u2580\u2580 \u2580\u2580"
2805
+ // ▀▀ ▀▀ feet
2793
2806
  ];
2794
2807
  var LOGO_INLINE = [
2795
2808
  " \u2588\u2588\u2584 \u2588 \u2584\u2580\u2580\u2584 \u2580\u2588\u2580 \u2584\u2580\u2580\u2584 \u2588 \u2588",
@@ -2798,7 +2811,7 @@ var LOGO_INLINE = [
2798
2811
  ];
2799
2812
  function colorMantis(line) {
2800
2813
  const t = theme();
2801
- return line.replace(/\u25c9/g, t.mascotAccent("\u25C9")).replace(/[\u2588]/g, (ch) => t.mascot(ch)).replace(/[\u2584\u2580]/g, (ch) => t.mascot(ch)).replace(/\u2590/g, t.mascot("\u2590")).replace(/\u258c/g, t.mascot("\u258C"));
2814
+ return line.replace(/[\u2571\u2572]/g, (ch) => t.mascot(ch)).replace(/\u25c9/g, t.mascotAccent("\u25C9")).replace(/[\u2588]/g, (ch) => t.mascot(ch)).replace(/[\u2584\u2580]/g, (ch) => t.mascot(ch)).replace(/\u2590/g, t.mascot("\u2590")).replace(/\u258c/g, t.mascot("\u258C"));
2802
2815
  }
2803
2816
  function printBanner(version, modelLabel, modelId, modelSize, project) {
2804
2817
  const t = theme();
@@ -2816,7 +2829,7 @@ function printBanner(version, modelLabel, modelId, modelSize, project) {
2816
2829
  const divider = t.border(" " + "\u2500".repeat(divWidth));
2817
2830
  console.log(divider);
2818
2831
  console.log(
2819
- t.dim(" ") + t.bold(modelLabel) + t.dim(` (${modelSize})`) + t.dim(" \u2502 v") + t.text(version) + t.dim(" \u2502 ") + t.dim("by ") + t.tagline("Driftrail")
2832
+ t.dim(" ") + t.bold(modelLabel) + t.dim(" \u2502 v") + t.text(version) + t.dim(" \u2502 ") + t.dim("by ") + t.tagline("Driftrail")
2820
2833
  );
2821
2834
  console.log(t.dim(` ${project}`));
2822
2835
  console.log(divider);
@@ -3716,7 +3729,7 @@ function completeFilePath(partial, cwd) {
3716
3729
 
3717
3730
  // src/index.ts
3718
3731
  import fs18 from "fs/promises";
3719
- var VERSION = "0.4.0";
3732
+ var VERSION = "0.4.1";
3720
3733
  var modelChoices = MODEL_IDS.join(", ");
3721
3734
  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})`).option("--base-url <url>", "Override Notch API base URL").option("--api-key <key>", "Notch API key (prefer NOTCH_API_KEY env var)").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").parse(process.argv);
3722
3735
  var opts = program.opts();
@@ -3728,9 +3741,8 @@ function printModelTable(activeModel) {
3728
3741
  const info = MODEL_CATALOG[id];
3729
3742
  const active = id === activeModel ? t.success(" \u25CF") : " ";
3730
3743
  const label = id === activeModel ? t.bold(`${info.label}`) : t.dim(`${info.label}`);
3731
- const size = t.info(info.size.padEnd(4));
3732
3744
  const ctx = t.dim(`${(info.contextWindow / 1024).toFixed(0)}K ctx`);
3733
- console.log(` ${active} ${t.brand(id.padEnd(14))} ${size} ${label} ${ctx}`);
3745
+ console.log(` ${active} ${t.brand(id.padEnd(14))} ${label} ${ctx}`);
3734
3746
  }
3735
3747
  console.log(t.dim(`
3736
3748
  Switch with: /model <name>
@@ -3880,7 +3892,31 @@ async function main() {
3880
3892
  setTheme(config.theme);
3881
3893
  }
3882
3894
  let activeModelId = config.models.chat.model;
3883
- let model = resolveModel(config.models.chat);
3895
+ let model;
3896
+ try {
3897
+ model = resolveModel(config.models.chat);
3898
+ } catch (err) {
3899
+ if (err instanceof MissingApiKeyError) {
3900
+ console.log("");
3901
+ console.log(" \x1B[1m\x1B[36m\u26A1 Welcome to Notch CLI\x1B[0m");
3902
+ console.log("");
3903
+ console.log(" To get started, you need a Notch API key.");
3904
+ console.log("");
3905
+ console.log(" \x1B[1mOption 1:\x1B[0m Log in via browser (recommended)");
3906
+ console.log(" \x1B[33m$ notch login\x1B[0m");
3907
+ console.log("");
3908
+ console.log(" \x1B[1mOption 2:\x1B[0m Set your API key directly");
3909
+ console.log(" \x1B[33m$ export NOTCH_API_KEY=your-key-here\x1B[0m");
3910
+ console.log("");
3911
+ console.log(" \x1B[1mOption 3:\x1B[0m Pass it inline");
3912
+ console.log(" \x1B[33m$ notch --api-key your-key-here\x1B[0m");
3913
+ console.log("");
3914
+ console.log(" Get your key at: \x1B[4mhttps://freesyntax.com/settings\x1B[0m");
3915
+ console.log("");
3916
+ process.exit(0);
3917
+ }
3918
+ throw err;
3919
+ }
3884
3920
  const info = MODEL_CATALOG[activeModelId];
3885
3921
  printBanner(VERSION, info.label, info.id, info.size, config.projectRoot);
3886
3922
  checkForUpdates(VERSION).then((msg) => {
@@ -3912,10 +3948,12 @@ async function main() {
3912
3948
  spinner.warn("Could not build repo map");
3913
3949
  }
3914
3950
  }
3915
- const baseSystemPrompt = await buildSystemPrompt(config.projectRoot);
3951
+ const baseSystemPrompt = await buildSystemPrompt(config.projectRoot, activeModelId);
3952
+ const isCinder = activeModelId.includes("cinder");
3916
3953
  const systemPrompt = [
3917
3954
  baseSystemPrompt,
3918
- repoMapStr ? `
3955
+ // Skip repo map for Cinder — too many tokens for a 4B model
3956
+ !isCinder && repoMapStr ? `
3919
3957
  ## Repository Map
3920
3958
  ${repoMapStr}` : ""
3921
3959
  ].join("");
@@ -4126,7 +4164,7 @@ Analyze the above input.`;
4126
4164
  config.models.chat.model = activeModelId;
4127
4165
  model = resolveModel(config.models.chat);
4128
4166
  const switchedInfo = MODEL_CATALOG[activeModelId];
4129
- console.log(chalk8.green(` Switched to ${switchedInfo.label} (${switchedInfo.id}, ${switchedInfo.size})
4167
+ console.log(chalk8.green(` Switched to ${switchedInfo.label} (${switchedInfo.id})
4130
4168
  `));
4131
4169
  rl.prompt();
4132
4170
  return;
@@ -4707,7 +4745,7 @@ async function handleRalphSubcommand(args, cliOpts) {
4707
4745
  const config = await loadConfig(cliOpts.cwd ? { projectRoot: cliOpts.cwd } : {});
4708
4746
  if (cliOpts.model) config.models.chat.model = cliOpts.model;
4709
4747
  const model = resolveModel(config.models.chat);
4710
- const systemPrompt = await buildSystemPrompt(config.projectRoot);
4748
+ const systemPrompt = await buildSystemPrompt(config.projectRoot, config.models.chat.model);
4711
4749
  const toolCtx = {
4712
4750
  cwd: config.projectRoot,
4713
4751
  requireConfirm: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesyntax/notch-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Notch CLI — AI-powered coding assistant by Driftrail",
5
5
  "type": "module",
6
6
  "bin": {