@vietor/easy-agent 0.4.7 → 0.4.9
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 +4 -2
- package/dist/main.js +2 -2
- package/dist/tui/App.js +2 -2
- package/dist/tui/Spinner.js +2 -2
- package/package.json +2 -2
- package/dist/tui/ErrorBoundary.js +0 -15
- package/dist/tui/TimelineList.js +0 -9
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/`
|
|
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
|
|
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/main.js
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { loadConfig } from "./config.js";
|
|
6
|
-
import { tryLoadSkills, tryReadFileText, createSession } from "@vietor/easy-agent-core";
|
|
6
|
+
import { tryLoadSkills, tryReadFileText, createSession, SYSTEM_PROMPT_BOUNDARY } from "@vietor/easy-agent-core";
|
|
7
7
|
import { builtinCommands } from "./cmds/builtin.js";
|
|
8
8
|
import { startApp } from "./tui/App.js";
|
|
9
9
|
import { getPackageInfo } from "./util/package.js";
|
|
@@ -74,7 +74,7 @@ export async function main(argv = []) {
|
|
|
74
74
|
const projectPrompt = tryReadFileText(join(cwd, "AGENTS.md")) ?? tryReadFileText(join(cwd, "CLAUDE.md"));
|
|
75
75
|
const systemPrompt = [buildSystemPromptBase(cwd), globalPrompt, projectPrompt]
|
|
76
76
|
.filter(Boolean)
|
|
77
|
-
.join(
|
|
77
|
+
.join(SYSTEM_PROMPT_BOUNDARY);
|
|
78
78
|
const session = await createSession({
|
|
79
79
|
systemPrompt,
|
|
80
80
|
llmConfig: config.llm,
|
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,
|
|
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,
|
|
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 })] }));
|
package/dist/tui/Spinner.js
CHANGED
|
@@ -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,
|
|
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(
|
|
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.
|
|
3
|
+
"version": "0.4.9",
|
|
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.
|
|
26
|
+
"@vietor/easy-agent-core": "0.4.9"
|
|
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
|
-
}
|
package/dist/tui/TimelineList.js
DELETED
|
@@ -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
|
-
});
|