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.
Files changed (96) hide show
  1. package/CONTEXT.md +9 -0
  2. package/README.md +26 -15
  3. package/bootstrap.ts +161 -124
  4. package/harness/agents/oh-browser.md +97 -0
  5. package/harness/agents/oh-builder.md +78 -0
  6. package/harness/agents/oh-facade.md +75 -0
  7. package/harness/agents/oh-fusion.md +45 -0
  8. package/harness/agents/oh-gauntlet.md +71 -0
  9. package/harness/agents/oh-grill.md +71 -0
  10. package/harness/agents/oh-investigate.md +60 -0
  11. package/harness/agents/oh-manifest.md +95 -0
  12. package/harness/agents/oh-plan-review.md +40 -0
  13. package/harness/agents/oh-planner.md +50 -0
  14. package/harness/agents/oh-refactor.md +37 -0
  15. package/harness/agents/oh-retro.md +46 -0
  16. package/harness/agents/oh-review.md +85 -0
  17. package/harness/agents/oh-security.md +83 -0
  18. package/harness/agents/oh-ship.md +76 -0
  19. package/harness/agents/oh-skill-craft.md +38 -0
  20. package/harness/agents/openhermes.md +107 -53
  21. package/harness/codex/AUTOPILOT.md +143 -91
  22. package/harness/codex/CHARTER.md +81 -0
  23. package/harness/commands/oh-doctor.md +193 -14
  24. package/harness/instructions/SHELL.md +76 -0
  25. package/harness/skills/oh-ascii/DEEP.md +292 -0
  26. package/harness/skills/oh-ascii/SKILL.md +31 -0
  27. package/harness/skills/oh-ascii/scripts/check_ascii_alignment.py +596 -0
  28. package/harness/skills/oh-browser/DEEP.md +54 -0
  29. package/harness/skills/oh-browser/SKILL.md +30 -0
  30. package/harness/skills/oh-builder/DEEP.md +63 -0
  31. package/harness/skills/oh-builder/SKILL.md +12 -90
  32. package/harness/skills/oh-expert/DEEP.md +85 -0
  33. package/harness/skills/oh-expert/SKILL.md +13 -106
  34. package/harness/skills/oh-facade/DEEP.md +182 -0
  35. package/harness/skills/oh-facade/SKILL.md +15 -279
  36. package/harness/skills/oh-freeze/DEEP.md +18 -0
  37. package/harness/skills/oh-freeze/SKILL.md +10 -19
  38. package/harness/skills/oh-full-output/DEEP.md +25 -0
  39. package/harness/skills/oh-full-output/SKILL.md +12 -65
  40. package/harness/skills/oh-fusion/DEEP.md +120 -0
  41. package/harness/skills/oh-fusion/SKILL.md +17 -295
  42. package/harness/skills/oh-gauntlet/DEEP.md +77 -0
  43. package/harness/skills/oh-gauntlet/SKILL.md +13 -105
  44. package/harness/skills/oh-grill/DEEP.md +51 -0
  45. package/harness/skills/oh-grill/SKILL.md +12 -63
  46. package/harness/skills/oh-guard/DEEP.md +19 -0
  47. package/harness/skills/oh-guard/SKILL.md +10 -24
  48. package/harness/skills/oh-handoff/DEEP.md +48 -0
  49. package/harness/skills/oh-handoff/SKILL.md +13 -23
  50. package/harness/skills/oh-health/DEEP.md +74 -0
  51. package/harness/skills/oh-health/SKILL.md +13 -76
  52. package/harness/skills/oh-init/DEEP.md +85 -0
  53. package/harness/skills/oh-init/SKILL.md +13 -127
  54. package/harness/skills/oh-investigate/DEEP.md +171 -0
  55. package/harness/skills/oh-investigate/SKILL.md +13 -66
  56. package/harness/skills/oh-issue/DEEP.md +21 -0
  57. package/harness/skills/oh-issue/SKILL.md +11 -27
  58. package/harness/skills/oh-learn/DEEP.md +44 -0
  59. package/harness/skills/oh-learn/SKILL.md +12 -83
  60. package/harness/skills/oh-manifest/DEEP.md +92 -0
  61. package/harness/skills/oh-manifest/SKILL.md +11 -108
  62. package/harness/skills/oh-plan-review/DEEP.md +90 -0
  63. package/harness/skills/oh-plan-review/SKILL.md +13 -115
  64. package/harness/skills/oh-planner/DEEP.md +172 -0
  65. package/harness/skills/oh-planner/SKILL.md +12 -149
  66. package/harness/skills/oh-prd/DEEP.md +45 -0
  67. package/harness/skills/oh-prd/SKILL.md +10 -26
  68. package/harness/skills/oh-refactor/DEEP.md +122 -0
  69. package/harness/skills/oh-refactor/SKILL.md +17 -410
  70. package/harness/skills/oh-retro/DEEP.md +26 -0
  71. package/harness/skills/oh-retro/SKILL.md +12 -24
  72. package/harness/skills/oh-review/DEEP.md +87 -0
  73. package/harness/skills/oh-review/SKILL.md +11 -97
  74. package/harness/skills/oh-security/DEEP.md +83 -0
  75. package/harness/skills/oh-security/SKILL.md +14 -96
  76. package/harness/skills/oh-ship/DEEP.md +141 -0
  77. package/harness/skills/oh-ship/SKILL.md +13 -31
  78. package/harness/skills/oh-skill-craft/DEEP.md +369 -0
  79. package/harness/skills/oh-skill-craft/SKILL.md +17 -178
  80. package/harness/skills/oh-skills-link/DEEP.md +16 -0
  81. package/harness/skills/oh-skills-link/SKILL.md +10 -20
  82. package/harness/skills/oh-skills-list/DEEP.md +20 -0
  83. package/harness/skills/oh-skills-list/SKILL.md +9 -22
  84. package/harness/skills/oh-triage/DEEP.md +23 -0
  85. package/harness/skills/oh-triage/SKILL.md +8 -24
  86. package/harness/skills/oh-worktree/DEEP.md +169 -0
  87. package/harness/skills/oh-worktree/SKILL.md +32 -0
  88. package/lib/harness-resolver.ts +8 -10
  89. package/package.json +5 -3
  90. package/scripts/count-tokens.mjs +158 -0
  91. package/scripts/oh-doctor.ps1 +342 -0
  92. package/harness/codex/CONSTITUTION.md +0 -73
  93. package/harness/codex/ROUTING.md +0 -92
  94. package/harness/instructions/RUNTIME.md +0 -30
  95. package/harness/skills/oh-caveman/SKILL.md +0 -42
  96. package/lib/logger.ts +0 -75
