@freesyntax/notch-cli 0.5.16 → 0.5.17

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 +83 -85
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2450,16 +2450,6 @@ async function updateIndex() {
2450
2450
  }
2451
2451
 
2452
2452
  // src/agent/loop.ts
2453
- var MAX_RETRIES = 3;
2454
- var RETRY_DELAYS = [1e3, 3e3, 8e3];
2455
- function isRetryableError(err) {
2456
- if (!(err instanceof Error)) return false;
2457
- const msg = err.message.toLowerCase();
2458
- return msg.includes("502") || msg.includes("503") || msg.includes("429") || msg.includes("rate limit") || msg.includes("timeout") || msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("fetch failed") || msg.includes("network") || msg.includes("aborted");
2459
- }
2460
- async function sleep(ms) {
2461
- return new Promise((resolve2) => setTimeout(resolve2, ms));
2462
- }
2463
2453
  function getErrorSignature(toolName, result) {
2464
2454
  return {
2465
2455
  toolName,
@@ -2500,81 +2490,44 @@ async function runAgentLoop(messages, config) {
2500
2490
  const toolCalls = [];
2501
2491
  const toolResults = [];
2502
2492
  let streamUsage = null;
2503
- let lastError = null;
2504
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
2505
- if (attempt > 0) {
2506
- const delay = RETRY_DELAYS[Math.min(attempt - 1, RETRY_DELAYS.length - 1)];
2507
- config.onRetry?.(attempt, lastError instanceof Error ? lastError.message : "unknown");
2508
- await sleep(delay);
2509
- }
2510
- try {
2511
- fullText = "";
2512
- toolCalls.length = 0;
2513
- toolResults.length = 0;
2514
- const result = streamText({
2515
- model: config.model,
2516
- system: config.systemPrompt,
2517
- messages: history,
2518
- tools,
2519
- maxSteps: 1
2493
+ const result = streamText({
2494
+ model: config.model,
2495
+ system: config.systemPrompt,
2496
+ messages: history,
2497
+ tools,
2498
+ maxSteps: 1
2499
+ });
2500
+ for await (const event of result.fullStream) {
2501
+ if (event.type === "text-delta") {
2502
+ fullText += event.textDelta;
2503
+ config.onTextChunk?.(event.textDelta);
2504
+ } else if (event.type === "tool-call") {
2505
+ toolCalls.push({
2506
+ toolCallId: event.toolCallId,
2507
+ toolName: event.toolName,
2508
+ args: event.args
2520
2509
  });
2521
- for await (const event of result.fullStream) {
2522
- if (event.type === "text-delta") {
2523
- fullText += event.textDelta;
2524
- config.onTextChunk?.(event.textDelta);
2525
- } else if (event.type === "tool-call") {
2526
- toolCalls.push({
2527
- toolCallId: event.toolCallId,
2528
- toolName: event.toolName,
2529
- args: event.args
2530
- });
2531
- config.onToolCall?.(event.toolName, event.args);
2532
- }
2533
- const evt = event;
2534
- if (evt.type === "tool-result") {
2535
- const res = evt.result;
2536
- toolResults.push({
2537
- toolCallId: evt.toolCallId,
2538
- result: evt.result
2539
- });
2540
- config.onToolResult?.(
2541
- toolCalls.find((tc) => tc.toolCallId === evt.toolCallId)?.toolName ?? "unknown",
2542
- res?.content ?? String(evt.result),
2543
- res?.isError ?? false
2544
- );
2545
- }
2546
- }
2547
- try {
2548
- const u = await result.usage;
2549
- if (u) streamUsage = u;
2550
- } catch {
2551
- }
2552
- lastError = null;
2553
- break;
2554
- } catch (err) {
2555
- lastError = err;
2556
- if (attempt < MAX_RETRIES && isRetryableError(err)) {
2557
- continue;
2558
- }
2559
- const errMsg = err instanceof Error ? err.message : String(err);
2560
- history.push({
2561
- role: "assistant",
2562
- content: `[Error: ${errMsg}. The model endpoint may be unavailable. Try again or switch models with /model.]`
2510
+ config.onToolCall?.(event.toolName, event.args);
2511
+ }
2512
+ const evt = event;
2513
+ if (evt.type === "tool-result") {
2514
+ const res = evt.result;
2515
+ toolResults.push({
2516
+ toolCallId: evt.toolCallId,
2517
+ result: evt.result
2563
2518
  });
2564
- return {
2565
- text: `[Error: ${errMsg}]`,
2566
- messages: history,
2567
- iterations,
2568
- toolCallCount: totalToolCalls,
2569
- compressed: wasCompressed,
2570
- usage: {
2571
- promptTokens: totalPromptTokens,
2572
- completionTokens: totalCompletionTokens,
2573
- totalTokens: totalPromptTokens + totalCompletionTokens
2574
- }
2575
- };
2519
+ config.onToolResult?.(
2520
+ toolCalls.find((tc) => tc.toolCallId === evt.toolCallId)?.toolName ?? "unknown",
2521
+ res?.content ?? String(evt.result),
2522
+ res?.isError ?? false
2523
+ );
2576
2524
  }
2577
2525
  }
2526
+ try {
2527
+ const u = await result.usage;
2528
+ if (u) streamUsage = u;
2529
+ } catch {
2530
+ }
2578
2531
  if (streamUsage) {
2579
2532
  totalPromptTokens += streamUsage.promptTokens ?? 0;
2580
2533
  totalCompletionTokens += streamUsage.completionTokens ?? 0;
@@ -2881,12 +2834,12 @@ async function withRetry(fn, options = {}) {
2881
2834
  delay += Math.random() * delay * 0.5;
2882
2835
  }
2883
2836
  options.onRetry?.(attempt, err, delay);
2884
- await sleep2(delay);
2837
+ await sleep(delay);
2885
2838
  }
2886
2839
  }
2887
2840
  throw lastError;
2888
2841
  }
2889
- function sleep2(ms) {
2842
+ function sleep(ms) {
2890
2843
  return new Promise((resolve2) => setTimeout(resolve2, ms));
2891
2844
  }
2892
2845
 
@@ -3340,16 +3293,45 @@ async function buildRepoMap(root) {
3340
3293
  "**/.git/**",
3341
3294
  "**/dist/**",
3342
3295
  "**/build/**",
3296
+ "**/out/**",
3297
+ "**/.next/**",
3298
+ "**/.nuxt/**",
3299
+ "**/.output/**",
3343
3300
  "**/__pycache__/**",
3344
3301
  "**/target/**",
3302
+ "**/.venv/**",
3303
+ "**/venv/**",
3345
3304
  "**/*.test.*",
3346
3305
  "**/*.spec.*",
3347
- "**/*.d.ts"
3306
+ "**/*.d.ts",
3307
+ "**/*.config.*",
3308
+ // vite.config, tailwind.config, etc.
3309
+ "**/*.setup.*",
3310
+ // vitest.setup, jest.setup
3311
+ "**/postcss.config.*",
3312
+ "**/tsconfig.*",
3313
+ "**/.eslintrc.*",
3314
+ "**/.prettierrc.*",
3315
+ "**/env.d.ts",
3316
+ "**/vite-env.d.ts",
3317
+ "**/global.d.ts",
3318
+ "**/globals.d.ts",
3319
+ "**/next-env.d.ts",
3320
+ "**/migrations/**",
3321
+ "**/generated/**",
3322
+ "**/*.min.*",
3323
+ "**/vendor/**",
3324
+ "**/public/**",
3325
+ "**/coverage/**",
3326
+ "**/.cache/**",
3327
+ "**/lock.*",
3328
+ "**/*-lock.*"
3348
3329
  ],
3349
3330
  nodir: true
3350
3331
  });
3351
3332
  const entries = [];
3352
- for (const file of files.slice(0, 500)) {
3333
+ files.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
3334
+ for (const file of files.slice(0, 300)) {
3353
3335
  const fullPath = path15.resolve(root, file);
3354
3336
  try {
3355
3337
  const content = await fs12.readFile(fullPath, "utf-8");
@@ -7698,6 +7680,19 @@ Analyze the above input.`;
7698
7680
  }
7699
7681
  messages.push({ role: "user", content: finalPrompt });
7700
7682
  const spinner = ora4("Thinking...").start();
7683
+ const spinnerStart = Date.now();
7684
+ const spinnerTimer = setInterval(() => {
7685
+ if (!spinner.isSpinning) {
7686
+ clearInterval(spinnerTimer);
7687
+ return;
7688
+ }
7689
+ const elapsed = Math.floor((Date.now() - spinnerStart) / 1e3);
7690
+ if (elapsed >= 30) {
7691
+ spinner.text = `Waiting for model... (${elapsed}s \u2014 endpoint may be cold-starting)`;
7692
+ } else if (elapsed >= 10) {
7693
+ spinner.text = `Thinking... (${elapsed}s)`;
7694
+ }
7695
+ }, 2e3);
7701
7696
  try {
7702
7697
  const response = await withRetry(
7703
7698
  () => runAgentLoop(messages, {
@@ -7707,10 +7702,12 @@ Analyze the above input.`;
7707
7702
  maxIterations: config.maxIterations,
7708
7703
  contextWindow: MODEL_CATALOG[activeModelId].contextWindow,
7709
7704
  onTextChunk: (chunk) => {
7705
+ clearInterval(spinnerTimer);
7710
7706
  if (spinner.isSpinning) spinner.stop();
7711
7707
  process.stdout.write(chunk);
7712
7708
  },
7713
7709
  onToolCall: (name, args) => {
7710
+ clearInterval(spinnerTimer);
7714
7711
  if (spinner.isSpinning) spinner.stop();
7715
7712
  if ((name === "write" || name === "edit") && typeof args.path === "string") {
7716
7713
  const filePath = nodePath.isAbsolute(args.path) ? args.path : nodePath.resolve(toolCtx.cwd, args.path);
@@ -7772,6 +7769,7 @@ Analyze the above input.`;
7772
7769
  }
7773
7770
  }
7774
7771
  } catch (err) {
7772
+ clearInterval(spinnerTimer);
7775
7773
  spinner.fail(`Error: ${err.message}`);
7776
7774
  checkpoints.discard();
7777
7775
  const msg = err.message?.toLowerCase() ?? "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freesyntax/notch-cli",
3
- "version": "0.5.16",
3
+ "version": "0.5.17",
4
4
  "description": "Notch CLI — AI-powered coding assistant by Driftrail",
5
5
  "type": "module",
6
6
  "bin": {