hermes-action-bridge 0.2.0

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/CONTRIBUTING.md +28 -0
  3. package/LICENSE +21 -0
  4. package/README.md +323 -0
  5. package/dist/adapters/hermes-cli.d.ts +3 -0
  6. package/dist/adapters/hermes-cli.js +48 -0
  7. package/dist/adapters/hermes-cli.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +271 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +5 -0
  12. package/dist/config.js +172 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/context.d.ts +2 -0
  15. package/dist/context.js +15 -0
  16. package/dist/context.js.map +1 -0
  17. package/dist/doctor.d.ts +18 -0
  18. package/dist/doctor.js +77 -0
  19. package/dist/doctor.js.map +1 -0
  20. package/dist/install/file-edit.d.ts +12 -0
  21. package/dist/install/file-edit.js +18 -0
  22. package/dist/install/file-edit.js.map +1 -0
  23. package/dist/install/install-service.d.ts +26 -0
  24. package/dist/install/install-service.js +59 -0
  25. package/dist/install/install-service.js.map +1 -0
  26. package/dist/install/managed-file.d.ts +9 -0
  27. package/dist/install/managed-file.js +157 -0
  28. package/dist/install/managed-file.js.map +1 -0
  29. package/dist/install/marker-block.d.ts +10 -0
  30. package/dist/install/marker-block.js +61 -0
  31. package/dist/install/marker-block.js.map +1 -0
  32. package/dist/install/mcp-config.d.ts +15 -0
  33. package/dist/install/mcp-config.js +65 -0
  34. package/dist/install/mcp-config.js.map +1 -0
  35. package/dist/install/paths.d.ts +6 -0
  36. package/dist/install/paths.js +22 -0
  37. package/dist/install/paths.js.map +1 -0
  38. package/dist/install/templates.d.ts +16 -0
  39. package/dist/install/templates.js +86 -0
  40. package/dist/install/templates.js.map +1 -0
  41. package/dist/install/types.d.ts +18 -0
  42. package/dist/install/types.js +2 -0
  43. package/dist/install/types.js.map +1 -0
  44. package/dist/mcp-server.d.ts +1 -0
  45. package/dist/mcp-server.js +69 -0
  46. package/dist/mcp-server.js.map +1 -0
  47. package/dist/policy.d.ts +9 -0
  48. package/dist/policy.js +25 -0
  49. package/dist/policy.js.map +1 -0
  50. package/dist/prompt.d.ts +2 -0
  51. package/dist/prompt.js +34 -0
  52. package/dist/prompt.js.map +1 -0
  53. package/dist/run.d.ts +2 -0
  54. package/dist/run.js +27 -0
  55. package/dist/run.js.map +1 -0
  56. package/dist/status.d.ts +8 -0
  57. package/dist/status.js +10 -0
  58. package/dist/status.js.map +1 -0
  59. package/dist/types.d.ts +77 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/version.d.ts +1 -0
  63. package/dist/version.js +12 -0
  64. package/dist/version.js.map +1 -0
  65. package/docs/ARCHITECTURE.md +105 -0
  66. package/docs/FUNCTIONAL-TESTS.md +97 -0
  67. package/examples/claude-code/CLAUDE.md +21 -0
  68. package/examples/claude-code/SKILL.md +55 -0
  69. package/examples/codex/AGENTS.md +21 -0
  70. package/examples/codex/SKILL.md +55 -0
  71. package/examples/configs/basic.yaml +36 -0
  72. package/examples/configs/strict.yaml +26 -0
  73. package/examples/configs/yolo.yaml +19 -0
  74. package/package.json +59 -0
