Commit 8cfb28a
authored
Feat user skills (#139)
* fix: marked v15 + marked-terminal v7 incompat in markdown-renderer
marked v15's use() iterates 'for (prop in pack.renderer)' and validates every enumerable key against its known renderer method list, throwing "renderer 'o' does not exist" at module init.
The legacy 'new TerminalRenderer(opts)' route assigns config to own enumerable properties (this.o, this.tab, ...), so the first iteration hits an unknown key and crashes. This broke the agent on every 'just start' since PR #135 landed; CI never noticed because no test imports the module.
Switch to the modern markedTerminal() factory which returns a clean MarkedExtension containing only renderer method keys, and add a regression test that import-loads the module and smoke-tests rendering so a future bump can't reintroduce this class of crash.
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
* feat: user-generated skills from session learnings
Lets users persist what the agent learned in a session as a reusable skill at ~/.hyperagent/skills/<name>/SKILL.md, surviving upgrades and overriding system skills with the same name.
Triggered via:
- /save-skill [name] - slash command that builds a synthetic prompt from
session context (tool history, MCP servers, modules registered, recent
errors) and asks the LLM to call generate_skill()
- 'save this as a skill' (natural language) - system message documents
the generate_skill tool so the LLM can call it directly
Components added:
- src/agent/skill-writer.ts: validation + CRUD for user skills,
with HYPERAGENT_USER_SKILLS_DIR env override for tests
- src/agent/session-context.ts: pure extractor that rolls up tool
history, MCP servers, modules registered, and recent errors into a
prompt-ready string
- generate_skill tool: registered in all three gating points
(tools[], ALLOWED_TOOLS, availableTools[]) with interactive approval
- /skills enhanced with 'info <name>', 'edit <name>', 'delete <name>',
override-detection badge for user skills
- skill-loader now supports loading from multiple directories with
override semantics (later dirs win)
- state.ts tracks toolCallHistory (capped FIFO), mcpServersUsed,
modulesRegistered, pendingPrompt; populated by onPostToolUse hook
and registerModuleImpl
- system-message.ts documents the saving workflow for the LLM
- docs/SKILLS.md adds 'User Skills (Persist What You Learn)' section
Tests: 39 new (skill-writer 22, session-context 9, skill-loader +8). All 2443 TS tests pass; 124 Rust tests pass; lint clean.
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
* docs: add hand-off test plan for user-generated skills
Standalone walkthrough at docs/TESTING-USER-SKILLS.md covering smoke test, full workout, override behaviour, boundary cases, and likely failure modes. Intended to be passed to reviewers / testers who want to exercise the feature without reading the implementation.
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
* fix: address PR #139 review feedback (18 issues)
Security & correctness
- skill-writer: cap on UTF-8 byte length (not String.length) so a
multi-byte payload can't bypass the 64 KB limit
- skill-writer: reject reserved /skills subcommand names (info, edit,
delete, list) to prevent shadowing the CLI surface
- skill-writer: reject description/triggers containing newlines or
a bare '---' line so they can't break out of YAML frontmatter
- slash-commands /skills info|edit|delete: validate <name> via
validateSkillName before any filesystem join — closes the path
traversal vector pointed out by the reviewer
UX correctness
- index.ts generate_skill: surface an 'Overwrite existing user
skill?' confirmation when overwrite=true and the file already
exists
- slash-commands /save-skill: pass skipAutoSuggest=true so the
synthetic prompt's scaffolding terms don't trigger unrelated
skills via runSuggestApproach
- slash-commands /new: also reset currentUserPrompt + lastGuidance
- slash-commands /resume: reset toolCallHistory, mcpServersUsed,
modulesRegistered, currentUserPrompt, lastGuidance — local
session-learning state can't be reconstructed from a resumed
remote session
- slash-commands /save-skill: fix 'distinct tools' status line to
count the full tool history, not the bounded topTools view
- session-context: truncate currentUserPrompt to 2000 chars with
an ellipsis so a giant paste can't dominate the prompt
MCP session-learning correctness
- mcp/plugin-adapter: add optional onCall observer; agent wires it
to state.mcpServersUsed so calls made from inside
execute_javascript via host:mcp-<name> imports are now tracked
- state.ts: add skipNextAutoSuggest flag (consumed in
onUserPromptSubmitted)
Documentation
- docs/TESTING-USER-SKILLS.md: drop branch-name reference, switch
override example from non-existent 'code-review' to bundled
'kql-expert', clarify '/skills edit' prints a path (no $EDITOR),
describe the now-correct overwrite confirmation flow, note that
the override badge surfaces in '/skills' list view, fix approval
prompt wording (summary, not full content)
Tests
- Reserved-name rejection
- YAML-unsafe newline rejection (description + trigger)
- UTF-8 byte-length cap (32 KB of 4-byte chars)
- User-prompt truncation contract
Quality gate: 2448 TS tests pass (+5), 124 Rust tests pass.
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
---------
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>1 parent 2e17ac2 commit 8cfb28a
18 files changed
Lines changed: 2288 additions & 48 deletions
File tree
- docs
- src/agent
- mcp
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
304 | 304 | | |
305 | 305 | | |
306 | 306 | | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
307 | 366 | | |
308 | 367 | | |
309 | 368 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
323 | 323 | | |
324 | 324 | | |
325 | 325 | | |
326 | | - | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
327 | 329 | | |
328 | 330 | | |
329 | 331 | | |
330 | 332 | | |
331 | 333 | | |
332 | 334 | | |
333 | | - | |
| 335 | + | |
334 | 336 | | |
335 | 337 | | |
336 | 338 | | |
337 | 339 | | |
338 | 340 | | |
339 | | - | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
340 | 345 | | |
341 | 346 | | |
342 | 347 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
488 | 488 | | |
489 | 489 | | |
490 | 490 | | |
491 | | - | |
| 491 | + | |
492 | 492 | | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
493 | 496 | | |
494 | | - | |
| 497 | + | |
495 | 498 | | |
496 | 499 | | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
497 | 513 | | |
498 | 514 | | |
499 | 515 | | |
| |||
0 commit comments