Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions COPY.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,16 @@ section above, then delete it from this list.
is "Skilld is a curated registry of agent skills written by real people in the GitHub repos
you already depend on." Both are true and they are not the same sentence. Decide which is
canonical for a one-sentence slot, or record what each is for.

### npm package Skills

If the published package includes the Skill, show NPM first and select it by default.
Run is the second tab. Install follows it. Otherwise, keep Run as the default.
Offer pnpm and skills-npm setup within NPM. Highlight commands with rangi.
Use npm's official wordmark for NPM and pnpm's official colour symbol beside pnpm.
Show skills-npm as text. State each tool's minimum version and setup changes.
Explain that pnpm approval covers all package Skills and later package versions.
The CLI feature list links #234 as the required change until a supporting release has a version.
Describe ownership: doctor recognizes pnpm-managed Skills; pnpm controls their links and updates.
Selecting NPM hides the run block and unrelated setup links. Run and Install show their own content.
Install choices use text labels with Terminal, Claude, ChatGPT, and npm icons.
6 changes: 6 additions & 0 deletions app/assets/logos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Package logos

- `npm.svg`: [npm's official red wordmark](https://github.com/npm/logos/blob/master/npm%20logo/npm-logo-red.svg).
- `pnpm.svg` and `pnpm-light.svg`: [pnpm's official symbols](https://pnpm.io/logos).

These files preserve the source artwork and colours.
9 changes: 9 additions & 0 deletions app/assets/logos/npm.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions app/assets/logos/pnpm-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions app/assets/logos/pnpm.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 8 additions & 3 deletions app/components/CopyText.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
<script setup lang="ts">
import { highlightCodeBody } from '#shared/highlight'
/**
* One line of text to copy, such as a prompt or a URL, with a copy button.
* Commands use `CommandChip` instead, which records the copy.
*/
const { text, label } = defineProps<{
const { text, label, language = 'text' } = defineProps<{
text: string
language?: string
/** What the button copies, read by screen readers. */
label: string
}>()

const html = computed(() => highlightCodeBody(text, language))

const { copy, copied } = useClipboard({ copiedDuring: 2000, legacy: true })
const copyError = ref('')
const textRef = ref<HTMLElement>()
Expand All @@ -30,8 +34,9 @@ async function copyText(): Promise<void> {
<div class="flex items-start gap-2">
<code
ref="textRef"
class="min-w-0 flex-1 rounded-lg border border-default bg-muted px-3 py-2.5 font-mono text-xs leading-relaxed text-default [overflow-wrap:anywhere] whitespace-pre-wrap"
>{{ text }}</code>
class="shiki min-w-0 flex-1 rounded-lg border border-default bg-muted px-3 py-2.5 font-mono text-xs leading-relaxed text-default [overflow-wrap:anywhere] whitespace-pre-wrap"
v-html="html"
/>
<UButton
type="button"
:icon="copied ? 'i-lucide-check' : 'i-lucide-copy'"
Expand Down
9 changes: 9 additions & 0 deletions layers/marketing/app/pages/cli/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,15 @@ const codeClass = 'font-mono text-xs text-default'
No runtime to install, and it starts in under a millisecond. A curl or PowerShell install upgrades itself from signed releases. An npm install runs the same executable, and npm handles its upgrades.
</dd>
</div>
<div>
<dt class="flex items-center gap-2 text-base font-medium">
<UIcon name="i-simple-icons-pnpm" aria-hidden="true" class="size-4" />
pnpm compatibility
</dt>
<dd class="mt-2 text-sm leading-relaxed text-muted">
CLI builds containing <a href="https://github.com/skilld-dev/skilld/pull/234" target="_blank" rel="noopener" class="underline">#234</a> recognize pnpm-managed Skills in <code :class="codeClass">doctor</code>. pnpm keeps control of their links and updates.
</dd>
</div>
<div>
<dt class="text-base font-medium">
No telemetry
Expand Down
1 change: 1 addition & 0 deletions layers/marketing/app/utils/package-skill-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export function packageSkillGuide(setup: PackageSkillSetup) {
`Read ${packageSkillSource}.`,
`Draft a Skill for ${setup.package}, published through ${config.label}, using this repository.`,
`Read ${config.manifest}, the public API, and the current official documentation.`,
...(setup.ecosystem === 'npm' ? ['Save each Skill in skills/<name>/SKILL.md beside package.json, so pnpm can discover it.', 'Include the Skill and its linked files in the published tarball. Read https://pnpm.io/agent-skills for pnpm approval and linking.'] : []),
'Show the draft for review before replacing existing Skills.',
].join('\n\n'),
}
Expand Down
35 changes: 33 additions & 2 deletions layers/marketing/content/learn/author-npm-package-skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ relatedPages:
- path: /skills
title: Browse skills
createdAt: 2026-05-13
updatedAt: 2026-10-05
updatedAt: 2026-10-10
---

## 1. Draft your Skill
Expand All @@ -18,6 +18,8 @@ updatedAt: 2026-10-05
Point your agent at the directory containing your package's `package.json`.
For a monorepo, use the individual package directory.
Ask it to save the draft in `skills/your-skill/SKILL.md`.
Keep `skills/` beside that package's `package.json`.
[pnpm 12.11 and newer](https://pnpm.io/agent-skills) discover Skills one level deep at `skills/<name>/SKILL.md`.

Describe the tasks your package handles and the mistakes agents should avoid.
Check public exports, TypeScript types, supported runtimes, and version limits.
Expand Down Expand Up @@ -52,7 +54,36 @@ See the [npm pack reference](https://docs.npmjs.com/cli/v11/commands/npm-pack).

Commit the reviewed Skill and publish through your normal release process.
Link to its repository directory from your package README.
Installing the npm package does not automatically install the Skill into an agent.
Keep the Skill link visible so developers can choose how to use it.

Developers using pnpm 12.11 or newer can approve Skills from direct dependencies with `pnpm approve`.
pnpm records the choice in `pnpm-workspace.yaml`:

```yaml
permissions:
your-package:
skills: true
```

Replace `your-package` with the published package name, including its scope.
Merge this entry into existing permissions.
Approval covers every Skill in the package and every later version.
Ask developers to read the Skills before approving.

pnpm links approved Skills into existing Agent directories at the workspace root.
Developers can choose directories with `skills.dirs` in `pnpm-workspace.yaml`:

```yaml
skills:
dirs:
- .agents/skills
```

Merge this setting into the existing workspace file, then run `pnpm install`.
For `your-package`, the example Skill appears at `.agents/skills/pnpm-your-package-your-skill/SKILL.md`.
A scoped package replaces `/` with `+` in the link name.
pnpm owns these links and removes them when the package or approval disappears.
Global installs, `pnpm dlx`, and `pnpm deploy` do not link Skills.
See [pnpm's approval and linking rules](https://pnpm.io/agent-skills).

When your API changes, review the Skill alongside your package documentation.
14 changes: 14 additions & 0 deletions layers/registry/app/components/SkillDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { BehaviorReading, BehaviorReadingsResponse } from '#shared/behavior
import type { RunCheckFlagsResponse } from '#shared/run-check-flags'
import type { TrendingAward } from '#shared/trending-award'
import type { SkillDemoView } from '../../server/utils/skill-demos'
import type { PnpmPackage } from '../../shared/pnpm-package'
import type { ZipState } from '../utils/skill-zip'
import type { SkillBehavior } from './_SkillBehaviors.vue'
import { formatTimeAgo } from '@vueuse/core'
Expand Down Expand Up @@ -328,6 +329,14 @@ const liveSkillFetch = useAsyncData<LiveSkill | null>(
},
)

// Both responsive command blocks share one optional package check after hydration.
const packageFetch = useAsyncData<PnpmPackage | null>(
() => `skill-package:${slug.value}`,
() => $fetch<PnpmPackage>(`/api/skill-package/${slug.value}`),
{ server: false, default: () => null },
)
const pnpmPackage = computed(() => packageFetch.data.value?._tag === 'Found' ? packageFetch.data.value : null)

// Keep complete SSR for search and link previews. Client navigation renders
// the loading state immediately while these independent requests run together.
// A deep link renders its file on the server, so the link shows that file
Expand Down Expand Up @@ -596,6 +605,7 @@ const installCmd = computed(() => {
})

const commandMode = ref<'run' | 'install'>('run')
const npmSelected = ref(false)
const commandCopyError = ref('')

const { copy, copied } = useInstallCopy(
Expand Down Expand Up @@ -1779,8 +1789,10 @@ useHead(computed(() => ({
>
<SkillCommandPanel
v-model="commandMode"
v-model:npm-selected="npmSelected"
:run-url="runUrl"
:install-command="installCmd"
:published="pnpmPackage"
:run-copied="copied"
:install-copied="installCopied"
:copy-error="commandCopyError"
Expand All @@ -1801,11 +1813,13 @@ useHead(computed(() => ({
<section v-if="!data.sourceGone" aria-label="Run or install">
<SkillCommandPanel
v-model="commandMode"
v-model:npm-selected="npmSelected"
layout="stacked"
:zip-name="data.skillPath ? `${data.name}.zip` : undefined"
:zip-state="zipState"
:run-url="runUrl"
:install-command="installCmd"
:published="pnpmPackage"
:run-copied="copied"
:install-copied="installCopied"
:copy-error="commandCopyError"
Expand Down
Loading
Loading