@code4bug/jarvis-agent 1.3.2 → 1.3.4
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/commands/index.js +3 -0
- package/dist/components/DangerConfirm.js +14 -18
- package/dist/components/StatusBar.js +14 -1
- package/dist/screens/repl.js +7 -1
- package/dist/screens/slashCommands.js +3 -0
- package/dist/skills/index.js +1 -2
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +5 -0
- package/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
const builtinCommands = [
|
|
9
9
|
{ name: 'init', description: '初始化项目信息,生成 JARVIS.md', category: 'builtin', submitMode: 'action' },
|
|
10
10
|
{ name: 'new', description: '开启新会话,重新初始化上下文', category: 'builtin', submitMode: 'action' },
|
|
11
|
+
{ name: 'exit', description: '退出应用程序', category: 'builtin', submitMode: 'action' },
|
|
12
|
+
{ name: 'quit', description: '退出应用程序', category: 'builtin', submitMode: 'action' },
|
|
13
|
+
{ name: 'bye', description: '退出应用程序', category: 'builtin', submitMode: 'action' },
|
|
11
14
|
{ name: 'resume', description: '恢复历史会话上下文', category: 'builtin', submitMode: 'list' },
|
|
12
15
|
{ name: 'help', description: '显示帮助信息', category: 'builtin', submitMode: 'action' },
|
|
13
16
|
{ name: 'agent', description: '切换智能体', category: 'builtin', submitMode: 'list' },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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,
|
|
23
|
-
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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
|
}
|
package/dist/screens/repl.js
CHANGED
|
@@ -208,6 +208,12 @@ export default function REPL() {
|
|
|
208
208
|
const parts = trimmed.slice(1).split(/\s+/);
|
|
209
209
|
const cmdName = parts[0].toLowerCase();
|
|
210
210
|
const hasArgs = parts.length > 1 && parts.slice(1).join('').length > 0;
|
|
211
|
+
if (['exit', 'quit', 'bye'].includes(cmdName)) {
|
|
212
|
+
setInput('');
|
|
213
|
+
slashMenu.setSlashMenuVisible(false);
|
|
214
|
+
handleExit();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
211
217
|
// 内置命令
|
|
212
218
|
if (['new', 'help', 'init', 'session_clear', 'permissions', 'skills', 'version'].includes(cmdName)) {
|
|
213
219
|
setInput('');
|
|
@@ -319,7 +325,7 @@ export default function REPL() {
|
|
|
319
325
|
clearStream();
|
|
320
326
|
abortRequestedRef.current = false;
|
|
321
327
|
await engineRef.current.handleQuery(trimmed, callbacks);
|
|
322
|
-
}, [isProcessing, pushHistory, clearStream, slashMenu, handleNewSession]);
|
|
328
|
+
}, [isProcessing, pushHistory, clearStream, slashMenu, handleNewSession, handleExit]);
|
|
323
329
|
// ===== 输入处理 =====
|
|
324
330
|
const handleUpArrow = useCallback(() => {
|
|
325
331
|
const result = navigateUp(input);
|
|
@@ -26,6 +26,9 @@ export async function executeSlashCommand(cmdName) {
|
|
|
26
26
|
'可用命令:',
|
|
27
27
|
' /init 初始化项目信息,生成 JARVIS.md',
|
|
28
28
|
' /new 开启新会话,重新初始化上下文',
|
|
29
|
+
' /exit 退出应用程序',
|
|
30
|
+
' /quit 退出应用程序',
|
|
31
|
+
' /bye 退出应用程序',
|
|
29
32
|
' /resume 恢复历史会话(支持二级菜单选择)',
|
|
30
33
|
' /resume <ID> 直接恢复指定会话',
|
|
31
34
|
' /help 显示此帮助信息',
|
package/dist/skills/index.js
CHANGED
|
@@ -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
|
|
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 */
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -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;
|
package/dist/tools/index.js
CHANGED
|
@@ -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
|
+
}
|