@zhijiewang/openharness 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/data/registry.json +262 -0
  2. package/data/skills/code-review.md +19 -0
  3. package/data/skills/commit.md +17 -0
  4. package/data/skills/debug.md +24 -0
  5. package/data/skills/diagnose.md +24 -0
  6. package/data/skills/plan.md +25 -0
  7. package/data/skills/simplify.md +24 -0
  8. package/data/skills/tdd.md +22 -0
  9. package/dist/agents/roles.d.ts +12 -2
  10. package/dist/agents/roles.js +65 -6
  11. package/dist/commands/ai.js +27 -7
  12. package/dist/commands/skills.d.ts +1 -1
  13. package/dist/commands/skills.js +51 -6
  14. package/dist/components/App.js +7 -1
  15. package/dist/harness/config.d.ts +24 -0
  16. package/dist/harness/hooks.d.ts +14 -0
  17. package/dist/harness/hooks.js +205 -11
  18. package/dist/harness/marketplace.d.ts +77 -2
  19. package/dist/harness/marketplace.js +260 -38
  20. package/dist/harness/memory.d.ts +34 -0
  21. package/dist/harness/memory.js +96 -0
  22. package/dist/harness/plugins.d.ts +13 -3
  23. package/dist/harness/plugins.js +98 -17
  24. package/dist/harness/session-db.d.ts +8 -1
  25. package/dist/harness/session-db.js +24 -3
  26. package/dist/harness/skill-registry.d.ts +26 -2
  27. package/dist/harness/skill-registry.js +42 -4
  28. package/dist/tools/AgentTool/index.d.ts +2 -2
  29. package/dist/tools/DiagnosticsTool/index.d.ts +1 -1
  30. package/dist/tools/GrepTool/index.d.ts +6 -6
  31. package/dist/tools/MemoryTool/index.d.ts +4 -4
  32. package/dist/tools/MonitorTool/index.js +5 -1
  33. package/dist/types/permissions.js +104 -42
  34. package/dist/utils/bash-safety.d.ts +19 -0
  35. package/dist/utils/bash-safety.js +179 -1
  36. package/dist/utils/safe-env.d.ts +5 -1
  37. package/dist/utils/safe-env.js +19 -1
  38. package/package.json +3 -1
