openhermes 1.5.2 → 1.12.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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -157
  3. package/autorecall.mjs +2 -12
  4. package/bootstrap.mjs +158 -8
  5. package/curator.mjs +1 -5
  6. package/harness/commands/checkpoint.md +68 -0
  7. package/harness/commands/eval.md +89 -0
  8. package/harness/commands/go-build.md +87 -0
  9. package/harness/commands/go-review.md +71 -0
  10. package/harness/commands/harness-audit.md +90 -0
  11. package/harness/commands/learn.md +2 -2
  12. package/harness/commands/loop-start.md +38 -0
  13. package/harness/commands/loop-status.md +30 -0
  14. package/harness/commands/memory-search.md +2 -2
  15. package/harness/commands/model-route.md +32 -0
  16. package/harness/commands/orchestrate.md +88 -0
  17. package/harness/commands/quality-gate.md +35 -0
  18. package/harness/commands/refactor-clean.md +102 -0
  19. package/harness/commands/rust-build.md +78 -0
  20. package/harness/commands/rust-review.md +65 -0
  21. package/harness/commands/setup-pm.md +65 -0
  22. package/harness/commands/skill-create.md +99 -0
  23. package/harness/commands/test-coverage.md +80 -0
  24. package/harness/commands/update-codemaps.md +81 -0
  25. package/harness/commands/update-docs.md +67 -0
  26. package/harness/commands/verify.md +68 -0
  27. package/harness/instructions/CONVENTIONS.md +206 -0
  28. package/harness/instructions/RUNTIME.md +8 -1
  29. package/harness/prompts/build-cpp.md +84 -0
  30. package/harness/prompts/build-error-resolver.md +2 -1
  31. package/harness/prompts/build-go.md +326 -0
  32. package/harness/prompts/build-java.md +126 -0
  33. package/harness/prompts/build-kotlin.md +123 -0
  34. package/harness/prompts/build-rust.md +94 -0
  35. package/harness/prompts/code-reviewer.md +2 -1
  36. package/harness/prompts/doc-updater.md +193 -0
  37. package/harness/prompts/docs-lookup.md +60 -0
  38. package/harness/prompts/explore.md +1 -0
  39. package/harness/prompts/harness-optimizer.md +30 -0
  40. package/harness/prompts/loop-operator.md +42 -0
  41. package/harness/prompts/planner.md +3 -2
  42. package/harness/prompts/refactor-cleaner.md +242 -0
  43. package/harness/prompts/review-cpp.md +68 -0
  44. package/harness/prompts/review-database.md +248 -0
  45. package/harness/prompts/review-go.md +244 -0
  46. package/harness/prompts/review-java.md +100 -0
  47. package/harness/prompts/review-kotlin.md +130 -0
  48. package/harness/prompts/review-python.md +88 -0
  49. package/harness/prompts/review-rust.md +64 -0
  50. package/harness/prompts/security-reviewer.md +3 -2
  51. package/harness/prompts/tdd-guide.md +214 -0
  52. package/harness/rules/delegation.md +28 -22
  53. package/harness/rules/memory-management.md +4 -4
  54. package/harness/rules/retrieval.md +5 -5
  55. package/harness/rules/runtime-guards.md +1 -1
  56. package/harness/rules/session-start.md +4 -4
  57. package/harness/rules/skills-management.md +2 -2
  58. package/harness/rules/state-drift.md +1 -1
  59. package/harness/rules/verification.md +4 -4
  60. package/harness/skills/coding-standards/SKILL.md +1 -1
  61. package/index.mjs +25 -4
  62. package/lib/hardening.mjs +11 -1
  63. package/lib/memory-tools-plugin.mjs +101 -54
  64. package/lib/ohc/config.mjs +30 -0
  65. package/lib/ohc/pruner.mjs +239 -0
  66. package/lib/ohc/reaper.mjs +61 -0
  67. package/lib/ohc/state.mjs +32 -0
  68. package/lib/ohc/updater.mjs +110 -0
  69. package/package.json +1 -1
  70. package/skill-builder.mjs +2 -6
  71. package/lib/tools/_memory.mjs +0 -230
  72. package/lib/tools/hm_get.mjs +0 -13
  73. package/lib/tools/hm_latest.mjs +0 -12
  74. package/lib/tools/hm_list.mjs +0 -13
  75. package/lib/tools/hm_put.mjs +0 -14
  76. package/lib/tools/hm_search.mjs +0 -16
