@localskills/cli 0.1.0 → 0.1.3
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/README.md +148 -0
- package/dist/index.js +463 -60
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @localskills/cli
|
|
2
|
+
|
|
3
|
+
CLI for [localskills.sh](https://localskills.sh) — install, manage, and publish agent skills for AI coding tools.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @localskills/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Supported platforms
|
|
12
|
+
|
|
13
|
+
| Platform | ID | Global | Project | Method |
|
|
14
|
+
|---|---|---|---|---|
|
|
15
|
+
| Cursor | `cursor` | `~/.cursor/rules/` | `.cursor/rules/` | symlink |
|
|
16
|
+
| Claude Code | `claude` | `~/.claude/skills/` | `.claude/skills/` | symlink |
|
|
17
|
+
| Windsurf | `windsurf` | `~/.codeium/windsurf/memories/` | `.windsurf/rules/` | section (global), symlink (project) |
|
|
18
|
+
| Cline | `cline` | — | `.clinerules/` | symlink |
|
|
19
|
+
| GitHub Copilot | `copilot` | — | `.github/copilot-instructions.md` | section |
|
|
20
|
+
| Codex CLI | `codex` | `~/.codex/AGENTS.md` | `./AGENTS.md` | section |
|
|
21
|
+
| OpenCode | `opencode` | `~/.config/opencode/rules/` | `.opencode/rules/` | symlink |
|
|
22
|
+
| Aider | `aider` | — | `.aider/skills/` | symlink |
|
|
23
|
+
|
|
24
|
+
The CLI auto-detects which platforms are installed and pre-selects them during interactive flows.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
### `localskills login`
|
|
29
|
+
|
|
30
|
+
Authenticate with localskills.sh by pasting your API token.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
localskills login
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `localskills logout`
|
|
37
|
+
|
|
38
|
+
Clear stored credentials.
|
|
39
|
+
|
|
40
|
+
### `localskills whoami`
|
|
41
|
+
|
|
42
|
+
Show the current authenticated user.
|
|
43
|
+
|
|
44
|
+
### `localskills install [slug]`
|
|
45
|
+
|
|
46
|
+
Install a skill to one or more platforms. Without a slug, launches an interactive picker.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Interactive — prompts for skill, platforms, scope, and method
|
|
50
|
+
localskills install
|
|
51
|
+
|
|
52
|
+
# Direct — skip prompts
|
|
53
|
+
localskills install my-skill -t cursor,claude -g --symlink
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Options:**
|
|
57
|
+
|
|
58
|
+
| Flag | Description |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `-t, --target <targets...>` | Target platforms (comma-separated) |
|
|
61
|
+
| `-g, --global` | Install globally (home directory) |
|
|
62
|
+
| `-p, --project [dir]` | Install in project directory |
|
|
63
|
+
| `--symlink` | Symlink from cache (default) |
|
|
64
|
+
| `--copy` | Copy file instead of symlinking |
|
|
65
|
+
|
|
66
|
+
### `localskills uninstall [slug]`
|
|
67
|
+
|
|
68
|
+
Remove an installed skill from all platforms. Without a slug, launches an interactive picker.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
localskills uninstall my-skill --purge
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Options:**
|
|
75
|
+
|
|
76
|
+
| Flag | Description |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `--purge` | Also remove from local cache |
|
|
79
|
+
|
|
80
|
+
### `localskills pull [slug]`
|
|
81
|
+
|
|
82
|
+
Update installed skills by fetching the latest content from localskills.sh. Without a slug, updates all installed skills.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Update everything
|
|
86
|
+
localskills pull
|
|
87
|
+
|
|
88
|
+
# Update one skill
|
|
89
|
+
localskills pull my-skill
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Symlinked installations update automatically when the cache is refreshed. Copied and section-based installations are re-written in place.
|
|
93
|
+
|
|
94
|
+
### `localskills list`
|
|
95
|
+
|
|
96
|
+
List available skills from localskills.sh.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
localskills list
|
|
100
|
+
localskills list --public
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `localskills publish [file]`
|
|
104
|
+
|
|
105
|
+
Publish a local skill file to localskills.sh. Without a file argument, scans your project for unpublished skill files across all supported platform directories.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Scan and publish interactively
|
|
109
|
+
localskills publish
|
|
110
|
+
|
|
111
|
+
# Publish a specific file
|
|
112
|
+
localskills publish .cursor/rules/my-skill.mdc -n "My Skill" --visibility public
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Options:**
|
|
116
|
+
|
|
117
|
+
| Flag | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `-t, --team <id>` | Team ID or slug |
|
|
120
|
+
| `-n, --name <name>` | Skill name |
|
|
121
|
+
| `--visibility <level>` | `private`, `public`, or `unlisted` (default: `private`) |
|
|
122
|
+
| `-m, --message <message>` | Version message |
|
|
123
|
+
|
|
124
|
+
## Installation methods
|
|
125
|
+
|
|
126
|
+
- **Symlink** (default) — links the skill file from `~/.localskills/cache/` to the target location. Updates propagate automatically via `localskills pull`.
|
|
127
|
+
- **Copy** — writes a standalone copy. Independent of cache but requires re-install to update.
|
|
128
|
+
- **Section** — for single-file platforms (Copilot, Codex, Windsurf global), injects the skill into a shared file between `<!-- localskills:start:slug -->` / `<!-- localskills:end:slug -->` markers.
|
|
129
|
+
|
|
130
|
+
## Configuration
|
|
131
|
+
|
|
132
|
+
Config and cache are stored at `~/.localskills/`:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
~/.localskills/
|
|
136
|
+
config.json # Auth token, installed skills, preferences
|
|
137
|
+
cache/
|
|
138
|
+
my-skill/
|
|
139
|
+
raw.md # Original content
|
|
140
|
+
meta.json # Hash, version, metadata
|
|
141
|
+
cursor.mdc # Platform-specific transformed files
|
|
142
|
+
claude/
|
|
143
|
+
SKILL.md
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -161,7 +161,7 @@ var require_src = __commonJS({
|
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
// src/index.ts
|
|
164
|
-
import { Command as
|
|
164
|
+
import { Command as Command7 } from "commander";
|
|
165
165
|
|
|
166
166
|
// src/commands/auth.ts
|
|
167
167
|
import { Command } from "commander";
|
|
@@ -314,10 +314,10 @@ var loginCommand = new Command("login").description("Log in to localskills.sh").
|
|
|
314
314
|
input: process.stdin,
|
|
315
315
|
output: process.stdout
|
|
316
316
|
});
|
|
317
|
-
const token = await new Promise((
|
|
317
|
+
const token = await new Promise((resolve4) => {
|
|
318
318
|
rl.question("Paste your API token: ", (answer) => {
|
|
319
319
|
rl.close();
|
|
320
|
-
|
|
320
|
+
resolve4(answer.trim());
|
|
321
321
|
});
|
|
322
322
|
});
|
|
323
323
|
if (!token) {
|
|
@@ -345,6 +345,7 @@ var whoamiCommand = new Command("whoami").description("Show current user info").
|
|
|
345
345
|
import { Command as Command2 } from "commander";
|
|
346
346
|
|
|
347
347
|
// ../../node_modules/.pnpm/@clack+core@1.0.1/node_modules/@clack/core/dist/index.mjs
|
|
348
|
+
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
348
349
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
349
350
|
import { stdout as R, stdin as q } from "process";
|
|
350
351
|
import * as k from "readline";
|
|
@@ -769,9 +770,28 @@ var Wt = class extends x {
|
|
|
769
770
|
});
|
|
770
771
|
}
|
|
771
772
|
};
|
|
773
|
+
var $t = class extends x {
|
|
774
|
+
get userInputWithCursor() {
|
|
775
|
+
if (this.state === "submit") return this.userInput;
|
|
776
|
+
const e2 = this.userInput;
|
|
777
|
+
if (this.cursor >= e2.length) return `${this.userInput}\u2588`;
|
|
778
|
+
const s = e2.slice(0, this.cursor), [i, ...r] = e2.slice(this.cursor);
|
|
779
|
+
return `${s}${import_picocolors.default.inverse(i)}${r.join("")}`;
|
|
780
|
+
}
|
|
781
|
+
get cursor() {
|
|
782
|
+
return this._cursor;
|
|
783
|
+
}
|
|
784
|
+
constructor(e2) {
|
|
785
|
+
super({ ...e2, initialUserInput: e2.initialUserInput ?? e2.initialValue }), this.on("userInput", (s) => {
|
|
786
|
+
this._setValue(s);
|
|
787
|
+
}), this.on("finalize", () => {
|
|
788
|
+
this.value || (this.value = e2.defaultValue), this.value === void 0 && (this.value = "");
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
772
792
|
|
|
773
793
|
// ../../node_modules/.pnpm/@clack+prompts@1.0.1/node_modules/@clack/prompts/dist/index.mjs
|
|
774
|
-
var
|
|
794
|
+
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
775
795
|
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
776
796
|
import N2 from "process";
|
|
777
797
|
import { readdirSync as de, existsSync as $e, lstatSync as xt2 } from "fs";
|
|
@@ -812,26 +832,26 @@ var W2 = (t) => {
|
|
|
812
832
|
switch (t) {
|
|
813
833
|
case "initial":
|
|
814
834
|
case "active":
|
|
815
|
-
return
|
|
835
|
+
return import_picocolors2.default.cyan(Rt);
|
|
816
836
|
case "cancel":
|
|
817
|
-
return
|
|
837
|
+
return import_picocolors2.default.red(dt2);
|
|
818
838
|
case "error":
|
|
819
|
-
return
|
|
839
|
+
return import_picocolors2.default.yellow($t2);
|
|
820
840
|
case "submit":
|
|
821
|
-
return
|
|
841
|
+
return import_picocolors2.default.green(V);
|
|
822
842
|
}
|
|
823
843
|
};
|
|
824
844
|
var vt2 = (t) => {
|
|
825
845
|
switch (t) {
|
|
826
846
|
case "initial":
|
|
827
847
|
case "active":
|
|
828
|
-
return
|
|
848
|
+
return import_picocolors2.default.cyan(d);
|
|
829
849
|
case "cancel":
|
|
830
|
-
return
|
|
850
|
+
return import_picocolors2.default.red(d);
|
|
831
851
|
case "error":
|
|
832
|
-
return
|
|
852
|
+
return import_picocolors2.default.yellow(d);
|
|
833
853
|
case "submit":
|
|
834
|
-
return
|
|
854
|
+
return import_picocolors2.default.green(d);
|
|
835
855
|
}
|
|
836
856
|
};
|
|
837
857
|
var pe = (t) => t === 161 || t === 164 || t === 167 || t === 168 || t === 170 || t === 173 || t === 174 || t >= 176 && t <= 180 || t >= 182 && t <= 186 || t >= 188 && t <= 191 || t === 198 || t === 208 || t === 215 || t === 216 || t >= 222 && t <= 225 || t === 230 || t >= 232 && t <= 234 || t === 236 || t === 237 || t === 240 || t === 242 || t === 243 || t >= 247 && t <= 250 || t === 252 || t === 254 || t === 257 || t === 273 || t === 275 || t === 283 || t === 294 || t === 295 || t === 299 || t >= 305 && t <= 307 || t === 312 || t >= 319 && t <= 322 || t === 324 || t >= 328 && t <= 331 || t === 333 || t === 338 || t === 339 || t === 358 || t === 359 || t === 363 || t === 462 || t === 464 || t === 466 || t === 468 || t === 470 || t === 472 || t === 474 || t === 476 || t === 593 || t === 609 || t === 708 || t === 711 || t >= 713 && t <= 715 || t === 717 || t === 720 || t >= 728 && t <= 731 || t === 733 || t === 735 || t >= 768 && t <= 879 || t >= 913 && t <= 929 || t >= 931 && t <= 937 || t >= 945 && t <= 961 || t >= 963 && t <= 969 || t === 1025 || t >= 1040 && t <= 1103 || t === 1105 || t === 8208 || t >= 8211 && t <= 8214 || t === 8216 || t === 8217 || t === 8220 || t === 8221 || t >= 8224 && t <= 8226 || t >= 8228 && t <= 8231 || t === 8240 || t === 8242 || t === 8243 || t === 8245 || t === 8251 || t === 8254 || t === 8308 || t === 8319 || t >= 8321 && t <= 8324 || t === 8364 || t === 8451 || t === 8453 || t === 8457 || t === 8467 || t === 8470 || t === 8481 || t === 8482 || t === 8486 || t === 8491 || t === 8531 || t === 8532 || t >= 8539 && t <= 8542 || t >= 8544 && t <= 8555 || t >= 8560 && t <= 8569 || t === 8585 || t >= 8592 && t <= 8601 || t === 8632 || t === 8633 || t === 8658 || t === 8660 || t === 8679 || t === 8704 || t === 8706 || t === 8707 || t === 8711 || t === 8712 || t === 8715 || t === 8719 || t === 8721 || t === 8725 || t === 8730 || t >= 8733 && t <= 8736 || t === 8739 || t === 8741 || t >= 8743 && t <= 8748 || t === 8750 || t >= 8756 && t <= 8759 || t === 8764 || t === 8765 || t === 8776 || t === 8780 || t === 8786 || t === 8800 || t === 8801 || t >= 8804 && t <= 8807 || t === 8810 || t === 8811 || t === 8814 || t === 8815 || t === 8834 || t === 8835 || t === 8838 || t === 8839 || t === 8853 || t === 8857 || t === 8869 || t === 8895 || t === 8978 || t >= 9312 && t <= 9449 || t >= 9451 && t <= 9547 || t >= 9552 && t <= 9587 || t >= 9600 && t <= 9615 || t >= 9618 && t <= 9621 || t === 9632 || t === 9633 || t >= 9635 && t <= 9641 || t === 9650 || t === 9651 || t === 9654 || t === 9655 || t === 9660 || t === 9661 || t === 9664 || t === 9665 || t >= 9670 && t <= 9672 || t === 9675 || t >= 9678 && t <= 9681 || t >= 9698 && t <= 9701 || t === 9711 || t === 9733 || t === 9734 || t === 9737 || t === 9742 || t === 9743 || t === 9756 || t === 9758 || t === 9792 || t === 9794 || t === 9824 || t === 9825 || t >= 9827 && t <= 9829 || t >= 9831 && t <= 9834 || t === 9836 || t === 9837 || t === 9839 || t === 9886 || t === 9887 || t === 9919 || t >= 9926 && t <= 9933 || t >= 9935 && t <= 9939 || t >= 9941 && t <= 9953 || t === 9955 || t === 9960 || t === 9961 || t >= 9963 && t <= 9969 || t === 9972 || t >= 9974 && t <= 9977 || t === 9979 || t === 9980 || t === 9982 || t === 9983 || t === 10045 || t >= 10102 && t <= 10111 || t >= 11094 && t <= 11097 || t >= 12872 && t <= 12879 || t >= 57344 && t <= 63743 || t >= 65024 && t <= 65039 || t === 65533 || t >= 127232 && t <= 127242 || t >= 127248 && t <= 127277 || t >= 127280 && t <= 127337 || t >= 127344 && t <= 127373 || t === 127375 || t === 127376 || t >= 127387 && t <= 127404 || t >= 917760 && t <= 917999 || t >= 983040 && t <= 1048573 || t >= 1048576 && t <= 1114109;
|
|
@@ -851,13 +871,13 @@ var jt = (t, r = {}, s = {}) => {
|
|
|
851
871
|
if (B2 > I2 || m >= h && m > $) {
|
|
852
872
|
const _2 = t.slice(I2, B2) || t.slice($, m);
|
|
853
873
|
y2 = 0;
|
|
854
|
-
for (const
|
|
855
|
-
const T2 =
|
|
874
|
+
for (const D2 of _2.replaceAll(Fe, "")) {
|
|
875
|
+
const T2 = D2.codePointAt(0) || 0;
|
|
856
876
|
if (ge(T2) ? w = F : fe(T2) ? w = E : c !== p && pe(T2) ? w = c : w = p, A + w > S2 && (v = Math.min(v, Math.max(I2, $) + y2)), A + w > i) {
|
|
857
877
|
f = true;
|
|
858
878
|
break t;
|
|
859
879
|
}
|
|
860
|
-
y2 +=
|
|
880
|
+
y2 += D2.length, A += w;
|
|
861
881
|
}
|
|
862
882
|
I2 = B2 = 0;
|
|
863
883
|
}
|
|
@@ -1009,7 +1029,7 @@ var be = (t, r, s, i, a) => {
|
|
|
1009
1029
|
return { lineCount: o, removals: u };
|
|
1010
1030
|
};
|
|
1011
1031
|
var X2 = (t) => {
|
|
1012
|
-
const { cursor: r, options: s, style: i } = t, a = t.output ?? process.stdout, o = rt(a), u = t.columnPadding ?? 0, l = t.rowPadding ?? 4, n = o - u, c = nt(a), g =
|
|
1032
|
+
const { cursor: r, options: s, style: i } = t, a = t.output ?? process.stdout, o = rt(a), u = t.columnPadding ?? 0, l = t.rowPadding ?? 4, n = o - u, c = nt(a), g = import_picocolors2.default.dim("..."), F = t.maxItems ?? Number.POSITIVE_INFINITY, p = Math.max(c - l, 0), E = Math.max(Math.min(F, p), 5);
|
|
1013
1033
|
let $ = 0;
|
|
1014
1034
|
r >= E - 3 && ($ = Math.max(Math.min(r - E + 3, s.length - E), 0));
|
|
1015
1035
|
let m = E < s.length && $ > 0, h = E < s.length && $ + E < s.length;
|
|
@@ -1024,15 +1044,15 @@ var X2 = (t) => {
|
|
|
1024
1044
|
}
|
|
1025
1045
|
if (v > p) {
|
|
1026
1046
|
let A = 0, w = 0, _2 = v;
|
|
1027
|
-
const
|
|
1028
|
-
m ? ({ lineCount: _2, removals: A } = T2(0,
|
|
1047
|
+
const D2 = r - S2, T2 = (Y, L2) => be(f, _2, Y, L2, p);
|
|
1048
|
+
m ? ({ lineCount: _2, removals: A } = T2(0, D2), _2 > p && ({ lineCount: _2, removals: w } = T2(D2 + 1, f.length))) : ({ lineCount: _2, removals: w } = T2(D2 + 1, f.length), _2 > p && ({ lineCount: _2, removals: A } = T2(0, D2))), A > 0 && (m = true, f.splice(0, A)), w > 0 && (h = true, f.splice(f.length - w, w));
|
|
1029
1049
|
}
|
|
1030
1050
|
const B2 = [];
|
|
1031
1051
|
m && B2.push(g);
|
|
1032
1052
|
for (const A of f) for (const w of A) B2.push(w);
|
|
1033
1053
|
return h && B2.push(g), B2;
|
|
1034
1054
|
};
|
|
1035
|
-
var R2 = { message: (t = [], { symbol: r =
|
|
1055
|
+
var R2 = { message: (t = [], { symbol: r = import_picocolors2.default.gray(d), secondarySymbol: s = import_picocolors2.default.gray(d), output: i = process.stdout, spacing: a = 1, withGuide: o } = {}) => {
|
|
1036
1056
|
const u = [], l = o ?? _.withGuide, n = l ? s : "", c = l ? `${r} ` : "", g = l ? `${s} ` : "";
|
|
1037
1057
|
for (let p = 0; p < a; p++) u.push(n);
|
|
1038
1058
|
const F = Array.isArray(t) ? t : t.split(`
|
|
@@ -1046,30 +1066,30 @@ var R2 = { message: (t = [], { symbol: r = import_picocolors.default.gray(d), se
|
|
|
1046
1066
|
`)}
|
|
1047
1067
|
`);
|
|
1048
1068
|
}, info: (t, r) => {
|
|
1049
|
-
R2.message(t, { ...r, symbol:
|
|
1069
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.blue(ft2) });
|
|
1050
1070
|
}, success: (t, r) => {
|
|
1051
|
-
R2.message(t, { ...r, symbol:
|
|
1071
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.green(Ft2) });
|
|
1052
1072
|
}, step: (t, r) => {
|
|
1053
|
-
R2.message(t, { ...r, symbol:
|
|
1073
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.green(V) });
|
|
1054
1074
|
}, warn: (t, r) => {
|
|
1055
|
-
R2.message(t, { ...r, symbol:
|
|
1075
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.yellow(yt2) });
|
|
1056
1076
|
}, warning: (t, r) => {
|
|
1057
1077
|
R2.warn(t, r);
|
|
1058
1078
|
}, error: (t, r) => {
|
|
1059
|
-
R2.message(t, { ...r, symbol:
|
|
1079
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.red(Et2) });
|
|
1060
1080
|
} };
|
|
1061
1081
|
var Ne = (t = "", r) => {
|
|
1062
|
-
(r?.output ?? process.stdout).write(`${
|
|
1082
|
+
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(x2)} ${import_picocolors2.default.red(t)}
|
|
1063
1083
|
|
|
1064
1084
|
`);
|
|
1065
1085
|
};
|
|
1066
1086
|
var We = (t = "", r) => {
|
|
1067
|
-
(r?.output ?? process.stdout).write(`${
|
|
1087
|
+
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(ht2)} ${t}
|
|
1068
1088
|
`);
|
|
1069
1089
|
};
|
|
1070
1090
|
var Le = (t = "", r) => {
|
|
1071
|
-
(r?.output ?? process.stdout).write(`${
|
|
1072
|
-
${
|
|
1091
|
+
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(d)}
|
|
1092
|
+
${import_picocolors2.default.gray(x2)} ${t}
|
|
1073
1093
|
|
|
1074
1094
|
`);
|
|
1075
1095
|
};
|
|
@@ -1079,13 +1099,13 @@ var Z2 = (t, r) => t.split(`
|
|
|
1079
1099
|
var je = (t) => {
|
|
1080
1100
|
const r = (i, a) => {
|
|
1081
1101
|
const o = i.label ?? String(i.value);
|
|
1082
|
-
return a === "disabled" ? `${
|
|
1102
|
+
return a === "disabled" ? `${import_picocolors2.default.gray(q2)} ${Z2(o, (u) => import_picocolors2.default.strikethrough(import_picocolors2.default.gray(u)))}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint ?? "disabled"})`)}` : ""}` : a === "active" ? `${import_picocolors2.default.cyan(st2)} ${o}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint})`)}` : ""}` : a === "selected" ? `${import_picocolors2.default.green(U2)} ${Z2(o, import_picocolors2.default.dim)}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint})`)}` : ""}` : a === "cancelled" ? `${Z2(o, (u) => import_picocolors2.default.strikethrough(import_picocolors2.default.dim(u)))}` : a === "active-selected" ? `${import_picocolors2.default.green(U2)} ${o}${i.hint ? ` ${import_picocolors2.default.dim(`(${i.hint})`)}` : ""}` : a === "submitted" ? `${Z2(o, import_picocolors2.default.dim)}` : `${import_picocolors2.default.dim(q2)} ${Z2(o, import_picocolors2.default.dim)}`;
|
|
1083
1103
|
}, s = t.required ?? true;
|
|
1084
1104
|
return new Lt({ options: t.options, signal: t.signal, input: t.input, output: t.output, initialValues: t.initialValues, required: s, cursorAt: t.cursorAt, validate(i) {
|
|
1085
1105
|
if (s && (i === void 0 || i.length === 0)) return `Please select at least one option.
|
|
1086
|
-
${
|
|
1106
|
+
${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
|
|
1087
1107
|
}, render() {
|
|
1088
|
-
const i = xt(t.output, t.message, `${vt2(this.state)} `, `${W2(this.state)} `), a = `${
|
|
1108
|
+
const i = xt(t.output, t.message, `${vt2(this.state)} `, `${W2(this.state)} `), a = `${import_picocolors2.default.gray(d)}
|
|
1089
1109
|
${i}
|
|
1090
1110
|
`, o = this.value ?? [], u = (l, n) => {
|
|
1091
1111
|
if (l.disabled) return r(l, "disabled");
|
|
@@ -1094,19 +1114,19 @@ ${i}
|
|
|
1094
1114
|
};
|
|
1095
1115
|
switch (this.state) {
|
|
1096
1116
|
case "submit": {
|
|
1097
|
-
const l = this.options.filter(({ value: c }) => o.includes(c)).map((c) => r(c, "submitted")).join(
|
|
1117
|
+
const l = this.options.filter(({ value: c }) => o.includes(c)).map((c) => r(c, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none"), n = xt(t.output, l, `${import_picocolors2.default.gray(d)} `);
|
|
1098
1118
|
return `${a}${n}`;
|
|
1099
1119
|
}
|
|
1100
1120
|
case "cancel": {
|
|
1101
|
-
const l = this.options.filter(({ value: c }) => o.includes(c)).map((c) => r(c, "cancelled")).join(
|
|
1102
|
-
if (l.trim() === "") return `${a}${
|
|
1103
|
-
const n = xt(t.output, l, `${
|
|
1121
|
+
const l = this.options.filter(({ value: c }) => o.includes(c)).map((c) => r(c, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
1122
|
+
if (l.trim() === "") return `${a}${import_picocolors2.default.gray(d)}`;
|
|
1123
|
+
const n = xt(t.output, l, `${import_picocolors2.default.gray(d)} `);
|
|
1104
1124
|
return `${a}${n}
|
|
1105
|
-
${
|
|
1125
|
+
${import_picocolors2.default.gray(d)}`;
|
|
1106
1126
|
}
|
|
1107
1127
|
case "error": {
|
|
1108
|
-
const l = `${
|
|
1109
|
-
`).map((F, p) => p === 0 ? `${
|
|
1128
|
+
const l = `${import_picocolors2.default.yellow(d)} `, n = this.error.split(`
|
|
1129
|
+
`).map((F, p) => p === 0 ? `${import_picocolors2.default.yellow(x2)} ${import_picocolors2.default.yellow(F)}` : ` ${F}`).join(`
|
|
1110
1130
|
`), c = a.split(`
|
|
1111
1131
|
`).length, g = n.split(`
|
|
1112
1132
|
`).length + 1;
|
|
@@ -1116,17 +1136,17 @@ ${n}
|
|
|
1116
1136
|
`;
|
|
1117
1137
|
}
|
|
1118
1138
|
default: {
|
|
1119
|
-
const l = `${
|
|
1139
|
+
const l = `${import_picocolors2.default.cyan(d)} `, n = a.split(`
|
|
1120
1140
|
`).length;
|
|
1121
1141
|
return `${a}${l}${X2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: l.length, rowPadding: n + 2, style: u }).join(`
|
|
1122
1142
|
${l}`)}
|
|
1123
|
-
${
|
|
1143
|
+
${import_picocolors2.default.cyan(x2)}
|
|
1124
1144
|
`;
|
|
1125
1145
|
}
|
|
1126
1146
|
}
|
|
1127
1147
|
} }).prompt();
|
|
1128
1148
|
};
|
|
1129
|
-
var Ke =
|
|
1149
|
+
var Ke = import_picocolors2.default.magenta;
|
|
1130
1150
|
var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: a, frames: o = et2 ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: u = et2 ? 80 : 120, signal: l, ...n } = {}) => {
|
|
1131
1151
|
const c = ct2();
|
|
1132
1152
|
let g, F, p = false, E = false, $ = "", m, h = performance.now();
|
|
@@ -1144,11 +1164,11 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1144
1164
|
const b = J2(m, y2, { hard: true, trim: false }).split(`
|
|
1145
1165
|
`);
|
|
1146
1166
|
b.length > 1 && s.write(import_sisteransi2.cursor.up(b.length - 1)), s.write(import_sisteransi2.cursor.to(0)), s.write(import_sisteransi2.erase.down());
|
|
1147
|
-
}, _2 = (b) => b.replace(/\.+$/, ""),
|
|
1167
|
+
}, _2 = (b) => b.replace(/\.+$/, ""), D2 = (b) => {
|
|
1148
1168
|
const O2 = (performance.now() - b) / 1e3, j2 = Math.floor(O2 / 60), G2 = Math.floor(O2 % 60);
|
|
1149
1169
|
return j2 > 0 ? `[${j2}m ${G2}s]` : `[${G2}s]`;
|
|
1150
1170
|
}, T2 = n.withGuide ?? _.withGuide, Y = (b = "") => {
|
|
1151
|
-
p = true, g = Bt({ output: s }), $ = _2(b), h = performance.now(), T2 && s.write(`${
|
|
1171
|
+
p = true, g = Bt({ output: s }), $ = _2(b), h = performance.now(), T2 && s.write(`${import_picocolors2.default.gray(d)}
|
|
1152
1172
|
`);
|
|
1153
1173
|
let O2 = 0, j2 = 0;
|
|
1154
1174
|
B2(), F = setInterval(() => {
|
|
@@ -1157,7 +1177,7 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1157
1177
|
const G2 = f(o[O2]);
|
|
1158
1178
|
let tt2;
|
|
1159
1179
|
if (c) tt2 = `${G2} ${$}...`;
|
|
1160
|
-
else if (t === "timer") tt2 = `${G2} ${$} ${
|
|
1180
|
+
else if (t === "timer") tt2 = `${G2} ${$} ${D2(h)}`;
|
|
1161
1181
|
else {
|
|
1162
1182
|
const te = ".".repeat(Math.floor(j2)).slice(0, 3);
|
|
1163
1183
|
tt2 = `${G2} ${$}${te}`;
|
|
@@ -1168,8 +1188,8 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1168
1188
|
}, L2 = (b = "", O2 = 0, j2 = false) => {
|
|
1169
1189
|
if (!p) return;
|
|
1170
1190
|
p = false, clearInterval(F), w();
|
|
1171
|
-
const G2 = O2 === 0 ?
|
|
1172
|
-
$ = b ?? $, j2 || (t === "timer" ? s.write(`${G2} ${$} ${
|
|
1191
|
+
const G2 = O2 === 0 ? import_picocolors2.default.green(V) : O2 === 1 ? import_picocolors2.default.red(dt2) : import_picocolors2.default.red($t2);
|
|
1192
|
+
$ = b ?? $, j2 || (t === "timer" ? s.write(`${G2} ${$} ${D2(h)}
|
|
1173
1193
|
`) : s.write(`${G2} ${$}
|
|
1174
1194
|
`)), A(), g();
|
|
1175
1195
|
};
|
|
@@ -1189,33 +1209,33 @@ var Je = (t) => {
|
|
|
1189
1209
|
const a = s.label ?? String(s.value);
|
|
1190
1210
|
switch (i) {
|
|
1191
1211
|
case "disabled":
|
|
1192
|
-
return `${
|
|
1212
|
+
return `${import_picocolors2.default.gray(H2)} ${lt2(a, import_picocolors2.default.gray)}${s.hint ? ` ${import_picocolors2.default.dim(`(${s.hint ?? "disabled"})`)}` : ""}`;
|
|
1193
1213
|
case "selected":
|
|
1194
|
-
return `${lt2(a,
|
|
1214
|
+
return `${lt2(a, import_picocolors2.default.dim)}`;
|
|
1195
1215
|
case "active":
|
|
1196
|
-
return `${
|
|
1216
|
+
return `${import_picocolors2.default.green(Q2)} ${a}${s.hint ? ` ${import_picocolors2.default.dim(`(${s.hint})`)}` : ""}`;
|
|
1197
1217
|
case "cancelled":
|
|
1198
|
-
return `${lt2(a, (o) =>
|
|
1218
|
+
return `${lt2(a, (o) => import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o)))}`;
|
|
1199
1219
|
default:
|
|
1200
|
-
return `${
|
|
1220
|
+
return `${import_picocolors2.default.dim(H2)} ${lt2(a, import_picocolors2.default.dim)}`;
|
|
1201
1221
|
}
|
|
1202
1222
|
};
|
|
1203
1223
|
return new Wt({ options: t.options, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue, render() {
|
|
1204
|
-
const s = t.withGuide ?? _.withGuide, i = `${W2(this.state)} `, a = `${vt2(this.state)} `, o = xt(t.output, t.message, a, i), u = `${s ? `${
|
|
1224
|
+
const s = t.withGuide ?? _.withGuide, i = `${W2(this.state)} `, a = `${vt2(this.state)} `, o = xt(t.output, t.message, a, i), u = `${s ? `${import_picocolors2.default.gray(d)}
|
|
1205
1225
|
` : ""}${o}
|
|
1206
1226
|
`;
|
|
1207
1227
|
switch (this.state) {
|
|
1208
1228
|
case "submit": {
|
|
1209
|
-
const l = s ? `${
|
|
1229
|
+
const l = s ? `${import_picocolors2.default.gray(d)} ` : "", n = xt(t.output, r(this.options[this.cursor], "selected"), l);
|
|
1210
1230
|
return `${u}${n}`;
|
|
1211
1231
|
}
|
|
1212
1232
|
case "cancel": {
|
|
1213
|
-
const l = s ? `${
|
|
1233
|
+
const l = s ? `${import_picocolors2.default.gray(d)} ` : "", n = xt(t.output, r(this.options[this.cursor], "cancelled"), l);
|
|
1214
1234
|
return `${u}${n}${s ? `
|
|
1215
|
-
${
|
|
1235
|
+
${import_picocolors2.default.gray(d)}` : ""}`;
|
|
1216
1236
|
}
|
|
1217
1237
|
default: {
|
|
1218
|
-
const l = s ? `${
|
|
1238
|
+
const l = s ? `${import_picocolors2.default.cyan(d)} ` : "", n = s ? import_picocolors2.default.cyan(x2) : "", c = u.split(`
|
|
1219
1239
|
`).length, g = s ? 2 : 1;
|
|
1220
1240
|
return `${u}${l}${X2({ output: t.output, cursor: this.cursor, options: this.options, maxItems: t.maxItems, columnPadding: l.length, rowPadding: c + g, style: (F, p) => r(F, F.disabled ? "disabled" : p ? "active" : "inactive") }).join(`
|
|
1221
1241
|
${l}`)}
|
|
@@ -1225,7 +1245,36 @@ ${n}
|
|
|
1225
1245
|
}
|
|
1226
1246
|
} }).prompt();
|
|
1227
1247
|
};
|
|
1228
|
-
var Qt = `${
|
|
1248
|
+
var Qt = `${import_picocolors2.default.gray(d)} `;
|
|
1249
|
+
var Ze = (t) => new $t({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, output: t.output, signal: t.signal, input: t.input, render() {
|
|
1250
|
+
const r = t?.withGuide ?? _.withGuide, s = `${`${r ? `${import_picocolors2.default.gray(d)}
|
|
1251
|
+
` : ""}${W2(this.state)} `}${t.message}
|
|
1252
|
+
`, i = t.placeholder ? import_picocolors2.default.inverse(t.placeholder[0]) + import_picocolors2.default.dim(t.placeholder.slice(1)) : import_picocolors2.default.inverse(import_picocolors2.default.hidden("_")), a = this.userInput ? this.userInputWithCursor : i, o = this.value ?? "";
|
|
1253
|
+
switch (this.state) {
|
|
1254
|
+
case "error": {
|
|
1255
|
+
const u = this.error ? ` ${import_picocolors2.default.yellow(this.error)}` : "", l = r ? `${import_picocolors2.default.yellow(d)} ` : "", n = r ? import_picocolors2.default.yellow(x2) : "";
|
|
1256
|
+
return `${s.trim()}
|
|
1257
|
+
${l}${a}
|
|
1258
|
+
${n}${u}
|
|
1259
|
+
`;
|
|
1260
|
+
}
|
|
1261
|
+
case "submit": {
|
|
1262
|
+
const u = o ? ` ${import_picocolors2.default.dim(o)}` : "", l = r ? import_picocolors2.default.gray(d) : "";
|
|
1263
|
+
return `${s}${l}${u}`;
|
|
1264
|
+
}
|
|
1265
|
+
case "cancel": {
|
|
1266
|
+
const u = o ? ` ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o))}` : "", l = r ? import_picocolors2.default.gray(d) : "";
|
|
1267
|
+
return `${s}${l}${u}${o.trim() ? `
|
|
1268
|
+
${l}` : ""}`;
|
|
1269
|
+
}
|
|
1270
|
+
default: {
|
|
1271
|
+
const u = r ? `${import_picocolors2.default.cyan(d)} ` : "", l = r ? import_picocolors2.default.cyan(x2) : "";
|
|
1272
|
+
return `${s}${u}${a}
|
|
1273
|
+
${l}
|
|
1274
|
+
`;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
} }).prompt();
|
|
1229
1278
|
|
|
1230
1279
|
// src/lib/cache.ts
|
|
1231
1280
|
import {
|
|
@@ -1275,6 +1324,11 @@ function toClaudeMD(content, skill) {
|
|
|
1275
1324
|
function toPlainMD(content) {
|
|
1276
1325
|
return content;
|
|
1277
1326
|
}
|
|
1327
|
+
function stripFrontmatter(content) {
|
|
1328
|
+
const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
1329
|
+
if (match) return content.slice(match[0].length);
|
|
1330
|
+
return content;
|
|
1331
|
+
}
|
|
1278
1332
|
|
|
1279
1333
|
// src/lib/symlink.ts
|
|
1280
1334
|
import {
|
|
@@ -1424,8 +1478,8 @@ function uninstall2(installation, _slug) {
|
|
|
1424
1478
|
}
|
|
1425
1479
|
const parentDir = join3(installation.path, "..");
|
|
1426
1480
|
try {
|
|
1427
|
-
const { readdirSync:
|
|
1428
|
-
if (existsSync4(parentDir) &&
|
|
1481
|
+
const { readdirSync: readdirSync3 } = __require("fs");
|
|
1482
|
+
if (existsSync4(parentDir) && readdirSync3(parentDir).length === 0) {
|
|
1429
1483
|
rmSync3(parentDir, { recursive: true });
|
|
1430
1484
|
}
|
|
1431
1485
|
} catch {
|
|
@@ -1492,6 +1546,17 @@ function removeSection(filePath, slug) {
|
|
|
1492
1546
|
writeFileSync4(filePath, result ? result + "\n" : "");
|
|
1493
1547
|
return true;
|
|
1494
1548
|
}
|
|
1549
|
+
function listSections(filePath) {
|
|
1550
|
+
if (!existsSync5(filePath)) return [];
|
|
1551
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1552
|
+
const regex = /<!-- localskills:start:(.+?) -->/g;
|
|
1553
|
+
const slugs = [];
|
|
1554
|
+
let match;
|
|
1555
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1556
|
+
slugs.push(match[1]);
|
|
1557
|
+
}
|
|
1558
|
+
return slugs;
|
|
1559
|
+
}
|
|
1495
1560
|
|
|
1496
1561
|
// src/lib/installers/codex.ts
|
|
1497
1562
|
var descriptor3 = {
|
|
@@ -2386,8 +2451,345 @@ ${transformed}`
|
|
|
2386
2451
|
Le(`Pull complete. ${updated} updated, ${skipped} up to date.`);
|
|
2387
2452
|
});
|
|
2388
2453
|
|
|
2454
|
+
// src/commands/publish.ts
|
|
2455
|
+
import { Command as Command6 } from "commander";
|
|
2456
|
+
import { readFileSync as readFileSync6, existsSync as existsSync14 } from "fs";
|
|
2457
|
+
import { resolve as resolve3, basename as basename2, extname as extname2 } from "path";
|
|
2458
|
+
|
|
2459
|
+
// src/lib/scanner.ts
|
|
2460
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
2461
|
+
import { join as join12, basename, extname } from "path";
|
|
2462
|
+
import { homedir as homedir9 } from "os";
|
|
2463
|
+
import { readlinkSync as readlinkSync2, lstatSync as lstatSync2 } from "fs";
|
|
2464
|
+
function scanForSkills(projectDir) {
|
|
2465
|
+
const home = homedir9();
|
|
2466
|
+
const cwd = projectDir || process.cwd();
|
|
2467
|
+
const results = [];
|
|
2468
|
+
scanDirectory(join12(home, ".cursor", "rules"), ".mdc", "cursor", "global", results);
|
|
2469
|
+
scanDirectory(join12(cwd, ".cursor", "rules"), ".mdc", "cursor", "project", results);
|
|
2470
|
+
scanClaudeSkills(join12(home, ".claude", "skills"), "global", results);
|
|
2471
|
+
scanClaudeSkills(join12(cwd, ".claude", "skills"), "project", results);
|
|
2472
|
+
scanSingleFile(join12(home, ".codex", "AGENTS.md"), "codex", "global", results);
|
|
2473
|
+
scanSingleFile(join12(cwd, "AGENTS.md"), "codex", "project", results);
|
|
2474
|
+
scanSingleFile(
|
|
2475
|
+
join12(home, ".codeium", "windsurf", "memories", "global_rules.md"),
|
|
2476
|
+
"windsurf",
|
|
2477
|
+
"global",
|
|
2478
|
+
results
|
|
2479
|
+
);
|
|
2480
|
+
scanDirectory(join12(cwd, ".windsurf", "rules"), ".md", "windsurf", "project", results);
|
|
2481
|
+
scanDirectory(join12(cwd, ".clinerules"), ".md", "cline", "project", results);
|
|
2482
|
+
scanSingleFile(
|
|
2483
|
+
join12(cwd, ".github", "copilot-instructions.md"),
|
|
2484
|
+
"copilot",
|
|
2485
|
+
"project",
|
|
2486
|
+
results
|
|
2487
|
+
);
|
|
2488
|
+
scanDirectory(join12(home, ".config", "opencode", "rules"), ".md", "opencode", "global", results);
|
|
2489
|
+
scanDirectory(join12(cwd, ".opencode", "rules"), ".md", "opencode", "project", results);
|
|
2490
|
+
scanDirectory(join12(cwd, ".aider", "skills"), ".md", "aider", "project", results);
|
|
2491
|
+
return results;
|
|
2492
|
+
}
|
|
2493
|
+
function filterTracked(detected, config) {
|
|
2494
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
2495
|
+
for (const skill of Object.values(config.installed_skills)) {
|
|
2496
|
+
for (const inst of skill.installations) {
|
|
2497
|
+
trackedPaths.add(inst.path);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
const cacheDir = join12(homedir9(), ".localskills", "cache");
|
|
2501
|
+
return detected.filter((skill) => {
|
|
2502
|
+
if (trackedPaths.has(skill.filePath)) return false;
|
|
2503
|
+
try {
|
|
2504
|
+
const stat = lstatSync2(skill.filePath);
|
|
2505
|
+
if (stat.isSymbolicLink()) {
|
|
2506
|
+
const target = readlinkSync2(skill.filePath);
|
|
2507
|
+
if (target.startsWith(cacheDir)) return false;
|
|
2508
|
+
}
|
|
2509
|
+
} catch {
|
|
2510
|
+
}
|
|
2511
|
+
return true;
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
function slugFromFilename(filename) {
|
|
2515
|
+
return basename(filename, extname(filename));
|
|
2516
|
+
}
|
|
2517
|
+
function nameFromSlug(slug) {
|
|
2518
|
+
return slug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2519
|
+
}
|
|
2520
|
+
function scanDirectory(dir, ext, platform, scope, results) {
|
|
2521
|
+
if (!existsSync13(dir)) return;
|
|
2522
|
+
let entries;
|
|
2523
|
+
try {
|
|
2524
|
+
entries = readdirSync2(dir);
|
|
2525
|
+
} catch {
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
for (const entry of entries) {
|
|
2529
|
+
if (!entry.endsWith(ext)) continue;
|
|
2530
|
+
const filePath = join12(dir, entry);
|
|
2531
|
+
try {
|
|
2532
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
2533
|
+
const content = stripFrontmatter(raw).trim();
|
|
2534
|
+
if (!content) continue;
|
|
2535
|
+
const slug = slugFromFilename(entry);
|
|
2536
|
+
results.push({
|
|
2537
|
+
filePath,
|
|
2538
|
+
platform,
|
|
2539
|
+
scope,
|
|
2540
|
+
suggestedName: nameFromSlug(slug),
|
|
2541
|
+
suggestedSlug: slug,
|
|
2542
|
+
content
|
|
2543
|
+
});
|
|
2544
|
+
} catch {
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
function scanClaudeSkills(skillsDir, scope, results) {
|
|
2549
|
+
if (!existsSync13(skillsDir)) return;
|
|
2550
|
+
let entries;
|
|
2551
|
+
try {
|
|
2552
|
+
entries = readdirSync2(skillsDir);
|
|
2553
|
+
} catch {
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
for (const entry of entries) {
|
|
2557
|
+
const skillFile = join12(skillsDir, entry, "SKILL.md");
|
|
2558
|
+
if (!existsSync13(skillFile)) continue;
|
|
2559
|
+
try {
|
|
2560
|
+
const raw = readFileSync5(skillFile, "utf-8");
|
|
2561
|
+
const content = stripFrontmatter(raw).trim();
|
|
2562
|
+
if (!content) continue;
|
|
2563
|
+
results.push({
|
|
2564
|
+
filePath: skillFile,
|
|
2565
|
+
platform: "claude",
|
|
2566
|
+
scope,
|
|
2567
|
+
suggestedName: nameFromSlug(entry),
|
|
2568
|
+
suggestedSlug: entry,
|
|
2569
|
+
content
|
|
2570
|
+
});
|
|
2571
|
+
} catch {
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
function scanSingleFile(filePath, platform, scope, results) {
|
|
2576
|
+
if (!existsSync13(filePath)) return;
|
|
2577
|
+
let raw;
|
|
2578
|
+
try {
|
|
2579
|
+
raw = readFileSync5(filePath, "utf-8");
|
|
2580
|
+
} catch {
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
const sections = listSections(filePath);
|
|
2584
|
+
if (sections.length > 0) {
|
|
2585
|
+
for (const slug2 of sections) {
|
|
2586
|
+
const startMarker = `<!-- localskills:start:${slug2} -->`;
|
|
2587
|
+
const endMarker = `<!-- localskills:end:${slug2} -->`;
|
|
2588
|
+
const startIdx = raw.indexOf(startMarker);
|
|
2589
|
+
const endIdx = raw.indexOf(endMarker);
|
|
2590
|
+
if (startIdx === -1 || endIdx === -1) continue;
|
|
2591
|
+
const content2 = raw.slice(startIdx + startMarker.length, endIdx).trim();
|
|
2592
|
+
if (!content2) continue;
|
|
2593
|
+
results.push({
|
|
2594
|
+
filePath,
|
|
2595
|
+
platform,
|
|
2596
|
+
scope,
|
|
2597
|
+
suggestedName: nameFromSlug(slug2),
|
|
2598
|
+
suggestedSlug: slug2,
|
|
2599
|
+
content: content2
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
const content = stripFrontmatter(raw).trim();
|
|
2605
|
+
if (!content) return;
|
|
2606
|
+
const slug = slugFromFilename(filePath);
|
|
2607
|
+
results.push({
|
|
2608
|
+
filePath,
|
|
2609
|
+
platform,
|
|
2610
|
+
scope,
|
|
2611
|
+
suggestedName: nameFromSlug(slug),
|
|
2612
|
+
suggestedSlug: slug,
|
|
2613
|
+
content
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/commands/publish.ts
|
|
2618
|
+
var publishCommand = new Command6("publish").description("Publish local skill files to localskills.sh").argument("[file]", "Path to a specific file to publish").option("-t, --team <id>", "Team ID to publish to").option("-n, --name <name>", "Skill name").option(
|
|
2619
|
+
"--visibility <visibility>",
|
|
2620
|
+
"Visibility: public, private, or unlisted",
|
|
2621
|
+
"private"
|
|
2622
|
+
).option("-m, --message <message>", "Version message").action(
|
|
2623
|
+
async (fileArg, opts) => {
|
|
2624
|
+
const client = new ApiClient();
|
|
2625
|
+
if (!client.isAuthenticated()) {
|
|
2626
|
+
console.error("Not authenticated. Run `localskills login` first.");
|
|
2627
|
+
process.exit(1);
|
|
2628
|
+
}
|
|
2629
|
+
const teamsRes = await client.get("/api/tenants");
|
|
2630
|
+
if (!teamsRes.success || !teamsRes.data || teamsRes.data.length === 0) {
|
|
2631
|
+
console.error(
|
|
2632
|
+
"No teams found. Create a team at localskills.sh first."
|
|
2633
|
+
);
|
|
2634
|
+
process.exit(1);
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
const teams = teamsRes.data;
|
|
2638
|
+
if (fileArg) {
|
|
2639
|
+
const filePath = resolve3(fileArg);
|
|
2640
|
+
if (!existsSync14(filePath)) {
|
|
2641
|
+
console.error(`File not found: ${filePath}`);
|
|
2642
|
+
process.exit(1);
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
2645
|
+
const raw = readFileSync6(filePath, "utf-8");
|
|
2646
|
+
const content = stripFrontmatter(raw).trim();
|
|
2647
|
+
if (!content) {
|
|
2648
|
+
console.error("File is empty after stripping frontmatter.");
|
|
2649
|
+
process.exit(1);
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
const defaultSlug = basename2(filePath, extname2(filePath));
|
|
2653
|
+
const defaultName = defaultSlug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2654
|
+
const skillName = opts.name || defaultName;
|
|
2655
|
+
const visibility = validateVisibility(opts.visibility || "private");
|
|
2656
|
+
const tenantId = await resolveTeam(teams, opts.team);
|
|
2657
|
+
await uploadSkill(client, {
|
|
2658
|
+
name: skillName,
|
|
2659
|
+
content,
|
|
2660
|
+
tenantId,
|
|
2661
|
+
visibility
|
|
2662
|
+
});
|
|
2663
|
+
} else {
|
|
2664
|
+
We("localskills publish");
|
|
2665
|
+
const spinner = bt2();
|
|
2666
|
+
spinner.start("Scanning for skills...");
|
|
2667
|
+
const config = loadConfig();
|
|
2668
|
+
const allDetected = scanForSkills();
|
|
2669
|
+
const detected = filterTracked(allDetected, config);
|
|
2670
|
+
spinner.stop(
|
|
2671
|
+
detected.length > 0 ? `Found ${detected.length} skill file${detected.length !== 1 ? "s" : ""}.` : "No unpublished skill files found."
|
|
2672
|
+
);
|
|
2673
|
+
if (detected.length === 0) {
|
|
2674
|
+
Le("Nothing to publish.");
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2677
|
+
const selected = await je({
|
|
2678
|
+
message: "Select skills to publish",
|
|
2679
|
+
options: detected.map((s) => ({
|
|
2680
|
+
value: s,
|
|
2681
|
+
label: s.suggestedName,
|
|
2682
|
+
hint: `${s.platform}/${s.scope} ${shortenPath(s.filePath)}`
|
|
2683
|
+
})),
|
|
2684
|
+
required: true
|
|
2685
|
+
});
|
|
2686
|
+
if (Ct(selected)) {
|
|
2687
|
+
Ne("Cancelled.");
|
|
2688
|
+
process.exit(0);
|
|
2689
|
+
}
|
|
2690
|
+
const skills = selected;
|
|
2691
|
+
const tenantId = await resolveTeam(teams, opts.team);
|
|
2692
|
+
for (const skill of skills) {
|
|
2693
|
+
R2.step(`Publishing ${skill.suggestedName}...`);
|
|
2694
|
+
const name = await Ze({
|
|
2695
|
+
message: "Skill name?",
|
|
2696
|
+
initialValue: skill.suggestedName,
|
|
2697
|
+
validate: (v) => {
|
|
2698
|
+
if (!v || v.length < 1) return "Name is required";
|
|
2699
|
+
if (v.length > 100) return "Name must be 100 characters or less";
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
if (Ct(name)) {
|
|
2703
|
+
Ne("Cancelled.");
|
|
2704
|
+
process.exit(0);
|
|
2705
|
+
}
|
|
2706
|
+
const visibility = await Je({
|
|
2707
|
+
message: "Visibility?",
|
|
2708
|
+
options: [
|
|
2709
|
+
{ value: "private", label: "Private", hint: "Only team members" },
|
|
2710
|
+
{ value: "public", label: "Public", hint: "Anyone can install" },
|
|
2711
|
+
{ value: "unlisted", label: "Unlisted", hint: "Accessible via direct link" }
|
|
2712
|
+
],
|
|
2713
|
+
initialValue: "private"
|
|
2714
|
+
});
|
|
2715
|
+
if (Ct(visibility)) {
|
|
2716
|
+
Ne("Cancelled.");
|
|
2717
|
+
process.exit(0);
|
|
2718
|
+
}
|
|
2719
|
+
await uploadSkill(client, {
|
|
2720
|
+
name,
|
|
2721
|
+
content: skill.content,
|
|
2722
|
+
tenantId,
|
|
2723
|
+
visibility
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
Le("Done!");
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
);
|
|
2730
|
+
async function resolveTeam(teams, teamFlag) {
|
|
2731
|
+
if (teamFlag) {
|
|
2732
|
+
const match = teams.find((t) => t.id === teamFlag || t.slug === teamFlag);
|
|
2733
|
+
if (!match) {
|
|
2734
|
+
console.error(`Team not found: ${teamFlag}`);
|
|
2735
|
+
process.exit(1);
|
|
2736
|
+
}
|
|
2737
|
+
return match.id;
|
|
2738
|
+
}
|
|
2739
|
+
if (teams.length === 1) {
|
|
2740
|
+
return teams[0].id;
|
|
2741
|
+
}
|
|
2742
|
+
const selected = await Je({
|
|
2743
|
+
message: "Which team?",
|
|
2744
|
+
options: teams.map((t) => ({
|
|
2745
|
+
value: t.id,
|
|
2746
|
+
label: t.name,
|
|
2747
|
+
hint: t.slug
|
|
2748
|
+
}))
|
|
2749
|
+
});
|
|
2750
|
+
if (Ct(selected)) {
|
|
2751
|
+
Ne("Cancelled.");
|
|
2752
|
+
process.exit(0);
|
|
2753
|
+
}
|
|
2754
|
+
return selected;
|
|
2755
|
+
}
|
|
2756
|
+
async function uploadSkill(client, params) {
|
|
2757
|
+
const spinner = bt2();
|
|
2758
|
+
spinner.start(`Uploading ${params.name}...`);
|
|
2759
|
+
const res = await client.post("/api/skills", {
|
|
2760
|
+
name: params.name,
|
|
2761
|
+
content: params.content,
|
|
2762
|
+
tenantId: params.tenantId,
|
|
2763
|
+
visibility: params.visibility
|
|
2764
|
+
});
|
|
2765
|
+
if (!res.success || !res.data) {
|
|
2766
|
+
spinner.stop(`Failed: ${res.error || "Unknown error"}`);
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
spinner.stop(`Published!`);
|
|
2770
|
+
R2.success(`\u2192 localskills.sh/s/${res.data.slug}`);
|
|
2771
|
+
}
|
|
2772
|
+
function validateVisibility(value) {
|
|
2773
|
+
if (value === "public" || value === "private" || value === "unlisted") {
|
|
2774
|
+
return value;
|
|
2775
|
+
}
|
|
2776
|
+
console.error(`Invalid visibility: ${value}. Use public, private, or unlisted.`);
|
|
2777
|
+
process.exit(1);
|
|
2778
|
+
}
|
|
2779
|
+
function shortenPath(filePath) {
|
|
2780
|
+
const home = __require("os").homedir();
|
|
2781
|
+
if (filePath.startsWith(home)) {
|
|
2782
|
+
return "~" + filePath.slice(home.length);
|
|
2783
|
+
}
|
|
2784
|
+
const cwd = process.cwd();
|
|
2785
|
+
if (filePath.startsWith(cwd)) {
|
|
2786
|
+
return "." + filePath.slice(cwd.length);
|
|
2787
|
+
}
|
|
2788
|
+
return filePath;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2389
2791
|
// src/index.ts
|
|
2390
|
-
var program = new
|
|
2792
|
+
var program = new Command7();
|
|
2391
2793
|
program.name("localskills").description("Install and manage agent skills from localskills.sh").version("0.1.0");
|
|
2392
2794
|
program.addCommand(loginCommand);
|
|
2393
2795
|
program.addCommand(logoutCommand);
|
|
@@ -2396,4 +2798,5 @@ program.addCommand(installCommand);
|
|
|
2396
2798
|
program.addCommand(uninstallCommand);
|
|
2397
2799
|
program.addCommand(listCommand);
|
|
2398
2800
|
program.addCommand(pullCommand);
|
|
2801
|
+
program.addCommand(publishCommand);
|
|
2399
2802
|
program.parse();
|