set-prompt 0.8.0 → 0.8.2
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 +34 -0
- package/README.md +2 -2
- package/dist/index.js +135 -21
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,40 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.8.2] - 2026-05-02
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Hermes `register_skill` argument types** — fixed `'str' object has no attribute 'exists'` error that surfaced after enabling the plugin in Hermes. The generated `__init__.py` was passing `str(skill_path)` (the skill directory as a string), but Hermes's `ctx.register_skill(name, path)` expects:
|
|
11
|
+
1. A `pathlib.Path` object (not a string)
|
|
12
|
+
2. The path to `SKILL.md` itself (not the parent directory)
|
|
13
|
+
Verified against the official "Build a Hermes plugin" guide example. Now passes `skill_path / "SKILL.md"` as a `Path`.
|
|
14
|
+
- **Hermes command handler signature** — handler is now defined as `handler(raw_args: str = "") -> Optional[str]` matching the documented Hermes signature (previously `handler(_args=None)`).
|
|
15
|
+
- **Slash commands silent in gateway/TUI mode** — `ctx.inject_message()` is documented as **CLI-mode only** (returns `False` in gateway mode). The handler now falls back to **returning the markdown body** as the command's response when `inject_message` is unavailable, so the content is still visible to the user instead of being silently dropped.
|
|
16
|
+
- **`register_command` now uses keyword arguments** (`handler=`, `description=`) to match the official Hermes plugin examples (e.g. `disk-cleanup`) and stay robust if Hermes reorders positional parameters.
|
|
17
|
+
|
|
18
|
+
### Notes
|
|
19
|
+
- After upgrading, re-run `set-prompt link hermes` to regenerate `__init__.py`, then restart Hermes. The plugin should now load with `✓ set-prompt v1.0` and skills become loadable via `skill_view("set-prompt:<name>")` (plugin skills are opt-in explicit loads — they do not appear in Hermes's auto-listed `available_skills` index).
|
|
20
|
+
- Hook callbacks remain `def runner(*args, **kwargs)` — Hermes documents that all hook callbacks should accept `**kwargs` for forward compatibility, and the bridge's role is purely to forward the payload to the user-defined hook command via stdin JSON.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## [0.8.1] - 2026-05-02
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **Hermes auto-enable** — `link hermes` now writes `set-prompt` into the existing `~/.hermes/config.yaml` automatically, matching Claude Code / Codex behavior. Previously the file was left untouched and only a manual-merge snippet was printed, which left the plugin detected-but-disabled (`× set-prompt — not enabled in config`).
|
|
28
|
+
- Existing `plugins.enabled` list → `set-prompt` is appended (other entries preserved).
|
|
29
|
+
- `plugins` map exists but no `enabled` key → `enabled: [set-prompt]` is added.
|
|
30
|
+
- `plugins` key missing entirely → `plugins.enabled: [set-prompt]` is added.
|
|
31
|
+
- YAML comments and formatting are preserved (AST-based modification via `yaml` package).
|
|
32
|
+
- Atomic write with timestamped backup + rollback on failure (same pattern as `link claudecode` / `link codex`).
|
|
33
|
+
- **`unlinkHermes` surgical removal** — for user-authored `config.yaml` (no `# Generated by set-prompt` header), only the `- set-prompt` entry is removed; the rest of the file is preserved. Auto-generated configs are still deleted whole.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- `yaml` (eemeli/yaml) dependency for AST-based YAML editing.
|
|
37
|
+
- Tests for the new auto-enable branches: `plugins` key absent, `plugins.enabled` absent, comment preservation, and `unlinkHermes` surgical removal (17 cases total in `link-hermes.test.ts`).
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
7
41
|
## [0.8.0] - 2026-05-02
|
|
8
42
|
|
|
9
43
|
### Added
|
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ The interactive mode shows all agents with their current state. **Check to link,
|
|
|
123
123
|
|
|
124
124
|
> **Note on Gemini CLI**: Skills follow the standard `skills/<name>/SKILL.md` pattern. Commands use `.toml` format (not `.md`) and agents use `.md` with YAML frontmatter. Files in your repo's `commands/` must be TOML for Gemini CLI to recognize them. **Agents have strict frontmatter validation** — unknown keys (e.g. `allowed-tools`, `color`, `mode` from other platforms) cause Gemini CLI to reject the agent. See `SET_PROMPT_GUIDE.md` for the allowed keys.
|
|
125
125
|
|
|
126
|
-
> **Note on Hermes**: Hermes plugins must register skills/commands/hooks programmatically — directory drop-ins are not auto-discovered. set-prompt generates a small Python adapter (`~/.hermes/plugins/set-prompt/__init__.py`) with the repo's absolute path baked in, so no symlinks are needed. **Restart Hermes after `link` (or after adding/removing a skill in your repo)** — `register()` runs only once at Hermes startup. Activation requires `set-prompt` listed under `plugins.enabled` in `~/.hermes/config.yaml` — set-prompt
|
|
126
|
+
> **Note on Hermes**: Hermes plugins must register skills/commands/hooks programmatically — directory drop-ins are not auto-discovered. set-prompt generates a small Python adapter (`~/.hermes/plugins/set-prompt/__init__.py`) with the repo's absolute path baked in, so no symlinks are needed. **Restart Hermes after `link` (or after adding/removing a skill in your repo)** — `register()` runs only once at Hermes startup. Activation requires `set-prompt` listed under `plugins.enabled` in `~/.hermes/config.yaml` — set-prompt writes this entry automatically (creating the file when absent, or appending to the existing list while preserving other entries and comments via AST-based YAML editing with backup/rollback). **Plugin skills are opt-in explicit loads** — they do not appear in Hermes's auto-listed `available_skills` index; load them via `skill_view("set-prompt:<name>")`. Hooks are observation-only on the Hermes side: the same `hooks/hooks.json` is reused, but Hermes events (`pre_tool_call`, `on_session_start`, etc.) are picked up while Claude/Cursor keys are ignored. Hook scripts can reference `${SET_PROMPT_REPO}`.
|
|
127
127
|
|
|
128
128
|
---
|
|
129
129
|
|
|
@@ -255,7 +255,7 @@ set-prompt uninstall
|
|
|
255
255
|
- **Cursor** — replaces directories and `mcp.json` in `~/.cursor/`
|
|
256
256
|
- **OpenCode** — replaces directories in `~/.config/opencode/`
|
|
257
257
|
- **Gemini CLI** — replaces directories in `~/.gemini/` (`antigravity/` subtree untouched)
|
|
258
|
-
- **Hermes** — generates `~/.hermes/plugins/set-prompt/{plugin.yaml, __init__.py}` and
|
|
258
|
+
- **Hermes** — generates `~/.hermes/plugins/set-prompt/{plugin.yaml, __init__.py}` and adds `set-prompt` to `~/.hermes/config.yaml` under `plugins.enabled` (creates the file when absent; appends to the existing list while preserving other entries and comments)
|
|
259
259
|
|
|
260
260
|
Before making any changes, `set-prompt` creates a backup and rolls back automatically on failure. However, you should be aware that:
|
|
261
261
|
|
package/dist/index.js
CHANGED
|
@@ -549,7 +549,7 @@ required_environment_variables:
|
|
|
549
549
|
|
|
550
550
|
> **Gemini CLI note**: Only \`name\` and \`description\` are recognized. \`name\` must be lowercase with hyphens and match the directory name.
|
|
551
551
|
|
|
552
|
-
> **Hermes note (set-prompt integration)**: Hermes does not auto-discover files in standard directories \u2014 plugins must register skills programmatically. set-prompt generates \`~/.hermes/plugins/set-prompt/{plugin.yaml, __init__.py}\` on \`set-prompt link hermes\`. The \`__init__.py\` reads \`<repo>/skills/<skill-name>/SKILL.md\` directly (REPO_DIR is baked in at link time) and calls \`ctx.register_skill()\` at Hermes startup. The skill directory layout is the same as other platforms \u2014 no nested category folder.
|
|
552
|
+
> **Hermes note (set-prompt integration)**: Hermes does not auto-discover files in standard directories \u2014 plugins must register skills programmatically. set-prompt generates \`~/.hermes/plugins/set-prompt/{plugin.yaml, __init__.py}\` on \`set-prompt link hermes\`. The \`__init__.py\` reads \`<repo>/skills/<skill-name>/SKILL.md\` directly (REPO_DIR is baked in at link time) and calls \`ctx.register_skill(name, Path(SKILL.md))\` at Hermes startup. The skill directory layout is the same as other platforms \u2014 no nested category folder. **Plugin skills are opt-in explicit loads** \u2014 they do not appear in Hermes's auto-listed \`available_skills\` index; load via \`skill_view("set-prompt:<skill-name>")\`.
|
|
553
553
|
|
|
554
554
|
| Field | Required | Platform | Description |
|
|
555
555
|
|-------|----------|----------|-------------|
|
|
@@ -659,7 +659,7 @@ Include: 1) Refactored code. 2) Explanation of changes.
|
|
|
659
659
|
|
|
660
660
|
#### Hermes commands (set-prompt integration)
|
|
661
661
|
|
|
662
|
-
Hermes commands are registered programmatically. set-prompt's generated \`__init__.py\` walks \`<repo>/commands/*.md\`, parses each file's YAML frontmatter for \`name\` / \`description\`, and calls \`ctx.register_command(name, handler
|
|
662
|
+
Hermes commands are registered programmatically. set-prompt's generated \`__init__.py\` walks \`<repo>/commands/*.md\`, parses each file's YAML frontmatter for \`name\` / \`description\`, and calls \`ctx.register_command(name, handler=..., description=...)\`. The handler tries to inject the markdown body as a user message via \`ctx.inject_message(body, role="user")\` when the slash command is invoked. **\`inject_message\` is CLI-mode only** \u2014 in gateway/TUI mode it returns \`False\`, and the handler falls back to **returning the body** as the command's response so the content is still visible. Hermes does **not** read \`allowed-tools\`, \`model\`, \`agent\`, or other platform-specific frontmatter \u2014 only \`name\` and \`description\` are honored.
|
|
663
663
|
|
|
664
664
|
| Field | Required | Platform | Description |
|
|
665
665
|
|-------|----------|----------|-------------|
|
|
@@ -2490,6 +2490,7 @@ import path12 from "path";
|
|
|
2490
2490
|
import fs13 from "fs";
|
|
2491
2491
|
import chalk14 from "chalk";
|
|
2492
2492
|
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
2493
|
+
import YAML from "yaml";
|
|
2493
2494
|
var PLUGIN_YAML = `name: ${MARKET_NAME}
|
|
2494
2495
|
version: "1.0"
|
|
2495
2496
|
description: Managed by set-prompt
|
|
@@ -2536,8 +2537,18 @@ def _parse_frontmatter(text):
|
|
|
2536
2537
|
|
|
2537
2538
|
|
|
2538
2539
|
def _make_command_handler(ctx, body):
|
|
2539
|
-
|
|
2540
|
-
|
|
2540
|
+
# Hermes calls handlers as handler(raw_args: str) -> Optional[str].
|
|
2541
|
+
# Primary path: inject the markdown body as a user message (CLI mode).
|
|
2542
|
+
# Fallback: in gateway mode inject_message returns False \u2014 return the body
|
|
2543
|
+
# as the handler response so the content is at least visible to the user.
|
|
2544
|
+
def handler(raw_args=""):
|
|
2545
|
+
try:
|
|
2546
|
+
ok = ctx.inject_message(body, role="user")
|
|
2547
|
+
except Exception:
|
|
2548
|
+
ok = False
|
|
2549
|
+
if not ok:
|
|
2550
|
+
return body
|
|
2551
|
+
return None
|
|
2541
2552
|
return handler
|
|
2542
2553
|
|
|
2543
2554
|
|
|
@@ -2606,9 +2617,11 @@ def register(ctx):
|
|
|
2606
2617
|
for skill_path in sorted(skills_dir.iterdir()):
|
|
2607
2618
|
if not skill_path.is_dir():
|
|
2608
2619
|
continue
|
|
2609
|
-
|
|
2620
|
+
skill_md = skill_path / "SKILL.md"
|
|
2621
|
+
if not skill_md.exists():
|
|
2610
2622
|
continue
|
|
2611
|
-
|
|
2623
|
+
# Hermes expects (name, Path-to-SKILL.md), not (name, dir-as-str).
|
|
2624
|
+
ctx.register_skill(skill_path.name, skill_md)
|
|
2612
2625
|
|
|
2613
2626
|
commands_dir = REPO_DIR / "commands"
|
|
2614
2627
|
if commands_dir.exists():
|
|
@@ -2620,7 +2633,11 @@ def register(ctx):
|
|
|
2620
2633
|
meta, body = _parse_frontmatter(text)
|
|
2621
2634
|
name = meta.get("name") or cmd_file.stem
|
|
2622
2635
|
description = meta.get("description") or ""
|
|
2623
|
-
ctx.register_command(
|
|
2636
|
+
ctx.register_command(
|
|
2637
|
+
name,
|
|
2638
|
+
handler=_make_command_handler(ctx, body),
|
|
2639
|
+
description=description,
|
|
2640
|
+
)
|
|
2624
2641
|
|
|
2625
2642
|
_register_hooks(ctx)
|
|
2626
2643
|
`;
|
|
@@ -2630,13 +2647,43 @@ plugins:
|
|
|
2630
2647
|
enabled:
|
|
2631
2648
|
- ${MARKET_NAME}
|
|
2632
2649
|
`;
|
|
2633
|
-
var ENABLE_SNIPPET = `plugins:
|
|
2634
|
-
enabled:
|
|
2635
|
-
- ${MARKET_NAME}`;
|
|
2636
2650
|
var writePluginManifest = (repoPath) => {
|
|
2637
2651
|
fs13.writeFileSync(path12.join(HERMES_PLUGIN_DIR, "plugin.yaml"), PLUGIN_YAML, "utf-8");
|
|
2638
2652
|
fs13.writeFileSync(path12.join(HERMES_PLUGIN_DIR, "__init__.py"), buildInitPy(repoPath), "utf-8");
|
|
2639
2653
|
};
|
|
2654
|
+
var writeWithBackup = (filePath, content) => {
|
|
2655
|
+
let backupPath = null;
|
|
2656
|
+
if (fs13.existsSync(filePath)) {
|
|
2657
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2658
|
+
backupPath = `${filePath}.bak.${timestamp}`;
|
|
2659
|
+
try {
|
|
2660
|
+
fs13.copyFileSync(filePath, backupPath);
|
|
2661
|
+
} catch {
|
|
2662
|
+
backupPath = null;
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
try {
|
|
2666
|
+
fs13.mkdirSync(path12.dirname(filePath), { recursive: true });
|
|
2667
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
2668
|
+
} catch (ex) {
|
|
2669
|
+
if (backupPath !== null) {
|
|
2670
|
+
try {
|
|
2671
|
+
fs13.copyFileSync(backupPath, filePath);
|
|
2672
|
+
fs13.unlinkSync(backupPath);
|
|
2673
|
+
console.warn(chalk14.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
2674
|
+
} catch {
|
|
2675
|
+
console.error(chalk14.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
throw ex;
|
|
2679
|
+
}
|
|
2680
|
+
if (backupPath !== null) {
|
|
2681
|
+
try {
|
|
2682
|
+
fs13.unlinkSync(backupPath);
|
|
2683
|
+
} catch {
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
};
|
|
2640
2687
|
var ensureHermesConfigEnabled = () => {
|
|
2641
2688
|
if (fs13.existsSync(HERMES_CONFIG_PATH) === false) {
|
|
2642
2689
|
fs13.mkdirSync(path12.dirname(HERMES_CONFIG_PATH), { recursive: true });
|
|
@@ -2645,17 +2692,54 @@ var ensureHermesConfigEnabled = () => {
|
|
|
2645
2692
|
return;
|
|
2646
2693
|
}
|
|
2647
2694
|
const content = fs13.readFileSync(HERMES_CONFIG_PATH, "utf-8");
|
|
2648
|
-
|
|
2649
|
-
|
|
2695
|
+
let doc;
|
|
2696
|
+
try {
|
|
2697
|
+
doc = YAML.parseDocument(content);
|
|
2698
|
+
} catch (ex) {
|
|
2699
|
+
console.warn(chalk14.yellow(` \u26A0 Failed to parse Hermes config (${ex.message}) \u2014 leaving file untouched.`));
|
|
2700
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
if (doc.errors.length > 0) {
|
|
2704
|
+
console.warn(chalk14.yellow(` \u26A0 Hermes config has YAML errors \u2014 leaving file untouched.`));
|
|
2705
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
let plugins = doc.get("plugins");
|
|
2709
|
+
if (plugins == null) {
|
|
2710
|
+
doc.set("plugins", doc.createNode({ enabled: [MARKET_NAME] }));
|
|
2711
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2712
|
+
console.log(chalk14.green(" \u2713 ") + chalk14.dim(`added plugins.enabled to ${HERMES_CONFIG_PATH}`));
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
if (!YAML.isMap(plugins)) {
|
|
2716
|
+
console.warn(chalk14.yellow(` \u26A0 "plugins" is not a map \u2014 leaving file untouched.`));
|
|
2717
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2718
|
+
return;
|
|
2719
|
+
}
|
|
2720
|
+
let enabled = plugins.get("enabled");
|
|
2721
|
+
if (enabled == null) {
|
|
2722
|
+
plugins.set("enabled", doc.createNode([MARKET_NAME]));
|
|
2723
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2724
|
+
console.log(chalk14.green(" \u2713 ") + chalk14.dim(`added plugins.enabled to ${HERMES_CONFIG_PATH}`));
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
if (!YAML.isSeq(enabled)) {
|
|
2728
|
+
console.warn(chalk14.yellow(` \u26A0 "plugins.enabled" is not a list \u2014 leaving file untouched.`));
|
|
2729
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
const alreadyListed = enabled.items.some((item) => {
|
|
2733
|
+
const v = YAML.isScalar(item) ? item.value : item;
|
|
2734
|
+
return v === MARKET_NAME;
|
|
2735
|
+
});
|
|
2736
|
+
if (alreadyListed) {
|
|
2650
2737
|
console.log(chalk14.dim(` \u2713 ${MARKET_NAME} already listed in ${HERMES_CONFIG_PATH}`));
|
|
2651
2738
|
return;
|
|
2652
2739
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
console.log(chalk14.
|
|
2656
|
-
console.log(chalk14.cyan(ENABLE_SNIPPET.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
2657
|
-
console.log(chalk14.dim(`
|
|
2658
|
-
File: ${HERMES_CONFIG_PATH}`));
|
|
2740
|
+
enabled.add(MARKET_NAME);
|
|
2741
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2742
|
+
console.log(chalk14.green(" \u2713 ") + chalk14.dim(`enabled "${MARKET_NAME}" in ${HERMES_CONFIG_PATH}`));
|
|
2659
2743
|
};
|
|
2660
2744
|
var linkHermes = async () => {
|
|
2661
2745
|
const repoPath = resolveRepoPath();
|
|
@@ -2702,14 +2786,44 @@ Removing Hermes plugin...`));
|
|
|
2702
2786
|
if (content.trimStart().startsWith("# Generated by set-prompt")) {
|
|
2703
2787
|
fs13.unlinkSync(HERMES_CONFIG_PATH);
|
|
2704
2788
|
console.log(chalk14.red(" removed") + chalk14.dim(`: ${HERMES_CONFIG_PATH}`));
|
|
2705
|
-
} else
|
|
2706
|
-
|
|
2707
|
-
console.log(chalk14.dim(` ${HERMES_CONFIG_PATH}`));
|
|
2789
|
+
} else {
|
|
2790
|
+
removeMarketEntryFromConfig();
|
|
2708
2791
|
}
|
|
2709
2792
|
}
|
|
2710
2793
|
configManager.hermes = null;
|
|
2711
2794
|
configManager.save();
|
|
2712
2795
|
};
|
|
2796
|
+
var removeMarketEntryFromConfig = () => {
|
|
2797
|
+
const content = fs13.readFileSync(HERMES_CONFIG_PATH, "utf-8");
|
|
2798
|
+
let doc;
|
|
2799
|
+
try {
|
|
2800
|
+
doc = YAML.parseDocument(content);
|
|
2801
|
+
} catch (ex) {
|
|
2802
|
+
console.warn(chalk14.yellow(` \u26A0 Failed to parse Hermes config (${ex.message}) \u2014 leaving file untouched.`));
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
if (doc.errors.length > 0) {
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
const plugins = doc.get("plugins");
|
|
2809
|
+
if (!YAML.isMap(plugins)) {
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
const enabled = plugins.get("enabled");
|
|
2813
|
+
if (!YAML.isSeq(enabled)) {
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2816
|
+
const idx = enabled.items.findIndex((item) => {
|
|
2817
|
+
const v = YAML.isScalar(item) ? item.value : item;
|
|
2818
|
+
return v === MARKET_NAME;
|
|
2819
|
+
});
|
|
2820
|
+
if (idx === -1) {
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
enabled.delete(idx);
|
|
2824
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2825
|
+
console.log(chalk14.red(" removed") + chalk14.dim(` "${MARKET_NAME}" from: ${HERMES_CONFIG_PATH}`));
|
|
2826
|
+
};
|
|
2713
2827
|
|
|
2714
2828
|
// src/commands/link-command.ts
|
|
2715
2829
|
var LINK_MAP = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "set-prompt",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "One repo. Every AI coding tool. Always in sync.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"cursor",
|
|
32
32
|
"codex",
|
|
33
33
|
"opencode",
|
|
34
|
-
"gemini-cli"
|
|
34
|
+
"gemini-cli",
|
|
35
|
+
"hermes"
|
|
35
36
|
],
|
|
36
37
|
"author": "juncha9 (https://github.com/juncha9)",
|
|
37
38
|
"license": "MIT",
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"ora": "^9.3.0",
|
|
53
54
|
"smol-toml": "^1.6.0",
|
|
54
55
|
"typia": "^11.0.3",
|
|
56
|
+
"yaml": "^2.8.4",
|
|
55
57
|
"zod": "^4.3.6"
|
|
56
58
|
},
|
|
57
59
|
"devDependencies": {
|