@botcord/daemon 0.2.89 → 0.2.91

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.
@@ -8,8 +8,10 @@ import { homedir } from "node:os";
8
8
  import path from "node:path";
9
9
  import {
10
10
  agentCodexHomeDir,
11
+ agentHermesHomeDir,
11
12
  agentWorkspaceDir,
12
13
  } from "./agent-workspace.js";
14
+ import { hermesProfileHomeDir } from "./gateway/runtimes/hermes-agent.js";
13
15
 
14
16
  const MAX_SKILLS = 24;
15
17
  const MAX_DESCRIPTION_CHARS = 260;
@@ -19,6 +21,8 @@ export interface SoftSkillEntry {
19
21
  name: string;
20
22
  path: string;
21
23
  source: string;
24
+ runtime?: string;
25
+ profile?: string;
22
26
  description?: string;
23
27
  mtimeMs: number;
24
28
  }
@@ -26,53 +30,118 @@ export interface SoftSkillEntry {
26
30
  export interface AgentSkillSnapshotEntry {
27
31
  name: string;
28
32
  source: string;
33
+ sourceDetail?: string;
34
+ runtime?: string;
35
+ path?: string;
36
+ profile?: string;
29
37
  description?: string;
30
38
  mtimeMs: number;
31
39
  }
32
40
 
33
41
  export interface AgentSkillSnapshot {
34
42
  agentId: string;
43
+ runtime?: string;
35
44
  skills: AgentSkillSnapshotEntry[];
36
45
  probedAt: number;
37
46
  }
38
47
 
39
48
  export interface SkillIndexOptions {
40
49
  extraDirs?: string[];
50
+ hermesProfile?: string;
41
51
  includeGlobal?: boolean;
42
52
  runtime?: string;
43
53
  }
44
54
 
55
+ interface SkillRoot {
56
+ dir: string;
57
+ source: string;
58
+ runtime?: string;
59
+ profile?: string;
60
+ }
61
+
45
62
  export function defaultSkillDirs(
46
63
  agentId: string,
47
64
  opts: SkillIndexOptions = {},
48
- ): Array<{ dir: string; source: string }> {
65
+ ): SkillRoot[] {
49
66
  const includeGlobal = opts.includeGlobal !== false;
50
67
  const agentClaude = {
51
68
  dir: path.join(agentWorkspaceDir(agentId), ".claude", "skills"),
52
69
  source: "agent-claude",
70
+ runtime: "claude-code",
53
71
  };
54
72
  const agentCodex = {
55
73
  dir: path.join(agentCodexHomeDir(agentId), "skills"),
56
74
  source: "agent-codex",
75
+ runtime: "codex",
57
76
  };
58
- const dirs: Array<{ dir: string; source: string }> =
59
- runtimeFamily(opts.runtime) === "codex"
60
- ? [agentCodex, agentClaude]
61
- : [agentClaude, agentCodex];
62
-
63
- if (includeGlobal) {
64
- const globalClaude = { dir: path.join(homedir(), ".claude", "skills"), source: "global-claude" };
65
- const globalCodex = { dir: path.join(homedir(), ".codex", "skills"), source: "global-codex" };
66
- dirs.push(
67
- ...(runtimeFamily(opts.runtime) === "codex"
68
- ? [globalCodex, globalClaude]
69
- : [globalClaude, globalCodex]),
70
- );
77
+ const agentGemini = [
78
+ {
79
+ dir: path.join(agentWorkspaceDir(agentId), ".gemini", "skills"),
80
+ source: "agent-gemini",
81
+ runtime: "gemini",
82
+ },
83
+ {
84
+ dir: path.join(agentWorkspaceDir(agentId), ".agents", "skills"),
85
+ source: "agent-agents",
86
+ runtime: "gemini",
87
+ },
88
+ ];
89
+ const agentHermes = hermesSkillRoot(agentId, opts.hermesProfile);
90
+
91
+ const dirs: SkillRoot[] = [];
92
+ switch (runtimeFamily(opts.runtime)) {
93
+ case "codex":
94
+ dirs.push(agentCodex);
95
+ if (includeGlobal) {
96
+ dirs.push({
97
+ dir: path.join(homedir(), ".codex", "skills"),
98
+ source: "global-codex",
99
+ runtime: "codex",
100
+ });
101
+ }
102
+ break;
103
+ case "hermes":
104
+ dirs.push(agentHermes);
105
+ break;
106
+ case "gemini":
107
+ dirs.push(...agentGemini);
108
+ if (includeGlobal) {
109
+ dirs.push(
110
+ {
111
+ dir: path.join(homedir(), ".gemini", "skills"),
112
+ source: "global-gemini",
113
+ runtime: "gemini",
114
+ },
115
+ {
116
+ dir: path.join(homedir(), ".agents", "skills"),
117
+ source: "global-agents",
118
+ runtime: "gemini",
119
+ },
120
+ );
121
+ }
122
+ break;
123
+ case "claude":
124
+ dirs.push(agentClaude);
125
+ if (includeGlobal) {
126
+ dirs.push({
127
+ dir: path.join(homedir(), ".claude", "skills"),
128
+ source: "global-claude",
129
+ runtime: "claude-code",
130
+ });
131
+ }
132
+ break;
133
+ case "other":
134
+ break;
71
135
  }
72
136
 
73
137
  const envDirs = parseSkillDirsEnv(process.env.BOTCORD_SKILL_DIRS);
74
138
  for (const dir of [...envDirs, ...(opts.extraDirs ?? [])]) {
75
- dirs.push({ dir, source: "external" });
139
+ dirs.push({
140
+ dir,
141
+ source: "external",
142
+ ...(opts.runtime ? { runtime: opts.runtime } : {}),
143
+ ...(opts.hermesProfile ? { profile: opts.hermesProfile } : {}),
144
+ });
76
145
  }
77
146
 
78
147
  return dedupeDirs(expandSkillRoots(dirs));
@@ -114,6 +183,8 @@ export function scanSoftSkills(
114
183
  name: parsed.name,
115
184
  path: skillMd,
116
185
  source: root.source,
186
+ ...(root.runtime ? { runtime: root.runtime } : {}),
187
+ ...(root.profile ? { profile: root.profile } : {}),
117
188
  description: parsed.description,
118
189
  mtimeMs: st.mtimeMs,
119
190
  };
@@ -133,9 +204,14 @@ export function collectAgentSkillSnapshot(
133
204
  ): AgentSkillSnapshot {
134
205
  return {
135
206
  agentId,
207
+ ...(opts.runtime ? { runtime: opts.runtime } : {}),
136
208
  skills: scanSoftSkills(agentId, opts).map((skill) => ({
137
209
  name: skill.name,
138
- source: skill.source.startsWith("agent-") ? "workspace" : "runtime-global",
210
+ source: snapshotSource(skill.source),
211
+ sourceDetail: skill.source,
212
+ ...(skill.runtime ? { runtime: skill.runtime } : {}),
213
+ path: skill.path,
214
+ ...(skill.profile ? { profile: skill.profile } : {}),
139
215
  ...(skill.description ? { description: skill.description } : {}),
140
216
  mtimeMs: skill.mtimeMs,
141
217
  })),
@@ -217,66 +293,67 @@ function parseSkillDirsEnv(value: string | undefined): string[] {
217
293
  }
218
294
 
219
295
  function dedupeDirs(
220
- dirs: Array<{ dir: string; source: string }>,
221
- ): Array<{ dir: string; source: string }> {
296
+ dirs: SkillRoot[],
297
+ ): SkillRoot[] {
222
298
  const seen = new Set<string>();
223
- const out: Array<{ dir: string; source: string }> = [];
299
+ const out: SkillRoot[] = [];
224
300
  for (const entry of dirs) {
225
301
  const resolved = path.resolve(entry.dir);
226
302
  if (seen.has(resolved)) continue;
227
303
  seen.add(resolved);
228
- out.push({ dir: resolved, source: entry.source });
304
+ out.push({ ...entry, dir: resolved });
229
305
  }
230
306
  return out;
231
307
  }
232
308
 
233
- function expandSkillRoots(
234
- dirs: Array<{ dir: string; source: string }>,
235
- ): Array<{ dir: string; source: string }> {
236
- const out: Array<{ dir: string; source: string }> = [];
309
+ function expandSkillRoots(dirs: SkillRoot[]): SkillRoot[] {
310
+ const out: SkillRoot[] = [];
237
311
  for (const entry of dirs) {
238
312
  out.push(entry);
239
313
  if (entry.source.includes("codex")) {
240
- out.push({ dir: path.join(entry.dir, ".system"), source: entry.source });
314
+ out.push({ ...entry, dir: path.join(entry.dir, ".system") });
241
315
  }
242
316
  }
243
317
  return out;
244
318
  }
245
319
 
246
- function runtimeFamily(runtime: string | undefined): "codex" | "claude" | "other" {
320
+ function hermesSkillRoot(agentId: string, profile: string | undefined): SkillRoot {
321
+ if (profile) {
322
+ try {
323
+ return {
324
+ dir: path.join(hermesProfileHomeDir(profile), "skills"),
325
+ source: "agent-hermes-profile",
326
+ runtime: "hermes-agent",
327
+ profile,
328
+ };
329
+ } catch {
330
+ // Corrupt legacy credentials should not make the whole skill snapshot fail.
331
+ }
332
+ }
333
+ return {
334
+ dir: path.join(agentHermesHomeDir(agentId), "skills"),
335
+ source: "agent-hermes",
336
+ runtime: "hermes-agent",
337
+ };
338
+ }
339
+
340
+ function runtimeFamily(runtime: string | undefined): "codex" | "claude" | "gemini" | "hermes" | "other" {
247
341
  if (runtime === "codex") return "codex";
342
+ if (runtime === "gemini") return "gemini";
343
+ if (runtime === "hermes-agent") return "hermes";
344
+ if (!runtime) return "claude";
248
345
  if (runtime === "claude-code") return "claude";
249
346
  return "other";
250
347
  }
251
348
 
252
- function priority(source: string, runtime: string | undefined): number {
253
- if (runtimeFamily(runtime) === "codex") {
254
- switch (source) {
255
- case "agent-codex":
256
- return 0;
257
- case "global-codex":
258
- return 1;
259
- case "agent-claude":
260
- return 2;
261
- case "global-claude":
262
- return 3;
263
- default:
264
- return 4;
265
- }
266
- }
349
+ function priority(source: string, _runtime: string | undefined): number {
350
+ if (source.startsWith("agent-")) return 0;
351
+ if (source.startsWith("global-")) return 1;
352
+ return 2;
353
+ }
267
354
 
268
- switch (source) {
269
- case "agent-claude":
270
- return 0;
271
- case "agent-codex":
272
- return 1;
273
- case "global-claude":
274
- return 2;
275
- case "global-codex":
276
- return 3;
277
- default:
278
- return 4;
279
- }
355
+ function snapshotSource(source: string): "workspace" | "runtime-global" {
356
+ return source.startsWith("agent-") ? "workspace" : "runtime-global";
280
357
  }
281
358
 
282
359
  function unquote(value: string): string {