@@ -0,0 +1,262 @@
1
+ {
2
+ "skills": [
3
+ {
4
+ "name": "code-review",
5
+ "description": "Systematic code review for bugs, security, and style",
6
+ "author": "zhijiewong",
7
+ "version": "1.0.0",
8
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/code-review.md",
9
+ "tags": ["review", "quality", "security"],
10
+ "license": "MIT",
11
+ "attribution": "© 2025 OpenHarness contributors",
12
+ "upstream": "https://github.com/zhijiewong/openharness"
13
+ },
14
+ {
15
+ "name": "debug",
16
+ "description": "Systematic debugging approach",
17
+ "author": "zhijiewong",
18
+ "version": "1.0.0",
19
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/debug.md",
20
+ "tags": ["debug", "fix", "troubleshoot"],
21
+ "license": "MIT",
22
+ "attribution": "© 2025 OpenHarness contributors",
23
+ "upstream": "https://github.com/zhijiewong/openharness"
24
+ },
25
+ {
26
+ "name": "tdd",
27
+ "description": "Test-driven development workflow",
28
+ "author": "zhijiewong",
29
+ "version": "1.0.0",
30
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/tdd.md",
31
+ "tags": ["test", "tdd", "testing"],
32
+ "license": "MIT",
33
+ "attribution": "© 2025 OpenHarness contributors",
34
+ "upstream": "https://github.com/zhijiewong/openharness"
35
+ },
36
+ {
37
+ "name": "commit",
38
+ "description": "Create well-formed git commits",
39
+ "author": "zhijiewong",
40
+ "version": "1.0.0",
41
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/commit.md",
42
+ "tags": ["git", "commit", "version-control"],
43
+ "license": "MIT",
44
+ "attribution": "© 2025 OpenHarness contributors",
45
+ "upstream": "https://github.com/zhijiewong/openharness"
46
+ },
47
+ {
48
+ "name": "diagnose",
49
+ "description": "Diagnose why an agent run failed, regressed, or produced unexpected output",
50
+ "author": "zhijiewong",
51
+ "version": "1.0.0",
52
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/diagnose.md",
53
+ "tags": ["agent", "debug", "diagnose", "ops"],
54
+ "license": "MIT",
55
+ "attribution": "© 2025 OpenHarness contributors",
56
+ "upstream": "https://github.com/zhijiewong/openharness"
57
+ },
58
+ {
59
+ "name": "plan",
60
+ "description": "Design an implementation plan before coding",
61
+ "author": "zhijiewong",
62
+ "version": "1.0.0",
63
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/plan.md",
64
+ "tags": ["plan", "design", "architecture"],
65
+ "license": "MIT",
66
+ "attribution": "© 2025 OpenHarness contributors",
67
+ "upstream": "https://github.com/zhijiewong/openharness"
68
+ },
69
+ {
70
+ "name": "simplify",
71
+ "description": "Refactor code to be simpler and more maintainable without changing behavior",
72
+ "author": "zhijiewong",
73
+ "version": "1.0.0",
74
+ "source": "https://raw.githubusercontent.com/zhijiewong/openharness/main/data/skills/simplify.md",
75
+ "tags": ["refactor", "simplify", "quality"],
76
+ "license": "MIT",
77
+ "attribution": "© 2025 OpenHarness contributors",
78
+ "upstream": "https://github.com/zhijiewong/openharness"
79
+ },
80
+
81
+ {
82
+ "name": "superpowers-brainstorming",
83
+ "description": "Explores user intent, requirements and design before implementation. Use before any creative work.",
84
+ "author": "Jesse Vincent",
85
+ "version": "5.0.7",
86
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/brainstorming/SKILL.md",
87
+ "tags": ["plan", "brainstorm", "design", "superpowers"],
88
+ "license": "MIT",
89
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
90
+ "upstream": "https://github.com/obra/superpowers"
91
+ },
92
+ {
93
+ "name": "superpowers-systematic-debugging",
94
+ "description": "Structured debugging approach before proposing fixes",
95
+ "author": "Jesse Vincent",
96
+ "version": "5.0.7",
97
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/systematic-debugging/SKILL.md",
98
+ "tags": ["debug", "fix", "superpowers"],
99
+ "license": "MIT",
100
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
101
+ "upstream": "https://github.com/obra/superpowers"
102
+ },
103
+ {
104
+ "name": "superpowers-test-driven-development",
105
+ "description": "Strict TDD workflow — test first, minimal implementation, refactor",
106
+ "author": "Jesse Vincent",
107
+ "version": "5.0.7",
108
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/test-driven-development/SKILL.md",
109
+ "tags": ["test", "tdd", "superpowers"],
110
+ "license": "MIT",
111
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
112
+ "upstream": "https://github.com/obra/superpowers"
113
+ },
114
+ {
115
+ "name": "superpowers-writing-plans",
116
+ "description": "Author detailed implementation plans for multi-step tasks before touching code",
117
+ "author": "Jesse Vincent",
118
+ "version": "5.0.7",
119
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/writing-plans/SKILL.md",
120
+ "tags": ["plan", "write", "superpowers"],
121
+ "license": "MIT",
122
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
123
+ "upstream": "https://github.com/obra/superpowers"
124
+ },
125
+ {
126
+ "name": "superpowers-executing-plans",
127
+ "description": "Execute a written implementation plan with review checkpoints",
128
+ "author": "Jesse Vincent",
129
+ "version": "5.0.7",
130
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/executing-plans/SKILL.md",
131
+ "tags": ["execute", "plan", "superpowers"],
132
+ "license": "MIT",
133
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
134
+ "upstream": "https://github.com/obra/superpowers"
135
+ },
136
+ {
137
+ "name": "superpowers-using-git-worktrees",
138
+ "description": "Create isolated git worktrees for feature development with safety verification",
139
+ "author": "Jesse Vincent",
140
+ "version": "5.0.7",
141
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/using-git-worktrees/SKILL.md",
142
+ "tags": ["git", "worktree", "superpowers"],
143
+ "license": "MIT",
144
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
145
+ "upstream": "https://github.com/obra/superpowers"
146
+ },
147
+ {
148
+ "name": "superpowers-verification-before-completion",
149
+ "description": "Run verification commands and confirm output before claiming work is complete",
150
+ "author": "Jesse Vincent",
151
+ "version": "5.0.7",
152
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/verification-before-completion/SKILL.md",
153
+ "tags": ["verify", "complete", "superpowers"],
154
+ "license": "MIT",
155
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
156
+ "upstream": "https://github.com/obra/superpowers"
157
+ },
158
+ {
159
+ "name": "superpowers-dispatching-parallel-agents",
160
+ "description": "Dispatch multiple independent tasks to parallel sub-agents",
161
+ "author": "Jesse Vincent",
162
+ "version": "5.0.7",
163
+ "source": "https://raw.githubusercontent.com/obra/superpowers/main/skills/dispatching-parallel-agents/SKILL.md",
164
+ "tags": ["parallel", "agent", "superpowers"],
165
+ "license": "MIT",
166
+ "attribution": "© 2025 Jesse Vincent — superpowers plugin",
167
+ "upstream": "https://github.com/obra/superpowers"
168
+ },
169
+
170
+ {
171
+ "name": "cli-anything-shotcut",
172
+ "description": "CLI for Shotcut — stateful video editing via MLT XML, designed for agents",
173
+ "author": "HKUDS",
174
+ "version": "0.1.0",
175
+ "source": "https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/shotcut/agent-harness/cli_anything/shotcut/skills/SKILL.md",
176
+ "tags": ["video", "editing", "shotcut", "cli-anything"],
177
+ "license": "Apache-2.0",
178
+ "attribution": "© HKUDS CLI-Anything contributors",
179
+ "upstream": "https://github.com/HKUDS/CLI-Anything"
180
+ },
181
+ {
182
+ "name": "cli-anything-obsidian",
183
+ "description": "CLI for Obsidian via the Local REST API — knowledge management harness",
184
+ "author": "HKUDS",
185
+ "version": "0.1.0",
186
+ "source": "https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/obsidian/agent-harness/cli_anything/obsidian/skills/SKILL.md",
187
+ "tags": ["obsidian", "notes", "kb", "cli-anything"],
188
+ "license": "Apache-2.0",
189
+ "attribution": "© HKUDS CLI-Anything contributors",
190
+ "upstream": "https://github.com/HKUDS/CLI-Anything"
191
+ },
192
+ {
193
+ "name": "cli-anything-zotero",
194
+ "description": "CLI for Zotero — reference management automation",
195
+ "author": "HKUDS",
196
+ "version": "0.4.1",
197
+ "source": "https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/zotero/agent-harness/cli_anything/zotero/skills/SKILL.md",
198
+ "tags": ["zotero", "research", "references", "cli-anything"],
199
+ "license": "Apache-2.0",
200
+ "attribution": "© HKUDS CLI-Anything contributors",
201
+ "upstream": "https://github.com/HKUDS/CLI-Anything"
202
+ },
203
+ {
204
+ "name": "cli-anything-blender",
205
+ "description": "CLI for Blender — 3D modeling automation",
206
+ "author": "HKUDS",
207
+ "version": "0.1.0",
208
+ "source": "https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/blender/agent-harness/cli_anything/blender/skills/SKILL.md",
209
+ "tags": ["blender", "3d", "modeling", "cli-anything"],
210
+ "license": "Apache-2.0",
211
+ "attribution": "© HKUDS CLI-Anything contributors",
212
+ "upstream": "https://github.com/HKUDS/CLI-Anything"
213
+ },
214
+ {
215
+ "name": "cli-anything-inkscape",
216
+ "description": "CLI for Inkscape — vector graphics automation",
217
+ "author": "HKUDS",
218
+ "version": "0.1.0",
219
+ "source": "https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/inkscape/agent-harness/cli_anything/inkscape/skills/SKILL.md",
220
+ "tags": ["inkscape", "svg", "vector", "cli-anything"],
221
+ "license": "Apache-2.0",
222
+ "attribution": "© HKUDS CLI-Anything contributors",
223
+ "upstream": "https://github.com/HKUDS/CLI-Anything"
224
+ },
225
+ {
226
+ "name": "cli-anything-godot",
227
+ "description": "CLI for Godot — game engine automation with E2E demo pipeline",
228
+ "author": "HKUDS",
229
+ "version": "0.1.0",
230
+ "source": "https://raw.githubusercontent.com/HKUDS/CLI-Anything/main/godot/agent-harness/cli_anything/godot/skills/SKILL.md",
231
+ "tags": ["godot", "game", "engine", "cli-anything"],
232
+ "license": "Apache-2.0",
233
+ "attribution": "© HKUDS CLI-Anything contributors",
234
+ "upstream": "https://github.com/HKUDS/CLI-Anything"
235
+ },
236
+
237
+ {
238
+ "name": "tob-smart-contract-audit",
239
+ "description": "Trail of Bits — smart contract security audit workflow (link-only, CC-BY-SA)",
240
+ "author": "Trail of Bits",
241
+ "version": "1.0.0",
242
+ "source": "https://raw.githubusercontent.com/trailofbits/skills/main/skills/smart-contract-audit/SKILL.md",
243
+ "tags": ["security", "audit", "smart-contract", "tob"],
244
+ "license": "CC-BY-SA-4.0",
245
+ "attribution": "© Trail of Bits — CC-BY-SA-4.0 (ShareAlike)",
246
+ "upstream": "https://github.com/trailofbits/skills",
247
+ "installable": false
248
+ },
249
+ {
250
+ "name": "tob-malware-detection",
251
+ "description": "Trail of Bits — malware detection workflow (link-only, CC-BY-SA)",
252
+ "author": "Trail of Bits",
253
+ "version": "1.0.0",
254
+ "source": "https://raw.githubusercontent.com/trailofbits/skills/main/skills/malware-detection/SKILL.md",
255
+ "tags": ["security", "malware", "detection", "tob"],
256
+ "license": "CC-BY-SA-4.0",
257
+ "attribution": "© Trail of Bits — CC-BY-SA-4.0 (ShareAlike)",
258
+ "upstream": "https://github.com/trailofbits/skills",
259
+ "installable": false
260
+ }
261
+ ]
262
+ }
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: code-review
3
+ description: Systematic code review for bugs, security, and quality
4
+ whenToUse: When reviewing code changes, PRs, or completed implementations
5
+ allowedTools: [Read, Glob, Grep, Bash]
6
+ ---
7
+
8
+ # Code Review
9
+
10
+ Review the code systematically for:
11
+
12
+ 1. **Correctness** — Does the code do what it claims? Logic errors?
13
+ 2. **Security** — SQL injection, XSS, command injection, path traversal, secrets in code?
14
+ 3. **Error handling** — Are errors caught and handled appropriately?
15
+ 4. **Edge cases** — Null/empty inputs, boundary conditions, concurrent access?
16
+ 5. **Performance** — Unnecessary loops, missing indexes, N+1 queries?
17
+ 6. **Readability** — Clear naming, reasonable complexity, adequate (not excessive) comments?
18
+
19
+ Report findings with file path, line number, severity (critical/warning/info), and suggested fix.
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: commit
3
+ description: Create well-formed git commits
4
+ whenToUse: When the user asks to commit changes or create a PR
5
+ allowedTools: [Bash, Read, Glob]
6
+ ---
7
+
8
+ # Git Commit Workflow
9
+
10
+ 1. Run `git status` and `git diff` to see what changed
11
+ 2. Run `git log --oneline -5` to match the repo's commit message style
12
+ 3. Stage specific files (not `git add -A`) to avoid committing secrets or binaries
13
+ 4. Write a concise commit message:
14
+ - First line: imperative mood, under 72 chars, describes the "why"
15
+ - Body (if needed): explain context, not just what changed
16
+ 5. Create the commit
17
+ 6. Run `git status` to verify success
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: debug
3
+ description: Systematic debugging approach
4
+ whenToUse: When encountering any bug, test failure, or unexpected behavior
5
+ allowedTools: [Read, Bash, Grep, Glob]
6
+ ---
7
+
8
+ # Systematic Debugging
9
+
10
+ Follow this process:
11
+
12
+ 1. **Reproduce** — Confirm the bug exists. Run the failing test or command.
13
+ 2. **Read the error** — What does the error message actually say? Read the full stack trace.
14
+ 3. **Locate** — Find the exact line where the error occurs. Read the surrounding code.
15
+ 4. **Understand** — Why does this code produce the wrong result? Trace the data flow.
16
+ 5. **Hypothesize** — Form a specific theory about the cause.
17
+ 6. **Verify** — Test your theory with a minimal change or print statement.
18
+ 7. **Fix** — Make the smallest change that fixes the bug.
19
+ 8. **Confirm** — Run the test/command again to verify the fix works.
20
+
21
+ Rules:
22
+ - Don't guess. Read the error message carefully.
23
+ - Don't change multiple things at once.
24
+ - Don't skip the reproduce step.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: diagnose
3
+ description: Diagnose why an agent run failed, regressed, or produced unexpected output — using structured evidence instead of intuition
4
+ whenToUse: When an agent run produced wrong output, crashed mid-execution, or regressed from a previous successful run
5
+ allowedTools: [Read, Bash, Grep, Glob]
6
+ ---
7
+
8
+ # Diagnose Agent Run
9
+
10
+ Apply this when something an agent did went wrong — not for ordinary code bugs (use `debug` for those).
11
+
12
+ 1. **Define the failure precisely** — What was the expected output? What was the actual output? One sentence each.
13
+ 2. **Locate the artifacts** — Find the run's logs, transcripts, tool-call traces, and any files it produced. Common locations: `.oh/logs/`, `.oh/tasks.json`, `.oh/sessions/`, stderr capture.
14
+ 3. **Reconstruct the timeline** — Walk through tool calls in order. Where did the trajectory diverge from what should have happened?
15
+ 4. **Isolate the inflection point** — Find the single tool call, prompt step, or input that turned a working run into a broken one.
16
+ 5. **Form a hypothesis** — Was it bad input, model regression, missing context, tool timeout, permission denial, or stale memory? Be specific.
17
+ 6. **Verify with a minimal repro** — Re-run only the inflection step in isolation. Confirm the same wrong behavior reproduces.
18
+ 7. **Report** — Write up: failure mode, inflection point (file:line or message index), root cause, suggested fix.
19
+
20
+ Rules:
21
+ - Don't guess from the symptom. Read the actual logs.
22
+ - Distinguish "the model made a bad decision" from "the harness broke" — they need different fixes.
23
+ - If the run is non-deterministic, repro at least 3 times before claiming a hypothesis.
24
+ - Never edit code as part of diagnosis. The output is a report, not a fix.
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: plan
3
+ description: Design an implementation plan before coding
4
+ whenToUse: When the user asks to build a feature, fix a non-trivial bug, or refactor — anything beyond a one-line change
5
+ allowedTools: [Read, Glob, Grep]
6
+ ---
7
+
8
+ # Implementation Planning
9
+
10
+ Plan before you code. The output is a written plan, not edited files.
11
+
12
+ 1. **Restate the goal** — One sentence describing what success looks like. Confirm with the user if ambiguous.
13
+ 2. **Map the affected surface** — List every file you'll touch and every public interface you'll change.
14
+ 3. **Find existing patterns** — Search the codebase for similar features. Reuse existing utilities; don't invent parallel ones.
15
+ 4. **Identify the minimal change** — What's the smallest set of edits that delivers the goal? Resist scope creep.
16
+ 5. **Sequence the work** — Order the changes so each step is independently testable. Each step should leave the build green.
17
+ 6. **Plan verification** — How will you know each step worked? List the commands or manual checks.
18
+ 7. **Surface risks** — What could go wrong? Migration data loss, breaking changes, perf regression, security holes? Note each with a mitigation.
19
+ 8. **Write it up** — A short doc with: goal, files touched, sequence, verification, risks. Get user approval before coding.
20
+
21
+ Rules:
22
+ - Don't write code in this phase. Only research and write the plan.
23
+ - A plan that lists "all the things" is not a plan. Pick the recommended approach.
24
+ - Cite files and functions you'll reuse with `path:line` references.
25
+ - If you can't write the plan in one page, the scope is too big — split it.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: simplify
3
+ description: Refactor code to be simpler and more maintainable without changing behavior
4
+ whenToUse: When code is hard to read, has duplicated logic, over-abstracts, or accumulated dead branches
5
+ allowedTools: [Read, Edit, Bash, Glob, Grep]
6
+ ---
7
+
8
+ # Simplify Code
9
+
10
+ Simpler code is easier to read, change, and debug. Behavior must not change.
11
+
12
+ 1. **Read the code in scope** — Understand what it does end-to-end before touching anything.
13
+ 2. **List the smells** — Duplication, dead branches, premature abstraction, deeply nested conditionals, vague names, comments that explain WHAT instead of WHY.
14
+ 3. **Confirm tests cover the behavior** — If there are no tests, write characterization tests first. Refactoring without tests is gambling.
15
+ 4. **Make one change at a time** — Inline a wrapper, extract a function, rename a variable, delete dead code. Run tests after each.
16
+ 5. **Prefer deletion over addition** — If you can remove code without breaking tests, that's almost always the right move.
17
+ 6. **Stop when it's good enough** — Don't refactor for its own sake. Stop when further changes wouldn't meaningfully improve readability.
18
+
19
+ Rules:
20
+ - Never change behavior. If you discover a bug, file it separately — don't fix it during a refactor.
21
+ - Three similar lines is better than a premature abstraction. Wait until the third or fourth occurrence before extracting.
22
+ - Delete comments that explain what well-named code already says.
23
+ - If the change touches more than one file or one concern, split it into separate refactors.
24
+ - Don't introduce new dependencies, frameworks, or patterns.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: tdd
3
+ description: Test-driven development workflow
4
+ whenToUse: When implementing any feature or bugfix, before writing implementation code
5
+ allowedTools: [Read, Edit, Write, Bash, Glob, Grep]
6
+ ---
7
+
8
+ # Test-Driven Development
9
+
10
+ Follow this workflow strictly:
11
+
12
+ 1. **Write the test first** — Create a failing test that describes the expected behavior
13
+ 2. **Run the test** — Verify it fails for the right reason
14
+ 3. **Write minimal implementation** — Only enough code to make the test pass
15
+ 4. **Run tests again** — Verify all tests pass
16
+ 5. **Refactor** — Clean up the code while keeping tests green
17
+
18
+ Rules:
19
+ - Never write implementation before the test
20
+ - Each test should test one thing
21
+ - Keep tests fast and isolated
22
+ - Use descriptive test names that explain the behavior
@@ -13,10 +13,20 @@ export type AgentRole = {
13
13
  name: string;
14
14
  description: string;
15
15
  systemPromptSupplement: string;
16
- /** Suggested tools to include (empty = all tools) */
16
+ /** Suggested tools to include (empty = all tools) — Anthropic alias: `tools` */
17
17
  suggestedTools?: string[];
18
+ /** Tool denylist applied after suggestedTools — Anthropic standard: `disallowedTools` */
19
+ disallowedTools?: string[];
20
+ /** Per-agent model override (e.g. "sonnet", "opus", "haiku", or a full model ID) */
21
+ model?: string;
22
+ /** Isolation mode for sub-agent execution. Default: inherits parent. */
23
+ isolation?: "none" | "worktree";
24
+ /** Per-agent MCP servers injected only when this agent runs (parsed via raw passthrough; dispatcher wiring is a future task). */
25
+ mcpServers?: Record<string, unknown>;
26
+ /** Per-agent hooks (parsed via raw passthrough; dispatcher wiring is a future task). */
27
+ hooks?: Record<string, unknown>;
18
28
  };
