@code4bug/jarvis-agent 1.3.8 → 1.3.10
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 +1 -0
- package/dist/cli.js +24 -3
- package/dist/commands/index.js +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -3
- package/dist/screens/AppBootstrap.d.ts +5 -1
- package/dist/screens/AppBootstrap.js +2 -2
- package/dist/screens/repl.d.ts +5 -1
- package/dist/screens/repl.js +54 -3
- package/dist/screens/slashCommands.js +58 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
import { APP_VERSION } from './config/constants.js';
|
|
3
3
|
import { ensureLoggerReady, logError, logInfo } from './core/logger.js';
|
|
4
4
|
import { startJarvis } from './index.js';
|
|
5
|
-
const
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const arg = args[0];
|
|
7
|
+
function printCliUsage() {
|
|
8
|
+
console.log([
|
|
9
|
+
'用法:',
|
|
10
|
+
' jarvis',
|
|
11
|
+
' jarvis --version',
|
|
12
|
+
' jarvis --resume <sessionId>',
|
|
13
|
+
].join('\n'));
|
|
14
|
+
}
|
|
6
15
|
ensureLoggerReady();
|
|
7
16
|
logInfo('cli.launch', {
|
|
8
|
-
argv:
|
|
17
|
+
argv: args,
|
|
9
18
|
version: APP_VERSION,
|
|
10
19
|
});
|
|
11
20
|
if (arg === '--version' || arg === '-v' || arg === 'version') {
|
|
@@ -13,10 +22,22 @@ if (arg === '--version' || arg === '-v' || arg === 'version') {
|
|
|
13
22
|
console.log(APP_VERSION);
|
|
14
23
|
process.exit(0);
|
|
15
24
|
}
|
|
25
|
+
if (arg === '--resume') {
|
|
26
|
+
const sessionId = args[1]?.trim();
|
|
27
|
+
if (!sessionId) {
|
|
28
|
+
logError('cli.resume.missing_session_id', new Error('missing session id'));
|
|
29
|
+
printCliUsage();
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
logInfo('cli.resume', { sessionId });
|
|
33
|
+
startJarvis({ initialResumeSessionId: sessionId });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
startJarvis();
|
|
37
|
+
}
|
|
16
38
|
process.on('uncaughtException', (error) => {
|
|
17
39
|
logError('process.uncaught_exception', error);
|
|
18
40
|
});
|
|
19
41
|
process.on('unhandledRejection', (reason) => {
|
|
20
42
|
logError('process.unhandled_rejection', reason);
|
|
21
43
|
});
|
|
22
|
-
startJarvis();
|
package/dist/commands/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
/** 内置命令 */
|
|
8
8
|
const builtinCommands = [
|
|
9
9
|
{ name: 'init', description: '初始化项目信息,生成 JARVIS.md', category: 'builtin', submitMode: 'action' },
|
|
10
|
+
{ name: 'about', description: '查看 Jarvis 的详细特性、功能与信息', category: 'builtin', submitMode: 'action' },
|
|
10
11
|
{ name: 'new', description: '开启新会话,重新初始化上下文', category: 'builtin', submitMode: 'action' },
|
|
11
12
|
{ name: 'exit', description: '退出应用程序', category: 'builtin', submitMode: 'action' },
|
|
12
13
|
{ name: 'quit', description: '退出应用程序', category: 'builtin', submitMode: 'action' },
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { render } from 'ink';
|
|
3
3
|
import { logInfo } from './core/logger.js';
|
|
4
4
|
import AppBootstrap from './screens/AppBootstrap.js';
|
|
5
|
-
export function startJarvis() {
|
|
6
|
-
logInfo('app.render.start');
|
|
7
|
-
render(_jsx(AppBootstrap, {}), { exitOnCtrlC: false });
|
|
5
|
+
export function startJarvis(options = {}) {
|
|
6
|
+
logInfo('app.render.start', { initialResumeSessionId: options.initialResumeSessionId });
|
|
7
|
+
render(_jsx(AppBootstrap, { initialResumeSessionId: options.initialResumeSessionId }), { exitOnCtrlC: false });
|
|
8
8
|
}
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
interface AppBootstrapProps {
|
|
2
|
+
initialResumeSessionId?: string;
|
|
3
|
+
}
|
|
4
|
+
export default function AppBootstrap({ initialResumeSessionId }: AppBootstrapProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export {};
|
|
@@ -3,10 +3,10 @@ import { useState } from 'react';
|
|
|
3
3
|
import REPL from './repl.js';
|
|
4
4
|
import SetupWizard from './setup/SetupWizard.js';
|
|
5
5
|
import { checkBootstrapStatus } from '../config/bootstrap.js';
|
|
6
|
-
export default function AppBootstrap() {
|
|
6
|
+
export default function AppBootstrap({ initialResumeSessionId }) {
|
|
7
7
|
const [status, setStatus] = useState(() => checkBootstrapStatus());
|
|
8
8
|
if (status.ok) {
|
|
9
|
-
return _jsx(REPL, {});
|
|
9
|
+
return _jsx(REPL, { initialResumeSessionId: initialResumeSessionId });
|
|
10
10
|
}
|
|
11
11
|
return (_jsx(SetupWizard, { initialStatus: status, onCompleted: () => {
|
|
12
12
|
setStatus(checkBootstrapStatus());
|
package/dist/screens/repl.d.ts
CHANGED
package/dist/screens/repl.js
CHANGED
|
@@ -21,14 +21,28 @@ import { getAgentSubCommands } from '../commands/index.js';
|
|
|
21
21
|
import { setActiveAgent } from '../config/agentState.js';
|
|
22
22
|
import { buildShortcutHelpText } from '../config/shortcuts.js';
|
|
23
23
|
import { hideTerminalCursor, showTerminalCursor } from '../terminal/cursor.js';
|
|
24
|
-
export default function REPL() {
|
|
24
|
+
export default function REPL({ initialResumeSessionId }) {
|
|
25
25
|
const { exit } = useApp();
|
|
26
26
|
const width = useTerminalWidth();
|
|
27
27
|
const windowFocused = useWindowFocus();
|
|
28
|
+
const buildExitHint = useCallback(() => {
|
|
29
|
+
const sessionId = sessionRef.current.id?.trim();
|
|
30
|
+
if (!sessionId)
|
|
31
|
+
return '';
|
|
32
|
+
const separatorWidth = Math.max(process.stdout.columns ?? 0, 80);
|
|
33
|
+
const separator = '─'.repeat(separatorWidth);
|
|
34
|
+
return `\n${separator}\n\nResume this session with:\njarvis --resume ${sessionId}\n\n`;
|
|
35
|
+
}, []);
|
|
28
36
|
const handleExit = useCallback(() => {
|
|
37
|
+
const exitHint = buildExitHint();
|
|
29
38
|
exit();
|
|
30
|
-
setTimeout(() =>
|
|
31
|
-
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
showTerminalCursor();
|
|
41
|
+
if (exitHint)
|
|
42
|
+
process.stdout.write(exitHint);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}, 50);
|
|
45
|
+
}, [buildExitHint, exit]);
|
|
32
46
|
const { countdown, handleCtrlC } = useDoubleCtrlCExit(handleExit);
|
|
33
47
|
const { pushHistory, navigateUp, navigateDown, resetNavigation } = useInputHistory();
|
|
34
48
|
const [messages, setMessages] = useState([]);
|
|
@@ -145,6 +159,43 @@ export default function REPL() {
|
|
|
145
159
|
logInfo('ui.repl.unmounted');
|
|
146
160
|
};
|
|
147
161
|
}, []);
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!initialResumeSessionId || !engineRef.current)
|
|
164
|
+
return;
|
|
165
|
+
const result = engineRef.current.loadSession(initialResumeSessionId);
|
|
166
|
+
if (result) {
|
|
167
|
+
stopAll();
|
|
168
|
+
setMessages([
|
|
169
|
+
...result.messages,
|
|
170
|
+
{
|
|
171
|
+
id: `resume-cli-${Date.now()}`,
|
|
172
|
+
type: 'system',
|
|
173
|
+
status: 'success',
|
|
174
|
+
content: `已通过启动参数恢复会话 ${initialResumeSessionId.slice(0, 8)}...(${result.messages.length} 条消息)`,
|
|
175
|
+
timestamp: Date.now(),
|
|
176
|
+
},
|
|
177
|
+
]);
|
|
178
|
+
sessionRef.current = result.session;
|
|
179
|
+
tokenCountRef.current = result.session.totalTokens;
|
|
180
|
+
syncTokenDisplay(result.session.totalTokens);
|
|
181
|
+
setLoopState(null);
|
|
182
|
+
setIsProcessing(false);
|
|
183
|
+
setShowWelcome(false);
|
|
184
|
+
logInfo('ui.resume_from_cli.success', {
|
|
185
|
+
sessionId: initialResumeSessionId,
|
|
186
|
+
messageCount: result.messages.length,
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
setMessages((prev) => [...prev, {
|
|
191
|
+
id: `resume-cli-error-${Date.now()}`,
|
|
192
|
+
type: 'error',
|
|
193
|
+
status: 'error',
|
|
194
|
+
content: `启动时恢复会话失败:${initialResumeSessionId} 不存在或已损坏`,
|
|
195
|
+
timestamp: Date.now(),
|
|
196
|
+
}]);
|
|
197
|
+
logWarn('ui.resume_from_cli.failed', { sessionId: initialResumeSessionId });
|
|
198
|
+
}, [initialResumeSessionId, stopAll, syncTokenDisplay]);
|
|
148
199
|
// 订阅后台 SubAgent 计数变化
|
|
149
200
|
useEffect(() => {
|
|
150
201
|
return subscribeAgentCount((count) => setActiveAgents(count));
|
|
@@ -3,6 +3,7 @@ import { APP_VERSION } from '../config/constants.js';
|
|
|
3
3
|
import { allTools } from '../tools/index.js';
|
|
4
4
|
import { listSkills } from '../skills/index.js';
|
|
5
5
|
import { getExternalSkillsDir } from '../skills/loader.js';
|
|
6
|
+
import { loadAllAgents } from '../agents/index.js';
|
|
6
7
|
import { listPermanentAuthorizations, DANGER_RULES, } from '../core/safeguard.js';
|
|
7
8
|
/**
|
|
8
9
|
* 斜杠命令执行器
|
|
@@ -25,6 +26,7 @@ export async function executeSlashCommand(cmdName) {
|
|
|
25
26
|
const helpText = [
|
|
26
27
|
'可用命令:',
|
|
27
28
|
' /init 初始化项目信息,生成 JARVIS.md',
|
|
29
|
+
' /about 查看 Jarvis 的详细特性、功能与信息',
|
|
28
30
|
' /new 开启新会话,重新初始化上下文',
|
|
29
31
|
' /exit 退出应用程序',
|
|
30
32
|
' /quit 退出应用程序',
|
|
@@ -59,6 +61,62 @@ export async function executeSlashCommand(cmdName) {
|
|
|
59
61
|
timestamp: Date.now(),
|
|
60
62
|
};
|
|
61
63
|
}
|
|
64
|
+
case 'about': {
|
|
65
|
+
const skills = listSkills();
|
|
66
|
+
const agents = Array.from(loadAllAgents().values());
|
|
67
|
+
const parts = [
|
|
68
|
+
'Jarvis 详细信息',
|
|
69
|
+
'',
|
|
70
|
+
`- 版本:${APP_VERSION}`,
|
|
71
|
+
'- 定位:运行在终端里的轻量级 AI Agent,面向代码、命令行与多步骤任务协作',
|
|
72
|
+
'- 技术栈:React + TypeScript + Ink',
|
|
73
|
+
'',
|
|
74
|
+
'核心能力:',
|
|
75
|
+
'- Agentic Loop:可连续推理、调用工具、读取结果并继续决策',
|
|
76
|
+
'- 流式终端交互:在终端里实时展示回复、状态与工具执行过程',
|
|
77
|
+
'- 文件与目录操作:支持读写文件、列目录、搜索内容、语义检索',
|
|
78
|
+
'- 命令执行:通过 Bash 工具执行命令,并统一经过安全围栏',
|
|
79
|
+
'- 多智能体协作:支持同步子 Agent、后台子 Agent 与消息总线通信',
|
|
80
|
+
'- 外部扩展:支持从 ~/.jarvis/skills/ 动态加载外部 Skill',
|
|
81
|
+
'- 会话与记忆:支持会话持久化、摘要、Token 统计与长期记忆',
|
|
82
|
+
'',
|
|
83
|
+
'当前实例概况:',
|
|
84
|
+
`- 内置工具:${allTools.length} 个`,
|
|
85
|
+
`- 外部 Skills:${skills.length} 个`,
|
|
86
|
+
`- 可切换 Agents:${agents.length} 个`,
|
|
87
|
+
'',
|
|
88
|
+
'内置工具:',
|
|
89
|
+
...allTools.map((tool, index) => ` ${index + 1}. ${tool.name} - ${tool.description.split('\n')[0]}`),
|
|
90
|
+
'',
|
|
91
|
+
'可切换 Agents:',
|
|
92
|
+
...agents.map((agent, index) => ` ${index + 1}. ${agent.meta.name} - ${agent.meta.description}`),
|
|
93
|
+
'',
|
|
94
|
+
'安全与交互:',
|
|
95
|
+
'- 危险命令会被安全围栏识别,并支持交互式确认与持久化授权',
|
|
96
|
+
'- 支持斜杠菜单、历史会话恢复、会话回退与快捷键操作',
|
|
97
|
+
'- 支持多个只读工具并行执行,提高响应效率',
|
|
98
|
+
'',
|
|
99
|
+
'常用入口:',
|
|
100
|
+
'- /help:查看所有斜杠命令',
|
|
101
|
+
'- /skills:查看当前 tools 和 skills',
|
|
102
|
+
'- /permissions:查看持久化授权列表',
|
|
103
|
+
'- /version:查看当前版本号',
|
|
104
|
+
];
|
|
105
|
+
if (skills.length > 0) {
|
|
106
|
+
parts.push('');
|
|
107
|
+
parts.push('已加载 Skills:');
|
|
108
|
+
skills.forEach((skill, index) => {
|
|
109
|
+
parts.push(` ${index + 1}. ${skill.meta.name} - ${skill.meta.description}`);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
id: `about-${Date.now()}`,
|
|
114
|
+
type: 'system',
|
|
115
|
+
status: 'success',
|
|
116
|
+
content: parts.join('\n'),
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
62
120
|
case 'permissions': {
|
|
63
121
|
const perms = listPermanentAuthorizations();
|
|
64
122
|
const lines = ['持久化授权列表 (~/.jarvis/.permissions.json)', ''];
|