From bfbb0dece8345845d96aaa74268c88b916540978 Mon Sep 17 00:00:00 2001 From: Test Date: Sat, 5 Sep 2026 23:21:06 +0200 Subject: [PATCH 1/3] feat(config): reorder providers, add Oh My Pi and Grok CLI (#617) Put Agents first in the install provider picker, followed by Claude Code, Pi, OpenCode, and Codex; the remaining providers keep their order. Add `omp` (Oh My Pi) and `grok` (Grok CLI) as built-in providers. Reordering the defaults alone would not reach anyone who already has a saved config, so `mergeWithDefaults` now canonicalizes the provider order on load. User-added providers keep their anchor: each stays just after whichever built-in preceded it in the saved config. The Agents row carries a note that most harnesses support it, except Claude Code. The note leads the hint so it survives truncation on a narrow terminal. First-time setup now pre-checks Agents and Claude Code. Claude-Session: https://claude.ai/code/session_01L7wEboaD5rvJst7KeKiEzq --- README.md | 12 ++-- src/config.test.ts | 81 +++++++++++++++++++++++---- src/config.ts | 69 +++++++++++++++++------ src/installer-link.ts | 11 +++- src/installer.test.ts | 31 ++++++---- website-src/src/pages/DocsPage.jsx | 10 +++- website-src/src/pages/LandingPage.jsx | 2 +- 7 files changed, 167 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 2590629..35a7173 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ | Pain | Without `asm` | With `asm` | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| Scattered installs | Same skill copied into `~/.claude/skills/`, `~/.codex/skills/`, `~/.cursor/rules/` — different versions, no single view | One `asm list` across all 19 providers and scopes | +| Scattered installs | Same skill copied into `~/.claude/skills/`, `~/.codex/skills/`, `~/.cursor/rules/` — different versions, no single view | One `asm list` across all 21 providers and scopes | | No inventory | `ls` through hidden dirs; no idea what is installed, duplicated, or outdated | `asm search`, `asm inspect`, `asm stats`, `asm audit` | | Invisible context cost | Every installed skill's description is resident in the agent's prompt on every message, whether or not it ever fires | `asm stats --tokens`, `asm audit residency` | | Install just to read once | Installing is the only way to get a skill in front of an agent — and it stays resident forever afterwards | `asm get ` prints the body and installs nothing | @@ -281,7 +281,7 @@ MIT licensed. No accounts or paywalls. Yes. Commands return structured JSON (`--json`), accept non-interactive flags (`--yes`, `--machine`), and map to discrete actions an agent can chain — list inventory, search catalog, install, audit, uninstall. **Which agents are supported?** -19 providers: Claude Code, Codex, OpenClaw, Cursor, Windsurf, Cline, Roo Code, Continue, GitHub Copilot, Aider, OpenCode, Zed, Augment, Amp, Gemini CLI, Google Antigravity, Pi, Hermes, and a generic Agents provider. Disable any via `asm config edit`. +21 providers: a generic Agents provider (supported by most harnesses except Claude Code), Claude Code, Pi, OpenCode, Codex, Oh My Pi, Grok CLI, Hermes, OpenClaw, Cursor, GitHub Copilot, Windsurf, Google Antigravity, Gemini CLI, Cline, Roo Code, Continue, Aider, Zed, Augment, and Amp. Disable any via `asm config edit`. **What about the TUI?** Run `asm` with no args for an optional local browser. The CLI is the primary interface for scripts, CI, and agent tool calls. @@ -509,14 +509,16 @@ asm install github:anthropics/skills --all
Supported agent tools -19 built-in providers, all enabled by default. Disable via `asm config edit`. +21 built-in providers, all enabled by default. Disable via `asm config edit`. | Tool | Global Path | Project Path | | ------------------ | --------------------------------- | ----------------------- | +| Agents (generic) | `~/.agents/skills/` | `.agents/skills/` | | Claude Code | `~/.claude/skills/` | `.claude/skills/` | | Codex | `~/.codex/skills/` | `.codex/skills/` | +| Oh My Pi | `~/.omp/agent/skills/` | `.omp/skills/` | +| Grok CLI | `~/.grok/skills/` | `.grok/skills/` | | OpenClaw | `~/.openclaw/skills/` | `.openclaw/skills/` | -| Agents (generic) | `~/.agents/skills/` | `.agents/skills/` | | Cursor | `~/.cursor/rules/` | `.cursor/rules/` | | Windsurf | `~/.windsurf/rules/` | `.windsurf/rules/` | | Cline | `~/Documents/Cline/Rules/` | `.clinerules/` | @@ -964,7 +966,7 @@ On first run, config is created at `~/.config/agent-skill-manager/config.json`: } ``` -- All 19 providers are enabled by default +- All 21 providers are enabled by default - Set `"enabled": false` to skip a provider you don't use - Add arbitrary directories via `customPaths` - Manage via `asm config show|path|reset|edit` or press `c` in the TUI diff --git a/src/config.test.ts b/src/config.test.ts index 3ef94d4..cd4cc4d 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -26,20 +26,22 @@ describe("getDefaultConfig", () => { it("returns 19 default providers", () => { const config = getDefaultConfig(); - expect(config.providers).toHaveLength(19); + expect(config.providers).toHaveLength(21); }); - it("includes all 19 default providers in priority order", () => { + it("includes all 21 default providers in priority order", () => { const config = getDefaultConfig(); const names = config.providers.map((p) => p.name); expect(names).toEqual([ + "agents", "claude", - "codex", - "opencode", "pi", + "opencode", + "codex", + "omp", + "grok", "hermes", "openclaw", - "agents", "cursor", "copilot", "windsurf", @@ -55,9 +57,9 @@ describe("getDefaultConfig", () => { ]); }); - it("all 19 providers are enabled by default", () => { + it("all 21 providers are enabled by default", () => { const config = getDefaultConfig(); - expect(config.providers).toHaveLength(19); + expect(config.providers).toHaveLength(21); expect(config.providers.every((p) => p.enabled)).toBe(true); }); @@ -76,7 +78,7 @@ describe("getDefaultConfig", () => { const a = getDefaultConfig(); const b = getDefaultConfig(); a.providers[0].name = "mutated"; - expect(b.providers[0].name).toBe("claude"); + expect(b.providers[0].name).toBe("agents"); }); }); @@ -202,7 +204,7 @@ describe("config backup on corruption", () => { // Should return defaults expect(config.version).toBe(1); - expect(config.providers).toHaveLength(19); + expect(config.providers).toHaveLength(21); // Should have created backup const backup = await readFile(backupPath, "utf-8"); @@ -446,13 +448,68 @@ describe("mergeWithDefaults priority-order insertion", () => { const config = await loadConfig(); const piIdx = config.providers.findIndex((p) => p.name === "pi"); + const claudeIdx = config.providers.findIndex((p) => p.name === "claude"); const opencodeIdx = config.providers.findIndex( (p) => p.name === "opencode", ); - const hermesIdx = config.providers.findIndex((p) => p.name === "hermes"); - expect(piIdx).toBeGreaterThan(opencodeIdx); - expect(piIdx).toBeLessThan(hermesIdx); + // Post-#617 canonical slot: pi sits between claude and opencode. + expect(piIdx).toBeGreaterThan(claudeIdx); + expect(piIdx).toBeLessThan(opencodeIdx); + }); + + it("canonicalizes a saved config that predates the #617 reorder", async () => { + await mkdir(dirname(configPath), { recursive: true }); + // Pre-#617 saved order: claude first, agents seventh, no omp/grok. + const legacyNames = [ + "claude", + "codex", + "opencode", + "pi", + "hermes", + "openclaw", + "agents", + "cursor", + "copilot", + "windsurf", + "antigravity", + "gemini", + "cline", + "roocode", + "continue", + "aider", + "zed", + "augment", + "amp", + ]; + const partial = { + version: 1, + providers: legacyNames.map((name) => ({ + name, + label: name, + global: `~/.${name}/skills`, + project: `.${name}/skills`, + enabled: true, + })), + preferences: { defaultScope: "both", defaultSort: "name" }, + }; + await writeFile(configPath, JSON.stringify(partial), "utf-8"); + + const config = await loadConfig(); + const names = config.providers.map((p) => p.name); + + expect(names.slice(0, 7)).toEqual([ + "agents", + "claude", + "pi", + "opencode", + "codex", + "omp", + "grok", + ]); + expect(names).toHaveLength(21); + // Nothing the user had is dropped by the reorder. + for (const name of legacyNames) expect(names).toContain(name); }); it("preserves user-added custom providers in place when adding new defaults", async () => { diff --git a/src/config.ts b/src/config.ts index bcfb0b5..5a872b7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -24,7 +24,14 @@ export function getConfigDir(): string { } const DEFAULT_PROVIDERS: ProviderConfig[] = [ - // ── Priority providers (ordered by user preference) ── + // ── Priority providers (ordered by user preference, #617) ── + { + name: "agents", + label: "Agents", + global: "~/.agents/skills", + project: ".agents/skills", + enabled: true, + }, { name: "claude", label: "Claude Code", @@ -33,10 +40,10 @@ const DEFAULT_PROVIDERS: ProviderConfig[] = [ enabled: true, }, { - name: "codex", - label: "Codex", - global: "~/.codex/skills", - project: ".codex/skills", + name: "pi", + label: "Pi", + global: "~/.pi/skills", + project: ".pi/skills", enabled: true, }, { @@ -47,12 +54,27 @@ const DEFAULT_PROVIDERS: ProviderConfig[] = [ enabled: true, }, { - name: "pi", - label: "Pi", - global: "~/.pi/skills", - project: ".pi/skills", + name: "codex", + label: "Codex", + global: "~/.codex/skills", + project: ".codex/skills", + enabled: true, + }, + { + name: "omp", + label: "Oh My Pi", + global: "~/.omp/agent/skills", + project: ".omp/skills", enabled: true, }, + { + name: "grok", + label: "Grok CLI", + global: "~/.grok/skills", + project: ".grok/skills", + enabled: true, + }, + // ── Additional providers ── { name: "hermes", label: "Hermes", @@ -67,14 +89,6 @@ const DEFAULT_PROVIDERS: ProviderConfig[] = [ project: ".openclaw/skills", enabled: true, }, - // ── Additional providers ── - { - name: "agents", - label: "Agents", - global: "~/.agents/skills", - project: ".agents/skills", - enabled: true, - }, { name: "cursor", label: "Cursor", @@ -264,6 +278,27 @@ function mergeWithDefaults(config: Partial): AppConfig { existingNames.add(defaultProvider.name); } + // Canonicalize order so DEFAULT_PROVIDERS ordering wins for built-ins (#617). + // Reordering the defaults must reach users who already have a saved config, + // not just fresh installs. A user-added provider keeps its anchor: it stays + // just after whichever built-in preceded it in the saved config. + const defaultRank = new Map( + defaults.providers.map((p, i) => [p.name, i] as const), + ); + const sortKey = new Map(); + let lastRank = -1; + for (const p of providers) { + const rank = defaultRank.get(p.name); + if (rank === undefined) { + sortKey.set(p, lastRank + 0.5); + } else { + lastRank = rank; + sortKey.set(p, rank); + } + } + // Stable sort keeps consecutive user-added providers in their saved order. + providers.sort((a, b) => sortKey.get(a)! - sortKey.get(b)!); + return { version: config.version ?? defaults.version, providers, diff --git a/src/installer-link.ts b/src/installer-link.ts index 82306ea..4ef4752 100644 --- a/src/installer-link.ts +++ b/src/installer-link.ts @@ -211,8 +211,15 @@ export async function resolveProvider( const pickerItems = config.providers.map((p) => ({ label: `${p.label} (${p.name})`, - hint: p.global, - checked: savedSet ? savedSet.has(p.name) : p.name === "agents", + // The note leads so it survives hint truncation on narrow terminals (#617). + hint: + p.name === "agents" + ? `most harnesses except Claude Code — ${p.global}` + : p.global, + // First-time setup pre-checks Agents + Claude Code (#617). + checked: savedSet + ? savedSet.has(p.name) + : p.name === "agents" || p.name === "claude", })); const selectedIndices = await checkboxPicker({ items: pickerItems }); diff --git a/src/installer.test.ts b/src/installer.test.ts index f200aeb..431e30b 100644 --- a/src/installer.test.ts +++ b/src/installer.test.ts @@ -940,7 +940,7 @@ describe("resolveProvider", () => { ); }); - test("interactive picker: no saved tools defaults agents to checked", async () => { + test("interactive picker: no saved tools defaults agents + claude to checked", async () => { const agents: ProviderConfig = { name: "agents", label: "Agents", @@ -961,9 +961,18 @@ describe("resolveProvider", () => { await resolveProvider(config, null, true); expect(capturedItems).toHaveLength(3); - expect((capturedItems[0] as { checked: boolean }).checked).toBe(false); + // claude + agents pre-checked on first-time setup (#617); codex is not. + expect((capturedItems[0] as { checked: boolean }).checked).toBe(true); expect((capturedItems[1] as { checked: boolean }).checked).toBe(true); expect((capturedItems[2] as { checked: boolean }).checked).toBe(false); + // The agents row leads with the harness-support note so it survives + // hint truncation on a narrow terminal (#617). + expect((capturedItems[1] as { hint: string }).hint).toBe( + "most harnesses except Claude Code — ~/.agents/skills", + ); + expect((capturedItems[0] as { hint: string }).hint).toBe( + "~/.claude/skills", + ); }); test("interactive picker: saved tools override default checked state", async () => { @@ -1026,7 +1035,7 @@ describe("resolveProvider", () => { expect(savedNames).toEqual(["claude", "agents"]); }); - test("interactive picker: empty saved tools array falls back to agents default", async () => { + test("interactive picker: empty saved tools array falls back to agents + claude default", async () => { let capturedItems: unknown[] = []; const agents: ProviderConfig = { name: "agents", @@ -1050,19 +1059,19 @@ describe("resolveProvider", () => { preferences: { defaultScope: "both", defaultSort: "name", - selectedTools: [], // empty array — should fall back to agents default + selectedTools: [], // empty array — falls back to the first-run default }, }; await resolveProvider(config, null, true); expect(capturedItems).toHaveLength(3); - // Falls back to agents default - expect((capturedItems[0] as { checked: boolean }).checked).toBe(false); + // Falls back to the agents + claude first-run default (#617) + expect((capturedItems[0] as { checked: boolean }).checked).toBe(true); expect((capturedItems[1] as { checked: boolean }).checked).toBe(true); expect((capturedItems[2] as { checked: boolean }).checked).toBe(false); }); - test("interactive picker: all items default to deselected when no agents and no saved", async () => { + test("interactive picker: only claude is pre-checked when no agents and no saved", async () => { let capturedItems: unknown[] = []; const pickerFn = vi.fn((opts: { items: unknown[] }) => { capturedItems = opts.items; @@ -1076,12 +1085,13 @@ describe("resolveProvider", () => { await resolveProvider(config, null, true); expect(capturedItems).toHaveLength(3); - expect((capturedItems[0] as { checked: boolean }).checked).toBe(false); + // No agents provider present, so only claude is pre-checked (#617). + expect((capturedItems[0] as { checked: boolean }).checked).toBe(true); expect((capturedItems[1] as { checked: boolean }).checked).toBe(false); expect((capturedItems[2] as { checked: boolean }).checked).toBe(false); }); - test("interactive picker: all providers disabled shows picker with nothing pre-checked", async () => { + test("interactive picker: all providers disabled still shows the claude pre-check", async () => { const allDisabled: ProviderConfig[] = [ { ...claude, enabled: false }, { ...codex, enabled: false }, @@ -1099,7 +1109,8 @@ describe("resolveProvider", () => { const result = await resolveProvider(config, null, true); expect(result.provider.name).toBe("claude"); expect(capturedItems).toHaveLength(2); - expect((capturedItems[0] as { checked: boolean }).checked).toBe(false); + // Disabled providers still show; claude keeps its first-run pre-check. + expect((capturedItems[0] as { checked: boolean }).checked).toBe(true); expect((capturedItems[1] as { checked: boolean }).checked).toBe(false); }); diff --git a/website-src/src/pages/DocsPage.jsx b/website-src/src/pages/DocsPage.jsx index 34892e6..7404a90 100644 --- a/website-src/src/pages/DocsPage.jsx +++ b/website-src/src/pages/DocsPage.jsx @@ -796,7 +796,7 @@ metadata:

Supported Agent Tools

- asm ships with 19 built-in providers, all enabled by + asm ships with 21 built-in providers, all enabled by default:

@@ -853,6 +853,12 @@ metadata: p=".opencode/skills/" /> + + Config file is created at{" "} ~/.config/agent-skill-manager/config.json on first run - with 19 default providers. + with 21 default providers.

diff --git a/website-src/src/pages/LandingPage.jsx b/website-src/src/pages/LandingPage.jsx index 26660c9..8633193 100644 --- a/website-src/src/pages/LandingPage.jsx +++ b/website-src/src/pages/LandingPage.jsx @@ -376,7 +376,7 @@ function Solution() { { icon: "05", head: "Works with every major agent", - body: "19 providers built in: Claude Code, Codex, Cursor, Windsurf, Cline, Roo, Continue, Copilot, Aider, Zed, Gemini CLI, and more. Add custom ones in seconds.", + body: "21 providers built in: Agents, Claude Code, Pi, OpenCode, Codex, Oh My Pi, Grok CLI, Cursor, Windsurf, Cline, Gemini CLI, and more. Add custom ones in seconds.", }, { icon: "06", From 40ed06acbd2e03fd026f01447c561f2a2c23827d Mon Sep 17 00:00:00 2001 From: Test Date: Sat, 5 Sep 2026 23:28:51 +0200 Subject: [PATCH 2/3] fix(install): never pre-check a disabled provider (#617) Review feedback: the first-run pre-check ticked Agents and Claude Code regardless of `enabled`, and the picker does not filter disabled rows out of the selection, so a provider the user had turned off would install on Enter and be persisted to `selectedTools`. Gate the pre-check on `p.enabled` and restore the assertion that pins it. Claude-Session: https://claude.ai/code/session_01L7wEboaD5rvJst7KeKiEzq --- src/config.test.ts | 2 +- src/config.ts | 5 +++-- src/installer-link.ts | 5 +++-- src/installer.test.ts | 7 ++++--- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index cd4cc4d..ef0ce00 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -24,7 +24,7 @@ describe("getDefaultConfig", () => { expect(config.version).toBe(1); }); - it("returns 19 default providers", () => { + it("returns 21 default providers", () => { const config = getDefaultConfig(); expect(config.providers).toHaveLength(21); }); diff --git a/src/config.ts b/src/config.ts index 5a872b7..7badc1c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -280,8 +280,9 @@ function mergeWithDefaults(config: Partial): AppConfig { // Canonicalize order so DEFAULT_PROVIDERS ordering wins for built-ins (#617). // Reordering the defaults must reach users who already have a saved config, - // not just fresh installs. A user-added provider keeps its anchor: it stays - // just after whichever built-in preceded it in the saved config. + // not just fresh installs. A user-added provider sorts just after the + // built-in that preceded it at this point, which is after the new defaults + // the insertion loop above already slotted in ahead of it. const defaultRank = new Map( defaults.providers.map((p, i) => [p.name, i] as const), ); diff --git a/src/installer-link.ts b/src/installer-link.ts index 4ef4752..7052603 100644 --- a/src/installer-link.ts +++ b/src/installer-link.ts @@ -216,10 +216,11 @@ export async function resolveProvider( p.name === "agents" ? `most harnesses except Claude Code — ${p.global}` : p.global, - // First-time setup pre-checks Agents + Claude Code (#617). + // First-time setup pre-checks Agents + Claude Code (#617), but never a + // provider the user has disabled — a pre-checked row installs on Enter. checked: savedSet ? savedSet.has(p.name) - : p.name === "agents" || p.name === "claude", + : p.enabled && (p.name === "agents" || p.name === "claude"), })); const selectedIndices = await checkboxPicker({ items: pickerItems }); diff --git a/src/installer.test.ts b/src/installer.test.ts index 431e30b..f704a0b 100644 --- a/src/installer.test.ts +++ b/src/installer.test.ts @@ -1091,7 +1091,7 @@ describe("resolveProvider", () => { expect((capturedItems[2] as { checked: boolean }).checked).toBe(false); }); - test("interactive picker: all providers disabled still shows the claude pre-check", async () => { + test("interactive picker: all providers disabled shows picker with nothing pre-checked", async () => { const allDisabled: ProviderConfig[] = [ { ...claude, enabled: false }, { ...codex, enabled: false }, @@ -1109,8 +1109,9 @@ describe("resolveProvider", () => { const result = await resolveProvider(config, null, true); expect(result.provider.name).toBe("claude"); expect(capturedItems).toHaveLength(2); - // Disabled providers still show; claude keeps its first-run pre-check. - expect((capturedItems[0] as { checked: boolean }).checked).toBe(true); + // Disabled providers still show, but the first-run pre-check never + // ticks one the user turned off (#617). + expect((capturedItems[0] as { checked: boolean }).checked).toBe(false); expect((capturedItems[1] as { checked: boolean }).checked).toBe(false); }); From d411447769dca55d28e8b7e719dc106611a2fdb9 Mon Sep 17 00:00:00 2001 From: Test Date: Sat, 5 Sep 2026 23:48:38 +0200 Subject: [PATCH 3/3] fix(docs): sync provider count, Agents hint, and tools table (#617) Bump landing/README counts to 21, lead the Agents picker hint with the Claude Code exception so it survives narrow-terminal truncation, and list docs/README providers in picker order. --- README.md | 50 ++++++------ src/installer-link.ts | 5 +- src/installer.test.ts | 4 +- website-src/src/__tests__/docs-page.test.jsx | 18 +++++ website-src/src/pages/DocsPage.jsx | 80 ++++++++++---------- website-src/src/pages/LandingPage.jsx | 2 +- 6 files changed, 91 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 35a7173..54f9fd8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ # CLI to install and manage agent skills -**agent-skill-manager** (`asm`) is a scriptable CLI built for AI agents and automation — install, search, audit, and organize skills across Claude Code, Codex, Cursor, and 16 more tools. Every command supports `--json` and `--yes` for non-interactive use. An optional TUI (`asm`) is available for local browsing. +**agent-skill-manager** (`asm`) is a scriptable CLI built for AI agents and automation — install, search, audit, and organize skills across Claude Code, Codex, Cursor, and 18 more tools. Every command supports `--json` and `--yes` for non-interactive use. An optional TUI (`asm`) is available for local browsing. [**Get Started**](#getting-started) · [**Browse 4,300+ skills**](https://luongnv.com/asm/#/skills) · [**Full docs**](#documentation) @@ -38,7 +38,7 @@ | | | | ------------------- | ------------------------------------------------------------------------------ | | **Catalog** | 4,672 skills from 58 repos — [browse online](https://luongnv.com/asm/#/skills) | -| **Providers** | 19 agents (Claude, Codex, Cursor, Windsurf, Copilot, …) | +| **Providers** | 21 providers (Agents, Claude Code, Pi, OpenCode, Codex, …) | | **Agent-ready CLI** | `--json`, `--yes`, `--machine` on list, search, install, audit, eval | | **Security** | Pre-install scan for shell exec, network access, credential exposure | | **License** | MIT — no accounts, no telemetry | @@ -511,29 +511,29 @@ asm install github:anthropics/skills --all 21 built-in providers, all enabled by default. Disable via `asm config edit`. -| Tool | Global Path | Project Path | -| ------------------ | --------------------------------- | ----------------------- | -| Agents (generic) | `~/.agents/skills/` | `.agents/skills/` | -| Claude Code | `~/.claude/skills/` | `.claude/skills/` | -| Codex | `~/.codex/skills/` | `.codex/skills/` | -| Oh My Pi | `~/.omp/agent/skills/` | `.omp/skills/` | -| Grok CLI | `~/.grok/skills/` | `.grok/skills/` | -| OpenClaw | `~/.openclaw/skills/` | `.openclaw/skills/` | -| Cursor | `~/.cursor/rules/` | `.cursor/rules/` | -| Windsurf | `~/.windsurf/rules/` | `.windsurf/rules/` | -| Cline | `~/Documents/Cline/Rules/` | `.clinerules/` | -| Roo Code | `~/.roo/rules/` | `.roo/rules/` | -| Continue | `~/.continue/rules/` | `.continue/rules/` | -| GitHub Copilot | `~/.github/instructions/` | `.github/instructions/` | -| Aider | `~/.aider/skills/` | `.aider/skills/` | -| OpenCode | `~/.config/opencode/skills/` | `.opencode/skills/` | -| Zed | `~/.config/zed/prompt_overrides/` | `.zed/rules/` | -| Augment | `~/.augment/rules/` | `.augment/rules/` | -| Amp | `~/.amp/skills/` | `.amp/skills/` | -| Gemini CLI | `~/.gemini/skills/` | `.gemini/skills/` | -| Google Antigravity | `~/.antigravity/skills/` | `.antigravity/skills/` | -| Pi | `~/.pi/skills/` | `.pi/skills/` | -| Hermes | `~/.hermes/skills/` | `.hermes/skills/` | +| Tool | Global Path | Project Path | +| ------------------------------------------ | --------------------------------- | ----------------------- | +| Agents (most harnesses except Claude Code) | `~/.agents/skills/` | `.agents/skills/` | +| Claude Code | `~/.claude/skills/` | `.claude/skills/` | +| Pi | `~/.pi/skills/` | `.pi/skills/` | +| OpenCode | `~/.config/opencode/skills/` | `.opencode/skills/` | +| Codex | `~/.codex/skills/` | `.codex/skills/` | +| Oh My Pi | `~/.omp/agent/skills/` | `.omp/skills/` | +| Grok CLI | `~/.grok/skills/` | `.grok/skills/` | +| Hermes | `~/.hermes/skills/` | `.hermes/skills/` | +| OpenClaw | `~/.openclaw/skills/` | `.openclaw/skills/` | +| Cursor | `~/.cursor/rules/` | `.cursor/rules/` | +| GitHub Copilot | `~/.github/instructions/` | `.github/instructions/` | +| Windsurf | `~/.windsurf/rules/` | `.windsurf/rules/` | +| Google Antigravity | `~/.antigravity/skills/` | `.antigravity/skills/` | +| Gemini CLI | `~/.gemini/skills/` | `.gemini/skills/` | +| Cline | `~/Documents/Cline/Rules/` | `.clinerules/` | +| Roo Code | `~/.roo/rules/` | `.roo/rules/` | +| Continue | `~/.continue/rules/` | `.continue/rules/` | +| Aider | `~/.aider/skills/` | `.aider/skills/` | +| Zed | `~/.config/zed/prompt_overrides/` | `.zed/rules/` | +| Augment | `~/.augment/rules/` | `.augment/rules/` | +| Amp | `~/.amp/skills/` | `.amp/skills/` | Add custom providers in config. diff --git a/src/installer-link.ts b/src/installer-link.ts index 7052603..f86549d 100644 --- a/src/installer-link.ts +++ b/src/installer-link.ts @@ -211,10 +211,11 @@ export async function resolveProvider( const pickerItems = config.providers.map((p) => ({ label: `${p.label} (${p.name})`, - // The note leads so it survives hint truncation on narrow terminals (#617). + // The Claude Code exception leads so it survives hint truncation on + // narrow terminals (#617) — a 15–25 col hint still names the exception. hint: p.name === "agents" - ? `most harnesses except Claude Code — ${p.global}` + ? `except Claude Code; most harnesses — ${p.global}` : p.global, // First-time setup pre-checks Agents + Claude Code (#617), but never a // provider the user has disabled — a pre-checked row installs on Enter. diff --git a/src/installer.test.ts b/src/installer.test.ts index f704a0b..582bdf3 100644 --- a/src/installer.test.ts +++ b/src/installer.test.ts @@ -965,10 +965,10 @@ describe("resolveProvider", () => { expect((capturedItems[0] as { checked: boolean }).checked).toBe(true); expect((capturedItems[1] as { checked: boolean }).checked).toBe(true); expect((capturedItems[2] as { checked: boolean }).checked).toBe(false); - // The agents row leads with the harness-support note so it survives + // The agents row leads with the Claude Code exception so it survives // hint truncation on a narrow terminal (#617). expect((capturedItems[1] as { hint: string }).hint).toBe( - "most harnesses except Claude Code — ~/.agents/skills", + "except Claude Code; most harnesses — ~/.agents/skills", ); expect((capturedItems[0] as { hint: string }).hint).toBe( "~/.claude/skills", diff --git a/website-src/src/__tests__/docs-page.test.jsx b/website-src/src/__tests__/docs-page.test.jsx index e526789..ecd0b7e 100644 --- a/website-src/src/__tests__/docs-page.test.jsx +++ b/website-src/src/__tests__/docs-page.test.jsx @@ -118,4 +118,22 @@ describe("DocsPage", () => { expect(Element.prototype.scrollIntoView).toHaveBeenCalled(); expect(window.location.hash).toMatch(/s=eval/); }); + + it("lists providers in picker order with the Agents harness note", () => { + renderDocs(); + const section = document.getElementById("tools"); + const names = [...section.querySelectorAll("tbody tr td:first-child")].map( + (td) => td.textContent, + ); + expect(names.slice(0, 7)).toEqual([ + "Agents — most harnesses except Claude Code", + "Claude Code", + "Pi", + "OpenCode", + "Codex", + "Oh My Pi", + "Grok CLI", + ]); + expect(names).toHaveLength(21); + }); }); diff --git a/website-src/src/pages/DocsPage.jsx b/website-src/src/pages/DocsPage.jsx index 7404a90..91e1610 100644 --- a/website-src/src/pages/DocsPage.jsx +++ b/website-src/src/pages/DocsPage.jsx @@ -808,28 +808,61 @@ metadata: + + + + + + + - + + - - - - - - - -
@@ -1733,10 +1734,13 @@ function FieldRow({ name, req, desc }) { ); } -function ToolRow({ tool, g, p }) { +function ToolRow({ tool, g, p, note }) { return ( - {tool} + + {tool} + {note ? ` — ${note}` : null} + {g} diff --git a/website-src/src/pages/LandingPage.jsx b/website-src/src/pages/LandingPage.jsx index 8633193..494892c 100644 --- a/website-src/src/pages/LandingPage.jsx +++ b/website-src/src/pages/LandingPage.jsx @@ -10,7 +10,7 @@ import { prefersReducedMotion } from "../lib/motion.js"; const REPO_URL = "https://github.com/luongnv89/asm"; const NPM_CMD = "npm install -g agent-skill-manager"; -const PROVIDER_COUNT = 19; +const PROVIDER_COUNT = 21; /** * Marketing landing page (route `/`). The catalog lives at `/skills`.