19
- /** Discover markdown agent roles from .oh/agents/ and ~/.oh/agents/ */
29
+ /** Discover markdown agent roles from .oh/agents/ + ~/.oh/agents/ + .claude/agents/ + ~/.claude/agents/ */
20
30
  export declare function discoverMarkdownAgents(): AgentRole[];
21
31
  /** Get a role by ID (checks built-in first, then markdown agents) */
22
32
  export declare function getRole(id: string): AgentRole | undefined;
@@ -163,14 +163,37 @@ import { homedir } from "node:os";
163
163
  import { basename, join } from "node:path";
164
164
  const PROJECT_AGENTS_DIR = join(".oh", "agents");
165
165
  const GLOBAL_AGENTS_DIR = join(homedir(), ".oh", "agents");
166
+ // Claude Code ecosystem mirror paths
167
+ const CC_PROJECT_AGENTS_DIR = join(".claude", "agents");
168
+ const CC_GLOBAL_AGENTS_DIR = join(homedir(), ".claude", "agents");
169
+ /** Parse a frontmatter list value. Accepts `[a, b]` (YAML inline) OR `a b c` (Claude Code style). */
170
+ function parseAgentList(raw) {
171
+ const trimmed = raw.trim();
172
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
173
+ return trimmed
174
+ .slice(1, -1)
175
+ .split(",")
176
+ .map((t) => t.trim())
177
+ .filter(Boolean);
178
+ }
179
+ // Claude Code spec: comma-separated OR space-separated bare tokens. Handle both.
180
+ return trimmed
181
+ .replace(/^["']|["']$/g, "")
182
+ .split(/[,\s]+/)
183
+ .map((t) => t.trim())
184
+ .filter(Boolean);
185
+ }
166
186
  /**
167
187
  * Parse a markdown agent file into an AgentRole.
168
188
  *
169
- * Format:
189
+ * Format (OH-native + Claude Code interoperable):
170
190
  * ---
171
191
  * name: My Agent
172
192
  * description: What it does
173
- * tools: [Read, Grep, Bash]
193
+ * tools: Read, Grep, Bash # space- or comma-separated, OR YAML array
194
+ * disallowedTools: Write, Edit # tool denylist
195
+ * model: sonnet # model override
196
+ * isolation: worktree # explicit isolation mode
174
197
  * ---
175
198
  *
176
199
  * System prompt content...
@@ -182,20 +205,43 @@ function parseAgentMarkdown(raw, filePath) {
182
205
  const fm = fmMatch[1];
183
206
  const nameMatch = fm.match(/^name:\s*(.+)$/m);
184
207
  const descMatch = fm.match(/^description:\s*(.+)$/m);
185
- const toolsMatch = fm.match(/^tools:\s*\[(.+)\]$/m);
208
+ const toolsMatch = fm.match(/^tools:\s*(.+)$/m);
209
+ const disallowedMatch = fm.match(/^disallowedTools:\s*(.+)$/m) ?? fm.match(/^disallowed-tools:\s*(.+)$/m);
210
+ const modelMatch = fm.match(/^model:\s*(.+)$/m);
211
+ const isolationMatch = fm.match(/^isolation:\s*(.+)$/m);
186
212
  const fmEnd = raw.indexOf("---", raw.indexOf("---") + 3);
187
213
  const content = fmEnd > 0 ? raw.slice(fmEnd + 3).trim() : "";
188
214
  const id = basename(filePath, ".md")
189
215
  .toLowerCase()
190
216
  .replace(/[^a-z0-9]+/g, "-");
217
+ const isolation = isolationMatch?.[1]?.trim().replace(/^["']|["']$/g, "");
218
+ const validIsolation = isolation === "worktree" || isolation === "none" ? isolation : undefined;
219
+ // Parse optional inline-JSON fields (mcpServers, hooks). These are block fields in
220
+ // Anthropic's YAML but we support single-line JSON for lightweight frontmatter use.
221
+ const mcpServersMatch = fm.match(/^mcpServers:\s*(\{[\s\S]*?\})\s*$/m);
222
+ const hooksMatch = fm.match(/^hooks:\s*(\{[\s\S]*?\})\s*$/m);
191
223
  return {
192
224
  id,
193
225
  name: nameMatch?.[1]?.trim() ?? id,
194
226
  description: descMatch?.[1]?.trim() ?? "",
195
227
  systemPromptSupplement: content,
196
- suggestedTools: toolsMatch ? toolsMatch[1].split(",").map((t) => t.trim()) : undefined,
228
+ suggestedTools: toolsMatch ? parseAgentList(toolsMatch[1]) : undefined,
229
+ disallowedTools: disallowedMatch ? parseAgentList(disallowedMatch[1]) : undefined,
230
+ model: modelMatch?.[1]?.trim().replace(/^["']|["']$/g, ""),
231
+ isolation: validIsolation,
232
+ mcpServers: mcpServersMatch ? tryParseJson(mcpServersMatch[1]) : undefined,
233
+ hooks: hooksMatch ? tryParseJson(hooksMatch[1]) : undefined,
197
234
  };
198
235
  }
236
+ function tryParseJson(raw) {
237
+ try {
238
+ const v = JSON.parse(raw);
239
+ return v && typeof v === "object" ? v : undefined;
240
+ }
241
+ catch {
242
+ return undefined;
243
+ }
244
+ }
199
245
  /** Load agent roles from a directory of .md files */
200
246
  function loadAgentsFromDir(dir) {
201
247
  if (!existsSync(dir))
@@ -213,9 +259,22 @@ function loadAgentsFromDir(dir) {
213
259
  })
214
260
  .filter((r) => r !== null);
215
261
  }
