A generic CLI that wraps npx skills add and converts installed skills to slash commands — plus event hooks (session_start, user_prompt_submit, and more) that auto-trigger skills on any coding agent.
Works with any skills repo: adlc-team-skills, mattpocock/skills, addyosmani/agent-skills, obra/superpowers, or your own.
npx skills add installs SKILL.md files to an agent's skills directory, but does not generate slash command files or wire event hooks. Many coding agents only support commands (not skills), and auto-triggering skills at lifecycle points (session start, prompt submit) requires per-agent native hook configuration. This CLI fills both gaps.
# Install skills + generate commands + wire events (if .events.json present)
npx adlc-skills-cli add tikalk/adlc-team-skills -a opencode
# Works with any skills repo — events auto-skip if no .events.json
npx adlc-skills-cli add mattpocock/skills -a claude-code --no-events
npx adlc-skills-cli add addyosmani/agent-skills -a opencode -a cursoradlc-skills-cli add <source> -a <agent>
│
├─ 1. npx skills add <source> -a <npx_agent> ← installs SKILL.md files
│
├─ 2. Discover installed skills ← reads SKILL.md frontmatter
│
├─ 3. Generate command files ← slash commands (/name)
│ .opencode/commands/<name>.md inline: embeds skill body
│ wrapper: references skill
│
└─ 4. Wire events (if .events.json in source) ← lifecycle hooks
.agents/dispatcher.mjs generic dispatcher (shipped)
.opencode/plugin/adlc-skills-events.ts agent-native hook config
| Command | Description |
|---|---|
add <source> -a <agent> |
Install skills via npx skills add + generate commands + wire events |
upgrade [-a <agent>] |
Re-generate commands from currently-installed skills (header-matched overwrite) |
remove [-a <agent>] |
Remove generated commands + event configs (marker-matched only) |
status [-a <agent>] |
Show what's installed per agent + dispatcher + event status |
agents |
List supported agents, directories, formats, event support |
| Flag | Description |
|---|---|
-a <agent> |
Target agent (repeatable). Run agents to list. |
-g, --global |
Install to user directory instead of project |
--no-events |
Skip event config generation |
--prefix <str> |
Namespace command filenames (e.g., adlc.team-setup.md) |
--mode <mode> |
inline (default, embeds full skill body) or wrapper (references skill by name). |
--skill <name> |
Install/generate for one skill only (use '*' for all) |
--copy |
Copy files instead of symlinking (passthrough to npx skills) |
-y, --yes |
Skip confirmation prompts |
23 agents across 3 command formats. 9 agents support event hooks.
| Agent | Commands dir | Format | Events |
|---|---|---|---|
| opencode | .opencode/commands/ |
markdown | yes |
| claude-code | .claude/commands/ |
markdown | yes |
| cursor | .cursor/commands/ |
markdown | yes |
| github-copilot | .github/prompts/ |
markdown | yes |
| codex | ~/.codex/prompts/ |
markdown | yes |
| devin | .devin/commands/ |
markdown | yes |
| qwen-code | .qwen/commands/ |
markdown | yes |
| gemini-cli | .gemini/commands/ |
toml | yes |
| tabnine-cli | .tabnine/agent/commands/ |
toml | yes |
| amp | .agents/commands/ |
markdown | no |
| goose | .goose/recipes/ |
yaml | no |
| ...and 12 more |
Run adlc-skills-cli agents for the full list.
Embeds the full SKILL.md body in the command file — self-contained, works on any agent even without skill support:
---
description: Clone, scaffold, or configure a team AI directives repository
---
<!-- generated by adlc-skills-cli; source: tikalk/adlc-team-skills — do not edit -->
Base directory for this skill: /project/.agents/skills/team-setup
Relative paths in this skill are relative to this base directory.
---
# Team Setup
[full skill body...]
$ARGUMENTSThin command that references the installed skill — requires the agent to have skill support:
---
description: Clone, scaffold, or configure a team AI directives repository
---
<!-- generated by adlc-skills-cli; source: tikalk/adlc-team-skills — do not edit -->
Invoke the `team-setup` skill.
$ARGUMENTSUser-invoked skills (disable-model-invocation: true in frontmatter) are meant to be triggered explicitly, not auto-invoked by the model. On wrapper-default agents (those with a skill tool), the command body strategy is chosen per agent via user_invoked_mode:
user_invoked_mode |
Used by | Command body | Why |
|---|---|---|---|
"wrapper" |
opencode |
Invoke the \team-setup` skill.` + summary |
opencode ignores disable-model-invocation — the skill stays in <available_skills> and loads via the skill tool. The wrapper command tells the model to invoke it, and it can. |
"execution" (default) |
claude-code, cursor, copilot, codex |
Full body inlined, framed as imperative steps | These agents respect disable-model-invocation — the skill is hidden from the model, so skill({name}) would fail. Inlining the body is the only working path. |
For opencode, /team-setup expands to the wrapper text, the model sees team-setup in <available_skills>, and calls skill({ name: "team-setup" }) — the skill loads via the tool with full progressive disclosure (base dir + sampled files, invocation visible in traces).
# One-off (no install needed)
npx adlc-skills-cli add tikalk/adlc-team-skills -a opencode
# Install as global binary → adlc-skills-cli available everywhere
npm install -g adlc-skills-cli
adlc-skills-cli add tikalk/adlc-team-skills -a opencodeFor agents with native hook support (9 agents), the CLI wires event hooks that auto-trigger skills at lifecycle points — replacing the "model must invoke the skill" soft directive with deterministic, runtime-enforced context injection.
Events are auto-enabled when:
- The agent supports events (9 agents above)
- The source repo declares a
.events.jsonmanifest --no-eventsis not set
Skills repos declare events at the repo root:
{
"events": {
"session_start": [
{ "skill": "team-boot", "description": "Bootstrap session with team context", "timeout": 60 }
],
"user_prompt_submit": [
{ "skill": "team-discover", "description": "Fetch relevant context for the current prompt", "timeout": 30 }
]
}
}Repos without .events.json (e.g., mattpocock/skills) simply get commands only — events are skipped silently.
A generic dispatcher (.agents/dispatcher.mjs) is shipped to the project. When a native hook fires, it calls the dispatcher with (event, skill, skills_dir, timeout). The dispatcher resolves the skill and executes it via one of two paths:
| Path | When | How | Best for |
|---|---|---|---|
| Script (spec-kit model) | Skill has scripts: in frontmatter |
Runs the script (sh/ps/py variant) → stdout | Deterministic logic (file assembly, keyword matching) |
| Body (superpowers model) | No scripts: block |
Outputs the skill's markdown body → stdout | Orientation/instruction skills (pure LLM prompts) |
Both paths share the stdout → context injection pipeline: whatever the dispatcher outputs to stdout is captured by the agent's native hook and injected as session context.
# Skill with a script (deterministic path)
---
name: team-boot
scripts:
sh: scripts/boot.sh
---# Skill without a script (body path — default)
---
name: using-superpowers
description: Orientation skill injected at session start
---| Event | Fires when | Body path? | Script path? |
|---|---|---|---|
session_start |
Agent session begins | yes | yes |
user_prompt_submit |
User sends a prompt (payload via stdin) | yes | yes |
pre_tool_use |
Before a tool call | no | yes |
post_tool_use |
After a tool call | no | yes |
session_end |
Session ends | no | yes |
stop |
Agent stops | no | yes |
The CLI generates agent-native hook configs that call the dispatcher. Each agent's native event names and config format are handled automatically:
| Agent | Config file | Format | Timeout unit |
|---|---|---|---|
| opencode | .opencode/plugin/adlc-skills-events.ts |
TS plugin | seconds |
| claude-code | .claude/settings.json (merged) |
JSON nested | seconds |
| cursor | .cursor/hooks.json (merged) |
JSON nested | seconds |
| github-copilot | .github/hooks/adlc-skills.json |
JSON (bash+powershell) | seconds |
| codex | .codex/config.toml (merged) |
TOML | seconds |
| gemini-cli | .gemini/settings.json (merged) |
JSON nested | milliseconds |
| qwen-code | .qwen/settings.json (merged) |
JSON nested | milliseconds |
| devin | .devin/hooks.v1.json (merged) |
JSON root-nested | seconds |
| tabnine-cli | .tabnine/agent/settings.json (merged) |
JSON nested | milliseconds |
Not every agent injects a hook's plain-text stdout as model context. The dispatcher wraps its stdout in the JSON envelope each agent's hook protocol requires (passed as a 5th argv arg), per this matrix:
| Agent | session_start injection |
user_prompt_submit injection |
Mechanism |
|---|---|---|---|
| claude-code | ✅ plain stdout | ✅ plain stdout | none needed |
| codex | ✅ plain stdout | ✅ plain stdout | none needed |
| gemini-cli | ✅ JSON envelope | ✅ JSON envelope | {"hookSpecificOutput":{"additionalContext":...}} |
| tabnine-cli | ✅ JSON envelope | ✅ JSON envelope | same |
| qwen-code | ✅ JSON envelope | ✅ JSON envelope | same |
| devin | ✅ JSON envelope | ✅ JSON envelope | same |
| github-copilot | ✅ JSON envelope | ⛔ impossible | {"additionalContext":...} (top-level); userPromptSubmitted output is not processed |
| cursor | ✅ JSON envelope | ⛔ impossible | {"additional_context":...} (snake_case); beforeSubmitPrompt has no context field |
| opencode | ✅ system.transform |
✅ chat.message |
plugin pushes into hook outputs (no stdout) |
For strict-JSON agents (gemini-cli, tabnine-cli, qwen-code, devin), plain stdout on non-injectable events would become user-facing noise or a hook parse error — there the dispatcher is invoked with suppress and emits nothing. On cursor, everything except sessionStart is suppressed for the same reason.
Codex trust review: Codex skips non-managed hooks until you review and trust them. After installing, run
/hooksin Codex once and trust the generated entries (trust is recorded against the hook hash, so upgrades that change the entries need re-trusting). The repo's.codex/layer must also be trusted. Hooks are enabled by default otherwise —[features] hooks = falseinconfig.tomlis the kill switch.
opencode compatibility: the generated TS plugin is verified against opencode ≥ 1.18. opencode's
Part.idmust start withprt(itsIdentifierbrand) and hooks must never throw — a schema error or rethrown exception in a plugin crashes the whole session. The plugin derives the injected part's id from the last existing part at runtime (so it inherits opencode's brand even if the prefix changes), falling back toprt_, and wraps every hook body in try/catch that logs instead of rethrowing. If you upgrade opencode, re-runadlc-skills-cli upgrade -a opencodeto regenerate the plugin.
- Idempotent merge: re-install never duplicates hook entries (marker-based dedup)
- Surgical teardown:
removestrips only our entries, preserves user hooks - JSONC preservation: malformed JSON aborts, never resets user content
- Safe-destination validation: rejects symlink redirects outside project root
- Shell-safe argv:
execFileSyncwith argv arrays, no shell injection - stdin payload forwarding:
user_prompt_submitreceives the user's prompt
Every generated file includes a <!-- generated by adlc-skills-cli --> header. Event hook entries carry a _adlc_skills_cli: true marker (JSON) or adlc_skills_marker = true (TOML). This enables:
removeto safely delete only our files (user-created commands/hooks are never touched)upgradeto overwrite our files while skipping user-modified ones (header removed = user-owned)
No manifest database or state file needed.
Upgrading from
adlc-agents-cli(v0.3.0 or earlier): The CLI and generated artifacts were renamed in v0.4.0. If you installed skills/events withadlc-agents-cli, runnpx adlc-agents-cli removebefore installing withnpx adlc-skills-cli add.
# Run tests
npm test
# Run the CLI locally
node bin/adlc-skills-cli.mjs agents
node bin/adlc-skills-cli.mjs help
node bin/adlc-skills-cli.mjs status -a opencodeZero runtime dependencies. Requires Node.js >= 18.
MIT