openhermes 2.6.1 → 4.0.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.
Files changed (158) hide show
  1. package/CONTEXT.md +18 -0
  2. package/ETHOS.md +15 -0
  3. package/README.md +135 -292
  4. package/bootstrap.mjs +174 -499
  5. package/harness/agents/openhermes.md +87 -0
  6. package/harness/codex/CONSTITUTION.md +70 -148
  7. package/harness/codex/ROUTING.md +126 -0
  8. package/harness/commands/oh-doctor.md +26 -0
  9. package/harness/instructions/CONVENTIONS.md +206 -206
  10. package/harness/instructions/RUNTIME.md +54 -31
  11. package/harness/skills/oh-builder/SKILL.md +98 -0
  12. package/harness/skills/oh-caveman/SKILL.md +33 -0
  13. package/harness/skills/oh-expert/SKILL.md +121 -0
  14. package/harness/skills/oh-freeze/SKILL.md +28 -0
  15. package/harness/skills/oh-gauntlet/SKILL.md +119 -0
  16. package/harness/skills/oh-grill/SKILL.md +77 -0
  17. package/harness/skills/oh-guard/SKILL.md +33 -0
  18. package/harness/skills/oh-handoff/SKILL.md +33 -0
  19. package/harness/skills/oh-health/SKILL.md +90 -0
  20. package/harness/skills/oh-init/SKILL.md +78 -0
  21. package/harness/skills/oh-investigate/SKILL.md +35 -0
  22. package/harness/skills/oh-issue/SKILL.md +36 -0
  23. package/harness/skills/oh-learn/SKILL.md +28 -0
  24. package/harness/skills/oh-manifest/SKILL.md +84 -0
  25. package/harness/skills/oh-plan-review/SKILL.md +128 -0
  26. package/harness/skills/oh-planner/SKILL.md +157 -0
  27. package/harness/skills/oh-prd/SKILL.md +35 -0
  28. package/harness/skills/oh-retro/SKILL.md +33 -0
  29. package/harness/skills/oh-review/SKILL.md +110 -0
  30. package/harness/skills/oh-security/SKILL.md +110 -0
  31. package/harness/skills/oh-ship/SKILL.md +39 -0
  32. package/harness/skills/oh-skill-craft/SKILL.md +107 -0
  33. package/harness/skills/oh-skills-link/SKILL.md +29 -0
  34. package/harness/skills/oh-skills-list/SKILL.md +31 -0
  35. package/harness/skills/oh-triage/SKILL.md +36 -0
  36. package/index.mjs +3 -58
  37. package/lib/harness-resolver.mjs +77 -0
  38. package/lib/logger.mjs +62 -0
  39. package/package.json +49 -53
  40. package/test/plugins-behavioral.test.mjs +64 -0
  41. package/test/plugins.test.mjs +62 -0
  42. package/autorecall.mjs +0 -237
  43. package/curator.mjs +0 -455
  44. package/harness/commands/build-fix.md +0 -60
  45. package/harness/commands/checkpoint.md +0 -68
  46. package/harness/commands/code-review.md +0 -71
  47. package/harness/commands/doctor.md +0 -42
  48. package/harness/commands/eval.md +0 -89
  49. package/harness/commands/go-build.md +0 -87
  50. package/harness/commands/go-review.md +0 -71
  51. package/harness/commands/harness-audit.md +0 -90
  52. package/harness/commands/learn.md +0 -37
  53. package/harness/commands/loop-start.md +0 -38
  54. package/harness/commands/loop-status.md +0 -30
  55. package/harness/commands/memory-search.md +0 -37
  56. package/harness/commands/model-route.md +0 -32
  57. package/harness/commands/ohc.md +0 -13
  58. package/harness/commands/orchestrate.md +0 -88
  59. package/harness/commands/plan.md +0 -53
  60. package/harness/commands/quality-gate.md +0 -35
  61. package/harness/commands/refactor-clean.md +0 -102
  62. package/harness/commands/rust-build.md +0 -78
  63. package/harness/commands/rust-review.md +0 -65
  64. package/harness/commands/security.md +0 -93
  65. package/harness/commands/setup-pm.md +0 -65
  66. package/harness/commands/skill-create.md +0 -99
  67. package/harness/commands/test-coverage.md +0 -80
  68. package/harness/commands/update-codemaps.md +0 -81
  69. package/harness/commands/update-docs.md +0 -67
  70. package/harness/commands/verify.md +0 -68
  71. package/harness/prompts/architect.txt +0 -189
  72. package/harness/prompts/build-cpp.md +0 -98
  73. package/harness/prompts/build-error-resolver.md +0 -44
  74. package/harness/prompts/build-go.md +0 -340
  75. package/harness/prompts/build-java.md +0 -140
  76. package/harness/prompts/build-kotlin.md +0 -137
  77. package/harness/prompts/build-rust.md +0 -108
  78. package/harness/prompts/code-reviewer.md +0 -40
  79. package/harness/prompts/doc-updater.md +0 -206
  80. package/harness/prompts/docs-lookup.md +0 -71
  81. package/harness/prompts/e2e-runner.txt +0 -317
  82. package/harness/prompts/explore.md +0 -42
  83. package/harness/prompts/harness-optimizer.md +0 -42
  84. package/harness/prompts/loop-operator.md +0 -53
  85. package/harness/prompts/planner.md +0 -37
  86. package/harness/prompts/refactor-cleaner.md +0 -256
  87. package/harness/prompts/review-cpp.md +0 -81
  88. package/harness/prompts/review-database.md +0 -261
  89. package/harness/prompts/review-go.md +0 -257
  90. package/harness/prompts/review-java.md +0 -113
  91. package/harness/prompts/review-kotlin.md +0 -143
  92. package/harness/prompts/review-python.md +0 -101
  93. package/harness/prompts/review-rust.md +0 -77
  94. package/harness/prompts/security-reviewer.md +0 -42
  95. package/harness/prompts/tdd-guide.md +0 -228
  96. package/harness/rules/audit.md +0 -84
  97. package/harness/rules/checkpointing.md +0 -75
  98. package/harness/rules/context-loading.md +0 -33
  99. package/harness/rules/credential-exposure.md +0 -0
  100. package/harness/rules/delegation.md +0 -80
  101. package/harness/rules/handoff.md +0 -267
  102. package/harness/rules/memory-management.md +0 -28
  103. package/harness/rules/precedence.md +0 -52
  104. package/harness/rules/promotion.md +0 -46
  105. package/harness/rules/ranking.md +0 -64
  106. package/harness/rules/retrieval.md +0 -94
  107. package/harness/rules/runtime-guards.md +0 -196
  108. package/harness/rules/self-heal.md +0 -79
  109. package/harness/rules/session-start.md +0 -34
  110. package/harness/rules/skills-management.md +0 -165
  111. package/harness/rules/state-drift.md +0 -192
  112. package/harness/rules/verification.md +0 -88
  113. package/harness/scripts/sync-commands.mjs +0 -259
  114. package/harness/skills/.bundled_manifest +0 -17
  115. package/harness/skills/.usage.json +0 -6
  116. package/harness/skills/api-design/SKILL.md +0 -523
  117. package/harness/skills/backend-patterns/SKILL.md +0 -598
  118. package/harness/skills/coding-standards/SKILL.md +0 -549
  119. package/harness/skills/e2e-testing/SKILL.md +0 -326
  120. package/harness/skills/frontend-patterns/SKILL.md +0 -642
  121. package/harness/skills/frontend-slides/SKILL.md +0 -184
  122. package/harness/skills/security-review/SKILL.md +0 -495
  123. package/harness/skills/strategic-compact/SKILL.md +0 -131
  124. package/harness/skills/tdd-workflow/SKILL.md +0 -463
  125. package/harness/skills/verification-loop/SKILL.md +0 -126
  126. package/lib/ambient-memory.mjs +0 -167
  127. package/lib/handoff.mjs +0 -176
  128. package/lib/hardening.mjs +0 -128
  129. package/lib/memory-tools-plugin.mjs +0 -365
  130. package/lib/ohc/block-sync.mjs +0 -69
  131. package/lib/ohc/compress/search.mjs +0 -152
  132. package/lib/ohc/compress/state.mjs +0 -76
  133. package/lib/ohc/config.mjs +0 -186
  134. package/lib/ohc/message-ids.mjs +0 -168
  135. package/lib/ohc/notify.mjs +0 -154
  136. package/lib/ohc/protected-patterns.mjs +0 -54
  137. package/lib/ohc/prune-apply.mjs +0 -134
  138. package/lib/ohc/pruner.mjs +0 -610
  139. package/lib/ohc/reaper.mjs +0 -70
  140. package/lib/ohc/state.mjs +0 -266
  141. package/lib/ohc/strategies/deduplication.mjs +0 -72
  142. package/lib/ohc/strategies/index.mjs +0 -2
  143. package/lib/ohc/strategies/purge-errors.mjs +0 -43
  144. package/lib/ohc/token-utils.mjs +0 -26
  145. package/lib/ohc/updater.mjs +0 -133
  146. package/lib/paths.mjs +0 -50
  147. package/lib/schema-validator.mjs +0 -77
  148. package/lib/search.mjs +0 -48
  149. package/schemas/audit.schema.json +0 -82
  150. package/schemas/backlog.schema.json +0 -63
  151. package/schemas/checkpoint.schema.json +0 -65
  152. package/schemas/constraint.schema.json +0 -62
  153. package/schemas/decision.schema.json +0 -63
  154. package/schemas/instinct.schema.json +0 -63
  155. package/schemas/loop-state.schema.json +0 -33
  156. package/schemas/mistake.schema.json +0 -64
  157. package/schemas/verification_receipt.schema.json +0 -88
  158. package/skill-builder.mjs +0 -88
