@saleso.innovations/bridge 0.1.27 → 0.1.28

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.
@@ -1,5 +1,5 @@
1
1
  import { type ShellDeltaEmitter } from "./shellSession.js";
2
- export declare const HERMES_COMMAND_NAMES: readonly ["runtime.health", "runtime.detailedHealth", "runtime.version", "runtime.capabilities", "models.list", "model.set", "responses.create", "runs.create", "runs.status", "runs.stop", "jobs.list", "jobs.get", "jobs.create", "jobs.update", "jobs.pause", "jobs.resume", "jobs.runNow", "jobs.delete", "profiles.list", "profiles.create", "gateway.start", "gateway.stop", "gateway.restart", "hermes.update", "sessions.messages.list", "sessions.messages.countSent", "sessions.titles.resolve", "sessions.usage.get", "sessions.list", "skills.list", "files.list", "files.read", "files.write", "memories.list", "shell.exec", "shell.session.reset"];
2
+ export declare const HERMES_COMMAND_NAMES: readonly ["runtime.health", "runtime.detailedHealth", "runtime.version", "runtime.capabilities", "models.list", "model.set", "responses.create", "runs.create", "runs.status", "runs.stop", "jobs.list", "jobs.get", "jobs.create", "jobs.update", "jobs.pause", "jobs.resume", "jobs.runNow", "jobs.delete", "profiles.list", "profiles.create", "gateway.start", "gateway.stop", "gateway.restart", "hermes.update", "sessions.messages.list", "sessions.messages.countSent", "sessions.titles.resolve", "sessions.usage.get", "sessions.list", "skills.list", "files.list", "files.read", "files.write", "files.delete", "memories.list", "shell.exec", "shell.session.reset"];
3
3
  export type HermesCommandName = (typeof HERMES_COMMAND_NAMES)[number];
4
4
  export declare function isHermesCommandName(value: string): value is HermesCommandName;
5
5
  export type HermesCommandErrorCode = "command_unsupported" | "hermes_unreachable" | "hermes_request_failed" | "invalid_command_args" | "unsupported_by_http";
