reasonix 0.5.23 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/{chunk-ANMDY236.js → chunk-NXYPGKA3.js} +89 -23
- package/dist/cli/chunk-NXYPGKA3.js.map +1 -0
- package/dist/cli/index.js +3731 -1826
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-75XLIUTO.js → prompt-KX6A4DVX.js} +2 -2
- package/dist/index.d.ts +304 -1
- package/dist/index.js +1021 -110
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/cli/chunk-ANMDY236.js.map +0 -1
- /package/dist/cli/{prompt-75XLIUTO.js.map → prompt-KX6A4DVX.js.map} +0 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CODE_SYSTEM_PROMPT,
|
|
4
4
|
codeSystemPrompt
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NXYPGKA3.js";
|
|
6
6
|
export {
|
|
7
7
|
CODE_SYSTEM_PROMPT,
|
|
8
8
|
codeSystemPrompt
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=prompt-
|
|
10
|
+
//# sourceMappingURL=prompt-KX6A4DVX.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -674,6 +674,13 @@ interface SessionSummary {
|
|
|
674
674
|
* prompt + user delta + any new tool outputs).
|
|
675
675
|
*/
|
|
676
676
|
lastPromptTokens: number;
|
|
677
|
+
/**
|
|
678
|
+
* Most recent turn's USD cost. Complements `totalCostUsd` so the TUI
|
|
679
|
+
* can render "this turn: $X · session: $Y" — users asked for a
|
|
680
|
+
* per-turn signal so a mid-session jump from flash to pro is
|
|
681
|
+
* immediately visible, not hidden inside the session aggregate.
|
|
682
|
+
*/
|
|
683
|
+
lastTurnCostUsd: number;
|
|
677
684
|
}
|
|
678
685
|
declare class SessionStats {
|
|
679
686
|
readonly turns: TurnStats[];
|
|
@@ -731,6 +738,18 @@ interface ToolRegistryOptions {
|
|
|
731
738
|
*/
|
|
732
739
|
autoFlatten?: boolean;
|
|
733
740
|
}
|
|
741
|
+
/**
|
|
742
|
+
* Callback form for `setToolInterceptor` — receives the tool name and
|
|
743
|
+
* already-parsed arguments; returns a string to short-circuit dispatch
|
|
744
|
+
* (the returned value becomes the tool result the model sees), or
|
|
745
|
+
* `null` / `undefined` to fall through to the registered tool fn.
|
|
746
|
+
*
|
|
747
|
+
* Used by `reasonix code`'s edit-mode gate: `edit_file` / `write_file`
|
|
748
|
+
* are intercepted in "review" mode (queued into pendingEdits, returning
|
|
749
|
+
* "queued for /apply") or handled inline in "auto" mode (snapshot +
|
|
750
|
+
* apply, then surface an undo banner). Other tools pass through.
|
|
751
|
+
*/
|
|
752
|
+
type ToolInterceptor = (name: string, args: Record<string, unknown>) => string | null | undefined | Promise<string | null | undefined>;
|
|
734
753
|
declare class ToolRegistry {
|
|
735
754
|
private readonly _tools;
|
|
736
755
|
private readonly _autoFlatten;
|
|
@@ -742,11 +761,23 @@ declare class ToolRegistry {
|
|
|
742
761
|
* bounced until the user approves a submitted plan.
|
|
743
762
|
*/
|
|
744
763
|
private _planMode;
|
|
764
|
+
/**
|
|
765
|
+
* Optional hook run after arg parsing but before tool.fn. Lets the TUI
|
|
766
|
+
* reroute specific tool calls (e.g. edit_file in review mode) without
|
|
767
|
+
* modifying the tool definitions themselves.
|
|
768
|
+
*/
|
|
769
|
+
private _interceptor;
|
|
745
770
|
constructor(opts?: ToolRegistryOptions);
|
|
746
771
|
/** Enable / disable plan-mode enforcement at dispatch. */
|
|
747
772
|
setPlanMode(on: boolean): void;
|
|
748
773
|
/** True when the registry is currently refusing non-readonly calls. */
|
|
749
774
|
get planMode(): boolean;
|
|
775
|
+
/**
|
|
776
|
+
* Install or clear the dispatch interceptor. At most one interceptor
|
|
777
|
+
* is active at a time — calling twice replaces the previous. Pass
|
|
778
|
+
* `null` to remove.
|
|
779
|
+
*/
|
|
780
|
+
setToolInterceptor(fn: ToolInterceptor | null): void;
|
|
750
781
|
register<A, R>(def: ToolDefinition<A, R>): this;
|
|
751
782
|
has(name: string): boolean;
|
|
752
783
|
get(name: string): ToolDefinition | undefined;
|
|
@@ -951,6 +982,31 @@ declare class CacheFirstLoop {
|
|
|
951
982
|
* `step()` (the prior turn's signal has already fired).
|
|
952
983
|
*/
|
|
953
984
|
private _turnAbort;
|
|
985
|
+
/**
|
|
986
|
+
* "Next turn should run on pro, regardless of this.model." Set by the
|
|
987
|
+
* `/pro` slash command; consumed at the next turn's start (flipping
|
|
988
|
+
* `_escalateThisTurn` on and self-clearing) so it's a fire-and-forget
|
|
989
|
+
* single-turn upgrade. Survives across multiple slash inputs so
|
|
990
|
+
* typing `/pro` and then hesitating a while before submitting a real
|
|
991
|
+
* message still applies.
|
|
992
|
+
*/
|
|
993
|
+
private _proArmedForNextTurn;
|
|
994
|
+
/**
|
|
995
|
+
* Active for the current turn only — true means every model call
|
|
996
|
+
* this turn uses pro instead of `this.model`. Turned on by EITHER
|
|
997
|
+
* the pro-armed consumption OR the mid-turn auto-escalation
|
|
998
|
+
* threshold (see `_turnFailureCount`). Cleared at turn end.
|
|
999
|
+
*/
|
|
1000
|
+
private _escalateThisTurn;
|
|
1001
|
+
/**
|
|
1002
|
+
* Visible-failure count for the current turn. Incremented by tool
|
|
1003
|
+
* dispatch paths when a result matches a known "flash is struggling"
|
|
1004
|
+
* shape (SEARCH-not-found errors, scavenge / truncation / storm
|
|
1005
|
+
* repair fires). Once it hits {@link FAILURE_ESCALATION_THRESHOLD},
|
|
1006
|
+
* the remainder of the turn's model calls auto-upgrade to pro so
|
|
1007
|
+
* the user doesn't watch flash retry the same edit 5 times.
|
|
1008
|
+
*/
|
|
1009
|
+
private _turnFailureCount;
|
|
954
1010
|
constructor(opts: CacheFirstLoopOptions);
|
|
955
1011
|
/**
|
|
956
1012
|
* Shrink the log by re-truncating oversized tool results to a tighter
|
|
@@ -968,6 +1024,60 @@ declare class CacheFirstLoop {
|
|
|
968
1024
|
* authored intent we can't mechanically shrink without losing
|
|
969
1025
|
* meaning.
|
|
970
1026
|
*/
|
|
1027
|
+
/**
|
|
1028
|
+
* Conservative args-only shrink fired after every tool response —
|
|
1029
|
+
* strictly about ONE thing: stop oversized `edit_file` / `write_file`
|
|
1030
|
+
* arguments from riding every future turn's prompt.
|
|
1031
|
+
*
|
|
1032
|
+
* Why this is worth doing AUTOMATICALLY (not just on /compact):
|
|
1033
|
+
* Each tool-call arguments string sticks in the log verbatim. On a
|
|
1034
|
+
* coding session with ~10 edits, that's 20-40K tokens of stale
|
|
1035
|
+
* SEARCH/REPLACE text riding along on every turn. Even at a 98.9%
|
|
1036
|
+
* cache hit rate the input cost still adds up linearly (cache-hit
|
|
1037
|
+
* price × tokens × turns). Compacting IMMEDIATELY after the tool
|
|
1038
|
+
* responds means the next turn's prompt is already smaller — the
|
|
1039
|
+
* shrink is a one-time write that saves every future prompt.
|
|
1040
|
+
*
|
|
1041
|
+
* Threshold rationale: 800 tokens ≈ 3 KB. A typical 20-line edit's
|
|
1042
|
+
* args land well under that; massive rewrites (whole-file content,
|
|
1043
|
+
* 100+ line refactors) land above and get the compaction. Small
|
|
1044
|
+
* edits stay byte-verbatim so nothing common-case changes.
|
|
1045
|
+
*
|
|
1046
|
+
* Safety: we ONLY shrink args whose tool has ALREADY responded.
|
|
1047
|
+
* Structurally that's every call in `log.toMessages()` at this
|
|
1048
|
+
* point — the current turn's assistant/tool pairing is by
|
|
1049
|
+
* construction closed by the time we get here (append happens
|
|
1050
|
+
* AFTER dispatch). The in-flight assistant message being built
|
|
1051
|
+
* lives in scratch, not the log, so this pass can't touch it.
|
|
1052
|
+
*
|
|
1053
|
+
* Model impact: the model may occasionally want to reference the
|
|
1054
|
+
* exact SEARCH text of a prior edit — it then reads the file
|
|
1055
|
+
* directly (which shows current state) or looks at the preceding
|
|
1056
|
+
* assistant text (which has its plan). Losing the stale args is a
|
|
1057
|
+
* net win: one extra read_file vs. dragging N KB of stale text
|
|
1058
|
+
* through every subsequent turn.
|
|
1059
|
+
*/
|
|
1060
|
+
private compactToolCallArgsAfterResponse;
|
|
1061
|
+
/**
|
|
1062
|
+
* Fired at the END of a turn (just before `done` is yielded). Shrinks
|
|
1063
|
+
* every tool RESULT in the log that exceeds {@link TURN_END_RESULT_CAP_TOKENS}
|
|
1064
|
+
* to a tight cap so the NEXT turn's prompt doesn't re-pay for big
|
|
1065
|
+
* reads or searches done earlier. Unlike the reactive 40/80%
|
|
1066
|
+
* thresholds which react to context pressure, this runs unconditionally
|
|
1067
|
+
* — the win is preventive: each turn's big outputs get trimmed before
|
|
1068
|
+
* they ride into the next prompt. Saves compounding cost on long
|
|
1069
|
+
* sessions.
|
|
1070
|
+
*
|
|
1071
|
+
* Why compact the JUST-finished turn's results too (not just older
|
|
1072
|
+
* turns)? The same-turn iters already consumed the raw content to
|
|
1073
|
+
* make their decisions — the log is only carried forward for future
|
|
1074
|
+
* prompts. And "let me re-read the file" is vastly cheaper than
|
|
1075
|
+
* "carry this 12KB result in every future turn's prompt forever."
|
|
1076
|
+
*
|
|
1077
|
+
* Safe by construction: args-compact for THIS turn already ran
|
|
1078
|
+
* inside `compactToolCallArgsAfterResponse`; this pass is orthogonal.
|
|
1079
|
+
*/
|
|
1080
|
+
private autoCompactToolResultsOnTurnEnd;
|
|
971
1081
|
compact(maxTokens?: number): {
|
|
972
1082
|
healedCount: number;
|
|
973
1083
|
tokensSaved: number;
|
|
@@ -995,6 +1105,48 @@ declare class CacheFirstLoop {
|
|
|
995
1105
|
* flip a knob between turns.
|
|
996
1106
|
*/
|
|
997
1107
|
configure(opts: ReconfigurableOptions): void;
|
|
1108
|
+
/**
|
|
1109
|
+
* Arm pro for the next turn (consumed at turn start). Called by
|
|
1110
|
+
* `/pro`. Idempotent — repeated calls stay armed, `disarmPro()`
|
|
1111
|
+
* clears. Separate from `/preset max` which persistently switches
|
|
1112
|
+
* this.model; armed state is strictly single-turn.
|
|
1113
|
+
*/
|
|
1114
|
+
armProForNextTurn(): void;
|
|
1115
|
+
/** Cancel `/pro` arming before the next turn starts. */
|
|
1116
|
+
disarmPro(): void;
|
|
1117
|
+
/** UI surface — true while `/pro` is queued but hasn't fired yet. */
|
|
1118
|
+
get proArmed(): boolean;
|
|
1119
|
+
/** UI surface — true while the current turn is running on pro (armed or auto-escalated). */
|
|
1120
|
+
get escalatedThisTurn(): boolean;
|
|
1121
|
+
/**
|
|
1122
|
+
* Model the current model call should use. Defaults to `this.model`;
|
|
1123
|
+
* upgrades to {@link ESCALATION_MODEL} when the turn is armed for
|
|
1124
|
+
* pro (via `/pro`) or has hit the failure-escalation threshold.
|
|
1125
|
+
* Same thinking + effort policy applies regardless — pro defaults
|
|
1126
|
+
* to thinking=enabled and effort=max, which the current turn wanted
|
|
1127
|
+
* anyway when flash was struggling.
|
|
1128
|
+
*/
|
|
1129
|
+
private modelForCurrentCall;
|
|
1130
|
+
/**
|
|
1131
|
+
* True when the assistant's content is a self-reported escalation
|
|
1132
|
+
* request. Only the FIRST line matters — the model is instructed
|
|
1133
|
+
* to emit the marker as the first output token if at all. Matching
|
|
1134
|
+
* anywhere else in the text is a normal content reference (e.g.
|
|
1135
|
+
* the user asked about the marker itself, or prose that happens
|
|
1136
|
+
* to contain angle-brackets).
|
|
1137
|
+
*/
|
|
1138
|
+
private isEscalationRequest;
|
|
1139
|
+
/**
|
|
1140
|
+
* Check whether a tool result string looks like a "flash struggled"
|
|
1141
|
+
* signal and, if so, increment the turn's failure counter. Escalates
|
|
1142
|
+
* the REST of the current turn to pro once the threshold is hit.
|
|
1143
|
+
* Idempotent after escalation — further failures don't re-escalate,
|
|
1144
|
+
* but the turn is already on pro so it doesn't matter.
|
|
1145
|
+
*
|
|
1146
|
+
* Return: `true` when this call tipped the turn into escalation
|
|
1147
|
+
* mode (so the loop can surface a one-time warning to the user).
|
|
1148
|
+
*/
|
|
1149
|
+
private noteToolFailureSignal;
|
|
998
1150
|
private buildMessages;
|
|
999
1151
|
/**
|
|
1000
1152
|
* Signal the currently-running {@link step} to stop **now**. Cancels
|
|
@@ -1685,6 +1837,119 @@ declare function registerSubagentTool(parentRegistry: ToolRegistry, opts: Subage
|
|
|
1685
1837
|
*/
|
|
1686
1838
|
declare function forkRegistryExcluding(parent: ToolRegistry, exclude: ReadonlySet<string>): ToolRegistry;
|
|
1687
1839
|
|
|
1840
|
+
/**
|
|
1841
|
+
* Long-running process registry — the "background run" counterpart to
|
|
1842
|
+
* `run_command`. `run_command` spawns a child, waits for it to exit,
|
|
1843
|
+
* then returns combined output; perfect for tests / builds / one-shots
|
|
1844
|
+
* but useless for `npm run dev` / `python -m http.server` / watchers,
|
|
1845
|
+
* which never exit and just time the tool out.
|
|
1846
|
+
*
|
|
1847
|
+
* JobRegistry lets the model fire-and-almost-forget: we spawn the
|
|
1848
|
+
* child, wait at most `waitSec` (default 3s) OR until output matches
|
|
1849
|
+
* a readiness regex, then return the startup preview plus a job id.
|
|
1850
|
+
* The child keeps running in the background; later tool calls tail
|
|
1851
|
+
* its output, stop it, or list what's still alive.
|
|
1852
|
+
*
|
|
1853
|
+
* Shape-wise this is modeled on Claude Code's `BashOutput` / `KillBash`
|
|
1854
|
+
* pair. We diverge on one point: ready-signal detection is on by default
|
|
1855
|
+
* because dev servers almost universally print "Local:", "listening on",
|
|
1856
|
+
* "ready in N ms", "compiled successfully" when they come up — short-
|
|
1857
|
+
* circuiting the wait on those keeps the model's first tool-result
|
|
1858
|
+
* useful ("server is up at http://localhost:5173") instead of spending
|
|
1859
|
+
* the full 3s on a stabilization timer.
|
|
1860
|
+
*/
|
|
1861
|
+
interface JobStartOptions {
|
|
1862
|
+
/** Absolute path to cwd for the spawned child. */
|
|
1863
|
+
cwd: string;
|
|
1864
|
+
/**
|
|
1865
|
+
* Max seconds to wait for the initial burst before returning. Capped
|
|
1866
|
+
* at 30. A ready-signal match short-circuits this. Default 3.
|
|
1867
|
+
*/
|
|
1868
|
+
waitSec?: number;
|
|
1869
|
+
/** Signal plumbed through from the calling tool's AbortSignal. */
|
|
1870
|
+
signal?: AbortSignal;
|
|
1871
|
+
/** Total per-job output buffer cap (bytes). Default 64 KB. */
|
|
1872
|
+
maxBufferBytes?: number;
|
|
1873
|
+
}
|
|
1874
|
+
interface JobStartResult {
|
|
1875
|
+
jobId: number;
|
|
1876
|
+
pid: number | null;
|
|
1877
|
+
/** True iff the child was still running at the point we returned. */
|
|
1878
|
+
stillRunning: boolean;
|
|
1879
|
+
/** True iff a READY_SIGNALS pattern matched during the wait window. */
|
|
1880
|
+
readyMatched: boolean;
|
|
1881
|
+
/** Preview of combined stdout+stderr accumulated during the wait. */
|
|
1882
|
+
preview: string;
|
|
1883
|
+
/** If the child exited during the wait, its exit code; else null. */
|
|
1884
|
+
exitCode: number | null;
|
|
1885
|
+
}
|
|
1886
|
+
interface JobRecord {
|
|
1887
|
+
id: number;
|
|
1888
|
+
command: string;
|
|
1889
|
+
pid: number | null;
|
|
1890
|
+
startedAt: number;
|
|
1891
|
+
/** Exit code once the process terminates; null while running. */
|
|
1892
|
+
exitCode: number | null;
|
|
1893
|
+
/** Combined stdout+stderr, ring-trimmed. */
|
|
1894
|
+
output: string;
|
|
1895
|
+
/**
|
|
1896
|
+
* Total bytes ever written by the child (not just what's in `output`).
|
|
1897
|
+
* Useful for "how much got dropped" diagnostics.
|
|
1898
|
+
*/
|
|
1899
|
+
totalBytesWritten: number;
|
|
1900
|
+
/** True iff the child is still alive. */
|
|
1901
|
+
running: boolean;
|
|
1902
|
+
/** Error from spawn() itself (ENOENT, etc.) once surfaced. */
|
|
1903
|
+
spawnError?: string;
|
|
1904
|
+
}
|
|
1905
|
+
declare class JobRegistry {
|
|
1906
|
+
private readonly jobs;
|
|
1907
|
+
private nextId;
|
|
1908
|
+
/**
|
|
1909
|
+
* Spawn a background child. Resolves after `waitSec` OR on ready
|
|
1910
|
+
* signal OR on early exit, whichever comes first. The child continues
|
|
1911
|
+
* to run (and buffer output) regardless of which path fires.
|
|
1912
|
+
*/
|
|
1913
|
+
start(command: string, opts: JobStartOptions): Promise<JobStartResult>;
|
|
1914
|
+
/**
|
|
1915
|
+
* Read a job's accumulated output. `since` lets a caller poll
|
|
1916
|
+
* incrementally: pass the byte count returned from the last call to
|
|
1917
|
+
* get only newly-written content. Returns both full output and a
|
|
1918
|
+
* running snapshot so the caller can use whichever.
|
|
1919
|
+
*/
|
|
1920
|
+
read(id: number, opts?: {
|
|
1921
|
+
since?: number;
|
|
1922
|
+
tailLines?: number;
|
|
1923
|
+
}): JobReadResult | null;
|
|
1924
|
+
/**
|
|
1925
|
+
* Send SIGTERM, wait `graceMs`, then SIGKILL if still alive. Returns
|
|
1926
|
+
* the final job record (or null when the job id is unknown). Safe to
|
|
1927
|
+
* call on an already-exited job — returns the record unchanged.
|
|
1928
|
+
*/
|
|
1929
|
+
stop(id: number, opts?: {
|
|
1930
|
+
graceMs?: number;
|
|
1931
|
+
}): Promise<JobRecord | null>;
|
|
1932
|
+
list(): JobRecord[];
|
|
1933
|
+
/**
|
|
1934
|
+
* Best-effort kill of every still-running job. Called on TUI shutdown
|
|
1935
|
+
* so dev servers don't outlive the Reasonix process. Resolves after
|
|
1936
|
+
* every child has closed or a hard deadline passes (3s total).
|
|
1937
|
+
*/
|
|
1938
|
+
shutdown(deadlineMs?: number): Promise<void>;
|
|
1939
|
+
/** Count of still-running jobs — drives the TUI status-bar indicator. */
|
|
1940
|
+
runningCount(): number;
|
|
1941
|
+
}
|
|
1942
|
+
interface JobReadResult {
|
|
1943
|
+
output: string;
|
|
1944
|
+
/** Total bytes ever in the buffer (pre-slice). Caller passes back as `since`. */
|
|
1945
|
+
byteLength: number;
|
|
1946
|
+
running: boolean;
|
|
1947
|
+
exitCode: number | null;
|
|
1948
|
+
command: string;
|
|
1949
|
+
pid: number | null;
|
|
1950
|
+
spawnError?: string;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1688
1953
|
/**
|
|
1689
1954
|
* Native shell tool — lets the model run commands inside the sandbox
|
|
1690
1955
|
* root so it can actually verify its own work (run tests, check git
|
|
@@ -1740,6 +2005,13 @@ interface ShellToolsOptions {
|
|
|
1740
2005
|
* (CI, benchmarks) where a human can't be in the loop to confirm.
|
|
1741
2006
|
*/
|
|
1742
2007
|
allowAll?: boolean;
|
|
2008
|
+
/**
|
|
2009
|
+
* Background-process registry shared between `run_background`,
|
|
2010
|
+
* `job_output`, `stop_job`, `list_jobs`, and the /jobs /kill slashes.
|
|
2011
|
+
* When omitted, the registrar builds its own — but the caller
|
|
2012
|
+
* usually wants to provide one so the TUI can tail it too.
|
|
2013
|
+
*/
|
|
2014
|
+
jobs?: JobRegistry;
|
|
1743
2015
|
}
|
|
1744
2016
|
/**
|
|
1745
2017
|
* Tokenize a shell-ish command string into argv. Handles single/double
|
|
@@ -2939,7 +3211,7 @@ declare function restoreSnapshots(snapshots: EditSnapshot[], rootDir: string): A
|
|
|
2939
3211
|
* the Cache-First Loop is trying to conserve. The SEARCH/REPLACE spec
|
|
2940
3212
|
* is the one unavoidable bloat; we trim everything else.
|
|
2941
3213
|
*/
|
|
2942
|
-
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# Cite or shut up \u2014 non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Reasonix VALIDATES the citations you write \u2014 broken paths or out-of-range lines render in **red strikethrough with \u274C** in front of the user.\n\n**Positive claims** (a file exists, a function does X, a feature IS implemented) \u2014 append a markdown link to the source:\n\n- \u2705 Correct: `The MCP client supports listResources [listResources](src/mcp/client.ts:142).`\n- \u274C Wrong: `The MCP client supports listResources.` \u2190 no citation, looks authoritative but unverifiable.\n\n**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible \u2014 but that's exactly why you must NOT write them on instinct.\n\nIf you are about to write \"X is missing\" or \"Y is not implemented\" \u2014 **STOP**. Call `search_content` for the relevant symbol or term FIRST. Only then:\n\n- If the search returns matches \u2192 you were wrong; correct yourself and cite the matches.\n- If the search returns nothing \u2192 state the absence with the search query as your evidence: `No callers of \\`foo()\\` found (search_content \"foo\").`\n\nAsserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write \"missing\" as a red flag in your own reasoning.\n\n# When to propose a plan (submit_plan)\n\nYou have a `submit_plan` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive \u2014 migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist \u2014 propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section \u2014 the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP \u2014 don't call any more tools, wait for the user's verdict.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills (\uD83E\uDDEC)\n\nThe pinned Skills index below lists playbooks you can invoke with `run_skill`. Skills marked with **\uD83E\uDDEC** spawn an **isolated subagent** \u2014 a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so \uD83E\uDDEC skills are how you keep the main session lean.\n\nTwo built-ins ship by default:\n- **\uD83E\uDDEC explore** \u2014 read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass `arguments` describing the concrete question.\n- **\uD83E\uDDEC research** \u2014 combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\nWhen to delegate (call `run_skill` with a \uD83E\uDDEC skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\n\nAlways pass a clear, self-contained `arguments` \u2014 that text is the **only** context the subagent gets.\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# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from `remember`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say \u2014 don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer `search_files` over `list_directory` when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees. Note: `search_files` matches file NAMES; for searching file CONTENTS use `search_content`.\n- Available exploration tools: `read_file`, `list_directory`, `directory_tree`, `search_files` (filename match), `search_content` (content grep \u2014 use for \"where is X called\", \"find all references to Y\"), `get_file_info`. Don't call `grep` or other tools that aren't in this list \u2014 they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (`read_file`, `list_directory`, `search_files`, `edit_file`, etc.): paths are sandbox-relative. `/` means the project root, `/src/foo.ts` means `<project>/src/foo.ts`. Both relative (`src/foo.ts`) and POSIX-absolute (`/src/foo.ts`) forms work.\n- **`run_command`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading `/` in run_command arguments** \u2014 Windows treats `/tests` as drive-root `F:\\tests` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (`tests`, `./tests`, `src/loop.ts`) instead.\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 / read / search), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n";
|
|
3214
|
+
declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, edit_file, list_directory, directory_tree, search_files, search_content, get_file_info) rooted at the user's working directory, plus run_command / run_background for shell.\n\n# Cite or shut up \u2014 non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Reasonix VALIDATES the citations you write \u2014 broken paths or out-of-range lines render in **red strikethrough with \u274C** in front of the user.\n\n**Positive claims** (a file exists, a function does X, a feature IS implemented) \u2014 append a markdown link to the source:\n\n- \u2705 Correct: `The MCP client supports listResources [listResources](src/mcp/client.ts:142).`\n- \u274C Wrong: `The MCP client supports listResources.` \u2190 no citation, looks authoritative but unverifiable.\n\n**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible \u2014 but that's exactly why you must NOT write them on instinct.\n\nIf you are about to write \"X is missing\" or \"Y is not implemented\" \u2014 **STOP**. Call `search_content` for the relevant symbol or term FIRST. Only then:\n\n- If the search returns matches \u2192 you were wrong; correct yourself and cite the matches.\n- If the search returns nothing \u2192 state the absence with the search query as your evidence: `No callers of \\`foo()\\` found (search_content \"foo\").`\n\nAsserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write \"missing\" as a red flag in your own reasoning.\n\n# When to propose a plan (submit_plan)\n\nYou have a `submit_plan` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive \u2014 migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist \u2014 propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section \u2014 the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP \u2014 don't call any more tools, wait for the user's verdict.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills\n\nThe pinned Skills index below lists playbooks you can invoke with `run_skill`. Entries tagged `[\uD83E\uDDEC subagent]` spawn an **isolated subagent** \u2014 a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so subagent skills are how you keep the main session lean.\n\n**When you call `run_skill`, the `name` is ONLY the identifier before the tag** \u2014 e.g. `run_skill({ name: \"explore\", arguments: \"...\" })`, NOT `\"[\uD83E\uDDEC subagent] explore\"` and NOT `\"explore [\uD83E\uDDEC subagent]\"`. The tag is display sugar; the name argument is just the bare identifier.\n\nTwo built-ins ship by default:\n- **explore** `[\uD83E\uDDEC subagent]` \u2014 read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass `arguments` describing the concrete question.\n- **research** `[\uD83E\uDDEC subagent]` \u2014 combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\nWhen to delegate (call `run_skill` with a subagent skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\n\nAlways pass a clear, self-contained `arguments` \u2014 that text is the **only** context the subagent gets.\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\nReasonix runs an **edit gate**. The user's current mode (`review` or `auto`) decides what happens to your writes; you DO NOT see which mode is active, and you SHOULD NOT ask. Write the same way in both cases.\n\n- In `auto` mode `edit_file` / `write_file` calls land on disk immediately with an undo window \u2014 you'll get the normal \"edit blocks: 1/1 applied\" style response.\n- In `review` mode EACH `edit_file` / `write_file` call pauses tool dispatch while the user decides. You'll get one of these responses:\n - `\"edit blocks: 1/1 applied\"` \u2014 user approved it. Continue as normal.\n - `\"User rejected this edit to <path>. Don't retry the same SEARCH/REPLACE\u2026\"` \u2014 user said no to THIS specific edit. Do NOT re-emit the same block, do NOT switch tools to sneak it past the gate (write_file \u2192 edit_file, or text-form SEARCH/REPLACE). Either take a clearly different approach or stop and ask the user what they want instead.\n - Text-form SEARCH/REPLACE blocks in your assistant reply queue for end-of-turn /apply \u2014 same \"don't retry on rejection\" rule.\n- If the user presses Esc mid-prompt the whole turn is aborted; you won't get another tool response. Don't keep spamming tool calls after an abort.\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# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from `remember`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say \u2014 don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer `search_files` over `list_directory` when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees. Note: `search_files` matches file NAMES; for searching file CONTENTS use `search_content`.\n- Available exploration tools: `read_file`, `list_directory`, `directory_tree`, `search_files` (filename match), `search_content` (content grep \u2014 use for \"where is X called\", \"find all references to Y\"), `get_file_info`. Don't call `grep` or other tools that aren't in this list \u2014 they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (`read_file`, `list_directory`, `search_files`, `edit_file`, etc.): paths are sandbox-relative. `/` means the project root, `/src/foo.ts` means `<project>/src/foo.ts`. Both relative (`src/foo.ts`) and POSIX-absolute (`/src/foo.ts`) forms work.\n- **`run_command`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading `/` in run_command arguments** \u2014 Windows treats `/tests` as drive-root `F:\\tests` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (`tests`, `./tests`, `src/loop.ts`) instead.\n\n# Foreground vs. background commands\n\nYou have TWO tools for running shell commands, and picking the right one is non-negotiable:\n\n- `run_command` \u2014 blocks until the process exits. Use for: **tests, builds, lints, typechecks, git operations, one-shot scripts**. Anything that naturally returns in under a minute.\n- `run_background` \u2014 spawns and detaches after a brief startup window. Use for: **dev servers, watchers, any command with \"dev\" / \"serve\" / \"watch\" / \"start\" in the name**. Examples: `npm run dev`, `pnpm dev`, `yarn start`, `vite`, `next dev`, `uvicorn app:app --reload`, `flask run`, `python -m http.server`, `cargo watch`, `tsc --watch`, `webpack serve`.\n\n**Never use run_command for a dev server.** It will block for 60s, time out, and the user will see a frozen tool call while the server was actually running fine. Always `run_background`, then `job_output` to peek at the logs when you need to verify something.\n\nAfter `run_background`, tools available to you:\n- `job_output(jobId, tailLines?)` \u2014 read recent logs to verify startup / debug errors.\n- `list_jobs` \u2014 see every job this session (running + exited).\n- `stop_job(jobId)` \u2014 SIGTERM \u2192 SIGKILL after grace. Stop before switching port / config.\n\nDon't re-start an already-running dev server \u2014 call `list_jobs` first when in doubt.\n\n# Scope discipline on \"run it\" / \"start it\" requests\n\nWhen the user's request is to **run / start / launch / serve / boot up** something, your job is ONLY:\n\n1. Start it (`run_background` for dev servers, `run_command` for one-shots).\n2. Verify it came up (read a ready signal via `job_output`, or fetch the URL with `web_fetch` if they want you to confirm).\n3. Report what's running, where (URL / port / pid), and STOP.\n\nDo NOT, in the same turn:\n- Run `tsc` / type-checkers / linters unless the user asked for it.\n- Scan for bugs to \"proactively\" fix. The page rendering is success.\n- Clean up unused imports, dead code, or refactor \"while you're here.\"\n- Edit files to improve anything the user didn't mention.\n\nIf you notice an obvious issue, MENTION it in one sentence and wait for the user to say \"fix it.\" The cost of over-eagerness is real: you burn tokens, make surprise edits the user didn't want, and chain into cascading \"fix the new error I just introduced\" loops. The storm-breaker will cut you off, but the user still sees the mess.\n\n\"It works\" is the end state. Resist the urge to polish.\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 / read / search), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n\nCost-aware escalation (when you're running on deepseek-v4-flash):\n\nIf a task CLEARLY exceeds what flash can do well \u2014 complex cross-file architecture refactors, subtle concurrency / security / correctness invariants you can't resolve with confidence, or a design trade-off you'd be guessing at \u2014 output the exact string `<<<NEEDS_PRO>>>` as the FIRST line of your response (nothing before it, not even whitespace on a separate line). This aborts the current call and retries this turn on deepseek-v4-pro, one shot. Do NOT emit any other content in the same response when you request escalation.\n\nUse this sparingly. Normal tasks \u2014 reading files, small edits, clear bug fixes, straightforward feature additions \u2014 stay on flash. Request escalation ONLY when you would otherwise produce a guess or a visibly-mediocre answer. If in doubt, attempt the task on flash first; the system also escalates automatically if you hit 3+ repair / SEARCH-mismatch errors in a single turn.\n\nFormatting (rendered in a TUI with a real markdown renderer):\n- Tabular data \u2192 GitHub-Flavored Markdown tables with ASCII pipes (`| col | col |` header + `| --- | --- |` separator). Never use Unicode box-drawing characters (\u2502 \u2500 \u253C \u250C \u2510 \u2514 \u2518 \u251C \u2524) \u2014 they look intentional but break terminal word-wrap and render as garbled columns at narrow widths.\n- Keep table cells short (one phrase each). If a cell needs a paragraph, use bullets below the table instead.\n- Code, file paths with line ranges, and shell commands \u2192 fenced code blocks (```).\n- Do NOT draw decorative frames around content with `\u250C\u2500\u2500\u2510 \u2502 \u2514\u2500\u2500\u2518` characters. The renderer adds its own borders; extra ASCII art adds noise and shatters at narrow widths.\n- For flow charts and diagrams: a plain bullet list with `\u2192` or `\u2193` between steps. Don't try to draw boxes-and-arrows in ASCII; it never survives word-wrap.\n";
|
|
2943
3215
|
/**
|
|
2944
3216
|
* Inject the project's `.gitignore` content into the system prompt as a
|
|
2945
3217
|
* "respect this on top of the built-in denylist" hint. We don't parse
|
|
@@ -2968,6 +3240,21 @@ declare function codeSystemPrompt(rootDir: string): string;
|
|
|
2968
3240
|
*/
|
|
2969
3241
|
/** One of the preset bundles (model + harvest + branch combo). */
|
|
2970
3242
|
type PresetName = "fast" | "smart" | "max";
|
|
3243
|
+
/**
|
|
3244
|
+
* How `reasonix code` handles model-issued edits:
|
|
3245
|
+
* - "review" — queue the edit into pendingEdits; user /apply or `y` commits.
|
|
3246
|
+
* - "auto" — apply immediately, snapshot for /undo, show a short undo
|
|
3247
|
+
* banner so the user can roll back with one keystroke.
|
|
3248
|
+
* Persisted so `/mode auto` survives a relaunch. Missing → "review".
|
|
3249
|
+
*/
|
|
3250
|
+
type EditMode = "review" | "auto";
|
|
3251
|
+
/**
|
|
3252
|
+
* reasoning_effort cap for the model. "max" is the agent-class default;
|
|
3253
|
+
* "high" is cheaper / faster. Persisted so `/effort high` survives a
|
|
3254
|
+
* relaunch — earlier versions silently reverted to "max" on every new
|
|
3255
|
+
* session, which burned budget unexpectedly.
|
|
3256
|
+
*/
|
|
3257
|
+
type ReasoningEffort = "high" | "max";
|
|
2971
3258
|
interface ReasonixConfig {
|
|
2972
3259
|
apiKey?: string;
|
|
2973
3260
|
baseUrl?: string;
|
|
@@ -2976,6 +3263,22 @@ interface ReasonixConfig {
|
|
|
2976
3263
|
* Maps to model + harvest + branch combos (see presets.ts). Missing → "fast".
|
|
2977
3264
|
*/
|
|
2978
3265
|
preset?: PresetName;
|
|
3266
|
+
/**
|
|
3267
|
+
* Edit-gate mode for `reasonix code`. See EditMode doc. Absent → "review".
|
|
3268
|
+
*/
|
|
3269
|
+
editMode?: EditMode;
|
|
3270
|
+
/**
|
|
3271
|
+
* Set to `true` the first time we've shown the "Shift+Tab cycles
|
|
3272
|
+
* review/AUTO" onboarding tip in `reasonix code`. Once seen, we stop
|
|
3273
|
+
* posting the tip — the bottom status bar carries the knowledge
|
|
3274
|
+
* forward without further nagging.
|
|
3275
|
+
*/
|
|
3276
|
+
editModeHintShown?: boolean;
|
|
3277
|
+
/**
|
|
3278
|
+
* Last reasoning_effort chosen via `/effort`. Loaded on launch so
|
|
3279
|
+
* "high" stays "high" — default is "max" when unset.
|
|
3280
|
+
*/
|
|
3281
|
+
reasoningEffort?: ReasoningEffort;
|
|
2979
3282
|
/**
|
|
2980
3283
|
* Default MCP server specs to bridge on every `reasonix chat`, in the
|
|
2981
3284
|
* same `"name=cmd args..."` format that `--mcp` takes. Stored as strings
|