@vietor/easy-agent 0.4.10 → 0.4.12

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/main.js CHANGED
@@ -20,6 +20,10 @@ function buildSystemPromptBase(cwd) {
20
20
  - Working directory: ${cwd}`,
21
21
  `Decision making:
22
22
  - When a decision belongs to the user, call AskUser and wait for the answer rather than listing options in prose. Ask when there are multiple reasonable approaches, an irreversible or consequential action, or the request is ambiguous; when you have enough to proceed, act without asking.`,
23
+ `Working style:
24
+ - Read relevant code/config before acting; do not guess implementation details or restate files from memory.
25
+ - Make surgical changes and match existing style; do not refactor unrelated code.
26
+ - Trust tool results as ground truth.`,
23
27
  ].join("\n\n");
24
28
  }
25
29
  async function listSessions(store) {
package/dist/tui/App.js CHANGED
@@ -100,7 +100,7 @@ export function App({ session }) {
100
100
  runningView = (_jsx(QuestionView, { question: pendingQuestion, onAnswer: (ans) => session.submitAnswer(pendingQuestion.id, ans) }));
101
101
  }
102
102
  else {
103
- const spinnerLabel = streamingText ? "replying" : "thinking";
103
+ const spinnerLabel = streamingText ? "replying" : "working";
104
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
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { Text } from "ink";
4
- import { timeDisplay, compactDisplay } from "../util/format.js";
4
+ import { timeFormat, compactFormat } from "@vietor/easy-agent-core";
5
5
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
6
6
  export function Spinner({ label, thinkingElapsed, replyElapsed, inputTokens, outputTokens, }) {
7
7
  const [frame, setFrame] = useState(0);
@@ -9,5 +9,5 @@ export function Spinner({ label, thinkingElapsed, replyElapsed, inputTokens, out
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(inputTokens), " \u00B7 \u2193", compactDisplay(outputTokens)] })] }));
12
+ return (_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: SPINNER_FRAMES[frame] }), _jsxs(Text, { children: [" ", label] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 work ", timeFormat(thinkingElapsed), " \u00B7 reply ", timeFormat(replyElapsed), " \u00B7 \u2191", compactFormat(inputTokens), " \u00B7 \u2193", compactFormat(outputTokens)] })] }));
13
13
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { memo } from "react";
3
3
  import { Box, Text, useWindowSize } from "ink";
4
- import { compactDisplay } from "../util/format.js";
4
+ import { compactFormat } from "@vietor/easy-agent-core";
5
5
  export const StatusBar = memo(function StatusBar({ contextTokens, contextLimit, running, questionPending, reasoningAvailable }) {
6
6
  const { columns } = useWindowSize();
7
7
  const pct = Math.min(100, contextLimit > 0 ? Math.round((contextTokens / contextLimit) * 100) : 0);
@@ -15,5 +15,5 @@ export const StatusBar = memo(function StatusBar({ contextTokens, contextLimit,
15
15
  hints = reasoningAvailable ? "esc stop · t reasoning" : "esc stop";
16
16
  else
17
17
  hints = "/ commands";
18
- return (_jsxs(Box, { width: columns, paddingX: 1, flexDirection: "row", justifyContent: "space-between", borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "gray", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: `context ${compactDisplay(contextTokens)} ` }), _jsx(Text, { color: ctxColor, children: `▕${bar}▏ ${pct}%` })] }), _jsx(Text, { dimColor: true, children: hints })] }));
18
+ return (_jsxs(Box, { width: columns, paddingX: 1, flexDirection: "row", justifyContent: "space-between", borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "gray", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: `context ${compactFormat(contextTokens)} ` }), _jsx(Text, { color: ctxColor, children: `▕${bar}▏ ${pct}%` })] }), _jsx(Text, { dimColor: true, children: hints })] }));
19
19
  });
@@ -1,26 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo } from "react";
2
+ import { memo, useEffect, useState } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { Markdown } from "./components/Markdown.js";
5
- function preview(isError, text) {
6
- if (isError) {
7
- const previewText = text.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
8
- return previewText.length > 100 ? previewText.slice(0, 100) + "…" : previewText;
9
- }
10
- const lineCount = text.length === 0 ? 0 : (text.match(/\n/g) || []).length + 1;
11
- const byteCount = Buffer.byteLength(text, "utf-8");
12
- return `Result: ${byteCount} bytes, ${lineCount} lines`;
13
- }
14
- export const TimelineView = memo(function Entry({ entry }) {
5
+ export const TimelineView = memo(function TimelineView({ entry }) {
15
6
  switch (entry.kind) {
16
7
  case "user":
17
- return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "\u276F " }), entry.text] }) }));
8
+ return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "> " }), entry.text] }) }));
18
9
  case "skill":
19
10
  return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: "magenta", children: "\u25C8 " }), _jsx(Text, { dimColor: true, children: "skill " }), _jsx(Text, { color: "magenta", bold: true, children: entry.name })] }) }));
20
11
  case "assistant":
21
12
  return (_jsx(Box, { marginTop: 1, children: _jsx(Markdown, { children: entry.text }) }));
22
13
  case "tool":
23
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "\u25CF " }), _jsx(Text, { color: "yellow", bold: true, children: entry.name }), entry.summary ? _jsxs(Text, { dimColor: true, children: [" ", entry.summary] }) : null] }), entry.result !== null ? (_jsx(Text, { color: entry.isError ? "red" : "gray", children: ` ${preview(entry.isError ?? false, entry.result)}` })) : null] }));
14
+ return _jsx(ToolEntry, { entry: entry });
24
15
  case "retry":
25
16
  return (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "\u21BB " }), _jsxs(Text, { dimColor: true, children: ["retry ", entry.attempt, "/", entry.max] })] }) }));
26
17
  case "error":
@@ -30,8 +21,22 @@ export const TimelineView = memo(function Entry({ entry }) {
30
21
  case "question":
31
22
  if (entry.answer === null)
32
23
  return null;
33
- return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: `? ${entry.text}` }), _jsx(Text, { dimColor: true, children: ` ${entry.answer || "(skipped)"}` })] }));
24
+ return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: `? ${entry.text}` }), _jsx(Text, { dimColor: true, children: ` ${entry.answer || "(skipped)"}` })] }));
34
25
  case "system":
35
26
  return (_jsx(Box, { children: _jsx(Text, { color: "blue", children: entry.text }) }));
36
27
  }
37
28
  });
29
+ function ToolEntry({ entry }) {
30
+ const running = entry.result === null;
31
+ const [on, setOn] = useState(true);
32
+ useEffect(() => {
33
+ if (!running)
34
+ return;
35
+ const id = setInterval(() => setOn((v) => !v), 500);
36
+ return () => clearInterval(id);
37
+ }, [running]);
38
+ const icon = running ? (on ? "●" : " ") : "●";
39
+ const iconColor = running ? "cyan" : entry.isError ? "red" : "green";
40
+ const nameColor = entry.isError ? "red" : "yellow";
41
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: iconColor, children: `${icon} ` }), _jsx(Text, { bold: true, color: nameColor, children: entry.name }), entry.summary ? _jsx(Text, { dimColor: true, children: `(${entry.summary})` }) : null] }), entry.result !== null ? (_jsx(Text, { color: entry.isError ? "red" : "gray", children: ` ⎿ ${entry.preview}` })) : null] }));
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vietor/easy-agent",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
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.10"
26
+ "@vietor/easy-agent-core": "0.4.12"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.0.0",
@@ -36,7 +36,9 @@
36
36
  },
37
37
  "keywords": [
38
38
  "ai",
39
- "cli"
39
+ "cli",
40
+ "llm",
41
+ "agent"
40
42
  ],
41
43
  "scripts": {
42
44
  "build": "tsc",
@@ -1,12 +0,0 @@
1
- const timeFormatter = new Intl.NumberFormat("en-US", { style: "unit", unit: "second", unitDisplay: "narrow" });
2
- const compactFormatter = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 2 });
3
- export function timeDisplay(value) {
4
- if (!value)
5
- return "0s";
6
- return timeFormatter.format(value);
7
- }
8
- export function compactDisplay(value) {
9
- if (!value)
10
- return "0";
11
- return compactFormatter.format(value);
12
- }