@code4bug/jarvis-agent 1.3.2 → 1.3.3

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.
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * 危险命令交互式确认组件
4
4
  *
@@ -7,21 +7,18 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
7
7
  * 2. 持久授权(写入 ~/.jarvis/.permissions.json)
8
8
  * 3. 取消执行
9
9
  */
10
- import React, { useState } from 'react';
10
+ import { useState } from 'react';
11
11
  import { Box, Text, useInput, useStdout } from 'ink';
12
12
  const OPTIONS = [
13
13
  { key: 'once', label: '临时执行(仅本次会话)', color: 'yellow' },
14
14
  { key: 'always', label: '持久授权(写入 ~/.jarvis/.permissions.json)', color: 'green' },
15
15
  { key: 'cancel', label: '取消执行', color: 'red' },
16
16
  ];
17
- // 圆角边框字符
18
- const BORDER = { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│' };
19
17
  export default function DangerConfirm({ command, reason, ruleName, onSelect }) {
20
18
  const [selectedIndex, setSelectedIndex] = useState(2); // 默认选中「取消」,最安全
21
19
  const { stdout } = useStdout();
22
- const cols = Math.min(stdout?.columns ?? 80, 80);
23
- // 内容区宽度 = 总宽度 - 左右边框(2) - 左右 padding(2)
24
- const innerWidth = cols - 4;
20
+ const cols = Math.min(stdout?.columns ?? 80, 84);
21
+ const contentWidth = Math.max(32, cols - 8);
25
22
  useInput((_input, key) => {
26
23
  if (key.upArrow) {
27
24
  setSelectedIndex((prev) => (prev > 0 ? prev - 1 : OPTIONS.length - 1));
@@ -36,15 +33,14 @@ export default function DangerConfirm({ command, reason, ruleName, onSelect }) {
36
33
  onSelect('cancel');
37
34
  }
38
35
  });
39
- const hLine = BORDER.h.repeat(cols - 2);
40
- const topBorder = BORDER.tl + hLine + BORDER.tr;
41
- const bottomBorder = BORDER.bl + hLine + BORDER.br;
42
- /** 渲染一行带左右边框的内容 */
43
- const row = (children) => (_jsxs(Box, { children: [_jsxs(Text, { color: "yellow", children: [BORDER.v, " "] }), _jsx(Box, { width: innerWidth, children: children }), _jsxs(Text, { color: "yellow", children: [" ", BORDER.v] })] }));
44
- const emptyRow = row(_jsx(Text, { children: " " }));
45
- return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: "yellow", children: topBorder }), row(_jsx(Text, { color: "yellow", bold: true, children: "\u5B89\u5168\u56F4\u680F\u62E6\u622A" })), emptyRow, row(_jsxs(Text, { color: "gray", children: ["\u547D\u4EE4: ", _jsx(Text, { color: "white", bold: true, children: command })] })), row(_jsxs(Text, { color: "gray", children: ["\u89C4\u5219: ", _jsx(Text, { color: "cyan", children: ruleName })] })), row(_jsx(Text, { color: "red", children: reason })), emptyRow, row(_jsx(Text, { color: "gray", dimColor: true, children: "\u4F7F\u7528 \u2191\u2193 \u9009\u62E9\uFF0CEnter \u786E\u8BA4\uFF0CESC \u53D6\u6D88:" })), emptyRow, OPTIONS.map((opt, i) => {
46
- const isSelected = i === selectedIndex;
47
- const prefix = isSelected ? '❯ ' : ' ';
48
- return (_jsx(React.Fragment, { children: row(_jsxs(Text, { color: isSelected ? opt.color : 'gray', bold: isSelected, children: [prefix, opt.label] })) }, opt.key));
49
- }), _jsx(Text, { color: "yellow", children: bottomBorder })] }));
36
+ const section = (label, value) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "gray", children: label }), _jsx(Box, { width: contentWidth, marginTop: 0, children: value })] }));
37
+ return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { backgroundColor: "yellow", color: "black", bold: true, children: ' 安全围栏 ' }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, color: "white", children: "\u68C0\u6D4B\u5230\u9AD8\u98CE\u9669\u547D\u4EE4" }), _jsx(Text, { color: "gray", children: "\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u6267\u884C" })] })] }), section('命令', _jsx(Text, { color: "white", bold: true, wrap: "truncate-end", children: command })), section('规则', _jsx(Text, { color: "cyan", children: ruleName })), section('原因', _jsx(Text, { color: "red", children: reason })), _jsx(Box, { marginTop: 2, children: _jsx(Text, { color: "gray", dimColor: true, children: "\u4F7F\u7528 \u2191\u2193 \u9009\u62E9\uFF0CEnter \u786E\u8BA4\uFF0CESC \u53D6\u6D88" }) }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: OPTIONS.map((opt, i) => {
38
+ const isSelected = i === selectedIndex;
39
+ const prefix = isSelected ? '›' : ' ';
40
+ const textColor = isSelected ? 'black' : opt.color;
41
+ const backgroundColor = isSelected
42
+ ? (opt.color === 'yellow' ? 'yellow' : opt.color === 'green' ? 'green' : 'red')
43
+ : undefined;
44
+ return (_jsx(Box, { marginTop: i === 0 ? 0 : 1, children: _jsx(Text, { color: textColor, backgroundColor: backgroundColor, bold: isSelected, children: ` ${prefix} ${opt.label} ` }) }, opt.key));
45
+ }) })] }));
50
46
  }
