Install
$ agentstack add skill-nklisch-skilltap-citty ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
citty — CLI Framework Reference
citty is a lightweight, TypeScript-first CLI builder from the UnJS ecosystem. It uses native Node.js util.parseArgs under the hood. Zero dependencies.
Install: bun add citty
Import: import { defineCommand, runMain, runCommand, createMain, parseArgs, renderUsage, showUsage } from "citty"
defineCommand
The primary API. Defines a command with metadata, arguments, subcommands, and lifecycle hooks.
import { defineCommand, runMain } from "citty"
const main = defineCommand({
meta: {
name: "skilltap",
version: "0.1.0",
description: "Install agent skills from any git host",
},
args: {
verbose: {
type: "boolean",
description: "Enable verbose output",
alias: "v",
},
},
subCommands: {
install: () => import("./commands/install").then(m => m.default),
remove: () => import("./commands/remove").then(m => m.default),
list: () => import("./commands/list").then(m => m.default),
// Nested subcommands via inline define
tap: defineCommand({
meta: { name: "tap", description: "Manage taps" },
subCommands: {
add: () => import("./commands/tap/add").then(m => m.default),
remove: () => import("./commands/tap/remove").then(m => m.default),
},
}),
},
run({ args }) {
// Runs if no subcommand matched
showUsage(this)
},
})
runMain(main)
Argument Definitions
Each key in args defines an argument or option. Properties:
| Property | Type | Description | |----------|------|-------------| | type | "positional" \| "boolean" | Positional arg or boolean flag. Omit for string options. | | description | string | Help text shown in --help | | alias | string \| string[] | Short alias(es), e.g. "v" for -v | | default | string \| boolean | Default value | | required | boolean | Whether the argument is required | | valueHint | string | Placeholder in help, e.g. "file" shows --output |
Argument types
Positional — matched by order of definition:
args: {
source: {
type: "positional",
description: "Git URL, tap name, or local path",
required: true,
},
}
// Usage: skilltap install https://example.com/repo
// args.source === "https://example.com/repo"
Boolean flags — true when present, false when absent:
args: {
project: {
type: "boolean",
description: "Install to project scope",
default: false,
},
yes: {
type: "boolean",
alias: "y",
description: "Auto-accept prompts",
},
}
// Usage: skilltap install foo --project -y
// args.project === true, args.yes === true
String options — omit type (default behavior):
args: {
also: {
description: "Also symlink to agent directory",
alias: "a",
valueHint: "agent",
},
ref: {
description: "Branch or tag to install",
valueHint: "ref",
},
}
// Usage: skilltap install foo --also claude-code --ref v1.2.0
// args.also === "claude-code", args.ref === "v1.2.0"
Parsing behavior
- Kebab-to-camel:
--dry-run→args.dryRun - Negation:
--no-color→args.color === false(for booleans withdefault: true) - Equals syntax:
--output=result.jsonworks - Rest args:
args._contains ALL positional args (including the first one captured as a named positional). Do NOT use[args.source, ...args._]— that duplicates the first positional. Useargs._ as string[]directly to get all values.
Subcommands
Subcommands can be static objects, lazy functions, or async imports:
subCommands: {
// Static
list: listCommand,
// Lazy (loaded only when invoked)
install: () => installCommand,
// Async import (code-split)
update: () => import("./commands/update").then(m => m.default),
// Nested (subcommands can have their own subcommands)
config: defineCommand({
meta: { name: "config" },
subCommands: {
"agent-mode": agentModeCommand,
},
}),
}
Lifecycle Hooks
Three hooks run in order: setup → run → cleanup.
defineCommand({
args: { /* ... */ },
async setup({ args }) {
// Pre-processing, validation, initialization
// Runs before run() or subcommand dispatch
},
async run({ args }) {
// Main command logic
// Only runs if no subcommand was matched
},
async cleanup({ args }) {
// Runs after run() completes (even on error)
// Close connections, clean temp files, etc.
},
})
The CommandContext passed to each hook:
interface CommandContext {
rawArgs: string[] // Raw argv array
args: ParsedArgs // Typed parsed arguments
cmd: CommandDef // The command definition
subCommand?: CommandDef // Matched subcommand (if any)
}
runMain(command, options?)
Entry point for CLI apps. Handles:
- Parsing
process.argv --help/-hauto-handling (prints usage, exits)--version/-Vauto-handling (prints version, exits)- Subcommand dispatch
- Error handling with formatted output +
process.exit(1)
runMain(main)
// Custom argv:
runMain(main, { rawArgs: ["install", "foo", "--project"] })
runCommand(command, options)
Lower-level — runs a command without process.exit behavior. Good for programmatic use and testing.
await runCommand(installCommand, {
rawArgs: ["https://example.com/repo", "--project"],
})
Other Exports
| Function | Purpose | |----------|---------| | createMain(cmd) | Returns (rawArgs?) => Promise wrapper around runMain | | parseArgs(rawArgs, argsDef) | Low-level arg parser. Returns typed ParsedArgs | | renderUsage(cmd) | Returns formatted usage/help string | | showUsage(cmd) | Prints usage to stdout |
Type Inference
citty infers parsed arg types from definitions:
const cmd = defineCommand({
args: {
name: { type: "positional", required: true },
count: { default: "5" }, // string (has default)
verbose: { type: "boolean" }, // boolean
},
run({ args }) {
args.name // string
args.count // string
args.verbose // boolean
},
})
Built-in Flags (automatic)
| Flag | Alias | Behavior | |------|-------|----------| | --help | -h | Prints formatted usage, exits 0 | | --version | -V | Prints meta.version, exits 0 |
Pattern: skilltap Command Structure
This is the pattern used in this project for packages/cli/src/commands/:
// packages/cli/src/commands/install.ts
import { defineCommand } from "citty"
import { installSkill } from "@skilltap/core"
export default defineCommand({
meta: {
name: "install",
description: "Install a skill from a URL, tap name, or local path",
},
args: {
source: {
type: "positional",
description: "Git URL, github:owner/repo, tap skill name, or local path",
required: true,
},
project: {
type: "boolean",
description: "Install to .agents/skills/ in current project",
default: false,
},
also: {
description: "Create symlink in agent-specific directory",
valueHint: "agent",
},
ref: {
description: "Branch or tag to install",
valueHint: "ref",
},
"skip-scan": {
type: "boolean",
description: "Skip security scanning",
default: false,
},
yes: {
type: "boolean",
alias: "y",
description: "Auto-accept prompts",
default: false,
},
strict: {
type: "boolean",
description: "Abort on any security warning",
},
semantic: {
type: "boolean",
description: "Force semantic scan",
default: false,
},
},
async run({ args }) {
// Command implementation
},
})
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: nklisch
- Source: nklisch/skilltap
- License: MIT
- Homepage: https://skilltap.dev/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.