openhermes 1.12.1 → 1.13.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/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ---
14
14
 
15
- **Your OpenCode agent, leveled up.** Add it to your plugins and your agent gains a personality, a memory, a conscience, 25 specialist subagents, 27 slash commands, 6 native memory tools, 10 procedural skills, autonomous checkpointing, and the discipline to self-improve.
15
+ **Your OpenCode agent, leveled up.** Add it to your plugins and your agent gains a personality, a memory, a conscience, 25 specialist subagents, 28 slash commands, 6 native memory tools, 10 procedural skills, autonomous checkpointing, and the discipline to self-improve.
16
16
 
17
17
  ```bash
18
18
  npm i openhermes
@@ -73,7 +73,7 @@ Either way, **no other config needed.** The plugin auto-registers:
73
73
  | What | Details |
74
74
  |------|---------|
75
75
  | **25 subagents** | 7 core + 18 specialist:<br>**Core:** architect, planner, build-error-resolver, code-reviewer, security-reviewer, e2e-runner, explore<br>**Specialist:** tdd-guide, docs-lookup, doc-updater, refactor-cleaner, loop-operator, harness-optimizer, review-go, build-go, review-rust, build-rust, review-python, review-java, build-java, review-kotlin, build-kotlin, review-cpp, build-cpp, review-database |
76
- | **27 slash commands** | `/plan`, `/build-fix`, `/code-review`, `/security`, `/doctor`, `/memory-search`, `/learn`, `/ohc`, `/orchestrate`, `/eval`, `/model-route`, `/quality-gate`, `/test-coverage`, `/update-docs`, `/update-codemaps`, `/refactor-clean`, `/verify`, `/checkpoint`, `/loop-start`, `/loop-status`, `/harness-audit`, `/setup-pm`, `/go-build`, `/go-review`, `/rust-build`, `/rust-review`, `/skill-create`, `/update-me` |
76
+ | **28 slash commands** | `/build-fix`, `/checkpoint`, `/code-review`, `/doctor`, `/eval`, `/go-build`, `/go-review`, `/harness-audit`, `/learn`, `/loop-start`, `/loop-status`, `/memory-search`, `/model-route`, `/ohc`, `/orchestrate`, `/plan`, `/quality-gate`, `/refactor-clean`, `/rust-build`, `/rust-review`, `/security`, `/setup-pm`, `/skill-create`, `/test-coverage`, `/update-codemaps`, `/update-docs`, `/update-me`, `/verify` |
77
77
  | **6 native memory tools** | `add_memory`, `fetch_memory`, `list_memory`, `latest_memory`, `search_memory`, `archive_memory` — in-process, no MCP server needed |
78
78
  | **10 procedural skills** | API design, backend patterns, coding standards, E2E testing, frontend patterns, frontend slides, security review, strategic compaction, TDD workflow, verification loop |
79
79
  | **6 lifecycle plugins** | bootstrap, curator, autorecall, skill-builder, memory-tools, ohc |
@@ -85,7 +85,7 @@ You only need to define primary agents (like `build` or `OpenHermes`) in `openco
85
85
  <details>
86
86
  <summary><b>What happens on your next session</b></summary>
87
87
 
88
- 1. **Config hook** — BootstrapPlugin registers auto-config: 25 subagents, 27 commands, 10 skill dirs.
88
+ 1. **Config hook** — BootstrapPlugin registers auto-config: 25 subagents, 28 commands, 10 skill dirs.
89
89
  2. **Chat transform hook** — bootstrap content is injected into the first user message:
90
90
  - &#9733; **Constitution** (`soul.md`) — 11 immutable principles
91
91
  - &#9733; **Runtime** (`RUNTIME.md`) — gather → delegate → verify → compress
@@ -153,7 +153,7 @@ System prompt is injected with your budget and floor. As context grows, progress
153
153
  ### BootstrapPlugin
154
154
  _Registers agents, commands, skills at config hook; injects constitution + router + runtime into every session._
155
155
  - **Hooks:** `config`, `chat.transform`
156
- - Registers 25 subagents, 27 commands, 10 skill paths
156
+ - Registers 25 subagents, 28 commands, 10 skill paths
157
157
 