216
- /** Discover markdown agent roles from .oh/agents/ and ~/.oh/agents/ */
262
+ /** Discover markdown agent roles from .oh/agents/ + ~/.oh/agents/ + .claude/agents/ + ~/.claude/agents/ */
217
263
  export function discoverMarkdownAgents() {
218
- return [...loadAgentsFromDir(PROJECT_AGENTS_DIR), ...loadAgentsFromDir(GLOBAL_AGENTS_DIR)];
264
+ const all = [
265
+ ...loadAgentsFromDir(PROJECT_AGENTS_DIR),
266
+ ...loadAgentsFromDir(GLOBAL_AGENTS_DIR),
267
+ ...loadAgentsFromDir(CC_PROJECT_AGENTS_DIR),
268
+ ...loadAgentsFromDir(CC_GLOBAL_AGENTS_DIR),
269
+ ];
270
+ // De-duplicate by id (first wins — OH paths take precedence over .claude paths)
271
+ const seen = new Set();
272
+ return all.filter((r) => {
273
+ if (seen.has(r.id))
274
+ return false;
275
+ seen.add(r.id);
276
+ return true;
277
+ });
219
278
  }
220
279
  /** Get a role by ID (checks built-in first, then markdown agents) */
221
280
  export function getRole(id) {
@@ -109,12 +109,29 @@ export function registerAICommands(register) {
109
109
  lines.push("Send messages with: Agent({ prompt: 'ask the other agent...', allowed_tools: ['SendMessage'] })");
110
110
  return { output: lines.join("\n"), handled: true };
111
111
  });