@@ -0,0 +1,69 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { z } from "zod";
4
+ import { loadConfig } from "./config.js";
5
+ import { buildEffectiveRun } from "./run.js";
6
+ import { runHermesCli } from "./adapters/hermes-cli.js";
7
+ import { checkHermesStatus } from "./status.js";
8
+ import { version } from "./version.js";
9
+ const modeSchema = z.enum(["plan", "draft", "execute", "request-approval"]);
10
+ async function delegate(config, options) {
11
+ const dryRun = options.dryRun ?? false;
12
+ const run = buildEffectiveRun(config, {
13
+ prompt: options.prompt,
14
+ mode: options.mode,
15
+ preset: options.preset,
16
+ contextFiles: options.contextFiles ?? [],
17
+ yolo: options.yolo ?? false,
18
+ dryRun,
19
+ json: false,
20
+ });
21
+ const result = await runHermesCli(config, run, dryRun);
22
+ const sections = [result.stdout, result.stderr].filter((section) => section.trim().length > 0);
23
+ const text = sections.length > 0 ? sections.join("\n") : `Hermes exited with code ${result.exitCode}`;
24
+ const content = [{ type: "text", text }];
25
+ return result.ok ? { content } : { content, isError: true };
26
+ }
27
+ export async function startMcpServer(configPath) {
28
+ const config = loadConfig(process.cwd(), configPath);
29
+ const server = new McpServer({ name: "hermes-action-bridge", version });
30
+ server.registerTool("hermes_run", {
31
+ title: "Delegate a request to Hermes",
32
+ description: "Run Hermes through the configured hermes-action-bridge policy and presets.",
33
+ inputSchema: {
34
+ prompt: z.string().min(1),
35
+ mode: modeSchema.optional(),
36
+ preset: z.string().optional(),
37
+ contextFiles: z.array(z.string()).optional(),
38
+ yolo: z.boolean().optional(),
39
+ dryRun: z.boolean().optional(),
40
+ },
41
+ }, (args) => delegate(config, {
42
+ prompt: args.prompt,
43
+ mode: args.mode,
44
+ preset: args.preset,
45
+ contextFiles: args.contextFiles,
46
+ yolo: args.yolo,
47
+ dryRun: args.dryRun,
48
+ }));
49
+ server.registerTool("hermes_plan", {
50
+ title: "Ask Hermes for a plan",
51
+ description: "Shortcut for hermes_run with mode=plan.",
52
+ inputSchema: { prompt: z.string().min(1), preset: z.string().optional(), contextFiles: z.array(z.string()).optional() },
53
+ }, (args) => delegate(config, {
54
+ prompt: args.prompt,
55
+ mode: "plan",
56
+ preset: args.preset,
57
+ contextFiles: args.contextFiles,
58
+ }));
59
+ server.registerTool("hermes_presets", {
60
+ title: "List presets",
61
+ description: "List configured hermes-action-bridge presets.",
62
+ }, () => ({ content: [{ type: "text", text: JSON.stringify(config.presets, null, 2) }] }));
63
+ server.registerTool("hermes_status", {
64
+ title: "Check Hermes runtime status",
65
+ description: "Check whether the configured Hermes command is available.",
66
+ }, () => ({ content: [{ type: "text", text: JSON.stringify(checkHermesStatus(config), null, 2) }] }));
67
+ await server.connect(new StdioServerTransport());
68
+ }
69
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { BridgeMode, PolicyConfig, RiskCategory } from "./types.js";
2
+ export interface PolicyDecision {
3
+ mode: BridgeMode;
4
+ requestedMode: BridgeMode;
5
+ detectedRisks: RiskCategory[];
6
+ approvalRequired: boolean;
7
+ }
8
+ export declare function detectRisks(text: string): RiskCategory[];
9
+ export declare function applyPolicy(requestedMode: BridgeMode, policy: PolicyConfig, prompt: string, yoloFlag: boolean): PolicyDecision;
package/dist/policy.js ADDED
@@ -0,0 +1,25 @@
1
+ const riskMatchers = [
2
+ { risk: "publish_external", pattern: /\b(publish|post|tweet|linkedin|reddit|x\.com|twitter|social)\b/i },
3
+ { risk: "send_message", pattern: /\b(send|message|dm|discord|telegram|slack|whatsapp|sms)\b/i },
4
+ { risk: "send_email", pattern: /\b(email|e-mail|gmail|send mail|reply to)\b/i },
5
+ { risk: "delete", pattern: /\b(delete|remove|destroy|drop|wipe|rm\s+-rf)\b/i },
6
+ { risk: "payment", pattern: /\b(pay|payment|purchase|buy|subscribe|credit card|checkout)\b/i },
7
+ { risk: "git_push", pattern: /\b(git push|push to|publish branch|create repo|gh repo create)\b/i },
8
+ { risk: "credential_change", pattern: /\b(token|secret|api key|credential|password|oauth|keychain)\b/i },
9
+ ];
10
+ export function detectRisks(text) {
11
+ const risks = new Set();
12
+ for (const matcher of riskMatchers) {
13
+ if (matcher.pattern.test(text))
14
+ risks.add(matcher.risk);
15
+ }
16
+ return [...risks];
17
+ }
18
+ export function applyPolicy(requestedMode, policy, prompt, yoloFlag) {
19
+ const detectedRisks = detectRisks(prompt);
20
+ const yolo = yoloFlag || policy.yolo;
21
+ const approvalRequired = !yolo && detectedRisks.some((risk) => policy.requireApprovalFor.includes(risk));
22
+ const mode = approvalRequired && requestedMode === "execute" ? "request-approval" : requestedMode;
23
+ return { mode, requestedMode, detectedRisks, approvalRequired };
24
+ }
25
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAmD;IACnE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,iEAAiE,EAAE;IACxG,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,4DAA4D,EAAE;IAC/F,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,8CAA8C,EAAE;IAC/E,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iDAAiD,EAAE;IAC9E,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,gEAAgE,EAAE;IAC9F,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,mEAAmE,EAAE;IAClG,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,gEAAgE,EAAE;CACzG,CAAC;AASF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IACtC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,aAAyB,EAAE,MAAoB,EAAE,MAAc,EAAE,QAAiB;IAC5G,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC;IACrC,MAAM,gBAAgB,GAAG,CAAC,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACzG,MAAM,IAAI,GAAG,gBAAgB,IAAI,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,aAAa,CAAC;IAClG,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC;AAClE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { EffectiveRun } from "./types.js";
2
+ export declare function buildHermesPrompt(run: EffectiveRun): string;
package/dist/prompt.js ADDED
@@ -0,0 +1,34 @@
1
+ export function buildHermesPrompt(run) {
2
+ const contextBlock = run.contextDocuments.length
3
+ ? run.contextDocuments
4
+ .map((doc) => `<context path="${escapeAttribute(doc.path)}">\n${doc.content}\n</context>`)
5
+ .join("\n\n")
6
+ : "<context>None provided.</context>";
7
+ return [
8
+ "You are Hermes Agent being called by an external agent through hermes-action-bridge.",
9
+ "",
10
+ `Mode: ${run.mode}`,
11
+ `Requested mode: ${run.requestedMode}`,
12
+ `Preset: ${run.presetName}`,
13
+ `YOLO: ${run.yolo ? "enabled" : "disabled"}`,
14
+ `Detected risks: ${run.detectedRisks.length ? run.detectedRisks.join(", ") : "none"}`,
15
+ "",
16
+ "Policy:",
17
+ "- Follow the user's request using the tools and skills available in this Hermes session.",
18
+ "- If Mode is plan, return a plan only. Do not perform side effects.",
19
+ "- If Mode is draft, produce the requested artifact only. Do not perform external side effects.",
20
+ "- If Mode is request-approval, prepare the action and explicitly ask the human for approval before irreversible external effects.",
21
+ "- If Mode is execute, perform allowed actions, but still obey Hermes' own safety rules and any platform approval prompts.",
22
+ "- Never reveal secrets. Do not print API keys, OAuth tokens, passwords, private keys, or session cookies.",
23
+ "",
24
+ "External agent request:",
25
+ run.prompt,
26
+ "",
27
+ "Context files:",
28
+ contextBlock,
29
+ ].join("\n");
30
+ }
31
+ function escapeAttribute(value) {
32
+ return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");
33
+ }
34
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,GAAiB;IACjD,MAAM,YAAY,GAAG,GAAG,CAAC,gBAAgB,CAAC,MAAM;QAC9C,CAAC,CAAC,GAAG,CAAC,gBAAgB;aACjB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,cAAc,CAAC;aACzF,IAAI,CAAC,MAAM,CAAC;QACjB,CAAC,CAAC,mCAAmC,CAAC;IAExC,OAAO;QACL,sFAAsF;QACtF,EAAE;QACF,SAAS,GAAG,CAAC,IAAI,EAAE;QACnB,mBAAmB,GAAG,CAAC,aAAa,EAAE;QACtC,WAAW,GAAG,CAAC,UAAU,EAAE;QAC3B,SAAS,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE;QAC5C,mBAAmB,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;QACrF,EAAE;QACF,SAAS;QACT,0FAA0F;QAC1F,qEAAqE;QACrE,gGAAgG;QAChG,mIAAmI;QACnI,2HAA2H;QAC3H,2GAA2G;QAC3G,EAAE;QACF,yBAAyB;QACzB,GAAG,CAAC,MAAM;QACV,EAAE;QACF,gBAAgB;QAChB,YAAY;KACb,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC1F,CAAC"}
package/dist/run.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { BridgeConfig, EffectiveRun, RunOptions } from "./types.js";
2
+ export declare function buildEffectiveRun(config: BridgeConfig, options: RunOptions): EffectiveRun;
package/dist/run.js ADDED
@@ -0,0 +1,27 @@
1
+ import { applyPolicy } from "./policy.js";
2
+ import { readContextFiles } from "./context.js";
3
+ export function buildEffectiveRun(config, options) {
4
+ const presetName = options.preset || config.defaults.preset;
5
+ const preset = config.presets[presetName];
6
+ if (!preset)
7
+ throw new Error(`Unknown preset: ${presetName}`);
8
+ const requestedMode = options.mode || config.defaults.mode;
9
+ const yolo = options.yolo || config.policy.yolo;
10
+ const decision = applyPolicy(requestedMode, config.policy, options.prompt, yolo);
11
+ return {
12
+ mode: decision.mode,
13
+ requestedMode: decision.requestedMode,
14
+ presetName,
15
+ preset,
16
+ prompt: options.prompt,
17
+ source: options.source || preset.source || config.defaults.source,
18
+ profile: options.profile || preset.profile || config.defaults.profile,
19
+ provider: options.provider || preset.provider,
20
+ model: options.model || preset.model,
21
+ maxTurns: options.maxTurns || preset.maxTurns || config.defaults.maxTurns,
22
+ yolo,
23
+ detectedRisks: decision.detectedRisks,
24
+ contextDocuments: readContextFiles(options.contextFiles),
25
+ };
26
+ }
27
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,UAAU,iBAAiB,CAAC,MAAoB,EAAE,OAAmB;IACzE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;IAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAChD,MAAM,QAAQ,GAAG,WAAW,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEjF,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,UAAU;QACV,MAAM;QACN,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM;QACjE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO;QACrE,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;QAC7C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;QACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ;QACzE,IAAI;QACJ,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,gBAAgB,EAAE,gBAAgB,CAAC,OAAO,CAAC,YAAY,CAAC;KACzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { BridgeConfig } from "./types.js";
2
+ export interface StatusResult {
3
+ command: string;
4
+ available: boolean;
5
+ version?: string;
6
+ error?: string;
7
+ }
8
+ export declare function checkHermesStatus(config: BridgeConfig): StatusResult;
package/dist/status.js ADDED
@@ -0,0 +1,10 @@
1
+ import { spawnSync } from "node:child_process";
2
+ export function checkHermesStatus(config) {
3
+ const result = spawnSync(config.runtime.command, ["--version"], { encoding: "utf8" });
4
+ if (result.error)
5
+ return { command: config.runtime.command, available: false, error: result.error.message };
6
+ if (result.status !== 0)
7
+ return { command: config.runtime.command, available: false, error: result.stderr.trim() || `exit ${result.status}` };
8
+ return { command: config.runtime.command, available: true, version: result.stdout.trim() };
9
+ }
10
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAU/C,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACtF,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC5G,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;IAC9I,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;AAC7F,CAAC"}
@@ -0,0 +1,77 @@
1
+ export declare const modes: readonly ["plan", "draft", "execute", "request-approval"];
2
+ export type BridgeMode = (typeof modes)[number];
3
+ export type RuntimeAdapter = "hermes-cli";
4
+ export interface RuntimeConfig {
5
+ adapter: RuntimeAdapter;
6
+ command: string;
7
+ }
8
+ export interface PresetConfig {
9
+ description?: string | undefined;
10
+ skills: string[];
11
+ toolsets: string[];
12
+ provider?: string | undefined;
13
+ model?: string | undefined;
14
+ profile?: string | undefined;
15
+ maxTurns?: number | undefined;
16
+ source?: string | undefined;
17
+ }
18
+ export interface DefaultsConfig {
19
+ mode: BridgeMode;
20
+ profile?: string | undefined;
21
+ source: string;
22
+ maxTurns: number;
23
+ preset: string;
24
+ }
25
+ export type RiskCategory = "publish_external" | "send_message" | "send_email" | "delete" | "payment" | "git_push" | "credential_change";
26
+ export interface PolicyConfig {
27
+ yolo: boolean;
28
+ requireApprovalFor: RiskCategory[];
29
+ }
30
+ export interface BridgeConfig {
31
+ runtime: RuntimeConfig;
32
+ defaults: DefaultsConfig;
33
+ presets: Record<string, PresetConfig>;
34
+ policy: PolicyConfig;
35
+ }
36
+ export interface RunOptions {
37
+ prompt: string;
38
+ mode?: BridgeMode | undefined;
39
+ preset?: string | undefined;
40
+ contextFiles: string[];
41
+ yolo: boolean;
42
+ dryRun: boolean;
43
+ json: boolean;
44
+ profile?: string | undefined;
45
+ provider?: string | undefined;
46
+ model?: string | undefined;
47
+ maxTurns?: number | undefined;
48
+ source?: string | undefined;
49
+ }
50
+ export interface ContextDocument {
51
+ path: string;
52
+ content: string;
53
+ }
54
+ export interface EffectiveRun {
55
+ mode: BridgeMode;
56
+ requestedMode: BridgeMode;
57
+ presetName: string;
58
+ preset: PresetConfig;
59
+ prompt: string;
60
+ source: string;
61
+ profile?: string | undefined;
62
+ provider?: string | undefined;
63
+ model?: string | undefined;
64
+ maxTurns: number;
65
+ yolo: boolean;
66
+ detectedRisks: RiskCategory[];
67
+ contextDocuments: ContextDocument[];
68
+ }
69
+ export interface AdapterResult {
70
+ ok: boolean;
71
+ exitCode: number;
72
+ stdout: string;
73
+ stderr: string;
74
+ command: string[];
75
+ prompt: string;
76
+ dryRun: boolean;
77
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export const modes = ["plan", "draft", "execute", "request-approval"];
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,kBAAkB,CAAU,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const version: string;
@@ -0,0 +1,12 @@
1
+ import { readFileSync } from "node:fs";
2
+ function readVersion() {
3
+ try {
4
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
5
+ return pkg.version ?? "0.0.0";
6
+ }
7
+ catch {
8
+ return "0.0.0";
9
+ }
10
+ }
11
+ export const version = readVersion();
12
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvC,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAgB,CAAC;QACzG,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC"}
@@ -0,0 +1,105 @@
1
+ # Architecture
2
+
3
+ Hermes Action Bridge is deliberately small. It does not try to become another automation platform.
4
+
5
+ Its job is to let an external agent ask Hermes Agent to handle a task, while applying a local, configurable policy layer.
6
+
7
+ ```text
8
+ Claude Code / Codex / Cursor / CI / shell
9
+ |
10
+ v
11
+ hermes-action CLI or MCP server
12
+ |
13
+ v
14
+ Hermes Agent CLI session
15
+ |
16
+ v
17
+ Hermes skills, tools, browser automation, MCPs, cron jobs, messaging, APIs
18
+ ```
19
+
20
+ ## Core modules
21
+
22
+ - `src/cli.ts`: command-line interface.
23
+ - `src/config.ts`: YAML config loading and normalization.
24
+ - `src/policy.ts`: risk detection and mode switching.
25
+ - `src/prompt.ts`: prompt envelope sent to Hermes.
26
+ - `src/context.ts`: context file loading with size guardrails.
27
+ - `src/adapters/hermes-cli.ts`: Hermes CLI adapter.
28
+ - `src/mcp-server.ts`: minimal MCP server.
29
+ - `src/status.ts`: runtime availability check.
30
+ - `src/version.ts`: single source of truth for the package version.
31
+ - `src/doctor.ts`: environment checks for `hermes-action doctor`.
32
+
33
+ ## Native skills installer (`src/install/`)
34
+
35
+ The `install` / `uninstall` commands install an open-standard `SKILL.md` for Claude Code (`~/.claude/skills/`) and Codex (`~/.codex/skills/`). The CLI remains the deterministic execution layer; the skill only tells the agent when to delegate.
36
+
37
+ - `src/install/paths.ts`: cross-platform skill and instruction-file paths (injectable home/cwd).
38
+ - `src/install/templates.ts`: single source for the `SKILL.md` body, project hint, and MCP snippets.
39
+ - `src/install/managed-file.ts`: lock-based provenance for skill bundles (classify, atomic write, refuse-unmanaged).
40
+ - `src/install/marker-block.ts`: fail-safe marker block for the optional `CLAUDE.md` / `AGENTS.md` hint.
41
+ - `src/install/file-edit.ts`: shared read-edit-write helper used by the hint and the `.mcp.json` writer.
42
+ - `src/install/mcp-config.ts`: MCP snippets and the Claude Code `.mcp.json` merge/unmerge writer.
43
+ - `src/install/install-service.ts`: per-agent install/uninstall with failure isolation.
44
+
45
+ Defaults are conservative: never modify `CLAUDE.md` / `AGENTS.md` without `--project-hint`, never overwrite a file the installer did not generate, and stay idempotent.
46
+
47
+ ## Config precedence
48
+
49
+ The bridge merges config in this order:
50
+
51
+ ```text
52
+ built-in defaults -> user config -> project config -> CLI flags
53
+ ```
54
+
55
+ User config:
56
+
57
+ ```text
58
+ ~/.config/hermes-action/config.yaml
59
+ ```
60
+
61
+ Project config:
62
+
63
+ ```text
64
+ .hermes-action.yaml
65
+ ```
66
+
67
+ ## Modes
68
+
69
+ The bridge does not decide how Hermes completes the task. It only labels the requested behavior:
70
+
71
+ - `plan`: no side effects.
72
+ - `draft`: produce an artifact, no external side effects.
73
+ - `execute`: execute allowed actions.
74
+ - `request-approval`: prepare the action and ask for approval.
75
+
76
+ ## Policy
77
+
78
+ Policy is intentionally conservative and transparent.
79
+
80
+ When a prompt contains risky intent and `mode=execute`, the bridge changes the effective mode to `request-approval` unless YOLO is enabled.
81
+
82
+ The detected risks are included in the prompt envelope so Hermes knows why the mode changed.
83
+
84
+ ## YOLO
85
+
86
+ YOLO is explicit and local:
87
+
88
+ ```bash
89
+ hermes-action run --yolo --mode execute "..."
90
+ ```
91
+
92
+ It bypasses the bridge policy only. Hermes Agent still enforces its own rules and any tool/provider/platform approval flow.
93
+
94
+ ## MCP design
95
+
96
+ The MCP server exposes a small delegation surface instead of mirroring every Hermes tool.
97
+
98
+ Tools:
99
+
100
+ - `hermes_run`
101
+ - `hermes_plan`
102
+ - `hermes_presets`
103
+ - `hermes_status`
104
+
105
+ This keeps schema drift low and lets Hermes remain the action runtime.
@@ -0,0 +1,97 @@
1
+ # Functional Testing
2
+
3
+ This project has two validation layers.
4
+
5
+ ## Automated tests
6
+
7
+ Run:
8
+
9
+ ```bash
10
+ npm run check
11
+ ```
12
+
13
+ This executes:
14
+
15
+ - TypeScript build with `tsc`.
16
+ - Vitest unit tests.
17
+ - Functional CLI tests using a fake Hermes executable.
18
+
19
+ The fake Hermes tests verify:
20
+
21
+ - `hermes-action run` calls the configured Hermes command.
22
+ - Skills and toolsets are passed as CLI flags.
23
+ - Risky `execute` requests become `request-approval`.
24
+ - `--yolo` passes through and keeps `execute` mode.
25
+ - The prompt envelope contains mode, preset, YOLO state, detected risks, user request, and context.
26
+ - A failed Hermes run is surfaced through the MCP server as an error result (`isError`).
27
+
28
+ The installer tests verify (with injected home/cwd and temp `HOME`, no real agents required):
29
+
30
+ - `install` / `uninstall` are idempotent and never touch `CLAUDE.md` / `AGENTS.md` by default.
31
+ - `--dry-run` and `--print` write nothing; `--force` replaces a managed skill; a non-managed file is refused.
32
+ - `--project-hint` adds and removes a marker block without losing existing content.
33
+ - `--project` installs a project-local skill without creating the global one.
34
+ - `install mcp --write` merges and `uninstall mcp --write` unmerges the project `.mcp.json`, preserving other servers.
35
+ - `doctor` assembles its checks and JSON report, and `--probe` accepts the sentinel on stdout or stderr.
36
+ - The shipped `examples/*/SKILL.md` match the installer template exactly (no drift).
37
+
38
+ ## Live Hermes smoke test
39
+
40
+ Use a safe dry-run first:
41
+
42
+ ```bash
43
+ npm run build
44
+ node dist/cli.js run --dry-run --json "Return the word BRIDGE_OK only."
45
+ ```
46
+
47
+ Then run a minimal real Hermes call if you intentionally want to spend provider tokens:
48
+
49
+ ```bash
50
+ node dist/cli.js run \
51
+ --mode plan \
52
+ --max-turns 2 \
53
+ "Return the word BRIDGE_OK only. Do not use tools."
54
+ ```
55
+
56
+ ## Claude Code smoke test
57
+
58
+ After `npm link`, ask Claude Code to run:
59
+
60
+ ```bash
61
+ hermes-action run --dry-run --json "Return BRIDGE_OK only."
62
+ ```
63
+
64
+ Expected result:
65
+
66
+ - The command exits `0`.
67
+ - The JSON output contains a `command` array starting with `hermes chat -Q`.
68
+ - The prompt contains `Mode: plan`.
69
+
70
+ ## Codex smoke test
71
+
72
+ After `npm link`, ask Codex to run:
73
+
74
+ ```bash
75
+ hermes-action run --dry-run --json "Return BRIDGE_OK only."
76
+ ```
77
+
78
+ Expected result is the same as Claude Code.
79
+
80
+ ## MCP smoke test
81
+
82
+ Configure an MCP client with:
83
+
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "hermes-action": {
88
+ "command": "hermes-action",
89
+ "args": ["mcp"]
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ Call `hermes_status` first. It should report whether the configured Hermes command is available.
96
+
97
+ Then call `hermes_run` with `dryRun: true` before making real Hermes calls.
@@ -0,0 +1,21 @@
1
+ # Claude Code: Hermes Action Bridge
2
+
3
+ Install the native skill so Claude Code knows when to delegate real-world actions to Hermes:
4
+
5
+ ```bash
6
+ hermes-action install claude-code
7
+ ```
8
+
9
+ This writes `~/.claude/skills/hermes-action-bridge/SKILL.md`. Preview the exact content first with:
10
+
11
+ ```bash
12
+ hermes-action install claude-code --print
13
+ ```
14
+
15
+ To add a small marker-managed hint to this project's `CLAUDE.md` (instead of, or in addition to, the global skill):
16
+
17
+ ```bash
18
+ hermes-action install claude-code --project-hint
19
+ ```
20
+
21
+ The generated skill is shown in [SKILL.md](SKILL.md). Check your setup with `hermes-action doctor`. Remove everything with `hermes-action uninstall claude-code --project-hint`.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: hermes-action-bridge
3
+ description: Delegate external actions, Hermes skills, browser automation, messaging, cron jobs, and integrations to Hermes Agent through hermes-action. Use when a task needs capabilities outside the local coding session.
4
+ ---
5
+
6
+ # Hermes Action Bridge
7
+
8
+ Use this skill when a task needs capabilities outside the local coding session:
9
+ Hermes skills or memory, messaging platforms, browser automation, scheduled or
10
+ cron automation, external research, or any platform integration already
11
+ configured in Hermes Agent.
12
+
13
+ Do not reimplement those integrations here. Delegate them to Hermes through the
14
+ `hermes-action` CLI.
15
+
16
+ ## When to use
17
+
18
+ - The task requires a Hermes skill, tool, profile, or connected service.
19
+ - The task needs a real-world action that is not local code editing.
20
+ - The task needs research, messaging, browser, or scheduled automation Hermes owns.
21
+
22
+ ## Safe default
23
+
24
+ Ask Hermes for a plan first; it has no side effects:
25
+
26
+ ```bash
27
+ hermes-action run --mode plan "<describe what Hermes should do>"
28
+ ```
29
+
30
+ ## With context
31
+
32
+ Write a concise handoff to a file and pass it explicitly:
33
+
34
+ ```bash
35
+ hermes-action run --preset coding --context ./handoff.md "<request>"
36
+ ```
37
+
38
+ ## External side effects
39
+
40
+ For public posts, outbound email or messages, deletes, payments, credential
41
+ changes, or git pushes, require human approval:
42
+
43
+ ```bash
44
+ hermes-action run --mode request-approval "<request requiring side effects>"
45
+ ```
46
+
47
+ Never use `--yolo` unless the human explicitly asked for trusted local
48
+ execution. It bypasses only the bridge policy, not Hermes' own safety rules.
49
+
50
+ ## Verify setup
51
+
52
+ ```bash
53
+ hermes-action status
54
+ hermes-action run --dry-run --json "Return BRIDGE_OK only."
55
+ ```
@@ -0,0 +1,21 @@
1
+ # Codex: Hermes Action Bridge
2
+
3
+ Install the native skill so Codex knows when to delegate real-world actions to Hermes:
4
+
5
+ ```bash
6
+ hermes-action install codex
7
+ ```
8
+
9
+ This writes `~/.codex/skills/hermes-action-bridge/SKILL.md`. Preview the exact content first with:
10
+
11
+ ```bash
12
+ hermes-action install codex --print
13
+ ```
14
+
15
+ To add a small marker-managed hint to this project's `AGENTS.md` (instead of, or in addition to, the global skill):
16
+
17
+ ```bash
18
+ hermes-action install codex --project-hint
19
+ ```
20
+
21
+ The generated skill is shown in [SKILL.md](SKILL.md). Check your setup with `hermes-action doctor`. Remove everything with `hermes-action uninstall codex --project-hint`.