158
158
  ### MemoryToolsPlugin
159
159
  _Provides 6 native memory tools — no MCP server, no network, no sidecars._
package/bootstrap.mjs CHANGED
@@ -37,6 +37,7 @@ Snapshot before mutation. Never delete unrelated files. Never assume \`%USERPROF
37
37
  | **Memory recall cache** | \`openhermes/memory/recall/cache.json\` — read on session start, no MCP round-trip |
38
38
  | **Subagents** | \`explore\` (read-only), \`general\` (multi-step), \`architect\`, \`planner\`, \`build-error-resolver\`, \`code-reviewer\`, \`security-reviewer\`, \`e2e-runner\`, \`docs-lookup\`, \`doc-updater\`, \`refactor-cleaner\`, \`loop-operator\`, \`harness-optimizer\`, \`tdd-guide\`, \`review-go\`, \`build-go\`, \`review-database\`, \`review-cpp\`, \`build-cpp\`, \`review-java\`, \`build-java\`, \`review-kotlin\`, \`build-kotlin\`, \`review-python\`, \`review-rust\`, \`build-rust\` |
39
39
  | **Plugins** | \`curator\` (checkpoints, mistakes, audit, compaction), \`autorecall\` (recall cache on \`session.created\`), \`skill-builder\` (complex session detection) |
40
+ | **Slash commands** | <!-- COMMANDS:START --> \`/build-fix\`, \`/checkpoint\`, \`/code-review\`, \`/doctor\`, \`/eval\`, \`/go-build\`, \`/go-review\`, \`/harness-audit\`, \`/learn\`, \`/loop-start\`, \`/loop-status\`, \`/memory-search\`, \`/model-route\`, \`/ohc\`, \`/orchestrate\`, \`/plan\`, \`/quality-gate\`, \`/refactor-clean\`, \`/rust-build\`, \`/rust-review\`, \`/security\`, \`/setup-pm\`, \`/skill-create\`, \`/test-coverage\`, \`/update-codemaps\`, \`/update-docs\`, \`/update-me\`, \`/verify\`<!-- COMMANDS:END --> |
40
41
 
41
42
  ## Skills (available via \`skill\` tool)
42
43
 
@@ -169,6 +170,7 @@ export const BootstrapPlugin = async ({ client, directory }) => {
169
170
  "memory-search": { agent: "OpenHermes", description: "Search OpenHermes memory with LLM summarization", subtask: true, template: ct("memory-search.md") },
170
171
  "learn": { agent: "OpenHermes", description: "Create a new skill from recent work patterns", subtask: true, template: ct("learn.md") },
171
172
  "ohc": { template: "", description: "OHC context management: /ohc status, /ohc compress [focus]" },
173
+ "update-me": { template: "", description: "Force reinstall OpenHermes plugin from latest source" },
172
174
  "orchestrate": { agent: "planner", description: "Orchestrate multiple agents for complex tasks", subtask: true, template: ct("orchestrate.md") },
173
175
  "eval": { agent: "planner", description: "Evaluate implementation against acceptance criteria", subtask: true, template: ct("eval.md") },
174
176
  "model-route": { agent: "OpenHermes", description: "Recommend model tier by task complexity and budget", subtask: true, template: ct("model-route.md") },
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: OHC context management: /ohc status, /ohc compress [focus]
3
+ agent: OpenHermes
4
+ subtask: true
5
+ ---
6
+
7
+ # Ohc Command
8
+
9
+ OHC context management — hook-handled.
10
+
11
+ Run `/ohc status` to check context usage, `/ohc compress [targetTokens] [focus]` to free space.
12
+
13
+ $ARGUMENTS
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+ import { fileURLToPath } from "node:url"
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
+ const REPO_ROOT = path.resolve(__dirname, "..", "..")
8
+ const COMMANDS_DIR = path.join(REPO_ROOT, "harness", "commands")
9
+ const BOOTSTRAP_FILE = path.join(REPO_ROOT, "bootstrap.mjs")
10
+ const README_FILE = path.join(REPO_ROOT, "README.md")
11
+
12
+ const COMMANDS_START = "<!-- COMMANDS:START -->"
13
+ const COMMANDS_END = "<!-- COMMANDS:END -->"
14
+
15
+ const HOOK_ONLY = new Set(["update-me"])
16
+
17
+ function parseRegistry(src) {
18
+ const commands = {}
19
+ const blockMatch = src.match(/config\.command\s*=\s*\{([\s\S]*?)\n\s*\}/)
20
+ if (!blockMatch) return commands
21
+
22
+ let rest = blockMatch[1]
23
+ const entryRe = /^\s*"([^"]+)"\s*:\s*\{([\s\S]*?)\},?\s*$/gm
24
+ let m
25
+ while ((m = entryRe.exec(rest)) !== null) {
26
+ const name = m[1]
27
+ const body = m[2]
28
+ const agent = body.match(/agent:\s*"([^"]+)"/)?.[1] ?? ""
29
+ const description = body.match(/description:\s*"([^"]+)"/)?.[1] ?? ""
30
+ const template = body.match(/template:\s*ct\("([^"]+)"\)/) ? m[0].match(/template:\s*(ct\("[^"]+"\)|""|'')/)?.[1] : ""
31
+ const hasFile = template.includes("ct(")
32
+ commands[name] = { name, agent, description, hasFile }
33
+ }
34
+ return commands
35
+ }
36
+
37
+ function parseCommandFile(filePath) {
38
+ const src = fs.readFileSync(filePath, "utf8")
39
+ const name = path.basename(filePath, ".md")
40
+ const description = src.match(/^description:\s*(.+)$/m)?.[1] ?? ""
41
+ const agent = src.match(/^agent:\s*(.+)$/m)?.[1] ?? ""
42
+ return { name, description, agent }
43
+ }
44
+
45
+ function scanCommandFiles() {
46
+ const files = fs.readdirSync(COMMANDS_DIR).filter(f => f.endsWith(".md"))
47
+ const map = {}
48
+ for (const f of files) {
49
+ const parsed = parseCommandFile(path.join(COMMANDS_DIR, f))
50
+ map[parsed.name] = parsed
51
+ }
52
+ return map
53
+ }
54
+
55
+ function getHookCommands() {
56
+ return [...HOOK_ONLY].map(name => ({ name, agent: "", description: "Hook-handled (see plugin source)" }))
57
+ }
58
+
59
+ function printReport(registry, filesOnDisk, hookCommands, cmdNames) {
60
+ console.log("")
61
+ console.log(" COMMAND SYNC REPORT")
62
+ console.log(" " + "=".repeat(50))
63
+ const registered = new Set(Object.keys(registry))
64
+ const onDisk = new Set(Object.keys(filesOnDisk))
65
+ const allHooks = new Set(hookCommands.map(h => h.name))
66
+ const hookNoReg = [...allHooks].filter(n => !registered.has(n) && !onDisk.has(n))
67
+
68
+ console.log(` Registered in bootstrap.mjs: ${registered.size}`)
69
+ console.log(` Files in harness/commands/: ${onDisk.size}`)
70
+ console.log(` Hook-only (no registry): ${hookNoReg.length}`)
71
+ console.log(` Total real commands: ${cmdNames.length}`)
72
+ console.log("")
73
+
74
+ const registeredNoFile = [...registered].filter(n => !onDisk.has(n))
75
+ const fileNotRegistered = [...onDisk].filter(n => !registered.has(n) && !allHooks.has(n))
76
+
77
+ if (registeredNoFile.length) {
78
+ console.log(" ⚠ REGISTERED, NO FILE:")
79
+ for (const n of registeredNoFile) {
80
+ const e = registry[n]
81
+ console.log(` /${n} — ${e.description}`)
82
+ }
83
+ console.log("")
84
+ }
85
+
86
+ if (fileNotRegistered.length) {
87
+ console.log(" ⚠ FILE EXISTS, NOT REGISTERED:")
88
+ for (const n of fileNotRegistered) {
89
+ console.log(` /${n}`)
90
+ }
91
+ console.log("")
92
+ }
93
+
94
+ if (hookNoReg.length) {
95
+ console.log(" ℹ HOOK-ONLY (no registry, no file):")
96
+ for (const n of hookNoReg) {
97
+ console.log(` /${n}`)
98
+ }
99
+ console.log("")
100
+ }
101
+
102
+ if (!registeredNoFile.length && !fileNotRegistered.length) {
103
+ console.log(" ✓ Everything in sync — no gaps.")
104
+ console.log("")
105
+ }
106
+ }
107
+
108
+ function generateMissingFiles(registry, filesOnDisk) {
109
+ let count = 0
110
+ for (const [name, entry] of Object.entries(registry)) {
111
+ if (filesOnDisk[name]) continue
112
+
113
+ const templateFile = `${name}.md`
114
+ const agent = entry.agent || "OpenHermes"
115
+ const desc = entry.description
116
+
117
+ let md = `---
118
+ description: ${desc}
119
+ agent: ${agent}
120
+ subtask: true
121
+ ---
122
+
123
+ # ${name.charAt(0).toUpperCase() + name.slice(1)} Command
124
+
125
+ `
126
+
127
+ if (name === "ohc") {
128
+ md += `OHC context management — hook-handled.\n\nRun \`/ohc status\` to check context usage, \`/ohc compress [targetTokens] [focus]\` to free space.\n\n$ARGUMENTS\n`
129
+ } else {
130
+ md += `${desc}: $ARGUMENTS\n`
131
+ }
132
+
133
+ fs.writeFileSync(path.join(COMMANDS_DIR, templateFile), md, "utf8")
134
+ console.log(` ✓ Created harness/commands/${templateFile}`)
135
+ count++
136
+ }
137
+
138
+ if (count === 0) console.log(" No missing files to generate.")
139
+ return count
140
+ }
141
+
142
+ function getCommandNames(registry, hookCommands) {
143
+ const registered = new Set(Object.keys(registry))
144
+ const hookOnly = hookCommands.filter(h => !registered.has(h.name)).map(h => h.name)
145
+ const names = [...registered, ...hookOnly]
146
+ names.sort()
147
+ return names
148
+ }
149
+
150
+ function formatCommandRow(names) {
151
+ const count = names.length
152
+ const list = names.map(n => `\`/${n}\``).join(", ")
153
+ return `| **${count} slash commands** | ${list} |`
154
+ }
155
+
156
+ function formatBacktickList(names) {
157
+ return ` ${names.map(n => `\\\`/${n}\\\``).join(", ")}`
158
+ }
159
+
160
+ function syncBootstrap(registry, hookCommands) {
161
+ let src = fs.readFileSync(BOOTSTRAP_FILE, "utf8")
162
+ const cmdNames = getCommandNames(registry, hookCommands)
163
+ const row = formatCommandRow(cmdNames)
164
+
165
+ const startIdx = src.indexOf(COMMANDS_START)
166
+ const endIdx = src.indexOf(COMMANDS_END)
167
+
168
+ if (startIdx === -1 || endIdx === -1) {
169
+ console.log(" ✗ Markers not found in bootstrap.mjs. Inserting markers.")
170
+ const plugRowRe = /(\|\s+\*\*Plugins\s*\*\*.*\|)/
171
+ const match = src.match(plugRowRe)
172
+ if (!match) {
173
+ console.log(" ✗ Could not find Plugins row to anchor insertion.")
174
+ return false
175
+ }
176
+ const insertAfter = match.index + match[0].length
177
+ const newRow = `\n| **Slash commands** | ${COMMANDS_START} ${COMMANDS_END} |`
178
+ src = src.slice(0, insertAfter) + newRow + src.slice(insertAfter)
179
+ // Re-find markers for content replacement below
180
+ const newStart = src.indexOf(COMMANDS_START)
181
+ const newEnd = src.indexOf(COMMANDS_END)
182
+ if (newStart !== -1 && newEnd !== -1 && newEnd > newStart) {
183
+ const content = formatBacktickList(cmdNames)
184
+ src = src.slice(0, newStart + COMMANDS_START.length) + content + src.slice(newEnd)
185
+ }
186
+ fs.writeFileSync(BOOTSTRAP_FILE, src, "utf8")
187
+ console.log(" ✓ Inserted Slash commands row in bootstrap.mjs")
188
+ return true
189
+ }
190
+
191
+ if (endIdx <= startIdx) {
192
+ console.log(" ✗ Markers out of order in bootstrap.mjs")
193
+ return false
194
+ }
195
+
196
+ const content = formatBacktickList(cmdNames)
197
+ src = src.slice(0, startIdx + COMMANDS_START.length) + content + src.slice(endIdx)
198
+ fs.writeFileSync(BOOTSTRAP_FILE, src, "utf8")
199
+ console.log(" ✓ Updated Slash commands row in bootstrap.mjs")
200
+ return true
201
+ }
202
+
203
+ function syncReadme(registry, hookCommands) {
204
+ let src = fs.readFileSync(README_FILE, "utf8")
205
+ const cmdNames = getCommandNames(registry, hookCommands)
206
+ const row = formatCommandRow(cmdNames)
207
+
208
+ const tableRowRe = /^\|\s*\*\*(\d+)\s*slash commands\s*\*\*.*\|$/m
209
+ const match = src.match(tableRowRe)
210
+ if (!match) {
211
+ console.log(" ✗ Could not find slash commands row in README.md")
212
+ return false
213
+ }
214
+
215
+ src = src.replace(tableRowRe, row)
216
+ fs.writeFileSync(README_FILE, src, "utf8")
217
+ console.log(` ✓ Updated README.md: ${cmdNames.length} commands`)
218
+ return true
219
+ }
220
+
221
+ // Main
222
+ const args = process.argv.slice(2)
223
+ const doAudit = args.includes("--audit") || args.length === 0
224
+ const doGenerate = args.includes("--generate") || args.length === 0
225
+ const doSyncBootstrap = args.includes("--sync-bootstrap") || args.includes("--sync") || args.length === 0
226
+ const doSyncReadme = args.includes("--sync-readme") || args.includes("--sync") || args.length === 0
227
+
228
+ console.log(" ▸ syncing command definitions...")
229
+ console.log("")
230
+
231
+ const bootstrapSrc = fs.readFileSync(BOOTSTRAP_FILE, "utf8")
232
+ const registry = parseRegistry(bootstrapSrc)
233
+ const filesOnDisk = scanCommandFiles()
234
+ const hookCommands = getHookCommands()
235
+ const cmdNames = getCommandNames(registry, hookCommands)
236
+
237
+ if (doAudit) printReport(registry, filesOnDisk, hookCommands, cmdNames)
238
+
239
+ if (doGenerate) {
240
+ console.log(" [generate]")
241
+ generateMissingFiles(registry, filesOnDisk)
242
+ console.log("")
243
+ }
244
+
245
+ if (doSyncBootstrap) {
246
+ console.log(" [sync-bootstrap]")
247
+ const ok = syncBootstrap(registry, hookCommands)
248
+ if (!ok) process.exitCode = 1
249
+ console.log("")
250
+ }
251
+
252
+ if (doSyncReadme) {
253
+ console.log(" [sync-readme]")
254
+ const ok = syncReadme(registry, hookCommands)
255
+ if (!ok) process.exitCode = 1
256
+ console.log("")
257
+ }
258
+
259
+ console.log(" done.")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openhermes",
3
- "version": "1.12.1",
3
+ "version": "1.13.1",
4
4
  "description": "OpenHermes plugin suite for OpenCode — autonomous checkpointing, native memory tools, subagent routing, slash commands, and skill-candidate detection.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,11 @@
26
26
  "harness/"
27
27
  ],
28
28
  "scripts": {
29
- "test": "node --test"
29
+ "test": "node --test",
30
+ "commands:audit": "node harness/scripts/sync-commands.mjs --audit",
31
+ "commands:generate": "node harness/scripts/sync-commands.mjs --generate",
32
+ "commands:sync": "node harness/scripts/sync-commands.mjs --sync",
33
+ "commands:full": "node harness/scripts/sync-commands.mjs"
30
34
  },
31
35
  "keywords": [
32
36
  "opencode",