@@ -1,230 +0,0 @@
1
- import fs from "fs"
2
- import path from "path"
3
- import os from "os"
4
- import { getMemoryRoot, getDataRoot } from "../../lib/paths.mjs"
5
-
6
- const MEMORY_DIR = getMemoryRoot()
7
-
8
- const CLASSES = ["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]
9
- const PLURALS = {
10
- audit: "audits", checkpoint: "checkpoints", mistake: "mistakes",
11
- instinct: "instincts", decision: "decisions", constraint: "constraints",
12
- backlog: "backlog", verification_receipt: "verification_receipts",
13
- }
14
-
15
- function isPlainObject(v) { return !!v && typeof v === "object" && !Array.isArray(v) }
16
-
17
- function stableStringify(v, space = 0) {
18
- function sort(o) {
19
- if (Array.isArray(o)) return o.map(sort)
20
- if (!isPlainObject(o)) return o
21
- const r = {}
22
- for (const k of Object.keys(o).sort()) r[k] = sort(o[k])
23
- return r
24
- }
25
- return JSON.stringify(sort(v), null, space)
26
- }
27
-
28
- function classDir(cls) { return path.join(MEMORY_DIR, PLURALS[cls]) }
29
-
30
- function atomicWriteJson(fp, data) {
31
- const tmp = fp + ".tmp"
32
- fs.writeFileSync(tmp, stableStringify(data, 2), "utf8")
33
- fs.renameSync(tmp, fp)
34
- }
35
-
36
- function readJSON(fp, fallback) {
37
- try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
38
- }
39
-
40
- function readJSONL(fp) {
41
- try {
42
- return fs.readFileSync(fp, "utf8").split(/\r?\n/).map(l => l.trim()).filter(Boolean).map(l => JSON.parse(l))
43
- } catch { return [] }
44
- }
45
-
46
- function buildEntry(cls, r) {
47
- const e = {
48
- id: r.id, summary: r.summary, status: r.status,
49
- updated_at: r.updated_at ?? r.created_at,
50
- path: path.join("openhermes", "memory", PLURALS[cls], `${r.id}.json`),
51
- scope: r.scope ?? null, project: r.project ?? null,
52
- }
53
- if (cls === "audit") { e.target = r.target; e.overall_score = r.overall_score }
54
- if (cls === "backlog") { e.priority = r.priority; e.trigger = r.trigger }
55
- return e
56
- }
57
-
58
- function hasExpired(r) {
59
- if (r?.status === "expired" || r?.status === "decayed") return true
60
- if (r?.decay_at && Date.parse(r.decay_at) < Date.now()) return true
61
- if (r?.expires_at && Date.parse(r.expires_at) < Date.now()) return true
62
- return false
63
- }
64
-
65
- function sortRecent(entries) {
66
- function ts(e) { return e?.updated_at ?? e?.created_at ?? "" }
67
- return [...entries].sort((a, b) => {
68
- const at = Date.parse(ts(a)), bt = Date.parse(ts(b))
69
- if (!Number.isNaN(at) && !Number.isNaN(bt) && at !== bt) return bt - at
70
- return String(ts(b)).localeCompare(String(ts(a)))
71
- })
72
- }
73
-
74
- function filterActive(entries) { return entries.filter(e => !hasExpired(e)) }
75
-
76
- function validateSchemaSimple(record) {
77
- const required = {
78
- audit: ["id", "class", "summary", "target", "overall_score", "checks"],
79
- checkpoint: ["id", "class", "summary", "mission", "current_state", "provenance"],
80
- mistake: ["id", "class", "summary", "failure", "root_cause", "type", "strike"],
81
- instinct: ["id", "class", "summary", "claim", "provenance"],
82
- decision: ["id", "class", "summary", "decision", "rationale", "provenance"],
83
- constraint: ["id", "class", "summary", "constraint", "provenance"],
84
- backlog: ["id", "class", "summary", "priority"],
85
- verification_receipt: ["id", "class", "summary", "fingerprint", "method", "result"],
86
- }
87
- const cls = record.class
88
- const reqs = required[cls]
89
- if (!reqs) return { ok: true }
90
- const missing = reqs.filter(r => !record[r] && record[r] !== null && record[r] !== undefined)
91
- if (missing.length) return { ok: false, errors: missing.map(m => `$.${m} is required`) }
92
- return { ok: true }
93
- }
94
-
95
- function queryList(cls, limit = 10) {
96
- if (cls === "mistake") {
97
- return sortRecent(filterActive(readJSONL(path.join(classDir(cls), "mistakes.jsonl")))).slice(0, limit)
98
- }
99
- const dir = classDir(cls)
100
- let files = []
101
- try { files = fs.readdirSync(dir).filter(f => f.endsWith(".json") && f !== "index.json").map(f => path.join(dir, f)) } catch { return [] }
102
- const entries = files.map(f => readJSON(f, null)).filter(Boolean).map(r => buildEntry(cls, r))
103
- return sortRecent(filterActive(entries)).slice(0, limit)
104
- }
105
-
106
- function queryGet(cls, id) {
107
- if (cls === "mistake") return readJSONL(path.join(classDir(cls), "mistakes.jsonl")).find(e => e?.id === id) ?? null
108
- return readJSON(path.join(classDir(cls), `${id}.json`), null)
109
- }
110
-
111
- function scoreRelevance(r, query, project) {
112
- const q = query.toLowerCase()
113
- let score = 0
114
- const fields = [r.summary, r.id, r.description, r.mission, r.current_state, r.failure, r.root_cause, r.fix, r.prevention, r.command, r.project, r.scope, ...(Array.isArray(r.tags) ? r.tags : []), ...(Array.isArray(r.next_actions) ? r.next_actions : []), ...(Array.isArray(r.refs) ? r.refs : [])].filter(Boolean)
115
- for (const f of fields) {
116
- const str = String(f).toLowerCase()
117
- let idx = 0; let count = 0
118
- while ((idx = str.indexOf(q, idx)) !== -1) { count++; idx += q.length }
119
- score += count * 10
120
- if (str.startsWith(q)) score += 5
121
- if (str.includes(q)) score += 2
122
- }
123
- if (r.project && r.project.toLowerCase() === (project || "").toLowerCase()) score += 20
124
- if (r.project && project && r.project.toLowerCase().includes(project.toLowerCase())) score += 10
125
- const age = Date.now() - Date.parse(r.updated_at || r.created_at || 0)
126
- if (!Number.isNaN(age)) score += Math.max(0, 10 - age / 604800000)
127
- if (r.status === "active") score += 3
128
- if (r.status === "closed") score -= 2
129
- return score
130
- }
131
-
132
- function writeObject(cls, record) {
133
- const dir = classDir(cls)
134
- fs.mkdirSync(dir, { recursive: true })
135
- const fp = path.join(dir, `${record.id}.json`)
136
- atomicWriteJson(fp, record)
137
- const indexPath = path.join(dir, "index.json")
138
- let index = readJSON(indexPath, [])
139
- if (!Array.isArray(index)) index = []
140
- const idx = index.findIndex(e => e?.id === record.id)
141
- const entry = buildEntry(cls, record)
142
- if (idx >= 0) index[idx] = entry; else index.push(entry)
143
- atomicWriteJson(indexPath, index)
144
- }
145
-
146
- function upsertMistake(record) {
147
- const dir = classDir("mistake")
148
- fs.mkdirSync(dir, { recursive: true })
149
- const fp = path.join(dir, "mistakes.jsonl")
150
- let entries = readJSONL(fp)
151
- const idx = entries.findIndex(e => e?.id === record.id)
152
- if (idx >= 0) entries[idx] = record; else entries.push(record)
153
- const text = entries.map(e => stableStringify(e)).join("\n")
154
- fs.writeFileSync(fp, text ? `${text}\n` : "", "utf8")
155
- }
156
-
157
- export function handlePut(cls, id, dataStr) {
158
- let parsed
159
- try { parsed = JSON.parse(dataStr) } catch (e) { return `data must be valid JSON: ${e.message}` }
160
- if (!isPlainObject(parsed)) return "data must be a JSON object"
161
- if (!id?.trim()) return "non-blank id is required"
162
-
163
- const now = new Date().toISOString()
164
- const record = {
165
- ...parsed, id, class: cls,
166
- source: parsed.source ?? "agent",
167
- status: parsed.status ?? "active",
168
- created_at: parsed.created_at ?? now,
169
- updated_at: now,
170
- }
171
-
172
- const validation = validateSchemaSimple(record)
173
- if (!validation.ok) return `Validation errors: ${validation.errors.join("; ")}`
174
-
175
- if (cls === "mistake") upsertMistake(record)
176
- else writeObject(cls, record)
177
-
178
- return stableStringify({ ok: true, id })
179
- }
180
-
181
- export function handleGet(cls, id) {
182
- if (!id?.trim()) return "non-blank id is required"
183
- const record = queryGet(cls, id.trim())
184
- if (!record) return stableStringify({ ok: false, found: false })
185
- return stableStringify({ ok: true, record })
186
- }
187
-
188
- export function handleList(cls, limit = 10) {
189
- const entries = queryList(cls, Math.min(limit, 100))
190
- return stableStringify({ ok: true, count: entries.length, entries })
191
- }
192
-
193
- export function handleLatest(cls) {
194
- const list = queryList(cls, 100)
195
- const active = filterActive(list)
196
- if (!active[0]?.id) return stableStringify({ ok: false, found: false })
197
- const record = queryGet(cls, active[0].id)
198
- if (!record) return stableStringify({ ok: false, found: false })
199
- return stableStringify({ ok: true, record })
200
- }
201
-
202
- export function handleSearch(query, scope, classes, project, limit) {
203
- const q = (query || "").trim()
204
- if (!q) return "non-blank query is required"
205
- const clsList = Array.isArray(classes) && classes.length ? classes : CLASSES
206
- const lim = Math.min(limit ?? 10, 50)
207
- let records = []
208
- for (const cls of clsList) {
209
- if (cls === "mistake") {
210
- for (const m of readJSONL(path.join(classDir(cls), "mistakes.jsonl"))) {
211
- if (!hasExpired(m)) records.push(m)
212
- }
213
- } else {
214
- const dir = classDir(cls)
215
- let files = []
216
- try { files = fs.readdirSync(dir).filter(f => f.endsWith(".json") && f !== "index.json") } catch { continue }
217
- for (const f of files) {
218
- const r = readJSON(path.join(dir, f), null)
219
- if (r && !hasExpired(r)) records.push(r)
220
- }
221
- }
222
- }
223
- if (scope === "global") records = records.filter(r => r.scope === "global" || !r.scope)
224
- else if (scope === "local") records = records.filter(r => r.scope === "project" || r.scope === "session")
225
- const scored = records.map(r => ({ ...buildEntry(r.class || "verification_receipt", r), score: scoreRelevance(r, q, project || "") }))
226
- .filter(e => e.score > 0)
227
- .sort((a, b) => b.score - a.score)
228
- .slice(0, lim)
229
- return stableStringify({ ok: true, count: scored.length, query: q, results: scored })
230
- }
@@ -1,13 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin"
2
- import { handleGet } from "./_memory.mjs"
3
-
4
- export default tool({
5
- description: "Get a specific OpenHermes memory record by class and ID",
6
- args: {
7
- class: tool.schema.enum(["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]).describe("Memory class"),
8
- id: tool.schema.string().describe("Record ID to retrieve"),
9
- },
10
- async execute(args) {
11
- return handleGet(args.class, args.id)
12
- },
13
- })
@@ -1,12 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin"
2
- import { handleLatest } from "./_memory.mjs"
3
-
4
- export default tool({
5
- description: "Get the latest active OpenHermes memory record by class (returns the full record, not just metadata)",
6
- args: {
7
- class: tool.schema.enum(["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]).describe("Memory class to get the latest from"),
8
- },
9
- async execute(args) {
10
- return handleLatest(args.class)
11
- },
12
- })
@@ -1,13 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin"
2
- import { handleList } from "./_memory.mjs"
3
-
4
- export default tool({
5
- description: "List OpenHermes memory records by class, sorted by recency (newest first)",
6
- args: {
7
- class: tool.schema.enum(["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]).describe("Memory class to list"),
8
- limit: tool.schema.number().optional().default(10).describe("Max results (max 100)"),
9
- },
10
- async execute(args) {
11
- return handleList(args.class, args.limit)
12
- },
13
- })
@@ -1,14 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin"
2
- import { handlePut } from "./_memory.mjs"
3
-
4
- export default tool({
5
- description: "Create or update an OpenHermes memory record (checkpoints, mistakes, decisions, constraints, instincts, audits, backlog items, verification receipts)",
6
- args: {
7
- class: tool.schema.enum(["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]).describe("Memory class to write to"),
8
- id: tool.schema.string().describe("Unique record ID (e.g. 'chk_2026-01-01T00-00-00-000Z')"),
9
- data: tool.schema.string().describe("JSON string of the record fields (summary, scope, provenance, etc.)"),
10
- },
11
- async execute(args) {
12
- return handlePut(args.class, args.id, args.data)
13
- },
14
- })
@@ -1,16 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin"
2
- import { handleSearch } from "./_memory.mjs"
3
-
4
- export default tool({
5
- description: "Search OpenHermes memory records with keyword matching and relevance ranking. Searches across all classes by default.",
6
- args: {
7
- query: tool.schema.string().describe("Search query string (matches against summary, id, description, tags, etc.)"),
8
- scope: tool.schema.enum(["global", "local", "auto"]).optional().default("auto").describe("Search scope: global (only global-scope records), local (only project/session-scope), auto (all)"),
9
- classes: tool.schema.array(tool.schema.enum(["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"])).optional().describe("Memory classes to search (default: all)"),
10
- project: tool.schema.string().optional().describe("Project name filter for boosted relevance scoring"),
11
- limit: tool.schema.number().optional().default(10).describe("Max results (max 50)"),
12
- },
13
- async execute(args) {
14
- return handleSearch(args.query, args.scope, args.classes, args.project, args.limit)
15
- },
16
- })