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
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oh-skills-list
|
|
3
|
-
description: "
|
|
3
|
+
description: "Use when the user wants to see available OH skills. Lists all oh-* skills with descriptions."
|
|
4
4
|
tier: 2
|
|
5
|
-
triggers:
|
|
6
|
-
- "list skills"
|
|
7
|
-
- "show skills"
|
|
8
|
-
- "what skills"
|
|
9
5
|
route:
|
|
10
6
|
pass: done
|
|
11
7
|
fail: surface
|
|
@@ -14,27 +10,18 @@ route:
|
|
|
14
10
|
|
|
15
11
|
# oh-skills-list
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
To discover what skills are available. Lists every skill with its name, description, and category.
|
|
13
|
+
List all available oh-* skills with tier and description.
|
|
19
14
|
|
|
20
|
-
##
|
|
21
|
-
Markdown table of skills:
|
|
15
|
+
## Steps
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| oh-qa | Full QA workflow | Quality |
|
|
27
|
-
| ... | ... | ... |
|
|
28
|
-
|
|
29
|
-
## Anti-patterns
|
|
30
|
-
- Listing "all available" but missing recently installed skills
|
|
31
|
-
- Showing skill file paths instead of human-readable descriptions
|
|
32
|
-
- Not categorising skills (flat list is hard to scan)
|
|
17
|
+
1. Gather all oh-* skills from the harness
|
|
18
|
+
2. Format as a table: Skill | Tier | Purpose
|
|
19
|
+
3. Output the table to the user
|
|
33
20
|
|
|
34
21
|
## Routing
|
|
35
22
|
|
|
36
23
|
| Outcome | Route |
|
|
37
24
|
|---------|-------|
|
|
38
|
-
| pass | →
|
|
39
|
-
| fail | →
|
|
40
|
-
| blocker | → surface
|
|
25
|
+
| pass | → done |
|
|
26
|
+
| fail | → surface |
|
|
27
|
+
| blocker | → surface |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# oh-triage — Deep Reference
|
|
2
|
+
|
|
3
|
+
## When to Use
|
|
4
|
+
|
|
5
|
+
New issues or backlog review. Drives through triage state machine.
|
|
6
|
+
|
|
7
|
+
## States
|
|
8
|
+
|
|
9
|
+
1. Needs triage (new, unclassified)
|
|
10
|
+
2. Needs info (waiting on reporter)
|
|
11
|
+
3. Ready for agent (well-specified)
|
|
12
|
+
4. Ready for human (needs judgment/access)
|
|
13
|
+
5. Wontfix (declined with reason)
|
|
14
|
+
|
|
15
|
+
## Anti-patterns
|
|
16
|
+
|
|
17
|
+
- Issues stuck in "needs triage"
|
|
18
|
+
- Triaging without reading full issue
|
|
19
|
+
- Wontfix without explanation
|
|
20
|
+
|
|
21
|
+
## Reference
|
|
22
|
+
|
|
23
|
+
**Example:** New issues appear with needs-triage label. Read issue, classify as bug/feature/enhancement, assess severity, assign state (ready-for-agent / ready-for-human / needs-info).
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oh-triage
|
|
3
|
-
description: "
|
|
3
|
+
description: "Use when new issues need triage — classify, prioritise, and assign through the triage state machine."
|
|
4
4
|
tier: 2
|
|
5
|
-
triggers:
|
|
6
|
-
- "triage this issue"
|
|
7
|
-
- "classify this issue"
|
|
8
|
-
- "triage the backlog"
|
|
9
5
|
route:
|
|
10
6
|
pass:
|
|
11
7
|
- oh-issue
|
|
@@ -16,32 +12,20 @@ route:
|
|
|
16
12
|
|
|
17
13
|
# oh-triage
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
When new issues come in, or when reviewing the issue backlog. Drives issues through a triage state machine.
|
|
15
|
+
Classify, prioritise, and assign new issues through the triage state machine.
|
|
21
16
|
|
|
22
|
-
##
|
|
23
|
-
1. **Needs triage** — new issue, unclassified
|
|
24
|
-
2. **Needs info** — waiting on reporter for clarification
|
|
25
|
-
3. **Ready for agent** — well-specified, can be picked up
|
|
26
|
-
4. **Ready for human** — needs human judgment or access
|
|
27
|
-
5. **Wontfix** — declined with reason
|
|
17
|
+
## Steps
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
1. Read new issues (label: `needs-triage`)
|
|
19
|
+
1. Read issues with `needs-triage` label
|
|
31
20
|
2. Classify: bug / feature / enhancement / question
|
|
32
21
|
3. Assess severity and priority
|
|
33
|
-
4. Assign
|
|
22
|
+
4. Assign state and owner
|
|
34
23
|
5. Add triage metadata labels
|
|
35
24
|
|
|
36
|
-
## Anti-patterns
|
|
37
|
-
- Leaving issues in "needs triage" indefinitely
|
|
38
|
-
- Triaging without reading the full issue
|
|
39
|
-
- Wontfix without explanation (leaves reporter unhappy)
|
|
40
|
-
|
|
41
25
|
## Routing
|
|
42
26
|
|
|
43
27
|
| Outcome | Route |
|
|
44
28
|
|---------|-------|
|
|
45
|
-
| pass | → oh-issue
|
|
46
|
-
| fail | → oh-expert
|
|
47
|
-
| blocker | → surface
|
|
29
|
+
| pass | → oh-issue or oh-handoff |
|
|
30
|
+
| fail | → oh-expert |
|
|
31
|
+
| blocker | → surface |
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# oh-worktree — Deep Reference
|
|
2
|
+
|
|
3
|
+
## When to Use
|
|
4
|
+
|
|
5
|
+
Starting feature work that needs isolation from the current workspace. Ensures work happens in an isolated workspace. Prefers native worktree tools. Falls back to manual git worktrees only when no native tool is available.
|
|
6
|
+
|
|
7
|
+
**Core principle:** Detect existing isolation first. Use native tools. Fall back to git. Never fight the harness.
|
|
8
|
+
|
|
9
|
+
**Example:** User says "set up worktree for feature-x." Run detection → create worktree → install deps → verify baseline tests pass. Report ready.
|
|
10
|
+
|
|
11
|
+
## Phases
|
|
12
|
+
|
|
13
|
+
### Step 0: Detect Existing Isolation
|
|
14
|
+
|
|
15
|
+
**Before creating anything, check if you are already in an isolated workspace.**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null && cd "$(git rev-parse --git-dir)" && pwd -P)
|
|
19
|
+
GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null && cd "$(git rev-parse --git-common-dir)" && pwd -P)
|
|
20
|
+
BRANCH=$(git branch --show-current)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Submodule guard:** `GIT_DIR != GIT_COMMON` is also true inside git submodules. Before concluding "already in a worktree," verify you are not in a submodule:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git rev-parse --show-superproject-working-tree 2>/dev/null
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If this returns a path, you're in a submodule — treat as normal repo.
|
|
30
|
+
|
|
31
|
+
**If `GIT_DIR != GIT_COMMON` (and not a submodule):** Already in a linked worktree. Skip to Step 3. Do NOT create another worktree.
|
|
32
|
+
|
|
33
|
+
- On a branch: "Already in isolated workspace at `<path>` on branch `<name>`."
|
|
34
|
+
- Detached HEAD: "Already in isolated workspace at `<path>` (detached HEAD, externally managed). Branch creation needed at finish time."
|
|
35
|
+
|
|
36
|
+
**If `GIT_DIR == GIT_COMMON` (or in a submodule):** Normal repo checkout. Ask for consent before creating a worktree:
|
|
37
|
+
|
|
38
|
+
> "Would you like me to set up an isolated worktree? It protects your current branch from changes."
|
|
39
|
+
|
|
40
|
+
Honor any declared preference without asking. If declined, work in place and skip to Step 3.
|
|
41
|
+
|
|
42
|
+
### Step 1: Create Isolated Workspace
|
|
43
|
+
|
|
44
|
+
Two mechanisms. Try in order.
|
|
45
|
+
|
|
46
|
+
#### 1a. Native Worktree Tools (preferred)
|
|
47
|
+
|
|
48
|
+
If a native worktree tool exists (e.g., `EnterWorktree`, `WorktreeCreate`, a `/worktree` command, or a `--worktree` flag), use it and skip to Step 3. Native tools handle directory placement, branch creation, and cleanup automatically. Using `git worktree add` when a native tool exists creates phantom state the harness cannot see or manage.
|
|
49
|
+
|
|
50
|
+
Only proceed to Step 1b if no native tool is available.
|
|
51
|
+
|
|
52
|
+
#### 1b. Git Worktree Fallback
|
|
53
|
+
|
|
54
|
+
Only if Step 1a does not apply.
|
|
55
|
+
|
|
56
|
+
##### Directory Selection
|
|
57
|
+
|
|
58
|
+
Follow this priority. Explicit user preference always beats observed state.
|
|
59
|
+
|
|
60
|
+
1. Check instructions for a declared worktree directory preference. If specified, use it without asking.
|
|
61
|
+
2. Check for an existing project-local worktree directory:
|
|
62
|
+
```bash
|
|
63
|
+
ls -d .worktrees 2>/dev/null # Preferred (hidden)
|
|
64
|
+
ls -d worktrees 2>/dev/null # Alternative
|
|
65
|
+
```
|
|
66
|
+
If found, use it. If both exist, `.worktrees` wins.
|
|
67
|
+
3. Check for an existing global directory:
|
|
68
|
+
```bash
|
|
69
|
+
project=$(basename "$(git rev-parse --show-toplevel)")
|
|
70
|
+
ls -d ~/.config/opencode/worktrees/$project 2>/dev/null
|
|
71
|
+
```
|
|
72
|
+
If found, use it (backward compatibility).
|
|
73
|
+
4. If no other guidance, default to `.worktrees/` at the project root.
|
|
74
|
+
|
|
75
|
+
##### Safety Verification (project-local directories only)
|
|
76
|
+
|
|
77
|
+
**Must verify directory is ignored before creating worktree:**
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If NOT ignored, add to `.gitignore` and commit the change before proceeding.
|
|
84
|
+
|
|
85
|
+
Why critical: Prevents accidentally committing worktree contents to the repository. Global directories (`~/.config/opencode/worktrees/`) need no verification.
|
|
86
|
+
|
|
87
|
+
##### Create the Worktree
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
project=$(basename "$(git rev-parse --show-toplevel)")
|
|
91
|
+
# project-local: path="$LOCATION/$BRANCH_NAME"
|
|
92
|
+
# global: path="~/.config/opencode/worktrees/$project/$BRANCH_NAME"
|
|
93
|
+
git worktree add "$path" -b "$BRANCH_NAME"
|
|
94
|
+
cd "$path"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Sandbox fallback:** If `git worktree add` fails with a permission error (sandbox denial), report the sandbox blocked worktree creation and work in the current directory instead. Then run setup and baseline tests in place.
|
|
98
|
+
|
|
99
|
+
### Step 3: Project Setup
|
|
100
|
+
|
|
101
|
+
Auto-detect and run appropriate setup:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
if [ -f package.json ]; then npm install; fi
|
|
105
|
+
if [ -f Cargo.toml ]; then cargo build; fi
|
|
106
|
+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
107
|
+
if [ -f pyproject.toml ]; then poetry install; fi
|
|
108
|
+
if [ -f go.mod ]; then go mod download; fi
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Step 4: Verify Clean Baseline
|
|
112
|
+
|
|
113
|
+
Run tests to ensure workspace starts clean using the project-appropriate command (`npm test` / `cargo test` / `pytest` / `go test ./...`).
|
|
114
|
+
|
|
115
|
+
- Tests pass: Report ready.
|
|
116
|
+
- Tests fail: Report failures, ask whether to proceed or investigate.
|
|
117
|
+
|
|
118
|
+
#### Report
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Worktree ready at <full-path>
|
|
122
|
+
Tests passing (<N> tests, 0 failures)
|
|
123
|
+
Ready to implement <feature-name>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Quick Reference
|
|
127
|
+
|
|
128
|
+
| Situation | Action |
|
|
129
|
+
|-----------|--------|
|
|
130
|
+
| Already in linked worktree | Skip creation (Step 0) |
|
|
131
|
+
| In a submodule | Treat as normal repo (Step 0 guard) |
|
|
132
|
+
| Native worktree tool available | Use it (Step 1a) |
|
|
133
|
+
| No native tool | Git worktree fallback (Step 1b) |
|
|
134
|
+
| `.worktrees/` exists | Use it (verify ignored) |
|
|
135
|
+
| `worktrees/` exists | Use it (verify ignored) |
|
|
136
|
+
| Both exist | Use `.worktrees/` |
|
|
137
|
+
| Neither exists | Check instruction file, then default `.worktrees/` |
|
|
138
|
+
| Global path exists | Use it (backward compat) |
|
|
139
|
+
| Directory not ignored | Add to .gitignore + commit |
|
|
140
|
+
| Permission error on create | Sandbox fallback, work in place |
|
|
141
|
+
| Tests fail during baseline | Report failures + ask |
|
|
142
|
+
| No package.json/Cargo.toml | Skip dependency install |
|
|
143
|
+
|
|
144
|
+
## Anti-patterns
|
|
145
|
+
|
|
146
|
+
- **Fighting the harness:** Using `git worktree add` when the platform already provides isolation. Fix: Step 0 detects existing isolation; Step 1a defers to native tools.
|
|
147
|
+
- **Skipping detection:** Creating a nested worktree inside an existing one. Fix: Always run Step 0 before creating anything.
|
|
148
|
+
- **Skipping ignore verification:** Worktree contents get tracked, pollute git status. Fix: Always use `git check-ignore` before creating a project-local worktree.
|
|
149
|
+
- **Assuming directory location:** Creates inconsistency, violates project conventions. Fix: Follow priority: existing > global legacy > instruction file > default.
|
|
150
|
+
- **Proceeding with failing tests:** Cannot distinguish new bugs from pre-existing issues. Fix: Report failures, get explicit permission to proceed.
|
|
151
|
+
- **Jumping to git fallback:** Skipping Step 1a and going straight to `git worktree add` when a native tool exists. This is the most common mistake.
|
|
152
|
+
|
|
153
|
+
## Red Flags
|
|
154
|
+
|
|
155
|
+
**Never:**
|
|
156
|
+
- Create a worktree when Step 0 detects existing isolation
|
|
157
|
+
- Use `git worktree add` when a native worktree tool is available
|
|
158
|
+
- Skip Step 1a by jumping straight to Step 1b's git commands
|
|
159
|
+
- Create a worktree without verifying it is ignored (project-local)
|
|
160
|
+
- Skip baseline test verification
|
|
161
|
+
- Proceed with failing tests without asking
|
|
162
|
+
|
|
163
|
+
**Always:**
|
|
164
|
+
- Run Step 0 detection first
|
|
165
|
+
- Prefer native tools over git fallback
|
|
166
|
+
- Follow directory priority: existing > global legacy > instruction file > default
|
|
167
|
+
- Verify directory is ignored for project-local paths
|
|
168
|
+
- Auto-detect and run project setup
|
|
169
|
+
- Verify clean test baseline
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oh-worktree
|
|
3
|
+
description: "Use when starting feature work that needs isolation from the current workspace or before executing implementation plans. Manages workspace isolation via git worktrees."
|
|
4
|
+
tier: 3
|
|
5
|
+
route:
|
|
6
|
+
pass: oh-manifest
|
|
7
|
+
fail: surface
|
|
8
|
+
blocker: surface
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# oh-worktree
|
|
12
|
+
|
|
13
|
+
Create isolated workspaces via git worktrees for safe feature development.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
1. Detect existing isolation — check if already in a linked worktree
|
|
18
|
+
2. Check submodule status to avoid false worktree detection
|
|
19
|
+
3. Ask for user consent if not already isolated
|
|
20
|
+
4. Prefer native worktree tools over git fallback
|
|
21
|
+
5. Select worktree directory following priority: existing > global legacy > instruction file > default
|
|
22
|
+
6. Verify directory is git-ignored for project-local paths
|
|
23
|
+
7. Auto-detect and run project setup (npm install, cargo build, etc.)
|
|
24
|
+
8. Verify clean test baseline and report ready
|
|
25
|
+
|
|
26
|
+
## Routing
|
|
27
|
+
|
|
28
|
+
| Outcome | Route |
|
|
29
|
+
|---------|-------|
|
|
30
|
+
| pass | → oh-manifest |
|
|
31
|
+
| fail | → surface |
|
|
32
|
+
| blocker | → surface |
|
package/lib/harness-resolver.ts
CHANGED
|
@@ -9,12 +9,12 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
|
9
9
|
const PKG_DIR = path.resolve(__dirname, "..")
|
|
10
10
|
|
|
11
11
|
const REQUIRED_HARNESS_FILES: ReadonlyArray<readonly string[]> = [
|
|
12
|
-
["codex", "
|
|
13
|
-
["
|
|
12
|
+
["codex", "CHARTER.md"],
|
|
13
|
+
["codex", "AUTOPILOT.md"],
|
|
14
14
|
["skills", "oh-planner", "SKILL.md"],
|
|
15
15
|
]
|
|
16
16
|
|
|
17
|
-
function ancestorDirs(start: string, limit =
|
|
17
|
+
function ancestorDirs(start: string, limit = 3): string[] {
|
|
18
18
|
const dirs: string[] = []
|
|
19
19
|
let current = path.resolve(start)
|
|
20
20
|
for (let i = 0; i < limit; i++) {
|
|
@@ -26,17 +26,15 @@ function ancestorDirs(start: string, limit = 6): string[] {
|
|
|
26
26
|
return dirs
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function buildHarnessCandidates(currentDir: string,
|
|
30
|
-
const roots = [
|
|
31
|
-
const seen = new Set(
|
|
29
|
+
function buildHarnessCandidates(currentDir: string, cwd: string): string[] {
|
|
30
|
+
const roots: string[] = []
|
|
31
|
+
const seen = new Set<string>()
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
for (const anchor of anchors) {
|
|
33
|
+
for (const anchor of [currentDir, cwd]) {
|
|
35
34
|
for (const dir of ancestorDirs(anchor)) {
|
|
36
35
|
for (const root of [
|
|
37
36
|
path.join(dir, "harness"),
|
|
38
37
|
path.join(dir, "node_modules", "openhermes", "harness"),
|
|
39
|
-
path.join(dir, "bin", "node_modules", "openhermes", "harness"),
|
|
40
38
|
]) {
|
|
41
39
|
const normalized = path.normalize(root)
|
|
42
40
|
if (seen.has(normalized)) continue
|
|
@@ -64,7 +62,7 @@ export function resolveHarnessRoot({
|
|
|
64
62
|
cwd?: string
|
|
65
63
|
candidateRoots?: string[]
|
|
66
64
|
} = {}): string {
|
|
67
|
-
const roots = candidateRoots ?? buildHarnessCandidates(currentDir,
|
|
65
|
+
const roots = candidateRoots ?? buildHarnessCandidates(currentDir, cwd)
|
|
68
66
|
for (const root of roots) {
|
|
69
67
|
if (hasRequiredHarnessFiles(root)) return root
|
|
70
68
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openhermes",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.9.2",
|
|
4
|
+
"description": "Autonomous agent orchestration for OpenCode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"engines": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"main": "./index.ts",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@opencode-ai/plugin": "1.
|
|
12
|
+
"@opencode-ai/plugin": "1.15.0",
|
|
13
|
+
"gpt-tokenizer": "^3.4.0"
|
|
13
14
|
},
|
|
14
15
|
"exports": {
|
|
15
16
|
".": "./index.ts",
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
"ETHOS.md",
|
|
23
24
|
"CONTEXT.md",
|
|
24
25
|
"lib/",
|
|
26
|
+
"scripts/",
|
|
25
27
|
"harness/codex/",
|
|
26
28
|
"harness/instructions/",
|
|
27
29
|
"harness/skills/",
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact token counting using gpt-tokenizer.
|
|
3
|
+
* Counts every SKILL.md, bootstrap doc, AGENTS.md, and instructions.
|
|
4
|
+
*/
|
|
5
|
+
import { encode } from "gpt-tokenizer";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
|
|
9
|
+
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
10
|
+
const ROOT = path.resolve("Q:/PROJECTS/PERSONAL/openhermes-pkg");
|
|
11
|
+
|
|
12
|
+
const categories = {
|
|
13
|
+
"OH Bootstrap Docs (on disk, NOT injected)": [
|
|
14
|
+
path.join(ROOT, "harness/codex/AUTOPILOT.md"),
|
|
15
|
+
path.join(ROOT, "harness/codex/CHARTER.md"),
|
|
16
|
+
path.join(ROOT, "harness/instructions/SHELL.md"),
|
|
17
|
+
path.join(ROOT, "CONTEXT.md"),
|
|
18
|
+
path.join(ROOT, "ETHOS.md"),
|
|
19
|
+
],
|
|
20
|
+
"OH Agent Prompt (injected as system prompt)": [
|
|
21
|
+
path.join(ROOT, "harness/agents/openhermes.md"),
|
|
22
|
+
],
|
|
23
|
+
"OH Built-in Skills (31, registered)": [],
|
|
24
|
+
"User Skills (.agents/skills) — on disk, NOT registered": [],
|
|
25
|
+
"User Skills (.config/opencode/skills) — on disk, NOT registered": [],
|
|
26
|
+
"Instructions": [
|
|
27
|
+
path.join(ROOT, "AGENTS.md"),
|
|
28
|
+
path.join(HOME, ".config/opencode/AGENTS.md"),
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Discover OH built-in skills
|
|
33
|
+
const skillsDir = path.join(ROOT, "harness/skills");
|
|
34
|
+
for (const entry of fs.readdirSync(skillsDir)) {
|
|
35
|
+
const skPath = path.join(skillsDir, entry, "SKILL.md");
|
|
36
|
+
if (fs.existsSync(skPath)) categories["OH Built-in Skills (31, registered)"].push(skPath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Discover user skills
|
|
40
|
+
const userDirs = [
|
|
41
|
+
path.join(HOME, ".agents/skills"),
|
|
42
|
+
path.join(HOME, ".config/opencode/skills"),
|
|
43
|
+
];
|
|
44
|
+
for (const dir of userDirs) {
|
|
45
|
+
const key = dir.includes(".agents")
|
|
46
|
+
? "User Skills (.agents/skills) — on disk, NOT registered"
|
|
47
|
+
: "User Skills (.config/opencode/skills) — on disk, NOT registered";
|
|
48
|
+
if (fs.existsSync(dir)) {
|
|
49
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
50
|
+
const skPath = path.join(dir, entry, "SKILL.md");
|
|
51
|
+
if (fs.existsSync(skPath)) categories[key].push(skPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Extract frontmatter description
|
|
57
|
+
function extractDescription(filePath) {
|
|
58
|
+
try {
|
|
59
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
60
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
61
|
+
if (!match) return { description: "", body: content };
|
|
62
|
+
const fm = match[1];
|
|
63
|
+
const descLine = fm.split("\n").find(l => l.trim().startsWith("description:"));
|
|
64
|
+
if (!descLine) return { description: "", body: content.slice(match[0].length) };
|
|
65
|
+
const desc = descLine.slice(descLine.indexOf(":") + 1).trim().replace(/^['"]|['"]$/g, "");
|
|
66
|
+
return { description: desc, body: content.slice(match[0].length) };
|
|
67
|
+
} catch { return { description: "", body: "" }; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Build what the available_skills XML block looks like (OH built-in skills only)
|
|
71
|
+
function buildAvailableSkillsXml(includeUserSkills = false) {
|
|
72
|
+
const parts = ['<available_skills>'];
|
|
73
|
+
const allDirs = [path.join(ROOT, "harness/skills")];
|
|
74
|
+
if (includeUserSkills) {
|
|
75
|
+
allDirs.push(
|
|
76
|
+
path.join(HOME, ".agents/skills"),
|
|
77
|
+
path.join(HOME, ".config/opencode/skills"),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
for (const dir of allDirs) {
|
|
81
|
+
if (!fs.existsSync(dir)) continue;
|
|
82
|
+
for (const entry of fs.readdirSync(dir).sort()) {
|
|
83
|
+
const skPath = path.join(dir, entry, "SKILL.md");
|
|
84
|
+
if (!fs.existsSync(skPath)) continue;
|
|
85
|
+
const { description } = extractDescription(skPath);
|
|
86
|
+
parts.push(` <skill>`);
|
|
87
|
+
parts.push(` <name>${entry}</name>`);
|
|
88
|
+
parts.push(` <description>${description}</description>`);
|
|
89
|
+
parts.push(` <location>file:///${skPath.replace(/\\/g, '/')}</location>`);
|
|
90
|
+
parts.push(` </skill>`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
parts.push('</available_skills>');
|
|
94
|
+
return parts.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Count everything
|
|
98
|
+
console.log("=".repeat(80));
|
|
99
|
+
console.log("EXACT TOKEN COUNTS (gpt-tokenizer)");
|
|
100
|
+
console.log("=".repeat(80));
|
|
101
|
+
|
|
102
|
+
let grandTotal = 0;
|
|
103
|
+
|
|
104
|
+
for (const [category, files] of Object.entries(categories)) {
|
|
105
|
+
console.log(`\n--- ${category} ---`);
|
|
106
|
+
let catTotal = 0;
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
if (!fs.existsSync(file)) continue;
|
|
109
|
+
const content = fs.readFileSync(file, "utf8");
|
|
110
|
+
const tokens = encode(content).length;
|
|
111
|
+
const name = path.basename(path.dirname(file)) !== "skills"
|
|
112
|
+
? path.basename(file)
|
|
113
|
+
: path.basename(path.dirname(file));
|
|
114
|
+
console.log(` ${name.padEnd(40)} ${String(tokens).padStart(6)} tokens (${content.length.toLocaleString()} bytes)`);
|
|
115
|
+
catTotal += tokens;
|
|
116
|
+
}
|
|
117
|
+
console.log(` ${"-".repeat(50)}`);
|
|
118
|
+
console.log(` TOTAL: ${catTotal} tokens`);
|
|
119
|
+
grandTotal += catTotal;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Available skills XML cost (OH built-in only — user skills no longer registered)
|
|
123
|
+
console.log(`\n--- available_skills XML Block (OH only, 31 skills) ---`);
|
|
124
|
+
const xmlContent = buildAvailableSkillsXml(false);
|
|
125
|
+
const xmlTokens = encode(xmlContent).length;
|
|
126
|
+
console.log(` Total: ${xmlTokens} tokens (${xmlContent.length.toLocaleString()} bytes)`);
|
|
127
|
+
|
|
128
|
+
console.log(`\n--- available_skills XML Block (ALL 61 skills, for comparison) ---`);
|
|
129
|
+
const xmlContentAll = buildAvailableSkillsXml(true);
|
|
130
|
+
const xmlTokensAll = encode(xmlContentAll).length;
|
|
131
|
+
console.log(` Total: ${xmlTokensAll} tokens (${xmlContentAll.length.toLocaleString()} bytes)`);
|
|
132
|
+
|
|
133
|
+
grandTotal += xmlTokens;
|
|
134
|
+
|
|
135
|
+
// System prompt components
|
|
136
|
+
const instructionsTotal = categories["Instructions"].reduce((s, f) => {
|
|
137
|
+
if (!fs.existsSync(f)) return s;
|
|
138
|
+
return s + encode(fs.readFileSync(f, "utf8")).length;
|
|
139
|
+
}, 0);
|
|
140
|
+
const agentPromptTotal = categories["OH Agent Prompt (injected as system prompt)"].reduce((s, f) => {
|
|
141
|
+
if (!fs.existsSync(f)) return s;
|
|
142
|
+
return s + encode(fs.readFileSync(f, "utf8")).length;
|
|
143
|
+
}, 0);
|
|
144
|
+
|
|
145
|
+
console.log(`\n${"=".repeat(80)}`);
|
|
146
|
+
console.log(`TOTAL ALL SKILL FILES: ${categories["OH Built-in Skills (31, registered)"].reduce((s, f) => s + encode(fs.readFileSync(f, "utf8")).length, 0)} tokens`);
|
|
147
|
+
console.log(`TOTAL USER SKILL FILES: ${categories["User Skills (.agents/skills) — on disk, NOT registered"].reduce((s, f) => s + encode(fs.readFileSync(f, "utf8")).length, 0)} tokens`);
|
|
148
|
+
console.log(`TOTAL CONFIG SKILLS: ${categories["User Skills (.config/opencode/skills) — on disk, NOT registered"].reduce((s, f) => s + encode(fs.readFileSync(f, "utf8")).length, 0)} tokens`);
|
|
149
|
+
console.log(`TOTAL INSTRUCTIONS: ${instructionsTotal} tokens`);
|
|
150
|
+
console.log(`AGENT PROMPT (openhermes.md): ${agentPromptTotal} tokens`);
|
|
151
|
+
console.log(`available_skills XML (ALL 61 skills, registered): ${xmlTokensAll} tokens`);
|
|
152
|
+
console.log(`\nBOOTSTRAP INJECTION: REMOVED (was 4,503 via transform hook)`);
|
|
153
|
+
console.log(`ROUTING INVENTORY: REMOVED (was ~902 via buildRoutingInventory())`);
|
|
154
|
+
console.log(`\nPER-TURN SYSTEM PROMPT COST: ${instructionsTotal + xmlTokensAll + agentPromptTotal} tokens`);
|
|
155
|
+
console.log(` (instructions ${instructionsTotal} + available_skills ${xmlTokensAll} + agent prompt ${agentPromptTotal})`);
|
|
156
|
+
console.log(`PRE-REFACTOR COST: ${instructionsTotal + xmlTokensAll + 4503 + 902} tokens`);
|
|
157
|
+
console.log(`SAVINGS: ${(instructionsTotal + xmlTokensAll + 4503 + 902) - (instructionsTotal + xmlTokensAll + agentPromptTotal)} tokens per turn`);
|
|
158
|
+
console.log("=".repeat(80));
|