@sstar/skill-install 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ekko.bao
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,296 @@
1
+ # Agent Skill Installer
2
+
3
+ A command-line tool for installing Agent Skills from various sources (public URLs, Wiki with authentication, or local archive files). Automatically downloads, extracts, validates, and installs skills for **Claude Code** or **Codex**.
4
+
5
+ ## Features
6
+
7
+ - **Multi-tool support**: Works with both Claude Code (`~/.claude/skills/`) and Codex (`~/.codex/skills/`)
8
+ - **Auto-detect source**: Automatically detects if the source is a public URL, Wiki URL (with auth), or local file
9
+ - **Multiple archive formats**: Supports `.zip` and `.tar.gz` archives
10
+ - **Strict validation**: Validates that archives contain a valid `SKILL.md` with required frontmatter
11
+ - **Wiki authentication**: Supports authentication with internal Confluence Wiki
12
+ - **Skill management**: List installed skills and uninstall them
13
+ - **Interactive prompts**: Choose between global/local directories and AI tools
14
+ - **Self-signed certificates**: Built-in support for corporate SSL certificates
15
+
16
+ ## What are Skills?
17
+
18
+ Skills are a lightweight, open format for extending AI agent capabilities with specialized knowledge and workflows. At its core, a skill is a folder containing a `SKILL.md` file with metadata (`name` and `description`) and instructions.
19
+
20
+ Learn more at: https://agentskills.io/what-are-skills
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ cd /home/ekko.bao/work/tools/wiki_download
26
+ npm install
27
+ npm run build
28
+ npm link # Optional: for global installation
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```bash
34
+ # Install with interactive AI tool and directory selection
35
+ skill-install install https://github.com/user/repo/archive/main.zip
36
+
37
+ # Install for specific AI tool (skip prompt)
38
+ skill-install -t claude install https://github.com/user/repo/archive/main.zip
39
+ skill-install -t codex install https://github.com/user/repo/archive/main.zip
40
+
41
+ # Install to global directory explicitly
42
+ skill-install install https://example.com/skill.zip --global
43
+
44
+ # Install to local directory explicitly
45
+ skill-install install https://example.com/skill.zip --local
46
+
47
+ # Install with custom directory
48
+ skill-install -s /path/to/skills install https://example.com/skill.zip
49
+
50
+ # Install from Wiki with authentication
51
+ skill-install install https://wiki.company.com/download/attachments/123/skill.zip -u username
52
+
53
+ # Install from a local archive file
54
+ skill-install install ./my-skill.zip
55
+
56
+ # List installed skills (prompts for AI tool)
57
+ skill-install list
58
+
59
+ # List installed skills for specific AI tool (skip prompt)
60
+ skill-install -t claude list
61
+ skill-install -t codex list
62
+
63
+ # List skills from both global and local directories
64
+ skill-install list --all
65
+
66
+ # Uninstall a skill (prompts for AI tool)
67
+ skill-install uninstall my-skill
68
+
69
+ # Uninstall from specific AI tool (skip prompt)
70
+ skill-install -t claude uninstall my-skill
71
+ skill-install -t codex uninstall my-skill
72
+
73
+ # Force reinstall if skill already exists
74
+ skill-install install ./my-skill.zip --force
75
+ ```
76
+
77
+ ## Options
78
+
79
+ ### Global Options
80
+ | Option | Description |
81
+ |--------|-------------|
82
+ | `-t, --tool <tool>` | AI tool: `claude` or `codex` (skip prompt if specified) |
83
+ | `-s, --skills-dir <path>` | Custom skills directory |
84
+ | `-v, --verbose` | Enable verbose logging |
85
+ | `-h, --help` | Display help |
86
+
87
+ **Note**: If `-t` is not specified, the tool will prompt interactively to choose between Claude Code and Codex.
88
+
89
+ ### Install Command
90
+ | Option | Description |
91
+ |--------|-------------|
92
+ | `<source>` | URL or local file path to skill archive |
93
+ | `-g, --global` | Install to global directory (`~/.claude/skills/` or `~/.codex/skills/`) |
94
+ | `-l, --local` | Install to local directory (`./.claude/skills/` or `./.codex/skills/`) |
95
+ | `-u, --username <username>` | Wiki username (for Wiki URLs) |
96
+ | `-p, --password <password>` | Wiki password (or use env var) |
97
+ | `--allow-self-signed` | Allow self-signed SSL certificates (default: true) |
98
+ | `-f, --force` | Reinstall if skill already exists |
99
+
100
+ **Note**: If neither `-g` nor `-l` nor `-s` is specified, the tool will prompt interactively to choose between global and local directories.
101
+
102
+ ### List Command
103
+ | Option | Description |
104
+ |--------|-------------|
105
+ | `-a, --all` | List skills from both global and local directories |
106
+
107
+ ### Uninstall Command
108
+ | Option | Description |
109
+ |--------|-------------|
110
+ | `[name]` | Name of the skill to uninstall (optional - leave empty to select from list) |
111
+ | `-y, --yes` | Skip confirmation prompt |
112
+
113
+ **Note**: If no skill name is provided, the tool will display all installed skills and allow you to select which ones to uninstall by entering numbers separated by spaces (e.g., `1 3 5`).
114
+
115
+ ## Environment Variables
116
+
117
+ | Variable | Description |
118
+ |----------|-------------|
119
+ | `WIKI_USERNAME` | Default Wiki username |
120
+ | `WIKI_PASSWORD` | Default Wiki password |
121
+
122
+ ## Skill Validation
123
+
124
+ A valid skill must have a `SKILL.md` file at the root with YAML frontmatter containing:
125
+
126
+ ```yaml
127
+ ---
128
+ name: my-skill
129
+ description: A brief description of what this skill does
130
+ ---
131
+
132
+ # My Skill
133
+
134
+ Instructions for the agent...
135
+ ```
136
+
137
+ The tool validates:
138
+ - `SKILL.md` exists
139
+ - YAML frontmatter is present (wrapped in `---`)
140
+ - `name` field is present
141
+ - `description` field is present
142
+
143
+ ## Skill Directory Structure
144
+
145
+ Skills can be installed to different locations based on the AI tool:
146
+
147
+ ### Claude Code
148
+ - **Global**: `~/.claude/skills/` - Available to all projects
149
+ - **Local**: `./.claude/skills/` - Project-specific
150
+
151
+ ### Codex
152
+ - **Global**: `~/.codex/skills/` - Available to all projects
153
+ - **Local**: `./.codex/skills/` - Project-specific
154
+
155
+ ### Directory Selection
156
+ - **Global**: Use `--global` flag or select "1" when prompted (recommended for commonly used skills)
157
+ - **Local**: Use `--local` flag or select "2" when prompted (recommended for project-specific skills)
158
+ - **Custom**: Use `-s /path/to/skills` for a custom location
159
+
160
+ A typical installed skill:
161
+
162
+ ```
163
+ ~/.claude/skills/my-skill/
164
+ ├── SKILL.md # Required: instructions + metadata
165
+ ├── scripts/ # Optional: executable code
166
+ ├── references/ # Optional: documentation
167
+ └── assets/ # Optional: templates, resources
168
+ ```
169
+
170
+ ## Examples
171
+
172
+ ### Interactive installation (prompts for AI tool and directory)
173
+
174
+ ```bash
175
+ skill-install install https://github.com/user/repo/archive/main.zip
176
+
177
+ # Output:
178
+ # Select AI tool:
179
+ # 1. Claude Code (~/.claude/skills/)
180
+ # 2. Codex (~/.codex/skills/)
181
+ #
182
+ # Choose [1/2]: 1
183
+ #
184
+ # Select installation directory:
185
+ # 1. Global: /home/user/.claude/skills
186
+ # 2. Local: /home/user/project/.claude/skills
187
+ #
188
+ # Choose [1/2]: 1
189
+ ```
190
+
191
+ ### Install for specific AI tool (skip prompt)
192
+
193
+ ```bash
194
+ # Install for Claude Code
195
+ skill-install -t claude install https://example.com/skill.zip
196
+
197
+ # Install for Codex
198
+ skill-install -t codex install https://example.com/skill.zip
199
+
200
+ # Install to Codex global directory
201
+ skill-install -t codex install https://example.com/skill.zip --global
202
+ ```
203
+
204
+ ### Install to specific directory
205
+
206
+ ```bash
207
+ # Install to global directory
208
+ skill-install install https://example.com/skill.zip --global
209
+
210
+ # Install to local directory
211
+ skill-install install https://example.com/skill.zip --local
212
+
213
+ # Install to custom directory
214
+ skill-install -s /custom/path install https://example.com/skill.zip
215
+ ```
216
+
217
+ ### Install from Wiki
218
+
219
+ ```bash
220
+ skill-install install "https://wiki.company.com/download/attachments/12345/skill.zip?api=v2" -u john.doe
221
+ ```
222
+
223
+ ### Install from local file
224
+
225
+ ```bash
226
+ skill-install install ~/Downloads/my-skill.tar.gz
227
+ ```
228
+
229
+ ### List and uninstall
230
+
231
+ ```bash
232
+ # List skills (prompts for AI tool)
233
+ skill-install list
234
+
235
+ # List skills for specific AI tool (skip prompt)
236
+ skill-install -t claude list
237
+ skill-install -t codex list
238
+
239
+ # List skills from both directories
240
+ skill-install -t claude list --all
241
+
242
+ # Uninstall a specific skill (prompts for AI tool)
243
+ skill-install uninstall my-skill
244
+
245
+ # Uninstall without prompts (specify AI tool and skip confirmation)
246
+ skill-install -t codex uninstall my-skill -y
247
+
248
+ # Interactive uninstall (select from list)
249
+ skill-install uninstall
250
+
251
+ # Output:
252
+ # Select AI tool:
253
+ # 1. Claude Code (~/.claude/skills/)
254
+ # 2. Codex (~/.codex/skills/)
255
+ #
256
+ # Choose [1/2]: 1
257
+ #
258
+ # Installed skills:
259
+ #
260
+ # [1] repo-git
261
+ # Provides multi-repository version control capabilities.
262
+ #
263
+ # [2] pdf-processing
264
+ # Extract text and tables from PDF files.
265
+ #
266
+ # Enter the numbers of the skills to uninstall (separated by spaces, e.g., "1 3 5"):
267
+ # Press Enter to cancel:
268
+ # Your choice: 1 2
269
+ #
270
+ # Selected skills: repo-git, pdf-processing
271
+ ```
272
+
273
+ ## Project Structure
274
+
275
+ ```
276
+ src/
277
+ ├── cli.ts # CLI entry point
278
+ ├── installer/
279
+ │ └── install-service.ts # Core installation logic
280
+ ├── download/
281
+ │ └── download-manager.ts # Download manager (wiki + public)
282
+ ├── archive/
283
+ │ └── archive-extractor.ts # Archive extraction (ZIP, tar.gz)
284
+ ├── source/
285
+ │ └── source-detector.ts # Source type detection
286
+ ├── skills/
287
+ │ ├── skills-manager.ts # List/uninstall operations
288
+ │ └── skill-validator.ts # SKILL.md validation
289
+ ├── auth/ # Wiki authentication
290
+ ├── http/ # HTTP client with cookies
291
+ └── core/ # Error handling & logging
292
+ ```
293
+
294
+ ## License
295
+
296
+ MIT
@@ -0,0 +1,29 @@
1
+ export type ArchiveType = 'zip' | 'tar' | 'tar.gz' | 'tgz' | 'unknown';
2
+ export declare class ArchiveExtractor {
3
+ private readonly logger;
4
+ /**
5
+ * Detect the archive type from a filename
6
+ */
7
+ detectArchiveType(filename: string): ArchiveType;
8
+ /**
9
+ * Extract an archive to a directory
10
+ * @param archivePath Path to the archive file
11
+ * @param targetDir Directory to extract to
12
+ * @throws WikiError if extraction fails
13
+ */
14
+ extract(archivePath: string, targetDir: string): Promise<void>;
15
+ /**
16
+ * Extract a ZIP archive
17
+ */
18
+ private extractZip;
19
+ /**
20
+ * Extract a tar/tar.gz archive
21
+ */
22
+ private extractTar;
23
+ /**
24
+ * Get the root directory name from an archive
25
+ * Useful for detecting if the archive contains a single root directory
26
+ */
27
+ getRootDirectoryName(archivePath: string): Promise<string | null>;
28
+ private getZipRootDir;
29
+ }
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ArchiveExtractor = void 0;
7
+ const promises_1 = require("fs/promises");
8
+ const path_1 = require("path");
9
+ const tar_1 = require("tar");
10
+ const adm_zip_1 = __importDefault(require("adm-zip"));
11
+ const logger_1 = require("../core/logger");
12
+ const errors_1 = require("../core/errors");
13
+ class ArchiveExtractor {
14
+ constructor() {
15
+ this.logger = new logger_1.Logger('ArchiveExtractor');
16
+ }
17
+ /**
18
+ * Detect the archive type from a filename
19
+ */
20
+ detectArchiveType(filename) {
21
+ const ext = (0, path_1.extname)(filename).toLowerCase();
22
+ const base = (0, path_1.basename)(filename, ext).toLowerCase();
23
+ // Check for compound extensions
24
+ if (filename.toLowerCase().endsWith('.tar.gz') || filename.toLowerCase().endsWith('.tgz')) {
25
+ return 'tar.gz';
26
+ }
27
+ if (ext === '.zip') {
28
+ return 'zip';
29
+ }
30
+ if (ext === '.tar' || base.endsWith('.tar')) {
31
+ return 'tar';
32
+ }
33
+ return 'unknown';
34
+ }
35
+ /**
36
+ * Extract an archive to a directory
37
+ * @param archivePath Path to the archive file
38
+ * @param targetDir Directory to extract to
39
+ * @throws WikiError if extraction fails
40
+ */
41
+ async extract(archivePath, targetDir) {
42
+ const archiveType = this.detectArchiveType(archivePath);
43
+ this.logger.info(`Extracting ${archiveType} archive: ${archivePath} -> ${targetDir}`);
44
+ switch (archiveType) {
45
+ case 'zip':
46
+ await this.extractZip(archivePath, targetDir);
47
+ break;
48
+ case 'tar':
49
+ case 'tar.gz':
50
+ case 'tgz':
51
+ await this.extractTar(archivePath, targetDir);
52
+ break;
53
+ default:
54
+ throw new errors_1.WikiError(errors_1.ErrorType.EXTRACTION_FAILED, `Unknown archive type: ${archivePath}. Supported formats: .zip, .tar, .tar.gz`);
55
+ }
56
+ this.logger.info(`Extraction complete: ${targetDir}`);
57
+ }
58
+ /**
59
+ * Extract a ZIP archive
60
+ */
61
+ async extractZip(archivePath, targetDir) {
62
+ try {
63
+ const zip = new adm_zip_1.default(archivePath);
64
+ zip.extractAllTo(targetDir, true);
65
+ }
66
+ catch (error) {
67
+ throw new errors_1.WikiError(errors_1.ErrorType.EXTRACTION_FAILED, `Failed to extract ZIP archive: ${archivePath}`, error);
68
+ }
69
+ }
70
+ /**
71
+ * Extract a tar/tar.gz archive
72
+ */
73
+ async extractTar(archivePath, targetDir) {
74
+ try {
75
+ await (0, promises_1.mkdir)(targetDir, { recursive: true });
76
+ await (0, tar_1.extract)({
77
+ file: archivePath,
78
+ cwd: targetDir,
79
+ strip: 1 // Strip the root directory if present
80
+ });
81
+ }
82
+ catch (error) {
83
+ throw new errors_1.WikiError(errors_1.ErrorType.EXTRACTION_FAILED, `Failed to extract tar archive: ${archivePath}`, error);
84
+ }
85
+ }
86
+ /**
87
+ * Get the root directory name from an archive
88
+ * Useful for detecting if the archive contains a single root directory
89
+ */
90
+ async getRootDirectoryName(archivePath) {
91
+ const archiveType = this.detectArchiveType(archivePath);
92
+ if (archiveType === 'zip') {
93
+ return this.getZipRootDir(archivePath);
94
+ }
95
+ if (archiveType === 'tar' || archiveType === 'tar.gz' || archiveType === 'tgz') {
96
+ // For tar archives, we'll just return null and let the extraction handle it
97
+ return null;
98
+ }
99
+ return null;
100
+ }
101
+ getZipRootDir(archivePath) {
102
+ try {
103
+ const zip = new adm_zip_1.default(archivePath);
104
+ const entries = zip.getEntries();
105
+ if (entries.length === 0) {
106
+ return null;
107
+ }
108
+ // Get the first entry's path
109
+ const firstEntry = entries[0].entryName;
110
+ // Check if all entries share a common root directory
111
+ const parts = firstEntry.split('/');
112
+ if (parts.length > 1) {
113
+ return parts[0];
114
+ }
115
+ return null;
116
+ }
117
+ catch (error) {
118
+ this.logger.warn(`Failed to determine root directory: ${error}`);
119
+ return null;
120
+ }
121
+ }
122
+ }
123
+ exports.ArchiveExtractor = ArchiveExtractor;
@@ -0,0 +1,21 @@
1
+ import { HttpClient } from '../http/http-client';
2
+ export interface AuthConfig {
3
+ baseUrl: string;
4
+ username: string;
5
+ password: string;
6
+ }
7
+ export declare class AuthService {
8
+ private readonly http;
9
+ private readonly config;
10
+ private readonly logger;
11
+ private session;
12
+ private authInFlight;
13
+ constructor(http: HttpClient, config: AuthConfig);
14
+ ensureAuthenticated(): Promise<void>;
15
+ private authenticate;
16
+ private getLoginFormData;
17
+ private performLogin;
18
+ private checkLoginSuccess;
19
+ private isSessionValid;
20
+ clearSession(): void;
21
+ }