@vietor/easy-agent 0.4.7 → 0.4.8

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/README.md CHANGED
@@ -104,7 +104,7 @@ Both global and project files are loaded and concatenated into the system prompt
104
104
 
105
105
  ### Skills (optional)
106
106
 
107
- Skills are reusable prompts that register themselves as slash commands. Create a subdirectory for each skill under `~/.easy-agent/skills/` (or `~/.claude/skills/`) with a `SKILL.md` file:
107
+ Skills are reusable prompts that register themselves as slash commands. Create a subdirectory for each skill under `~/.easy-agent/skills/` with a `SKILL.md` file (falls back to `~/.claude/skills/`):
108
108
 
109
109
  ```
110
110
  ~/.easy-agent/skills/
@@ -140,7 +140,9 @@ easy-agent --resume # list all saved sessions for this directory
140
140
 
141
141
  Type a prompt and press Enter. The agent streams its reply and calls tools as needed, showing each tool call and a one-line preview of its result. It iterates until the task is done (capped at 50 tool rounds per turn).
142
142
 
143
- The TUI automatically adapts to your terminal width. A status bar at the bottom shows the current context token count, with hints for keyboard shortcuts (<kbd>ESC</kbd> to abort a running task, <kbd>/</kbd> for commands).
143
+ The TUI header shows the model name, version, and configured reasoning effort (e.g. `deepseek-v4-flash · reasoning high`). While the agent is running, a spinner displays the thinking and reply elapsed time alongside token counts (e.g. `⠋ thinking · think 2s · reply 0s · ↑1.2k · ↓0.4k`). If the LLM emits extended thinking blocks, a collapsible reasoning panel appears — press <kbd>t</kbd> to expand/collapse it.
144
+
145
+ A status bar at the bottom shows the context token usage with a progress bar and percentage, along with keyboard shortcut hints: <kbd>ESC</kbd> to abort a running task, <kbd>t</kbd> to toggle reasoning (when available), and <kbd>/</kbd> for commands.
144
146
 
145
147
  ### Built-in tools
146
148
 
package/dist/tui/App.js CHANGED
@@ -14,7 +14,7 @@ export function App({ session }) {
14
14
  const { exit } = useApp();
15
15
  const { columns } = useWindowSize();
16
16
  const view = useSyncExternalStore(session.subscribe, session.getSnapshot);
17
- const [runState, setRunState] = useState({ running: false, elapsed: 0, thinkingElapsed: 0, replyElapsed: 0, promptTokens: 0, completionTokens: 0 });
17
+ const [runState, setRunState] = useState({ running: false, elapsed: 0, thinkingElapsed: 0, replyElapsed: 0, inputTokens: 0, outputTokens: 0 });
18
18
  const [streamingText, setStreamingText] = useState("");
19
19
  const streamingRef = useRef("");
20
20
  const renderTimerRef = useRef(undefined);
@@ -101,7 +101,7 @@ export function App({ session }) {
101
101
  }
102
102
  else {
103
103
  const spinnerLabel = streamingText ? "replying" : "thinking";
104
- runningView = (_jsxs(_Fragment, { children: [reasoningText ? renderReasoning(reasoningText, showReasoning) : null, streamingText ? (_jsx(Box, { marginTop: 1, paddingLeft: 1, paddingRight: 1, borderStyle: "single", borderTop: false, borderRight: false, borderBottom: false, borderColor: "gray", children: _jsx(Markdown, { children: streamingText }) })) : null, _jsx(Box, { marginTop: 1, paddingLeft: 1, children: _jsx(Spinner, { label: spinnerLabel, thinkingElapsed: runState.thinkingElapsed, replyElapsed: runState.replyElapsed, promptTokens: runState.promptTokens, completionTokens: runState.completionTokens }) })] }));
104
+ runningView = (_jsxs(_Fragment, { children: [reasoningText ? renderReasoning(reasoningText, showReasoning) : null, streamingText ? (_jsx(Box, { marginTop: 1, paddingLeft: 1, paddingRight: 1, borderStyle: "single", borderTop: false, borderRight: false, borderBottom: false, borderColor: "gray", children: _jsx(Markdown, { children: streamingText }) })) : null, _jsx(Box, { marginTop: 1, paddingLeft: 1, children: _jsx(Spinner, { label: spinnerLabel, thinkingElapsed: runState.thinkingElapsed, replyElapsed: runState.replyElapsed, inputTokens: runState.inputTokens, outputTokens: runState.outputTokens }) })] }));
105
105
  }
106
106
  }
107
107
  return (_jsxs(Box, { width: columns, flexDirection: "column", children: [_jsx(AppHeader, { cwd: session.cwd, model: session.model, reasoningEffort: session.reasoningEffort }), view.timeline.length > 0 ? (_jsx(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: view.timeline.map((entry, i) => (_jsx(TimelineView, { entry: entry }, i))) })) : null, runningView, view.todos.length > 0 ? _jsx(TodoView, { todos: view.todos }) : null, !runState.running ? (_jsx(PromptOrCommandInput, { commands: allCmds, onCommand: handleCommand, onPrompt: handlePrompt })) : null, _jsx(StatusBar, { contextTokens: session.contextTokens, contextLimit: session.compactThreshold, running: runState.running, questionPending: !!pendingQuestion, reasoningAvailable: !!reasoningText })] }));
@@ -3,11 +3,11 @@ import { useEffect, useState } from "react";
3
3
  import { Text } from "ink";
4
4
  import { timeDisplay, compactDisplay } from "../util/format.js";
5
5
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
6
- export function Spinner({ label, thinkingElapsed, replyElapsed, promptTokens, completionTokens, }) {
6
+ export function Spinner({ label, thinkingElapsed, replyElapsed, inputTokens, outputTokens, }) {
7
7
  const [frame, setFrame] = useState(0);
8
8
  useEffect(() => {
9
9
  const id = setInterval(() => setFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
10
10
  return () => clearInterval(id);
11
11
  }, []);
12
- return (_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: SPINNER_FRAMES[frame] }), _jsxs(Text, { children: [" ", label] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 think ", timeDisplay(thinkingElapsed), " \u00B7 reply ", timeDisplay(replyElapsed), " \u00B7 \u2191", compactDisplay(promptTokens), " \u00B7 \u2193", compactDisplay(completionTokens)] })] }));
12
+ return (_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: SPINNER_FRAMES[frame] }), _jsxs(Text, { children: [" ", label] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 think ", timeDisplay(thinkingElapsed), " \u00B7 reply ", timeDisplay(replyElapsed), " \u00B7 \u2191", compactDisplay(inputTokens), " \u00B7 \u2193", compactDisplay(outputTokens)] })] }));
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vietor/easy-agent",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "type": "module",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "react": "^19.2.7",
24
24
  "string-width": "^8.2.1",
25
25
  "zod": "^4.4.3",
26
- "@vietor/easy-agent-core": "0.4.7"
26
+ "@vietor/easy-agent-core": "0.4.8"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.0.0",
@@ -1,15 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Component } from "react";
3
- import { Box, Text } from "ink";
4
- export class ErrorBoundary extends Component {
5
- state = { error: null };
6
- static getDerivedStateFromError(error) {
7
- return { error };
8
- }
9
- render() {
10
- if (this.state.error) {
11
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "red", bold: true, children: "\u2717 TUI Error" }), _jsx(Text, { dimColor: true, children: this.state.error.message })] }));
12
- }
13
- return this.props.children;
14
- }
15
- }
@@ -1,9 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { memo, useSyncExternalStore } from "react";
3
- import { Box } from "ink";
4
- import { TimelineView } from "./TimelineView.js";
5
- export const TimelineList = memo(function TimelineList({ session }) {
6
- const view = useSyncExternalStore(session.subscribe, session.getSnapshot);
7
- const entries = view.timeline;
8
- return (_jsx(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: entries.map((entry, i) => (_jsx(TimelineView, { entry: entry }, i))) }));
9
- });