@yeaft/webchat-agent 0.1.246 → 0.1.248
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/connection/message-router.js +4 -1
- package/conversation.js +52 -0
- package/crew/session.js +9 -0
- package/index.js +13 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
createConversation, resumeConversation, deleteConversation,
|
|
13
13
|
handleRefreshConversation, handleCancelExecution,
|
|
14
14
|
handleUserInput, handleUpdateConversationSettings, handleAskUserAnswer,
|
|
15
|
-
sendConversationList, handleBtwQuestion
|
|
15
|
+
sendConversationList, handleBtwQuestion, preloadSlashCommands
|
|
16
16
|
} from '../conversation.js';
|
|
17
17
|
import {
|
|
18
18
|
createCrewSession, handleCrewHumanInput, handleCrewControl,
|
|
@@ -65,6 +65,9 @@ export async function handleMessage(msg) {
|
|
|
65
65
|
if (ctx.mcpServers.length > 0) {
|
|
66
66
|
sendToServer({ type: 'mcp_servers_list', servers: ctx.mcpServers });
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
// ★ Preload slash commands for immediate skill availability in new sessions
|
|
70
|
+
preloadSlashCommands().catch(() => {});
|
|
68
71
|
break;
|
|
69
72
|
|
|
70
73
|
case 'create_conversation':
|
package/conversation.js
CHANGED
|
@@ -21,6 +21,58 @@ function prestartClaude(conversationId, workDir, resumeSessionId) {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Preload slash commands from Claude CLI (fire-and-forget).
|
|
26
|
+
* Spawns a minimal CLI process with a cheap built-in command (/cost)
|
|
27
|
+
* to trigger the system init message, captures slash_commands,
|
|
28
|
+
* then aborts immediately (no API tokens consumed).
|
|
29
|
+
*
|
|
30
|
+
* @param {string} [workDir] - Project directory (default: agent workDir)
|
|
31
|
+
* @param {string} [targetId] - conversationId to key the update to
|
|
32
|
+
* ('__preload__' for agent-level, or crewSessionId)
|
|
33
|
+
*/
|
|
34
|
+
export async function preloadSlashCommands(workDir, targetId = '__preload__') {
|
|
35
|
+
const effectiveWorkDir = workDir || ctx.CONFIG.workDir;
|
|
36
|
+
try {
|
|
37
|
+
const abortController = new AbortController();
|
|
38
|
+
// Use --print with a cheap built-in command to trigger system init.
|
|
39
|
+
// In stream-json mode, Claude CLI won't emit init until a user message arrives,
|
|
40
|
+
// so we use string prompt mode instead which triggers init immediately.
|
|
41
|
+
const claudeQuery = query({
|
|
42
|
+
prompt: '/cost',
|
|
43
|
+
options: {
|
|
44
|
+
cwd: effectiveWorkDir,
|
|
45
|
+
permissionMode: 'bypassPermissions',
|
|
46
|
+
abort: abortController.signal,
|
|
47
|
+
maxTurns: 1
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
for await (const message of claudeQuery) {
|
|
51
|
+
if (message.type === 'system' && message.subtype === 'init') {
|
|
52
|
+
const slashCommands = message.slash_commands || [];
|
|
53
|
+
if (slashCommands.length > 0) {
|
|
54
|
+
// Update agent-level cache if this is the first load
|
|
55
|
+
if (ctx.slashCommands.length === 0) {
|
|
56
|
+
ctx.slashCommands = [...slashCommands];
|
|
57
|
+
}
|
|
58
|
+
ctx.sendToServer({
|
|
59
|
+
type: 'slash_commands_update',
|
|
60
|
+
conversationId: targetId,
|
|
61
|
+
slashCommands
|
|
62
|
+
});
|
|
63
|
+
console.log(`[Preload] ${targetId}: loaded ${slashCommands.length} slash commands from ${effectiveWorkDir}`);
|
|
64
|
+
}
|
|
65
|
+
abortController.abort();
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (err.name !== 'AbortError') {
|
|
71
|
+
console.warn(`[Preload] Failed for ${targetId}: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
24
76
|
/**
|
|
25
77
|
* 解析斜杠命令
|
|
26
78
|
* @param {string} message - 用户消息
|
package/crew/session.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
loadSessionMeta, saveSessionMeta, loadSessionMessages, getMaxShardIndex
|
|
13
13
|
} from './persistence.js';
|
|
14
14
|
import { sendCrewMessage, sendCrewOutput, sendStatusUpdate } from './ui-messages.js';
|
|
15
|
+
import { preloadSlashCommands } from '../conversation.js';
|
|
15
16
|
|
|
16
17
|
// =====================================================================
|
|
17
18
|
// Data Structures
|
|
@@ -295,6 +296,9 @@ export async function createCrewSession(msg) {
|
|
|
295
296
|
});
|
|
296
297
|
}
|
|
297
298
|
|
|
299
|
+
// ★ Preload project-level skills for crew session input autocomplete
|
|
300
|
+
preloadSlashCommands(projectDir, sessionId).catch(() => {});
|
|
301
|
+
|
|
298
302
|
return session;
|
|
299
303
|
}
|
|
300
304
|
|
|
@@ -463,6 +467,8 @@ export async function resumeCrewSession(msg) {
|
|
|
463
467
|
hasOlderMessages
|
|
464
468
|
});
|
|
465
469
|
sendStatusUpdate(session);
|
|
470
|
+
// ★ Preload project-level skills for crew session input autocomplete
|
|
471
|
+
preloadSlashCommands(session.projectDir, sessionId).catch(() => {});
|
|
466
472
|
return;
|
|
467
473
|
}
|
|
468
474
|
|
|
@@ -540,6 +546,9 @@ export async function resumeCrewSession(msg) {
|
|
|
540
546
|
await upsertCrewIndex(session);
|
|
541
547
|
await saveSessionMeta(session);
|
|
542
548
|
|
|
549
|
+
// ★ Preload project-level skills for crew session input autocomplete
|
|
550
|
+
preloadSlashCommands(session.projectDir, sessionId).catch(() => {});
|
|
551
|
+
|
|
543
552
|
console.log(`[Crew] Session ${sessionId} resumed, waiting for human input`);
|
|
544
553
|
}
|
|
545
554
|
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { platform, homedir } from 'os';
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync } from 'fs';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, chmodSync, readdirSync } from 'fs';
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
5
|
import { exec } from 'child_process';
|
|
6
6
|
import { promisify } from 'util';
|
|
@@ -217,10 +217,22 @@ async function ensureYeaftSkills() {
|
|
|
217
217
|
writeFileSync(knownFile, JSON.stringify(known, null, 2));
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
// --- Layer 2.5: Ensure hook scripts are executable (git clone may lose +x on some OS) ---
|
|
221
|
+
const fixHookPermissions = (dir) => {
|
|
222
|
+
const hooksDir = join(dir, 'hooks');
|
|
223
|
+
if (existsSync(hooksDir)) {
|
|
224
|
+
for (const f of readdirSync(hooksDir)) {
|
|
225
|
+
try { chmodSync(join(hooksDir, f), 0o755); } catch { /* ignore */ }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
fixHookPermissions(installDir);
|
|
230
|
+
|
|
220
231
|
// --- Layer 3: Copy to plugin cache (on first install or after update) ---
|
|
221
232
|
if (!existsSync(cacheDir) || needsCacheUpdate) {
|
|
222
233
|
mkdirSync(cacheDir, { recursive: true });
|
|
223
234
|
cpSync(installDir, cacheDir, { recursive: true });
|
|
235
|
+
fixHookPermissions(cacheDir);
|
|
224
236
|
console.log(`[Startup] yeaft-skills ${needsCacheUpdate ? 'updated in' : 'copied to'} plugin cache`);
|
|
225
237
|
}
|
|
226
238
|
|