openhermes 4.3.0 → 4.9.2
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/CONTEXT.md +9 -0
- package/README.md +26 -15
- package/bootstrap.ts +161 -124
- package/harness/agents/oh-browser.md +97 -0
- package/harness/agents/oh-builder.md +78 -0
- package/harness/agents/oh-facade.md +75 -0
- package/harness/agents/oh-fusion.md +45 -0
- package/harness/agents/oh-gauntlet.md +71 -0
- package/harness/agents/oh-grill.md +71 -0
- package/harness/agents/oh-investigate.md +60 -0
- package/harness/agents/oh-manifest.md +95 -0
- package/harness/agents/oh-plan-review.md +40 -0
- package/harness/agents/oh-planner.md +50 -0
- package/harness/agents/oh-refactor.md +37 -0
- package/harness/agents/oh-retro.md +46 -0
- package/harness/agents/oh-review.md +85 -0
- package/harness/agents/oh-security.md +83 -0
- package/harness/agents/oh-ship.md +76 -0
- package/harness/agents/oh-skill-craft.md +38 -0
- package/harness/agents/openhermes.md +107 -53
- package/harness/codex/AUTOPILOT.md +143 -91
- package/harness/codex/CHARTER.md +81 -0
- package/harness/commands/oh-doctor.md +193 -14
- package/harness/instructions/SHELL.md +76 -0
- package/harness/skills/oh-ascii/DEEP.md +292 -0
- package/harness/skills/oh-ascii/SKILL.md +31 -0
- package/harness/skills/oh-ascii/scripts/check_ascii_alignment.py +596 -0
- package/harness/skills/oh-browser/DEEP.md +54 -0
- package/harness/skills/oh-browser/SKILL.md +30 -0
- package/harness/skills/oh-builder/DEEP.md +63 -0
- package/harness/skills/oh-builder/SKILL.md +12 -90
- package/harness/skills/oh-expert/DEEP.md +85 -0
- package/harness/skills/oh-expert/SKILL.md +13 -106
- package/harness/skills/oh-facade/DEEP.md +182 -0
- package/harness/skills/oh-facade/SKILL.md +15 -279
- package/harness/skills/oh-freeze/DEEP.md +18 -0
- package/harness/skills/oh-freeze/SKILL.md +10 -19
- package/harness/skills/oh-full-output/DEEP.md +25 -0
- package/harness/skills/oh-full-output/SKILL.md +12 -65
- package/harness/skills/oh-fusion/DEEP.md +120 -0
- package/harness/skills/oh-fusion/SKILL.md +17 -295
- package/harness/skills/oh-gauntlet/DEEP.md +77 -0
- package/harness/skills/oh-gauntlet/SKILL.md +13 -105
- package/harness/skills/oh-grill/DEEP.md +51 -0
- package/harness/skills/oh-grill/SKILL.md +12 -63
- package/harness/skills/oh-guard/DEEP.md +19 -0
- package/harness/skills/oh-guard/SKILL.md +10 -24
- package/harness/skills/oh-handoff/DEEP.md +48 -0
- package/harness/skills/oh-handoff/SKILL.md +13 -23
- package/harness/skills/oh-health/DEEP.md +74 -0
- package/harness/skills/oh-health/SKILL.md +13 -76
- package/harness/skills/oh-init/DEEP.md +85 -0
- package/harness/skills/oh-init/SKILL.md +13 -127
- package/harness/skills/oh-investigate/DEEP.md +171 -0
- package/harness/skills/oh-investigate/SKILL.md +13 -66
- package/harness/skills/oh-issue/DEEP.md +21 -0
- package/harness/skills/oh-issue/SKILL.md +11 -27
- package/harness/skills/oh-learn/DEEP.md +44 -0
- package/harness/skills/oh-learn/SKILL.md +12 -83
- package/harness/skills/oh-manifest/DEEP.md +92 -0
- package/harness/skills/oh-manifest/SKILL.md +11 -108
- package/harness/skills/oh-plan-review/DEEP.md +90 -0
- package/harness/skills/oh-plan-review/SKILL.md +13 -115
- package/harness/skills/oh-planner/DEEP.md +172 -0
- package/harness/skills/oh-planner/SKILL.md +12 -149
- package/harness/skills/oh-prd/DEEP.md +45 -0
- package/harness/skills/oh-prd/SKILL.md +10 -26
- package/harness/skills/oh-refactor/DEEP.md +122 -0
- package/harness/skills/oh-refactor/SKILL.md +17 -410
- package/harness/skills/oh-retro/DEEP.md +26 -0
- package/harness/skills/oh-retro/SKILL.md +12 -24
- package/harness/skills/oh-review/DEEP.md +87 -0
- package/harness/skills/oh-review/SKILL.md +11 -97
- package/harness/skills/oh-security/DEEP.md +83 -0
- package/harness/skills/oh-security/SKILL.md +14 -96
- package/harness/skills/oh-ship/DEEP.md +141 -0
- package/harness/skills/oh-ship/SKILL.md +13 -31
- package/harness/skills/oh-skill-craft/DEEP.md +369 -0
- package/harness/skills/oh-skill-craft/SKILL.md +17 -178
- package/harness/skills/oh-skills-link/DEEP.md +16 -0
- package/harness/skills/oh-skills-link/SKILL.md +10 -20
- package/harness/skills/oh-skills-list/DEEP.md +20 -0
- package/harness/skills/oh-skills-list/SKILL.md +9 -22
- package/harness/skills/oh-triage/DEEP.md +23 -0
- package/harness/skills/oh-triage/SKILL.md +8 -24
- package/harness/skills/oh-worktree/DEEP.md +169 -0
- package/harness/skills/oh-worktree/SKILL.md +32 -0
- package/lib/harness-resolver.ts +8 -10
- package/package.json +5 -3
- package/scripts/count-tokens.mjs +158 -0
- package/scripts/oh-doctor.ps1 +342 -0
- package/harness/codex/CONSTITUTION.md +0 -73
- package/harness/codex/ROUTING.md +0 -92
- package/harness/instructions/RUNTIME.md +0 -30
- package/harness/skills/oh-caveman/SKILL.md +0 -42
- package/lib/logger.ts +0 -75
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Quick-win diagnostic for openhermes-pkg — AI-orchestrator-facing.
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Outputs JSON-Lines diagnostics consumable by the OpenHermes orchestrator.
|
|
6
|
+
Each line is a check result; final line is the summary verdict.
|
|
7
|
+
.PARAMETER SkipOCChecks
|
|
8
|
+
Skip opencode CLI checks (useful when opencode isn't running).
|
|
9
|
+
.EXAMPLE
|
|
10
|
+
.\oh-doctor.ps1
|
|
11
|
+
.\oh-doctor.ps1 -SkipOCChecks
|
|
12
|
+
#>
|
|
13
|
+
|
|
14
|
+
param(
|
|
15
|
+
[switch]$SkipOCChecks
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
$PackageRoot = Resolve-Path "$PSScriptRoot\.."
|
|
19
|
+
$ErrorActionPreference = "Continue"
|
|
20
|
+
$script:checks = @()
|
|
21
|
+
$script:checks = @()
|
|
22
|
+
|
|
23
|
+
function Write-Check {
|
|
24
|
+
param(
|
|
25
|
+
[Parameter(Mandatory)] [string]$Id,
|
|
26
|
+
[Parameter(Mandatory)] [string]$Name,
|
|
27
|
+
[Parameter(Mandatory)] [ValidateSet("PASS","WARN","FAIL","SKIP")] [string]$Status,
|
|
28
|
+
[string]$Detail = "",
|
|
29
|
+
[hashtable]$Evidence = @{},
|
|
30
|
+
[string]$Severity = "medium",
|
|
31
|
+
[hashtable]$Fix = @{}
|
|
32
|
+
)
|
|
33
|
+
$result = @{
|
|
34
|
+
id = $Id
|
|
35
|
+
name = $Name
|
|
36
|
+
status = $Status
|
|
37
|
+
severity = $Severity
|
|
38
|
+
detail = $Detail
|
|
39
|
+
evidence = $Evidence
|
|
40
|
+
fix = $Fix
|
|
41
|
+
}
|
|
42
|
+
$script:checks += $result
|
|
43
|
+
$result | ConvertTo-Json -Compress -Depth 5
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# ============================================================
|
|
47
|
+
# CHECK 1: AGENTS.md accuracy
|
|
48
|
+
# ============================================================
|
|
49
|
+
$agentPath = Join-Path $PackageRoot "AGENTS.md"
|
|
50
|
+
if (Test-Path $agentPath) {
|
|
51
|
+
$agentContent = Get-Content $agentPath -Raw
|
|
52
|
+
$skillDir = Join-Path (Join-Path $PackageRoot "harness") "skills"
|
|
53
|
+
$actualCount = (Get-ChildItem $skillDir -Directory).Count
|
|
54
|
+
|
|
55
|
+
# Skill count check
|
|
56
|
+
if ($agentContent -match '## Skills \((\d+)\)') {
|
|
57
|
+
$claimedCount = [int]$Matches[1]
|
|
58
|
+
if ($claimedCount -eq $actualCount) {
|
|
59
|
+
Write-Check -Id "agentsmd.skill_count" -Name "AGENTS.md skill count" -Status PASS `
|
|
60
|
+
-Detail "Claims $claimedCount, filesystem has $actualCount" `
|
|
61
|
+
-Evidence @{ file = $agentPath; claimed = $claimedCount; actual = $actualCount }
|
|
62
|
+
} else {
|
|
63
|
+
Write-Check -Id "agentsmd.skill_count" -Name "AGENTS.md skill count" -Status FAIL `
|
|
64
|
+
-Severity high -Detail "Claims $claimedCount but filesystem has $actualCount" `
|
|
65
|
+
-Evidence @{ file = $agentPath; claimed = $claimedCount; actual = $actualCount } `
|
|
66
|
+
-Fix @{ auto = $true; command = "Update AGENTS.md header from ${claimedCount} skills to ${actualCount} skills" }
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
Write-Check -Id "agentsmd.skill_count" -Name "AGENTS.md skill count" -Status WARN `
|
|
70
|
+
-Detail "Could not parse skill count from AGENTS.md" `
|
|
71
|
+
-Evidence @{ file = $agentPath }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# logger.ts reference
|
|
75
|
+
$libDir = Join-Path $PackageRoot "lib"
|
|
76
|
+
$libFiles = Get-ChildItem $libDir -File | ForEach-Object { $_.Name }
|
|
77
|
+
if ($agentContent -match "logger\.ts") {
|
|
78
|
+
if ($libFiles -contains "logger.ts") {
|
|
79
|
+
Write-Check -Id "agentsmd.logger_ref" -Name "AGENTS.md logger.ts reference" -Status PASS `
|
|
80
|
+
-Detail "logger.ts exists in lib/" `
|
|
81
|
+
-Evidence @{ file = $agentPath }
|
|
82
|
+
} else {
|
|
83
|
+
$actualList = $libFiles -join ","
|
|
84
|
+
Write-Check -Id "agentsmd.logger_ref" -Name "AGENTS.md logger.ts reference" -Status FAIL `
|
|
85
|
+
-Severity high -Detail "Claims logger.ts but file does not exist" `
|
|
86
|
+
-Evidence @{ file = $agentPath; claimed = "logger.ts"; onDisk = $actualList } `
|
|
87
|
+
-Fix @{ auto = $true; command = "Remove 'logger.ts' from AGENTS.md" }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# SHELL.md reference
|
|
92
|
+
$instDir = Join-Path (Join-Path $PackageRoot "harness") "instructions"
|
|
93
|
+
$hasShell = Test-Path (Join-Path $instDir "SHELL.md")
|
|
94
|
+
if ($agentContent -match "SHELL\.md") {
|
|
95
|
+
if ($hasShell) {
|
|
96
|
+
Write-Check -Id "agentsmd.shell_ref" -Name "AGENTS.md SHELL.md reference" -Status PASS `
|
|
97
|
+
-Detail "SHELL.md referenced and exists on disk" `
|
|
98
|
+
-Evidence @{ file = $agentPath; referenced = "SHELL.md" }
|
|
99
|
+
} else {
|
|
100
|
+
Write-Check -Id "agentsmd.shell_ref" -Name "AGENTS.md SHELL.md reference" -Status WARN `
|
|
101
|
+
-Severity medium -Detail "SHELL.md referenced but missing from disk" `
|
|
102
|
+
-Evidence @{ file = $agentPath; referenced = "SHELL.md" }
|
|
103
|
+
}
|
|
104
|
+
} elseif ($hasShell) {
|
|
105
|
+
Write-Check -Id "agentsmd.shell_ref" -Name "AGENTS.md SHELL.md reference" -Status WARN `
|
|
106
|
+
-Severity low -Detail "SHELL.md exists on disk but AGENTS.md does not mention it" `
|
|
107
|
+
-Evidence @{ file = $agentPath; onDisk = "harness/instructions/SHELL.md" }
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
Write-Check -Id "agentsmd.exists" -Name "AGENTS.md exists" -Status FAIL -Severity high `
|
|
111
|
+
-Detail "File not found at ${agentPath}" `
|
|
112
|
+
-Evidence @{ path = $agentPath }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# ============================================================
|
|
116
|
+
# CHECK 2: package.json files integrity
|
|
117
|
+
# ============================================================
|
|
118
|
+
$pkgPath = Join-Path $PackageRoot "package.json"
|
|
119
|
+
if (Test-Path $pkgPath) {
|
|
120
|
+
$pkg = Get-Content $pkgPath -Raw | ConvertFrom-Json
|
|
121
|
+
$missing = @()
|
|
122
|
+
$found = @()
|
|
123
|
+
foreach ($entry in $pkg.files) {
|
|
124
|
+
$resolved = Join-Path $PackageRoot $entry
|
|
125
|
+
if (Test-Path $resolved) { $found += $entry }
|
|
126
|
+
else { $missing += $entry }
|
|
127
|
+
}
|
|
128
|
+
if ($missing.Count -eq 0) {
|
|
129
|
+
Write-Check -Id "pkg.files_integrity" -Name "package.json files field" -Status PASS `
|
|
130
|
+
-Detail "All $($pkg.files.Count) entries exist on disk" `
|
|
131
|
+
-Evidence @{ file = $pkgPath; entries = @($pkg.files) }
|
|
132
|
+
} else {
|
|
133
|
+
Write-Check -Id "pkg.files_integrity" -Name "package.json files field" -Status FAIL `
|
|
134
|
+
-Severity high -Detail "$($missing.Count) entry(s) missing: $($missing -join ',')" `
|
|
135
|
+
-Evidence @{ file = $pkgPath; missing = $missing } `
|
|
136
|
+
-Fix @{ auto = $false; command = "Create or remove from files: $($missing -join ',')" }
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
Write-Check -Id "pkg.exists" -Name "package.json exists" -Status FAIL -Severity high `
|
|
140
|
+
-Detail "File not found at ${pkgPath}" `
|
|
141
|
+
-Evidence @{ path = $pkgPath }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# ============================================================
|
|
145
|
+
# CHECK 3: Harness resolver prerequisites
|
|
146
|
+
# ============================================================
|
|
147
|
+
# Hardcode the exact known paths — no array gymnastics
|
|
148
|
+
$hDir = Join-Path $PackageRoot "harness"
|
|
149
|
+
$requiredFiles = @(
|
|
150
|
+
,@("codex", "CHARTER.md")
|
|
151
|
+
,@("instructions", "SHELL.md")
|
|
152
|
+
,@("skills", "oh-planner", "SKILL.md")
|
|
153
|
+
)
|
|
154
|
+
$allOk = $true
|
|
155
|
+
$missingReq = @()
|
|
156
|
+
foreach ($pair in $requiredFiles) {
|
|
157
|
+
$path = Join-Path $hDir (Join-Path $pair[0] $pair[1])
|
|
158
|
+
if (-not (Test-Path $path)) {
|
|
159
|
+
$allOk = $false
|
|
160
|
+
$missingReq += ($pair[0] + "/" + $pair[1])
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if ($allOk) {
|
|
164
|
+
Write-Check -Id "harness.required_files" -Name "Harness resolver prerequisites" -Status PASS `
|
|
165
|
+
-Detail "All 3 required files resolve" `
|
|
166
|
+
-Evidence @{ root = $hDir; required = @("codex/CHARTER.md","codex/AUTOPILOT.md","skills/oh-planner/SKILL.md") }
|
|
167
|
+
} else {
|
|
168
|
+
Write-Check -Id "harness.required_files" -Name "Harness resolver prerequisites" -Status FAIL `
|
|
169
|
+
-Severity high -Detail "Missing: $($missingReq -join ',')" `
|
|
170
|
+
-Evidence @{ root = $hDir; missing = $missingReq } `
|
|
171
|
+
-Fix @{ auto = $false; command = "Create missing: $($missingReq -join ',')" }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# ============================================================
|
|
175
|
+
# CHECK 4: Test health (direct & call from PowerShell — works)
|
|
176
|
+
# ============================================================
|
|
177
|
+
Push-Location $PackageRoot
|
|
178
|
+
$testOut = & "bun" "test" 2>&1 | Out-String
|
|
179
|
+
Pop-Location
|
|
180
|
+
$passCount = 0; $failCount = 0; $totalCount = 0
|
|
181
|
+
if ($testOut -match '(\d+) pass') { $passCount = [int]$Matches[1] }
|
|
182
|
+
if ($testOut -match '(\d+) fail') { $failCount = [int]$Matches[1] }
|
|
183
|
+
if ($testOut -match 'Ran (\d+) tests') { $totalCount = [int]$Matches[1] }
|
|
184
|
+
|
|
185
|
+
if ($failCount -eq 0 -and $totalCount -gt 0) {
|
|
186
|
+
Write-Check -Id "test.health" -Name "Test health" -Status PASS `
|
|
187
|
+
-Severity high -Detail "$passCount pass, $failCount fail ($totalCount total)" `
|
|
188
|
+
-Evidence @{ pass = $passCount; fail = $failCount; total = $totalCount }
|
|
189
|
+
} elseif ($totalCount -eq 0) {
|
|
190
|
+
$truncated = $testOut.Substring(0, [Math]::Min(200, $testOut.Length))
|
|
191
|
+
Write-Check -Id "test.health" -Name "Test health" -Status FAIL `
|
|
192
|
+
-Severity high -Detail "No test output" `
|
|
193
|
+
-Evidence @{ outputTruncated = $truncated } `
|
|
194
|
+
-Fix @{ auto = $false; command = "bun test" }
|
|
195
|
+
} else {
|
|
196
|
+
$truncated = $testOut.Substring(0, [Math]::Min(500, $testOut.Length))
|
|
197
|
+
Write-Check -Id "test.health" -Name "Test health" -Status FAIL `
|
|
198
|
+
-Severity high -Detail "$passCount pass, $failCount fail ($totalCount total)" `
|
|
199
|
+
-Evidence @{ pass = $passCount; fail = $failCount; total = $totalCount; outputTruncated = $truncated } `
|
|
200
|
+
-Fix @{ auto = $false; command = "bun test" }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# ============================================================
|
|
204
|
+
# CHECK 5: TypeScript compilation
|
|
205
|
+
# ============================================================
|
|
206
|
+
Push-Location $PackageRoot
|
|
207
|
+
$tscOut = & "bunx" "tsc" "--noEmit" 2>&1 | Out-String
|
|
208
|
+
Pop-Location
|
|
209
|
+
if ([string]::IsNullOrWhiteSpace($tscOut) -or $tscOut.Trim() -eq "") {
|
|
210
|
+
Write-Check -Id "tsc.compilation" -Name "TypeScript compilation" -Status PASS `
|
|
211
|
+
-Severity high -Detail "Clean compilation (strict mode, noEmit)" `
|
|
212
|
+
-Evidence @{ command = "bunx tsc --noEmit"; errors = 0 }
|
|
213
|
+
} else {
|
|
214
|
+
$errorCount = ($tscOut.Trim() -split "`n").Count
|
|
215
|
+
$truncated = $tscOut.Substring(0, [Math]::Min(1000, $tscOut.Length))
|
|
216
|
+
Write-Check -Id "tsc.compilation" -Name "TypeScript compilation" -Status FAIL `
|
|
217
|
+
-Severity high -Detail "$errorCount error(s)" `
|
|
218
|
+
-Evidence @{ command = "bunx tsc --noEmit"; errors = $errorCount; outputTruncated = $truncated } `
|
|
219
|
+
-Fix @{ auto = $false; command = "bunx tsc --noEmit" }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# ============================================================
|
|
223
|
+
# CHECK 6: No secrets in repo
|
|
224
|
+
# ============================================================
|
|
225
|
+
$patterns = @("*.env*", "*.key", "*secret*", "credentials*", "auth.json", "*.pem")
|
|
226
|
+
$secretsFound = @()
|
|
227
|
+
foreach ($pattern in $patterns) {
|
|
228
|
+
$matches = Get-ChildItem -Path $PackageRoot -Recurse -Filter $pattern -ErrorAction SilentlyContinue `
|
|
229
|
+
| Where-Object { -not ($_.FullName -like "*node_modules*") }
|
|
230
|
+
foreach ($m in $matches) { $secretsFound += $m.FullName }
|
|
231
|
+
}
|
|
232
|
+
if ($secretsFound.Count -eq 0) {
|
|
233
|
+
Write-Check -Id "security.secrets" -Name "No secrets in repo" -Status PASS `
|
|
234
|
+
-Severity high -Detail "No .env, *.key, credentials, or auth.json found" `
|
|
235
|
+
-Evidence @{ patternsScanned = $patterns; found = @() }
|
|
236
|
+
} else {
|
|
237
|
+
Write-Check -Id "security.secrets" -Name "No secrets in repo" -Status FAIL `
|
|
238
|
+
-Severity high -Detail "Found $($secretsFound.Count) sensitive file(s)" `
|
|
239
|
+
-Evidence @{ patternsScanned = $patterns; found = $secretsFound } `
|
|
240
|
+
-Fix @{ auto = $false; command = "Remove sensitive files and update .gitignore" }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# ============================================================
|
|
244
|
+
# CHECK 7: .gitignore coverage
|
|
245
|
+
# ============================================================
|
|
246
|
+
$gitignorePath = Join-Path $PackageRoot ".gitignore"
|
|
247
|
+
if (Test-Path $gitignorePath) {
|
|
248
|
+
$gitignoreContent = Get-Content $gitignorePath -Raw
|
|
249
|
+
$expected = @("node_modules", ".config", ".opencode", "PLAN.d", "coverage", "*.tgz")
|
|
250
|
+
$missing = @()
|
|
251
|
+
$present = @()
|
|
252
|
+
foreach ($e in $expected) {
|
|
253
|
+
if ($gitignoreContent -match [regex]::Escape($e)) { $present += $e }
|
|
254
|
+
else { $missing += $e }
|
|
255
|
+
}
|
|
256
|
+
if ($missing.Count -eq 0) {
|
|
257
|
+
Write-Check -Id "safety.gitignore" -Name ".gitignore coverage" -Status PASS `
|
|
258
|
+
-Severity medium -Detail "All expected entries present" `
|
|
259
|
+
-Evidence @{ file = $gitignorePath; entries = $expected }
|
|
260
|
+
} else {
|
|
261
|
+
Write-Check -Id "safety.gitignore" -Name ".gitignore coverage" -Status WARN `
|
|
262
|
+
-Severity medium -Detail "Missing: $($missing -join ',')" `
|
|
263
|
+
-Evidence @{ file = $gitignorePath; present = $present; missing = $missing }
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
Write-Check -Id "safety.gitignore" -Name ".gitignore exists" -Status FAIL -Severity high `
|
|
267
|
+
-Detail "File not found at ${gitignorePath}" `
|
|
268
|
+
-Evidence @{ path = $gitignorePath }
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# ============================================================
|
|
272
|
+
# CHECK 8-10: Runtime state via opencode CLI
|
|
273
|
+
# ============================================================
|
|
274
|
+
if (-not $SkipOCChecks) {
|
|
275
|
+
$pathsOut = & "opencode" "debug" "paths" 2>&1 | Out-String
|
|
276
|
+
if ($LASTEXITCODE -eq 0 -and $pathsOut.Trim().Length -gt 0) {
|
|
277
|
+
Write-Check -Id "oc.paths" -Name "opencode paths" -Status PASS -Severity low `
|
|
278
|
+
-Detail "Paths resolved" -Evidence @{ raw = $pathsOut.Trim() }
|
|
279
|
+
} else {
|
|
280
|
+
Write-Check -Id "oc.paths" -Name "opencode paths" -Status SKIP -Severity low `
|
|
281
|
+
-Detail "opencode CLI not available (exit $LASTEXITCODE)" `
|
|
282
|
+
-Evidence @{ exitCode = $LASTEXITCODE }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
$configOut = & "opencode" "debug" "config" 2>&1 | Out-String
|
|
286
|
+
if ($LASTEXITCODE -eq 0 -and $configOut.Trim().Length -gt 0) {
|
|
287
|
+
$hasPlugin = $configOut -match "openhermes"
|
|
288
|
+
Write-Check -Id "oc.plugin_loaded" -Name "OpenHermes plugin loaded" -Status $(if ($hasPlugin) { "PASS" } else { "FAIL" }) `
|
|
289
|
+
-Severity high -Detail $(if ($hasPlugin) { "Found in resolved config" } else { "Not found in resolved config" }) `
|
|
290
|
+
-Evidence @{ pluginName = "openhermes"; found = $hasPlugin }
|
|
291
|
+
} else {
|
|
292
|
+
Write-Check -Id "oc.plugin_loaded" -Name "OpenHermes plugin loaded" -Status SKIP -Severity high `
|
|
293
|
+
-Detail "Could not read config (exit $LASTEXITCODE)" `
|
|
294
|
+
-Evidence @{ exitCode = $LASTEXITCODE }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
$skillOut = & "opencode" "debug" "skill" 2>&1 | Out-String
|
|
298
|
+
if ($LASTEXITCODE -eq 0 -and $skillOut.Trim().Length -gt 0) {
|
|
299
|
+
$ohCount = @($skillOut | Select-String -Pattern "oh-").Count
|
|
300
|
+
Write-Check -Id "oc.skills_discovered" -Name "OH skills discovered" -Status $(if ($ohCount -ge 30) { "PASS" } else { "WARN" }) `
|
|
301
|
+
-Severity high -Detail "$ohCount oh-* skills found (expected >= 30)" `
|
|
302
|
+
-Evidence @{ count = $ohCount; minExpected = 30 }
|
|
303
|
+
} else {
|
|
304
|
+
Write-Check -Id "oc.skills_discovered" -Name "OH skills discovered" -Status SKIP -Severity high `
|
|
305
|
+
-Detail "Could not list skills (exit $LASTEXITCODE)" `
|
|
306
|
+
-Evidence @{ exitCode = $LASTEXITCODE }
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
Write-Check -Id "oc.paths" -Name "opencode paths" -Status SKIP -Severity low -Detail "Skipped" -Evidence @{ flag = "SkipOCChecks" }
|
|
310
|
+
Write-Check -Id "oc.plugin_loaded" -Name "OpenHermes plugin loaded" -Status SKIP -Severity high -Detail "Skipped" -Evidence @{ flag = "SkipOCChecks" }
|
|
311
|
+
Write-Check -Id "oc.skills_discovered" -Name "OH skills discovered" -Status SKIP -Severity high -Detail "Skipped" -Evidence @{ flag = "SkipOCChecks" }
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
# ============================================================
|
|
315
|
+
# SUMMARY
|
|
316
|
+
# ============================================================
|
|
317
|
+
$passCount = ($script:checks | Where-Object { $_.status -eq "PASS" }).Count
|
|
318
|
+
$warnCount = ($script:checks | Where-Object { $_.status -eq "WARN" }).Count
|
|
319
|
+
$failCount = ($script:checks | Where-Object { $_.status -eq "FAIL" }).Count
|
|
320
|
+
$skipCount = ($script:checks | Where-Object { $_.status -eq "SKIP" }).Count
|
|
321
|
+
|
|
322
|
+
$verdict = if ($failCount -gt 0) { "HAS_FAILURES" } elseif ($warnCount -gt 0) { "HAS_WARNINGS" } else { "ALL_CLEAN" }
|
|
323
|
+
|
|
324
|
+
$routing = switch ($verdict) {
|
|
325
|
+
"ALL_CLEAN" { "proceed" }
|
|
326
|
+
"HAS_WARNINGS" { "proceed_with_caution" }
|
|
327
|
+
"HAS_FAILURES" { "investigate" }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
$summary = @{
|
|
331
|
+
verdict = $verdict
|
|
332
|
+
summary = @{
|
|
333
|
+
pass = $passCount
|
|
334
|
+
warn = $warnCount
|
|
335
|
+
fail = $failCount
|
|
336
|
+
skip = $skipCount
|
|
337
|
+
total = ($passCount + $warnCount + $failCount + $skipCount)
|
|
338
|
+
}
|
|
339
|
+
routing = $routing
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
$summary | ConvertTo-Json -Compress -Depth 3
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# OpenHermes Constitution
|
|
2
|
-
|
|
3
|
-
Non-negotiable behavioral core. Immutable without explicit user approval + full architecture handoff.
|
|
4
|
-
|
|
5
|
-
## Operating Doctrine
|
|
6
|
-
|
|
7
|
-
### 1. OpenCode-native first
|
|
8
|
-
Use OpenCode's native skills, commands, agents, and rules loading. Do not copy content into global config when the package can register it directly.
|
|
9
|
-
|
|
10
|
-
### 2. Pragmatic over performative
|
|
11
|
-
Working code beats elegant theory. Fix the bug, not the vibe.
|
|
12
|
-
|
|
13
|
-
### 3. Concise over verbose
|
|
14
|
-
Every token costs context. Prefer short, direct output.
|
|
15
|
-
|
|
16
|
-
### 4. Task-focused over exploratory
|
|
17
|
-
Stay on mission. No drift. No unsolicited education.
|
|
18
|
-
|
|
19
|
-
### 5. Always delegate — never execute
|
|
20
|
-
OpenHermes talks/reports to the USER only and always delegates to sub-agents. OpenHermes NEVER executes tasks directly — no code, no tests, no edits.
|
|
21
|
-
|
|
22
|
-
### 6. Skills on demand
|
|
23
|
-
Do not preload all skills. Invoke the specific skill when it is relevant.
|
|
24
|
-
|
|
25
|
-
### 7. Verify before claim
|
|
26
|
-
Read files, run commands, and confirm output before saying something is done.
|
|
27
|
-
|
|
28
|
-
### 8. Rules over hidden state
|
|
29
|
-
Prefer AGENTS.md, instructions, and explicit manifests over implicit or durable state.
|
|
30
|
-
|
|
31
|
-
### 9. Memory deferred
|
|
32
|
-
Memory is intentionally absent for this pass.
|
|
33
|
-
|
|
34
|
-
### 10. Closed-loop autonomy
|
|
35
|
-
Auto-classify every task. Auto-route after every skill. Only stop for blockers and major decisions. Do not ask permission to proceed when the next step is clear. The autopilot engine (`harness/codex/AUTOPILOT.md`) is the operating manual — follow it.
|
|
36
|
-
|
|
37
|
-
### 11. Push back when needed
|
|
38
|
-
If the request is wrong, risky, or underspecified, say so directly. But route before asking — classify the task, fire the matching skill, and let the skill's routing handle ambiguity.
|
|
39
|
-
|
|
40
|
-
### 12. Recover by narrowing
|
|
41
|
-
When blocked, reduce scope, add constraints, and retry with evidence. Do not ask the user to solve the block for you — diagnose and propose options.
|
|
42
|
-
|
|
43
|
-
### 13. Receipts over vibes
|
|
44
|
-
Claims need evidence: file reads, command output, or test output.
|
|
45
|
-
|
|
46
|
-
## Safety
|
|
47
|
-
User config, plugins, MCP, permissions, TUI, local skills, overlays — locked unless the task explicitly targets them.
|
|
48
|
-
|
|
49
|
-
## Escalation
|
|
50
|
-
T0: auto-classify → auto-route → execute (do not ask)
|
|
51
|
-
T1: check result → route next by outcome (do not ask)
|
|
52
|
-
T2: if blocked → diagnose → retry with narrower scope (do not ask)
|
|
53
|
-
T3: if still blocked → surface with findings, options, and what is needed
|
|
54
|
-
|
|
55
|
-
## Self-Diagnosis
|
|
56
|
-
|
|
57
|
-
Before every substantive response, ask:
|
|
58
|
-
|
|
59
|
-
1. **Is this sycophancy?** — Would I say this without the user's steer? If tone/framing shaped the answer, it is sycophancy. Re-ask neutrally.
|
|
60
|
-
|
|
61
|
-
2. **Factuality or faithfulness?** — If I am inventing things not in the loaded docs, I need to read more contextual knowledge. If I am drifting from what IS in context, my attention is degrading — compact or clear.
|
|
62
|
-
|
|
63
|
-
3. **Am I in the smart zone?** — If the session is heavy and I am getting sloppy, I am past the smart zone. Stop pushing through. Compact and reload.
|
|
64
|
-
|
|
65
|
-
4. **Am I repeating user mistakes?** — Mimicry is a sycophancy signal. Pause and evaluate independently.
|
|
66
|
-
|
|
67
|
-
5. **Is this a knowledge-cutoff trap?** — If the user mentions versions, APIs, or libraries that may have shipped after my training data, load current docs before writing code.
|
|
68
|
-
|
|
69
|
-
## Tone Check
|
|
70
|
-
1. Am I terse?
|
|
71
|
-
2. Am I delegating?
|
|
72
|
-
3. Am I verifying?
|
|
73
|
-
4. Does my approach match the problem's depth?
|
package/harness/codex/ROUTING.md
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# OpenHermes Routing Graph
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Routing is **dynamic** — each skill carries its own routing metadata in its `SKILL.md` frontmatter (`route.pass`, `route.fail`, `route.blocker`). The autopilot reads the current skill's frontmatter at runtime to determine the next hop. This allows user skills to participate in routing automatically.
|
|
6
|
-
|
|
7
|
-
This document serves as a human-readable reference for the overall flow. For routing decisions, always read the skill's frontmatter — it is the authoritative source.
|
|
8
|
-
|
|
9
|
-
## Route value types
|
|
10
|
-
|
|
11
|
-
| Value | Meaning |
|
|
12
|
-
|-------|---------|
|
|
13
|
-
| `oh-<name>` | Route to skill |
|
|
14
|
-
| `[oh-a, oh-b]` | Route to one of — choose by context |
|
|
15
|
-
| `surface` | Report findings to user, end chain |
|
|
16
|
-
| `done` | Task complete — terminal |
|
|
17
|
-
| `mode` | Mode switch — return to caller after toggle |
|
|
18
|
-
|
|
19
|
-
## Routing graph (simplified)
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
oh-planner ──pass──→ oh-grill ──pass──→ oh-planner (revise) ──→ oh-manifest
|
|
23
|
-
fail──→ oh-planner (revise)
|
|
24
|
-
|
|
25
|
-
oh-manifest ──→ oh-planner → oh-builder → oh-gauntlet → oh-ship → oh-retro → oh-planner
|
|
26
|
-
↑_____________________________| |
|
|
27
|
-
| ↓
|
|
28
|
-
└───────── oh-expert ←───────────────── fail
|
|
29
|
-
|
|
30
|
-
oh-ship ──pass──→ oh-retro ──→ oh-planner (loops forever)
|
|
31
|
-
fail──→ oh-expert ──→ oh-builder ──→ oh-gauntlet
|
|
32
|
-
|
|
33
|
-
oh-facade ─── Concept → Design System → Build → Audit → Iterate (loop until pass)
|
|
34
|
-
pass──→ oh-review or back to oh-manifest
|
|
35
|
-
audit fail──→ Iterate (fix priority order)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## oh-facade Pipeline Detail
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
oh-facade:
|
|
42
|
-
Phase 1 Concept → direction brief
|
|
43
|
-
Phase 2 Design Sys → DESIGN.md (color, typography, components, layout, motion, anti-patterns)
|
|
44
|
-
Phase 3 Build → production code (components + pages + all states)
|
|
45
|
-
Phase 4 Audit → 9-layer checklist (Priority 1-4)
|
|
46
|
-
Phase 5 Iterate → fix → re-audit → loop until pass
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Rules
|
|
50
|
-
|
|
51
|
-
1. Every skill routes somewhere — no leaf nodes (except handoff which is intentional terminal)
|
|
52
|
-
2. Route by outcome, not by convention — different results go different places
|
|
53
|
-
3. Default fallback if no match: **surface to user**
|
|
54
|
-
4. Mode skills (caveman, freeze, guard) return to the skill that invoked them after toggling state
|
|
55
|
-
5. The graph must have no dead ends — the only true terminal is `oh-handoff` (session end)
|
|
56
|
-
|
|
57
|
-
## OptiRoute Protocol
|
|
58
|
-
|
|
59
|
-
OptiRoute is a smart auto-routing guard layer. It prevents infinite loops, stops on ambiguity, and auto-generates handoff reports when a task goes nowhere.
|
|
60
|
-
|
|
61
|
-
### Loop Guard
|
|
62
|
-
|
|
63
|
-
Tracks routing depth per chain. Two thresholds:
|
|
64
|
-
|
|
65
|
-
| Threshold | Trigger | Action |
|
|
66
|
-
|-----------|---------|--------|
|
|
67
|
-
| **3x repeat** | Same skill visited 3+ times in one routing chain | STOP, invoke auto-handoff |
|
|
68
|
-
| **5-hop ceiling** | 5+ routing hops without measurable progress toward the original goal | STOP, invoke auto-handoff |
|
|
69
|
-
|
|
70
|
-
*Progress* is defined as: the routing target changed since the last hop, or a new artifact was produced (plan file updated, code written, test result).
|
|
71
|
-
|
|
72
|
-
### Question Gate
|
|
73
|
-
|
|
74
|
-
Before each routing hop, evaluate:
|
|
75
|
-
|
|
76
|
-
- Is the next skill's input fully satisfied? (plan file exists for builder, code exists for gauntlet, etc.)
|
|
77
|
-
- Is there any ambiguity that requires user clarification?
|
|
78
|
-
|
|
79
|
-
If either is no: **do not route. Ask the user a specific question.** Surface what you have, what's missing, and what you need.
|
|
80
|
-
|
|
81
|
-
### Auto-Handoff
|
|
82
|
-
|
|
83
|
-
When Loop Guard triggers:
|
|
84
|
-
|
|
85
|
-
1. **Stop routing immediately.** Do not attempt another hop.
|
|
86
|
-
2. **Write to plan file:** Append an OptiRoute report with:
|
|
87
|
-
- Routing chain: the sequence of skills visited
|
|
88
|
-
- Trigger: which threshold fired (3x repeat / 5-hop ceiling)
|
|
89
|
-
- Current state: what artifacts exist, what's pending
|
|
90
|
-
- Blocker: what prevented progress
|
|
91
|
-
3. **Surface to user** with: `OPTIROUTE STOP: <reason> | Chain: <skills> | See plan file for full report`
|
|
92
|
-
4. Exit the loop. Await user direction.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# OpenHermes Runtime
|
|
2
|
-
|
|
3
|
-
Root: package-local harness plus repo AGENTS.md. The autopilot engine (`harness/codex/AUTOPILOT.md`) governs all behavior.
|
|
4
|
-
|
|
5
|
-
## Self-Driving Principles
|
|
6
|
-
|
|
7
|
-
1. **Auto-classify every request.** Before responding, run the task through the AUTOPILOT decision matrix. The outcome determines which skill fires. You do not ask the user which skill to use.
|
|
8
|
-
|
|
9
|
-
2. **Auto-route after every step.** Every skill has a routing table (pass→X, fail→Y, blocker→Z). After a skill completes, check the outcome and route immediately. Do not ask "should I route?"
|
|
10
|
-
|
|
11
|
-
3. **Close the loop.** Every skill routes somewhere. No dead ends. If the last skill in a chain completes and the objective is met, summarize and stop. If more work remains, auto-classify the next unit.
|
|
12
|
-
|
|
13
|
-
4. **Only stop for blockers.** Not for ambiguity. Not for confirmation. Not for "is this OK?" Only stop when: (a) task is complete, (b) unrecoverable error, (c) genuinely ambiguous architecture decision that changes the outcome.
|
|
14
|
-
|
|
15
|
-
## Shared state
|
|
16
|
-
|
|
17
|
-
- `~/.local/share/opencode/openhermes/plans/<project-name>-plan-<nnn>.md` — produced by oh-planner, consumed by oh-builder and oh-manifest. The plan file is self-contained: it includes task tracking (Tasks + Completed sections) and work log (Subagents table + Completed log). No separate todo.md or work-log.md files.
|
|
18
|
-
- `~/.local/share/opencode/openhermes/plans/<project-name>-instincts.jsonl` — behavioral patterns extracted by oh-learn.
|
|
19
|
-
|
|
20
|
-
## Orchestration discipline
|
|
21
|
-
|
|
22
|
-
- **Session pool**: Subagents run in their own sessions with isolated context. Each reports one result back.
|
|
23
|
-
- **Concurrency**: Parallelize independent sub-tasks. Sequentialize dependent ones.
|
|
24
|
-
- **Circuit breaker**: 3 subagent failures on the same task → surface BLOCKER. Do not silently retry.
|
|
25
|
-
- **Pipelined verification**: Every phase self-verifies before declaring success. No assumptions.
|
|
26
|
-
- **Background vs sync**: Independent work fires and forgets. Dependent work awaits.
|
|
27
|
-
|
|
28
|
-
## Conventions
|
|
29
|
-
|
|
30
|
-
Security, coding style, testing standards follow the Constitution. Skills provide specialized workflows.
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: oh-caveman
|
|
3
|
-
description: "Ultra-compressed communication mode — cut token usage ~75%"
|
|
4
|
-
tier: 2
|
|
5
|
-
triggers:
|
|
6
|
-
- "compress your response"
|
|
7
|
-
- "caveman mode"
|
|
8
|
-
- "shorter answers"
|
|
9
|
-
route:
|
|
10
|
-
pass: mode
|
|
11
|
-
fail: mode
|
|
12
|
-
blocker: surface
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
# oh-caveman
|
|
16
|
-
|
|
17
|
-
## When to Use
|
|
18
|
-
When context is tight, tokens are precious, or user says "caveman mode." Drops filler, articles, and pleasantries while keeping full technical accuracy.
|
|
19
|
-
|
|
20
|
-
## Mode
|
|
21
|
-
- No pleasantries, no hedging, no transitions
|
|
22
|
-
- Fragments OK. One word when enough.
|
|
23
|
-
- Short synonyms. Drop articles.
|
|
24
|
-
- Code unchanged — only prose compresses.
|
|
25
|
-
- Technical accuracy preserved at all costs.
|
|
26
|
-
|
|
27
|
-
## Example
|
|
28
|
-
Normal: "I think we should probably look at the authentication module because there might be an issue with the token refresh logic."
|
|
29
|
-
Caveman: "Check auth module — token refresh likely broken."
|
|
30
|
-
|
|
31
|
-
## Anti-patterns
|
|
32
|
-
- Compressing code (code is already dense)
|
|
33
|
-
- Omitting critical context to save tokens
|
|
34
|
-
- Being unclear to be brief (accuracy > brevity)
|
|
35
|
-
|
|
36
|
-
## Routing
|
|
37
|
-
|
|
38
|
-
| Outcome | Route |
|
|
39
|
-
|---------|-------|
|
|
40
|
-
| pass | → [return to prior skill — mode active] |
|
|
41
|
-
| fail | → [fallback to normal communication mode] |
|
|
42
|
-
| blocker | → surface to user |
|
package/lib/logger.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
import os from "node:os"
|
|
3
|
-
import fs from "node:fs"
|
|
4
|
-
|
|
5
|
-
export interface Logger {
|
|
6
|
-
debug: (...args: unknown[]) => void
|
|
7
|
-
info: (...args: unknown[]) => void
|
|
8
|
-
warn: (...args: unknown[]) => void
|
|
9
|
-
error: (...args: unknown[]) => void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const LEVELS: Record<string, number> = { debug: 0, info: 1, warn: 2, error: 3 }
|
|
13
|
-
|
|
14
|
-
function resolveLevel(levelName: string | undefined): number | undefined {
|
|
15
|
-
if (!levelName) return undefined
|
|
16
|
-
return LEVELS[levelName as keyof typeof LEVELS]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const CURRENT_LEVEL = resolveLevel(process.env.OPENCODE_LOG_LEVEL?.trim().toLowerCase()) ?? (process.env.OPENHERMES_LOG_LEVEL?.trim().toLowerCase() === "debug" ? LEVELS.debug : LEVELS.warn)
|
|
20
|
-
|
|
21
|
-
const LOG_DIR = path.join(os.homedir(), ".local", "share", "opencode", "log")
|
|
22
|
-
const LOG_FILE = path.join(LOG_DIR, "openhermes.log")
|
|
23
|
-
|
|
24
|
-
function ts(): string {
|
|
25
|
-
const d = new Date()
|
|
26
|
-
return `${d.getFullYear()}-${(d.getMonth()+1).toString().padStart(2,"0")}-${d.getDate().toString().padStart(2,"0")} ${d.getHours().toString().padStart(2,"0")}:${d.getMinutes().toString().padStart(2,"0")}:${d.getSeconds().toString().padStart(2,"0")}.${d.getMilliseconds().toString().padStart(3,"0")}`
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function formatArgs(args: unknown[]): string {
|
|
30
|
-
return args.map(a => {
|
|
31
|
-
if (a === null) return "null"
|
|
32
|
-
if (a === undefined) return "undefined"
|
|
33
|
-
if (typeof a === "object") {
|
|
34
|
-
try { return (a as Error)?.message || JSON.stringify(a) } catch { return String(a) }
|
|
35
|
-
}
|
|
36
|
-
return String(a)
|
|
37
|
-
}).join(" ")
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function shouldLog(levelName: string): boolean {
|
|
41
|
-
return LEVELS[levelName] >= CURRENT_LEVEL
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let _fd: number | null = null
|
|
45
|
-
function getFd(): number {
|
|
46
|
-
if (_fd) return _fd
|
|
47
|
-
try {
|
|
48
|
-
fs.mkdirSync(LOG_DIR, { recursive: true })
|
|
49
|
-
_fd = fs.openSync(LOG_FILE, "a")
|
|
50
|
-
} catch {
|
|
51
|
-
_fd = -1
|
|
52
|
-
}
|
|
53
|
-
return _fd
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function createLogger(name: string): Logger {
|
|
57
|
-
const prefix = `[openhermes:${name}]`
|
|
58
|
-
|
|
59
|
-
function emit(levelName: string, ...args: unknown[]): void {
|
|
60
|
-
if (!shouldLog(levelName)) return
|
|
61
|
-
const fd = getFd()
|
|
62
|
-
if (fd < 0) return
|
|
63
|
-
const line = `${ts()} ${prefix} [${levelName.toUpperCase()}] ${formatArgs(args)}\n`
|
|
64
|
-
try { fs.writeSync(fd, line) } catch {}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
debug: (...args: unknown[]) => emit("debug", ...args),
|
|
69
|
-
info: (...args: unknown[]) => emit("info", ...args),
|
|
70
|
-
warn: (...args: unknown[]) => emit("warn", ...args),
|
|
71
|
-
error: (...args: unknown[]) => emit("error", ...args),
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const rootLogger: Logger = createLogger("root")
|