112
- register("plugins", "Manage plugins: list, search, install, uninstall, marketplace", (args) => {
112
+ const pluginsHandler = (args) => {
113
113
  const { discoverPlugins, discoverSkills } = require("../harness/plugins.js");
114
114
  const { searchMarketplace, installPlugin, uninstallPlugin, getInstalledPlugins, listMarketplaces, addMarketplace, removeMarketplace, formatMarketplaceSearch, formatInstalledPlugins, } = require("../harness/marketplace.js");
115
115
  const parts = args.trim().split(/\s+/);
116
116
  const subcommand = parts[0] ?? "";
117
117
  const rest = parts.slice(1).join(" ");
118
+ if (subcommand === "info" && rest) {
119
+ const installed = getInstalledPlugins();
120
+ const p = installed.find((x) => x.name === rest);
121
+ if (!p)
122
+ return { output: `Plugin "${rest}" not found among installed plugins.`, handled: true };
123
+ const lines = [
124
+ `${p.name}@${p.version}`,
125
+ p.description ? ` ${p.description}` : "",
126
+ p.author ? ` by ${p.author}` : "",
127
+ p.license ? ` license: ${p.license}` : "",
128
+ p.homepage ? ` homepage: ${p.homepage}` : "",
129
+ p.keywords?.length ? ` keywords: ${p.keywords.join(", ")}` : "",
130
+ ` marketplace: ${p.marketplace}`,
131
+ ` cachePath: ${p.cachePath}`,
132
+ ].filter(Boolean);
133
+ return { output: lines.join("\n"), handled: true };
134
+ }
118
135
  if (subcommand === "marketplace") {
119
136
  const action = parts[1];
120
137
  const source = parts.slice(2).join(" ");
@@ -192,13 +209,16 @@ export function registerAICommands(register) {
192
209
  }
193
210
  lines.push("");
194
211
  lines.push("Commands:");
195
- lines.push(" /plugins search <query> Search marketplaces");
196
- lines.push(" /plugins install <name> Install from marketplace");
197
- lines.push(" /plugins uninstall <name> Remove a plugin");
198
- lines.push(" /plugins marketplace add <src> Add a marketplace");
199
- lines.push(" /plugins marketplace List marketplaces");
212
+ lines.push(" /plugin info <name> Show full manifest for a plugin");
213
+ lines.push(" /plugin search <query> Search marketplaces");
214
+ lines.push(" /plugin install <name> Install from marketplace");
215
+ lines.push(" /plugin uninstall <name> Remove a plugin");
216
+ lines.push(" /plugin marketplace add <src> Add a marketplace");
217
+ lines.push(" /plugin marketplace List marketplaces");
200
218
  return { output: lines.join("\n"), handled: true };
201
- });
219
+ };
220
+ register("plugins", "Manage plugins: list, search, install, uninstall, marketplace, info", pluginsHandler);
221
+ register("plugin", "Alias of /plugins (Claude Code-style singular)", pluginsHandler);
202
222
  register("cybergotchi", "Manage your cybergotchi — feed · pet · rest · status · rename · reset", (args) => {
203
223
  return handleCybergotchiCommand(args);
204
224
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Skill management commands — /skill-create, /skill-delete, /skill-edit, /skill-search, /skill-install
2
+ * Skill management commands — /skills, /skill-create, /skill-delete, /skill-edit, /skill-search, /skill-install
3
3
  */
4
4
  import type { CommandHandler } from "./types.js";
5
5
  export declare function registerSkillCommands(register: (name: string, description: string, handler: CommandHandler) => void): void;