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.
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +28 -0
- package/LICENSE +21 -0
- package/README.md +323 -0
- package/dist/adapters/hermes-cli.d.ts +3 -0
- package/dist/adapters/hermes-cli.js +48 -0
- package/dist/adapters/hermes-cli.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +271 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +172 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +2 -0
- package/dist/context.js +15 -0
- package/dist/context.js.map +1 -0
- package/dist/doctor.d.ts +18 -0
- package/dist/doctor.js +77 -0
- package/dist/doctor.js.map +1 -0
- package/dist/install/file-edit.d.ts +12 -0
- package/dist/install/file-edit.js +18 -0
- package/dist/install/file-edit.js.map +1 -0
- package/dist/install/install-service.d.ts +26 -0
- package/dist/install/install-service.js +59 -0
- package/dist/install/install-service.js.map +1 -0
- package/dist/install/managed-file.d.ts +9 -0
- package/dist/install/managed-file.js +157 -0
- package/dist/install/managed-file.js.map +1 -0
- package/dist/install/marker-block.d.ts +10 -0
- package/dist/install/marker-block.js +61 -0
- package/dist/install/marker-block.js.map +1 -0
- package/dist/install/mcp-config.d.ts +15 -0
- package/dist/install/mcp-config.js +65 -0
- package/dist/install/mcp-config.js.map +1 -0
- package/dist/install/paths.d.ts +6 -0
- package/dist/install/paths.js +22 -0
- package/dist/install/paths.js.map +1 -0
- package/dist/install/templates.d.ts +16 -0
- package/dist/install/templates.js +86 -0
- package/dist/install/templates.js.map +1 -0
- package/dist/install/types.d.ts +18 -0
- package/dist/install/types.js +2 -0
- package/dist/install/types.js.map +1 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +69 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/policy.d.ts +9 -0
- package/dist/policy.js +25 -0
- package/dist/policy.js.map +1 -0
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.js +34 -0
- package/dist/prompt.js.map +1 -0
- package/dist/run.d.ts +2 -0
- package/dist/run.js +27 -0
- package/dist/run.js.map +1 -0
- package/dist/status.d.ts +8 -0
- package/dist/status.js +10 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/docs/ARCHITECTURE.md +105 -0
- package/docs/FUNCTIONAL-TESTS.md +97 -0
- package/examples/claude-code/CLAUDE.md +21 -0
- package/examples/claude-code/SKILL.md +55 -0
- package/examples/codex/AGENTS.md +21 -0
- package/examples/codex/SKILL.md +55 -0
- package/examples/configs/basic.yaml +36 -0
- package/examples/configs/strict.yaml +26 -0
- package/examples/configs/yolo.yaml +19 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-edit.js","sourceRoot":"","sources":["../../src/install/file-edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AASlE;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAA6C,EAAE,MAAe;IACxG,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1E,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM;YAAE,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACzH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACtE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC3G,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FileChange, InstallOptions, InstallScope, ManagedState, PathContext, SkillAgent } from "./types.js";
|
|
2
|
+
export type SkillTarget = SkillAgent | "all";
|
|
3
|
+
export interface ServiceOptions extends InstallOptions {
|
|
4
|
+
scope: InstallScope;
|
|
5
|
+
}
|
|
6
|
+
export interface TargetResult {
|
|
7
|
+
agent: SkillAgent;
|
|
8
|
+
ok: boolean;
|
|
9
|
+
changes: FileChange[];
|
|
10
|
+
}
|
|
11
|
+
export interface SkillPreview {
|
|
12
|
+
agent: SkillAgent;
|
|
13
|
+
files: {
|
|
14
|
+
path: string;
|
|
15
|
+
content: string;
|
|
16
|
+
}[];
|
|
17
|
+
}
|
|
18
|
+
export declare function installSkills(target: SkillTarget, ctx: PathContext, options: ServiceOptions): TargetResult[];
|
|
19
|
+
export declare function uninstallSkills(target: SkillTarget, ctx: PathContext, options: ServiceOptions): TargetResult[];
|
|
20
|
+
export declare function installHint(target: SkillTarget, ctx: PathContext, options: ServiceOptions): TargetResult[];
|
|
21
|
+
export declare function uninstallHint(target: SkillTarget, ctx: PathContext, options: ServiceOptions): TargetResult[];
|
|
22
|
+
export declare function previewSkills(target: SkillTarget, ctx: PathContext, scope: InstallScope): SkillPreview[];
|
|
23
|
+
export declare function skillStates(ctx: PathContext, scope: InstallScope): {
|
|
24
|
+
agent: SkillAgent;
|
|
25
|
+
state: ManagedState;
|
|
26
|
+
}[];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { instructionFile, skillDir } from "./paths.js";
|
|
3
|
+
import { projectHint, skillMarkdown } from "./templates.js";
|
|
4
|
+
import { classifyBundle, removeBundle, writeBundle } from "./managed-file.js";
|
|
5
|
+
import { removeBlock, upsertBlock } from "./marker-block.js";
|
|
6
|
+
import { applyFileEdit } from "./file-edit.js";
|
|
7
|
+
const allAgents = ["claude-code", "codex"];
|
|
8
|
+
function agentsFor(target) {
|
|
9
|
+
return target === "all" ? allAgents : [target];
|
|
10
|
+
}
|
|
11
|
+
function bundleAt(dir) {
|
|
12
|
+
return { dir, files: { "SKILL.md": skillMarkdown() } };
|
|
13
|
+
}
|
|
14
|
+
function bundleFor(agent, ctx, scope) {
|
|
15
|
+
return bundleAt(skillDir(agent, ctx, scope));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run one filesystem operation per agent, isolating failures so one agent cannot abort the others.
|
|
19
|
+
* The per-agent target path is resolved once and passed to `op`, and reused to report a thrown error.
|
|
20
|
+
*/
|
|
21
|
+
function perAgent(target, pathOf, op) {
|
|
22
|
+
return agentsFor(target).map((agent) => {
|
|
23
|
+
const path = pathOf(agent);
|
|
24
|
+
try {
|
|
25
|
+
const changes = op(path);
|
|
26
|
+
return { agent, ok: !changes.some((change) => change.action === "refused"), changes };
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
30
|
+
return { agent, ok: false, changes: [{ path, action: "refused", reason }] };
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function installSkills(target, ctx, options) {
|
|
35
|
+
return perAgent(target, (agent) => skillDir(agent, ctx, options.scope), (dir) => writeBundle(bundleAt(dir), options));
|
|
36
|
+
}
|
|
37
|
+
export function uninstallSkills(target, ctx, options) {
|
|
38
|
+
return perAgent(target, (agent) => skillDir(agent, ctx, options.scope), (dir) => removeBundle(dir, options));
|
|
39
|
+
}
|
|
40
|
+
export function installHint(target, ctx, options) {
|
|
41
|
+
return perAgent(target, (agent) => instructionFile(agent, ctx), (file) => [
|
|
42
|
+
applyFileEdit(file, (existing) => upsertBlock(existing, projectHint()), options.dryRun),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
export function uninstallHint(target, ctx, options) {
|
|
46
|
+
return perAgent(target, (agent) => instructionFile(agent, ctx), (file) => [
|
|
47
|
+
applyFileEdit(file, (existing) => (existing === null ? { content: "", action: "unchanged" } : removeBlock(existing)), options.dryRun),
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
export function previewSkills(target, ctx, scope) {
|
|
51
|
+
return agentsFor(target).map((agent) => {
|
|
52
|
+
const bundle = bundleFor(agent, ctx, scope);
|
|
53
|
+
return { agent, files: Object.entries(bundle.files).map(([name, content]) => ({ path: join(bundle.dir, name), content })) };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function skillStates(ctx, scope) {
|
|
57
|
+
return allAgents.map((agent) => ({ agent, state: classifyBundle(bundleFor(agent, ctx, scope)) }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=install-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-service.js","sourceRoot":"","sources":["../../src/install/install-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAsB,MAAM,mBAAmB,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAoB/C,MAAM,SAAS,GAAiB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAEzD,SAAS,SAAS,CAAC,MAAmB;IACpC,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,SAAS,CAAC,KAAiB,EAAE,GAAgB,EAAE,KAAmB;IACzE,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,MAAmB,EAAE,MAAqC,EAAE,EAAkC;IAC9G,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;QACxF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,GAAgB,EAAE,OAAuB;IAC1F,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AACxH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,GAAgB,EAAE,OAAuB;IAC5F,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/G,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAmB,EAAE,GAAgB,EAAE,OAAuB;IACxF,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QACxE,aAAa,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC;KACxF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,GAAgB,EAAE,OAAuB;IAC1F,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QACxE,aAAa,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC;KACtI,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,GAAgB,EAAE,KAAmB;IACtF,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;IAC9H,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAgB,EAAE,KAAmB;IAC/D,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FileChange, InstallOptions, ManagedState } from "./types.js";
|
|
2
|
+
export interface ManagedBundle {
|
|
3
|
+
dir: string;
|
|
4
|
+
/** Filename (relative to `dir`) -> file content. */
|
|
5
|
+
files: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function classifyBundle(bundle: ManagedBundle): ManagedState;
|
|
8
|
+
export declare function writeBundle(bundle: ManagedBundle, options: InstallOptions): FileChange[];
|
|
9
|
+
export declare function removeBundle(dir: string, options: InstallOptions): FileChange[];
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { contentVersion } from "./templates.js";
|
|
4
|
+
/**
|
|
5
|
+
* Provenance lockfile written alongside the managed content. Its presence marks the directory as
|
|
6
|
+
* ours; the recorded per-file hashes detect later hand-edits, and `version` detects template drift.
|
|
7
|
+
*/
|
|
8
|
+
const lockName = ".hermes-action-managed.json";
|
|
9
|
+
function hashFiles(files) {
|
|
10
|
+
return Object.fromEntries(Object.entries(files).map(([name, content]) => [name, contentVersion(content)]));
|
|
11
|
+
}
|
|
12
|
+
function versionFromHashes(hashes) {
|
|
13
|
+
const canonical = Object.entries(hashes).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
14
|
+
return contentVersion(JSON.stringify(canonical));
|
|
15
|
+
}
|
|
16
|
+
function bundleVersion(files) {
|
|
17
|
+
return versionFromHashes(hashFiles(files));
|
|
18
|
+
}
|
|
19
|
+
function readLock(dir) {
|
|
20
|
+
const lockPath = join(dir, lockName);
|
|
21
|
+
if (!existsSync(lockPath))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
25
|
+
if (parsed === null || typeof parsed !== "object")
|
|
26
|
+
return null;
|
|
27
|
+
const lock = parsed;
|
|
28
|
+
if (typeof lock.version !== "string" || typeof lock.files !== "object" || lock.files === null)
|
|
29
|
+
return null;
|
|
30
|
+
const files = lock.files;
|
|
31
|
+
if (!Object.values(files).every((value) => typeof value === "string"))
|
|
32
|
+
return null;
|
|
33
|
+
return { version: lock.version, files: files };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Inspection helpers never throw: an unreadable file is reported as "not matching" rather than crashing. */
|
|
40
|
+
function diskHash(path) {
|
|
41
|
+
try {
|
|
42
|
+
return contentVersion(readFileSync(path, "utf8"));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function dirHasEntries(dir) {
|
|
49
|
+
try {
|
|
50
|
+
return readdirSync(dir).length > 0;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** True when every file recorded in the lock is still on disk with its recorded content. */
|
|
57
|
+
function lockIntact(dir, lock) {
|
|
58
|
+
return Object.entries(lock.files).every(([name, hash]) => diskHash(join(dir, name)) === hash);
|
|
59
|
+
}
|
|
60
|
+
export function classifyBundle(bundle) {
|
|
61
|
+
const lock = readLock(bundle.dir);
|
|
62
|
+
if (lock) {
|
|
63
|
+
if (!lockIntact(bundle.dir, lock))
|
|
64
|
+
return "user-modified";
|
|
65
|
+
return lock.version === bundleVersion(bundle.files) ? "current" : "stale";
|
|
66
|
+
}
|
|
67
|
+
// No readable lock: a present-but-unparseable lockfile, or any of our content files, means
|
|
68
|
+
// something we did not write is here -> refuse (foreign). An empty directory means absent.
|
|
69
|
+
const lockFilePresent = existsSync(join(bundle.dir, lockName));
|
|
70
|
+
const anyContentPresent = Object.keys(bundle.files).some((name) => existsSync(join(bundle.dir, name)));
|
|
71
|
+
return lockFilePresent || anyContentPresent ? "foreign" : "absent";
|
|
72
|
+
}
|
|
73
|
+
// Write-path failures (permission denied, disk full) intentionally propagate to the caller, which
|
|
74
|
+
// reports them per target; only best-effort directory cleanup is swallowed.
|
|
75
|
+
function atomicWrite(filePath, content) {
|
|
76
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
77
|
+
const tmp = `${filePath}.hab-tmp`;
|
|
78
|
+
writeFileSync(tmp, content, "utf8");
|
|
79
|
+
renameSync(tmp, filePath);
|
|
80
|
+
}
|
|
81
|
+
function writeBundleFiles(bundle) {
|
|
82
|
+
const previous = readLock(bundle.dir);
|
|
83
|
+
const hashes = hashFiles(bundle.files);
|
|
84
|
+
for (const [name, content] of Object.entries(bundle.files)) {
|
|
85
|
+
atomicWrite(join(bundle.dir, name), content);
|
|
86
|
+
}
|
|
87
|
+
const lock = { version: versionFromHashes(hashes), files: hashes };
|
|
88
|
+
atomicWrite(join(bundle.dir, lockName), `${JSON.stringify(lock, null, 2)}\n`);
|
|
89
|
+
removeDroppedFiles(bundle.dir, previous, bundle.files);
|
|
90
|
+
}
|
|
91
|
+
/** Delete files recorded by a previous version that the current bundle no longer ships. */
|
|
92
|
+
function removeDroppedFiles(dir, previous, current) {
|
|
93
|
+
if (!previous)
|
|
94
|
+
return;
|
|
95
|
+
for (const name of Object.keys(previous.files)) {
|
|
96
|
+
if (name in current)
|
|
97
|
+
continue;
|
|
98
|
+
const path = join(dir, name);
|
|
99
|
+
if (existsSync(path))
|
|
100
|
+
unlinkSync(path);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function changes(bundle, action) {
|
|
104
|
+
return Object.keys(bundle.files).map((name) => ({ path: join(bundle.dir, name), action }));
|
|
105
|
+
}
|
|
106
|
+
function refusal(dir, reason) {
|
|
107
|
+
return [{ path: dir, action: "refused", reason }];
|
|
108
|
+
}
|
|
109
|
+
export function writeBundle(bundle, options) {
|
|
110
|
+
const state = classifyBundle(bundle);
|
|
111
|
+
if (state === "current")
|
|
112
|
+
return changes(bundle, "unchanged");
|
|
113
|
+
if (state === "foreign") {
|
|
114
|
+
return refusal(bundle.dir, `a non-managed file already exists in ${bundle.dir}; refusing to overwrite`);
|
|
115
|
+
}
|
|
116
|
+
if ((state === "stale" || state === "user-modified") && !options.force) {
|
|
117
|
+
const why = state === "stale" ? "is out of date" : "was modified locally";
|
|
118
|
+
return refusal(bundle.dir, `the installed skill ${why}; re-run with --force to overwrite`);
|
|
119
|
+
}
|
|
120
|
+
const action = state === "absent" ? "created" : "updated";
|
|
121
|
+
if (!options.dryRun)
|
|
122
|
+
writeBundleFiles(bundle);
|
|
123
|
+
return changes(bundle, action);
|
|
124
|
+
}
|
|
125
|
+
export function removeBundle(dir, options) {
|
|
126
|
+
const lock = readLock(dir);
|
|
127
|
+
if (!lock) {
|
|
128
|
+
if (dirHasEntries(dir))
|
|
129
|
+
return refusal(dir, `${dir} is not a managed skill; refusing to remove`);
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
if (!lockIntact(dir, lock) && !options.force) {
|
|
133
|
+
return refusal(dir, `the installed skill was modified locally; re-run with --force to remove`);
|
|
134
|
+
}
|
|
135
|
+
if (!options.dryRun)
|
|
136
|
+
removeBundleFiles(dir, lock);
|
|
137
|
+
return [{ path: dir, action: "removed" }];
|
|
138
|
+
}
|
|
139
|
+
function removeBundleFiles(dir, lock) {
|
|
140
|
+
for (const name of Object.keys(lock.files)) {
|
|
141
|
+
const path = join(dir, name);
|
|
142
|
+
if (existsSync(path))
|
|
143
|
+
unlinkSync(path);
|
|
144
|
+
}
|
|
145
|
+
const lockPath = join(dir, lockName);
|
|
146
|
+
if (existsSync(lockPath))
|
|
147
|
+
unlinkSync(lockPath);
|
|
148
|
+
if (!dirHasEntries(dir)) {
|
|
149
|
+
try {
|
|
150
|
+
rmdirSync(dir);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Best effort: leaving an empty directory behind is harmless.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=managed-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"managed-file.js","sourceRoot":"","sources":["../../src/install/managed-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FileAction } from "./types.js";
|
|
2
|
+
export interface MarkerResult {
|
|
3
|
+
content: string;
|
|
4
|
+
action: FileAction;
|
|
5
|
+
reason?: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
/** Insert or update the single managed block, preserving all content outside it. */
|
|
8
|
+
export declare function upsertBlock(existing: string | null, innerText: string): MarkerResult;
|
|
9
|
+
/** Remove the managed block, tidying only the seam and leaving all other content byte-for-byte. */
|
|
10
|
+
export declare function removeBlock(existing: string): MarkerResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const startMarker = "<!-- hermes-action-bridge:start -->";
|
|
2
|
+
const endMarker = "<!-- hermes-action-bridge:end -->";
|
|
3
|
+
function block(innerText) {
|
|
4
|
+
return `${startMarker}\n${innerText}\n${endMarker}`;
|
|
5
|
+
}
|
|
6
|
+
function countOccurrences(haystack, needle) {
|
|
7
|
+
return haystack.split(needle).length - 1;
|
|
8
|
+
}
|
|
9
|
+
/** Reject ambiguous marker states rather than guessing block boundaries (data-loss guard). */
|
|
10
|
+
function markersMalformed(content) {
|
|
11
|
+
const starts = countOccurrences(content, startMarker);
|
|
12
|
+
const ends = countOccurrences(content, endMarker);
|
|
13
|
+
if (starts !== ends || starts > 1)
|
|
14
|
+
return true;
|
|
15
|
+
return starts === 1 && content.indexOf(startMarker) > content.indexOf(endMarker);
|
|
16
|
+
}
|
|
17
|
+
/** Insert or update the single managed block, preserving all content outside it. */
|
|
18
|
+
export function upsertBlock(existing, innerText) {
|
|
19
|
+
const desired = block(innerText);
|
|
20
|
+
if (existing === null)
|
|
21
|
+
return { content: `${desired}\n`, action: "created" };
|
|
22
|
+
if (markersMalformed(existing)) {
|
|
23
|
+
return { content: existing, action: "refused", reason: "existing hermes-action-bridge markers are malformed" };
|
|
24
|
+
}
|
|
25
|
+
if (existing.includes(startMarker)) {
|
|
26
|
+
const before = existing.slice(0, existing.indexOf(startMarker));
|
|
27
|
+
const after = existing.slice(existing.indexOf(endMarker) + endMarker.length);
|
|
28
|
+
const content = before + desired + after;
|
|
29
|
+
return { content, action: content === existing ? "unchanged" : "updated" };
|
|
30
|
+
}
|
|
31
|
+
if (existing.trim() === "")
|
|
32
|
+
return { content: `${desired}\n`, action: "updated" };
|
|
33
|
+
// Append after the existing content, preserving its bytes, with exactly one blank line before the block.
|
|
34
|
+
const blankLineGap = "\n".repeat(Math.max(0, 2 - trailingNewlines(existing)));
|
|
35
|
+
return { content: `${existing}${blankLineGap}${desired}\n`, action: "updated" };
|
|
36
|
+
}
|
|
37
|
+
/** Remove the managed block, tidying only the seam and leaving all other content byte-for-byte. */
|
|
38
|
+
export function removeBlock(existing) {
|
|
39
|
+
if (markersMalformed(existing)) {
|
|
40
|
+
return { content: existing, action: "refused", reason: "existing hermes-action-bridge markers are malformed" };
|
|
41
|
+
}
|
|
42
|
+
if (!existing.includes(startMarker))
|
|
43
|
+
return { content: existing, action: "unchanged" };
|
|
44
|
+
const before = existing.slice(0, existing.indexOf(startMarker)).replace(/(\r?\n)+$/, "");
|
|
45
|
+
const after = existing.slice(existing.indexOf(endMarker) + endMarker.length).replace(/^(\r?\n)+/, "");
|
|
46
|
+
return { content: joinSeam(before, after), action: "removed" };
|
|
47
|
+
}
|
|
48
|
+
/** Re-join content around a removed block: one blank line if it sat between sections, else a clean edge. */
|
|
49
|
+
function joinSeam(before, after) {
|
|
50
|
+
if (before === "")
|
|
51
|
+
return after;
|
|
52
|
+
if (after === "")
|
|
53
|
+
return `${before}\n`;
|
|
54
|
+
return `${before}\n\n${after}`;
|
|
55
|
+
}
|
|
56
|
+
/** Count trailing line breaks, tolerating both LF and CRLF endings. */
|
|
57
|
+
function trailingNewlines(text) {
|
|
58
|
+
const tail = text.slice(text.replace(/(\r?\n)+$/, "").length);
|
|
59
|
+
return tail === "" ? 0 : tail.split(/\r?\n/).length - 1;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=marker-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marker-block.js","sourceRoot":"","sources":["../../src/install/marker-block.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,qCAAqC,CAAC;AAC1D,MAAM,SAAS,GAAG,mCAAmC,CAAC;AAQtD,SAAS,KAAK,CAAC,SAAiB;IAC9B,OAAO,GAAG,WAAW,KAAK,SAAS,KAAK,SAAS,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB,EAAE,MAAc;IACxD,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,8FAA8F;AAC9F,SAAS,gBAAgB,CAAC,OAAe;IACvC,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAClD,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACnF,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,QAAuB,EAAE,SAAiB;IACpE,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC7E,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,qDAAqD,EAAE,CAAC;IACjH,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7E,MAAM,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;QACzC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAC7E,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClF,yGAAyG;IACzG,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC9E,OAAO,EAAE,OAAO,EAAE,GAAG,QAAQ,GAAG,YAAY,GAAG,OAAO,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAClF,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,qDAAqD,EAAE,CAAC;IACjH,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACvF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACzF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACtG,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACjE,CAAC;AAED,4GAA4G;AAC5G,SAAS,QAAQ,CAAC,MAAc,EAAE,KAAa;IAC7C,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAChC,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,GAAG,MAAM,IAAI,CAAC;IACvC,OAAO,GAAG,MAAM,OAAO,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,uEAAuE;AACvE,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC9D,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type EditResult } from "./file-edit.js";
|
|
2
|
+
import type { FileChange } from "./types.js";
|
|
3
|
+
export interface McpClientSnippet {
|
|
4
|
+
client: string;
|
|
5
|
+
location: string;
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
/** Copy-paste MCP config per client. Printing is the default; only the Claude Code writer mutates a file. */
|
|
9
|
+
export declare function mcpSnippets(): McpClientSnippet[];
|
|
10
|
+
/** Add the hermes-action server to a Claude Code `.mcp.json`, preserving every other key. */
|
|
11
|
+
export declare function mergeMcpJson(existing: string | null): EditResult;
|
|
12
|
+
/** Remove the hermes-action server from a Claude Code `.mcp.json`, leaving other servers in place. */
|
|
13
|
+
export declare function unmergeMcpJson(existing: string | null): EditResult;
|
|
14
|
+
export declare function writeMcpJson(file: string, dryRun: boolean): FileChange;
|
|
15
|
+
export declare function removeMcpJson(file: string, dryRun: boolean): FileChange;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { mcpServerEntry, mcpServerName, mcpSnippetCodexToml, mcpSnippetJson } from "./templates.js";
|
|
2
|
+
import { applyFileEdit } from "./file-edit.js";
|
|
3
|
+
/** Copy-paste MCP config per client. Printing is the default; only the Claude Code writer mutates a file. */
|
|
4
|
+
export function mcpSnippets() {
|
|
5
|
+
return [
|
|
6
|
+
{ client: "Claude Code / Cursor / VS Code", location: "project .mcp.json (or ~/.cursor/mcp.json)", content: mcpSnippetJson() },
|
|
7
|
+
{ client: "Codex", location: "~/.codex/config.toml", content: mcpSnippetCodexToml() },
|
|
8
|
+
];
|
|
9
|
+
}
|
|
10
|
+
function isObject(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function render(value) {
|
|
14
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
15
|
+
}
|
|
16
|
+
/** Stable serialization (object keys sorted) so the idempotency check ignores key order. */
|
|
17
|
+
function canonical(value) {
|
|
18
|
+
return JSON.stringify(value, (_key, val) => isObject(val) ? Object.fromEntries(Object.entries(val).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) : val);
|
|
19
|
+
}
|
|
20
|
+
/** Add the hermes-action server to a Claude Code `.mcp.json`, preserving every other key. */
|
|
21
|
+
export function mergeMcpJson(existing) {
|
|
22
|
+
if (existing === null)
|
|
23
|
+
return { content: render({ mcpServers: { [mcpServerName]: mcpServerEntry } }), action: "created" };
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(existing);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return { content: existing, action: "refused", reason: "existing .mcp.json is not valid JSON" };
|
|
30
|
+
}
|
|
31
|
+
if (!isObject(parsed))
|
|
32
|
+
return { content: existing, action: "refused", reason: "existing .mcp.json is not a JSON object" };
|
|
33
|
+
if ("mcpServers" in parsed && !isObject(parsed.mcpServers)) {
|
|
34
|
+
return { content: existing, action: "refused", reason: "existing .mcp.json has a non-object mcpServers; refusing to overwrite" };
|
|
35
|
+
}
|
|
36
|
+
const servers = isObject(parsed.mcpServers) ? parsed.mcpServers : {};
|
|
37
|
+
if (canonical(servers[mcpServerName]) === canonical(mcpServerEntry))
|
|
38
|
+
return { content: existing, action: "unchanged" };
|
|
39
|
+
return { content: render({ ...parsed, mcpServers: { ...servers, [mcpServerName]: mcpServerEntry } }), action: "updated" };
|
|
40
|
+
}
|
|
41
|
+
/** Remove the hermes-action server from a Claude Code `.mcp.json`, leaving other servers in place. */
|
|
42
|
+
export function unmergeMcpJson(existing) {
|
|
43
|
+
if (existing === null)
|
|
44
|
+
return { content: "", action: "unchanged" };
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(existing);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return { content: existing, action: "refused", reason: "existing .mcp.json is not valid JSON" };
|
|
51
|
+
}
|
|
52
|
+
if (!isObject(parsed) || !isObject(parsed.mcpServers) || !(mcpServerName in parsed.mcpServers)) {
|
|
53
|
+
return { content: existing, action: "unchanged" };
|
|
54
|
+
}
|
|
55
|
+
const servers = { ...parsed.mcpServers };
|
|
56
|
+
delete servers[mcpServerName];
|
|
57
|
+
return { content: render({ ...parsed, mcpServers: servers }), action: "removed" };
|
|
58
|
+
}
|
|
59
|
+
export function writeMcpJson(file, dryRun) {
|
|
60
|
+
return applyFileEdit(file, mergeMcpJson, dryRun);
|
|
61
|
+
}
|
|
62
|
+
export function removeMcpJson(file, dryRun) {
|
|
63
|
+
return applyFileEdit(file, unmergeMcpJson, dryRun);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=mcp-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../src/install/mcp-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InstallScope, PathContext, SkillAgent } from "./types.js";
|
|
2
|
+
export declare const skillName = "hermes-action-bridge";
|
|
3
|
+
/** Directory that holds the bridge skill for an agent, e.g. `~/.claude/skills/hermes-action-bridge`. */
|
|
4
|
+
export declare function skillDir(agent: SkillAgent, ctx: PathContext, scope: InstallScope): string;
|
|
5
|
+
/** Project instruction file for an agent (`CLAUDE.md` or `AGENTS.md`), always resolved against the project cwd. */
|
|
6
|
+
export declare function instructionFile(agent: SkillAgent, ctx: PathContext): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
export const skillName = "hermes-action-bridge";
|
|
3
|
+
const agentConfigDir = {
|
|
4
|
+
"claude-code": ".claude",
|
|
5
|
+
codex: ".codex",
|
|
6
|
+
};
|
|
7
|
+
const agentInstructionFile = {
|
|
8
|
+
"claude-code": "CLAUDE.md",
|
|
9
|
+
codex: "AGENTS.md",
|
|
10
|
+
};
|
|
11
|
+
function scopeRoot(ctx, scope) {
|
|
12
|
+
return scope === "global" ? ctx.homeDir : ctx.cwd;
|
|
13
|
+
}
|
|
14
|
+
/** Directory that holds the bridge skill for an agent, e.g. `~/.claude/skills/hermes-action-bridge`. */
|
|
15
|
+
export function skillDir(agent, ctx, scope) {
|
|
16
|
+
return join(scopeRoot(ctx, scope), agentConfigDir[agent], "skills", skillName);
|
|
17
|
+
}
|
|
18
|
+
/** Project instruction file for an agent (`CLAUDE.md` or `AGENTS.md`), always resolved against the project cwd. */
|
|
19
|
+
export function instructionFile(agent, ctx) {
|
|
20
|
+
return join(ctx.cwd, agentInstructionFile[agent]);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/install/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,CAAC,MAAM,SAAS,GAAG,sBAAsB,CAAC;AAEhD,MAAM,cAAc,GAA+B;IACjD,aAAa,EAAE,SAAS;IACxB,KAAK,EAAE,QAAQ;CAChB,CAAC;AAEF,MAAM,oBAAoB,GAA+B;IACvD,aAAa,EAAE,WAAW;IAC1B,KAAK,EAAE,WAAW;CACnB,CAAC;AAEF,SAAS,SAAS,CAAC,GAAgB,EAAE,KAAmB;IACtD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;AACpD,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,QAAQ,CAAC,KAAiB,EAAE,GAAgB,EAAE,KAAmB;IAC/E,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AACjF,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,eAAe,CAAC,KAAiB,EAAE,GAAgB;IACjE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Short, stable content hash used to detect drift between an installed file and the current template. */
|
|
2
|
+
export declare function contentVersion(content: string): string;
|
|
3
|
+
/** Shared `SKILL.md` body. Identical for Claude Code and Codex (SKILL.md is an open standard). */
|
|
4
|
+
export declare function skillMarkdown(): string;
|
|
5
|
+
/** Inner text of the marker-managed hint added to CLAUDE.md / AGENTS.md (opt-in only). */
|
|
6
|
+
export declare function projectHint(): string;
|
|
7
|
+
/** Canonical MCP server entry for hermes-action, shared by the printed snippets and the .mcp.json writer. */
|
|
8
|
+
export declare const mcpServerName = "hermes-action";
|
|
9
|
+
export declare const mcpServerEntry: {
|
|
10
|
+
readonly command: "hermes-action";
|
|
11
|
+
readonly args: readonly ["mcp"];
|
|
12
|
+
};
|
|
13
|
+
/** MCP config snippet for JSON clients (Claude Code, Cursor, VS Code). */
|
|
14
|
+
export declare function mcpSnippetJson(): string;
|
|
15
|
+
/** MCP config snippet for Codex (`~/.codex/config.toml`, TOML format). */
|
|
16
|
+
export declare function mcpSnippetCodexToml(): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/** Short, stable content hash used to detect drift between an installed file and the current template. */
|
|
3
|
+
export function contentVersion(content) {
|
|
4
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
5
|
+
}
|
|
6
|
+
/** Shared `SKILL.md` body. Identical for Claude Code and Codex (SKILL.md is an open standard). */
|
|
7
|
+
export function skillMarkdown() {
|
|
8
|
+
return `---
|
|
9
|
+
name: hermes-action-bridge
|
|
10
|
+
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.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Hermes Action Bridge
|
|
14
|
+
|
|
15
|
+
Use this skill when a task needs capabilities outside the local coding session:
|
|
16
|
+
Hermes skills or memory, messaging platforms, browser automation, scheduled or
|
|
17
|
+
cron automation, external research, or any platform integration already
|
|
18
|
+
configured in Hermes Agent.
|
|
19
|
+
|
|
20
|
+
Do not reimplement those integrations here. Delegate them to Hermes through the
|
|
21
|
+
\`hermes-action\` CLI.
|
|
22
|
+
|
|
23
|
+
## When to use
|
|
24
|
+
|
|
25
|
+
- The task requires a Hermes skill, tool, profile, or connected service.
|
|
26
|
+
- The task needs a real-world action that is not local code editing.
|
|
27
|
+
- The task needs research, messaging, browser, or scheduled automation Hermes owns.
|
|
28
|
+
|
|
29
|
+
## Safe default
|
|
30
|
+
|
|
31
|
+
Ask Hermes for a plan first; it has no side effects:
|
|
32
|
+
|
|
33
|
+
\`\`\`bash
|
|
34
|
+
hermes-action run --mode plan "<describe what Hermes should do>"
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
## With context
|
|
38
|
+
|
|
39
|
+
Write a concise handoff to a file and pass it explicitly:
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
hermes-action run --preset coding --context ./handoff.md "<request>"
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
## External side effects
|
|
46
|
+
|
|
47
|
+
For public posts, outbound email or messages, deletes, payments, credential
|
|
48
|
+
changes, or git pushes, require human approval:
|
|
49
|
+
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
hermes-action run --mode request-approval "<request requiring side effects>"
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
Never use \`--yolo\` unless the human explicitly asked for trusted local
|
|
55
|
+
execution. It bypasses only the bridge policy, not Hermes' own safety rules.
|
|
56
|
+
|
|
57
|
+
## Verify setup
|
|
58
|
+
|
|
59
|
+
\`\`\`bash
|
|
60
|
+
hermes-action status
|
|
61
|
+
hermes-action run --dry-run --json "Return BRIDGE_OK only."
|
|
62
|
+
\`\`\`
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
/** Inner text of the marker-managed hint added to CLAUDE.md / AGENTS.md (opt-in only). */
|
|
66
|
+
export function projectHint() {
|
|
67
|
+
return [
|
|
68
|
+
"When a task requires Hermes Agent capabilities (skills, memory, messaging,",
|
|
69
|
+
"browser automation, cron, research, or platform integrations), use the",
|
|
70
|
+
"`hermes-action-bridge` skill and delegate through `hermes-action`. Prefer",
|
|
71
|
+
"`--mode plan` or `--mode request-approval`; never use `--yolo` unless the",
|
|
72
|
+
"human explicitly requested trusted local execution.",
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
/** Canonical MCP server entry for hermes-action, shared by the printed snippets and the .mcp.json writer. */
|
|
76
|
+
export const mcpServerName = "hermes-action";
|
|
77
|
+
export const mcpServerEntry = { command: "hermes-action", args: ["mcp"] };
|
|
78
|
+
/** MCP config snippet for JSON clients (Claude Code, Cursor, VS Code). */
|
|
79
|
+
export function mcpSnippetJson() {
|
|
80
|
+
return `${JSON.stringify({ mcpServers: { [mcpServerName]: mcpServerEntry } }, null, 2)}\n`;
|
|
81
|
+
}
|
|
82
|
+
/** MCP config snippet for Codex (`~/.codex/config.toml`, TOML format). */
|
|
83
|
+
export function mcpSnippetCodexToml() {
|
|
84
|
+
return ['[mcp_servers.hermes-action]', 'command = "hermes-action"', 'args = ["mcp"]', ""].join("\n");
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/install/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,0GAA0G;AAC1G,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDR,CAAC;AACF,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,4EAA4E;QAC5E,wEAAwE;QACxE,2EAA2E;QAC3E,2EAA2E;QAC3E,qDAAqD;KACtD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,6GAA6G;AAC7G,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAW,CAAC;AAEnF,0EAA0E;AAC1E,MAAM,UAAU,cAAc;IAC5B,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC7F,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,mBAAmB;IACjC,OAAO,CAAC,6BAA6B,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvG,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type SkillAgent = "claude-code" | "codex";
|
|
2
|
+
export type InstallTarget = SkillAgent | "all" | "mcp";
|
|
3
|
+
export type InstallScope = "global" | "project";
|
|
4
|
+
export type ManagedState = "absent" | "current" | "stale" | "user-modified" | "foreign";
|
|
5
|
+
export type FileAction = "created" | "updated" | "unchanged" | "removed" | "refused";
|
|
6
|
+
export interface FileChange {
|
|
7
|
+
path: string;
|
|
8
|
+
action: FileAction;
|
|
9
|
+
reason?: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface PathContext {
|
|
12
|
+
homeDir: string;
|
|
13
|
+
cwd: string;
|
|
14
|
+
}
|
|
15
|
+
export interface InstallOptions {
|
|
16
|
+
force: boolean;
|
|
17
|
+
dryRun: boolean;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/install/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startMcpServer(configPath?: string): Promise<void>;
|