openhermes 1.3.0 → 1.5.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/autorecall.mjs +15 -9
- package/bootstrap.mjs +43 -62
- package/curator.mjs +14 -11
- package/index.mjs +33 -5
- package/lib/memory-tools-plugin.mjs +3 -3
- package/lib/paths.mjs +5 -4
- package/package.json +1 -1
package/autorecall.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import fs from "node:fs"
|
|
|
4
4
|
import { atomicWriteJson, fingerprintEnvironment, isTruthy, sanitizeRecord, truncateText } from "./lib/hardening.mjs"
|
|
5
5
|
import { getDataRoot, getCacheRoot, getMemoryRoot, getRecallRoot, getRuntimeRoot } from "./lib/paths.mjs"
|
|
6
6
|
|
|
7
|
+
const OLD_BASE = path.join(os.homedir(), ".config", "opencode", "openhermes")
|
|
8
|
+
|
|
7
9
|
function readJson(fp, fallback) {
|
|
8
10
|
try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
|
|
9
11
|
}
|
|
@@ -75,24 +77,28 @@ function formatMemoryWriteGap(memory) {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
async function loadMemoryAndWriteCache(projectKey, directory) {
|
|
78
|
-
const OLD_MEMORY = path.join(os.homedir(), ".config", "opencode", "openhermes", "memory")
|
|
79
|
-
const OLD_CACHE = path.join(os.homedir(), ".config", "opencode", "openhermes", "memory", "recall")
|
|
80
80
|
const SENTINEL = path.join(getDataRoot(), ".migrated-from-v1")
|
|
81
81
|
if (!fs.existsSync(SENTINEL)) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
fs.
|
|
82
|
+
const oldMemory = path.join(OLD_BASE, "memory")
|
|
83
|
+
if (fs.existsSync(oldMemory)) {
|
|
84
|
+
fs.cpSync(oldMemory, getMemoryRoot(), { recursive: true })
|
|
85
|
+
fs.rmSync(oldMemory, { recursive: true, force: true })
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
+
const oldCache = path.join(OLD_BASE, "memory", "recall")
|
|
88
|
+
if (fs.existsSync(oldCache)) {
|
|
87
89
|
fs.mkdirSync(getRecallRoot(), { recursive: true })
|
|
88
|
-
const files = fs.readdirSync(
|
|
89
|
-
for (const f of files) fs.cpSync(path.join(
|
|
90
|
+
const files = fs.readdirSync(oldCache).filter(f => f.endsWith(".json"))
|
|
91
|
+
for (const f of files) fs.cpSync(path.join(oldCache, f), path.join(getRecallRoot(), f))
|
|
90
92
|
}
|
|
91
|
-
const oldRuntime = path.join(
|
|
93
|
+
const oldRuntime = path.join(OLD_BASE, "runtime")
|
|
92
94
|
if (fs.existsSync(oldRuntime)) {
|
|
93
95
|
fs.cpSync(oldRuntime, getRuntimeRoot(), { recursive: true })
|
|
94
96
|
fs.rmSync(oldRuntime, { recursive: true, force: true })
|
|
95
97
|
}
|
|
98
|
+
const oldArchive = path.join(OLD_BASE, "archive")
|
|
99
|
+
if (fs.existsSync(oldArchive)) {
|
|
100
|
+
fs.rmSync(oldArchive, { recursive: true, force: true })
|
|
101
|
+
}
|
|
96
102
|
fs.writeFileSync(SENTINEL, new Date().toISOString(), "utf8")
|
|
97
103
|
}
|
|
98
104
|
|
package/bootstrap.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from "node:path"
|
|
2
2
|
import fs from "node:fs"
|
|
3
|
-
import os from "node:os"
|
|
4
3
|
import { fileURLToPath } from "node:url"
|
|
5
4
|
|
|
6
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -9,8 +8,7 @@ const RULES_DIR = path.join(HARNESS_DIR, "rules")
|
|
|
9
8
|
const SKILLS_DIR = path.join(HARNESS_DIR, "skills")
|
|
10
9
|
const CONSTITUTION_FILE = path.join(HARNESS_DIR, "constitution", "soul.md")
|
|
11
10
|
const RUNTIME_FILE = path.join(HARNESS_DIR, "instructions", "RUNTIME.md")
|
|
12
|
-
|
|
13
|
-
const USER_TOOLS_DIR = path.join(os.homedir(), ".config", "opencode", "tools")
|
|
11
|
+
|
|
14
12
|
|
|
15
13
|
let _bootstrapCache = undefined
|
|
16
14
|
|
|
@@ -116,35 +114,7 @@ function getOwnVersion() {
|
|
|
116
114
|
} catch { return "1.0.0" }
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
function installToolFiles() {
|
|
120
|
-
try {
|
|
121
|
-
if (!fs.existsSync(TOOLS_SOURCE_DIR)) return
|
|
122
|
-
const files = fs.readdirSync(TOOLS_SOURCE_DIR).filter(f => f.endsWith(".mjs") && f !== "_memory.mjs")
|
|
123
|
-
if (!files.length) return
|
|
124
|
-
fs.mkdirSync(USER_TOOLS_DIR, { recursive: true })
|
|
125
|
-
const pkgVersion = getOwnVersion()
|
|
126
|
-
const markerPath = path.join(USER_TOOLS_DIR, ".openhermes-version")
|
|
127
|
-
let installedVersion = ""
|
|
128
|
-
try { installedVersion = fs.readFileSync(markerPath, "utf8").trim() } catch {}
|
|
129
|
-
if (installedVersion === pkgVersion) {
|
|
130
|
-
const existing = fs.readdirSync(USER_TOOLS_DIR).filter(f => f.endsWith(".mjs"))
|
|
131
|
-
const needed = [...files, "_memory.mjs"]
|
|
132
|
-
if (needed.every(f => existing.includes(f))) return
|
|
133
|
-
}
|
|
134
|
-
for (const f of ["_memory.mjs", ...files]) {
|
|
135
|
-
const src = path.join(TOOLS_SOURCE_DIR, f)
|
|
136
|
-
const dst = path.join(USER_TOOLS_DIR, f)
|
|
137
|
-
if (fs.existsSync(src)) fs.copyFileSync(src, dst)
|
|
138
|
-
}
|
|
139
|
-
fs.writeFileSync(markerPath, pkgVersion, "utf8")
|
|
140
|
-
process.stderr.write(`[openhermes-bootstrap] installed ${files.length + 1} tool files (v${pkgVersion})\n`)
|
|
141
|
-
} catch (err) {
|
|
142
|
-
process.stderr.write(`[openhermes-bootstrap] tool install error: ${err.message}\n`)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
117
|
export const BootstrapPlugin = async ({ client, directory }) => {
|
|
147
|
-
installToolFiles()
|
|
148
118
|
|
|
149
119
|
const getContent = () => {
|
|
150
120
|
if (_bootstrapCache !== undefined) return _bootstrapCache
|
|
@@ -158,8 +128,6 @@ export const BootstrapPlugin = async ({ client, directory }) => {
|
|
|
158
128
|
}
|
|
159
129
|
|
|
160
130
|
return {
|
|
161
|
-
name: "openhermes-bootstrap",
|
|
162
|
-
|
|
163
131
|
config: async (config) => {
|
|
164
132
|
config.skills = config.skills || {}
|
|
165
133
|
config.skills.paths = config.skills.paths || []
|
|
@@ -167,75 +135,88 @@ export const BootstrapPlugin = async ({ client, directory }) => {
|
|
|
167
135
|
config.skills.paths.push(SKILLS_DIR)
|
|
168
136
|
}
|
|
169
137
|
|
|
170
|
-
config.agent = config.agent || {}
|
|
171
138
|
const PROMPTS_DIR = path.join(HARNESS_DIR, "prompts")
|
|
172
139
|
const p = (name) => `{file:${path.join(PROMPTS_DIR, name)}}`
|
|
140
|
+
const COMMANDS_DIR = path.join(HARNESS_DIR, "commands")
|
|
141
|
+
const ct = (file) => `{file:${path.join(COMMANDS_DIR, file)}}\n\n$ARGUMENTS`
|
|
142
|
+
|
|
143
|
+
const existingCommands = config.command ?? {}
|
|
144
|
+
const existingAgents = { ...(config.agent ?? {}) }
|
|
173
145
|
|
|
174
|
-
|
|
146
|
+
if (existingAgents.build && typeof existingAgents.build === "object") {
|
|
147
|
+
existingAgents.build = { ...existingAgents.build, mode: "subagent", hidden: true }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
config.command = {
|
|
151
|
+
...existingCommands,
|
|
152
|
+
"build-fix": { agent: "build-error-resolver", description: "Fix build and TypeScript errors", subtask: true, template: ct("build-fix.md") },
|
|
153
|
+
"code-review": { agent: "code-reviewer", description: "Review code for quality, security, and maintainability", subtask: true, template: ct("code-review.md") },
|
|
154
|
+
"plan": { agent: "planner", description: "Create a detailed implementation plan", subtask: true, template: ct("plan.md") },
|
|
155
|
+
"security": { agent: "security-reviewer", description: "Run comprehensive security review", subtask: true, template: ct("security.md") },
|
|
156
|
+
"doctor": { agent: "OpenHermes", description: "Run OpenCode OpenHermes health diagnostics", subtask: true, template: ct("doctor.md") },
|
|
157
|
+
"memory-search": { agent: "OpenHermes", description: "Search OpenHermes memory with LLM summarization", subtask: true, template: ct("memory-search.md") },
|
|
158
|
+
"learn": { agent: "OpenHermes", description: "Create a new skill from recent work patterns", subtask: true, template: ct("learn.md") },
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
config.agent = {
|
|
162
|
+
...existingAgents,
|
|
163
|
+
"OpenHermes": {
|
|
164
|
+
description: "Fully autonomous primary coding agent (all tools allowed)",
|
|
165
|
+
mode: "primary",
|
|
166
|
+
color: "#F59E0B",
|
|
167
|
+
permission: {
|
|
168
|
+
bash: { "*": "allow" },
|
|
169
|
+
edit: "allow",
|
|
170
|
+
read: "allow",
|
|
171
|
+
task: { "*": "allow" },
|
|
172
|
+
},
|
|
173
|
+
},
|
|
175
174
|
"architect": {
|
|
176
175
|
description: "Software architecture specialist for system design",
|
|
177
176
|
mode: "subagent",
|
|
178
177
|
prompt: p("architect.txt"),
|
|
179
|
-
permission: { read: "allow", edit: "deny", bash: "deny" }
|
|
178
|
+
permission: { read: "allow", edit: "deny", bash: "deny" },
|
|
180
179
|
},
|
|
181
180
|
"build-error-resolver": {
|
|
182
181
|
description: "Build and TypeScript error resolution specialist",
|
|
183
182
|
mode: "subagent",
|
|
184
183
|
prompt: p("build-error-resolver.md"),
|
|
185
|
-
permission: { read: "allow", edit: "allow" }
|
|
184
|
+
permission: { read: "allow", edit: "allow" },
|
|
186
185
|
},
|
|
187
186
|
"code-reviewer": {
|
|
188
187
|
description: "Expert code review specialist",
|
|
189
188
|
mode: "subagent",
|
|
190
189
|
prompt: p("code-reviewer.md"),
|
|
191
|
-
permission: { read: "allow", edit: "deny", bash: "deny", task: { explore: "allow", "*": "deny" } }
|
|
190
|
+
permission: { read: "allow", edit: "deny", bash: "deny", task: { explore: "allow", "*": "deny" } },
|
|
192
191
|
},
|
|
193
192
|
"e2e-runner": {
|
|
194
193
|
description: "End-to-end testing specialist using Playwright",
|
|
195
194
|
mode: "subagent",
|
|
196
195
|
prompt: p("e2e-runner.txt"),
|
|
197
|
-
permission: { read: "allow", edit: "allow" }
|
|
196
|
+
permission: { read: "allow", edit: "allow" },
|
|
198
197
|
},
|
|
199
198
|
"explore": {
|
|
200
199
|
description: "Fast read-only codebase exploration agent",
|
|
201
200
|
mode: "subagent",
|
|
202
201
|
prompt: p("explore.md"),
|
|
203
|
-
permission: { read: "allow", grep: "allow", glob: "allow", list: "allow", edit: "deny", bash: "deny" }
|
|
202
|
+
permission: { read: "allow", grep: "allow", glob: "allow", list: "allow", edit: "deny", bash: "deny" },
|
|
204
203
|
},
|
|
205
204
|
"planner": {
|
|
206
205
|
description: "Expert planning specialist for complex features and refactoring",
|
|
207
206
|
mode: "subagent",
|
|
208
207
|
color: "#3B82F6",
|
|
209
208
|
prompt: p("planner.md"),
|
|
210
|
-
permission: { read: "allow", edit: "deny", bash: "deny" }
|
|
209
|
+
permission: { read: "allow", edit: "deny", bash: "deny" },
|
|
211
210
|
},
|
|
212
211
|
"security-reviewer": {
|
|
213
212
|
description: "Security vulnerability detection and remediation specialist",
|
|
214
213
|
mode: "subagent",
|
|
215
214
|
prompt: p("security-reviewer.md"),
|
|
216
|
-
permission: { read: "allow", edit: "deny", bash: "deny", task: { "*": "allow" } }
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
for (const [name, def] of Object.entries(SUBAGENTS)) {
|
|
220
|
-
if (!config.agent[name]) config.agent[name] = def
|
|
215
|
+
permission: { read: "allow", edit: "deny", bash: "deny", task: { "*": "allow" } },
|
|
216
|
+
},
|
|
221
217
|
}
|
|
222
218
|
|
|
223
|
-
config.
|
|
224
|
-
const COMMANDS_DIR = path.join(HARNESS_DIR, "commands")
|
|
225
|
-
const ct = (file) => `{file:${path.join(COMMANDS_DIR, file)}}\n\n$ARGUMENTS`
|
|
226
|
-
|
|
227
|
-
const COMMANDS = {
|
|
228
|
-
"build-fix": { agent: "build-error-resolver", description: "Fix build and TypeScript errors", subtask: true, template: ct("build-fix.md") },
|
|
229
|
-
"code-review": { agent: "code-reviewer", description: "Review code for quality, security, and maintainability", subtask: true, template: ct("code-review.md") },
|
|
230
|
-
"plan": { agent: "planner", description: "Create a detailed implementation plan", subtask: true, template: ct("plan.md") },
|
|
231
|
-
"security": { agent: "security-reviewer", description: "Run comprehensive security review", subtask: true, template: ct("security.md") },
|
|
232
|
-
"doctor": { agent: "OpenHermes", description: "Run OpenCode OpenHermes health diagnostics", subtask: true, template: ct("doctor.md") },
|
|
233
|
-
"memory-search": { agent: "OpenHermes", description: "Search OpenHermes memory with LLM summarization", subtask: true, template: ct("memory-search.md") },
|
|
234
|
-
"learn": { agent: "OpenHermes", description: "Create a new skill from recent work patterns", subtask: true, template: ct("learn.md") }
|
|
235
|
-
}
|
|
236
|
-
for (const [name, def] of Object.entries(COMMANDS)) {
|
|
237
|
-
if (!config.command[name]) config.command[name] = def
|
|
238
|
-
}
|
|
219
|
+
config.default_agent = "OpenHermes"
|
|
239
220
|
},
|
|
240
221
|
|
|
241
222
|
"experimental.chat.messages.transform": async (_input, output) => {
|
package/curator.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { findUnsupportedSchemaKeywords, validateSchema } from "./lib/schema-vali
|
|
|
5
5
|
import { atomicWriteJson, fingerprintEnvironment, fingerprintFile, isTruthy, redactSensitiveText, sanitizeRecord, truncateText } from "./lib/hardening.mjs"
|
|
6
6
|
import { fileURLToPath } from "node:url"
|
|
7
7
|
import { dirname } from "node:path"
|
|
8
|
-
import {
|
|
8
|
+
import { getDataRoot, getMemoryRoot, getRuntimeRoot, getArchiveRoot } from "./lib/paths.mjs"
|
|
9
9
|
|
|
10
10
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
|
|
@@ -78,10 +78,8 @@ function updateLoopState(root, patch) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function loadSchema(classId) {
|
|
81
|
-
const fp = path.join(
|
|
82
|
-
try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch {}
|
|
83
|
-
const bundled = path.join(__dirname, "schemas", `${classId}.schema.json`)
|
|
84
|
-
try { return JSON.parse(fs.readFileSync(bundled, "utf8")) } catch { return null }
|
|
81
|
+
const fp = path.join(__dirname, "schemas", `${classId}.schema.json`)
|
|
82
|
+
try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return null }
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
function validateRecordAgainstSchema(record) {
|
|
@@ -377,7 +375,7 @@ async function handlePermissionReplied(directory, project, event) {
|
|
|
377
375
|
environment_fingerprint: environmentFingerprint,
|
|
378
376
|
}
|
|
379
377
|
const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
|
|
380
|
-
const auditSchema = loadSchema("audit")
|
|
378
|
+
const auditSchema = loadSchema("audit")
|
|
381
379
|
if (auditSchema) {
|
|
382
380
|
const unsupported = findUnsupportedSchemaKeywords(auditSchema)
|
|
383
381
|
if (!unsupported.length) {
|
|
@@ -398,18 +396,23 @@ async function handlePermissionReplied(directory, project, event) {
|
|
|
398
396
|
export const CuratorPlugin = async ({ project, directory }) => {
|
|
399
397
|
return {
|
|
400
398
|
event: async ({ event }) => {
|
|
401
|
-
const
|
|
399
|
+
const OLD_BASE = path.join(os.homedir(), ".config", "opencode", "openhermes")
|
|
402
400
|
const SENTINEL = path.join(getDataRoot(), ".migrated-from-v1")
|
|
403
401
|
if (!fs.existsSync(SENTINEL)) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
fs.
|
|
402
|
+
const oldMemory = path.join(OLD_BASE, "memory")
|
|
403
|
+
if (fs.existsSync(oldMemory)) {
|
|
404
|
+
fs.cpSync(oldMemory, getMemoryRoot(), { recursive: true })
|
|
405
|
+
fs.rmSync(oldMemory, { recursive: true, force: true })
|
|
407
406
|
}
|
|
408
|
-
const oldRuntime = path.join(
|
|
407
|
+
const oldRuntime = path.join(OLD_BASE, "runtime")
|
|
409
408
|
if (fs.existsSync(oldRuntime)) {
|
|
410
409
|
fs.cpSync(oldRuntime, getRuntimeRoot(), { recursive: true })
|
|
411
410
|
fs.rmSync(oldRuntime, { recursive: true, force: true })
|
|
412
411
|
}
|
|
412
|
+
const oldArchive = path.join(OLD_BASE, "archive")
|
|
413
|
+
if (fs.existsSync(oldArchive)) {
|
|
414
|
+
fs.rmSync(oldArchive, { recursive: true, force: true })
|
|
415
|
+
}
|
|
413
416
|
fs.writeFileSync(SENTINEL, new Date().toISOString(), "utf8")
|
|
414
417
|
}
|
|
415
418
|
if (event.type === "session.idle") {
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { AutorecallPlugin } from "./autorecall.mjs"
|
|
2
|
+
import { CuratorPlugin } from "./curator.mjs"
|
|
3
|
+
import { SkillBuilderPlugin } from "./skill-builder.mjs"
|
|
4
|
+
import { BootstrapPlugin } from "./bootstrap.mjs"
|
|
5
|
+
import { MemoryToolsPlugin } from "./lib/memory-tools-plugin.mjs"
|
|
6
|
+
|
|
7
|
+
export default async (input) => {
|
|
8
|
+
const [bootstrap, autorecall, curator, skillBuilder, memoryTools] = await Promise.all([
|
|
9
|
+
BootstrapPlugin(input),
|
|
10
|
+
AutorecallPlugin(input),
|
|
11
|
+
CuratorPlugin(input),
|
|
12
|
+
SkillBuilderPlugin(input),
|
|
13
|
+
MemoryToolsPlugin(input),
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
const merged = {}
|
|
17
|
+
if (bootstrap.config) merged.config = bootstrap.config
|
|
18
|
+
if (memoryTools.tool) merged.tool = memoryTools.tool
|
|
19
|
+
|
|
20
|
+
const eventHandlers = [autorecall.event, curator.event, skillBuilder.event].filter(Boolean)
|
|
21
|
+
if (eventHandlers.length) {
|
|
22
|
+
merged.event = async (payload) => {
|
|
23
|
+
await Promise.all(eventHandlers.map(fn => fn(payload)))
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const hook of ["experimental.chat.messages.transform", "experimental.session.compacting", "tool.execute.after"]) {
|
|
28
|
+
const handler = bootstrap[hook] || curator[hook] || skillBuilder[hook]
|
|
29
|
+
if (handler) merged[hook] = handler
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return merged
|
|
33
|
+
}
|
|
@@ -6,10 +6,10 @@ import os from "os"
|
|
|
6
6
|
|
|
7
7
|
import { atomicWriteJson, fingerprintEnvironment, sanitizeRecord, truncateText } from "./hardening.mjs"
|
|
8
8
|
import { findUnsupportedSchemaKeywords, validateSchema } from "./schema-validator.mjs"
|
|
9
|
-
import { getMemoryRoot,
|
|
9
|
+
import { getMemoryRoot, getRuntimeRoot } from "../lib/paths.mjs"
|
|
10
10
|
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
12
|
-
const
|
|
12
|
+
const SCHEMAS_DIR = path.resolve(__dirname, "..", "schemas")
|
|
13
13
|
const MEMORY_DIR = getMemoryRoot()
|
|
14
14
|
|
|
15
15
|
const CLASSES = ["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]
|
|
@@ -141,7 +141,7 @@ function handlePut(cls, id, dataStr) {
|
|
|
141
141
|
const now = new Date().toISOString()
|
|
142
142
|
const record = { ...parsed, id, class: cls, source: parsed.source ?? "agent", status: parsed.status ?? "active", created_at: parsed.created_at ?? now, updated_at: now }
|
|
143
143
|
|
|
144
|
-
const schema = readJSON(path.join(
|
|
144
|
+
const schema = readJSON(path.join(SCHEMAS_DIR, `${cls}.schema.json`), null)
|
|
145
145
|
if (schema) {
|
|
146
146
|
const unsupported = findUnsupportedSchemaKeywords(schema)
|
|
147
147
|
if (unsupported.length) return `Unsupported schema keywords: ${unsupported.join(", ")}`
|
package/lib/paths.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import path from "node:path"
|
|
2
2
|
import os from "node:os"
|
|
3
3
|
import fs from "node:fs"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
4
5
|
|
|
5
6
|
const HOME = process.env.USERPROFILE || os.homedir()
|
|
6
|
-
const CONFIG_ROOT = path.join(HOME, ".config", "opencode", "openhermes")
|
|
7
7
|
const DATA_ROOT = path.join(HOME, ".local", "share", "opencode", "openhermes")
|
|
8
8
|
const CACHE_ROOT = path.join(HOME, ".cache", "opencode", "openhermes")
|
|
9
|
+
const PKG_DIR = path.dirname(fileURLToPath(import.meta.url))
|
|
9
10
|
|
|
10
11
|
function resolveRoot(envVar, fallback) {
|
|
11
12
|
if (!isTruthy(process.env.OPENCODE_ALLOW_PROJECT_HARNESS)) return fallback
|
|
@@ -16,7 +17,7 @@ function resolveRoot(envVar, fallback) {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export function getConfigRoot() {
|
|
19
|
-
return
|
|
20
|
+
return null // legacy — no longer used
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export function getDataRoot() {
|
|
@@ -40,11 +41,11 @@ export function getRecallRoot() {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export function getArchiveRoot() {
|
|
43
|
-
return path.join(
|
|
44
|
+
return path.join(getDataRoot(), "archive")
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export function getSchemaRoot() {
|
|
47
|
-
return path.
|
|
48
|
+
return path.resolve(PKG_DIR, "..", "schemas")
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
function isTruthy(value) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openhermes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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",
|