Commit 19e4fbf
feat(tui): install skills via typed query in the /skills list (#1005)
* feat(tui): install skills via typed query in the /skills list
The `/skills` list didn't offer a discoverable way to install from a
GitHub repo / URL / absolute path — the only entry point was `ctrl+i`,
whose wire byte (0x09) collides with Tab on default terminals, so many
users couldn't trigger it.
Surface a synthetic "Install <query>" row at the top of the list when the
filter matches an installable shape, so pressing Enter opens the install
dialog prefilled with the typed text. Backed by a shared
`classifyInstallSource` classifier used by both the list preview and
`installSkillDirect` — an earlier `q.includes("/")` check drifted from
the installer and offered the row for skill-search queries like
`dbt/snowflake` that the installer then rejected as "Path not found".
- Add module-scope `classifyInstallSource` (recognises github URLs, clean
`owner/repo` shorthand, POSIX absolute paths, Windows drive-letter
paths — rejects short strings, sub-paths, `~`, relatives).
- Route the sentinel through `onSelect` to `showInstall`, forward the
captured filter text as `initialValue` on `DialogSkillInstall` and
`DialogSkillCreate`.
- Filter the sentinel out of `onMove` so highlighting the synthetic row
doesn't set `currentSkill` to the sentinel string (would trip
`ctrl+a`'s action picker into a degenerate lookup miss).
- Harden `showActions` to bail on the sentinel or a lookup miss as
belt-and-braces.
- Build options via a spread instead of `Array.prototype.unshift` so the
memo stays pure (Solid dev-mode double-eval would otherwise
double-prepend).
- Slim the fork-feature-guards test to a minimal presence check
(sentinel, classifier symbol, sentinel-route in `onSelect`, prefill
plumbing) — behavioural coverage of the classifier moves to a new
`test/altimate/skill-install-classifier.test.ts` unit suite.
Verified: `bun run typecheck` clean; new classifier suite 10/10;
fork-feature-guards 18/18; `test/session` 731/731.
* fix(tui): address OpenCodeReview findings on skills-install classifier
Two medium findings from the automated review pass on this PR:
1. `installSkillDirect` used the raw `normalized` input when building a
clone URL, but `classifyInstallSource` tolerates a trailing `.git`
suffix / whitespace / trailing dots. An input like `owner/repo.git`
passed the classifier's `owner-repo` check and then produced
`https://github.com/owner/repo.git.git`. Extracted the trim/strip
logic into `normalizeInstallSource` and use it in both the classifier
and the URL builder so the two can't disagree on shape.
2. The `options` `createMemo` read the `filter()` signal, so every
keystroke re-executed the full `list.map` — including
`detectToolReferences` (regex parse per skill). Split into a
`baseOptions` memo that depends only on `skills()` and a derived
`options` memo that composes the synthetic Install row on top. Now a
filter change only re-checks `classifyInstallSource(q)` and prepends
one item; `detectToolReferences` only re-runs when the underlying
skills list changes.
Also adds a `normalizeInstallSource` unit-test suite (4 cases pinning
the `.git` / trailing-dot / whitespace behaviour) alongside the existing
classifier tests.
Verified: `bun run typecheck` clean; classifier suite 14/14 (54 expects);
fork-feature-guards 18/18 (61 expects).
* fix(tui): route owner/repo shorthand by classifier kind, not http-prefix probe
Second review pass on this PR — one live-reproduced bug + two auxiliary
finds:
1. `installSkillDirect` branched clone-URL construction on
`cleaned.startsWith("http")`, which misfired for owners whose name
literally begins with `http` (`httpie/httpie`,
`http-party/http-server`, `httpwg/http-extensions`): they classify as
`owner-repo` yet passed the prefix probe, so the installer tried to
`git clone httpie/httpie` and failed. Fix: branch on the already-
computed `kind` from `classifyInstallSource`. As a bonus, the
`github-url` branch now preserves the trailing `.git` the user typed
(matters for self-hosted git servers that require it), while the
`owner-repo` branch continues to normalize.
2. Merge-drop guard in `fork-feature-guards.test.ts` didn't cover the
memo block that actually creates the synthetic Install row —
deleting the block still passed all guards (verified by mutation).
Add a proximity-anchored `classifyInstallSource(q) → value:
INSTALL_ACTION_VALUE` match; the 300-char bound is deliberately
tight so a real deletion collapses it to zero and fails loudly.
3. Classifier comments and one test title claimed `dbt/snowflake` was
rejected, but the classifier returns `owner-repo` for any clean
two-segment string — intentional, since skill names can't contain a
slash. Correct the docstrings and pin the actual behaviour with
explicit `expect(...dbt/snowflake).toBe("owner-repo")` and
`expect(...httpie/httpie).toBe("owner-repo")` assertions.
Not addressed here: the pre-existing `/skills` slash-command collision
between the upstream `prompt.skills` and this plugin's
`altimate.skill.list`. Already on `main`, already tracked as a comment
on the Jira ticket, out of scope for this PR.
Verified: `bun run typecheck` clean; classifier + fork-guard suites
34/34 (100 expects); `test/session` 731/731 (1636 expects).
---------
Co-authored-by: Haider <haider@altimate.ai>1 parent 8c9a9a8 commit 19e4fbf
3 files changed
Lines changed: 279 additions & 23 deletions
File tree
- packages/opencode
- src/plugin/tui/altimate
- test
- altimate
- upstream
Lines changed: 139 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 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 | + | |
37 | 75 | | |
38 | 76 | | |
39 | 77 | | |
| |||
143 | 181 | | |
144 | 182 | | |
145 | 183 | | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
152 | 205 | | |
153 | 206 | | |
154 | 207 | | |
| |||
295 | 348 | | |
296 | 349 | | |
297 | 350 | | |
298 | | - | |
| 351 | + | |
299 | 352 | | |
300 | 353 | | |
301 | 354 | | |
302 | 355 | | |
303 | 356 | | |
304 | 357 | | |
305 | 358 | | |
| 359 | + | |
306 | 360 | | |
307 | 361 | | |
308 | 362 | | |
| |||
350 | 404 | | |
351 | 405 | | |
352 | 406 | | |
353 | | - | |
| 407 | + | |
354 | 408 | | |
355 | 409 | | |
356 | 410 | | |
| |||
359 | 413 | | |
360 | 414 | | |
361 | 415 | | |
| 416 | + | |
362 | 417 | | |
363 | 418 | | |
364 | 419 | | |
| |||
552 | 607 | | |
553 | 608 | | |
554 | 609 | | |
555 | | - | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
556 | 622 | | |
557 | 623 | | |
558 | 624 | | |
| |||
570 | 636 | | |
571 | 637 | | |
572 | 638 | | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
573 | 663 | | |
574 | 664 | | |
575 | | - | |
576 | | - | |
| 665 | + | |
| 666 | + | |
577 | 667 | | |
578 | | - | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
579 | 675 | | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
580 | 682 | | |
581 | 683 | | |
582 | 684 | | |
| |||
589 | 691 | | |
590 | 692 | | |
591 | 693 | | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
592 | 700 | | |
593 | 701 | | |
594 | 702 | | |
595 | 703 | | |
596 | 704 | | |
597 | 705 | | |
598 | | - | |
599 | | - | |
600 | | - | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
601 | 712 | | |
602 | 713 | | |
603 | | - | |
| 714 | + | |
604 | 715 | | |
605 | | - | |
| 716 | + | |
606 | 717 | | |
607 | 718 | | |
608 | 719 | | |
609 | 720 | | |
610 | | - | |
| 721 | + | |
611 | 722 | | |
612 | | - | |
| 723 | + | |
613 | 724 | | |
614 | 725 | | |
615 | 726 | | |
616 | 727 | | |
617 | 728 | | |
618 | | - | |
619 | | - | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
620 | 736 | | |
621 | 737 | | |
622 | 738 | | |
| |||
649 | 765 | | |
650 | 766 | | |
651 | 767 | | |
652 | | - | |
| 768 | + | |
653 | 769 | | |
654 | 770 | | |
655 | 771 | | |
| |||
659 | 775 | | |
660 | 776 | | |
661 | 777 | | |
662 | | - | |
| 778 | + | |
663 | 779 | | |
664 | 780 | | |
665 | 781 | | |
| |||
Lines changed: 115 additions & 0 deletions
| 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 | + | |
0 commit comments