Repository navigation
Proxima v1.1.0
What's changed
Added
Terminal UX — Rich Feedback & Activity Indicators
-
AgentCallbacks.onIterationStart(iteration, maxIterations)— called at the start of each loop iteration;Rendereruses it to show⠋ Thinking… [N/10]spinner with iteration depth between tool calls -
AgentCallbacks.onToolExecuting(toolCall)— fires immediately before tool execution (after permission grant);Renderershows◆ Reading core/session.dart…activity line -
Animated blink dots on in-progress tool lines — executing line pulses
…→….→…..→…...every 400ms viaTimer.periodicso users see the tool is alive, not stuck -
Context preserved in result lines — the activity label from
onToolExecutingis held in_pendingLabeland reused inonToolResult; result now reads✓ Reading core/session.dart… (1423 lines) 0.3sinstead of✓ read_file (1423 lines) -
Per-tool elapsed time —
_toolStartTimecaptured atonToolExecuting, formatted asmsorsand appended dim to every result line -
Turn elapsed time in usage report —
_turnStartTimeset on iteration 1 viaonIterationStart; appended to the↑N ↓N total: Nline:↑1240 ↓387 total: 14832 4.2s cost: $0.0042 -
onThinkingnow shows⠋ Working…spinner after printing the reasoning line, bridging the gap between reasoning and the first tool executing line -
Clarify styling —
onClarifynow renders? questionwith cyan?instead of raw bold text -
_fmtElapsed(Duration)helper —<1000ms→NNNms,≥1000ms→N.Ns -
Static
showSpinner('Thinking...')calls removed fromrepl.dart; spinner is now driven entirely byonIterationStart -
342 tests total (up from 237 — 105 new tests across
find_references_test.dart,get_imports_test.dart,test/tools/plugin/shell_plugin_test.dart) -
find_referencestool (lib/tools/search/find_references_tool.dart,safe) — finds all usages of a symbol across the codebase using\b<symbol>\bword-boundary matching. Supportspathscoping,file_extensionsfilter,exclude_definitionflag (skips definition lines using the same heuristics assearch_symbol), andmax_results(default 100). Skips.git,node_modules,build,.dart_tool,.pub-cache, and generated files. Output:file:line contentlines followed by aFound N references in M files.summary. -
get_importstool (lib/tools/search/get_imports_tool.dart,safe) — parses all import/require statements from a single file and categorises them by source type. Supports Dart ([dart]/[package]/[local]), JS/TS ([node]/[local]), Python ([stdlib]/[local]), and Go ([stdlib]/[external]).resolve_paths: trueexpands local imports to project-relative paths. Throws typedToolError(notFound)when the file does not exist;ToolError(parseError)for binary files or unsupported extensions. -
Plugin system — shell/binary drop-in tools via
.proxima/plugins/<name>/:ShellPluginTool(lib/tools/plugin/shell_plugin_tool.dart) — wraps any executable as aProximaTool; passes args as JSON on stdin, reads result from stdout, enforces configurabletimeout_seconds; non-zero exit →ToolErrorPluginLoader(lib/tools/plugin/plugin_loader.dart) —load(dirs, workingDir)discovers plugin directories, validatesplugin.json(required fields:name,description,executable,input_schema), checks the executable exists and has the execute bit, parsesrisk_levelstring →RiskLevel(defaults toconfirmfor unrecognised values); logs startup warnings for invalid plugins and never throwsProximaConfig.pluginDirsfield —List<String>, default['.proxima/plugins']; parsed fromplugin_dirsYAML key; included incopyWith()_buildToolRegistry()inrepl.dartis nowasync; loads plugins after all built-ins; silently skips any plugin whose name conflicts with a built-in tool (built-in wins)
Plan Mode
/plan <task>slash command — runs the agent inSessionMode.safewithisPlanMode: true, produces.proxima/plan.md, then shows an arrow-key picker (Execute / Edit / Skip) before executing/executeslash command — executes the saved plan in.proxima/plan.mdwithout re-running the research phase/costslash command — shows current session cost and a cost table for the 10 most recent sessions- Shift+Tab REPL mode cycle — Shift+Tab now cycles through three modes: normal → plan → accept-edits → normal; each press updates the prompt tag (
❯/❯ [plan]/❯ [edits]) and shows a compact tip line below the prompt (⏸ plan mode on/⏵⏵ accept edits on); the previous verbose green print above the prompt is removed - Accept-edits mode (
❯ [edits]) — third cycle state; sets_permissionGate.modetoSessionMode.autotransiently so confirm-level tool calls (writes, commands) execute without the Approve/Deny picker; cycling back to normal restores the configured mode;_session.modeand the session file are unchanged (transient only) _ReplModeenum (normal | plan | acceptEdits) inrepl.dartreplaces the formerbool _planModefield_cycleReplMode()replaces_togglePlanMode(); syncs permission gate and calls_renderer.updateStatus(planMode:, acceptEditsMode:)with no print_modeTip()helper returns the tip string for the active mode (null when normal)ReadLine.readLine()gains optionalString? statusTipparameter; stored as_statusTipfield and folded into_renderPanel()— when no suggestions are visible and a tip is set, one dim tip line is drawn below the prompt; suggestions take priority (tip suppressed while panel is open)ReadLine.readLine()gainsonShiftTabcallback; detectsESC [ Z(standard terminal Shift+Tab), fires the callback, and returnsReadLine.shiftTabSentinel— no history entry, no completion side-effectsReadLine.shiftTabSentinelpublic constant (\x00__shift_tab__) — lets the REPL identify the toggle without a magic string literalProximaSession.isPlanModeruntime-only bool (not persisted) — set byProximaSession.create(..., isPlanMode: true)ProximaSession.cumulativeCostfield — tracks session-total USD cost; persisted to session JSON ascumulative_cost; incremented viarecordCost(double)ContextBuilderinjects a PLAN MODE system-prompt block whensession.isPlanModeis true — instructs the agent to use only read tools, then callwrite_planwith a structured markdown plan (context, step-by-step changes, tests, risks)WritePlanTool(lib/tools/agent/write_plan_tool.dart) —confirm-risk tool that writes agent-produced plans to.proxima/plan.md; supportsdryRun()
Model Persistence
/model <name>now persists the selected model asmodel:in~/.proxima/config.yaml— future sessions start with the last chosen modelProximaConfig.saveDefaultModel(model)static helper — creates~/.proxima/config.yaml(and parent directory) if absent; replaces themodel:line in-place, leaving all other keys untouched
Cost Tracking
CostCalculator.compute(model, usage)— maps known Anthropic model IDs to per-token USD rates; returns0.0for unknown/local modelsCostCalculator.format(cost)— formats USD cost as$0.0000(4 decimal places)AgentCallbacks.onUsageReportextended withturnCostandsessionCostparameters;Rendererappendscost: $N.NNNNto the usage line when non-zeroAgentLoopcomputesturnCostviaCostCalculator.computeand passes it toonUsageReportalongside the session cumulative/statusnow showsCostrow when session cost is non-zero
Search
search_symboltool (lib/tools/search/search_symbol_tool.dart) — searches for symbol definitions (class, function, variable, any) using regex across the working directory; supportspathscoping andkindfiltering; rejects path traversal
Interactive UX — Consistent Arrow-Key Pickers & Status Line
PickerWidget(lib/renderer/picker_widget.dart) — shared synchronous arrow-key single-choice picker; renders▶ Option hintrows with inverse-video highlight; used everywhere a fixed choice list is presented; Escape/Ctrl-C returnsdefaultIndex- Permission prompt → picker —
PermissionPrompt._confirmPrompt()replaced withPickerWidget.pick(options: ['Approve', 'Deny'], ...)with dim hints; high-risk typedCONFIRMpath unchanged - Stuck dialog → picker —
TaskSummaryRenderer.renderStuck()a/csingle-keypress replaced withPickerWidget.pick(options: ['Continue', 'Abort'], defaultIndex: 1)(Abort is the safer default when stuck) /modeno-arg → picker —/modewith no argument opens an arrow-key picker pre-selected to the current mode (safe / confirm / auto with hints); previously showed a dim text line only- Plan approval picker simplified —
_planApprovalPicker()inrepl.dartnow delegates toPickerWidgetinstead of the hand-rolled_renderPlanPicker/_clearPlanPickerhelpers (removed) - Persistent status line after each turn —
Renderer.onUsageReport()calls_printStatusLine()after the token line; showsmodel: <name>always,mode: <name>when non-default (non-confirm),[plan]when plan mode active,[edits]when accept-edits mode active Renderer.updateStatus({model, mode, planMode, acceptEditsMode})— push current state into the renderer; called frominitialize(),_switchModel(),_switchMode(), and_cycleReplMode()ClarifyResponsegainsoptionsfield —List<String> options(default empty); when non-empty the agent loop stays alive:onClarifyWithOptionsshows a picker, the selected answer is injected as a user message, and the loop continues without exitingAgentCallbacks.onClarifyWithOptions(question, options)— new abstract method;Rendererimplements it viaPickerWidget.pick(options: options); all mock callbacks in tests returnFuture.value(0)SchemaValidator._validateClarify()parses optionaloptionsarray from the LLM responseContextBuilderadds operating rule 8 — instructs the LLM to use"options"array inclarifyresponses when presenting a fixed choice list
Fixed
- Plan mode agent offered numbered options menu instead of stopping — system prompt now explicitly forbids presenting options or asking for approval after
write_plan; instructs the agent to emit a brief confirmation and stop onFinalResponseprinted a spurious blank line during streaming — when streaming was active,textis''; the renderer now skipswriteln('')+_renderMarkdown('')when text is empty, avoiding a double blank line above the closing separator_dispatchPlansilently discarded async results —_runPlan()and_handleExecutePlan()are now wrapped withunawaited()making the fire-and-forget intent explicit- Redundant
Researching…spinner in_runPlan— removed manualshowSpinner/hideSpinnercalls; the renderer drives the spinner correctly viaonIterationStart - Plan approval picker
Edithint was misleading — said "save to .proxima/plan.md" (plan is already saved at that point); corrected to "edit .proxima/plan.md, then run /execute" write_plancalled twice by small local models — the agent loop now exits immediately after the first successfulwrite_planin a plan-mode session (session.planWritten = true); the LLM no longer takes a second turn, eliminating the duplicate write and the "Would you like to: 1. Approve..." options noise that appeared before the picker- Status line printed before plan content —
onUsageReportwas firing (and printing the status line) before_maybeShowPlanPickerdisplayed the plan text and picker; fixed bysuppressNextStatusLine()before the plan agent turn andprintStatusLine()explicitly after the picker - Shift+Tab verbose print removed — the green
Plan mode ON/OFF+ dim description line printed above the prompt on every toggle is replaced by the compact tip line rendered below the prompt byReadLine - Shift+Tab detection used exact byte match only — added
endsWith('[Z')guard to cover terminal emulators that deliver the sequence with a prefix variation
Installation
macOS / Linux — install script
curl -fsSL https://raw.githubusercontent.com/jizzel/proxima/main/install.sh | shmacOS (Apple Silicon + Intel via Rosetta 2)
curl -fsSL https://github.com/jizzel/proxima/releases/download/v1.1.0/proxima-macos-arm64 \
-o /usr/local/bin/proxima && chmod +x /usr/local/bin/proximaLinux (x86_64)
curl -fsSL https://github.com/jizzel/proxima/releases/download/v1.1.0/proxima-linux-x64 \
-o /usr/local/bin/proxima && chmod +x /usr/local/bin/proximaWindows (x86_64) — PowerShell
irm https://raw.githubusercontent.com/jizzel/proxima/main/install.ps1 | iexOr download proxima-windows-x64.exe below and add it to a directory on your PATH.