reasonix 0.3.1 → 0.4.1

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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CODE_SYSTEM_PROMPT,
4
+ codeSystemPrompt
5
+ } from "./chunk-2P2MZLCE.js";
6
+ export {
7
+ CODE_SYSTEM_PROMPT,
8
+ codeSystemPrompt
9
+ };
10
+ //# sourceMappingURL=prompt-MMANQ36Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.d.ts CHANGED
@@ -433,7 +433,15 @@ declare class ToolRegistry {
433
433
  dispatch(name: string, argumentsRaw: string | Record<string, unknown>): Promise<string>;
434
434
  }
435
435
 
436
- type EventRole = "assistant_delta" | "assistant_final" | "tool" | "done" | "error" | "branch_start" | "branch_progress" | "branch_done";
436
+ type EventRole = "assistant_delta" | "assistant_final"
437
+ /**
438
+ * Yielded immediately before a tool is dispatched. Lets the TUI put
439
+ * up a "▸ tool<X> running…" spinner while the tool's Promise is
440
+ * pending — otherwise the UI looks frozen whenever a tool call
441
+ * takes more than a few hundred ms (a big `filesystem_edit_file`
442
+ * is a typical trigger).
443
+ */
444
+ | "tool_start" | "tool" | "done" | "error" | "warning" | "branch_start" | "branch_progress" | "branch_done";
437
445
  interface BranchSummary {
438
446
  budget: number;
439
447
  chosenIndex: number;
@@ -465,6 +473,16 @@ interface LoopEvent {
465
473
  branch?: BranchSummary;
466
474
  branchProgress?: BranchProgress;
467
475
  error?: string;
476
+ /**
477
+ * True on `assistant_final` events emitted by the no-tools fallback
478
+ * when the loop hit its budget, was aborted, or tripped the
479
+ * token-context guard. Consumers that act on assistant text (notably
480
+ * the code-mode edit applier) MUST treat these as display-only —
481
+ * the model is "wrapping up," not proposing new work. Applying
482
+ * SEARCH/REPLACE blocks found in a forced summary caused the
483
+ * "analysis became edits" bug in v0.4.1 and earlier.
484
+ */
485
+ forcedSummary?: boolean;
468
486
  }
469
487
  interface CacheFirstLoopOptions {
470
488
  client: DeepSeekClient;
@@ -528,6 +546,12 @@ declare class CacheFirstLoop {
528
546
  readonly resumedMessageCount: number;
529
547
  private _turn;
530
548
  private _streamPreference;
549
+ /**
550
+ * Set by {@link abort} to short-circuit the tool-call loop after the
551
+ * current iteration. Reset at the start of each `step()` so an Esc
552
+ * during one turn doesn't poison the next.
553
+ */
554
+ private _aborted;
531
555
  constructor(opts: CacheFirstLoopOptions);
532
556
  /**
533
557
  * Shrink the log by re-truncating oversized tool results to a tighter
@@ -553,6 +577,14 @@ declare class CacheFirstLoop {
553
577
  */
554
578
  configure(opts: ReconfigurableOptions): void;
555
579
  private buildMessages;
580
+ /**
581
+ * Signal the currently-running {@link step} that the user wants to
582
+ * stop exploring. Takes effect at the next iteration boundary — if a
583
+ * tool call is mid-flight it will be allowed to finish, then the
584
+ * loop diverts to the forced-summary path so the user gets an
585
+ * answer instead of a cliff. Called by the TUI on Esc.
586
+ */
587
+ abort(): void;
556
588
  step(userInput: string): AsyncGenerator<LoopEvent>;
557
589
  private forceSummaryAfterIterLimit;
558
590
  run(userInput: string, onEvent?: (ev: LoopEvent) => void): Promise<string>;
@@ -1224,6 +1256,113 @@ interface SseMcpSpec {
1224
1256
  type McpSpec = StdioMcpSpec | SseMcpSpec;
1225
1257
  declare function parseMcpSpec(input: string): McpSpec;
1226
1258
 
1259
+ /**
1260
+ * Aider-style SEARCH/REPLACE edit blocks.
1261
+ *
1262
+ * The model emits blocks in this exact shape, one or more per response:
1263
+ *
1264
+ * path/to/file.ts
1265
+ * <<<<<<< SEARCH
1266
+ * exact existing lines (whitespace-sensitive)
1267
+ * =======
1268
+ * replacement lines
1269
+ * >>>>>>> REPLACE
1270
+ *
1271
+ * We chose this over unified diffs because:
1272
+ * - Models produce it reliably — no line-number drift.
1273
+ * - It tolerates multi-edit responses without ambiguity over which
1274
+ * hunk belongs to which file.
1275
+ * - Aider has years of evidence that this format works even against
1276
+ * weaker models than DeepSeek R1, so it's a conservative pick.
1277
+ *
1278
+ * The SEARCH text must match the file byte-for-byte. Empty SEARCH is a
1279
+ * sentinel for "create new file" — the REPLACE becomes the whole file.
1280
+ * If SEARCH doesn't match we refuse the edit and surface the failure;
1281
+ * we do NOT guess or fuzzy-match. A wrong silent edit is worse than a
1282
+ * missing one — the user can re-ask with the exact current content.
1283
+ */
1284
+ interface EditBlock {
1285
+ /** Path as written by the model — relative to rootDir, or absolute. */
1286
+ path: string;
1287
+ /** Literal text to match in the target file. Empty → create new file. */
1288
+ search: string;
1289
+ /** Replacement text to write in place of `search`. */
1290
+ replace: string;
1291
+ /** Char offset in the source message where this block started. */
1292
+ offset: number;
1293
+ }
1294
+ type ApplyStatus =
1295
+ /** Edit landed on disk. */
1296
+ "applied"
1297
+ /** New file created (SEARCH was empty and file didn't exist). */
1298
+ | "created"
1299
+ /** File exists but SEARCH block wasn't found in its content. */
1300
+ | "not-found"
1301
+ /** File doesn't exist and SEARCH was non-empty (can't create without content). */
1302
+ | "file-missing"
1303
+ /** Path escapes rootDir — refused on safety grounds. */
1304
+ | "path-escape"
1305
+ /** fs write / read threw. */
1306
+ | "error";
1307
+ interface ApplyResult {
1308
+ path: string;
1309
+ status: ApplyStatus;
1310
+ /** Extra detail (e.g. error message) for logs. */
1311
+ message?: string;
1312
+ }
1313
+ declare function parseEditBlocks(text: string): EditBlock[];
1314
+ declare function applyEditBlock(block: EditBlock, rootDir: string): ApplyResult;
1315
+ declare function applyEditBlocks(blocks: EditBlock[], rootDir: string): ApplyResult[];
1316
+ interface EditSnapshot {
1317
+ /** Path relative to rootDir, as the block named it. */
1318
+ path: string;
1319
+ /**
1320
+ * File content before the edit batch was applied. `null` means the
1321
+ * file didn't exist yet — restoring that means deleting whatever the
1322
+ * edit created.
1323
+ */
1324
+ prevContent: string | null;
1325
+ }
1326
+ /**
1327
+ * Capture the current state of every file an edit batch is about to
1328
+ * touch, so `/undo` can roll back if the user doesn't like the result.
1329
+ * De-duplicates by path because one batch can contain multiple blocks
1330
+ * for the same file, and we only want one "before" snapshot per file.
1331
+ */
1332
+ declare function snapshotBeforeEdits(blocks: EditBlock[], rootDir: string): EditSnapshot[];
1333
+ /**
1334
+ * Restore files to their snapshotted state. Snapshots with
1335
+ * `prevContent === null` were created by the edit, so undo = delete.
1336
+ * Otherwise the prior content is written back, replacing whatever the
1337
+ * edit left behind.
1338
+ */
1339
+ declare function restoreSnapshots(snapshots: EditSnapshot[], rootDir: string): ApplyResult[];
1340
+
1341
+ /**
1342
+ * System prompt used by `reasonix code`. Teaches the model:
1343
+ *
1344
+ * 1. It has a filesystem MCP bridge rooted at the user's CWD.
1345
+ * 2. To modify files it emits SEARCH/REPLACE blocks (not
1346
+ * `write_file` — that would whole-file rewrite and kill diff
1347
+ * reviewability).
1348
+ * 3. Read first, edit second — SEARCH must match byte-for-byte.
1349
+ * 4. Be concise. The user can read a diff faster than prose.
1350
+ *
1351
+ * Kept short on purpose. Long system prompts eat context budget that
1352
+ * the Cache-First Loop is trying to conserve. The SEARCH/REPLACE spec
1353
+ * is the one unavoidable bloat; we trim everything else.
1354
+ */
1355
+ declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to `/apply` or `/discard`. Don't assume they'll accept \u2014 write as if each edit will be audited, because it will.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files \u2014 the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n\n# Exploration\n\n- Avoid listing or reading inside these common dependency / build directories unless the user explicitly asks about them: node_modules, dist, build, out, .next, .nuxt, .svelte-kit, .git, .venv, venv, __pycache__, target, coverage, .turbo, .cache. They're expensive and usually irrelevant.\n- Prefer search_files / grep over list_directory when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / grep / read), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n";
1356
+ /**
1357
+ * Inject the project's `.gitignore` content into the system prompt as a
1358
+ * "respect this on top of the built-in denylist" hint. We don't parse
1359
+ * the file — we hand it to the model as-is. Truncate long ones so we
1360
+ * don't eat context budget on huge generated ignore lists.
1361
+ *
1362
+ * Missing or unreadable .gitignore → returns the base prompt unchanged.
1363
+ */
1364
+ declare function codeSystemPrompt(rootDir: string): string;
1365
+
1227
1366
  /**
1228
1367
  * User-level config storage for the Reasonix CLI.
1229
1368
  *
@@ -1277,6 +1416,6 @@ declare function redactKey(key: string): string;
1277
1416
 
1278
1417
  /** Reasonix — DeepSeek-native agent framework. Library entry point. */
1279
1418
 
1280
- declare const VERSION = "0.3.1";
1419
+ declare const VERSION = "0.4.1";
1281
1420
 
1282
- export { AppendOnlyLog, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, DEFAULT_MAX_RESULT_CHARS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EventRole, type FlattenDecision, type FlattenOptions, type HarvestOptions, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, type ListToolsResult, type LoopEvent, MCP_PROTOCOL_VERSION, McpClient, type McpClientOptions, type McpContentBlock, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type RetryInfo, type RetryOptions, type Role, type ScavengeOptions, type ScavengeResult, type SessionInfo, SessionStats, type SessionSummary, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, StormBreaker, type StreamChunk, type ToolCall, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, Usage, VERSION, VolatileScratch, aggregateBranchUsage, analyzeSchema, appendSessionMessage, bridgeMcpTools, claudeEquivalentCost, computeReplayStats, costUsd, defaultConfigPath, defaultSelector, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, formatLoopError, harvest, healLoadedMessages, isJsonRpcError, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadSessionMessages, nestArguments, openTranscriptFile, parseMcpSpec, parseTranscript, readConfig, readTranscript, recordFromLoopEvent, redactKey, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, runBranches, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, truncateForModel, writeConfig, writeMeta, writeRecord };
1421
+ export { AppendOnlyLog, type ApplyResult, type ApplyStatus, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, DEFAULT_MAX_RESULT_CHARS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FlattenDecision, type FlattenOptions, type HarvestOptions, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, type ListToolsResult, type LoopEvent, MCP_PROTOCOL_VERSION, McpClient, type McpClientOptions, type McpContentBlock, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type RetryInfo, type RetryOptions, type Role, type ScavengeOptions, type ScavengeResult, type SessionInfo, SessionStats, type SessionSummary, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, StormBreaker, type StreamChunk, type ToolCall, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, Usage, VERSION, VolatileScratch, aggregateBranchUsage, analyzeSchema, appendSessionMessage, applyEditBlock, applyEditBlocks, bridgeMcpTools, claudeEquivalentCost, codeSystemPrompt, computeReplayStats, costUsd, defaultConfigPath, defaultSelector, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, formatLoopError, harvest, healLoadedMessages, isJsonRpcError, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadSessionMessages, nestArguments, openTranscriptFile, parseEditBlocks, parseMcpSpec, parseTranscript, readConfig, readTranscript, recordFromLoopEvent, redactKey, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, restoreSnapshots, runBranches, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, truncateForModel, writeConfig, writeMeta, writeRecord };