@@ -1 +1 @@
1
- {"version":3,"file":"hermesCommands.d.ts","sourceRoot":"","sources":["../src/hermesCommands.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAuC,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGhG,eAAO,MAAM,oBAAoB,moBAqCvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,iBAAiB,CAE7E;AAED,MAAM,MAAM,sBAAsB,GAC9B,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,qBAAqB,CAAC;AAE1B,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;gBAE1B,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM;CAI1D;AA6GD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAAO,GAC9F,OAAO,CAAC,OAAO,CAAC,CA0NlB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAetG"}
1
+ {"version":3,"file":"hermesCommands.d.ts","sourceRoot":"","sources":["../src/hermesCommands.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAuC,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGhG,eAAO,MAAM,oBAAoB,mpBAsCvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,iBAAiB,CAE7E;AAED,MAAM,MAAM,sBAAsB,GAC9B,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,qBAAqB,CAAC;AAE1B,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;gBAE1B,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM;CAI1D;AA6GD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAAO,GAC9F,OAAO,CAAC,OAAO,CAAC,CAiOlB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAetG"}
@@ -4,7 +4,7 @@ import { listHermesCronJobs } from "./cronList.js";
4
4
  import { listHermesSessions, listSessionMessages, countUserMessagesSent, getSessionUsage, resolveSessionTitles, } from "./hermesSessionDb.js";
5
5
  import { listHermesSkills } from "./skillsList.js";
6
6
  import { runHermesUpdate } from "./hermesUpdate.js";
7
- import { executeFilesList, executeFilesRead, executeFilesWrite, executeMemoriesList, } from "./hermesFileCommands.js";
7
+ import { executeFilesList, executeFilesRead, executeFilesWrite, executeFilesDelete, executeMemoriesList, } from "./hermesFileCommands.js";
8
8
  import { executeShellExec, resetShellSession } from "./shellSession.js";
9
9
  import { fetchHermesRuntimeVersion } from "./runtimeVersion.js";
10
10
  export const HERMES_COMMAND_NAMES = [
@@ -41,6 +41,7 @@ export const HERMES_COMMAND_NAMES = [
41
41
  "files.list",
42
42
  "files.read",
43
43
  "files.write",
44
+ "files.delete",
44
45
  "memories.list",
45
46
  "shell.exec",
46
47
  "shell.session.reset",
@@ -342,6 +343,13 @@ export async function executeHermesCommand(command, args, options = {}) {
342
343
  }
343
344
  return await executeFilesWrite({ path, content });
344
345
  }
346
+ case "files.delete": {
347
+ const path = optionalString(args, "path");
348
+ if (!path) {
349
+ throw new HermesCommandError("invalid_command_args", 'Missing "path"');
350
+ }
351
+ return await executeFilesDelete({ path });
352
+ }
345
353
  case "memories.list":
346
354
  return await executeMemoriesList();
347
355
  case "shell.exec": {
@@ -6,6 +6,10 @@ export declare function executeFilesWrite(args: Record<string, unknown>): Promis
6
6
  relativePath: string;
7
7
  size: number;
8
8
  }>;
9
+ export declare function executeFilesDelete(args: Record<string, unknown>): Promise<{
10
+ ok: true;
11
+ deletedPath: string;
12
+ }>;
9
13
  export declare function executeMemoriesList(): Promise<{
10
14
  files: import("./hermesFiles.js").HermesFileEntry[];
11
15
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"hermesFileCommands.d.ts","sourceRoot":"","sources":["../src/hermesFileCommands.ts"],"names":[],"mappings":"AA+BA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;GAKnE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;GAUpE;AAED,wBAAsB,mBAAmB;;GAExC;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;;GAkCnE"}
1
+ {"version":3,"file":"hermesFileCommands.d.ts","sourceRoot":"","sources":["../src/hermesFileCommands.ts"],"names":[],"mappings":"AAgCA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;GAKnE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;GAUpE;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;GAMrE;AAED,wBAAsB,mBAAmB;;GAExC;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;;GAkCnE"}
@@ -1,4 +1,4 @@
1
- import { DEFAULT_FILES_READ_MAX_BYTES, listHermesFiles, listHermesMemoryFiles, readHermesFileBytes, writeHermesFile, } from "./hermesFiles.js";
1
+ import { DEFAULT_FILES_READ_MAX_BYTES, deleteHermesFile, listHermesFiles, listHermesMemoryFiles, readHermesFileBytes, writeHermesFile, } from "./hermesFiles.js";
2
2
  import { uploadFileToConvex } from "./convexRelay.js";
3
3
  import { loadCredentials } from "./credentials.js";
4
4
  function optionalNumber(args, key) {
@@ -39,6 +39,13 @@ export async function executeFilesWrite(args) {
39
39
  }
40
40
  return writeHermesFile(path, content);
41
41
  }
42
+ export async function executeFilesDelete(args) {
43
+ const path = typeof args.path === "string" ? args.path.trim() : "";
44
+ if (!path) {
45
+ throw new Error('Missing "path"');
46
+ }
47
+ return deleteHermesFile(path);
48
+ }
42
49
  export async function executeMemoriesList() {
43
50
  return listHermesMemoryFiles();
44
51
  }
@@ -2,6 +2,8 @@ export declare const MAX_RELAY_UPLOAD_BYTES: number;
2
2
  export declare const DEFAULT_FILES_READ_MAX_BYTES: number;
3
3
  export declare const SOUL_RELATIVE_PATH = "SOUL.md";
4
4
  export declare const MEMORIES_DIR = "memories";
5
+ export declare const SKILLS_DIR = "skills";
6
+ export declare const SKILL_FILE_NAME = "SKILL.md";
5
7
  export declare const MEMORY_MD_RELATIVE_PATH = "memories/MEMORY.md";
6
8
  export declare const USER_MD_RELATIVE_PATH = "memories/USER.md";
7
9
  export declare const MEMORY_MD_CHAR_LIMIT = 2200;
@@ -50,12 +52,17 @@ export declare function listHermesFiles(options?: {
50
52
  files: HermesFileEntry[];
51
53
  };
52
54
  export declare function normalizeHermesRelativePath(inputPath: string): string;
55
+ export declare function isWritableSkillPath(relativePath: string): boolean;
53
56
  export declare function assertWritableHermesPath(inputPath: string): string;
54
57
  export declare function writeHermesFile(inputPath: string, content: string): {
55
58
  ok: true;
56
59
  relativePath: string;
57
60
  size: number;
58
61
  };
62
+ export declare function deleteHermesFile(inputPath: string): {
63
+ ok: true;
64
+ deletedPath: string;
65
+ };
59
66
  export declare function listHermesMemoryFiles(): {
60
67
  files: HermesFileEntry[];
61
68
  };
@@ -1 +1 @@
1
- {"version":3,"file":"hermesFiles.d.ts","sourceRoot":"","sources":["../src/hermesFiles.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hermesFiles.d.ts","sourceRoot":"","sources":["../src/hermesFiles.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync, } from "node:fs";
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
4
  import { loadHermesCronJobNames } from "./hermesCronJobs.js";
@@ -6,6 +6,8 @@ export const MAX_RELAY_UPLOAD_BYTES = 25 * 1024 * 1024;
6
6
  export const DEFAULT_FILES_READ_MAX_BYTES = 512 * 1024;
7
7
  export const SOUL_RELATIVE_PATH = "SOUL.md";
8
8
  export const MEMORIES_DIR = "memories";
9
+ export const SKILLS_DIR = "skills";
10
+ export const SKILL_FILE_NAME = "SKILL.md";
9
11
  export const MEMORY_MD_RELATIVE_PATH = "memories/MEMORY.md";
10
12
  export const USER_MD_RELATIVE_PATH = "memories/USER.md";
11
13
  export const MEMORY_MD_CHAR_LIMIT = 2200;
@@ -241,6 +243,15 @@ export function listHermesFiles(options = {}) {
241
243
  export function normalizeHermesRelativePath(inputPath) {
242
244
  return inputPath.trim().replace(/\\/g, "/").replace(/^\/+/, "");
243
245
  }
246
+ export function isWritableSkillPath(relativePath) {
247
+ if (!relativePath.startsWith(`${SKILLS_DIR}/`)) {
248
+ return false;
249
+ }
250
+ if (relativePath.includes("..")) {
251
+ return false;
252
+ }
253
+ return relativePath.endsWith(`/${SKILL_FILE_NAME}`);
254
+ }
244
255
  export function assertWritableHermesPath(inputPath) {
245
256
  const relativePath = normalizeHermesRelativePath(inputPath);
246
257
  if (!relativePath) {
@@ -249,6 +260,9 @@ export function assertWritableHermesPath(inputPath) {
249
260
  if (relativePath === SOUL_RELATIVE_PATH) {
250
261
  return relativePath;
251
262
  }
263
+ if (isWritableSkillPath(relativePath)) {
264
+ return relativePath;
265
+ }
252
266
  const memoriesPrefix = `${MEMORIES_DIR}/`;
253
267
  if (!relativePath.startsWith(memoriesPrefix)) {
254
268
  throw new Error(`Path is not writable: ${inputPath}`);
@@ -277,13 +291,30 @@ export function writeHermesFile(inputPath, content) {
277
291
  }
278
292
  const home = resolveHermesHome();
279
293
  const resolved = resolveSandboxedPath(relativePath);
280
- if (relativePath.startsWith(`${MEMORIES_DIR}/`)) {
294
+ if (relativePath.startsWith(`${SKILLS_DIR}/`)) {
295
+ mkdirSync(dirname(resolved), { recursive: true });
296
+ }
297
+ else if (relativePath.startsWith(`${MEMORIES_DIR}/`)) {
281
298
  mkdirSync(join(home, MEMORIES_DIR), { recursive: true });
282
299
  }
283
300
  const bytes = Buffer.from(content, "utf8");
284
301
  writeFileSync(resolved, bytes);
285
302
  return { ok: true, relativePath, size: bytes.length };
286
303
  }
304
+ export function deleteHermesFile(inputPath) {
305
+ const relativePath = normalizeHermesRelativePath(inputPath);
306
+ if (!isWritableSkillPath(relativePath)) {
307
+ throw new Error(`Path is not deletable: ${inputPath}`);
308
+ }
309
+ const resolved = resolveSandboxedPath(relativePath);
310
+ if (!existsSync(resolved)) {
311
+ throw new Error(`File not found: ${inputPath}`);
312
+ }
313
+ const skillDir = dirname(resolved);
314
+ const deletedPath = relativePath.slice(0, relativePath.length - `/${SKILL_FILE_NAME}`.length);
315
+ rmSync(skillDir, { recursive: true, force: true });
316
+ return { ok: true, deletedPath };
317
+ }
287
318
  function memoryFileEntry(home, fileName) {
288
319
  const relativePath = `${MEMORIES_DIR}/${fileName}`;
289
320
  const fullPath = join(home, MEMORIES_DIR, fileName);
@@ -1,9 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
- import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
2
+ import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import test from "node:test";
6
- import { assertWritableHermesPath, cronOutputSourceKey, listHermesFiles, listHermesMemoryFiles, MEMORY_MD_CHAR_LIMIT, MEMORY_MD_RELATIVE_PATH, readHermesFileBytes, SOUL_RELATIVE_PATH, USER_MD_CHAR_LIMIT, writeHermesFile, } from "./hermesFiles.js";
6
+ import { assertWritableHermesPath, cronOutputSourceKey, deleteHermesFile, listHermesFiles, listHermesMemoryFiles, MEMORY_MD_CHAR_LIMIT, MEMORY_MD_RELATIVE_PATH, readHermesFileBytes, SKILLS_DIR, SKILL_FILE_NAME, SOUL_RELATIVE_PATH, USER_MD_CHAR_LIMIT, writeHermesFile, } from "./hermesFiles.js";
7
7
  test("cronOutputSourceKey extracts jobId/filename from cron output paths", () => {
8
8
  assert.equal(cronOutputSourceKey("cron/output/abc123/2026-05-25_06-30-00.md"), "abc123/2026-05-25_06-30-00.md");
9
9
  assert.equal(cronOutputSourceKey("cache/documents/report.pdf"), undefined);
@@ -36,13 +36,38 @@ test("listHermesFiles includes cron output with source metadata", () => {
36
36
  }
37
37
  }
38
38
  });
39
- test("assertWritableHermesPath allows SOUL.md and memories markdown only", () => {
39
+ test("assertWritableHermesPath allows SOUL.md, memories markdown, and skills", () => {
40
40
  assert.equal(assertWritableHermesPath("SOUL.md"), SOUL_RELATIVE_PATH);
41
41
  assert.equal(assertWritableHermesPath("memories/MEMORY.md"), MEMORY_MD_RELATIVE_PATH);
42
+ assert.equal(assertWritableHermesPath(`${SKILLS_DIR}/my-skill/${SKILL_FILE_NAME}`), `${SKILLS_DIR}/my-skill/${SKILL_FILE_NAME}`);
42
43
  assert.throws(() => assertWritableHermesPath("cron/output/x.md"));
43
44
  assert.throws(() => assertWritableHermesPath("memories/nested/x.md"));
45
+ assert.throws(() => assertWritableHermesPath(`${SKILLS_DIR}/my-skill/other.md`));
44
46
  assert.throws(() => assertWritableHermesPath("../SOUL.md"));
45
47
  });
48
+ test("writeHermesFile creates skill directories and deleteHermesFile removes them", () => {
49
+ const home = mkdtempSync(join(tmpdir(), "hermes-skill-write-test-"));
50
+ const previousHome = process.env.HERMES_HOME;
51
+ process.env.HERMES_HOME = home;
52
+ try {
53
+ const skillPath = `${SKILLS_DIR}/demo-skill/${SKILL_FILE_NAME}`;
54
+ const content = "---\nname: demo-skill\ndescription: Demo\n---\n\n# Demo\n";
55
+ writeHermesFile(skillPath, content);
56
+ const written = readHermesFileBytes(skillPath);
57
+ assert.equal(written.bytes.toString("utf8"), content);
58
+ const result = deleteHermesFile(skillPath);
59
+ assert.equal(result.deletedPath, `${SKILLS_DIR}/demo-skill`);
60
+ assert.equal(existsSync(join(home, SKILLS_DIR, "demo-skill")), false);
61
+ }
62
+ finally {
63
+ if (previousHome === undefined) {
64
+ delete process.env.HERMES_HOME;
65
+ }
66
+ else {
67
+ process.env.HERMES_HOME = previousHome;
68
+ }
69
+ }
70
+ });
46
71
  test("writeHermesFile enforces memory char limits and round-trips", () => {
47
72
  const home = mkdtempSync(join(tmpdir(), "hermes-write-test-"));
48
73
  const previousHome = process.env.HERMES_HOME;
@@ -2,6 +2,7 @@ export type HermesSkillEntry = {
2
2
  name: string;
3
3
  description: string;
4
4
  category?: string;
5
+ relativePath?: string;
5
6
  };
6
7
  export declare function listHermesSkills(): Promise<{
7
8
  skills: HermesSkillEntry[];
@@ -1 +1 @@
1
- {"version":3,"file":"skillsList.d.ts","sourceRoot":"","sources":["../src/skillsList.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AA4LF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC,CAOhF"}
1
+ {"version":3,"file":"skillsList.d.ts","sourceRoot":"","sources":["../src/skillsList.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAuOF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC,CAOhF"}
@@ -1,14 +1,17 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
- import { homedir } from "node:os";
4
3
  import { join, relative } from "node:path";
5
4
  import { promisify } from "node:util";
5
+ import { resolveHermesHome } from "./hermesFiles.js";
6
6
  const execFileAsync = promisify(execFile);
7
- const HERMES_SKILLS_DIR = join(homedir(), ".hermes", "skills");
8
7
  const LIST_TIMEOUT_MS = 30_000;
9
8
  const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
10
9
  const SKILL_FILE_NAME = "SKILL.md";
10
+ const SKILLS_DIR = "skills";
11
11
  const HIDDEN_DIR_NAMES = new Set([".hub", ".git"]);
12
+ function skillsRoot() {
13
+ return join(resolveHermesHome(), SKILLS_DIR);
14
+ }
12
15
  function normalizeSkillsPayload(value) {
13
16
  if (Array.isArray(value)) {
14
17
  return { skills: value.flatMap((item) => parseSkillRecord(item)) };
@@ -33,11 +36,15 @@ function parseSkillRecord(value) {
33
36
  return [];
34
37
  }
35
38
  const description = pickString(record, ["description", "desc", "summary"]) ?? "";
36
- const category = pickString(record, ["category", "path", "group"]);
39
+ const category = pickString(record, ["category", "group"]);
40
+ const relativePath = pickString(record, ["relativePath", "relative_path", "path"]);
37
41
  const entry = { name, description };
38
42
  if (category) {
39
43
  entry.category = category;
40
44
  }
45
+ if (relativePath && relativePath.endsWith(`/${SKILL_FILE_NAME}`)) {
46
+ entry.relativePath = relativePath.replace(/\\/g, "/");
47
+ }
41
48
  return [entry];
42
49
  }
43
50
  function pickString(record, keys) {
@@ -79,9 +86,9 @@ function parseFrontmatter(raw) {
79
86
  }
80
87
  return frontmatter;
81
88
  }
82
- function deriveCategory(skillsRoot, skillFilePath) {
89
+ function deriveCategory(skillsRootDir, skillFilePath) {
83
90
  const skillDir = join(skillFilePath, "..");
84
- const relativePath = relative(skillsRoot, skillDir);
91
+ const relativePath = relative(skillsRootDir, skillDir);
85
92
  if (!relativePath || relativePath === ".") {
86
93
  return undefined;
87
94
  }
@@ -91,8 +98,16 @@ function deriveCategory(skillsRoot, skillFilePath) {
91
98
  }
92
99
  return segments[0];
93
100
  }
101
+ function skillLookupKey(skill) {
102
+ return `${skill.category ?? ""}\0${skill.name.toLowerCase()}`;
103
+ }
104
+ function toHermesRelativeSkillPath(skillsRootDir, skillFilePath) {
105
+ const fromSkillsDir = relative(skillsRootDir, skillFilePath).replace(/\\/g, "/");
106
+ return `${SKILLS_DIR}/${fromSkillsDir}`;
107
+ }
94
108
  function readSkillsFromDirectory() {
95
- if (!existsSync(HERMES_SKILLS_DIR)) {
109
+ const root = skillsRoot();
110
+ if (!existsSync(root)) {
96
111
  return { skills: [] };
97
112
  }
98
113
  const skills = [];
@@ -134,22 +149,26 @@ function readSkillsFromDirectory() {
134
149
  continue;
135
150
  }
136
151
  const frontmatter = parseFrontmatter(raw);
137
- const relativePath = relative(HERMES_SKILLS_DIR, join(fullPath, ".."));
138
- const fallbackName = relativePath.split(/[/\\]/).filter(Boolean).pop() ?? relativePath;
152
+ const dirRelativePath = relative(root, join(fullPath, ".."));
153
+ const fallbackName = dirRelativePath.split(/[/\\]/).filter(Boolean).pop() ?? dirRelativePath;
139
154
  const name = frontmatter.name?.trim() || fallbackName;
140
155
  if (!name) {
141
156
  continue;
142
157
  }
143
158
  const description = frontmatter.description?.trim() ?? "";
144
- const category = deriveCategory(HERMES_SKILLS_DIR, fullPath);
145
- const skill = { name, description };
159
+ const category = deriveCategory(root, fullPath);
160
+ const skill = {
161
+ name,
162
+ description,
163
+ relativePath: toHermesRelativeSkillPath(root, fullPath),
164
+ };
146
165
  if (category) {
147
166
  skill.category = category;
148
167
  }
149
168
  skills.push(skill);
150
169
  }
151
170
  }
152
- walk(HERMES_SKILLS_DIR);
171
+ walk(root);
153
172
  skills.sort((left, right) => {
154
173
  const categoryCompare = (left.category ?? "").localeCompare(right.category ?? "", undefined, {
155
174
  sensitivity: "base",
@@ -161,6 +180,25 @@ function readSkillsFromDirectory() {
161
180
  });
162
181
  return { skills };
163
182
  }
183
+ function enrichSkillsWithFilesystemPaths(skills) {
184
+ const filesystemSkills = readSkillsFromDirectory();
185
+ const pathByKey = new Map();
186
+ for (const skill of filesystemSkills.skills) {
187
+ if (skill.relativePath) {
188
+ pathByKey.set(skillLookupKey(skill), skill.relativePath);
189
+ }
190
+ }
191
+ return skills.map((skill) => {
192
+ if (skill.relativePath) {
193
+ return skill;
194
+ }
195
+ const relativePath = pathByKey.get(skillLookupKey(skill));
196
+ if (!relativePath) {
197
+ return skill;
198
+ }
199
+ return { ...skill, relativePath };
200
+ });
201
+ }
164
202
  async function runHermesSkillsListJson() {
165
203
  try {
166
204
  const { stdout } = await execFileAsync("hermes", ["skills", "list", "--json"], {
@@ -181,7 +219,7 @@ async function runHermesSkillsListJson() {
181
219
  export async function listHermesSkills() {
182
220
  const jsonResult = await runHermesSkillsListJson();
183
221
  if (jsonResult != null) {
184
- return jsonResult;
222
+ return { skills: enrichSkillsWithFilesystemPaths(jsonResult.skills) };
185
223
  }
186
224
  return readSkillsFromDirectory();
187
225
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saleso.innovations/bridge",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "Connect your Hermes agent to the Cleos iOS app via pairing code.",
5
5
  "type": "module",
6
6
  "license": "MIT",