set-prompt 0.8.0 → 0.8.1
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 +17 -0
- package/README.md +2 -2
- package/dist/index.js +112 -14
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.8.1] - 2026-05-02
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **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`).
|
|
11
|
+
- Existing `plugins.enabled` list → `set-prompt` is appended (other entries preserved).
|
|
12
|
+
- `plugins` map exists but no `enabled` key → `enabled: [set-prompt]` is added.
|
|
13
|
+
- `plugins` key missing entirely → `plugins.enabled: [set-prompt]` is added.
|
|
14
|
+
- YAML comments and formatting are preserved (AST-based modification via `yaml` package).
|
|
15
|
+
- Atomic write with timestamped backup + rollback on failure (same pattern as `link claudecode` / `link codex`).
|
|
16
|
+
- **`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.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `yaml` (eemeli/yaml) dependency for AST-based YAML editing.
|
|
20
|
+
- 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`).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
7
24
|
## [0.8.0] - 2026-05-02
|
|
8
25
|
|
|
9
26
|
### 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). 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
|
@@ -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
|
|
@@ -2630,13 +2631,43 @@ plugins:
|
|
|
2630
2631
|
enabled:
|
|
2631
2632
|
- ${MARKET_NAME}
|
|
2632
2633
|
`;
|
|
2633
|
-
var ENABLE_SNIPPET = `plugins:
|
|
2634
|
-
enabled:
|
|
2635
|
-
- ${MARKET_NAME}`;
|
|
2636
2634
|
var writePluginManifest = (repoPath) => {
|
|
2637
2635
|
fs13.writeFileSync(path12.join(HERMES_PLUGIN_DIR, "plugin.yaml"), PLUGIN_YAML, "utf-8");
|
|
2638
2636
|
fs13.writeFileSync(path12.join(HERMES_PLUGIN_DIR, "__init__.py"), buildInitPy(repoPath), "utf-8");
|
|
2639
2637
|
};
|
|
2638
|
+
var writeWithBackup = (filePath, content) => {
|
|
2639
|
+
let backupPath = null;
|
|
2640
|
+
if (fs13.existsSync(filePath)) {
|
|
2641
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2642
|
+
backupPath = `${filePath}.bak.${timestamp}`;
|
|
2643
|
+
try {
|
|
2644
|
+
fs13.copyFileSync(filePath, backupPath);
|
|
2645
|
+
} catch {
|
|
2646
|
+
backupPath = null;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
try {
|
|
2650
|
+
fs13.mkdirSync(path12.dirname(filePath), { recursive: true });
|
|
2651
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
2652
|
+
} catch (ex) {
|
|
2653
|
+
if (backupPath !== null) {
|
|
2654
|
+
try {
|
|
2655
|
+
fs13.copyFileSync(backupPath, filePath);
|
|
2656
|
+
fs13.unlinkSync(backupPath);
|
|
2657
|
+
console.warn(chalk14.yellow(" \u26A0 Write failed \u2014 rolled back to original."));
|
|
2658
|
+
} catch {
|
|
2659
|
+
console.error(chalk14.red(` \u274C Rollback failed. Backup preserved at: ${backupPath}`));
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
throw ex;
|
|
2663
|
+
}
|
|
2664
|
+
if (backupPath !== null) {
|
|
2665
|
+
try {
|
|
2666
|
+
fs13.unlinkSync(backupPath);
|
|
2667
|
+
} catch {
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
};
|
|
2640
2671
|
var ensureHermesConfigEnabled = () => {
|
|
2641
2672
|
if (fs13.existsSync(HERMES_CONFIG_PATH) === false) {
|
|
2642
2673
|
fs13.mkdirSync(path12.dirname(HERMES_CONFIG_PATH), { recursive: true });
|
|
@@ -2645,17 +2676,54 @@ var ensureHermesConfigEnabled = () => {
|
|
|
2645
2676
|
return;
|
|
2646
2677
|
}
|
|
2647
2678
|
const content = fs13.readFileSync(HERMES_CONFIG_PATH, "utf-8");
|
|
2648
|
-
|
|
2649
|
-
|
|
2679
|
+
let doc;
|
|
2680
|
+
try {
|
|
2681
|
+
doc = YAML.parseDocument(content);
|
|
2682
|
+
} catch (ex) {
|
|
2683
|
+
console.warn(chalk14.yellow(` \u26A0 Failed to parse Hermes config (${ex.message}) \u2014 leaving file untouched.`));
|
|
2684
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
if (doc.errors.length > 0) {
|
|
2688
|
+
console.warn(chalk14.yellow(` \u26A0 Hermes config has YAML errors \u2014 leaving file untouched.`));
|
|
2689
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
let plugins = doc.get("plugins");
|
|
2693
|
+
if (plugins == null) {
|
|
2694
|
+
doc.set("plugins", doc.createNode({ enabled: [MARKET_NAME] }));
|
|
2695
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2696
|
+
console.log(chalk14.green(" \u2713 ") + chalk14.dim(`added plugins.enabled to ${HERMES_CONFIG_PATH}`));
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
if (!YAML.isMap(plugins)) {
|
|
2700
|
+
console.warn(chalk14.yellow(` \u26A0 "plugins" is not a map \u2014 leaving file untouched.`));
|
|
2701
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2702
|
+
return;
|
|
2703
|
+
}
|
|
2704
|
+
let enabled = plugins.get("enabled");
|
|
2705
|
+
if (enabled == null) {
|
|
2706
|
+
plugins.set("enabled", doc.createNode([MARKET_NAME]));
|
|
2707
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2708
|
+
console.log(chalk14.green(" \u2713 ") + chalk14.dim(`added plugins.enabled to ${HERMES_CONFIG_PATH}`));
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
if (!YAML.isSeq(enabled)) {
|
|
2712
|
+
console.warn(chalk14.yellow(` \u26A0 "plugins.enabled" is not a list \u2014 leaving file untouched.`));
|
|
2713
|
+
console.log(chalk14.dim(` File: ${HERMES_CONFIG_PATH}`));
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
const alreadyListed = enabled.items.some((item) => {
|
|
2717
|
+
const v = YAML.isScalar(item) ? item.value : item;
|
|
2718
|
+
return v === MARKET_NAME;
|
|
2719
|
+
});
|
|
2720
|
+
if (alreadyListed) {
|
|
2650
2721
|
console.log(chalk14.dim(` \u2713 ${MARKET_NAME} already listed in ${HERMES_CONFIG_PATH}`));
|
|
2651
2722
|
return;
|
|
2652
2723
|
}
|
|
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}`));
|
|
2724
|
+
enabled.add(MARKET_NAME);
|
|
2725
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2726
|
+
console.log(chalk14.green(" \u2713 ") + chalk14.dim(`enabled "${MARKET_NAME}" in ${HERMES_CONFIG_PATH}`));
|
|
2659
2727
|
};
|
|
2660
2728
|
var linkHermes = async () => {
|
|
2661
2729
|
const repoPath = resolveRepoPath();
|
|
@@ -2702,14 +2770,44 @@ Removing Hermes plugin...`));
|
|
|
2702
2770
|
if (content.trimStart().startsWith("# Generated by set-prompt")) {
|
|
2703
2771
|
fs13.unlinkSync(HERMES_CONFIG_PATH);
|
|
2704
2772
|
console.log(chalk14.red(" removed") + chalk14.dim(`: ${HERMES_CONFIG_PATH}`));
|
|
2705
|
-
} else
|
|
2706
|
-
|
|
2707
|
-
console.log(chalk14.dim(` ${HERMES_CONFIG_PATH}`));
|
|
2773
|
+
} else {
|
|
2774
|
+
removeMarketEntryFromConfig();
|
|
2708
2775
|
}
|
|
2709
2776
|
}
|
|
2710
2777
|
configManager.hermes = null;
|
|
2711
2778
|
configManager.save();
|
|
2712
2779
|
};
|
|
2780
|
+
var removeMarketEntryFromConfig = () => {
|
|
2781
|
+
const content = fs13.readFileSync(HERMES_CONFIG_PATH, "utf-8");
|
|
2782
|
+
let doc;
|
|
2783
|
+
try {
|
|
2784
|
+
doc = YAML.parseDocument(content);
|
|
2785
|
+
} catch (ex) {
|
|
2786
|
+
console.warn(chalk14.yellow(` \u26A0 Failed to parse Hermes config (${ex.message}) \u2014 leaving file untouched.`));
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
if (doc.errors.length > 0) {
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const plugins = doc.get("plugins");
|
|
2793
|
+
if (!YAML.isMap(plugins)) {
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
const enabled = plugins.get("enabled");
|
|
2797
|
+
if (!YAML.isSeq(enabled)) {
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
const idx = enabled.items.findIndex((item) => {
|
|
2801
|
+
const v = YAML.isScalar(item) ? item.value : item;
|
|
2802
|
+
return v === MARKET_NAME;
|
|
2803
|
+
});
|
|
2804
|
+
if (idx === -1) {
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2807
|
+
enabled.delete(idx);
|
|
2808
|
+
writeWithBackup(HERMES_CONFIG_PATH, String(doc));
|
|
2809
|
+
console.log(chalk14.red(" removed") + chalk14.dim(` "${MARKET_NAME}" from: ${HERMES_CONFIG_PATH}`));
|
|
2810
|
+
};
|
|
2713
2811
|
|
|
2714
2812
|
// src/commands/link-command.ts
|
|
2715
2813
|
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.1",
|
|
4
4
|
"description": "One repo. Every AI coding tool. Always in sync.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"ora": "^9.3.0",
|
|
53
53
|
"smol-toml": "^1.6.0",
|
|
54
54
|
"typia": "^11.0.3",
|
|
55
|
+
"yaml": "^2.8.4",
|
|
55
56
|
"zod": "^4.3.6"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|