@botcord/daemon 0.2.89 → 0.2.90

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,89 @@ 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 agentHermes = hermesSkillRoot(agentId, opts.hermesProfile);
78
+
79
+ const dirs: SkillRoot[] = [];
80
+ switch (runtimeFamily(opts.runtime)) {
81
+ case "codex":
82
+ dirs.push(agentCodex);
83
+ if (includeGlobal) {
84
+ dirs.push({
85
+ dir: path.join(homedir(), ".codex", "skills"),
86
+ source: "global-codex",
87
+ runtime: "codex",
88
+ });
89
+ }
90
+ break;
91
+ case "hermes":
92
+ dirs.push(agentHermes);
93
+ break;
94
+ case "claude":
95
+ dirs.push(agentClaude);
96
+ if (includeGlobal) {
97
+ dirs.push({
98
+ dir: path.join(homedir(), ".claude", "skills"),
99
+ source: "global-claude",
100
+ runtime: "claude-code",
101
+ });
102
+ }
103
+ break;
104
+ case "other":
105
+ break;
71
106
  }
72
107
 
73
108
  const envDirs = parseSkillDirsEnv(process.env.BOTCORD_SKILL_DIRS);
74
109
  for (const dir of [...envDirs, ...(opts.extraDirs ?? [])]) {
75
- dirs.push({ dir, source: "external" });
110
+ dirs.push({
111
+ dir,
112
+ source: "external",
113
+ ...(opts.runtime ? { runtime: opts.runtime } : {}),
114
+ ...(opts.hermesProfile ? { profile: opts.hermesProfile } : {}),
115
+ });
76
116
  }
77
117
 
78
118
  return dedupeDirs(expandSkillRoots(dirs));
@@ -114,6 +154,8 @@ export function scanSoftSkills(
114
154
  name: parsed.name,
115
155
  path: skillMd,
116
156
  source: root.source,
157
+ ...(root.runtime ? { runtime: root.runtime } : {}),
158
+ ...(root.profile ? { profile: root.profile } : {}),
117
159
  description: parsed.description,
118
160
  mtimeMs: st.mtimeMs,
119
161
  };
@@ -133,9 +175,14 @@ export function collectAgentSkillSnapshot(
133
175
  ): AgentSkillSnapshot {
134
176
  return {
135
177
  agentId,
178
+ ...(opts.runtime ? { runtime: opts.runtime } : {}),
136
179
  skills: scanSoftSkills(agentId, opts).map((skill) => ({
137
180
  name: skill.name,
138
- source: skill.source.startsWith("agent-") ? "workspace" : "runtime-global",
181
+ source: snapshotSource(skill.source),
182
+ sourceDetail: skill.source,
183
+ ...(skill.runtime ? { runtime: skill.runtime } : {}),
184
+ path: skill.path,
185
+ ...(skill.profile ? { profile: skill.profile } : {}),
139
186
  ...(skill.description ? { description: skill.description } : {}),
140
187
  mtimeMs: skill.mtimeMs,
141
188
  })),
@@ -217,68 +264,77 @@ function parseSkillDirsEnv(value: string | undefined): string[] {
217
264
  }
218
265
 
219
266
  function dedupeDirs(
220
- dirs: Array<{ dir: string; source: string }>,
221
- ): Array<{ dir: string; source: string }> {
267
+ dirs: SkillRoot[],
268
+ ): SkillRoot[] {
222
269
  const seen = new Set<string>();
223
- const out: Array<{ dir: string; source: string }> = [];
270
+ const out: SkillRoot[] = [];
224
271
  for (const entry of dirs) {
225
272
  const resolved = path.resolve(entry.dir);
226
273
  if (seen.has(resolved)) continue;
227
274
  seen.add(resolved);
228
- out.push({ dir: resolved, source: entry.source });
275
+ out.push({ ...entry, dir: resolved });
229
276
  }
230
277
  return out;
231
278
  }
232
279
 
233
- function expandSkillRoots(
234
- dirs: Array<{ dir: string; source: string }>,
235
- ): Array<{ dir: string; source: string }> {
236
- const out: Array<{ dir: string; source: string }> = [];
280
+ function expandSkillRoots(dirs: SkillRoot[]): SkillRoot[] {
281
+ const out: SkillRoot[] = [];
237
282
  for (const entry of dirs) {
238
283
  out.push(entry);
239
284
  if (entry.source.includes("codex")) {
240
- out.push({ dir: path.join(entry.dir, ".system"), source: entry.source });
285
+ out.push({ ...entry, dir: path.join(entry.dir, ".system") });
241
286
  }
242
287
  }
243
288
  return out;
244
289
  }
245
290
 
246
- function runtimeFamily(runtime: string | undefined): "codex" | "claude" | "other" {
291
+ function hermesSkillRoot(agentId: string, profile: string | undefined): SkillRoot {
292
+ if (profile) {
293
+ try {
294
+ return {
295
+ dir: path.join(hermesProfileHomeDir(profile), "skills"),
296
+ source: "agent-hermes-profile",
297
+ runtime: "hermes-agent",
298
+ profile,
299
+ };
300
+ } catch {
301
+ // Corrupt legacy credentials should not make the whole skill snapshot fail.
302
+ }
303
+ }
304
+ return {
305
+ dir: path.join(agentHermesHomeDir(agentId), "skills"),
306
+ source: "agent-hermes",
307
+ runtime: "hermes-agent",
308
+ };
309
+ }
310
+
311
+ function runtimeFamily(runtime: string | undefined): "codex" | "claude" | "hermes" | "other" {
247
312
  if (runtime === "codex") return "codex";
313
+ if (runtime === "hermes-agent") return "hermes";
314
+ if (!runtime) return "claude";
248
315
  if (runtime === "claude-code") return "claude";
249
316
  return "other";
250
317
  }
251
318
 
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
- }
267
-
319
+ function priority(source: string, _runtime: string | undefined): number {
268
320
  switch (source) {
269
321
  case "agent-claude":
270
- return 0;
271
322
  case "agent-codex":
272
- return 1;
323
+ case "agent-hermes":
324
+ case "agent-hermes-profile":
325
+ return 0;
273
326
  case "global-claude":
274
- return 2;
275
327
  case "global-codex":
276
- return 3;
328
+ return 1;
277
329
  default:
278
- return 4;
330
+ return 2;
279
331
  }
280
332
  }
281
333
 
334
+ function snapshotSource(source: string): "workspace" | "runtime-global" {
335
+ return source.startsWith("agent-") ? "workspace" : "runtime-global";
336
+ }
337
+
282
338
  function unquote(value: string): string {
283
339
  const trimmed = value.trim();
284
340
  if (