@localskills/cli 0.1.0 → 0.1.4
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 +725 -242
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -161,190 +161,15 @@ 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";
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
171
|
-
import { join } from "path";
|
|
172
|
-
import { homedir } from "os";
|
|
173
|
-
var CONFIG_DIR = join(homedir(), ".localskills");
|
|
174
|
-
var CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
175
|
-
var DEFAULT_CONFIG = {
|
|
176
|
-
config_version: 2,
|
|
177
|
-
api_url: "https://localskills.sh",
|
|
178
|
-
token: null,
|
|
179
|
-
installed_skills: {},
|
|
180
|
-
defaults: {
|
|
181
|
-
scope: "project",
|
|
182
|
-
method: "symlink"
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
function loadConfig() {
|
|
186
|
-
if (!existsSync(CONFIG_PATH)) {
|
|
187
|
-
return { ...DEFAULT_CONFIG, installed_skills: {} };
|
|
188
|
-
}
|
|
189
|
-
try {
|
|
190
|
-
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
191
|
-
if (!raw.config_version || raw.config_version < 2) {
|
|
192
|
-
return migrateV1toV2(raw);
|
|
193
|
-
}
|
|
194
|
-
return { ...DEFAULT_CONFIG, ...raw };
|
|
195
|
-
} catch {
|
|
196
|
-
return { ...DEFAULT_CONFIG, installed_skills: {} };
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function saveConfig(config) {
|
|
200
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
201
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
202
|
-
}
|
|
203
|
-
function migrateV1toV2(v1) {
|
|
204
|
-
const v2 = {
|
|
205
|
-
config_version: 2,
|
|
206
|
-
api_url: v1.api_url || DEFAULT_CONFIG.api_url,
|
|
207
|
-
token: v1.token,
|
|
208
|
-
installed_skills: {},
|
|
209
|
-
defaults: {
|
|
210
|
-
scope: "project",
|
|
211
|
-
method: "symlink"
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
for (const [key, skill] of Object.entries(v1.installed_skills || {})) {
|
|
215
|
-
const isGlobal = skill.path.startsWith(homedir());
|
|
216
|
-
v2.installed_skills[key] = {
|
|
217
|
-
slug: skill.slug,
|
|
218
|
-
name: skill.slug,
|
|
219
|
-
hash: skill.hash,
|
|
220
|
-
version: 0,
|
|
221
|
-
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
222
|
-
installations: [
|
|
223
|
-
{
|
|
224
|
-
platform: skill.target,
|
|
225
|
-
scope: isGlobal ? "global" : "project",
|
|
226
|
-
method: "copy",
|
|
227
|
-
path: skill.path,
|
|
228
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
229
|
-
}
|
|
230
|
-
]
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
saveConfig(v2);
|
|
234
|
-
return v2;
|
|
235
|
-
}
|
|
236
|
-
function getToken() {
|
|
237
|
-
return loadConfig().token;
|
|
238
|
-
}
|
|
239
|
-
function setToken(token) {
|
|
240
|
-
const config = loadConfig();
|
|
241
|
-
config.token = token;
|
|
242
|
-
saveConfig(config);
|
|
243
|
-
}
|
|
244
|
-
function clearToken() {
|
|
245
|
-
const config = loadConfig();
|
|
246
|
-
config.token = null;
|
|
247
|
-
saveConfig(config);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/lib/api-client.ts
|
|
251
|
-
var ApiClient = class {
|
|
252
|
-
baseUrl;
|
|
253
|
-
token;
|
|
254
|
-
constructor() {
|
|
255
|
-
const config = loadConfig();
|
|
256
|
-
this.baseUrl = config.api_url;
|
|
257
|
-
this.token = config.token;
|
|
258
|
-
}
|
|
259
|
-
headers() {
|
|
260
|
-
const h = {
|
|
261
|
-
"Content-Type": "application/json"
|
|
262
|
-
};
|
|
263
|
-
if (this.token) {
|
|
264
|
-
h["Authorization"] = `Bearer ${this.token}`;
|
|
265
|
-
}
|
|
266
|
-
return h;
|
|
267
|
-
}
|
|
268
|
-
async get(path) {
|
|
269
|
-
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
270
|
-
headers: this.headers()
|
|
271
|
-
});
|
|
272
|
-
return res.json();
|
|
273
|
-
}
|
|
274
|
-
async post(path, body) {
|
|
275
|
-
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
276
|
-
method: "POST",
|
|
277
|
-
headers: this.headers(),
|
|
278
|
-
body: body ? JSON.stringify(body) : void 0
|
|
279
|
-
});
|
|
280
|
-
return res.json();
|
|
281
|
-
}
|
|
282
|
-
async put(path, body) {
|
|
283
|
-
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
284
|
-
method: "PUT",
|
|
285
|
-
headers: this.headers(),
|
|
286
|
-
body: JSON.stringify(body)
|
|
287
|
-
});
|
|
288
|
-
return res.json();
|
|
289
|
-
}
|
|
290
|
-
async delete(path) {
|
|
291
|
-
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
292
|
-
method: "DELETE",
|
|
293
|
-
headers: this.headers()
|
|
294
|
-
});
|
|
295
|
-
return res.json();
|
|
296
|
-
}
|
|
297
|
-
async getRaw(path) {
|
|
298
|
-
return fetch(`${this.baseUrl}${path}`, {
|
|
299
|
-
headers: this.headers()
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
isAuthenticated() {
|
|
303
|
-
return this.token !== null;
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
// src/commands/auth.ts
|
|
308
|
-
var loginCommand = new Command("login").description("Log in to localskills.sh").action(async () => {
|
|
309
|
-
const client = new ApiClient();
|
|
310
|
-
console.log("Opening browser for authentication...");
|
|
311
|
-
console.log("Visit: https://localskills.sh/api/cli/auth to authorize this device\n");
|
|
312
|
-
const readline = await import("readline");
|
|
313
|
-
const rl = readline.createInterface({
|
|
314
|
-
input: process.stdin,
|
|
315
|
-
output: process.stdout
|
|
316
|
-
});
|
|
317
|
-
const token = await new Promise((resolve3) => {
|
|
318
|
-
rl.question("Paste your API token: ", (answer) => {
|
|
319
|
-
rl.close();
|
|
320
|
-
resolve3(answer.trim());
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
if (!token) {
|
|
324
|
-
console.error("No token provided. Login cancelled.");
|
|
325
|
-
process.exit(1);
|
|
326
|
-
}
|
|
327
|
-
setToken(token);
|
|
328
|
-
console.log("Logged in successfully!");
|
|
329
|
-
});
|
|
330
|
-
var logoutCommand = new Command("logout").description("Log out of localskills.sh").action(() => {
|
|
331
|
-
clearToken();
|
|
332
|
-
console.log("Logged out.");
|
|
333
|
-
});
|
|
334
|
-
var whoamiCommand = new Command("whoami").description("Show current user info").action(async () => {
|
|
335
|
-
const token = getToken();
|
|
336
|
-
if (!token) {
|
|
337
|
-
console.log("Not logged in. Run `localskills login` to authenticate.");
|
|
338
|
-
process.exit(1);
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
console.log("Authenticated with token:", token.slice(0, 8) + "...");
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
// src/commands/install.ts
|
|
345
|
-
import { Command as Command2 } from "commander";
|
|
168
|
+
import { randomBytes, createHash } from "crypto";
|
|
169
|
+
import { execSync } from "child_process";
|
|
346
170
|
|
|
347
171
|
// ../../node_modules/.pnpm/@clack+core@1.0.1/node_modules/@clack/core/dist/index.mjs
|
|
172
|
+
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
348
173
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
349
174
|
import { stdout as R, stdin as q } from "process";
|
|
350
175
|
import * as k from "readline";
|
|
@@ -769,9 +594,28 @@ var Wt = class extends x {
|
|
|
769
594
|
});
|
|
770
595
|
}
|
|
771
596
|
};
|
|
597
|
+
var $t = class extends x {
|
|
598
|
+
get userInputWithCursor() {
|
|
599
|
+
if (this.state === "submit") return this.userInput;
|
|
600
|
+
const e2 = this.userInput;
|
|
601
|
+
if (this.cursor >= e2.length) return `${this.userInput}\u2588`;
|
|
602
|
+
const s = e2.slice(0, this.cursor), [i, ...r] = e2.slice(this.cursor);
|
|
603
|
+
return `${s}${import_picocolors.default.inverse(i)}${r.join("")}`;
|
|
604
|
+
}
|
|
605
|
+
get cursor() {
|
|
606
|
+
return this._cursor;
|
|
607
|
+
}
|
|
608
|
+
constructor(e2) {
|
|
609
|
+
super({ ...e2, initialUserInput: e2.initialUserInput ?? e2.initialValue }), this.on("userInput", (s) => {
|
|
610
|
+
this._setValue(s);
|
|
611
|
+
}), this.on("finalize", () => {
|
|
612
|
+
this.value || (this.value = e2.defaultValue), this.value === void 0 && (this.value = "");
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
};
|
|
772
616
|
|
|
773
617
|
// ../../node_modules/.pnpm/@clack+prompts@1.0.1/node_modules/@clack/prompts/dist/index.mjs
|
|
774
|
-
var
|
|
618
|
+
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
775
619
|
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
776
620
|
import N2 from "process";
|
|
777
621
|
import { readdirSync as de, existsSync as $e, lstatSync as xt2 } from "fs";
|
|
@@ -812,26 +656,26 @@ var W2 = (t) => {
|
|
|
812
656
|
switch (t) {
|
|
813
657
|
case "initial":
|
|
814
658
|
case "active":
|
|
815
|
-
return
|
|
659
|
+
return import_picocolors2.default.cyan(Rt);
|
|
816
660
|
case "cancel":
|
|
817
|
-
return
|
|
661
|
+
return import_picocolors2.default.red(dt2);
|
|
818
662
|
case "error":
|
|
819
|
-
return
|
|
663
|
+
return import_picocolors2.default.yellow($t2);
|
|
820
664
|
case "submit":
|
|
821
|
-
return
|
|
665
|
+
return import_picocolors2.default.green(V);
|
|
822
666
|
}
|
|
823
667
|
};
|
|
824
668
|
var vt2 = (t) => {
|
|
825
669
|
switch (t) {
|
|
826
670
|
case "initial":
|
|
827
671
|
case "active":
|
|
828
|
-
return
|
|
672
|
+
return import_picocolors2.default.cyan(d);
|
|
829
673
|
case "cancel":
|
|
830
|
-
return
|
|
674
|
+
return import_picocolors2.default.red(d);
|
|
831
675
|
case "error":
|
|
832
|
-
return
|
|
676
|
+
return import_picocolors2.default.yellow(d);
|
|
833
677
|
case "submit":
|
|
834
|
-
return
|
|
678
|
+
return import_picocolors2.default.green(d);
|
|
835
679
|
}
|
|
836
680
|
};
|
|
837
681
|
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 +695,13 @@ var jt = (t, r = {}, s = {}) => {
|
|
|
851
695
|
if (B2 > I2 || m >= h && m > $) {
|
|
852
696
|
const _2 = t.slice(I2, B2) || t.slice($, m);
|
|
853
697
|
y2 = 0;
|
|
854
|
-
for (const
|
|
855
|
-
const T2 =
|
|
698
|
+
for (const D2 of _2.replaceAll(Fe, "")) {
|
|
699
|
+
const T2 = D2.codePointAt(0) || 0;
|
|
856
700
|
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
701
|
f = true;
|
|
858
702
|
break t;
|
|
859
703
|
}
|
|
860
|
-
y2 +=
|
|
704
|
+
y2 += D2.length, A += w;
|
|
861
705
|
}
|
|
862
706
|
I2 = B2 = 0;
|
|
863
707
|
}
|
|
@@ -1009,7 +853,7 @@ var be = (t, r, s, i, a) => {
|
|
|
1009
853
|
return { lineCount: o, removals: u };
|
|
1010
854
|
};
|
|
1011
855
|
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 =
|
|
856
|
+
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
857
|
let $ = 0;
|
|
1014
858
|
r >= E - 3 && ($ = Math.max(Math.min(r - E + 3, s.length - E), 0));
|
|
1015
859
|
let m = E < s.length && $ > 0, h = E < s.length && $ + E < s.length;
|
|
@@ -1024,15 +868,15 @@ var X2 = (t) => {
|
|
|
1024
868
|
}
|
|
1025
869
|
if (v > p) {
|
|
1026
870
|
let A = 0, w = 0, _2 = v;
|
|
1027
|
-
const
|
|
1028
|
-
m ? ({ lineCount: _2, removals: A } = T2(0,
|
|
871
|
+
const D2 = r - S2, T2 = (Y, L2) => be(f, _2, Y, L2, p);
|
|
872
|
+
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
873
|
}
|
|
1030
874
|
const B2 = [];
|
|
1031
875
|
m && B2.push(g);
|
|
1032
876
|
for (const A of f) for (const w of A) B2.push(w);
|
|
1033
877
|
return h && B2.push(g), B2;
|
|
1034
878
|
};
|
|
1035
|
-
var R2 = { message: (t = [], { symbol: r =
|
|
879
|
+
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
880
|
const u = [], l = o ?? _.withGuide, n = l ? s : "", c = l ? `${r} ` : "", g = l ? `${s} ` : "";
|
|
1037
881
|
for (let p = 0; p < a; p++) u.push(n);
|
|
1038
882
|
const F = Array.isArray(t) ? t : t.split(`
|
|
@@ -1046,30 +890,30 @@ var R2 = { message: (t = [], { symbol: r = import_picocolors.default.gray(d), se
|
|
|
1046
890
|
`)}
|
|
1047
891
|
`);
|
|
1048
892
|
}, info: (t, r) => {
|
|
1049
|
-
R2.message(t, { ...r, symbol:
|
|
893
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.blue(ft2) });
|
|
1050
894
|
}, success: (t, r) => {
|
|
1051
|
-
R2.message(t, { ...r, symbol:
|
|
895
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.green(Ft2) });
|
|
1052
896
|
}, step: (t, r) => {
|
|
1053
|
-
R2.message(t, { ...r, symbol:
|
|
897
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.green(V) });
|
|
1054
898
|
}, warn: (t, r) => {
|
|
1055
|
-
R2.message(t, { ...r, symbol:
|
|
899
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.yellow(yt2) });
|
|
1056
900
|
}, warning: (t, r) => {
|
|
1057
901
|
R2.warn(t, r);
|
|
1058
902
|
}, error: (t, r) => {
|
|
1059
|
-
R2.message(t, { ...r, symbol:
|
|
903
|
+
R2.message(t, { ...r, symbol: import_picocolors2.default.red(Et2) });
|
|
1060
904
|
} };
|
|
1061
905
|
var Ne = (t = "", r) => {
|
|
1062
|
-
(r?.output ?? process.stdout).write(`${
|
|
906
|
+
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(x2)} ${import_picocolors2.default.red(t)}
|
|
1063
907
|
|
|
1064
908
|
`);
|
|
1065
909
|
};
|
|
1066
910
|
var We = (t = "", r) => {
|
|
1067
|
-
(r?.output ?? process.stdout).write(`${
|
|
911
|
+
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(ht2)} ${t}
|
|
1068
912
|
`);
|
|
1069
913
|
};
|
|
1070
914
|
var Le = (t = "", r) => {
|
|
1071
|
-
(r?.output ?? process.stdout).write(`${
|
|
1072
|
-
${
|
|
915
|
+
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(d)}
|
|
916
|
+
${import_picocolors2.default.gray(x2)} ${t}
|
|
1073
917
|
|
|
1074
918
|
`);
|
|
1075
919
|
};
|
|
@@ -1079,13 +923,13 @@ var Z2 = (t, r) => t.split(`
|
|
|
1079
923
|
var je = (t) => {
|
|
1080
924
|
const r = (i, a) => {
|
|
1081
925
|
const o = i.label ?? String(i.value);
|
|
1082
|
-
return a === "disabled" ? `${
|
|
926
|
+
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
927
|
}, s = t.required ?? true;
|
|
1084
928
|
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
929
|
if (s && (i === void 0 || i.length === 0)) return `Please select at least one option.
|
|
1086
|
-
${
|
|
930
|
+
${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
931
|
}, render() {
|
|
1088
|
-
const i = xt(t.output, t.message, `${vt2(this.state)} `, `${W2(this.state)} `), a = `${
|
|
932
|
+
const i = xt(t.output, t.message, `${vt2(this.state)} `, `${W2(this.state)} `), a = `${import_picocolors2.default.gray(d)}
|
|
1089
933
|
${i}
|
|
1090
934
|
`, o = this.value ?? [], u = (l, n) => {
|
|
1091
935
|
if (l.disabled) return r(l, "disabled");
|
|
@@ -1094,19 +938,19 @@ ${i}
|
|
|
1094
938
|
};
|
|
1095
939
|
switch (this.state) {
|
|
1096
940
|
case "submit": {
|
|
1097
|
-
const l = this.options.filter(({ value: c }) => o.includes(c)).map((c) => r(c, "submitted")).join(
|
|
941
|
+
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
942
|
return `${a}${n}`;
|
|
1099
943
|
}
|
|
1100
944
|
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, `${
|
|
945
|
+
const l = this.options.filter(({ value: c }) => o.includes(c)).map((c) => r(c, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
946
|
+
if (l.trim() === "") return `${a}${import_picocolors2.default.gray(d)}`;
|
|
947
|
+
const n = xt(t.output, l, `${import_picocolors2.default.gray(d)} `);
|
|
1104
948
|
return `${a}${n}
|
|
1105
|
-
${
|
|
949
|
+
${import_picocolors2.default.gray(d)}`;
|
|
1106
950
|
}
|
|
1107
951
|
case "error": {
|
|
1108
|
-
const l = `${
|
|
1109
|
-
`).map((F, p) => p === 0 ? `${
|
|
952
|
+
const l = `${import_picocolors2.default.yellow(d)} `, n = this.error.split(`
|
|
953
|
+
`).map((F, p) => p === 0 ? `${import_picocolors2.default.yellow(x2)} ${import_picocolors2.default.yellow(F)}` : ` ${F}`).join(`
|
|
1110
954
|
`), c = a.split(`
|
|
1111
955
|
`).length, g = n.split(`
|
|
1112
956
|
`).length + 1;
|
|
@@ -1116,17 +960,17 @@ ${n}
|
|
|
1116
960
|
`;
|
|
1117
961
|
}
|
|
1118
962
|
default: {
|
|
1119
|
-
const l = `${
|
|
963
|
+
const l = `${import_picocolors2.default.cyan(d)} `, n = a.split(`
|
|
1120
964
|
`).length;
|
|
1121
965
|
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
966
|
${l}`)}
|
|
1123
|
-
${
|
|
967
|
+
${import_picocolors2.default.cyan(x2)}
|
|
1124
968
|
`;
|
|
1125
969
|
}
|
|
1126
970
|
}
|
|
1127
971
|
} }).prompt();
|
|
1128
972
|
};
|
|
1129
|
-
var Ke =
|
|
973
|
+
var Ke = import_picocolors2.default.magenta;
|
|
1130
974
|
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
975
|
const c = ct2();
|
|
1132
976
|
let g, F, p = false, E = false, $ = "", m, h = performance.now();
|
|
@@ -1144,11 +988,11 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1144
988
|
const b = J2(m, y2, { hard: true, trim: false }).split(`
|
|
1145
989
|
`);
|
|
1146
990
|
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(/\.+$/, ""),
|
|
991
|
+
}, _2 = (b) => b.replace(/\.+$/, ""), D2 = (b) => {
|
|
1148
992
|
const O2 = (performance.now() - b) / 1e3, j2 = Math.floor(O2 / 60), G2 = Math.floor(O2 % 60);
|
|
1149
993
|
return j2 > 0 ? `[${j2}m ${G2}s]` : `[${G2}s]`;
|
|
1150
994
|
}, T2 = n.withGuide ?? _.withGuide, Y = (b = "") => {
|
|
1151
|
-
p = true, g = Bt({ output: s }), $ = _2(b), h = performance.now(), T2 && s.write(`${
|
|
995
|
+
p = true, g = Bt({ output: s }), $ = _2(b), h = performance.now(), T2 && s.write(`${import_picocolors2.default.gray(d)}
|
|
1152
996
|
`);
|
|
1153
997
|
let O2 = 0, j2 = 0;
|
|
1154
998
|
B2(), F = setInterval(() => {
|
|
@@ -1157,7 +1001,7 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1157
1001
|
const G2 = f(o[O2]);
|
|
1158
1002
|
let tt2;
|
|
1159
1003
|
if (c) tt2 = `${G2} ${$}...`;
|
|
1160
|
-
else if (t === "timer") tt2 = `${G2} ${$} ${
|
|
1004
|
+
else if (t === "timer") tt2 = `${G2} ${$} ${D2(h)}`;
|
|
1161
1005
|
else {
|
|
1162
1006
|
const te = ".".repeat(Math.floor(j2)).slice(0, 3);
|
|
1163
1007
|
tt2 = `${G2} ${$}${te}`;
|
|
@@ -1168,8 +1012,8 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1168
1012
|
}, L2 = (b = "", O2 = 0, j2 = false) => {
|
|
1169
1013
|
if (!p) return;
|
|
1170
1014
|
p = false, clearInterval(F), w();
|
|
1171
|
-
const G2 = O2 === 0 ?
|
|
1172
|
-
$ = b ?? $, j2 || (t === "timer" ? s.write(`${G2} ${$} ${
|
|
1015
|
+
const G2 = O2 === 0 ? import_picocolors2.default.green(V) : O2 === 1 ? import_picocolors2.default.red(dt2) : import_picocolors2.default.red($t2);
|
|
1016
|
+
$ = b ?? $, j2 || (t === "timer" ? s.write(`${G2} ${$} ${D2(h)}
|
|
1173
1017
|
`) : s.write(`${G2} ${$}
|
|
1174
1018
|
`)), A(), g();
|
|
1175
1019
|
};
|
|
@@ -1189,33 +1033,33 @@ var Je = (t) => {
|
|
|
1189
1033
|
const a = s.label ?? String(s.value);
|
|
1190
1034
|
switch (i) {
|
|
1191
1035
|
case "disabled":
|
|
1192
|
-
return `${
|
|
1036
|
+
return `${import_picocolors2.default.gray(H2)} ${lt2(a, import_picocolors2.default.gray)}${s.hint ? ` ${import_picocolors2.default.dim(`(${s.hint ?? "disabled"})`)}` : ""}`;
|
|
1193
1037
|
case "selected":
|
|
1194
|
-
return `${lt2(a,
|
|
1038
|
+
return `${lt2(a, import_picocolors2.default.dim)}`;
|
|
1195
1039
|
case "active":
|
|
1196
|
-
return `${
|
|
1040
|
+
return `${import_picocolors2.default.green(Q2)} ${a}${s.hint ? ` ${import_picocolors2.default.dim(`(${s.hint})`)}` : ""}`;
|
|
1197
1041
|
case "cancelled":
|
|
1198
|
-
return `${lt2(a, (o) =>
|
|
1042
|
+
return `${lt2(a, (o) => import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o)))}`;
|
|
1199
1043
|
default:
|
|
1200
|
-
return `${
|
|
1044
|
+
return `${import_picocolors2.default.dim(H2)} ${lt2(a, import_picocolors2.default.dim)}`;
|
|
1201
1045
|
}
|
|
1202
1046
|
};
|
|
1203
1047
|
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 ? `${
|
|
1048
|
+
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
1049
|
` : ""}${o}
|
|
1206
1050
|
`;
|
|
1207
1051
|
switch (this.state) {
|
|
1208
1052
|
case "submit": {
|
|
1209
|
-
const l = s ? `${
|
|
1053
|
+
const l = s ? `${import_picocolors2.default.gray(d)} ` : "", n = xt(t.output, r(this.options[this.cursor], "selected"), l);
|
|
1210
1054
|
return `${u}${n}`;
|
|
1211
1055
|
}
|
|
1212
1056
|
case "cancel": {
|
|
1213
|
-
const l = s ? `${
|
|
1057
|
+
const l = s ? `${import_picocolors2.default.gray(d)} ` : "", n = xt(t.output, r(this.options[this.cursor], "cancelled"), l);
|
|
1214
1058
|
return `${u}${n}${s ? `
|
|
1215
|
-
${
|
|
1059
|
+
${import_picocolors2.default.gray(d)}` : ""}`;
|
|
1216
1060
|
}
|
|
1217
1061
|
default: {
|
|
1218
|
-
const l = s ? `${
|
|
1062
|
+
const l = s ? `${import_picocolors2.default.cyan(d)} ` : "", n = s ? import_picocolors2.default.cyan(x2) : "", c = u.split(`
|
|
1219
1063
|
`).length, g = s ? 2 : 1;
|
|
1220
1064
|
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
1065
|
${l}`)}
|
|
@@ -1225,7 +1069,292 @@ ${n}
|
|
|
1225
1069
|
}
|
|
1226
1070
|
} }).prompt();
|
|
1227
1071
|
};
|
|
1228
|
-
var Qt = `${
|
|
1072
|
+
var Qt = `${import_picocolors2.default.gray(d)} `;
|
|
1073
|
+
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() {
|
|
1074
|
+
const r = t?.withGuide ?? _.withGuide, s = `${`${r ? `${import_picocolors2.default.gray(d)}
|
|
1075
|
+
` : ""}${W2(this.state)} `}${t.message}
|
|
1076
|
+
`, 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 ?? "";
|
|
1077
|
+
switch (this.state) {
|
|
1078
|
+
case "error": {
|
|
1079
|
+
const u = this.error ? ` ${import_picocolors2.default.yellow(this.error)}` : "", l = r ? `${import_picocolors2.default.yellow(d)} ` : "", n = r ? import_picocolors2.default.yellow(x2) : "";
|
|
1080
|
+
return `${s.trim()}
|
|
1081
|
+
${l}${a}
|
|
1082
|
+
${n}${u}
|
|
1083
|
+
`;
|
|
1084
|
+
}
|
|
1085
|
+
case "submit": {
|
|
1086
|
+
const u = o ? ` ${import_picocolors2.default.dim(o)}` : "", l = r ? import_picocolors2.default.gray(d) : "";
|
|
1087
|
+
return `${s}${l}${u}`;
|
|
1088
|
+
}
|
|
1089
|
+
case "cancel": {
|
|
1090
|
+
const u = o ? ` ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o))}` : "", l = r ? import_picocolors2.default.gray(d) : "";
|
|
1091
|
+
return `${s}${l}${u}${o.trim() ? `
|
|
1092
|
+
${l}` : ""}`;
|
|
1093
|
+
}
|
|
1094
|
+
default: {
|
|
1095
|
+
const u = r ? `${import_picocolors2.default.cyan(d)} ` : "", l = r ? import_picocolors2.default.cyan(x2) : "";
|
|
1096
|
+
return `${s}${u}${a}
|
|
1097
|
+
${l}
|
|
1098
|
+
`;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
} }).prompt();
|
|
1102
|
+
|
|
1103
|
+
// src/lib/config.ts
|
|
1104
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
1105
|
+
import { join } from "path";
|
|
1106
|
+
import { homedir } from "os";
|
|
1107
|
+
var CONFIG_DIR = join(homedir(), ".localskills");
|
|
1108
|
+
var CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
1109
|
+
var DEFAULT_CONFIG = {
|
|
1110
|
+
config_version: 2,
|
|
1111
|
+
api_url: "https://localskills.sh",
|
|
1112
|
+
token: null,
|
|
1113
|
+
installed_skills: {},
|
|
1114
|
+
defaults: {
|
|
1115
|
+
scope: "project",
|
|
1116
|
+
method: "symlink"
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
function loadConfig() {
|
|
1120
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
1121
|
+
return { ...DEFAULT_CONFIG, installed_skills: {} };
|
|
1122
|
+
}
|
|
1123
|
+
try {
|
|
1124
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1125
|
+
if (!raw.config_version || raw.config_version < 2) {
|
|
1126
|
+
return migrateV1toV2(raw);
|
|
1127
|
+
}
|
|
1128
|
+
return { ...DEFAULT_CONFIG, ...raw };
|
|
1129
|
+
} catch {
|
|
1130
|
+
return { ...DEFAULT_CONFIG, installed_skills: {} };
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
function saveConfig(config) {
|
|
1134
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1135
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
1136
|
+
}
|
|
1137
|
+
function migrateV1toV2(v1) {
|
|
1138
|
+
const v2 = {
|
|
1139
|
+
config_version: 2,
|
|
1140
|
+
api_url: v1.api_url || DEFAULT_CONFIG.api_url,
|
|
1141
|
+
token: v1.token,
|
|
1142
|
+
installed_skills: {},
|
|
1143
|
+
defaults: {
|
|
1144
|
+
scope: "project",
|
|
1145
|
+
method: "symlink"
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
for (const [key, skill] of Object.entries(v1.installed_skills || {})) {
|
|
1149
|
+
const isGlobal = skill.path.startsWith(homedir());
|
|
1150
|
+
v2.installed_skills[key] = {
|
|
1151
|
+
slug: skill.slug,
|
|
1152
|
+
name: skill.slug,
|
|
1153
|
+
hash: skill.hash,
|
|
1154
|
+
version: 0,
|
|
1155
|
+
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1156
|
+
installations: [
|
|
1157
|
+
{
|
|
1158
|
+
platform: skill.target,
|
|
1159
|
+
scope: isGlobal ? "global" : "project",
|
|
1160
|
+
method: "copy",
|
|
1161
|
+
path: skill.path,
|
|
1162
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1163
|
+
}
|
|
1164
|
+
]
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
saveConfig(v2);
|
|
1168
|
+
return v2;
|
|
1169
|
+
}
|
|
1170
|
+
function getToken() {
|
|
1171
|
+
return loadConfig().token;
|
|
1172
|
+
}
|
|
1173
|
+
function setToken(token) {
|
|
1174
|
+
const config = loadConfig();
|
|
1175
|
+
config.token = token;
|
|
1176
|
+
saveConfig(config);
|
|
1177
|
+
}
|
|
1178
|
+
function clearToken() {
|
|
1179
|
+
const config = loadConfig();
|
|
1180
|
+
config.token = null;
|
|
1181
|
+
saveConfig(config);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/lib/api-client.ts
|
|
1185
|
+
var ApiClient = class {
|
|
1186
|
+
baseUrl;
|
|
1187
|
+
token;
|
|
1188
|
+
constructor() {
|
|
1189
|
+
const config = loadConfig();
|
|
1190
|
+
this.baseUrl = config.api_url;
|
|
1191
|
+
this.token = config.token;
|
|
1192
|
+
}
|
|
1193
|
+
headers() {
|
|
1194
|
+
const h = {
|
|
1195
|
+
"Content-Type": "application/json"
|
|
1196
|
+
};
|
|
1197
|
+
if (this.token) {
|
|
1198
|
+
h["Authorization"] = `Bearer ${this.token}`;
|
|
1199
|
+
}
|
|
1200
|
+
return h;
|
|
1201
|
+
}
|
|
1202
|
+
async get(path) {
|
|
1203
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
1204
|
+
headers: this.headers()
|
|
1205
|
+
});
|
|
1206
|
+
return res.json();
|
|
1207
|
+
}
|
|
1208
|
+
async post(path, body) {
|
|
1209
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
1210
|
+
method: "POST",
|
|
1211
|
+
headers: this.headers(),
|
|
1212
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1213
|
+
});
|
|
1214
|
+
return res.json();
|
|
1215
|
+
}
|
|
1216
|
+
async put(path, body) {
|
|
1217
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
1218
|
+
method: "PUT",
|
|
1219
|
+
headers: this.headers(),
|
|
1220
|
+
body: JSON.stringify(body)
|
|
1221
|
+
});
|
|
1222
|
+
return res.json();
|
|
1223
|
+
}
|
|
1224
|
+
async delete(path) {
|
|
1225
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
1226
|
+
method: "DELETE",
|
|
1227
|
+
headers: this.headers()
|
|
1228
|
+
});
|
|
1229
|
+
return res.json();
|
|
1230
|
+
}
|
|
1231
|
+
async getRaw(path) {
|
|
1232
|
+
return fetch(`${this.baseUrl}${path}`, {
|
|
1233
|
+
headers: this.headers()
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
isAuthenticated() {
|
|
1237
|
+
return this.token !== null;
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
// src/commands/auth.ts
|
|
1242
|
+
var USER_CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789";
|
|
1243
|
+
function generateUserCode(length = 8) {
|
|
1244
|
+
const bytes = randomBytes(length);
|
|
1245
|
+
return Array.from(bytes).map((b) => USER_CODE_CHARS[b % USER_CODE_CHARS.length]).join("");
|
|
1246
|
+
}
|
|
1247
|
+
function openBrowser(url) {
|
|
1248
|
+
try {
|
|
1249
|
+
const platform = process.platform;
|
|
1250
|
+
if (platform === "darwin") {
|
|
1251
|
+
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
1252
|
+
} else if (platform === "win32") {
|
|
1253
|
+
execSync(`start "" "${url}"`, { stdio: "ignore" });
|
|
1254
|
+
} else {
|
|
1255
|
+
execSync(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
1256
|
+
}
|
|
1257
|
+
} catch {
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
function sleep(ms) {
|
|
1261
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
1262
|
+
}
|
|
1263
|
+
var loginCommand = new Command("login").description("Log in to localskills.sh").option("--token <token>", "Use an API token directly (headless mode)").action(async (opts) => {
|
|
1264
|
+
if (opts.token) {
|
|
1265
|
+
setToken(opts.token);
|
|
1266
|
+
const client2 = new ApiClient();
|
|
1267
|
+
const res = await client2.get(
|
|
1268
|
+
"/api/cli/auth"
|
|
1269
|
+
);
|
|
1270
|
+
if (res.success && res.data) {
|
|
1271
|
+
const display = res.data.name || res.data.username || res.data.email;
|
|
1272
|
+
console.log(`Logged in as ${display}`);
|
|
1273
|
+
} else {
|
|
1274
|
+
console.error("Warning: Token stored but could not verify. Check that it's valid.");
|
|
1275
|
+
}
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
We("localskills login");
|
|
1279
|
+
const spinner = bt2();
|
|
1280
|
+
spinner.start("Initializing...");
|
|
1281
|
+
const deviceCode = randomBytes(32);
|
|
1282
|
+
const codeHash = createHash("sha256").update(deviceCode).digest("hex");
|
|
1283
|
+
const userCode = generateUserCode();
|
|
1284
|
+
const client = new ApiClient();
|
|
1285
|
+
const initRes = await client.post("/api/cli/auth/device", { codeHash, userCode });
|
|
1286
|
+
if (!initRes.success || !initRes.data) {
|
|
1287
|
+
spinner.stop(`Failed: ${initRes.error || "Could not start login"}`);
|
|
1288
|
+
process.exit(1);
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
spinner.stop("Ready!");
|
|
1292
|
+
const { verificationUrl } = initRes.data;
|
|
1293
|
+
R2.info(`Your verification code: ${userCode}`);
|
|
1294
|
+
R2.message(`Opening browser to ${verificationUrl}`);
|
|
1295
|
+
R2.message("If the browser doesn't open, visit the URL above manually.");
|
|
1296
|
+
openBrowser(verificationUrl);
|
|
1297
|
+
const pollSpinner = bt2();
|
|
1298
|
+
pollSpinner.start("Waiting for authorization...");
|
|
1299
|
+
const expiresAt = new Date(initRes.data.expiresAt).getTime();
|
|
1300
|
+
const POLL_INTERVAL = 2e3;
|
|
1301
|
+
while (Date.now() < expiresAt) {
|
|
1302
|
+
await sleep(POLL_INTERVAL);
|
|
1303
|
+
try {
|
|
1304
|
+
const pollRes = await client.get(
|
|
1305
|
+
`/api/cli/auth/poll?code_hash=${codeHash}`
|
|
1306
|
+
);
|
|
1307
|
+
if (!pollRes.success || !pollRes.data) continue;
|
|
1308
|
+
if (pollRes.data.status === "approved" && pollRes.data.token) {
|
|
1309
|
+
setToken(pollRes.data.token);
|
|
1310
|
+
pollSpinner.stop("Authorized!");
|
|
1311
|
+
const verifyClient = new ApiClient();
|
|
1312
|
+
const whoami = await verifyClient.get("/api/cli/auth");
|
|
1313
|
+
if (whoami.success && whoami.data) {
|
|
1314
|
+
const display = whoami.data.name || whoami.data.username || whoami.data.email;
|
|
1315
|
+
R2.success(`Logged in as ${display}`);
|
|
1316
|
+
}
|
|
1317
|
+
Le("Done!");
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
if (pollRes.data.status === "expired" || pollRes.data.status === "not_found") {
|
|
1321
|
+
pollSpinner.stop("Login expired. Please try again.");
|
|
1322
|
+
process.exit(1);
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
pollSpinner.stop("Login expired. Please try again.");
|
|
1329
|
+
process.exit(1);
|
|
1330
|
+
});
|
|
1331
|
+
var logoutCommand = new Command("logout").description("Log out of localskills.sh").action(() => {
|
|
1332
|
+
clearToken();
|
|
1333
|
+
console.log("Logged out.");
|
|
1334
|
+
});
|
|
1335
|
+
var whoamiCommand = new Command("whoami").description("Show current user info").action(async () => {
|
|
1336
|
+
const token = getToken();
|
|
1337
|
+
if (!token) {
|
|
1338
|
+
console.log("Not logged in. Run `localskills login` to authenticate.");
|
|
1339
|
+
process.exit(1);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const client = new ApiClient();
|
|
1343
|
+
const res = await client.get("/api/cli/auth");
|
|
1344
|
+
if (!res.success || !res.data) {
|
|
1345
|
+
console.error("Failed to fetch user info. Your token may be invalid.");
|
|
1346
|
+
console.error("Run `localskills login` to re-authenticate.");
|
|
1347
|
+
process.exit(1);
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
const { username, name, email } = res.data;
|
|
1351
|
+
if (name) console.log(` Name: ${name}`);
|
|
1352
|
+
if (username) console.log(` Username: @${username}`);
|
|
1353
|
+
console.log(` Email: ${email}`);
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
// src/commands/install.ts
|
|
1357
|
+
import { Command as Command2 } from "commander";
|
|
1229
1358
|
|
|
1230
1359
|
// src/lib/cache.ts
|
|
1231
1360
|
import {
|
|
@@ -1275,6 +1404,11 @@ function toClaudeMD(content, skill) {
|
|
|
1275
1404
|
function toPlainMD(content) {
|
|
1276
1405
|
return content;
|
|
1277
1406
|
}
|
|
1407
|
+
function stripFrontmatter(content) {
|
|
1408
|
+
const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
1409
|
+
if (match) return content.slice(match[0].length);
|
|
1410
|
+
return content;
|
|
1411
|
+
}
|
|
1278
1412
|
|
|
1279
1413
|
// src/lib/symlink.ts
|
|
1280
1414
|
import {
|
|
@@ -1424,8 +1558,8 @@ function uninstall2(installation, _slug) {
|
|
|
1424
1558
|
}
|
|
1425
1559
|
const parentDir = join3(installation.path, "..");
|
|
1426
1560
|
try {
|
|
1427
|
-
const { readdirSync:
|
|
1428
|
-
if (existsSync4(parentDir) &&
|
|
1561
|
+
const { readdirSync: readdirSync3 } = __require("fs");
|
|
1562
|
+
if (existsSync4(parentDir) && readdirSync3(parentDir).length === 0) {
|
|
1429
1563
|
rmSync3(parentDir, { recursive: true });
|
|
1430
1564
|
}
|
|
1431
1565
|
} catch {
|
|
@@ -1447,7 +1581,7 @@ var claudeAdapter = {
|
|
|
1447
1581
|
// src/lib/installers/codex.ts
|
|
1448
1582
|
import { join as join4 } from "path";
|
|
1449
1583
|
import { homedir as homedir4 } from "os";
|
|
1450
|
-
import { execSync } from "child_process";
|
|
1584
|
+
import { execSync as execSync2 } from "child_process";
|
|
1451
1585
|
|
|
1452
1586
|
// src/lib/marked-sections.ts
|
|
1453
1587
|
import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
|
|
@@ -1492,6 +1626,17 @@ function removeSection(filePath, slug) {
|
|
|
1492
1626
|
writeFileSync4(filePath, result ? result + "\n" : "");
|
|
1493
1627
|
return true;
|
|
1494
1628
|
}
|
|
1629
|
+
function listSections(filePath) {
|
|
1630
|
+
if (!existsSync5(filePath)) return [];
|
|
1631
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1632
|
+
const regex = /<!-- localskills:start:(.+?) -->/g;
|
|
1633
|
+
const slugs = [];
|
|
1634
|
+
let match;
|
|
1635
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1636
|
+
slugs.push(match[1]);
|
|
1637
|
+
}
|
|
1638
|
+
return slugs;
|
|
1639
|
+
}
|
|
1495
1640
|
|
|
1496
1641
|
// src/lib/installers/codex.ts
|
|
1497
1642
|
var descriptor3 = {
|
|
@@ -1505,7 +1650,7 @@ var descriptor3 = {
|
|
|
1505
1650
|
function detect3() {
|
|
1506
1651
|
let hasCommand = false;
|
|
1507
1652
|
try {
|
|
1508
|
-
|
|
1653
|
+
execSync2("which codex", { stdio: "ignore" });
|
|
1509
1654
|
hasCommand = true;
|
|
1510
1655
|
} catch {
|
|
1511
1656
|
}
|
|
@@ -1722,7 +1867,7 @@ var copilotAdapter = {
|
|
|
1722
1867
|
import { existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, unlinkSync as unlinkSync6 } from "fs";
|
|
1723
1868
|
import { join as join8 } from "path";
|
|
1724
1869
|
import { homedir as homedir6 } from "os";
|
|
1725
|
-
import { execSync as
|
|
1870
|
+
import { execSync as execSync3 } from "child_process";
|
|
1726
1871
|
var descriptor7 = {
|
|
1727
1872
|
id: "opencode",
|
|
1728
1873
|
name: "OpenCode",
|
|
@@ -1735,7 +1880,7 @@ function detect7(projectDir) {
|
|
|
1735
1880
|
const cwd = projectDir || process.cwd();
|
|
1736
1881
|
let hasCommand = false;
|
|
1737
1882
|
try {
|
|
1738
|
-
|
|
1883
|
+
execSync3("which opencode", { stdio: "ignore" });
|
|
1739
1884
|
hasCommand = true;
|
|
1740
1885
|
} catch {
|
|
1741
1886
|
}
|
|
@@ -1787,7 +1932,7 @@ var opencodeAdapter = {
|
|
|
1787
1932
|
// src/lib/installers/aider.ts
|
|
1788
1933
|
import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, readFileSync as readFileSync3 } from "fs";
|
|
1789
1934
|
import { join as join9 } from "path";
|
|
1790
|
-
import { execSync as
|
|
1935
|
+
import { execSync as execSync4 } from "child_process";
|
|
1791
1936
|
var descriptor8 = {
|
|
1792
1937
|
id: "aider",
|
|
1793
1938
|
name: "Aider",
|
|
@@ -1799,7 +1944,7 @@ var descriptor8 = {
|
|
|
1799
1944
|
function detect8() {
|
|
1800
1945
|
let hasCommand = false;
|
|
1801
1946
|
try {
|
|
1802
|
-
|
|
1947
|
+
execSync4("which aider", { stdio: "ignore" });
|
|
1803
1948
|
hasCommand = true;
|
|
1804
1949
|
} catch {
|
|
1805
1950
|
}
|
|
@@ -2386,8 +2531,345 @@ ${transformed}`
|
|
|
2386
2531
|
Le(`Pull complete. ${updated} updated, ${skipped} up to date.`);
|
|
2387
2532
|
});
|
|
2388
2533
|
|
|
2534
|
+
// src/commands/publish.ts
|
|
2535
|
+
import { Command as Command6 } from "commander";
|
|
2536
|
+
import { readFileSync as readFileSync6, existsSync as existsSync14 } from "fs";
|
|
2537
|
+
import { resolve as resolve3, basename as basename2, extname as extname2 } from "path";
|
|
2538
|
+
|
|
2539
|
+
// src/lib/scanner.ts
|
|
2540
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
2541
|
+
import { join as join12, basename, extname } from "path";
|
|
2542
|
+
import { homedir as homedir9 } from "os";
|
|
2543
|
+
import { readlinkSync as readlinkSync2, lstatSync as lstatSync2 } from "fs";
|
|
2544
|
+
function scanForSkills(projectDir) {
|
|
2545
|
+
const home = homedir9();
|
|
2546
|
+
const cwd = projectDir || process.cwd();
|
|
2547
|
+
const results = [];
|
|
2548
|
+
scanDirectory(join12(home, ".cursor", "rules"), ".mdc", "cursor", "global", results);
|
|
2549
|
+
scanDirectory(join12(cwd, ".cursor", "rules"), ".mdc", "cursor", "project", results);
|
|
2550
|
+
scanClaudeSkills(join12(home, ".claude", "skills"), "global", results);
|
|
2551
|
+
scanClaudeSkills(join12(cwd, ".claude", "skills"), "project", results);
|
|
2552
|
+
scanSingleFile(join12(home, ".codex", "AGENTS.md"), "codex", "global", results);
|
|
2553
|
+
scanSingleFile(join12(cwd, "AGENTS.md"), "codex", "project", results);
|
|
2554
|
+
scanSingleFile(
|
|
2555
|
+
join12(home, ".codeium", "windsurf", "memories", "global_rules.md"),
|
|
2556
|
+
"windsurf",
|
|
2557
|
+
"global",
|
|
2558
|
+
results
|
|
2559
|
+
);
|
|
2560
|
+
scanDirectory(join12(cwd, ".windsurf", "rules"), ".md", "windsurf", "project", results);
|
|
2561
|
+
scanDirectory(join12(cwd, ".clinerules"), ".md", "cline", "project", results);
|
|
2562
|
+
scanSingleFile(
|
|
2563
|
+
join12(cwd, ".github", "copilot-instructions.md"),
|
|
2564
|
+
"copilot",
|
|
2565
|
+
"project",
|
|
2566
|
+
results
|
|
2567
|
+
);
|
|
2568
|
+
scanDirectory(join12(home, ".config", "opencode", "rules"), ".md", "opencode", "global", results);
|
|
2569
|
+
scanDirectory(join12(cwd, ".opencode", "rules"), ".md", "opencode", "project", results);
|
|
2570
|
+
scanDirectory(join12(cwd, ".aider", "skills"), ".md", "aider", "project", results);
|
|
2571
|
+
return results;
|
|
2572
|
+
}
|
|
2573
|
+
function filterTracked(detected, config) {
|
|
2574
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
2575
|
+
for (const skill of Object.values(config.installed_skills)) {
|
|
2576
|
+
for (const inst of skill.installations) {
|
|
2577
|
+
trackedPaths.add(inst.path);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
const cacheDir = join12(homedir9(), ".localskills", "cache");
|
|
2581
|
+
return detected.filter((skill) => {
|
|
2582
|
+
if (trackedPaths.has(skill.filePath)) return false;
|
|
2583
|
+
try {
|
|
2584
|
+
const stat = lstatSync2(skill.filePath);
|
|
2585
|
+
if (stat.isSymbolicLink()) {
|
|
2586
|
+
const target = readlinkSync2(skill.filePath);
|
|
2587
|
+
if (target.startsWith(cacheDir)) return false;
|
|
2588
|
+
}
|
|
2589
|
+
} catch {
|
|
2590
|
+
}
|
|
2591
|
+
return true;
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
function slugFromFilename(filename) {
|
|
2595
|
+
return basename(filename, extname(filename));
|
|
2596
|
+
}
|
|
2597
|
+
function nameFromSlug(slug) {
|
|
2598
|
+
return slug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2599
|
+
}
|
|
2600
|
+
function scanDirectory(dir, ext, platform, scope, results) {
|
|
2601
|
+
if (!existsSync13(dir)) return;
|
|
2602
|
+
let entries;
|
|
2603
|
+
try {
|
|
2604
|
+
entries = readdirSync2(dir);
|
|
2605
|
+
} catch {
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
for (const entry of entries) {
|
|
2609
|
+
if (!entry.endsWith(ext)) continue;
|
|
2610
|
+
const filePath = join12(dir, entry);
|
|
2611
|
+
try {
|
|
2612
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
2613
|
+
const content = stripFrontmatter(raw).trim();
|
|
2614
|
+
if (!content) continue;
|
|
2615
|
+
const slug = slugFromFilename(entry);
|
|
2616
|
+
results.push({
|
|
2617
|
+
filePath,
|
|
2618
|
+
platform,
|
|
2619
|
+
scope,
|
|
2620
|
+
suggestedName: nameFromSlug(slug),
|
|
2621
|
+
suggestedSlug: slug,
|
|
2622
|
+
content
|
|
2623
|
+
});
|
|
2624
|
+
} catch {
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
function scanClaudeSkills(skillsDir, scope, results) {
|
|
2629
|
+
if (!existsSync13(skillsDir)) return;
|
|
2630
|
+
let entries;
|
|
2631
|
+
try {
|
|
2632
|
+
entries = readdirSync2(skillsDir);
|
|
2633
|
+
} catch {
|
|
2634
|
+
return;
|
|
2635
|
+
}
|
|
2636
|
+
for (const entry of entries) {
|
|
2637
|
+
const skillFile = join12(skillsDir, entry, "SKILL.md");
|
|
2638
|
+
if (!existsSync13(skillFile)) continue;
|
|
2639
|
+
try {
|
|
2640
|
+
const raw = readFileSync5(skillFile, "utf-8");
|
|
2641
|
+
const content = stripFrontmatter(raw).trim();
|
|
2642
|
+
if (!content) continue;
|
|
2643
|
+
results.push({
|
|
2644
|
+
filePath: skillFile,
|
|
2645
|
+
platform: "claude",
|
|
2646
|
+
scope,
|
|
2647
|
+
suggestedName: nameFromSlug(entry),
|
|
2648
|
+
suggestedSlug: entry,
|
|
2649
|
+
content
|
|
2650
|
+
});
|
|
2651
|
+
} catch {
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
function scanSingleFile(filePath, platform, scope, results) {
|
|
2656
|
+
if (!existsSync13(filePath)) return;
|
|
2657
|
+
let raw;
|
|
2658
|
+
try {
|
|
2659
|
+
raw = readFileSync5(filePath, "utf-8");
|
|
2660
|
+
} catch {
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
const sections = listSections(filePath);
|
|
2664
|
+
if (sections.length > 0) {
|
|
2665
|
+
for (const slug2 of sections) {
|
|
2666
|
+
const startMarker = `<!-- localskills:start:${slug2} -->`;
|
|
2667
|
+
const endMarker = `<!-- localskills:end:${slug2} -->`;
|
|
2668
|
+
const startIdx = raw.indexOf(startMarker);
|
|
2669
|
+
const endIdx = raw.indexOf(endMarker);
|
|
2670
|
+
if (startIdx === -1 || endIdx === -1) continue;
|
|
2671
|
+
const content2 = raw.slice(startIdx + startMarker.length, endIdx).trim();
|
|
2672
|
+
if (!content2) continue;
|
|
2673
|
+
results.push({
|
|
2674
|
+
filePath,
|
|
2675
|
+
platform,
|
|
2676
|
+
scope,
|
|
2677
|
+
suggestedName: nameFromSlug(slug2),
|
|
2678
|
+
suggestedSlug: slug2,
|
|
2679
|
+
content: content2
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
const content = stripFrontmatter(raw).trim();
|
|
2685
|
+
if (!content) return;
|
|
2686
|
+
const slug = slugFromFilename(filePath);
|
|
2687
|
+
results.push({
|
|
2688
|
+
filePath,
|
|
2689
|
+
platform,
|
|
2690
|
+
scope,
|
|
2691
|
+
suggestedName: nameFromSlug(slug),
|
|
2692
|
+
suggestedSlug: slug,
|
|
2693
|
+
content
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
// src/commands/publish.ts
|
|
2698
|
+
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(
|
|
2699
|
+
"--visibility <visibility>",
|
|
2700
|
+
"Visibility: public, private, or unlisted",
|
|
2701
|
+
"private"
|
|
2702
|
+
).option("-m, --message <message>", "Version message").action(
|
|
2703
|
+
async (fileArg, opts) => {
|
|
2704
|
+
const client = new ApiClient();
|
|
2705
|
+
if (!client.isAuthenticated()) {
|
|
2706
|
+
console.error("Not authenticated. Run `localskills login` first.");
|
|
2707
|
+
process.exit(1);
|
|
2708
|
+
}
|
|
2709
|
+
const teamsRes = await client.get("/api/tenants");
|
|
2710
|
+
if (!teamsRes.success || !teamsRes.data || teamsRes.data.length === 0) {
|
|
2711
|
+
console.error(
|
|
2712
|
+
"No teams found. Create a team at localskills.sh first."
|
|
2713
|
+
);
|
|
2714
|
+
process.exit(1);
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
const teams = teamsRes.data;
|
|
2718
|
+
if (fileArg) {
|
|
2719
|
+
const filePath = resolve3(fileArg);
|
|
2720
|
+
if (!existsSync14(filePath)) {
|
|
2721
|
+
console.error(`File not found: ${filePath}`);
|
|
2722
|
+
process.exit(1);
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
const raw = readFileSync6(filePath, "utf-8");
|
|
2726
|
+
const content = stripFrontmatter(raw).trim();
|
|
2727
|
+
if (!content) {
|
|
2728
|
+
console.error("File is empty after stripping frontmatter.");
|
|
2729
|
+
process.exit(1);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
const defaultSlug = basename2(filePath, extname2(filePath));
|
|
2733
|
+
const defaultName = defaultSlug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2734
|
+
const skillName = opts.name || defaultName;
|
|
2735
|
+
const visibility = validateVisibility(opts.visibility || "private");
|
|
2736
|
+
const tenantId = await resolveTeam(teams, opts.team);
|
|
2737
|
+
await uploadSkill(client, {
|
|
2738
|
+
name: skillName,
|
|
2739
|
+
content,
|
|
2740
|
+
tenantId,
|
|
2741
|
+
visibility
|
|
2742
|
+
});
|
|
2743
|
+
} else {
|
|
2744
|
+
We("localskills publish");
|
|
2745
|
+
const spinner = bt2();
|
|
2746
|
+
spinner.start("Scanning for skills...");
|
|
2747
|
+
const config = loadConfig();
|
|
2748
|
+
const allDetected = scanForSkills();
|
|
2749
|
+
const detected = filterTracked(allDetected, config);
|
|
2750
|
+
spinner.stop(
|
|
2751
|
+
detected.length > 0 ? `Found ${detected.length} skill file${detected.length !== 1 ? "s" : ""}.` : "No unpublished skill files found."
|
|
2752
|
+
);
|
|
2753
|
+
if (detected.length === 0) {
|
|
2754
|
+
Le("Nothing to publish.");
|
|
2755
|
+
return;
|
|
2756
|
+
}
|
|
2757
|
+
const selected = await je({
|
|
2758
|
+
message: "Select skills to publish",
|
|
2759
|
+
options: detected.map((s) => ({
|
|
2760
|
+
value: s,
|
|
2761
|
+
label: s.suggestedName,
|
|
2762
|
+
hint: `${s.platform}/${s.scope} ${shortenPath(s.filePath)}`
|
|
2763
|
+
})),
|
|
2764
|
+
required: true
|
|
2765
|
+
});
|
|
2766
|
+
if (Ct(selected)) {
|
|
2767
|
+
Ne("Cancelled.");
|
|
2768
|
+
process.exit(0);
|
|
2769
|
+
}
|
|
2770
|
+
const skills = selected;
|
|
2771
|
+
const tenantId = await resolveTeam(teams, opts.team);
|
|
2772
|
+
for (const skill of skills) {
|
|
2773
|
+
R2.step(`Publishing ${skill.suggestedName}...`);
|
|
2774
|
+
const name = await Ze({
|
|
2775
|
+
message: "Skill name?",
|
|
2776
|
+
initialValue: skill.suggestedName,
|
|
2777
|
+
validate: (v) => {
|
|
2778
|
+
if (!v || v.length < 1) return "Name is required";
|
|
2779
|
+
if (v.length > 100) return "Name must be 100 characters or less";
|
|
2780
|
+
}
|
|
2781
|
+
});
|
|
2782
|
+
if (Ct(name)) {
|
|
2783
|
+
Ne("Cancelled.");
|
|
2784
|
+
process.exit(0);
|
|
2785
|
+
}
|
|
2786
|
+
const visibility = await Je({
|
|
2787
|
+
message: "Visibility?",
|
|
2788
|
+
options: [
|
|
2789
|
+
{ value: "private", label: "Private", hint: "Only team members" },
|
|
2790
|
+
{ value: "public", label: "Public", hint: "Anyone can install" },
|
|
2791
|
+
{ value: "unlisted", label: "Unlisted", hint: "Accessible via direct link" }
|
|
2792
|
+
],
|
|
2793
|
+
initialValue: "private"
|
|
2794
|
+
});
|
|
2795
|
+
if (Ct(visibility)) {
|
|
2796
|
+
Ne("Cancelled.");
|
|
2797
|
+
process.exit(0);
|
|
2798
|
+
}
|
|
2799
|
+
await uploadSkill(client, {
|
|
2800
|
+
name,
|
|
2801
|
+
content: skill.content,
|
|
2802
|
+
tenantId,
|
|
2803
|
+
visibility
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
Le("Done!");
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
);
|
|
2810
|
+
async function resolveTeam(teams, teamFlag) {
|
|
2811
|
+
if (teamFlag) {
|
|
2812
|
+
const match = teams.find((t) => t.id === teamFlag || t.slug === teamFlag);
|
|
2813
|
+
if (!match) {
|
|
2814
|
+
console.error(`Team not found: ${teamFlag}`);
|
|
2815
|
+
process.exit(1);
|
|
2816
|
+
}
|
|
2817
|
+
return match.id;
|
|
2818
|
+
}
|
|
2819
|
+
if (teams.length === 1) {
|
|
2820
|
+
return teams[0].id;
|
|
2821
|
+
}
|
|
2822
|
+
const selected = await Je({
|
|
2823
|
+
message: "Which team?",
|
|
2824
|
+
options: teams.map((t) => ({
|
|
2825
|
+
value: t.id,
|
|
2826
|
+
label: t.name,
|
|
2827
|
+
hint: t.slug
|
|
2828
|
+
}))
|
|
2829
|
+
});
|
|
2830
|
+
if (Ct(selected)) {
|
|
2831
|
+
Ne("Cancelled.");
|
|
2832
|
+
process.exit(0);
|
|
2833
|
+
}
|
|
2834
|
+
return selected;
|
|
2835
|
+
}
|
|
2836
|
+
async function uploadSkill(client, params) {
|
|
2837
|
+
const spinner = bt2();
|
|
2838
|
+
spinner.start(`Uploading ${params.name}...`);
|
|
2839
|
+
const res = await client.post("/api/skills", {
|
|
2840
|
+
name: params.name,
|
|
2841
|
+
content: params.content,
|
|
2842
|
+
tenantId: params.tenantId,
|
|
2843
|
+
visibility: params.visibility
|
|
2844
|
+
});
|
|
2845
|
+
if (!res.success || !res.data) {
|
|
2846
|
+
spinner.stop(`Failed: ${res.error || "Unknown error"}`);
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
spinner.stop(`Published!`);
|
|
2850
|
+
R2.success(`\u2192 localskills.sh/s/${res.data.slug}`);
|
|
2851
|
+
}
|
|
2852
|
+
function validateVisibility(value) {
|
|
2853
|
+
if (value === "public" || value === "private" || value === "unlisted") {
|
|
2854
|
+
return value;
|
|
2855
|
+
}
|
|
2856
|
+
console.error(`Invalid visibility: ${value}. Use public, private, or unlisted.`);
|
|
2857
|
+
process.exit(1);
|
|
2858
|
+
}
|
|
2859
|
+
function shortenPath(filePath) {
|
|
2860
|
+
const home = __require("os").homedir();
|
|
2861
|
+
if (filePath.startsWith(home)) {
|
|
2862
|
+
return "~" + filePath.slice(home.length);
|
|
2863
|
+
}
|
|
2864
|
+
const cwd = process.cwd();
|
|
2865
|
+
if (filePath.startsWith(cwd)) {
|
|
2866
|
+
return "." + filePath.slice(cwd.length);
|
|
2867
|
+
}
|
|
2868
|
+
return filePath;
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2389
2871
|
// src/index.ts
|
|
2390
|
-
var program = new
|
|
2872
|
+
var program = new Command7();
|
|
2391
2873
|
program.name("localskills").description("Install and manage agent skills from localskills.sh").version("0.1.0");
|
|
2392
2874
|
program.addCommand(loginCommand);
|
|
2393
2875
|
program.addCommand(logoutCommand);
|
|
@@ -2396,4 +2878,5 @@ program.addCommand(installCommand);
|
|
|
2396
2878
|
program.addCommand(uninstallCommand);
|
|
2397
2879
|
program.addCommand(listCommand);
|
|
2398
2880
|
program.addCommand(pullCommand);
|
|
2881
|
+
program.addCommand(publishCommand);
|
|
2399
2882
|
program.parse();
|