@@ -1,126 +0,0 @@
1
- ---
2
- name: verification-loop
3
- description: "A comprehensive verification system for Claude Code sessions."
4
- origin: ECC
5
- ---
6
-
7
- # Verification Loop Skill
8
-
9
- A comprehensive verification system for Claude Code sessions.
10
-
11
- ## When to Use
12
-
13
- Invoke this skill:
14
- - After completing a feature or significant code change
15
- - Before creating a PR
16
- - When you want to ensure quality gates pass
17
- - After refactoring
18
-
19
- ## Verification Phases
20
-
21
- ### Phase 1: Build Verification
22
- ```bash
23
- # Check if project builds
24
- npm run build 2>&1 | tail -20
25
- # OR
26
- pnpm build 2>&1 | tail -20
27
- ```
28
-
29
- If build fails, STOP and fix before continuing.
30
-
31
- ### Phase 2: Type Check
32
- ```bash
33
- # TypeScript projects
34
- npx tsc --noEmit 2>&1 | head -30
35
-
36
- # Python projects
37
- pyright . 2>&1 | head -30
38
- ```
39
-
40
- Report all type errors. Fix critical ones before continuing.
41
-
42
- ### Phase 3: Lint Check
43
- ```bash
44
- # JavaScript/TypeScript
45
- npm run lint 2>&1 | head -30
46
-
47
- # Python
48
- ruff check . 2>&1 | head -30
49
- ```
50
-
51
- ### Phase 4: Test Suite
52
- ```bash
53
- # Run tests with coverage
54
- npm run test -- --coverage 2>&1 | tail -50
55
-
56
- # Check coverage threshold
57
- # Target: 80% minimum
58
- ```
59
-
60
- Report:
61
- - Total tests: X
62
- - Passed: X
63
- - Failed: X
64
- - Coverage: X%
65
-
66
- ### Phase 5: Security Scan
67
- ```bash
68
- # Check for secrets
69
- grep -rn "sk-" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
70
- grep -rn "api_key" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
71
-
72
- # Check for console.log
73
- grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | head -10
74
- ```
75
-
76
- ### Phase 6: Diff Review
77
- ```bash
78
- # Show what changed
79
- git diff --stat
80
- git diff HEAD~1 --name-only
81
- ```
82
-
83
- Review each changed file for:
84
- - Unintended changes
85
- - Missing error handling
86
- - Potential edge cases
87
-
88
- ## Output Format
89
-
90
- After running all phases, produce a verification report:
91
-
92
- ```
93
- VERIFICATION REPORT
94
- ==================
95
-
96
- Build: [PASS/FAIL]
97
- Types: [PASS/FAIL] (X errors)
98
- Lint: [PASS/FAIL] (X warnings)
99
- Tests: [PASS/FAIL] (X/Y passed, Z% coverage)
100
- Security: [PASS/FAIL] (X issues)
101
- Diff: [X files changed]
102
-
103
- Overall: [READY/NOT READY] for PR
104
-
105
- Issues to Fix:
106
- 1. ...
107
- 2. ...
108
- ```
109
-
110
- ## Continuous Mode
111
-
112
- For long sessions, run verification every 15 minutes or after major changes:
113
-
114
- ```markdown
115
- Set a mental checkpoint:
116
- - After completing each function
117
- - After finishing a component
118
- - Before moving to next task
119
-
120
- Run: /verify
121
- ```
122
-
123
- ## Integration with Hooks
124
-
125
- This skill complements PostToolUse hooks but provides deeper verification.
126
- Hooks catch issues immediately; this skill provides comprehensive review.
@@ -1,167 +0,0 @@
1
- import path from "node:path"
2
- import fs from "node:fs"
3
- import { readJson, readJsonl, truncateText } from "./hardening.mjs"
4
- import { getMemoryRoot, getRecallRoot, getDataRoot } from "./paths.mjs"
5
- import { scoreRelevance } from "./search.mjs"
6
-
7
- const PLURALS = { audit: "audits", checkpoint: "checkpoints", mistake: "mistakes", instinct: "instincts", decision: "decisions", constraint: "constraints", backlog: "backlog", verification_receipt: "verification_receipts" }
8
- const CLASSES = ["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]
9
-
10
- const MEMORY_QUERY_PATTERNS = [
11
- /where\s+(did|do)\s+we\s+(leave\s+off|left\s+off)/i,
12
- /where\s+were\s+we/i,
13
- /what\s+(was|is)\s+(the\s+)?(last|latest|most\s+recent)\s+(checkpoint|decision|constraint|feature|task|change|issue|fix|bug|request|request)/i,
14
- /remind\s+me\s+(about|of)/i,
15
- /what\s+were\s+we\s+(working\s+on|doing)/i,
16
- /what\s+(happened|changed)\s+(last|yesterday|before|previously)/i,
17
- /previous\s+session/i,
18
- /last\s+time/i,
19
- /recap/i,
20
- /what\s+is\s+(the\s+)?(status|state)\s+of/i,
21
- /did\s+we\s+(decide|agree|finish|resolve|discuss)/i,
22
- /where\s+(did|do)\s+i\s+(leave\s+off|left\s+off)/i,
23
- ]
24
-
25
- function hasExpired(r) {
26
- if (r?.status === "expired" || r?.status === "decayed") return true
27
- if (r?.decay_at && Date.parse(r.decay_at) < Date.now()) return true
28
- if (r?.expires_at && Date.parse(r.expires_at) < Date.now()) return true
29
- return false
30
- }
31
-
32
- function filterActive(entries) { return entries.filter(e => !hasExpired(e)) }
33
-
34
- function detectMemoryQuery(text) {
35
- return MEMORY_QUERY_PATTERNS.some(p => p.test(text))
36
- }
37
-
38
- function extractSearchTerms(text) {
39
- const cleaned = text.replace(/<[^>]+>/g, "").replace(/[^\w\s-]/g, " ").trim()
40
- return cleaned.slice(0, 200)
41
- }
42
-
43
- function classDir(cls) { return path.join(getMemoryRoot(), PLURALS[cls]) }
44
-
45
- function readAllRecords() {
46
- const records = []
47
- for (const cls of CLASSES) {
48
- if (cls === "mistake") {
49
- for (const m of readJsonl(path.join(classDir(cls), "mistakes.jsonl"))) {
50
- if (!hasExpired(m)) records.push({ ...m, _cls: cls })
51
- }
52
- } else {
53
- const dir = classDir(cls)
54
- const index = readJson(path.join(dir, "index.json"), [])
55
- if (!Array.isArray(index)) continue
56
- for (const entry of index) {
57
- if (!hasExpired(entry)) {
58
- records.push({ ...entry, _cls: cls })
59
- }
60
- }
61
- }
62
- }
63
- return records
64
- }
65
-
66
- function autoSearch(query) {
67
- const q = (query || "").trim()
68
- if (!q) return []
69
-
70
- const records = readAllRecords()
71
- const scored = records
72
- .map(r => ({ ...r, _score: scoreRelevance(r, q, "") }))
73
- .filter(r => r._score > 0)
74
- .sort((a, b) => b._score - a._score)
75
- .slice(0, 5)
76
-
77
- return scored
78
- }
79
-
80
- function buildMemoryBlock() {
81
- const parts = []
82
-
83
- const ckIndex = readJson(path.join(getMemoryRoot(), "checkpoints", "index.json"), [])
84
- if (Array.isArray(ckIndex) && ckIndex.length > 0) {
85
- const latest = [...ckIndex].sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))[0]
86
- if (latest) {
87
- const cp = readJson(path.join(getMemoryRoot(), "checkpoints", `${latest.id}.json`), null)
88
- if (cp) {
89
- parts.push(`Checkpoint: ${cp.summary || "N/A"}`)
90
- if (cp.mission) parts.push(`Mission: ${truncateText(cp.mission, 200)}`)
91
- if (Array.isArray(cp.next_actions) && cp.next_actions.length) parts.push(`Next: ${cp.next_actions.slice(0, 2).join("; ")}`)
92
- }
93
- }
94
- }
95
-
96
- const ctIndex = readJson(path.join(getMemoryRoot(), "constraints", "index.json"), [])
97
- if (Array.isArray(ctIndex)) {
98
- const active = ctIndex.filter(e => e.status === "active")
99
- if (active.length) parts.push(`Constraints (${active.length} active)`)
100
- }
101
-
102
- const dcIndex = readJson(path.join(getMemoryRoot(), "decisions", "index.json"), [])
103
- if (Array.isArray(dcIndex)) {
104
- const recent = filterActive(dcIndex).slice(0, 2)
105
- if (recent.length) parts.push(`Recent decisions: ${recent.map(d => truncateText(d.summary || "", 80)).join(" | ")}`)
106
- }
107
-
108
- const bkIndex = readJson(path.join(getMemoryRoot(), "backlog", "index.json"), [])
109
- if (Array.isArray(bkIndex)) {
110
- const open = bkIndex.filter(e => e.status === "open")
111
- if (open.length) parts.push(`Backlog: ${open.length} open`)
112
- }
113
-
114
- return parts.length ? parts.join("\n") : "No active memory records found."
115
- }
116
-
117
- export const AmbientMemoryPlugin = async () => ({
118
- "experimental.chat.messages.transform": async (_input, output) => {
119
- if (!output.messages?.length) return
120
-
121
- const firstUser = output.messages.find(m => m.info?.role === "user")
122
- if (!firstUser?.parts?.length) return
123
-
124
- const hasMemoryTag = firstUser.parts.some(p => p.type === "text" && p.text.includes("OPENHERMES_MEMORY"))
125
-
126
- if (!hasMemoryTag) {
127
- const block = buildMemoryBlock()
128
- const tag = `<OPENHERMES_MEMORY>\n${block}\n</OPENHERMES_MEMORY>`
129
- const textPart = firstUser.parts.find(p => p.type === "text")
130
- if (textPart) {
131
- textPart.text = `${tag}\n\n${textPart.text}`
132
- }
133
- }
134
-
135
- const lastUser = [...output.messages].reverse().find(m => m.info?.role === "user")
136
- if (!lastUser) return
137
-
138
- const text = lastUser.parts?.find(p => p.type === "text")?.text || ""
139
- if (!text || !detectMemoryQuery(text)) return
140
-
141
- const terms = extractSearchTerms(text)
142
- if (!terms) return
143
-
144
- const results = autoSearch(terms)
145
- if (!results.length) return
146
-
147
- const contextBlock = [
148
- `<memory-context>`,
149
- `[System note: auto-retrieved from durable memory — NOT new user input]`,
150
- ...results.slice(0, 3).map(r => {
151
- const cls = r.class || "?";
152
- const label = `${cls}:${r.id}`
153
- return `- ${label}: ${truncateText(r.summary || "", 120)}`
154
- }),
155
- results.length > 3 ? ` ... and ${results.length - 3} more results` : null,
156
- `</memory-context>`,
157
- ].filter(Boolean).join("\n")
158
-
159
- const idx = output.messages.indexOf(lastUser)
160
- if (idx > 0) {
161
- output.messages.splice(idx, 0, {
162
- parts: [{ type: "text", text: contextBlock }],
163
- info: { role: "system" },
164
- })
165
- }
166
- }
167
- })
package/lib/handoff.mjs DELETED
@@ -1,176 +0,0 @@
1
- // OpenHermes Agent Handoff — lightweight helper library
2
- // Convention-based, no runtime deps. Agents import and call.
3
-
4
- let _idSeq = 0
5
- const COMPLEXITY_RULES = [
6
- { maxFiles: 2, patterns: 0, label: "easy" },
7
- { maxFiles: 10, patterns: 0, label: "medium" },
8
- { maxFiles: 60, patterns: 0, label: "hard" },
9
- ]
10
-
11
- function nextId() {
12
- _idSeq++
13
- return `ho_${Date.now().toString(36)}_${_idSeq}`
14
- }
15
-
16
- function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)) }
17
-
18
- // Estimate task complexity from file count and description.
19
- export function assessComplexity(fileCount = 1, description = "", patterns = []) {
20
- const pScore = Array.isArray(patterns) ? patterns.length : 0
21
- const level = fileCount > 50 || pScore > 5 ? "very-large"
22
- : fileCount > 10 || pScore > 2 ? "hard"
23
- : fileCount > 2 || pScore > 0 ? "medium"
24
- : "easy"
25
- return {
26
- level,
27
- fileCount: clamp(fileCount, 0, 9999),
28
- patterns: pScore,
29
- strategy: level === "easy" ? "direct"
30
- : level === "medium" ? "sequential-or-fan-out"
31
- : level === "hard" ? "sequential-multi"
32
- : "fan-out"
33
- }
34
- }
35
-
36
- // Build a structured handoff request string for the `task` tool prompt.
37
- export function handoffRequest(opts) {
38
- const {
39
- agent,
40
- phase = "execute",
41
- context = "",
42
- goal = "",
43
- expected = "",
44
- permissions = "",
45
- limits = "",
46
- complexity,
47
- } = opts || {}
48
- const id = nextId()
49
- const c = complexity || assessComplexity()
50
- const lines = [
51
- `## HANDOFF REQUEST`,
52
- `Agent: ${agent}`,
53
- `Task ID: ${id}`,
54
- `Phase: ${phase}`,
55
- `Complexity: ${c.level}`,
56
- ``,
57
- `### Context`,
58
- String(context).trim() || "(no context provided)",
59
- ``,
60
- `### Goal`,
61
- String(goal).trim() || "(no goal provided)",
62
- ``,
63
- `### Expected Output`,
64
- String(expected).trim() || "Return structured result with status, summary, details, receipts, next, learning.",
65
- ``,
66
- `### Permissions`,
67
- String(permissions).trim() || "(default permissions apply)",
68
- ``,
69
- `### Limits`,
70
- String(limits).trim() || "(no explicit limits)",
71
- ``,
72
- `### Handoff Result Format`,
73
- `When done, return:`,
74
- `## HANDOFF RESULT`,
75
- `Status: success | failure | partial`,
76
- `Task ID: ${id}`,
77
- `### Summary`,
78
- `### Details`,
79
- `### Receipts`,
80
- `### Next`,
81
- `### Learning`,
82
- ].join("\n")
83
- return { id, prompt: lines }
84
- }
85
-
86
- // Validate a subagent's result string has required sections.
87
- export function parseHandoffResult(text) {
88
- if (!text || typeof text !== "string") return { status: "invalid", error: "no result text" }
89
- const statusMatch = text.match(/Status:\s*(success|failure|partial)/i)
90
- const hasSummary = /### Summary/i.test(text)
91
- const hasDetails = /### Details/i.test(text)
92
- const hasReceipts = /### Receipts/i.test(text)
93
- const summaryMatch = text.match(/### Summary\s*\n\s*(.+)/i)
94
- return {
95
- status: statusMatch ? statusMatch[1].toLowerCase() : "unknown",
96
- summary: summaryMatch ? summaryMatch[1].trim() : "",
97
- hasSummary,
98
- hasDetails,
99
- hasReceipts,
100
- valid: !!(statusMatch && hasSummary),
101
- }
102
- }
103
-
104
- // Agent capability/role definitions for reference.
105
- export const AGENT_ROLES = {
106
- OpenHermes: { tier: 3, edit: true, exec: true, desc: "Primary agent — all tools, all permissions" },
107
- architect: { tier: 1, edit: false, exec: false, desc: "System architecture design" },
108
- planner: { tier: 1, edit: false, exec: false, desc: "Feature/refactor planning" },
109
- "code-reviewer": { tier: 1, edit: false, exec: false, desc: "Code quality review" },
110
- "security-reviewer": { tier: 1, edit: false, exec: false, desc: "Security audit (report only, no patches)" },
111
- explore: { tier: 1, edit: false, exec: false, desc: "Read-only codebase exploration" },
112
- "build-error-resolver": { tier: 2, edit: true, exec: true, desc: "Build/type error fixes" },
113
- "doc-updater": { tier: 2, edit: true, exec: true, desc: "Doc/codemap updates" },
114
- "refactor-cleaner": { tier: 2, edit: true, exec: true, desc: "Dead code cleanup" },
115
- "tdd-guide": { tier: 2, edit: true, exec: true, desc: "TDD red-green-refactor" },
116
- "loop-operator": { tier: 3, edit: true, exec: true, desc: "Managed autonomous loops" },
117
- "e2e-runner": { tier: 3, edit: true, exec: true, desc: "Playwright E2E tests" },
118
- "docs-lookup": { tier: 1, edit: false, exec: true, desc: "MCP doc lookup" },
119
- "harness-optimizer": { tier: 1, edit: false, exec: true, desc: "Harness config audit" },
120
- "review-database": { tier: 1, edit: false, exec: true, desc: "PostgreSQL review" },
121
- "review-go": { tier: 1, edit: false, exec: true, desc: "Go code review" },
122
- "build-go": { tier: 2, edit: true, exec: true, desc: "Go build fix" },
123
- "review-java": { tier: 1, edit: false, exec: true, desc: "Java review" },
124
- "build-java": { tier: 2, edit: true, exec: true, desc: "Java build fix" },
125
- "review-kotlin": { tier: 1, edit: false, exec: true, desc: "Kotlin review" },
126
- "build-kotlin": { tier: 2, edit: true, exec: true, desc: "Kotlin build fix" },
127
- "review-cpp": { tier: 1, edit: false, exec: true, desc: "C++ review" },
128
- "build-cpp": { tier: 2, edit: true, exec: true, desc: "C++ build fix" },
129
- "review-rust": { tier: 1, edit: false, exec: true, desc: "Rust review" },
130
- "build-rust": { tier: 2, edit: true, exec: true, desc: "Rust build fix" },
131
- "review-python": { tier: 1, edit: false, exec: true, desc: "Python review" },
132
- "general": { tier: 2, edit: true, exec: true, desc: "General purpose" },
133
- }
134
-
135
- // Suggest best agent for a task type.
136
- export function suggestAgent(taskType) {
137
- const map = {
138
- architecture: "architect",
139
- plan: "planner",
140
- "code-review": "code-reviewer",
141
- security: "security-reviewer",
142
- explore: "explore",
143
- "build-fix": "build-error-resolver",
144
- docs: "doc-updater",
145
- "dead-code": "refactor-cleaner",
146
- tdd: "tdd-guide",
147
- e2e: "e2e-runner",
148
- "doc-lookup": "docs-lookup",
149
- "db-review": "review-database",
150
- "go-review": "review-go",
151
- "go-build": "build-go",
152
- "java-review": "review-java",
153
- "java-build": "build-java",
154
- "kotlin-review": "review-kotlin",
155
- "kotlin-build": "build-kotlin",
156
- "cpp-review": "review-cpp",
157
- "cpp-build": "build-cpp",
158
- "rust-review": "review-rust",
159
- "rust-build": "build-rust",
160
- "python-review": "review-python",
161
- loop: "loop-operator",
162
- "harness-audit": "harness-optimizer",
163
- }
164
- return map[taskType] || null
165
- }
166
-
167
- // Quick permission check: can agent X do action Y?
168
- export function canAgent(agentName, action) {
169
- const role = AGENT_ROLES[agentName]
170
- if (!role) return false
171
- if (action === "edit") return role.edit
172
- if (action === "exec" || action === "bash") return role.exec
173
- if (action === "read") return true
174
- if (action === "delegate") return role.tier >= 1
175
- return false
176
- }
package/lib/hardening.mjs DELETED
@@ -1,128 +0,0 @@
1
- import crypto from "node:crypto"
2
- import fs from "node:fs"
3
- import os from "node:os"
4
- import path from "node:path"
5
-
6
- const SECRET_KEY_PATTERN = /(token|secret|password|passwd|passphrase|api[-_ ]?key|access[-_ ]?key|refresh[-_ ]?token|authorization|cookie|bearer)/i
7
- const TEXT_REDACTIONS = [
8
- [/\bsk-[A-Za-z0-9]{16,}\b/g, "[REDACTED]"],
9
- [/\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g, "[REDACTED]"],
10
- [/\bxox[baprs]-[A-Za-z0-9-]+\b/g, "[REDACTED]"],
11
- [/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}\b/gi, "Bearer [REDACTED]"],
12
- [/\b[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/g, "[REDACTED]"],
13
- ]
14
-
15
- function isPlainObject(value) {
16
- return !!value && typeof value === "object" && !Array.isArray(value)
17
- }
18
-
19
- function truncateText(text, limit = 12000) {
20
- const value = String(text ?? "")
21
- if (limit <= 0 || value.length <= limit) return value
22
- const suffix = "...[truncated]"
23
- const sliceLength = Math.max(0, limit - suffix.length)
24
- return value.slice(0, sliceLength) + suffix
25
- }
26
-
27
- function redactSensitiveText(text) {
28
- let output = String(text ?? "")
29
- for (const [pattern, replacement] of TEXT_REDACTIONS) {
30
- output = output.replace(pattern, replacement)
31
- }
32
- return output
33
- }
34
-
35
- function sanitizeValue(value, key = "", options = {}) {
36
- const maxStringLength = Number.isFinite(options.maxStringLength) ? options.maxStringLength : 12000
37
- if (value === null || value === undefined) return value
38
- if (typeof value === "string") return truncateText(redactSensitiveText(value), maxStringLength)
39
- if (typeof value === "number" || typeof value === "boolean") return value
40
- if (Array.isArray(value)) return value.map(item => sanitizeValue(item, key, options))
41
- if (isPlainObject(value)) {
42
- const redacted = {}
43
- for (const [childKey, childValue] of Object.entries(value)) {
44
- redacted[childKey] = SECRET_KEY_PATTERN.test(childKey)
45
- ? "[REDACTED]"
46
- : sanitizeValue(childValue, childKey, options)
47
- }
48
- return redacted
49
- }
50
- return truncateText(redactSensitiveText(String(value)), maxStringLength)
51
- }
52
-
53
- function sanitizeRecord(record, options = {}) {
54
- return sanitizeValue(record, "", options)
55
- }
56
-
57
- function fingerprintEnvironment(input = {}) {
58
- const fingerprint = {
59
- cwd: path.resolve(input.cwd || process.cwd()),
60
- harness_root: input.harnessRoot ? path.resolve(input.harnessRoot) : null,
61
- project_root: input.projectRoot ? path.resolve(input.projectRoot) : null,
62
- project: input.project || null,
63
- session_id: input.sessionId || null,
64
- os: process.platform,
65
- release: os.release(),
66
- arch: process.arch,
67
- shell: process.env.ComSpec || process.env.COMSPEC || "cmd.exe",
68
- provider: process.env.OPENCODE_PROVIDER || "lmstudio",
69
- model: process.env.OPENCODE_MODEL || null,
70
- }
71
- const sha256 = crypto.createHash("sha256").update(JSON.stringify(fingerprint)).digest("hex")
72
- return { ...fingerprint, sha256 }
73
- }
74
-
75
- function fingerprintFile(filePath) {
76
- try {
77
- const stat = fs.statSync(filePath)
78
- const content = fs.readFileSync(filePath)
79
- return {
80
- path: path.normalize(filePath),
81
- mtime: stat.mtime.toISOString(),
82
- size: stat.size,
83
- sha256: crypto.createHash("sha256").update(content).digest("hex"),
84
- }
85
- } catch {
86
- return null
87
- }
88
- }
89
-
90
- function atomicWriteJson(filePath, data) {
91
- const dir = path.dirname(filePath)
92
- fs.mkdirSync(dir, { recursive: true })
93
- const tmpPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`)
94
- const payload = `${JSON.stringify(data, null, 2)}\n`
95
- fs.writeFileSync(tmpPath, payload, "utf8")
96
- try {
97
- fs.renameSync(tmpPath, filePath)
98
- } catch (err) {
99
- fs.copyFileSync(tmpPath, filePath)
100
- fs.rmSync(tmpPath, { force: true })
101
- }
102
- }
103
-
104
- function isTruthy(value) {
105
- return /^(1|true|yes|on)$/i.test(String(value || ""))
106
- }
107
-
108
- function readJson(fp, fallback) {
109
- try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
110
- }
111
-
112
- function readJsonl(fp) {
113
- try {
114
- return fs.readFileSync(fp, "utf8").trim().split("\n").filter(Boolean).map(l => JSON.parse(l))
115
- } catch { return [] }
116
- }
117
-
118
- function buildEnvironmentFingerprint(root, directory, project) {
119
- return fingerprintEnvironment({
120
- cwd: directory,
121
- harnessRoot: root,
122
- projectRoot: directory,
123
- project: project?.name || path.basename(directory),
124
- sessionId: project?.session_id || null,
125
- })
126
- }
127
-
128
- export { atomicWriteJson, buildEnvironmentFingerprint, fingerprintEnvironment, fingerprintFile, isPlainObject, isTruthy, readJson, readJsonl, redactSensitiveText, sanitizeRecord, truncateText }