@@ -2,6 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import { PROJECT_NAME, getContextTokenLimit, getModelName, isThinkingModeToggleEnabled } from '../config/constants.js';
5
+ import { getToolStatsText } from '../tools/index.js';
6
+ function truncateText(text, maxChars) {
7
+ if (maxChars <= 0)
8
+ return '';
9
+ if (text.length <= maxChars)
10
+ return text;
11
+ if (maxChars === 1)
12
+ return '…';
13
+ return `${text.slice(0, maxChars - 1)}…`;
14
+ }
5
15
  /** 生成 token 用量进度条 */
6
16
  function tokenProgressBar(used, limit, barWidth) {
7
17
  const ratio = Math.min(used / limit, 1);
@@ -15,7 +25,8 @@ function StatusBar({ width, totalTokens, activeAgents = 0 }) {
15
25
  const modelName = getModelName();
16
26
  const contextTokenLimit = getContextTokenLimit();
17
27
  const thinkingModeToggleEnabled = isThinkingModeToggleEnabled();
18
- const left = ` ${modelName} │ ${PROJECT_NAME}`;
28
+ const toolStats = getToolStatsText();
29
+ const rawLeft = ` ${modelName} │ ${PROJECT_NAME} │ ${toolStats}`;
19
30
  // 右侧:智能体数量(有后台 Agent 时显示)+ token 进度条 + 思考模式切换(可选)
20
31
  const agentPart = activeAgents > 0 ? `⬡ ${activeAgents} agent${activeAgents > 1 ? 's' : ''} │ ` : '';
21
32
  const tokenLabel = `${totalTokens}/${contextTokenLimit}`;
@@ -24,6 +35,8 @@ function StatusBar({ width, totalTokens, activeAgents = 0 }) {
24
35
  const effortPart = thinkingModeToggleEnabled ? ' │ ● medium · /effort' : '';
25
36
  // 右侧完整文本长度(用于计算间距)
26
37
  const rightLen = agentPart.length + tokenLabel.length + 1 + barWidth + effortPart.length + 1;
38
+ const leftMaxWidth = Math.max(width - rightLen - 1, 1);
39
+ const left = truncateText(rawLeft, leftMaxWidth);
27
40
  const gap = Math.max(width - left.length - rightLen, 1);
28
41
  return (_jsxs(Box, { children: [_jsx(Text, { color: "gray", children: left }), _jsx(Text, { children: ' '.repeat(gap) }), activeAgents > 0 && _jsx(Text, { color: "cyan", children: agentPart }), _jsxs(Text, { color: "gray", children: [tokenLabel, " "] }), _jsx(Text, { color: color, children: bar }), thinkingModeToggleEnabled && _jsx(Text, { color: "gray", children: effortPart }), _jsx(Text, { children: " " })] }));
29
42
  }
@@ -12,7 +12,7 @@
12
12
  import { exec } from 'child_process';
13
13
  import fs from 'fs';
14
14
  import path from 'path';
15
- import { scanExternalSkills, getExternalSkillsDir } from './loader.js';
15
+ import { scanExternalSkills } from './loader.js';
16
16
  import { allTools as builtinTools } from '../tools/index.js';
17
17
  // ===== 缓存 =====
18
18
  let _skillCache = null;
@@ -210,7 +210,6 @@ export function loadExternalSkills() {
210
210
  if (_skillCache)
211
211
  return _skillCache;
212
212
  _skillCache = scanExternalSkills();
213
- console.log(`[skills] 已加载 ${_skillCache.length} 个外部 skill (${getExternalSkillsDir()})`);
214
213
  return _skillCache;
215
214
  }
216
215
  /** 获取合并后的所有工具:内置 tools + 外部 skills */
@@ -22,3 +22,5 @@ export declare function findTool(name: string): Tool | undefined;
22
22
  export declare function getAllTools(): Tool[];
23
23
  /** 按名称查找工具(内置 + 外部 skills) */
24
24
  export declare function findToolMerged(name: string): Tool | undefined;
25
+ /** 获取状态栏用的工具统计摘要 */
26
+ export declare function getToolStatsText(): string;
@@ -27,6 +27,7 @@ export function findTool(name) {
27
27
  }
28
28
  // ===== 合并工具(内置 + 外部 Skills)=====
29
29
  import { getMergedTools, findMergedTool } from '../skills/index.js';
30
+ import { listSkills } from '../skills/index.js';
30
31
  /** 获取所有工具(内置 + 外部 skills),供 QueryEngine 使用 */
31
32
  export function getAllTools() {
32
33
  return getMergedTools();
@@ -35,3 +36,7 @@ export function getAllTools() {
35
36
  export function findToolMerged(name) {
36
37
  return findMergedTool(name);
37
38
  }
39
+ /** 获取状态栏用的工具统计摘要 */
40
+ export function getToolStatsText() {
41
+ return `skills(${allTools.length}/${listSkills().length})`;
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code4bug/jarvis-agent",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "基于 React + TypeScript + Ink 构建的命令行智能体交互界面",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",