@code4bug/jarvis-agent 1.3.7 → 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/config/constants.d.ts +2 -0
- package/dist/config/constants.js +13 -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 +62 -4
- package/dist/screens/slashCommands.js +58 -0
- package/dist/services/userProfile.js +2 -1
- 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' },
|
|
@@ -26,3 +26,5 @@ export declare const DEFAULT_AGENT_EMOJI = ">";
|
|
|
26
26
|
/** 当前激活的智能体名称 — 启动时从 ~/.jarvis/agent.json 读取,运行时可切换 */
|
|
27
27
|
export declare function getDefaultAgent(): string;
|
|
28
28
|
export declare function getAppName(): string;
|
|
29
|
+
/** 启动欢迎词,跟随当前激活智能体与本机用户名 */
|
|
30
|
+
export declare function getStartupWelcomeMessage(): string;
|
package/dist/config/constants.js
CHANGED
|
@@ -80,3 +80,16 @@ export function getAppName() {
|
|
|
80
80
|
return 'Jarvis';
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
/** 启动欢迎词,跟随当前激活智能体与本机用户名 */
|
|
84
|
+
export function getStartupWelcomeMessage() {
|
|
85
|
+
const agentName = getAppName();
|
|
86
|
+
const userName = os.userInfo().username || '朋友';
|
|
87
|
+
const templates = [
|
|
88
|
+
`${agentName}:欢迎你,${userName}。我已经准备好了,输入 /help 查看命令,输入 ? 查看快捷键。`,
|
|
89
|
+
`${agentName}:你好,${userName}。今天想先处理什么?输入 /help 看命令,输入 ? 看快捷键。`,
|
|
90
|
+
`${agentName}:${userName},欢迎回来。需要我继续当前工作,还是开启一个新任务?`,
|
|
91
|
+
`${agentName}:已就绪,${userName}。你可以直接描述需求,也可以先用 /help 或 ? 看可用操作。`,
|
|
92
|
+
`${agentName}:见到你了,${userName}。命令在 /help,快捷键在 ?,我们可以直接开始。`,
|
|
93
|
+
];
|
|
94
|
+
return templates[Math.floor(Math.random() * templates.length)];
|
|
95
|
+
}
|
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
|
@@ -13,7 +13,7 @@ import { useStreamThrottle } from '../hooks/useStreamThrottle.js';
|
|
|
13
13
|
import { useSlashMenu } from '../hooks/useSlashMenu.js';
|
|
14
14
|
import { executeSlashCommand } from './slashCommands.js';
|
|
15
15
|
import { QueryEngine } from '../core/QueryEngine.js';
|
|
16
|
-
import { HIDE_WELCOME_AFTER_INPUT } from '../config/constants.js';
|
|
16
|
+
import { HIDE_WELCOME_AFTER_INPUT, getStartupWelcomeMessage } from '../config/constants.js';
|
|
17
17
|
import { generateAgentHint } from '../core/hint.js';
|
|
18
18
|
import { subscribeAgentCount, getActiveAgentCount } from '../core/spawnRegistry.js';
|
|
19
19
|
import { logError, logInfo, logWarn } from '../core/logger.js';
|
|
@@ -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([]);
|
|
@@ -133,11 +147,55 @@ export default function REPL() {
|
|
|
133
147
|
logError('ui.hint.init_failed', err);
|
|
134
148
|
console.error('[hint] 初始化提示失败:', err);
|
|
135
149
|
});
|
|
150
|
+
setMessages((prev) => (prev.length > 0 ? prev : [{
|
|
151
|
+
id: `startup-welcome-${Date.now()}`,
|
|
152
|
+
type: 'system',
|
|
153
|
+
status: 'success',
|
|
154
|
+
content: getStartupWelcomeMessage(),
|
|
155
|
+
timestamp: Date.now(),
|
|
156
|
+
}]));
|
|
136
157
|
logInfo('ui.repl.mounted');
|
|
137
158
|
return () => {
|
|
138
159
|
logInfo('ui.repl.unmounted');
|
|
139
160
|
};
|
|
140
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]);
|
|
141
199
|
// 订阅后台 SubAgent 计数变化
|
|
142
200
|
useEffect(() => {
|
|
143
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)', ''];
|