@dealdeploy/skl 1.10.0 → 1.10.2
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/.agents/skills/yolo/SKILL.md +23 -0
- package/add-tui.ts +291 -0
- package/index.ts +2 -1
- package/package.json +1 -1
- package/skills-lock.json +5 -0
- package/tui.ts +27 -17
- package/update.ts +94 -2
- package/.github/workflows/release.yml +0 -18
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yolo
|
|
3
|
+
description: Commit all changes, push to remote, and deploy in one shot with no confirmations. Use when user says "yolo", wants to ship fast, or asks to commit+push+deploy in one go.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# YOLO
|
|
7
|
+
|
|
8
|
+
Full send: commit, push, deploy. No stopping.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Stage everything**: `git add -A`
|
|
13
|
+
2. **Commit**: Write a concise commit message based on the diff. Do NOT ask the user for a message — just write one.
|
|
14
|
+
3. **Push**: `git push` to the current branch's remote. If no upstream, use `git push -u origin HEAD`.
|
|
15
|
+
4. **Deploy**: Run `bun run deploy` from the repo root. This does a patch bump by default.
|
|
16
|
+
- If the user says "minor" or "major", run `bun run deploy:minor` or `bun run deploy:major` instead.
|
|
17
|
+
|
|
18
|
+
## Rules
|
|
19
|
+
|
|
20
|
+
- Do NOT ask for confirmation at any step. Just do it.
|
|
21
|
+
- Do NOT pause between steps. Run them back to back.
|
|
22
|
+
- If any step fails, stop and report the error.
|
|
23
|
+
- Always include `Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>` in the commit message.
|
package/add-tui.ts
CHANGED
|
@@ -269,3 +269,294 @@ export function createAddTui(renderer: CliRenderer, deps: AddTuiDeps) {
|
|
|
269
269
|
},
|
|
270
270
|
};
|
|
271
271
|
}
|
|
272
|
+
|
|
273
|
+
// ── Multi-repo add TUI ──────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
export type AddSkillGroup = {
|
|
276
|
+
repo: string;
|
|
277
|
+
skills: AddSkillEntry[];
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export type MultiRepoAddTuiDeps = {
|
|
281
|
+
groups: AddSkillGroup[];
|
|
282
|
+
onConfirm: (selected: Map<string, AddSkillEntry[]>) => Promise<void>;
|
|
283
|
+
onCancel: () => void;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
type DisplayItem =
|
|
287
|
+
| { type: "header"; repo: string }
|
|
288
|
+
| { type: "skill"; skill: AddSkillEntry; groupIndex: number };
|
|
289
|
+
|
|
290
|
+
export function createMultiRepoAddTui(renderer: CliRenderer, deps: MultiRepoAddTuiDeps) {
|
|
291
|
+
const { groups } = deps;
|
|
292
|
+
|
|
293
|
+
const C = {
|
|
294
|
+
bg: "#1a1a2e",
|
|
295
|
+
rowBg: "#1a1a2e",
|
|
296
|
+
rowAltBg: "#1f1f38",
|
|
297
|
+
cursorBg: "#2a2a5a",
|
|
298
|
+
border: "#444477",
|
|
299
|
+
fg: "#ccccdd",
|
|
300
|
+
fgDim: "#666688",
|
|
301
|
+
checked: "#66ff88",
|
|
302
|
+
unchecked: "#555566",
|
|
303
|
+
warning: "#ffaa44",
|
|
304
|
+
accent: "#8888ff",
|
|
305
|
+
title: "#aaaaff",
|
|
306
|
+
footer: "#888899",
|
|
307
|
+
statusOk: "#66ff88",
|
|
308
|
+
statusErr: "#ff6666",
|
|
309
|
+
headerFg: "#9999bb",
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Build flat display list
|
|
313
|
+
const items: DisplayItem[] = [];
|
|
314
|
+
for (let gi = 0; gi < groups.length; gi++) {
|
|
315
|
+
const group = groups[gi]!;
|
|
316
|
+
items.push({ type: "header", repo: group.repo });
|
|
317
|
+
for (const skill of group.skills) {
|
|
318
|
+
items.push({ type: "skill", skill, groupIndex: gi });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let cursor = items.findIndex((it) => it.type === "skill");
|
|
323
|
+
if (cursor < 0) cursor = 0;
|
|
324
|
+
const checked = new Set<number>();
|
|
325
|
+
|
|
326
|
+
const outer = new BoxRenderable(renderer, {
|
|
327
|
+
id: "outer",
|
|
328
|
+
width: "100%",
|
|
329
|
+
height: "100%",
|
|
330
|
+
flexDirection: "column",
|
|
331
|
+
backgroundColor: C.bg,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const header = new TextRenderable(renderer, {
|
|
335
|
+
id: "header",
|
|
336
|
+
content: ` New skills (0 selected)`,
|
|
337
|
+
fg: C.title,
|
|
338
|
+
attributes: TextAttributes.BOLD,
|
|
339
|
+
height: 1,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const sep = new TextRenderable(renderer, {
|
|
343
|
+
id: "sep",
|
|
344
|
+
content: "\u2500".repeat(60),
|
|
345
|
+
fg: C.border,
|
|
346
|
+
height: 1,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const scrollBox = new ScrollBoxRenderable(renderer, {
|
|
350
|
+
id: "skill-list",
|
|
351
|
+
flexGrow: 1,
|
|
352
|
+
width: "100%",
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
type RowRef = {
|
|
356
|
+
row: BoxRenderable;
|
|
357
|
+
text: TextRenderable;
|
|
358
|
+
checkText?: TextRenderable;
|
|
359
|
+
};
|
|
360
|
+
const rows: RowRef[] = [];
|
|
361
|
+
|
|
362
|
+
for (let i = 0; i < items.length; i++) {
|
|
363
|
+
const item = items[i]!;
|
|
364
|
+
|
|
365
|
+
if (item.type === "header") {
|
|
366
|
+
const row = new BoxRenderable(renderer, {
|
|
367
|
+
id: `row-${i}`,
|
|
368
|
+
flexDirection: "row",
|
|
369
|
+
height: 1,
|
|
370
|
+
width: "100%",
|
|
371
|
+
paddingLeft: 1,
|
|
372
|
+
backgroundColor: C.bg,
|
|
373
|
+
});
|
|
374
|
+
const text = new TextRenderable(renderer, {
|
|
375
|
+
id: `text-${i}`,
|
|
376
|
+
content: ` ${item.repo}`,
|
|
377
|
+
fg: C.headerFg,
|
|
378
|
+
attributes: TextAttributes.BOLD,
|
|
379
|
+
});
|
|
380
|
+
row.add(text);
|
|
381
|
+
scrollBox.add(row);
|
|
382
|
+
rows.push({ row, text });
|
|
383
|
+
} else {
|
|
384
|
+
const row = new BoxRenderable(renderer, {
|
|
385
|
+
id: `row-${i}`,
|
|
386
|
+
flexDirection: "row",
|
|
387
|
+
height: 1,
|
|
388
|
+
width: "100%",
|
|
389
|
+
paddingLeft: 1,
|
|
390
|
+
backgroundColor: i % 2 === 0 ? C.rowBg : C.rowAltBg,
|
|
391
|
+
});
|
|
392
|
+
const checkText = new TextRenderable(renderer, {
|
|
393
|
+
id: `check-${i}`,
|
|
394
|
+
content: "[ ]",
|
|
395
|
+
fg: C.unchecked,
|
|
396
|
+
width: 4,
|
|
397
|
+
});
|
|
398
|
+
const text = new TextRenderable(renderer, {
|
|
399
|
+
id: `name-${i}`,
|
|
400
|
+
content: ` ${item.skill.name}`,
|
|
401
|
+
fg: C.fg,
|
|
402
|
+
width: 40,
|
|
403
|
+
});
|
|
404
|
+
row.add(checkText);
|
|
405
|
+
row.add(text);
|
|
406
|
+
scrollBox.add(row);
|
|
407
|
+
rows.push({ row, text, checkText });
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const footerSep = new TextRenderable(renderer, {
|
|
412
|
+
id: "footer-sep",
|
|
413
|
+
content: "\u2500".repeat(60),
|
|
414
|
+
fg: C.border,
|
|
415
|
+
height: 1,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const footer = new TextRenderable(renderer, {
|
|
419
|
+
id: "footer",
|
|
420
|
+
content: " j/k move space toggle a all enter confirm q cancel",
|
|
421
|
+
fg: C.footer,
|
|
422
|
+
height: 1,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const statusLine = new TextRenderable(renderer, {
|
|
426
|
+
id: "status",
|
|
427
|
+
content: "",
|
|
428
|
+
fg: C.statusOk,
|
|
429
|
+
height: 1,
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
outer.add(header);
|
|
433
|
+
outer.add(sep);
|
|
434
|
+
outer.add(scrollBox);
|
|
435
|
+
outer.add(footerSep);
|
|
436
|
+
outer.add(footer);
|
|
437
|
+
outer.add(statusLine);
|
|
438
|
+
renderer.root.add(outer);
|
|
439
|
+
|
|
440
|
+
let statusTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
441
|
+
|
|
442
|
+
function setStatus(msg: string, color: string) {
|
|
443
|
+
statusLine.content = ` ${msg}`;
|
|
444
|
+
statusLine.fg = color;
|
|
445
|
+
if (statusTimeout) clearTimeout(statusTimeout);
|
|
446
|
+
statusTimeout = setTimeout(() => {
|
|
447
|
+
statusLine.content = "";
|
|
448
|
+
}, 3000);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function updateHeader() {
|
|
452
|
+
header.content = ` New skills (${checked.size} selected)`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function updateRow(i: number) {
|
|
456
|
+
const item = items[i]!;
|
|
457
|
+
const r = rows[i]!;
|
|
458
|
+
const isCursor = cursor === i;
|
|
459
|
+
|
|
460
|
+
if (item.type === "header") return;
|
|
461
|
+
|
|
462
|
+
const baseBg = i % 2 === 0 ? C.rowBg : C.rowAltBg;
|
|
463
|
+
r.row.backgroundColor = isCursor ? C.cursorBg : baseBg;
|
|
464
|
+
|
|
465
|
+
const isChecked = checked.has(i);
|
|
466
|
+
r.checkText!.content = isChecked ? "[x]" : "[ ]";
|
|
467
|
+
r.checkText!.fg = isCursor ? C.accent : (isChecked ? C.checked : C.unchecked);
|
|
468
|
+
const pointer = isCursor ? "\u25b8" : " ";
|
|
469
|
+
r.text.content = `${pointer} ${item.skill.name}`;
|
|
470
|
+
r.text.fg = isCursor ? "#ffffff" : C.fg;
|
|
471
|
+
r.text.attributes = isCursor ? TextAttributes.BOLD : TextAttributes.NONE;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function refreshAll() {
|
|
475
|
+
for (let i = 0; i < items.length; i++) updateRow(i);
|
|
476
|
+
updateHeader();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function ensureVisible() {
|
|
480
|
+
scrollBox.scrollTo(Math.max(0, cursor - 2));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function nextSkill(from: number, dir: 1 | -1): number {
|
|
484
|
+
let pos = from + dir;
|
|
485
|
+
while (pos >= 0 && pos < items.length) {
|
|
486
|
+
if (items[pos]!.type === "skill") return pos;
|
|
487
|
+
pos += dir;
|
|
488
|
+
}
|
|
489
|
+
return from;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
refreshAll();
|
|
493
|
+
|
|
494
|
+
renderer.keyInput.on("keypress", (key: KeyEvent) => {
|
|
495
|
+
const prevCursor = cursor;
|
|
496
|
+
|
|
497
|
+
const navKey = (key.ctrl && { h: "left", j: "down", k: "up", l: "right" }[key.name]) || key.name;
|
|
498
|
+
|
|
499
|
+
switch (navKey) {
|
|
500
|
+
case "j":
|
|
501
|
+
case "down":
|
|
502
|
+
cursor = nextSkill(cursor, 1);
|
|
503
|
+
break;
|
|
504
|
+
case "k":
|
|
505
|
+
case "up":
|
|
506
|
+
cursor = nextSkill(cursor, -1);
|
|
507
|
+
break;
|
|
508
|
+
case "space": {
|
|
509
|
+
if (items[cursor]?.type !== "skill") break;
|
|
510
|
+
if (checked.has(cursor)) {
|
|
511
|
+
checked.delete(cursor);
|
|
512
|
+
} else {
|
|
513
|
+
checked.add(cursor);
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
case "a": {
|
|
518
|
+
const skillIndices = items
|
|
519
|
+
.map((it, i) => (it.type === "skill" ? i : -1))
|
|
520
|
+
.filter((i) => i >= 0);
|
|
521
|
+
const allChecked = skillIndices.every((i) => checked.has(i));
|
|
522
|
+
if (allChecked) {
|
|
523
|
+
for (const i of skillIndices) checked.delete(i);
|
|
524
|
+
} else {
|
|
525
|
+
for (const i of skillIndices) checked.add(i);
|
|
526
|
+
}
|
|
527
|
+
for (const i of skillIndices) updateRow(i);
|
|
528
|
+
updateHeader();
|
|
529
|
+
ensureVisible();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
case "return": {
|
|
533
|
+
const selected = new Map<string, AddSkillEntry[]>();
|
|
534
|
+
for (const idx of checked) {
|
|
535
|
+
const item = items[idx]!;
|
|
536
|
+
if (item.type !== "skill") continue;
|
|
537
|
+
const repo = groups[item.groupIndex]!.repo;
|
|
538
|
+
const list = selected.get(repo) ?? [];
|
|
539
|
+
list.push(item.skill);
|
|
540
|
+
selected.set(repo, list);
|
|
541
|
+
}
|
|
542
|
+
if (selected.size === 0) {
|
|
543
|
+
setStatus("Nothing selected \u2014 use space to toggle", C.warning);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
deps.onConfirm(selected);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
case "q":
|
|
550
|
+
case "escape":
|
|
551
|
+
deps.onCancel();
|
|
552
|
+
return;
|
|
553
|
+
default:
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (prevCursor !== cursor) updateRow(prevCursor);
|
|
558
|
+
updateRow(cursor);
|
|
559
|
+
updateHeader();
|
|
560
|
+
ensureVisible();
|
|
561
|
+
});
|
|
562
|
+
}
|
package/index.ts
CHANGED
|
@@ -30,7 +30,8 @@ Usage:
|
|
|
30
30
|
skl Open the interactive TUI
|
|
31
31
|
skl add <repo> Add skills from a GitHub repo (interactive)
|
|
32
32
|
skl add <repo> --all Add all skills from a repo (non-interactive)
|
|
33
|
-
skl update
|
|
33
|
+
skl update Update all remote skills
|
|
34
|
+
skl update --new, -n Update and discover new skills from repos
|
|
34
35
|
skl migrate Migrate old symlinks to use the catalog
|
|
35
36
|
skl migrate -l Migrate local project symlinks
|
|
36
37
|
skl help Show this help message
|
package/package.json
CHANGED
package/skills-lock.json
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"source": "vercel-labs/agent-browser",
|
|
6
6
|
"sourceType": "github",
|
|
7
7
|
"computedHash": "46eddb8bcb7de8e7c75ff53ee54bf764a576a923d19765b4ef6a71bb04755db6"
|
|
8
|
+
},
|
|
9
|
+
"yolo": {
|
|
10
|
+
"source": "matiacone/dotfiles",
|
|
11
|
+
"sourceType": "github",
|
|
12
|
+
"computedHash": "f60574ed5c0226568c153a01804b7ee0552e9dde4af13af28b1629b037d5bae1"
|
|
8
13
|
}
|
|
9
14
|
}
|
|
10
15
|
}
|
package/tui.ts
CHANGED
|
@@ -265,8 +265,6 @@ export function createTui(renderer: CliRenderer, deps: TuiDeps) {
|
|
|
265
265
|
};
|
|
266
266
|
const rows: RowRefs[] = new Array(allSkills.length);
|
|
267
267
|
|
|
268
|
-
scrollBox.add(searchRow);
|
|
269
|
-
|
|
270
268
|
for (let di = 0; di < displayItems.length; di++) {
|
|
271
269
|
const item = displayItems[di]!;
|
|
272
270
|
if (item.type === "header") {
|
|
@@ -369,6 +367,7 @@ export function createTui(renderer: CliRenderer, deps: TuiDeps) {
|
|
|
369
367
|
|
|
370
368
|
outer.add(colHeaderRow);
|
|
371
369
|
outer.add(sep);
|
|
370
|
+
outer.add(searchRow);
|
|
372
371
|
outer.add(scrollBox);
|
|
373
372
|
outer.add(footerSep);
|
|
374
373
|
outer.add(footer);
|
|
@@ -509,23 +508,34 @@ export function createTui(renderer: CliRenderer, deps: TuiDeps) {
|
|
|
509
508
|
function ensureVisible() {
|
|
510
509
|
if (focusArea === "search") {
|
|
511
510
|
scrollBox.scrollTo(0);
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
if (
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const targetDi = filteredDisplayIndices[cursor];
|
|
515
|
+
if (targetDi === undefined) return;
|
|
516
|
+
|
|
517
|
+
let cursorPos = 0;
|
|
518
|
+
for (let di = 0; di < targetDi; di++) {
|
|
519
|
+
const item = displayItems[di]!;
|
|
520
|
+
if (item.type === "header") {
|
|
521
|
+
const ref = headerRefs.get(di)!;
|
|
522
|
+
if (ref.row.visible) {
|
|
523
|
+
cursorPos++;
|
|
524
|
+
if (ref.spacer.visible) cursorPos++;
|
|
526
525
|
}
|
|
526
|
+
} else {
|
|
527
|
+
if (rows[item.skillIndex]!.row.visible) cursorPos++;
|
|
527
528
|
}
|
|
528
|
-
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const top = scrollBox.scrollTop;
|
|
532
|
+
const termH = (renderer as any).height ?? process.stdout.rows ?? 24;
|
|
533
|
+
const viewportH = termH - 6;
|
|
534
|
+
|
|
535
|
+
if (cursorPos < top) {
|
|
536
|
+
scrollBox.scrollTo(cursorPos);
|
|
537
|
+
} else if (cursorPos >= top + viewportH) {
|
|
538
|
+
scrollBox.scrollTo(cursorPos - viewportH + 1);
|
|
529
539
|
}
|
|
530
540
|
}
|
|
531
541
|
|
package/update.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { rmSync, mkdirSync } from "fs";
|
|
1
|
+
import { rmSync, mkdirSync, lstatSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
import {
|
|
4
4
|
readLock, writeLock, catalogDir, downloadSkillFiles,
|
|
5
|
-
groupByRepo, planUpdates,
|
|
5
|
+
groupByRepo, planUpdates, findSkillEntries, addToLock,
|
|
6
|
+
type TreeEntry, type LockEntry,
|
|
6
7
|
} from "./lib.ts";
|
|
7
8
|
|
|
9
|
+
const newFlag = process.argv.includes("--new") || process.argv.includes("-n");
|
|
10
|
+
|
|
8
11
|
const lock = readLock();
|
|
9
12
|
const byRepo = groupByRepo(lock);
|
|
10
13
|
|
|
@@ -31,6 +34,9 @@ let totalUpToDate = 0;
|
|
|
31
34
|
let totalErrors = 0;
|
|
32
35
|
const CATALOG = catalogDir();
|
|
33
36
|
|
|
37
|
+
type RepoTreeInfo = { repo: string; branch: string; tree: TreeEntry[] };
|
|
38
|
+
const repoTrees: RepoTreeInfo[] = [];
|
|
39
|
+
|
|
34
40
|
for (const [repo, skills] of byRepo) {
|
|
35
41
|
process.stdout.write(`Checking ${repo}...`);
|
|
36
42
|
|
|
@@ -71,6 +77,8 @@ for (const [repo, skills] of byRepo) {
|
|
|
71
77
|
|
|
72
78
|
console.log("");
|
|
73
79
|
|
|
80
|
+
if (newFlag) repoTrees.push({ repo, branch, tree });
|
|
81
|
+
|
|
74
82
|
const plan = planUpdates(skills, tree);
|
|
75
83
|
|
|
76
84
|
totalUpToDate += plan.upToDate.length;
|
|
@@ -104,3 +112,87 @@ if (totalUpdated > 0) parts.push(`Updated ${totalUpdated} skill(s)`);
|
|
|
104
112
|
if (totalUpToDate > 0) parts.push(`${totalUpToDate} already up to date`);
|
|
105
113
|
if (totalErrors > 0) parts.push(`${totalErrors} error(s)`);
|
|
106
114
|
console.log(`\n${parts.join(", ")}`);
|
|
115
|
+
|
|
116
|
+
// ── New-skills phase ──────────────────────────────────────────────────
|
|
117
|
+
if (newFlag && repoTrees.length > 0) {
|
|
118
|
+
const { createMultiRepoAddTui } = await import("./add-tui.ts");
|
|
119
|
+
|
|
120
|
+
const freshLock = readLock();
|
|
121
|
+
const groups: { repo: string; branch: string; skills: import("./add-tui.ts").AddSkillEntry[] }[] = [];
|
|
122
|
+
|
|
123
|
+
for (const { repo, branch, tree } of repoTrees) {
|
|
124
|
+
const entries = findSkillEntries(tree);
|
|
125
|
+
const newSkills = entries.filter((e) => !freshLock.skills[e.name]);
|
|
126
|
+
if (newSkills.length === 0) continue;
|
|
127
|
+
|
|
128
|
+
groups.push({
|
|
129
|
+
repo,
|
|
130
|
+
branch,
|
|
131
|
+
skills: newSkills.map((e) => ({
|
|
132
|
+
name: e.name,
|
|
133
|
+
prefix: e.prefix,
|
|
134
|
+
treeSHA: e.treeSHA,
|
|
135
|
+
exists: false,
|
|
136
|
+
})),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (groups.length === 0) {
|
|
141
|
+
console.log("\nNo new skills found.");
|
|
142
|
+
} else {
|
|
143
|
+
const total = groups.reduce((sum, g) => sum + g.skills.length, 0);
|
|
144
|
+
console.log(`\nFound ${total} new skill(s) across ${groups.length} repo(s).`);
|
|
145
|
+
|
|
146
|
+
const { createCliRenderer } = await import("@opentui/core");
|
|
147
|
+
const renderer = await createCliRenderer({ exitOnCtrlC: true });
|
|
148
|
+
|
|
149
|
+
await new Promise<void>((resolve) => {
|
|
150
|
+
createMultiRepoAddTui(renderer, {
|
|
151
|
+
groups,
|
|
152
|
+
async onConfirm(selected) {
|
|
153
|
+
renderer.destroy();
|
|
154
|
+
mkdirSync(CATALOG, { recursive: true });
|
|
155
|
+
let added = 0;
|
|
156
|
+
|
|
157
|
+
for (const [repoKey, skills] of selected) {
|
|
158
|
+
const repoInfo = repoTrees.find((r) => r.repo === repoKey)!;
|
|
159
|
+
for (const skill of skills) {
|
|
160
|
+
const skillDir = join(CATALOG, skill.name);
|
|
161
|
+
try {
|
|
162
|
+
if (lstatSync(skillDir).isSymbolicLink()) {
|
|
163
|
+
rmSync(skillDir, { force: true });
|
|
164
|
+
}
|
|
165
|
+
} catch {}
|
|
166
|
+
mkdirSync(skillDir, { recursive: true });
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const fileCount = await downloadSkillFiles(
|
|
170
|
+
repoKey, repoInfo.branch, skill.prefix, skillDir, repoInfo.tree,
|
|
171
|
+
);
|
|
172
|
+
const entry: LockEntry = {
|
|
173
|
+
source: repoKey,
|
|
174
|
+
sourceUrl: `https://github.com/${repoKey}`,
|
|
175
|
+
skillPath: skill.prefix,
|
|
176
|
+
treeSHA: skill.treeSHA,
|
|
177
|
+
addedAt: new Date().toISOString(),
|
|
178
|
+
};
|
|
179
|
+
addToLock(skill.name, entry);
|
|
180
|
+
console.log(` ${skill.name} — added (${fileCount} files)`);
|
|
181
|
+
added++;
|
|
182
|
+
} catch (e: any) {
|
|
183
|
+
console.log(` ${skill.name} — failed (${e.message})`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(`\nAdded ${added} new skill(s) to ~/.skl/catalog/`);
|
|
189
|
+
resolve();
|
|
190
|
+
},
|
|
191
|
+
onCancel() {
|
|
192
|
+
renderer.destroy();
|
|
193
|
+
resolve();
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
name: Publish
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
publish:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
- uses: oven-sh/setup-bun@v2
|
|
14
|
-
- run: bun install
|
|
15
|
-
- run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
|
16
|
-
env:
|
|
17
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
18
|
-
- run: npm publish --access public
|