@@ -1,11 +1,7 @@
1
1
  ---
2
2
  name: oh-skills-list
3
- description: "List all available oh-* skills with descriptions"
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
- ## When to Use
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
- ## Output
21
- Markdown table of skills:
15
+ ## Steps
22
16
 
23
- | Skill | Description | Category |
24
- |-------|-------------|----------|
25
- | oh-plan | Strategy + architecture review | Orchestration |
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 | → [done — read-only report] |
39
- | fail | → [surface issue to user] |
40
- | blocker | → surface to user |
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: "Issue triage state machine — classify, prioritise, assign"
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
- ## When to Use
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
- ## States
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
- ## Workflow
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 to appropriate state and owner
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 (publish triaged issues) or oh-handoff (pass to agent) |
46
- | fail | → oh-expert (clarify ambiguous issue) |
47
- | blocker | → surface to user |
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 |
@@ -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", "CONSTITUTION.md"],
13
- ["instructions", "RUNTIME.md"],
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 = 6): string[] {
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, execPath: string, cwd: string): string[] {
30
- const roots = [path.resolve(currentDir, "harness")]
31
- const seen = new Set(roots)
29
+ function buildHarnessCandidates(currentDir: string, cwd: string): string[] {
30
+ const roots: string[] = []
31
+ const seen = new Set<string>()
32
32
 
33
- const anchors = [path.dirname(execPath), path.dirname(path.dirname(execPath)), cwd]
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, execPath, cwd)
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.3.0",
4
- "description": "OpenCode-native orchestration for packaged skills, commands, and agents.",
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.14.46"
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));