ccjk 12.2.1 → 12.3.1
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/chunks/agent-teams.mjs +3 -3
- package/dist/chunks/agent.mjs +2 -2
- package/dist/chunks/agents.mjs +4 -5
- package/dist/chunks/api-cli.mjs +5 -5
- package/dist/chunks/api-providers.mjs +1 -1
- package/dist/chunks/api.mjs +4 -4
- package/dist/chunks/auto-bootstrap.mjs +1 -1
- package/dist/chunks/auto-fixer.mjs +3 -3
- package/dist/chunks/auto-init.mjs +3 -77
- package/dist/chunks/auto-updater.mjs +9 -9
- package/dist/chunks/banner.mjs +2 -2
- package/dist/chunks/boost.mjs +4 -4
- package/dist/chunks/ccjk-agents.mjs +3 -3
- package/dist/chunks/ccjk-all.mjs +6 -6
- package/dist/chunks/ccjk-config.mjs +2 -2
- package/dist/chunks/ccjk-hooks.mjs +4 -4
- package/dist/chunks/ccjk-mcp.mjs +5 -5
- package/dist/chunks/ccjk-setup.mjs +5 -5
- package/dist/chunks/ccjk-skills.mjs +5 -5
- package/dist/chunks/ccr.mjs +14 -14
- package/dist/chunks/ccu.mjs +2 -2
- package/dist/chunks/check-updates.mjs +6 -6
- package/dist/chunks/claude-code-config-manager.mjs +5 -4
- package/dist/chunks/claude-code-incremental-manager.mjs +6 -6
- package/dist/chunks/claude-config.mjs +1 -1
- package/dist/chunks/claude-wrapper.mjs +1 -1
- package/dist/chunks/cli-hook.mjs +998 -8
- package/dist/chunks/codex-config-switch.mjs +6 -6
- package/dist/chunks/codex-provider-manager.mjs +6 -6
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/codex.mjs +5 -5
- package/dist/chunks/commands.mjs +2 -2
- package/dist/chunks/commands2.mjs +1 -1
- package/dist/chunks/commit.mjs +2 -2
- package/dist/chunks/completion.mjs +2 -2
- package/dist/chunks/config-consolidator.mjs +2 -2
- package/dist/chunks/config-switch.mjs +7 -7
- package/dist/chunks/config.mjs +3 -3
- package/dist/chunks/config2.mjs +4 -4
- package/dist/chunks/config3.mjs +3 -3
- package/dist/chunks/constants.mjs +1 -1
- package/dist/chunks/context-opt.mjs +442 -0
- package/dist/chunks/convoy-manager.mjs +355 -3
- package/dist/chunks/dashboard.mjs +2 -2
- package/dist/chunks/doctor.mjs +4 -4
- package/dist/chunks/evolution.mjs +2 -2
- package/dist/chunks/health-alerts.mjs +530 -4
- package/dist/chunks/help.mjs +1 -1
- package/dist/chunks/index.mjs +0 -23
- package/dist/chunks/index10.mjs +571 -634
- package/dist/chunks/index11.mjs +569 -1061
- package/dist/chunks/index12.mjs +1076 -914
- package/dist/chunks/index13.mjs +951 -135
- package/dist/chunks/index14.mjs +184 -209
- package/dist/chunks/index15.mjs +218 -0
- package/dist/chunks/index2.mjs +24 -19
- package/dist/chunks/index3.mjs +12 -19085
- package/dist/chunks/index4.mjs +19092 -16
- package/dist/chunks/index5.mjs +16 -7602
- package/dist/chunks/index6.mjs +7590 -159
- package/dist/chunks/index7.mjs +171 -1602
- package/dist/chunks/index8.mjs +1602 -19
- package/dist/chunks/index9.mjs +15 -612
- package/dist/chunks/init.mjs +13 -13
- package/dist/chunks/installer.mjs +5 -5
- package/dist/chunks/installer2.mjs +1 -1
- package/dist/chunks/interview.mjs +4 -4
- package/dist/chunks/manager.mjs +1 -1
- package/dist/chunks/marketplace.mjs +2 -2
- package/dist/chunks/mcp-cli.mjs +9 -9
- package/dist/chunks/mcp.mjs +7 -7
- package/dist/chunks/memory.mjs +3 -3
- package/dist/chunks/menu-hierarchical.mjs +14 -14
- package/dist/chunks/menu.mjs +12 -12
- package/dist/chunks/metrics-display.mjs +1 -1
- package/dist/chunks/migrator.mjs +1 -1
- package/dist/chunks/monitor.mjs +2 -2
- package/dist/chunks/notification.mjs +4 -4
- package/dist/chunks/onboarding-wizard.mjs +2 -2
- package/dist/chunks/onboarding.mjs +4 -4
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/paradigm.mjs +1 -1
- package/dist/chunks/permission-manager.mjs +2 -2
- package/dist/chunks/permissions.mjs +3 -3
- package/dist/chunks/persistence-manager.mjs +3 -3
- package/dist/chunks/plugin.mjs +2 -2
- package/dist/chunks/prompts.mjs +5 -5
- package/dist/chunks/providers.mjs +2 -2
- package/dist/chunks/quick-actions.mjs +2 -2
- package/dist/chunks/quick-provider.mjs +6 -5
- package/dist/chunks/quick-setup.mjs +10 -10
- package/dist/chunks/remote.mjs +5 -5
- package/dist/chunks/session.mjs +2 -2
- package/dist/chunks/sessions.mjs +1 -1
- package/dist/chunks/silent-updater.mjs +1 -1
- package/dist/chunks/simple-config.mjs +1 -1
- package/dist/chunks/skill2.mjs +3 -3
- package/dist/chunks/skills-sync.mjs +4 -4
- package/dist/chunks/skills.mjs +3 -3
- package/dist/chunks/slash-commands.mjs +3 -3
- package/dist/chunks/startup.mjs +1 -1
- package/dist/chunks/stats.mjs +2 -2
- package/dist/chunks/status.mjs +2 -2
- package/dist/chunks/team.mjs +3 -3
- package/dist/chunks/thinking.mjs +4 -4
- package/dist/chunks/trace.mjs +2 -2
- package/dist/chunks/uninstall.mjs +8 -8
- package/dist/chunks/update.mjs +9 -9
- package/dist/chunks/upgrade-manager.mjs +3 -3
- package/dist/chunks/version-checker.mjs +4 -4
- package/dist/chunks/vim.mjs +3 -3
- package/dist/chunks/workflows.mjs +1 -1
- package/dist/chunks/wsl.mjs +1 -1
- package/dist/chunks/zero-config.mjs +3 -3
- package/dist/cli.mjs +56 -23
- package/dist/index.mjs +5 -5
- package/dist/shared/ccjk.B1TwPltj.mjs +78 -0
- package/dist/shared/{ccjk.CePkJq2S.mjs → ccjk.BfIpomdz.mjs} +1 -1
- package/dist/shared/{ccjk.D8ZLYSZZ.mjs → ccjk.CXzjn01x.mjs} +1 -1
- package/dist/shared/{ccjk.Cjj8SVrn.mjs → ccjk.Cot9p9_n.mjs} +1 -1
- package/dist/shared/{ccjk.CvChMYvB.mjs → ccjk.DCw2WnZU.mjs} +1 -1
- package/dist/shared/{ccjk.DG_o24cZ.mjs → ccjk.DJdmgr2d.mjs} +1 -1
- package/dist/shared/{ccjk.BIxuVL3_.mjs → ccjk.DcKLglJQ.mjs} +2 -2
- package/dist/shared/{ccjk.DLLw-h4Y.mjs → ccjk.DfXjf8EC.mjs} +2 -2
- package/dist/shared/{ccjk.KpFl2RDA.mjs → ccjk.DpstNaeR.mjs} +3 -3
- package/dist/shared/{ccjk.DOBWBkFR.mjs → ccjk.XsJWJuQP.mjs} +5 -5
- package/dist/shared/{ccjk._dESH4Rk.mjs → ccjk.dYDLfmph.mjs} +1 -1
- package/dist/shared/{ccjk.DS7UESmF.mjs → ccjk.hrRv8G6j.mjs} +4 -4
- package/dist/shared/{ccjk.BWFpnOr3.mjs → ccjk.mJpVRDZ8.mjs} +1 -1
- package/dist/templates/claude-code/common/settings.json +3 -1
- package/package.json +20 -18
- package/templates/claude-code/common/settings.json +3 -1
- package/dist/chunks/context-loader.mjs +0 -351
- package/dist/chunks/context.mjs +0 -372
- package/dist/chunks/health-check.mjs +0 -532
package/dist/chunks/cli-hook.mjs
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { a as getGlobalStateManager, g as getGlobalConvoyManager, S as SessionIntelligence } from './convoy-manager.mjs';
|
|
2
|
+
import { a as getGlobalStateManager, g as getGlobalConvoyManager, c as contextLoader, S as SessionIntelligence } from './convoy-manager.mjs';
|
|
3
|
+
import { l as logger } from '../shared/ccjk.DJdmgr2d.mjs';
|
|
4
|
+
import { h as hookRegistry } from '../shared/ccjk.B1TwPltj.mjs';
|
|
5
|
+
import { Buffer } from 'node:buffer';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { d as dirname, j as join } from '../shared/ccjk.bQ7Dh1g4.mjs';
|
|
3
9
|
import { n as nanoid } from '../shared/ccjk.BoApaI4j.mjs';
|
|
4
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
-
import { j as join } from '../shared/ccjk.bQ7Dh1g4.mjs';
|
|
6
10
|
import process__default from 'node:process';
|
|
11
|
+
import 'tinyglobby';
|
|
12
|
+
import '../shared/ccjk.BfIpomdz.mjs';
|
|
13
|
+
import './index3.mjs';
|
|
14
|
+
import '../shared/ccjk.BAGoDD49.mjs';
|
|
15
|
+
import '../shared/ccjk.BxSmJ8B7.mjs';
|
|
7
16
|
import 'node:child_process';
|
|
8
17
|
import 'node:fs/promises';
|
|
9
18
|
import 'node:os';
|
|
@@ -12,7 +21,918 @@ import './main.mjs';
|
|
|
12
21
|
import 'module';
|
|
13
22
|
import 'node:stream';
|
|
14
23
|
import 'node:readline';
|
|
15
|
-
|
|
24
|
+
|
|
25
|
+
async function emitCommandHookEvent(event, data, sessionId) {
|
|
26
|
+
try {
|
|
27
|
+
const context = {
|
|
28
|
+
event,
|
|
29
|
+
sessionId,
|
|
30
|
+
data,
|
|
31
|
+
timestamp: Date.now(),
|
|
32
|
+
metadata: {
|
|
33
|
+
source: "brain-router"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
await hookRegistry.execute(context);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.debug(`Command hook bridge skipped for "${event}": ${String(error)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const DEFAULT_CONFIG = {
|
|
43
|
+
enablePersistence: false,
|
|
44
|
+
maxHistorySize: 1e3,
|
|
45
|
+
messageRetentionTime: 24 * 60 * 60 * 1e3,
|
|
46
|
+
// 24 hours
|
|
47
|
+
enableLogging: true,
|
|
48
|
+
logLevel: "info",
|
|
49
|
+
enableValidation: true,
|
|
50
|
+
maxMessageSize: 1024 * 1024,
|
|
51
|
+
// 1MB
|
|
52
|
+
enableDeadLetterQueue: true
|
|
53
|
+
};
|
|
54
|
+
class FileMessageStorage {
|
|
55
|
+
filePath;
|
|
56
|
+
constructor(filePath) {
|
|
57
|
+
this.filePath = filePath;
|
|
58
|
+
this.ensureDirectory();
|
|
59
|
+
}
|
|
60
|
+
ensureDirectory() {
|
|
61
|
+
const dir = dirname(this.filePath);
|
|
62
|
+
if (!existsSync(dir)) {
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async save(message) {
|
|
67
|
+
try {
|
|
68
|
+
const messages = await this.load();
|
|
69
|
+
messages.push(message);
|
|
70
|
+
writeFileSync(this.filePath, JSON.stringify(messages, null, 2), "utf-8");
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error("Failed to save message:", error);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async load(filter) {
|
|
77
|
+
try {
|
|
78
|
+
if (!existsSync(this.filePath)) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const content = readFileSync(this.filePath, "utf-8");
|
|
82
|
+
const messages = JSON.parse(content);
|
|
83
|
+
if (filter) {
|
|
84
|
+
return messages.filter(filter);
|
|
85
|
+
}
|
|
86
|
+
return messages;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error("Failed to load messages:", error);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async delete(messageId) {
|
|
93
|
+
try {
|
|
94
|
+
const messages = await this.load();
|
|
95
|
+
const filtered = messages.filter((msg) => msg.id !== messageId);
|
|
96
|
+
writeFileSync(this.filePath, JSON.stringify(filtered, null, 2), "utf-8");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("Failed to delete message:", error);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async clear() {
|
|
103
|
+
try {
|
|
104
|
+
writeFileSync(this.filePath, JSON.stringify([], null, 2), "utf-8");
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error("Failed to clear messages:", error);
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async getStats() {
|
|
111
|
+
try {
|
|
112
|
+
if (!existsSync(this.filePath)) {
|
|
113
|
+
return { count: 0, size: 0 };
|
|
114
|
+
}
|
|
115
|
+
const messages = await this.load();
|
|
116
|
+
const content = readFileSync(this.filePath, "utf-8");
|
|
117
|
+
return {
|
|
118
|
+
count: messages.length,
|
|
119
|
+
size: Buffer.byteLength(content, "utf-8")
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error("Failed to get storage stats:", error);
|
|
123
|
+
return { count: 0, size: 0 };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
class MessageBus {
|
|
128
|
+
config;
|
|
129
|
+
subscriptions;
|
|
130
|
+
messageHistory;
|
|
131
|
+
deadLetterQueue;
|
|
132
|
+
storage;
|
|
133
|
+
stats;
|
|
134
|
+
constructor(config = {}) {
|
|
135
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
136
|
+
this.subscriptions = /* @__PURE__ */ new Map();
|
|
137
|
+
this.messageHistory = [];
|
|
138
|
+
this.deadLetterQueue = [];
|
|
139
|
+
if (this.config.enablePersistence && this.config.persistencePath) {
|
|
140
|
+
this.storage = new FileMessageStorage(this.config.persistencePath);
|
|
141
|
+
this.loadPersistedMessages();
|
|
142
|
+
}
|
|
143
|
+
this.stats = {
|
|
144
|
+
totalMessages: 0,
|
|
145
|
+
messagesByType: {},
|
|
146
|
+
messagesByStatus: {},
|
|
147
|
+
activeSubscriptions: 0,
|
|
148
|
+
historySize: 0,
|
|
149
|
+
deadLetterQueueSize: 0,
|
|
150
|
+
avgProcessingTime: 0
|
|
151
|
+
};
|
|
152
|
+
this.startCleanupInterval();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Publish a message to the bus
|
|
156
|
+
*/
|
|
157
|
+
async publish(type, from, to, subject, payload, options = {}) {
|
|
158
|
+
const message = {
|
|
159
|
+
id: randomUUID(),
|
|
160
|
+
type,
|
|
161
|
+
from,
|
|
162
|
+
to,
|
|
163
|
+
subject,
|
|
164
|
+
payload,
|
|
165
|
+
priority: options.priority || "normal",
|
|
166
|
+
status: "pending",
|
|
167
|
+
timestamp: Date.now(),
|
|
168
|
+
correlationId: options.correlationId,
|
|
169
|
+
replyTo: options.replyTo,
|
|
170
|
+
metadata: options.metadata
|
|
171
|
+
};
|
|
172
|
+
if (this.config.enableValidation) {
|
|
173
|
+
this.validateMessage(message);
|
|
174
|
+
}
|
|
175
|
+
this.logMessage("publish", message);
|
|
176
|
+
this.updateStats(message);
|
|
177
|
+
this.addToHistory(message);
|
|
178
|
+
if (this.storage) {
|
|
179
|
+
await this.storage.save(message);
|
|
180
|
+
}
|
|
181
|
+
await this.routeMessage(message);
|
|
182
|
+
return message.id;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Subscribe to messages
|
|
186
|
+
*/
|
|
187
|
+
subscribe(subscriber, handler, options = {}) {
|
|
188
|
+
const subscription = {
|
|
189
|
+
id: randomUUID(),
|
|
190
|
+
subscriber,
|
|
191
|
+
options,
|
|
192
|
+
handler,
|
|
193
|
+
createdAt: Date.now(),
|
|
194
|
+
unsubscribe: () => this.unsubscribe(subscription.id)
|
|
195
|
+
};
|
|
196
|
+
this.subscriptions.set(subscription.id, subscription);
|
|
197
|
+
this.stats.activeSubscriptions = this.subscriptions.size;
|
|
198
|
+
this.log("info", `Agent ${subscriber} subscribed with ID ${subscription.id}`);
|
|
199
|
+
return subscription;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Unsubscribe from messages
|
|
203
|
+
*/
|
|
204
|
+
unsubscribe(subscriptionId) {
|
|
205
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
206
|
+
if (subscription) {
|
|
207
|
+
this.subscriptions.delete(subscriptionId);
|
|
208
|
+
this.stats.activeSubscriptions = this.subscriptions.size;
|
|
209
|
+
this.log("info", `Subscription ${subscriptionId} removed`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get message by ID
|
|
214
|
+
*/
|
|
215
|
+
getMessage(messageId) {
|
|
216
|
+
return this.messageHistory.find((msg) => msg.id === messageId);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get messages by filter
|
|
220
|
+
*/
|
|
221
|
+
getMessages(filter) {
|
|
222
|
+
return this.messageHistory.filter(filter);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Update message status
|
|
226
|
+
*/
|
|
227
|
+
async updateMessageStatus(messageId, status, error) {
|
|
228
|
+
const message = this.getMessage(messageId);
|
|
229
|
+
if (message) {
|
|
230
|
+
message.status = status;
|
|
231
|
+
if (error) {
|
|
232
|
+
message.error = error;
|
|
233
|
+
}
|
|
234
|
+
if (status === "failed" && this.config.enableDeadLetterQueue) {
|
|
235
|
+
this.deadLetterQueue.push(message);
|
|
236
|
+
this.stats.deadLetterQueueSize = this.deadLetterQueue.length;
|
|
237
|
+
}
|
|
238
|
+
this.stats.messagesByStatus[status] = (this.stats.messagesByStatus[status] || 0) + 1;
|
|
239
|
+
if (this.storage) {
|
|
240
|
+
await this.storage.save(message);
|
|
241
|
+
}
|
|
242
|
+
this.log("debug", `Message ${messageId} status updated to ${status}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get message bus statistics
|
|
247
|
+
*/
|
|
248
|
+
getStats() {
|
|
249
|
+
return { ...this.stats };
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Clear message history
|
|
253
|
+
*/
|
|
254
|
+
async clearHistory() {
|
|
255
|
+
this.messageHistory = [];
|
|
256
|
+
this.stats.historySize = 0;
|
|
257
|
+
if (this.storage) {
|
|
258
|
+
await this.storage.clear();
|
|
259
|
+
}
|
|
260
|
+
this.log("info", "Message history cleared");
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Clear dead letter queue
|
|
264
|
+
*/
|
|
265
|
+
clearDeadLetterQueue() {
|
|
266
|
+
this.deadLetterQueue = [];
|
|
267
|
+
this.stats.deadLetterQueueSize = 0;
|
|
268
|
+
this.log("info", "Dead letter queue cleared");
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Get dead letter queue messages
|
|
272
|
+
*/
|
|
273
|
+
getDeadLetterQueue() {
|
|
274
|
+
return [...this.deadLetterQueue];
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Shutdown message bus
|
|
278
|
+
*/
|
|
279
|
+
async shutdown() {
|
|
280
|
+
this.log("info", "Shutting down message bus");
|
|
281
|
+
this.subscriptions.clear();
|
|
282
|
+
this.stats.activeSubscriptions = 0;
|
|
283
|
+
if (this.storage && this.messageHistory.length > 0) {
|
|
284
|
+
for (const message of this.messageHistory) {
|
|
285
|
+
await this.storage.save(message);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
this.log("info", "Message bus shutdown complete");
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Route message to subscribers
|
|
292
|
+
*/
|
|
293
|
+
async routeMessage(message) {
|
|
294
|
+
const startTime = Date.now();
|
|
295
|
+
const matchingSubscriptions = this.findMatchingSubscriptions(message);
|
|
296
|
+
this.log("debug", `Routing message ${message.id} to ${matchingSubscriptions.length} subscribers`);
|
|
297
|
+
for (const subscription of matchingSubscriptions) {
|
|
298
|
+
try {
|
|
299
|
+
message.status = "processing";
|
|
300
|
+
if (subscription.options.async) {
|
|
301
|
+
await subscription.handler(message);
|
|
302
|
+
} else {
|
|
303
|
+
subscription.handler(message);
|
|
304
|
+
}
|
|
305
|
+
message.status = "completed";
|
|
306
|
+
} catch (error) {
|
|
307
|
+
this.log("error", `Error handling message ${message.id} in subscription ${subscription.id}:`, error);
|
|
308
|
+
await this.updateMessageStatus(message.id, "failed", {
|
|
309
|
+
code: "HANDLER_ERROR",
|
|
310
|
+
message: error instanceof Error ? error.message : String(error),
|
|
311
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const processingTime = Date.now() - startTime;
|
|
316
|
+
this.stats.avgProcessingTime = (this.stats.avgProcessingTime + processingTime) / 2;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Find subscriptions matching the message
|
|
320
|
+
*/
|
|
321
|
+
findMatchingSubscriptions(message) {
|
|
322
|
+
const matching = [];
|
|
323
|
+
const subscriptions = Array.from(this.subscriptions.values());
|
|
324
|
+
for (const subscription of subscriptions) {
|
|
325
|
+
const isAddressed = message.to === "all" || message.to === subscription.subscriber || Array.isArray(message.to) && message.to.includes(subscription.subscriber);
|
|
326
|
+
if (!isAddressed) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (!this.matchesSubscriptionFilters(message, subscription.options)) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
matching.push(subscription);
|
|
333
|
+
}
|
|
334
|
+
return matching;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Check if message matches subscription filters
|
|
338
|
+
*/
|
|
339
|
+
matchesSubscriptionFilters(message, options) {
|
|
340
|
+
if (options.type) {
|
|
341
|
+
const types = Array.isArray(options.type) ? options.type : [options.type];
|
|
342
|
+
if (!types.includes(message.type)) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (options.from) {
|
|
347
|
+
const senders = Array.isArray(options.from) ? options.from : [options.from];
|
|
348
|
+
if (!senders.includes(message.from)) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (options.priority) {
|
|
353
|
+
const priorities = Array.isArray(options.priority) ? options.priority : [options.priority];
|
|
354
|
+
if (!priorities.includes(message.priority)) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (options.filter && !options.filter(message)) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Validate message
|
|
365
|
+
*/
|
|
366
|
+
validateMessage(message) {
|
|
367
|
+
if (!message.id) {
|
|
368
|
+
throw new Error("Message ID is required");
|
|
369
|
+
}
|
|
370
|
+
if (!message.type) {
|
|
371
|
+
throw new Error("Message type is required");
|
|
372
|
+
}
|
|
373
|
+
if (!message.from) {
|
|
374
|
+
throw new Error("Message sender is required");
|
|
375
|
+
}
|
|
376
|
+
if (!message.to) {
|
|
377
|
+
throw new Error("Message recipient is required");
|
|
378
|
+
}
|
|
379
|
+
if (!message.subject) {
|
|
380
|
+
throw new Error("Message subject is required");
|
|
381
|
+
}
|
|
382
|
+
if (this.config.maxMessageSize) {
|
|
383
|
+
const messageSize = Buffer.byteLength(JSON.stringify(message), "utf-8");
|
|
384
|
+
if (messageSize > this.config.maxMessageSize) {
|
|
385
|
+
throw new Error(`Message size ${messageSize} exceeds maximum ${this.config.maxMessageSize}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Add message to history
|
|
391
|
+
*/
|
|
392
|
+
addToHistory(message) {
|
|
393
|
+
this.messageHistory.push(message);
|
|
394
|
+
this.stats.historySize = this.messageHistory.length;
|
|
395
|
+
const maxHistorySize = this.config.maxHistorySize ?? 1e3;
|
|
396
|
+
if (this.messageHistory.length > maxHistorySize) {
|
|
397
|
+
this.messageHistory.shift();
|
|
398
|
+
this.stats.historySize = this.messageHistory.length;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Update statistics
|
|
403
|
+
*/
|
|
404
|
+
updateStats(message) {
|
|
405
|
+
this.stats.totalMessages++;
|
|
406
|
+
this.stats.messagesByType[message.type] = (this.stats.messagesByType[message.type] || 0) + 1;
|
|
407
|
+
this.stats.messagesByStatus[message.status] = (this.stats.messagesByStatus[message.status] || 0) + 1;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Load persisted messages
|
|
411
|
+
*/
|
|
412
|
+
async loadPersistedMessages() {
|
|
413
|
+
if (!this.storage) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
const messages = await this.storage.load();
|
|
418
|
+
this.messageHistory = messages;
|
|
419
|
+
this.stats.historySize = messages.length;
|
|
420
|
+
this.log("info", `Loaded ${messages.length} persisted messages`);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
this.log("error", "Failed to load persisted messages:", error);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Start cleanup interval for old messages
|
|
427
|
+
*/
|
|
428
|
+
startCleanupInterval() {
|
|
429
|
+
setInterval(() => {
|
|
430
|
+
const now = Date.now();
|
|
431
|
+
const retentionTime = this.config.messageRetentionTime ?? 864e5;
|
|
432
|
+
this.messageHistory = this.messageHistory.filter(
|
|
433
|
+
(msg) => now - msg.timestamp < retentionTime
|
|
434
|
+
);
|
|
435
|
+
this.stats.historySize = this.messageHistory.length;
|
|
436
|
+
this.deadLetterQueue = this.deadLetterQueue.filter(
|
|
437
|
+
(msg) => now - msg.timestamp < retentionTime
|
|
438
|
+
);
|
|
439
|
+
this.stats.deadLetterQueueSize = this.deadLetterQueue.length;
|
|
440
|
+
this.log("debug", "Cleanup completed");
|
|
441
|
+
}, 60 * 60 * 1e3);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Log message
|
|
445
|
+
*/
|
|
446
|
+
logMessage(action, message) {
|
|
447
|
+
if (!this.config.enableLogging) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const logLevel = this.config.logLevel;
|
|
451
|
+
const shouldLog = logLevel === "debug" || logLevel === "info" && ["publish", "subscribe"].includes(action) || logLevel === "warn" && message.priority === "high" || logLevel === "error" && message.status === "failed";
|
|
452
|
+
if (shouldLog) {
|
|
453
|
+
this.log("debug", `[${action}] ${message.type} from ${message.from} to ${message.to}: ${message.subject}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Internal logging
|
|
458
|
+
*/
|
|
459
|
+
log(level, message, ...args) {
|
|
460
|
+
if (!this.config.enableLogging) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
464
|
+
const configLevel = levels.indexOf(this.config.logLevel ?? "info");
|
|
465
|
+
const messageLevel = levels.indexOf(level);
|
|
466
|
+
if (messageLevel >= configLevel) {
|
|
467
|
+
console[level](`[MessageBus] ${message}`, ...args);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
let globalMessageBus = null;
|
|
472
|
+
function getMessageBus(config) {
|
|
473
|
+
if (!globalMessageBus) {
|
|
474
|
+
globalMessageBus = new MessageBus(config);
|
|
475
|
+
}
|
|
476
|
+
return globalMessageBus;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
class SkillRegistry extends EventEmitter {
|
|
480
|
+
skills;
|
|
481
|
+
filePathIndex;
|
|
482
|
+
triggerIndex;
|
|
483
|
+
messageBus = getMessageBus();
|
|
484
|
+
constructor() {
|
|
485
|
+
super();
|
|
486
|
+
this.skills = /* @__PURE__ */ new Map();
|
|
487
|
+
this.filePathIndex = /* @__PURE__ */ new Map();
|
|
488
|
+
this.triggerIndex = /* @__PURE__ */ new Map();
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Register a skill
|
|
492
|
+
*
|
|
493
|
+
* @param skill - Parsed skill file
|
|
494
|
+
* @param source - Skill source type
|
|
495
|
+
* @returns Registered entry
|
|
496
|
+
*/
|
|
497
|
+
register(skill, source = "user") {
|
|
498
|
+
const id = skill.metadata.name;
|
|
499
|
+
const existing = this.skills.get(id);
|
|
500
|
+
const dependencies = this.extractDependencies(skill.metadata);
|
|
501
|
+
const entry = {
|
|
502
|
+
id,
|
|
503
|
+
metadata: skill.metadata,
|
|
504
|
+
content: skill.content,
|
|
505
|
+
filePath: skill.filePath,
|
|
506
|
+
enabled: existing?.enabled ?? true,
|
|
507
|
+
source,
|
|
508
|
+
registeredAt: existing?.registeredAt ?? Date.now(),
|
|
509
|
+
modifiedAt: skill.modifiedAt?.getTime() ?? Date.now(),
|
|
510
|
+
estimatedTokens: this.estimateTokens(skill),
|
|
511
|
+
dependencies,
|
|
512
|
+
dependents: existing?.dependents || /* @__PURE__ */ new Set()
|
|
513
|
+
};
|
|
514
|
+
this.skills.set(id, entry);
|
|
515
|
+
this.filePathIndex.set(skill.filePath, id);
|
|
516
|
+
this.updateTriggerIndex(id, entry.metadata.triggers);
|
|
517
|
+
this.updateDependencyGraph(id, dependencies);
|
|
518
|
+
if (existing) {
|
|
519
|
+
this.emit("skill:updated", existing, entry);
|
|
520
|
+
this.publishMessage("skill:updated", { oldSkill: existing, newSkill: entry });
|
|
521
|
+
} else {
|
|
522
|
+
this.emit("skill:registered", entry);
|
|
523
|
+
this.publishMessage("skill:registered", entry);
|
|
524
|
+
}
|
|
525
|
+
return entry;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Unregister a skill by ID
|
|
529
|
+
*
|
|
530
|
+
* @param id - Skill ID
|
|
531
|
+
* @returns True if skill was unregistered
|
|
532
|
+
*/
|
|
533
|
+
unregister(id) {
|
|
534
|
+
const entry = this.skills.get(id);
|
|
535
|
+
if (!entry)
|
|
536
|
+
return false;
|
|
537
|
+
if (entry.dependents.size > 0) {
|
|
538
|
+
const dependentList = Array.from(entry.dependents).join(", ");
|
|
539
|
+
throw new Error(`Cannot unregister skill "${id}": depended upon by: ${dependentList}`);
|
|
540
|
+
}
|
|
541
|
+
this.skills.delete(id);
|
|
542
|
+
this.filePathIndex.delete(entry.filePath);
|
|
543
|
+
for (const trigger of entry.metadata.triggers) {
|
|
544
|
+
const skills = this.triggerIndex.get(trigger);
|
|
545
|
+
if (skills) {
|
|
546
|
+
skills.delete(id);
|
|
547
|
+
if (skills.size === 0)
|
|
548
|
+
this.triggerIndex.delete(trigger);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
for (const dep of entry.dependencies) {
|
|
552
|
+
const depEntry = this.skills.get(dep);
|
|
553
|
+
if (depEntry)
|
|
554
|
+
depEntry.dependents.delete(id);
|
|
555
|
+
}
|
|
556
|
+
this.emit("skill:unregistered", entry);
|
|
557
|
+
this.publishMessage("skill:unregistered", entry);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Unregister a skill by file path
|
|
562
|
+
*
|
|
563
|
+
* @param filePath - File path
|
|
564
|
+
* @returns True if skill was unregistered
|
|
565
|
+
*/
|
|
566
|
+
unregisterByPath(filePath) {
|
|
567
|
+
const id = this.filePathIndex.get(filePath);
|
|
568
|
+
return id ? this.unregister(id) : false;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Get a skill by ID
|
|
572
|
+
*
|
|
573
|
+
* @param id - Skill ID
|
|
574
|
+
* @returns Skill entry or undefined
|
|
575
|
+
*/
|
|
576
|
+
getById(id) {
|
|
577
|
+
return this.skills.get(id);
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get a skill by file path
|
|
581
|
+
*
|
|
582
|
+
* @param filePath - File path
|
|
583
|
+
* @returns Skill entry or undefined
|
|
584
|
+
*/
|
|
585
|
+
getByPath(filePath) {
|
|
586
|
+
const id = this.filePathIndex.get(filePath);
|
|
587
|
+
return id ? this.skills.get(id) : void 0;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get skills by trigger
|
|
591
|
+
*
|
|
592
|
+
* @param trigger - Trigger string (e.g., '/commit')
|
|
593
|
+
* @returns Array of matching skills
|
|
594
|
+
*/
|
|
595
|
+
getByTrigger(trigger) {
|
|
596
|
+
const ids = this.triggerIndex.get(trigger);
|
|
597
|
+
if (!ids)
|
|
598
|
+
return [];
|
|
599
|
+
return Array.from(ids).map((id) => this.skills.get(id)).filter((e) => e !== void 0 && e.enabled);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Lookup skills with filters
|
|
603
|
+
*
|
|
604
|
+
* @param options - Lookup options
|
|
605
|
+
* @returns Array of matching skills
|
|
606
|
+
*/
|
|
607
|
+
lookup(options = {}) {
|
|
608
|
+
let results = Array.from(this.skills.values());
|
|
609
|
+
if (options.enabled !== void 0) {
|
|
610
|
+
results = results.filter((e) => e.enabled === options.enabled);
|
|
611
|
+
}
|
|
612
|
+
if (options.category) {
|
|
613
|
+
results = results.filter((e) => e.metadata.category === options.category);
|
|
614
|
+
}
|
|
615
|
+
if (options.source) {
|
|
616
|
+
results = results.filter((e) => e.source === options.source);
|
|
617
|
+
}
|
|
618
|
+
if (options.userInvocable !== void 0) {
|
|
619
|
+
results = results.filter(
|
|
620
|
+
(e) => (e.metadata.user_invocable ?? true) === options.userInvocable
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
if (options.autoActivate !== void 0) {
|
|
624
|
+
results = results.filter(
|
|
625
|
+
(e) => (e.metadata.auto_activate ?? false) === options.autoActivate
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (options.agent) {
|
|
629
|
+
results = results.filter(
|
|
630
|
+
(e) => e.metadata.agent === options.agent || e.metadata.agents?.includes(options.agent)
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
if (options.search) {
|
|
634
|
+
const query = options.search.toLowerCase();
|
|
635
|
+
results = results.filter(
|
|
636
|
+
(e) => e.id.toLowerCase().includes(query) || e.metadata.description.toLowerCase().includes(query) || e.metadata.tags?.some((t) => t.toLowerCase().includes(query))
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
if (options.sortBy) {
|
|
640
|
+
const dir = options.sortDir === "desc" ? -1 : 1;
|
|
641
|
+
results.sort((a, b) => {
|
|
642
|
+
switch (options.sortBy) {
|
|
643
|
+
case "name":
|
|
644
|
+
return a.id.localeCompare(b.id) * dir;
|
|
645
|
+
case "priority":
|
|
646
|
+
return ((a.metadata.priority ?? 5) - (b.metadata.priority ?? 5)) * dir;
|
|
647
|
+
case "registeredAt":
|
|
648
|
+
return (a.registeredAt - b.registeredAt) * dir;
|
|
649
|
+
case "modifiedAt":
|
|
650
|
+
return (a.modifiedAt - b.modifiedAt) * dir;
|
|
651
|
+
default:
|
|
652
|
+
return 0;
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
if (options.limit) {
|
|
657
|
+
results = results.slice(0, options.limit);
|
|
658
|
+
}
|
|
659
|
+
return results;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Enable a skill
|
|
663
|
+
*
|
|
664
|
+
* @param id - Skill ID
|
|
665
|
+
* @returns True if enabled
|
|
666
|
+
*/
|
|
667
|
+
enable(id) {
|
|
668
|
+
const entry = this.skills.get(id);
|
|
669
|
+
if (!entry || entry.enabled)
|
|
670
|
+
return false;
|
|
671
|
+
entry.enabled = true;
|
|
672
|
+
this.emit("skill:enabled", entry);
|
|
673
|
+
this.publishMessage("skill:enabled", entry);
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Disable a skill
|
|
678
|
+
*
|
|
679
|
+
* @param id - Skill ID
|
|
680
|
+
* @returns True if disabled
|
|
681
|
+
*/
|
|
682
|
+
disable(id) {
|
|
683
|
+
const entry = this.skills.get(id);
|
|
684
|
+
if (!entry || !entry.enabled)
|
|
685
|
+
return false;
|
|
686
|
+
entry.enabled = false;
|
|
687
|
+
this.emit("skill:disabled", entry);
|
|
688
|
+
this.publishMessage("skill:disabled", entry);
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Toggle skill enabled state
|
|
693
|
+
*
|
|
694
|
+
* @param id - Skill ID
|
|
695
|
+
* @returns New enabled state
|
|
696
|
+
*/
|
|
697
|
+
toggle(id) {
|
|
698
|
+
const entry = this.skills.get(id);
|
|
699
|
+
if (!entry)
|
|
700
|
+
return false;
|
|
701
|
+
entry.enabled = !entry.enabled;
|
|
702
|
+
const event = entry.enabled ? "skill:enabled" : "skill:disabled";
|
|
703
|
+
this.emit(event, entry);
|
|
704
|
+
this.publishMessage(event, entry);
|
|
705
|
+
return entry.enabled;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Check if a skill exists
|
|
709
|
+
*
|
|
710
|
+
* @param id - Skill ID
|
|
711
|
+
* @returns True if skill exists
|
|
712
|
+
*/
|
|
713
|
+
has(id) {
|
|
714
|
+
return this.skills.has(id);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Check if a skill is enabled
|
|
718
|
+
*
|
|
719
|
+
* @param id - Skill ID
|
|
720
|
+
* @returns True if enabled, false if disabled or not found
|
|
721
|
+
*/
|
|
722
|
+
isEnabled(id) {
|
|
723
|
+
return this.skills.get(id)?.enabled ?? false;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Get all skill IDs
|
|
727
|
+
*
|
|
728
|
+
* @returns Array of skill IDs
|
|
729
|
+
*/
|
|
730
|
+
getIds() {
|
|
731
|
+
return Array.from(this.skills.keys());
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Get all entries
|
|
735
|
+
*
|
|
736
|
+
* @returns Array of all entries
|
|
737
|
+
*/
|
|
738
|
+
getAll() {
|
|
739
|
+
return Array.from(this.skills.values());
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Get enabled entries
|
|
743
|
+
*
|
|
744
|
+
* @returns Array of enabled entries
|
|
745
|
+
*/
|
|
746
|
+
getEnabled() {
|
|
747
|
+
return this.lookup({ enabled: true });
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Get registry statistics
|
|
751
|
+
*
|
|
752
|
+
* @returns Registry statistics
|
|
753
|
+
*/
|
|
754
|
+
getStats() {
|
|
755
|
+
const all = this.getAll();
|
|
756
|
+
const enabled = this.getEnabled();
|
|
757
|
+
const byCategory = {
|
|
758
|
+
dev: 0,
|
|
759
|
+
git: 0,
|
|
760
|
+
review: 0,
|
|
761
|
+
testing: 0,
|
|
762
|
+
docs: 0,
|
|
763
|
+
devops: 0,
|
|
764
|
+
planning: 0,
|
|
765
|
+
debugging: 0,
|
|
766
|
+
custom: 0
|
|
767
|
+
};
|
|
768
|
+
const bySource = { builtin: 0, user: 0, marketplace: 0 };
|
|
769
|
+
let totalTokens = 0;
|
|
770
|
+
let lastRegistered = 0;
|
|
771
|
+
let lastModified = 0;
|
|
772
|
+
for (const entry of all) {
|
|
773
|
+
byCategory[entry.metadata.category]++;
|
|
774
|
+
bySource[entry.source]++;
|
|
775
|
+
totalTokens += entry.estimatedTokens;
|
|
776
|
+
if (entry.registeredAt > lastRegistered)
|
|
777
|
+
lastRegistered = entry.registeredAt;
|
|
778
|
+
if (entry.modifiedAt > lastModified)
|
|
779
|
+
lastModified = entry.modifiedAt;
|
|
780
|
+
}
|
|
781
|
+
return {
|
|
782
|
+
totalSkills: all.length,
|
|
783
|
+
enabledSkills: enabled.length,
|
|
784
|
+
disabledSkills: all.length - enabled.length,
|
|
785
|
+
byCategory,
|
|
786
|
+
bySource,
|
|
787
|
+
totalTokens,
|
|
788
|
+
lastRegistered: lastRegistered > 0 ? this.getById(this.getEntriesSortedBy("registeredAt")[0]?.id || "")?.id : void 0,
|
|
789
|
+
lastModified: lastModified > 0 ? this.getById(this.getEntriesSortedBy("modifiedAt")[0]?.id || "")?.id : void 0
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Get dependent skills
|
|
794
|
+
*
|
|
795
|
+
* @param id - Skill ID
|
|
796
|
+
* @returns Array of dependent skill IDs
|
|
797
|
+
*/
|
|
798
|
+
getDependents(id) {
|
|
799
|
+
return Array.from(this.skills.get(id)?.dependents || []);
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Get skill dependencies
|
|
803
|
+
*
|
|
804
|
+
* @param id - Skill ID
|
|
805
|
+
* @returns Array of dependency IDs
|
|
806
|
+
*/
|
|
807
|
+
getDependencies(id) {
|
|
808
|
+
return this.skills.get(id)?.dependencies || [];
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Check for missing dependencies
|
|
812
|
+
*
|
|
813
|
+
* @param id - Skill ID
|
|
814
|
+
* @returns Array of missing dependency IDs
|
|
815
|
+
*/
|
|
816
|
+
getMissingDependencies(id) {
|
|
817
|
+
const deps = this.getDependencies(id);
|
|
818
|
+
return deps.filter((dep) => !this.has(dep));
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Validate all dependencies
|
|
822
|
+
*
|
|
823
|
+
* @returns Map of skill ID to missing dependencies
|
|
824
|
+
*/
|
|
825
|
+
validateDependencies() {
|
|
826
|
+
const missing = /* @__PURE__ */ new Map();
|
|
827
|
+
Array.from(this.skills.entries()).forEach(([id, entry]) => {
|
|
828
|
+
const missingDeps = entry.dependencies.filter((dep) => !this.has(dep));
|
|
829
|
+
if (missingDeps.length > 0) {
|
|
830
|
+
missing.set(id, missingDeps);
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
return missing;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Clear all skills from registry
|
|
837
|
+
*/
|
|
838
|
+
clear() {
|
|
839
|
+
this.skills.clear();
|
|
840
|
+
this.filePathIndex.clear();
|
|
841
|
+
this.triggerIndex.clear();
|
|
842
|
+
this.emit("registry:cleared");
|
|
843
|
+
this.publishMessage("registry:cleared", {});
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get the size of the registry
|
|
847
|
+
*
|
|
848
|
+
* @returns Number of registered skills
|
|
849
|
+
*/
|
|
850
|
+
size() {
|
|
851
|
+
return this.skills.size;
|
|
852
|
+
}
|
|
853
|
+
// ==========================================================================
|
|
854
|
+
// Private Methods
|
|
855
|
+
// ==========================================================================
|
|
856
|
+
/**
|
|
857
|
+
* Extract dependencies from skill metadata
|
|
858
|
+
*/
|
|
859
|
+
extractDependencies(metadata) {
|
|
860
|
+
const deps = [];
|
|
861
|
+
if (metadata.agents) {
|
|
862
|
+
deps.push(...metadata.agents);
|
|
863
|
+
}
|
|
864
|
+
if (metadata.related_skills) {
|
|
865
|
+
deps.push(...metadata.related_skills);
|
|
866
|
+
}
|
|
867
|
+
return deps;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Update trigger index
|
|
871
|
+
*/
|
|
872
|
+
updateTriggerIndex(id, triggers) {
|
|
873
|
+
Array.from(this.triggerIndex.entries()).forEach(([trigger, skillIds]) => {
|
|
874
|
+
skillIds.delete(id);
|
|
875
|
+
if (skillIds.size === 0)
|
|
876
|
+
this.triggerIndex.delete(trigger);
|
|
877
|
+
});
|
|
878
|
+
for (const trigger of triggers) {
|
|
879
|
+
if (!this.triggerIndex.has(trigger)) {
|
|
880
|
+
this.triggerIndex.set(trigger, /* @__PURE__ */ new Set());
|
|
881
|
+
}
|
|
882
|
+
this.triggerIndex.get(trigger).add(id);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Update dependency graph
|
|
887
|
+
*/
|
|
888
|
+
updateDependencyGraph(id, dependencies) {
|
|
889
|
+
Array.from(this.skills.entries()).forEach(([_depId, entry]) => {
|
|
890
|
+
if (entry.dependencies.includes(id)) {
|
|
891
|
+
entry.dependents.delete(id);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
for (const dep of dependencies) {
|
|
895
|
+
const depEntry = this.skills.get(dep);
|
|
896
|
+
if (depEntry) {
|
|
897
|
+
depEntry.dependents.add(id);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Estimate token count for a skill
|
|
903
|
+
*/
|
|
904
|
+
estimateTokens(skill) {
|
|
905
|
+
const contentTokens = Math.ceil(skill.content.length / 4);
|
|
906
|
+
const metadataTokens = Math.ceil(JSON.stringify(skill.metadata).length / 4);
|
|
907
|
+
return contentTokens + metadataTokens;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Get entries sorted by a field
|
|
911
|
+
*/
|
|
912
|
+
getEntriesSortedBy(field) {
|
|
913
|
+
return Array.from(this.skills.values()).sort((a, b) => b[field] - a[field]);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Publish message to event bus
|
|
917
|
+
*/
|
|
918
|
+
publishMessage(type, payload) {
|
|
919
|
+
this.messageBus.publish(
|
|
920
|
+
type,
|
|
921
|
+
"coordinator",
|
|
922
|
+
"all",
|
|
923
|
+
`Skill registry event: ${type}`,
|
|
924
|
+
payload,
|
|
925
|
+
{ priority: "normal" }
|
|
926
|
+
).catch(console.error);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
let registryInstance = null;
|
|
930
|
+
function getSkillRegistry() {
|
|
931
|
+
if (!registryInstance) {
|
|
932
|
+
registryInstance = new SkillRegistry();
|
|
933
|
+
}
|
|
934
|
+
return registryInstance;
|
|
935
|
+
}
|
|
16
936
|
|
|
17
937
|
class ProgressTracker extends EventEmitter {
|
|
18
938
|
config;
|
|
@@ -1304,7 +2224,7 @@ const promptUserQuestion = async (question) => {
|
|
|
1304
2224
|
return null;
|
|
1305
2225
|
}
|
|
1306
2226
|
try {
|
|
1307
|
-
const inquirer = (await import('./
|
|
2227
|
+
const inquirer = (await import('./index4.mjs').then(function (n) { return n.c; })).default;
|
|
1308
2228
|
const choices = question.options.map((option) => ({
|
|
1309
2229
|
name: option.description ? `${option.label} \u2014 ${option.description}` : option.label,
|
|
1310
2230
|
value: option.value
|
|
@@ -1879,6 +2799,11 @@ class AutoExecutor extends EventEmitter {
|
|
|
1879
2799
|
const executionId = `exec-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1880
2800
|
const startedAt = Date.now();
|
|
1881
2801
|
this.emit("execution:started", { input: userInput });
|
|
2802
|
+
this.emitCommandHook("command-start", {
|
|
2803
|
+
executionId,
|
|
2804
|
+
inputLength: userInput.length,
|
|
2805
|
+
source: "auto-executor"
|
|
2806
|
+
});
|
|
1882
2807
|
this.config.telemetry.record({
|
|
1883
2808
|
executionId,
|
|
1884
2809
|
phase: "execution",
|
|
@@ -1904,6 +2829,13 @@ class AutoExecutor extends EventEmitter {
|
|
|
1904
2829
|
complexity: routeResult.intent.complexity
|
|
1905
2830
|
}
|
|
1906
2831
|
});
|
|
2832
|
+
this.emitCommandHook("command-telemetry", {
|
|
2833
|
+
executionId,
|
|
2834
|
+
phase: "intent",
|
|
2835
|
+
action: "route",
|
|
2836
|
+
route: routeResult.route,
|
|
2837
|
+
confidence: routeResult.intent.confidence
|
|
2838
|
+
});
|
|
1907
2839
|
const elicitationResult = await this.resolveRouteWithElicitation(
|
|
1908
2840
|
routeResult.route,
|
|
1909
2841
|
routeResult.intent,
|
|
@@ -2039,6 +2971,12 @@ class AutoExecutor extends EventEmitter {
|
|
|
2039
2971
|
complexity: intent.complexity
|
|
2040
2972
|
}
|
|
2041
2973
|
});
|
|
2974
|
+
this.emitCommandHook("command-telemetry", {
|
|
2975
|
+
executionId,
|
|
2976
|
+
phase: "route",
|
|
2977
|
+
action: route,
|
|
2978
|
+
durationMs: routeDuration
|
|
2979
|
+
});
|
|
2042
2980
|
const totalDuration = Date.now() - startedAt;
|
|
2043
2981
|
this.metricsCollector.recordResponseTime("auto-executor", totalDuration);
|
|
2044
2982
|
this.metricsCollector.recordTaskCompletion("auto-executor", true, totalDuration);
|
|
@@ -2055,6 +2993,13 @@ class AutoExecutor extends EventEmitter {
|
|
|
2055
2993
|
mcpToolsUsed: mcpToolsUsed.length
|
|
2056
2994
|
}
|
|
2057
2995
|
});
|
|
2996
|
+
this.emitCommandHook("command-complete", {
|
|
2997
|
+
executionId,
|
|
2998
|
+
success: true,
|
|
2999
|
+
route,
|
|
3000
|
+
durationMs: totalDuration,
|
|
3001
|
+
mcpToolsUsed: mcpToolsUsed.length
|
|
3002
|
+
});
|
|
2058
3003
|
result.insights = this.buildExecutionInsights({
|
|
2059
3004
|
initialRoute: routeResult.route,
|
|
2060
3005
|
resolvedRoute: route,
|
|
@@ -2082,6 +3027,12 @@ class AutoExecutor extends EventEmitter {
|
|
|
2082
3027
|
error: errorMessage
|
|
2083
3028
|
}
|
|
2084
3029
|
});
|
|
3030
|
+
this.emitCommandHook("command-complete", {
|
|
3031
|
+
executionId,
|
|
3032
|
+
success: false,
|
|
3033
|
+
durationMs: totalDuration,
|
|
3034
|
+
error: errorMessage
|
|
3035
|
+
});
|
|
2085
3036
|
this.emit("execution:failed", { error, input: userInput });
|
|
2086
3037
|
throw error;
|
|
2087
3038
|
}
|
|
@@ -2552,6 +3503,9 @@ class AutoExecutor extends EventEmitter {
|
|
|
2552
3503
|
getTelemetryEvents(limit = 50) {
|
|
2553
3504
|
return this.config.telemetry.getRecent(limit);
|
|
2554
3505
|
}
|
|
3506
|
+
clearTelemetry() {
|
|
3507
|
+
this.config.telemetry.clear();
|
|
3508
|
+
}
|
|
2555
3509
|
getErrorMessage(error) {
|
|
2556
3510
|
if (error instanceof Error) {
|
|
2557
3511
|
return error.message;
|
|
@@ -2566,6 +3520,9 @@ class AutoExecutor extends EventEmitter {
|
|
|
2566
3520
|
console.log(`[AutoExecutor] ${message}`);
|
|
2567
3521
|
}
|
|
2568
3522
|
}
|
|
3523
|
+
emitCommandHook(event, data) {
|
|
3524
|
+
void emitCommandHookEvent(event, data);
|
|
3525
|
+
}
|
|
2569
3526
|
}
|
|
2570
3527
|
let globalExecutor = null;
|
|
2571
3528
|
function getGlobalAutoExecutor(config) {
|
|
@@ -2674,6 +3631,7 @@ class CliInterceptor extends EventEmitter {
|
|
|
2674
3631
|
autoExecute: config.autoExecute !== void 0 ? config.autoExecute : true,
|
|
2675
3632
|
showIntent: config.showIntent !== void 0 ? config.showIntent : true,
|
|
2676
3633
|
bypassKeywords: config.bypassKeywords || [],
|
|
3634
|
+
ccjkOwnedSlashPrefixes: config.ccjkOwnedSlashPrefixes || ["/ccjk", "/ccjk:", "/plugin", "/plugins", "/skill"],
|
|
2677
3635
|
verbose: config.verbose !== void 0 ? config.verbose : false
|
|
2678
3636
|
};
|
|
2679
3637
|
}
|
|
@@ -2690,6 +3648,7 @@ class CliInterceptor extends EventEmitter {
|
|
|
2690
3648
|
this.emit("intercept:started", { input: userInput });
|
|
2691
3649
|
const bypassCheck = this.shouldBypass(userInput);
|
|
2692
3650
|
if (bypassCheck.bypass) {
|
|
3651
|
+
await this.handleBypassedCommand(userInput, bypassCheck.reason);
|
|
2693
3652
|
this.emit("intercept:bypassed", { input: userInput, reason: bypassCheck.reason });
|
|
2694
3653
|
return {
|
|
2695
3654
|
intercepted: false,
|
|
@@ -2720,6 +3679,12 @@ class CliInterceptor extends EventEmitter {
|
|
|
2720
3679
|
*/
|
|
2721
3680
|
shouldBypass(input) {
|
|
2722
3681
|
const normalized = input.trim().toLowerCase();
|
|
3682
|
+
if (normalized.startsWith("/")) {
|
|
3683
|
+
const isCcjkOwned = this.config.ccjkOwnedSlashPrefixes.some((prefix) => normalized.startsWith(prefix));
|
|
3684
|
+
if (!isCcjkOwned) {
|
|
3685
|
+
return { bypass: true, reason: "Native slash command passthrough" };
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
2723
3688
|
if (this.systemCommands.some((cmd) => normalized.startsWith(cmd))) {
|
|
2724
3689
|
return { bypass: true, reason: "System command" };
|
|
2725
3690
|
}
|
|
@@ -2742,6 +3707,31 @@ class CliInterceptor extends EventEmitter {
|
|
|
2742
3707
|
console.log(" System will automatically handle: skills, agents, MCP tools\n");
|
|
2743
3708
|
console.log(" Smart mode: ambiguity checks + capability-ranked tool selection + telemetry\n");
|
|
2744
3709
|
}
|
|
3710
|
+
async handleBypassedCommand(input, reason) {
|
|
3711
|
+
const normalized = input.trim().toLowerCase();
|
|
3712
|
+
const command = this.extractCommandName(normalized);
|
|
3713
|
+
if (normalized.startsWith("/clear")) {
|
|
3714
|
+
this.autoExecutor.clearTelemetry();
|
|
3715
|
+
contextLoader.clearCache();
|
|
3716
|
+
getSkillRegistry().clear();
|
|
3717
|
+
await emitCommandHookEvent("command-clear", {
|
|
3718
|
+
command,
|
|
3719
|
+
cleared: ["telemetry", "context-cache", "skill-registry"]
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
if (normalized.startsWith("/")) {
|
|
3723
|
+
await emitCommandHookEvent("command-bypass", {
|
|
3724
|
+
command,
|
|
3725
|
+
reason
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
extractCommandName(input) {
|
|
3730
|
+
if (!input.startsWith("/")) {
|
|
3731
|
+
return "";
|
|
3732
|
+
}
|
|
3733
|
+
return input.split(/\s+/)[0];
|
|
3734
|
+
}
|
|
2745
3735
|
/**
|
|
2746
3736
|
* Enable interceptor
|
|
2747
3737
|
*/
|
|
@@ -2885,9 +3875,9 @@ class BrainCliHook extends EventEmitter {
|
|
|
2885
3875
|
if (this.initialized) {
|
|
2886
3876
|
return;
|
|
2887
3877
|
}
|
|
2888
|
-
const { getGlobalStateManager } = await import('./convoy-manager.mjs').then(function (n) { return n.
|
|
3878
|
+
const { getGlobalStateManager } = await import('./convoy-manager.mjs').then(function (n) { return n.d; });
|
|
2889
3879
|
const { getGlobalMailboxManager } = await Promise.resolve().then(function () { return persistentMailbox; });
|
|
2890
|
-
const { getGlobalConvoyManager } = await import('./convoy-manager.mjs').then(function (n) { return n.
|
|
3880
|
+
const { getGlobalConvoyManager } = await import('./convoy-manager.mjs').then(function (n) { return n.e; });
|
|
2891
3881
|
const stateManager = getGlobalStateManager();
|
|
2892
3882
|
const mailboxManager = getGlobalMailboxManager();
|
|
2893
3883
|
const convoyManager = getGlobalConvoyManager();
|
|
@@ -3042,7 +4032,7 @@ ${"=".repeat(60)}`);
|
|
|
3042
4032
|
*/
|
|
3043
4033
|
async buildAdditionalContext() {
|
|
3044
4034
|
try {
|
|
3045
|
-
const { loadContextAtDepth } = await import('./
|
|
4035
|
+
const { loadContextAtDepth } = await import('./convoy-manager.mjs').then(function (n) { return n.b; });
|
|
3046
4036
|
const ctx = await loadContextAtDepth("L0");
|
|
3047
4037
|
if (ctx.totalTokens > 0) {
|
|
3048
4038
|
return `[Brain Context: ${ctx.layers.size} layers, ~${ctx.totalTokens} tokens, depth=${ctx.depth}]`;
|