agent-skill-manager 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +5 -5
- package/README.md +41 -12
- package/RELEASE_NOTES.md +14 -23
- package/SECURITY.md +1 -1
- package/bin/agent-skill-manager.ts +12 -0
- package/docs/ARCHITECTURE.md +7 -7
- package/docs/CHANGELOG.md +25 -1
- package/docs/DEPLOYMENT.md +4 -4
- package/docs/DEVELOPMENT.md +2 -2
- package/install.sh +226 -0
- package/package.json +7 -6
- package/src/cli.ts +488 -0
- package/src/config.ts +1 -1
- package/src/formatter.ts +92 -0
- package/src/uninstaller.ts +2 -1
- package/src/views/dashboard.ts +1 -1
- package/bin/skill-manager.ts +0 -46
package/CODE_OF_CONDUCT.md
CHANGED
|
@@ -46,7 +46,7 @@ an individual is officially representing the community in public spaces.
|
|
|
46
46
|
|
|
47
47
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
48
48
|
reported to the project maintainers via
|
|
49
|
-
[GitHub Issues](https://github.com/luongnv89/skill-manager/issues).
|
|
49
|
+
[GitHub Issues](https://github.com/luongnv89/agent-skill-manager/issues).
|
|
50
50
|
|
|
51
51
|
All complaints will be reviewed and investigated promptly and fairly.
|
|
52
52
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Contributing to skill-manager
|
|
1
|
+
# Contributing to agent-skill-manager
|
|
2
2
|
|
|
3
3
|
Thanks for your interest in contributing! This guide will help you get started.
|
|
4
4
|
|
|
@@ -12,8 +12,8 @@ Thanks for your interest in contributing! This guide will help you get started.
|
|
|
12
12
|
### Development Setup
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
git clone https://github.com/luongnv89/skill-manager.git
|
|
16
|
-
cd skill-manager
|
|
15
|
+
git clone https://github.com/luongnv89/agent-skill-manager.git
|
|
16
|
+
cd agent-skill-manager
|
|
17
17
|
bun install
|
|
18
18
|
bun run start # Launch the TUI
|
|
19
19
|
```
|
|
@@ -29,7 +29,7 @@ bun run typecheck # Type-check without emitting
|
|
|
29
29
|
|
|
30
30
|
### Reporting Bugs
|
|
31
31
|
|
|
32
|
-
Open a [bug report](https://github.com/luongnv89/skill-manager/issues/new?template=bug_report.md) with:
|
|
32
|
+
Open a [bug report](https://github.com/luongnv89/agent-skill-manager/issues/new?template=bug_report.md) with:
|
|
33
33
|
|
|
34
34
|
- Steps to reproduce
|
|
35
35
|
- Expected vs. actual behavior
|
|
@@ -37,7 +37,7 @@ Open a [bug report](https://github.com/luongnv89/skill-manager/issues/new?templa
|
|
|
37
37
|
|
|
38
38
|
### Suggesting Features
|
|
39
39
|
|
|
40
|
-
Open a [feature request](https://github.com/luongnv89/skill-manager/issues/new?template=feature_request.md) describing the problem and your proposed solution.
|
|
40
|
+
Open a [feature request](https://github.com/luongnv89/agent-skill-manager/issues/new?template=feature_request.md) describing the problem and your proposed solution.
|
|
41
41
|
|
|
42
42
|
### Submitting Code
|
|
43
43
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<picture>
|
|
3
3
|
<source media="(prefers-color-scheme: dark)" srcset="assets/logo/logo-full.svg" />
|
|
4
4
|
<source media="(prefers-color-scheme: light)" srcset="assets/logo/logo-black.svg" />
|
|
5
|
-
<img src="assets/logo/logo-full.svg" alt="skill-manager" width="
|
|
5
|
+
<img src="assets/logo/logo-full.svg" alt="agent-skill-manager" width="560" />
|
|
6
6
|
</picture>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
@@ -25,12 +25,16 @@
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
**skill-manager** is an interactive terminal UI for managing installed skills across AI coding agents — [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenClaw](https://github.com/openclaw), and more. Built with [OpenTUI](https://github.com/nicholasgasior/opentui) and [Bun](https://bun.sh).
|
|
28
|
+
**agent-skill-manager** is an interactive terminal UI for managing installed skills across AI coding agents — [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenClaw](https://github.com/openclaw), and more. Built with [OpenTUI](https://github.com/nicholasgasior/opentui) and [Bun](https://bun.sh).
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<img src="assets/screenshots/agent-skill-manager.png" alt="agent-skill-manager TUI dashboard" width="800" />
|
|
32
|
+
</p>
|
|
29
33
|
|
|
30
34
|
## Features
|
|
31
35
|
|
|
32
36
|
- **Multi-agent support** — Manage skills for Claude Code, Codex, OpenClaw, and custom agent tools from one TUI
|
|
33
|
-
- **Configurable providers** — Define which agent tool directories to scan via `~/.config/skill-manager/config.json`
|
|
37
|
+
- **Configurable providers** — Define which agent tool directories to scan via `~/.config/agent-skill-manager/config.json`
|
|
34
38
|
- **Global & project scopes** — Filter skills by global (`~/.<tool>/skills/`) or project-level (`./<tool>/skills/`)
|
|
35
39
|
- **Real-time search** — Filter skills by name, description, or provider
|
|
36
40
|
- **Sort** — By name, version, or location
|
|
@@ -40,27 +44,52 @@
|
|
|
40
44
|
|
|
41
45
|
## Install
|
|
42
46
|
|
|
47
|
+
### Quick Install (one command)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -sSL https://raw.githubusercontent.com/luongnv89/agent-skill-manager/main/install.sh | bash
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or with wget:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
wget -qO- https://raw.githubusercontent.com/luongnv89/agent-skill-manager/main/install.sh | bash
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This will automatically install [Bun](https://bun.sh) (if not already installed) and then install `agent-skill-manager` globally.
|
|
60
|
+
|
|
61
|
+
### Manual Install
|
|
62
|
+
|
|
43
63
|
**Prerequisites:** [Bun](https://bun.sh) >= 1.0.0
|
|
44
64
|
|
|
45
65
|
```bash
|
|
46
66
|
bun install -g agent-skill-manager
|
|
47
67
|
```
|
|
48
68
|
|
|
49
|
-
|
|
69
|
+
### From Source
|
|
50
70
|
|
|
51
71
|
```bash
|
|
52
|
-
git clone https://github.com/luongnv89/skill-manager.git
|
|
53
|
-
cd skill-manager
|
|
72
|
+
git clone https://github.com/luongnv89/agent-skill-manager.git
|
|
73
|
+
cd agent-skill-manager
|
|
54
74
|
bun install
|
|
55
75
|
bun run start
|
|
56
76
|
```
|
|
57
77
|
|
|
78
|
+
### Advanced Options
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Download and inspect before running
|
|
82
|
+
curl -sSL https://raw.githubusercontent.com/luongnv89/agent-skill-manager/main/install.sh -o install.sh
|
|
83
|
+
less install.sh # review the script
|
|
84
|
+
bash install.sh
|
|
85
|
+
```
|
|
86
|
+
|
|
58
87
|
## Usage
|
|
59
88
|
|
|
60
89
|
```bash
|
|
61
|
-
skill-manager # Launch the interactive TUI
|
|
62
|
-
skill-manager --help # Show help
|
|
63
|
-
skill-manager --version # Show version
|
|
90
|
+
agent-skill-manager # Launch the interactive TUI
|
|
91
|
+
agent-skill-manager --help # Show help
|
|
92
|
+
agent-skill-manager --version # Show version
|
|
64
93
|
```
|
|
65
94
|
|
|
66
95
|
## Keyboard Shortcuts
|
|
@@ -81,7 +110,7 @@ skill-manager --version # Show version
|
|
|
81
110
|
|
|
82
111
|
## Configuration
|
|
83
112
|
|
|
84
|
-
On first run, a config file is created at `~/.config/skill-manager/config.json` with default providers:
|
|
113
|
+
On first run, a config file is created at `~/.config/agent-skill-manager/config.json` with default providers:
|
|
85
114
|
|
|
86
115
|
```json
|
|
87
116
|
{
|
|
@@ -145,9 +174,9 @@ Additional tools can be added via the config file.
|
|
|
145
174
|
## Project Structure
|
|
146
175
|
|
|
147
176
|
```
|
|
148
|
-
skill-manager/
|
|
177
|
+
agent-skill-manager/
|
|
149
178
|
├── bin/ # CLI entry point
|
|
150
|
-
│ └── skill-manager.ts
|
|
179
|
+
│ └── agent-skill-manager.ts
|
|
151
180
|
├── src/
|
|
152
181
|
│ ├── index.ts # App bootstrap & keyboard handling
|
|
153
182
|
│ ├── config.ts # Config loading & saving
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
|
-
## v1.
|
|
2
|
-
|
|
3
|
-
Initial release of **skill-manager** — the universal skill manager for AI coding agents.
|
|
1
|
+
## v1.1.0 — 2026-03-11
|
|
4
2
|
|
|
5
3
|
### Features
|
|
6
4
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Real-time search and sort (by name, version, location)
|
|
12
|
-
- Detailed skill view with SKILL.md frontmatter metadata
|
|
13
|
-
- Safe uninstall with confirmation dialog and full removal plan
|
|
14
|
-
- In-TUI config editor — toggle providers on/off or open in `$EDITOR`
|
|
15
|
-
- CLI entry point with `--help` and `--version` flags
|
|
16
|
-
- Neon green logo suite and brand kit
|
|
5
|
+
- Add one-command install script for `curl | bash` usage (dee8bb2)
|
|
6
|
+
- Add non-interactive CLI mode with `asm` shorthand command (3904bdc)
|
|
7
|
+
- Rebrand project to agent-skill-manager across all files (944c877)
|
|
8
|
+
- Rename package to agent-skill-manager and add version info to help (5fe6ed3)
|
|
17
9
|
|
|
18
|
-
###
|
|
10
|
+
### Bug Fixes
|
|
19
11
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- 63 unit tests covering config, scanner, uninstaller, and frontmatter modules
|
|
23
|
-
- OSS-ready: LICENSE, CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, issue/PR templates
|
|
12
|
+
- Handle Bun global bin PATH and create asm/agent-skill-manager aliases in installer (268c2bd)
|
|
13
|
+
- Remove external font import from SVGs for GitHub rendering (c75a985)
|
|
24
14
|
|
|
25
|
-
###
|
|
15
|
+
### Other Changes
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
- Rename bin entry point to agent-skill-manager.ts (8ac0ae1)
|
|
18
|
+
- Add .npmignore to exclude unnecessary files from npm package (8339a45)
|
|
19
|
+
- Add TUI screenshot to README and update logo width (339eb04)
|
|
20
|
+
- Remove obsolete RELEASE_NOTES.md and CLI_PLAN.md (ec0d9a9)
|
|
30
21
|
|
|
31
|
-
**Full Changelog**: https://github.com/luongnv89/skill-manager/
|
|
22
|
+
**Full Changelog**: https://github.com/luongnv89/agent-skill-manager/compare/v1.0.0...v1.1.0
|
package/SECURITY.md
CHANGED
|
@@ -13,7 +13,7 @@ We take security vulnerabilities seriously. If you discover a security issue, pl
|
|
|
13
13
|
### How to Report
|
|
14
14
|
|
|
15
15
|
1. **Do NOT** open a public GitHub issue for security vulnerabilities
|
|
16
|
-
2. Open a [private security advisory](https://github.com/luongnv89/skill-manager/security/advisories/new) on GitHub
|
|
16
|
+
2. Open a [private security advisory](https://github.com/luongnv89/agent-skill-manager/security/advisories/new) on GitHub
|
|
17
17
|
3. Include detailed steps to reproduce the vulnerability
|
|
18
18
|
4. Allow up to 48 hours for an initial response
|
|
19
19
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
skill-manager is a terminal UI application that scans, displays, and manages skills installed for various AI coding agents. It follows a simple layered architecture: CLI entry → app bootstrap → core modules → TUI views.
|
|
5
|
+
agent-skill-manager is a terminal UI application that scans, displays, and manages skills installed for various AI coding agents. It follows a simple layered architecture: CLI entry → app bootstrap → core modules → TUI views.
|
|
6
6
|
|
|
7
7
|
## Components
|
|
8
8
|
|
|
9
|
-
### CLI Entry (`bin/skill-manager.ts`)
|
|
9
|
+
### CLI Entry (`bin/agent-skill-manager.ts`)
|
|
10
10
|
|
|
11
11
|
Handles `--help` and `--version` flags, then delegates to the main app entry point.
|
|
12
12
|
|
|
@@ -16,11 +16,11 @@ Initializes the OpenTUI renderer, wires up keyboard handlers, and manages view s
|
|
|
16
16
|
|
|
17
17
|
### Core Modules
|
|
18
18
|
|
|
19
|
-
| Module | Responsibility
|
|
20
|
-
| -------------------- |
|
|
21
|
-
| `src/config.ts` | Load/save config from `~/.config/skill-manager/config.json` |
|
|
22
|
-
| `src/scanner.ts` | Walk provider directories, parse SKILL.md, filter & sort
|
|
23
|
-
| `src/uninstaller.ts` | Build removal plans and execute safe deletions
|
|
19
|
+
| Module | Responsibility |
|
|
20
|
+
| -------------------- | ----------------------------------------------------------------- |
|
|
21
|
+
| `src/config.ts` | Load/save config from `~/.config/agent-skill-manager/config.json` |
|
|
22
|
+
| `src/scanner.ts` | Walk provider directories, parse SKILL.md, filter & sort |
|
|
23
|
+
| `src/uninstaller.ts` | Build removal plans and execute safe deletions |
|
|
24
24
|
|
|
25
25
|
### Views (`src/views/`)
|
|
26
26
|
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -4,13 +4,37 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2026-03-11
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- One-command install script (`curl | bash`) with automatic Bun installation
|
|
12
|
+
- Non-interactive CLI mode with `asm` shorthand command
|
|
13
|
+
- .npmignore to exclude unnecessary files from npm package
|
|
14
|
+
- TUI screenshot in README
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Bun global bin PATH handling and asm/agent-skill-manager alias creation in installer
|
|
19
|
+
- External font import in SVGs for GitHub rendering
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Rebranded project to agent-skill-manager across all files
|
|
24
|
+
- Renamed bin entry point to `agent-skill-manager.ts` to match package name
|
|
25
|
+
- Renamed package to agent-skill-manager with version info in help output
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
- Obsolete CLI_PLAN.md
|
|
30
|
+
|
|
7
31
|
## [1.0.0] - 2025-03-11
|
|
8
32
|
|
|
9
33
|
### Added
|
|
10
34
|
|
|
11
35
|
- Interactive TUI dashboard with OpenTUI
|
|
12
36
|
- Multi-agent support: Claude Code, Codex, OpenClaw, and generic Agents
|
|
13
|
-
- Configurable providers via `~/.config/skill-manager/config.json`
|
|
37
|
+
- Configurable providers via `~/.config/agent-skill-manager/config.json`
|
|
14
38
|
- Global and project scope filtering
|
|
15
39
|
- Real-time search and sort (by name, version, location)
|
|
16
40
|
- Detailed skill view with SKILL.md frontmatter metadata
|
package/docs/DEPLOYMENT.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Publishing to npm (via Bun)
|
|
4
4
|
|
|
5
|
-
skill-manager is distributed as a global CLI package.
|
|
5
|
+
agent-skill-manager is distributed as a global CLI package.
|
|
6
6
|
|
|
7
7
|
### 1. Bump the version
|
|
8
8
|
|
|
9
9
|
Update the version in both files:
|
|
10
10
|
|
|
11
11
|
- `package.json` → `"version"`
|
|
12
|
-
- `bin/skill-manager.ts` → `VERSION` constant
|
|
12
|
+
- `bin/agent-skill-manager.ts` → `VERSION` constant
|
|
13
13
|
|
|
14
14
|
### 2. Build and publish
|
|
15
15
|
|
|
@@ -36,8 +36,8 @@ bun install -g agent-skill-manager
|
|
|
36
36
|
For development or CI environments:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
git clone https://github.com/luongnv89/skill-manager.git
|
|
40
|
-
cd skill-manager
|
|
39
|
+
git clone https://github.com/luongnv89/agent-skill-manager.git
|
|
40
|
+
cd agent-skill-manager
|
|
41
41
|
bun install
|
|
42
42
|
bun run start
|
|
43
43
|
```
|
package/docs/DEVELOPMENT.md
CHANGED
package/install.sh
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# ============================================================================
|
|
5
|
+
# agent-skill-manager Installer
|
|
6
|
+
# The universal skill manager for AI coding agents.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# curl -sSL https://raw.githubusercontent.com/luongnv89/agent-skill-manager/main/install.sh | bash
|
|
10
|
+
# wget -qO- https://raw.githubusercontent.com/luongnv89/agent-skill-manager/main/install.sh | bash
|
|
11
|
+
# ============================================================================
|
|
12
|
+
|
|
13
|
+
TOOL_NAME="agent-skill-manager"
|
|
14
|
+
REPO_OWNER="luongnv89"
|
|
15
|
+
REPO_NAME="agent-skill-manager"
|
|
16
|
+
BUN_MIN_VERSION="1.0.0"
|
|
17
|
+
|
|
18
|
+
# --- Color Output ---
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
GREEN='\033[0;32m'
|
|
21
|
+
YELLOW='\033[1;33m'
|
|
22
|
+
BLUE='\033[0;34m'
|
|
23
|
+
NC='\033[0m'
|
|
24
|
+
|
|
25
|
+
info() { printf "${BLUE}[INFO]${NC} %s\n" "$*"; }
|
|
26
|
+
ok() { printf "${GREEN}[ OK ]${NC} %s\n" "$*"; }
|
|
27
|
+
warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$*"; }
|
|
28
|
+
err() { printf "${RED}[ERR ]${NC} %s\n" "$*" >&2; }
|
|
29
|
+
die() { err "$@"; exit 1; }
|
|
30
|
+
|
|
31
|
+
# --- OS / Arch Detection ---
|
|
32
|
+
detect_os() {
|
|
33
|
+
local os
|
|
34
|
+
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
35
|
+
case "$os" in
|
|
36
|
+
linux*) echo "linux" ;;
|
|
37
|
+
darwin*) echo "macos" ;;
|
|
38
|
+
mingw*|msys*|cygwin*) echo "windows" ;;
|
|
39
|
+
*) die "Unsupported operating system: $os" ;;
|
|
40
|
+
esac
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
detect_arch() {
|
|
44
|
+
local arch
|
|
45
|
+
arch="$(uname -m)"
|
|
46
|
+
case "$arch" in
|
|
47
|
+
x86_64|amd64) echo "x86_64" ;;
|
|
48
|
+
aarch64|arm64) echo "arm64" ;;
|
|
49
|
+
armv7l) echo "armv7" ;;
|
|
50
|
+
*) die "Unsupported architecture: $arch" ;;
|
|
51
|
+
esac
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# --- Version Comparison ---
|
|
55
|
+
# Returns 0 if $1 >= $2 (semver)
|
|
56
|
+
version_gte() {
|
|
57
|
+
local IFS=.
|
|
58
|
+
local i ver1=($1) ver2=($2)
|
|
59
|
+
for ((i=0; i<${#ver2[@]}; i++)); do
|
|
60
|
+
local v1="${ver1[i]:-0}"
|
|
61
|
+
local v2="${ver2[i]:-0}"
|
|
62
|
+
if ((v1 > v2)); then return 0; fi
|
|
63
|
+
if ((v1 < v2)); then return 1; fi
|
|
64
|
+
done
|
|
65
|
+
return 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# --- Bun Detection & Installation ---
|
|
69
|
+
check_bun() {
|
|
70
|
+
if command -v bun &>/dev/null; then
|
|
71
|
+
local bun_version
|
|
72
|
+
bun_version="$(bun --version 2>/dev/null || echo "0.0.0")"
|
|
73
|
+
if version_gte "$bun_version" "$BUN_MIN_VERSION"; then
|
|
74
|
+
ok "Bun $bun_version found (>= $BUN_MIN_VERSION required)"
|
|
75
|
+
return 0
|
|
76
|
+
else
|
|
77
|
+
warn "Bun $bun_version found but >= $BUN_MIN_VERSION is required"
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
else
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
install_bun() {
|
|
86
|
+
info "Installing Bun..."
|
|
87
|
+
if command -v curl &>/dev/null; then
|
|
88
|
+
curl -fsSL https://bun.sh/install | bash
|
|
89
|
+
elif command -v wget &>/dev/null; then
|
|
90
|
+
wget -qO- https://bun.sh/install | bash
|
|
91
|
+
else
|
|
92
|
+
die "Neither curl nor wget found. Please install one of them first."
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Source bun into current shell
|
|
96
|
+
local bun_install="${BUN_INSTALL:-$HOME/.bun}"
|
|
97
|
+
if [ -f "$bun_install/bin/bun" ]; then
|
|
98
|
+
export BUN_INSTALL="$bun_install"
|
|
99
|
+
export PATH="$bun_install/bin:$PATH"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if ! command -v bun &>/dev/null; then
|
|
103
|
+
die "Bun installation completed but 'bun' is not in PATH. Please restart your shell and re-run this script."
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
ok "Bun $(bun --version) installed"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# --- Ensure Bun global bin is in PATH ---
|
|
110
|
+
ensure_bun_in_path() {
|
|
111
|
+
local bun_bin="${BUN_INSTALL:-$HOME/.bun}/bin"
|
|
112
|
+
if [[ ":$PATH:" != *":$bun_bin:"* ]]; then
|
|
113
|
+
export PATH="$bun_bin:$PATH"
|
|
114
|
+
info "Added $bun_bin to PATH for this session"
|
|
115
|
+
fi
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# --- Install agent-skill-manager ---
|
|
119
|
+
install_asm() {
|
|
120
|
+
info "Installing $TOOL_NAME globally via Bun..."
|
|
121
|
+
bun install -g "$TOOL_NAME"
|
|
122
|
+
ok "$TOOL_NAME installed globally"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# --- Create command aliases ---
|
|
126
|
+
create_aliases() {
|
|
127
|
+
local bun_bin="${BUN_INSTALL:-$HOME/.bun}/bin"
|
|
128
|
+
local bin_target=""
|
|
129
|
+
|
|
130
|
+
# Find the actual installed binary (could be skill-manager or agent-skill-manager)
|
|
131
|
+
for name in skill-manager agent-skill-manager; do
|
|
132
|
+
if [ -f "$bun_bin/$name" ] || [ -L "$bun_bin/$name" ]; then
|
|
133
|
+
bin_target="$bun_bin/$name"
|
|
134
|
+
break
|
|
135
|
+
fi
|
|
136
|
+
done
|
|
137
|
+
|
|
138
|
+
if [ -z "$bin_target" ]; then
|
|
139
|
+
warn "Could not find installed binary in $bun_bin"
|
|
140
|
+
return 1
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Create symlinks for all expected command names
|
|
144
|
+
for alias_name in agent-skill-manager asm; do
|
|
145
|
+
local alias_path="$bun_bin/$alias_name"
|
|
146
|
+
if [ ! -f "$alias_path" ] && [ ! -L "$alias_path" ]; then
|
|
147
|
+
ln -s "$bin_target" "$alias_path"
|
|
148
|
+
ok "Created alias: $alias_name"
|
|
149
|
+
fi
|
|
150
|
+
done
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# --- Verification ---
|
|
154
|
+
verify_installation() {
|
|
155
|
+
info "Verifying installation..."
|
|
156
|
+
local found=false
|
|
157
|
+
|
|
158
|
+
for cmd in agent-skill-manager asm skill-manager; do
|
|
159
|
+
if command -v "$cmd" &>/dev/null; then
|
|
160
|
+
ok "$cmd is available"
|
|
161
|
+
found=true
|
|
162
|
+
fi
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
if [ "$found" = false ]; then
|
|
166
|
+
warn "No commands found in PATH"
|
|
167
|
+
warn "Add Bun's global bin to your PATH by adding this to your shell profile:"
|
|
168
|
+
warn " export PATH=\"\$HOME/.bun/bin:\$PATH\""
|
|
169
|
+
return 1
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# --- Entry Point ---
|
|
176
|
+
main() {
|
|
177
|
+
echo ""
|
|
178
|
+
info "============================================"
|
|
179
|
+
info " $TOOL_NAME Installer"
|
|
180
|
+
info "============================================"
|
|
181
|
+
echo ""
|
|
182
|
+
|
|
183
|
+
local os arch
|
|
184
|
+
os="$(detect_os)"
|
|
185
|
+
arch="$(detect_arch)"
|
|
186
|
+
info "OS: $os | Arch: $arch"
|
|
187
|
+
echo ""
|
|
188
|
+
|
|
189
|
+
# Step 1: Ensure Bun is installed
|
|
190
|
+
if ! check_bun; then
|
|
191
|
+
install_bun
|
|
192
|
+
fi
|
|
193
|
+
echo ""
|
|
194
|
+
|
|
195
|
+
# Step 2: Ensure Bun global bin is in PATH
|
|
196
|
+
ensure_bun_in_path
|
|
197
|
+
|
|
198
|
+
# Step 3: Install agent-skill-manager
|
|
199
|
+
install_asm
|
|
200
|
+
echo ""
|
|
201
|
+
|
|
202
|
+
# Step 4: Create aliases (agent-skill-manager, asm)
|
|
203
|
+
create_aliases
|
|
204
|
+
echo ""
|
|
205
|
+
|
|
206
|
+
# Step 5: Verify
|
|
207
|
+
if verify_installation; then
|
|
208
|
+
echo ""
|
|
209
|
+
info "============================================"
|
|
210
|
+
ok "Installation complete!"
|
|
211
|
+
info "============================================"
|
|
212
|
+
echo ""
|
|
213
|
+
info "Get started:"
|
|
214
|
+
info " asm # Launch interactive TUI (shorthand)"
|
|
215
|
+
info " agent-skill-manager # Launch interactive TUI"
|
|
216
|
+
info " asm --help # Show help"
|
|
217
|
+
echo ""
|
|
218
|
+
else
|
|
219
|
+
echo ""
|
|
220
|
+
warn "Installation finished but verification had warnings."
|
|
221
|
+
warn "Try restarting your terminal, then run: agent-skill-manager"
|
|
222
|
+
echo ""
|
|
223
|
+
fi
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
main "$@"
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-skill-manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Interactive TUI for managing installed skills for AI coding agents (Claude Code, Codex, OpenClaw, and more)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"skill-manager": "bin/skill-manager.ts"
|
|
7
|
+
"agent-skill-manager": "bin/agent-skill-manager.ts",
|
|
8
|
+
"asm": "bin/agent-skill-manager.ts"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"start": "bun run src/index.ts",
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
"bun": ">=1.0.0"
|
|
20
21
|
},
|
|
21
22
|
"keywords": [
|
|
22
|
-
"skill-manager",
|
|
23
|
+
"agent-skill-manager",
|
|
23
24
|
"tui",
|
|
24
25
|
"cli",
|
|
25
26
|
"ai-agents",
|
|
@@ -30,13 +31,13 @@
|
|
|
30
31
|
],
|
|
31
32
|
"author": "luongnv89",
|
|
32
33
|
"license": "MIT",
|
|
33
|
-
"homepage": "https://github.com/luongnv89/skill-manager#readme",
|
|
34
|
+
"homepage": "https://github.com/luongnv89/agent-skill-manager#readme",
|
|
34
35
|
"repository": {
|
|
35
36
|
"type": "git",
|
|
36
|
-
"url": "git+https://github.com/luongnv89/skill-manager.git"
|
|
37
|
+
"url": "git+https://github.com/luongnv89/agent-skill-manager.git"
|
|
37
38
|
},
|
|
38
39
|
"bugs": {
|
|
39
|
-
"url": "https://github.com/luongnv89/skill-manager/issues"
|
|
40
|
+
"url": "https://github.com/luongnv89/agent-skill-manager/issues"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"bun-types": "^1.3.10"
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadConfig,
|
|
3
|
+
getConfigPath,
|
|
4
|
+
getDefaultConfig,
|
|
5
|
+
saveConfig,
|
|
6
|
+
} from "./config";
|
|
7
|
+
import { scanAllSkills, searchSkills, sortSkills } from "./scanner";
|
|
8
|
+
import {
|
|
9
|
+
buildFullRemovalPlan,
|
|
10
|
+
executeRemoval,
|
|
11
|
+
getExistingTargets,
|
|
12
|
+
} from "./uninstaller";
|
|
13
|
+
import {
|
|
14
|
+
formatSkillTable,
|
|
15
|
+
formatSkillDetail,
|
|
16
|
+
formatJSON,
|
|
17
|
+
ansi,
|
|
18
|
+
} from "./formatter";
|
|
19
|
+
import { VERSION_STRING } from "./utils/version";
|
|
20
|
+
import type { Scope, SortBy } from "./utils/types";
|
|
21
|
+
|
|
22
|
+
// ─── Arg Parser ─────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
interface ParsedArgs {
|
|
25
|
+
command: string | null;
|
|
26
|
+
subcommand: string | null;
|
|
27
|
+
positional: string[];
|
|
28
|
+
flags: {
|
|
29
|
+
help: boolean;
|
|
30
|
+
version: boolean;
|
|
31
|
+
json: boolean;
|
|
32
|
+
yes: boolean;
|
|
33
|
+
noColor: boolean;
|
|
34
|
+
scope: Scope;
|
|
35
|
+
sort: SortBy;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function parseArgs(argv: string[]): ParsedArgs {
|
|
40
|
+
const args = argv.slice(2); // skip bun and script path
|
|
41
|
+
|
|
42
|
+
const result: ParsedArgs = {
|
|
43
|
+
command: null,
|
|
44
|
+
subcommand: null,
|
|
45
|
+
positional: [],
|
|
46
|
+
flags: {
|
|
47
|
+
help: false,
|
|
48
|
+
version: false,
|
|
49
|
+
json: false,
|
|
50
|
+
yes: false,
|
|
51
|
+
noColor: false,
|
|
52
|
+
scope: "both",
|
|
53
|
+
sort: "name",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let i = 0;
|
|
58
|
+
while (i < args.length) {
|
|
59
|
+
const arg = args[i];
|
|
60
|
+
|
|
61
|
+
// Flags
|
|
62
|
+
if (arg === "--help" || arg === "-h") {
|
|
63
|
+
result.flags.help = true;
|
|
64
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
65
|
+
result.flags.version = true;
|
|
66
|
+
} else if (arg === "--json") {
|
|
67
|
+
result.flags.json = true;
|
|
68
|
+
} else if (arg === "--yes" || arg === "-y") {
|
|
69
|
+
result.flags.yes = true;
|
|
70
|
+
} else if (arg === "--no-color") {
|
|
71
|
+
result.flags.noColor = true;
|
|
72
|
+
} else if (arg === "--scope" || arg === "-s") {
|
|
73
|
+
i++;
|
|
74
|
+
const val = args[i];
|
|
75
|
+
if (val === "global" || val === "project" || val === "both") {
|
|
76
|
+
result.flags.scope = val;
|
|
77
|
+
} else {
|
|
78
|
+
error(`Invalid scope: "${val}". Must be global, project, or both.`);
|
|
79
|
+
process.exit(2);
|
|
80
|
+
}
|
|
81
|
+
} else if (arg === "--sort") {
|
|
82
|
+
i++;
|
|
83
|
+
const val = args[i];
|
|
84
|
+
if (val === "name" || val === "version" || val === "location") {
|
|
85
|
+
result.flags.sort = val;
|
|
86
|
+
} else {
|
|
87
|
+
error(`Invalid sort: "${val}". Must be name, version, or location.`);
|
|
88
|
+
process.exit(2);
|
|
89
|
+
}
|
|
90
|
+
} else if (arg.startsWith("-")) {
|
|
91
|
+
error(`Unknown option: ${arg}`);
|
|
92
|
+
console.error(`Run "asm --help" for usage.`);
|
|
93
|
+
process.exit(2);
|
|
94
|
+
} else {
|
|
95
|
+
// Positional: first is command, second is subcommand, rest are positional args
|
|
96
|
+
if (!result.command) {
|
|
97
|
+
result.command = arg;
|
|
98
|
+
} else if (!result.subcommand) {
|
|
99
|
+
result.subcommand = arg;
|
|
100
|
+
} else {
|
|
101
|
+
result.positional.push(arg);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── Output helpers ─────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
function error(msg: string) {
|
|
114
|
+
console.error(ansi.red(`Error: ${msg}`));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ─── Help text ──────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
function printMainHelp() {
|
|
120
|
+
console.log(`${ansi.blueBold("agent-skill-manager")} (${ansi.bold("asm")}) ${VERSION_STRING}
|
|
121
|
+
|
|
122
|
+
Interactive TUI and CLI for managing installed skills for AI coding agents.
|
|
123
|
+
|
|
124
|
+
${ansi.bold("Usage:")}
|
|
125
|
+
asm Launch interactive TUI
|
|
126
|
+
asm <command> [options] Run a CLI command
|
|
127
|
+
|
|
128
|
+
${ansi.bold("Commands:")}
|
|
129
|
+
list List all discovered skills
|
|
130
|
+
search <query> Search skills by name/description/provider
|
|
131
|
+
inspect <skill-name> Show detailed info for a skill
|
|
132
|
+
uninstall <skill-name> Remove a skill (with confirmation)
|
|
133
|
+
config show Print current config
|
|
134
|
+
config path Print config file path
|
|
135
|
+
config reset Reset config to defaults
|
|
136
|
+
config edit Open config in $EDITOR
|
|
137
|
+
|
|
138
|
+
${ansi.bold("Global Options:")}
|
|
139
|
+
-h, --help Show help for any command
|
|
140
|
+
-v, --version Print version and exit
|
|
141
|
+
--json Output as JSON (list, search, inspect)
|
|
142
|
+
-s, --scope <scope> Filter: global, project, or both (default: both)
|
|
143
|
+
--no-color Disable ANSI colors
|
|
144
|
+
--sort <field> Sort by: name, version, or location (default: name)
|
|
145
|
+
-y, --yes Skip confirmation prompts`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function printListHelp() {
|
|
149
|
+
console.log(`${ansi.bold("Usage:")} asm list [options]
|
|
150
|
+
|
|
151
|
+
List all discovered skills.
|
|
152
|
+
|
|
153
|
+
${ansi.bold("Options:")}
|
|
154
|
+
--sort <field> Sort by: name, version, or location (default: name)
|
|
155
|
+
-s, --scope <s> Filter: global, project, or both (default: both)
|
|
156
|
+
--json Output as JSON array
|
|
157
|
+
--no-color Disable ANSI colors`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function printSearchHelp() {
|
|
161
|
+
console.log(`${ansi.bold("Usage:")} asm search <query> [options]
|
|
162
|
+
|
|
163
|
+
Search skills by name, description, or provider.
|
|
164
|
+
|
|
165
|
+
${ansi.bold("Options:")}
|
|
166
|
+
--sort <field> Sort by: name, version, or location (default: name)
|
|
167
|
+
-s, --scope <s> Filter: global, project, or both (default: both)
|
|
168
|
+
--json Output as JSON array
|
|
169
|
+
--no-color Disable ANSI colors`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function printInspectHelp() {
|
|
173
|
+
console.log(`${ansi.bold("Usage:")} asm inspect <skill-name> [options]
|
|
174
|
+
|
|
175
|
+
Show detailed information for a skill. The <skill-name> is the directory name.
|
|
176
|
+
|
|
177
|
+
${ansi.bold("Options:")}
|
|
178
|
+
-s, --scope <s> Filter: global, project, or both (default: both)
|
|
179
|
+
--json Output as JSON object
|
|
180
|
+
--no-color Disable ANSI colors`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function printUninstallHelp() {
|
|
184
|
+
console.log(`${ansi.bold("Usage:")} asm uninstall <skill-name> [options]
|
|
185
|
+
|
|
186
|
+
Remove a skill and its associated rule files.
|
|
187
|
+
|
|
188
|
+
${ansi.bold("Options:")}
|
|
189
|
+
-y, --yes Skip confirmation prompt
|
|
190
|
+
-s, --scope <s> Filter: global, project, or both (default: both)
|
|
191
|
+
--no-color Disable ANSI colors`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function printConfigHelp() {
|
|
195
|
+
console.log(`${ansi.bold("Usage:")} asm config <subcommand>
|
|
196
|
+
|
|
197
|
+
Manage configuration.
|
|
198
|
+
|
|
199
|
+
${ansi.bold("Subcommands:")}
|
|
200
|
+
show Print current config as JSON
|
|
201
|
+
path Print config file path
|
|
202
|
+
reset Reset config to defaults (with confirmation)
|
|
203
|
+
edit Open config in $EDITOR`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ─── Command Handlers ───────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
async function cmdList(args: ParsedArgs) {
|
|
209
|
+
if (args.flags.help) {
|
|
210
|
+
printListHelp();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const config = await loadConfig();
|
|
215
|
+
const allSkills = await scanAllSkills(config, args.flags.scope);
|
|
216
|
+
const sorted = sortSkills(allSkills, args.flags.sort);
|
|
217
|
+
|
|
218
|
+
if (args.flags.json) {
|
|
219
|
+
console.log(formatJSON(sorted));
|
|
220
|
+
} else {
|
|
221
|
+
console.log(formatSkillTable(sorted));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function cmdSearch(args: ParsedArgs) {
|
|
226
|
+
if (args.flags.help) {
|
|
227
|
+
printSearchHelp();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const query = args.subcommand;
|
|
232
|
+
if (!query) {
|
|
233
|
+
error("Missing required argument: <query>");
|
|
234
|
+
console.error(`Run "asm search --help" for usage.`);
|
|
235
|
+
process.exit(2);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const config = await loadConfig();
|
|
239
|
+
const allSkills = await scanAllSkills(config, args.flags.scope);
|
|
240
|
+
const filtered = searchSkills(allSkills, query);
|
|
241
|
+
const sorted = sortSkills(filtered, args.flags.sort);
|
|
242
|
+
|
|
243
|
+
if (args.flags.json) {
|
|
244
|
+
console.log(formatJSON(sorted));
|
|
245
|
+
} else {
|
|
246
|
+
console.log(formatSkillTable(sorted));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function cmdInspect(args: ParsedArgs) {
|
|
251
|
+
if (args.flags.help) {
|
|
252
|
+
printInspectHelp();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const skillName = args.subcommand;
|
|
257
|
+
if (!skillName) {
|
|
258
|
+
error("Missing required argument: <skill-name>");
|
|
259
|
+
console.error(`Run "asm inspect --help" for usage.`);
|
|
260
|
+
process.exit(2);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const config = await loadConfig();
|
|
264
|
+
const allSkills = await scanAllSkills(config, args.flags.scope);
|
|
265
|
+
const matches = allSkills.filter((s) => s.dirName === skillName);
|
|
266
|
+
|
|
267
|
+
if (matches.length === 0) {
|
|
268
|
+
error(`Skill "${skillName}" not found.`);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (args.flags.json) {
|
|
273
|
+
console.log(formatJSON(matches.length === 1 ? matches[0] : matches));
|
|
274
|
+
} else {
|
|
275
|
+
for (let i = 0; i < matches.length; i++) {
|
|
276
|
+
if (i > 0) console.log("\n" + "─".repeat(40) + "\n");
|
|
277
|
+
console.log(formatSkillDetail(matches[i]));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function cmdUninstall(args: ParsedArgs) {
|
|
283
|
+
if (args.flags.help) {
|
|
284
|
+
printUninstallHelp();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const skillName = args.subcommand;
|
|
289
|
+
if (!skillName) {
|
|
290
|
+
error("Missing required argument: <skill-name>");
|
|
291
|
+
console.error(`Run "asm uninstall --help" for usage.`);
|
|
292
|
+
process.exit(2);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const config = await loadConfig();
|
|
296
|
+
const allSkills = await scanAllSkills(config, args.flags.scope);
|
|
297
|
+
const plan = buildFullRemovalPlan(skillName, allSkills, config);
|
|
298
|
+
|
|
299
|
+
const existing = await getExistingTargets(plan);
|
|
300
|
+
if (existing.length === 0) {
|
|
301
|
+
error(`Skill "${skillName}" not found or nothing to remove.`);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Show removal plan
|
|
306
|
+
console.error(ansi.bold("Removal plan:"));
|
|
307
|
+
for (const target of existing) {
|
|
308
|
+
console.error(` ${ansi.red("•")} ${target}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!args.flags.yes) {
|
|
312
|
+
// Interactive confirmation
|
|
313
|
+
if (!process.stdin.isTTY) {
|
|
314
|
+
error(
|
|
315
|
+
"Cannot prompt for confirmation in non-interactive mode. Use --yes to skip.",
|
|
316
|
+
);
|
|
317
|
+
process.exit(2);
|
|
318
|
+
}
|
|
319
|
+
process.stderr.write(`\n${ansi.bold("Proceed with removal?")} [y/N] `);
|
|
320
|
+
const answer = await readLine();
|
|
321
|
+
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
322
|
+
console.error("Aborted.");
|
|
323
|
+
process.exit(0);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const log = await executeRemoval(plan);
|
|
328
|
+
for (const entry of log) {
|
|
329
|
+
console.error(entry);
|
|
330
|
+
}
|
|
331
|
+
console.error(ansi.green("\nDone."));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function readLine(): Promise<string> {
|
|
335
|
+
return new Promise((resolve) => {
|
|
336
|
+
let data = "";
|
|
337
|
+
process.stdin.setEncoding("utf-8");
|
|
338
|
+
process.stdin.on("data", (chunk: string) => {
|
|
339
|
+
data += chunk;
|
|
340
|
+
if (data.includes("\n")) {
|
|
341
|
+
process.stdin.removeAllListeners("data");
|
|
342
|
+
resolve(data.trim());
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
process.stdin.resume();
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function cmdConfig(args: ParsedArgs) {
|
|
350
|
+
if (args.flags.help) {
|
|
351
|
+
printConfigHelp();
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const sub = args.subcommand;
|
|
356
|
+
|
|
357
|
+
if (!sub) {
|
|
358
|
+
error("Missing subcommand. Use: show, path, reset, or edit.");
|
|
359
|
+
console.error(`Run "asm config --help" for usage.`);
|
|
360
|
+
process.exit(2);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
switch (sub) {
|
|
364
|
+
case "show": {
|
|
365
|
+
const config = await loadConfig();
|
|
366
|
+
console.log(formatJSON(config));
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "path": {
|
|
370
|
+
console.log(getConfigPath());
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "reset": {
|
|
374
|
+
if (!args.flags.yes) {
|
|
375
|
+
if (!process.stdin.isTTY) {
|
|
376
|
+
error(
|
|
377
|
+
"Cannot prompt for confirmation in non-interactive mode. Use --yes to skip.",
|
|
378
|
+
);
|
|
379
|
+
process.exit(2);
|
|
380
|
+
}
|
|
381
|
+
process.stderr.write(
|
|
382
|
+
`${ansi.bold("Reset config to defaults?")} [y/N] `,
|
|
383
|
+
);
|
|
384
|
+
const answer = await readLine();
|
|
385
|
+
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
386
|
+
console.error("Aborted.");
|
|
387
|
+
process.exit(0);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const defaults = getDefaultConfig();
|
|
391
|
+
await saveConfig(defaults);
|
|
392
|
+
console.error(ansi.green("Config reset to defaults."));
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
case "edit": {
|
|
396
|
+
const editor = process.env.VISUAL || process.env.EDITOR || "vi";
|
|
397
|
+
const configPath = getConfigPath();
|
|
398
|
+
// Ensure config file exists
|
|
399
|
+
await loadConfig();
|
|
400
|
+
const proc = Bun.spawn([editor, configPath], {
|
|
401
|
+
stdin: "inherit",
|
|
402
|
+
stdout: "inherit",
|
|
403
|
+
stderr: "inherit",
|
|
404
|
+
});
|
|
405
|
+
await proc.exited;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
default: {
|
|
409
|
+
error(
|
|
410
|
+
`Unknown config subcommand: "${sub}". Use: show, path, reset, or edit.`,
|
|
411
|
+
);
|
|
412
|
+
process.exit(2);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ─── Main CLI dispatcher ────────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
export async function runCLI(argv: string[]): Promise<void> {
|
|
420
|
+
const args = parseArgs(argv);
|
|
421
|
+
|
|
422
|
+
// Apply --no-color
|
|
423
|
+
if (args.flags.noColor) {
|
|
424
|
+
(globalThis as any).__CLI_NO_COLOR = true;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// --version at top level
|
|
428
|
+
if (args.flags.version) {
|
|
429
|
+
console.log(`asm ${VERSION_STRING}`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// --help at top level (no command)
|
|
434
|
+
if (!args.command && args.flags.help) {
|
|
435
|
+
printMainHelp();
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// No command → return null to signal TUI launch
|
|
440
|
+
if (!args.command) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
switch (args.command) {
|
|
445
|
+
case "list":
|
|
446
|
+
await cmdList(args);
|
|
447
|
+
break;
|
|
448
|
+
case "search":
|
|
449
|
+
await cmdSearch(args);
|
|
450
|
+
break;
|
|
451
|
+
case "inspect":
|
|
452
|
+
await cmdInspect(args);
|
|
453
|
+
break;
|
|
454
|
+
case "uninstall":
|
|
455
|
+
await cmdUninstall(args);
|
|
456
|
+
break;
|
|
457
|
+
case "config":
|
|
458
|
+
await cmdConfig(args);
|
|
459
|
+
break;
|
|
460
|
+
default:
|
|
461
|
+
error(`Unknown command: "${args.command}"`);
|
|
462
|
+
console.error(`Run "asm --help" for usage.`);
|
|
463
|
+
process.exit(2);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ─── Check if CLI mode should run ──────────────────────────────────────────
|
|
468
|
+
|
|
469
|
+
export function isCLIMode(argv: string[]): boolean {
|
|
470
|
+
const args = argv.slice(2);
|
|
471
|
+
if (args.length === 0) return false;
|
|
472
|
+
|
|
473
|
+
// Known commands
|
|
474
|
+
const commands = ["list", "search", "inspect", "uninstall", "config"];
|
|
475
|
+
const first = args[0];
|
|
476
|
+
|
|
477
|
+
// If the first arg is a known command, it's CLI mode
|
|
478
|
+
if (commands.includes(first)) return true;
|
|
479
|
+
|
|
480
|
+
// --help and --version are handled in CLI mode too
|
|
481
|
+
if (first === "--help" || first === "-h") return true;
|
|
482
|
+
if (first === "--version" || first === "-v") return true;
|
|
483
|
+
|
|
484
|
+
// Unknown flags/commands → CLI mode (will show error)
|
|
485
|
+
if (first.startsWith("-") || first.length > 0) return true;
|
|
486
|
+
|
|
487
|
+
return false;
|
|
488
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { homedir } from "os";
|
|
|
4
4
|
import type { AppConfig, ProviderConfig } from "./utils/types";
|
|
5
5
|
|
|
6
6
|
const HOME = homedir();
|
|
7
|
-
const CONFIG_DIR = join(HOME, ".config", "skill-manager");
|
|
7
|
+
const CONFIG_DIR = join(HOME, ".config", "agent-skill-manager");
|
|
8
8
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
9
9
|
|
|
10
10
|
const DEFAULT_PROVIDERS: ProviderConfig[] = [
|
package/src/formatter.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { SkillInfo } from "./utils/types";
|
|
2
|
+
|
|
3
|
+
// ─── Color helpers ──────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
const useColor = (): boolean => {
|
|
6
|
+
if (process.env.NO_COLOR !== undefined) return false;
|
|
7
|
+
if ((globalThis as any).__CLI_NO_COLOR) return false;
|
|
8
|
+
if (!process.stdout.isTTY) return false;
|
|
9
|
+
return true;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const ansi = {
|
|
13
|
+
bold: (s: string) => (useColor() ? `\x1b[1m${s}\x1b[0m` : s),
|
|
14
|
+
cyan: (s: string) => (useColor() ? `\x1b[36m${s}\x1b[0m` : s),
|
|
15
|
+
green: (s: string) => (useColor() ? `\x1b[32m${s}\x1b[0m` : s),
|
|
16
|
+
yellow: (s: string) => (useColor() ? `\x1b[33m${s}\x1b[0m` : s),
|
|
17
|
+
dim: (s: string) => (useColor() ? `\x1b[2m${s}\x1b[0m` : s),
|
|
18
|
+
red: (s: string) => (useColor() ? `\x1b[31m${s}\x1b[0m` : s),
|
|
19
|
+
blueBold: (s: string) => (useColor() ? `\x1b[1m\x1b[34m${s}\x1b[0m` : s),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { ansi };
|
|
23
|
+
|
|
24
|
+
// ─── Table formatter ────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
export function formatSkillTable(skills: SkillInfo[]): string {
|
|
27
|
+
if (skills.length === 0) {
|
|
28
|
+
return "No skills found.";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const headers = ["Name", "Version", "Provider", "Scope", "Type", "Path"];
|
|
32
|
+
|
|
33
|
+
const rows = skills.map((s) => [
|
|
34
|
+
s.name,
|
|
35
|
+
s.version,
|
|
36
|
+
s.providerLabel,
|
|
37
|
+
s.scope,
|
|
38
|
+
s.isSymlink ? "symlink" : "directory",
|
|
39
|
+
s.path,
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Calculate column widths
|
|
43
|
+
const widths = headers.map((h, i) =>
|
|
44
|
+
Math.max(h.length, ...rows.map((r) => r[i].length)),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const pad = (str: string, width: number) => str.padEnd(width);
|
|
48
|
+
|
|
49
|
+
const headerLine = headers.map((h, i) => pad(h, widths[i])).join(" ");
|
|
50
|
+
const separator = widths.map((w) => "─".repeat(w)).join("──");
|
|
51
|
+
const dataLines = rows.map((row) =>
|
|
52
|
+
row.map((cell, i) => pad(cell, widths[i])).join(" "),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return [
|
|
56
|
+
useColor() ? ansi.bold(headerLine) : headerLine,
|
|
57
|
+
separator,
|
|
58
|
+
...dataLines,
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── Detail formatter ───────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
export function formatSkillDetail(skill: SkillInfo): string {
|
|
65
|
+
const lines: string[] = [];
|
|
66
|
+
const label = (key: string, value: string) =>
|
|
67
|
+
`${useColor() ? ansi.bold(key + ":") : key + ":"} ${value}`;
|
|
68
|
+
|
|
69
|
+
lines.push(label("Name", skill.name));
|
|
70
|
+
lines.push(label("Version", skill.version));
|
|
71
|
+
lines.push(label("Provider", skill.providerLabel));
|
|
72
|
+
lines.push(label("Scope", skill.scope));
|
|
73
|
+
lines.push(label("Location", skill.location));
|
|
74
|
+
lines.push(label("Path", skill.path));
|
|
75
|
+
lines.push(label("Type", skill.isSymlink ? "symlink" : "directory"));
|
|
76
|
+
if (skill.isSymlink && skill.symlinkTarget) {
|
|
77
|
+
lines.push(label("Symlink Target", skill.symlinkTarget));
|
|
78
|
+
}
|
|
79
|
+
lines.push(label("File Count", String(skill.fileCount)));
|
|
80
|
+
if (skill.description) {
|
|
81
|
+
lines.push("");
|
|
82
|
+
lines.push(label("Description", skill.description));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ─── JSON formatter ─────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
export function formatJSON(data: unknown): string {
|
|
91
|
+
return JSON.stringify(data, null, 2);
|
|
92
|
+
}
|
package/src/uninstaller.ts
CHANGED
|
@@ -122,7 +122,7 @@ async function removeAgentsMdBlock(
|
|
|
122
122
|
let content = await readFile(filePath, "utf-8");
|
|
123
123
|
|
|
124
124
|
// Try both new and old marker formats for backward compatibility
|
|
125
|
-
for (const prefix of ["skill-manager", "pskills"]) {
|
|
125
|
+
for (const prefix of ["agent-skill-manager", "skill-manager", "pskills"]) {
|
|
126
126
|
const startMarker = `<!-- ${prefix}: ${skillName} -->`;
|
|
127
127
|
const endMarker = `<!-- /${prefix}: ${skillName} -->`;
|
|
128
128
|
|
|
@@ -213,6 +213,7 @@ export async function getExistingTargets(plan: RemovalPlan): Promise<string[]> {
|
|
|
213
213
|
const content = await readFile(block.file, "utf-8");
|
|
214
214
|
// Check both new and old marker formats
|
|
215
215
|
if (
|
|
216
|
+
content.includes(`<!-- agent-skill-manager: ${block.skillName} -->`) ||
|
|
216
217
|
content.includes(`<!-- skill-manager: ${block.skillName} -->`) ||
|
|
217
218
|
content.includes(`<!-- pskills: ${block.skillName} -->`)
|
|
218
219
|
) {
|
package/src/views/dashboard.ts
CHANGED
package/bin/skill-manager.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import { VERSION, VERSION_STRING } from "../src/utils/version";
|
|
4
|
-
|
|
5
|
-
const arg = process.argv[2];
|
|
6
|
-
|
|
7
|
-
if (arg === "--help" || arg === "-h") {
|
|
8
|
-
console.log(`\x1b[1m\x1b[36mskill-manager\x1b[0m ${VERSION_STRING}
|
|
9
|
-
|
|
10
|
-
Interactive TUI for managing installed skills for AI coding agents (Claude Code, Codex, OpenClaw, and more).
|
|
11
|
-
|
|
12
|
-
\x1b[1mUsage:\x1b[0m
|
|
13
|
-
skill-manager Launch the interactive TUI dashboard
|
|
14
|
-
skill-manager --help Show this help message
|
|
15
|
-
skill-manager --version Show version
|
|
16
|
-
|
|
17
|
-
\x1b[1mRequirements:\x1b[0m
|
|
18
|
-
Bun >= 1.0.0 (https://bun.sh)
|
|
19
|
-
|
|
20
|
-
\x1b[1mConfig:\x1b[0m
|
|
21
|
-
~/.config/skill-manager/config.json
|
|
22
|
-
|
|
23
|
-
\x1b[1mTUI Keybindings:\x1b[0m
|
|
24
|
-
↑/↓ or j/k Navigate skill list
|
|
25
|
-
Enter View skill details
|
|
26
|
-
d Uninstall selected skill
|
|
27
|
-
/ Search / filter skills
|
|
28
|
-
Esc Back / clear filter / close dialog
|
|
29
|
-
Tab Cycle scope: Global → Project → Both
|
|
30
|
-
s Cycle sort: Name → Version → Location
|
|
31
|
-
r Refresh / rescan skills
|
|
32
|
-
c Open configuration
|
|
33
|
-
q Quit
|
|
34
|
-
? Toggle help overlay`);
|
|
35
|
-
process.exit(0);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (arg === "--version" || arg === "-v") {
|
|
39
|
-
console.log(`skill-manager ${VERSION_STRING}`);
|
|
40
|
-
process.exit(0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Launch the TUI
|
|
44
|
-
await import("../src/index.ts");
|
|
45
|
-
|
|
46
|
-
export {};
|