ht-skills 0.2.0 → 0.2.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/README.md +3 -3
- package/lib/cli.js +142 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,13 +12,13 @@ npx ht-skills add --skill repo-bug-analyze
|
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
14
|
ht-skills login [--registry <url>]
|
|
15
|
-
ht-skills publish [skillDir] [--registry <url>] [--
|
|
15
|
+
ht-skills publish [skillDir] [--registry <url>] [--access public|private|shared] [--shared-with a@b.com,c@d.com] [--publish-now]
|
|
16
16
|
ht-skills search <query> [--registry <url>] [--limit <n>]
|
|
17
17
|
ht-skills submit <skillDir> [--registry <url>] [--submitter <name>] [--visibility public|private|shared] [--shared-with a@b.com,c@d.com] [--publish-now]
|
|
18
18
|
ht-skills install <slug[@version]> [more-skills...] [--registry <url>] [--target <dir>] [--tool codex|claude|vscode]
|
|
19
19
|
ht-skills add [registry] --skill <slug[@version]>,<slug[@version]> [--tool codex|claude|vscode]
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
`login`
|
|
22
|
+
`login` prints a dedicated `/cli-login` URL, waits for Enter, opens the browser, and stores the approved registry token under `~/.ht-skills/config.json`.
|
|
23
23
|
|
|
24
|
-
`publish` zips the target skill directory, uploads it through the marketplace package inspection flow, and then submits the generated preview token for review.
|
|
24
|
+
`publish` zips the target skill directory, uploads it through the marketplace package inspection flow, and then submits the generated preview token for review. The default access is `public`; use `--access private` to keep the skill private before review.
|
package/lib/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ function printHelp() {
|
|
|
48
48
|
console.log(`Usage:
|
|
49
49
|
ht-skills search <query> [--registry <url>] [--limit <n>]
|
|
50
50
|
ht-skills login [--registry <url>]
|
|
51
|
-
ht-skills publish [skillDir] [--registry <url>] [--
|
|
51
|
+
ht-skills publish [skillDir] [--registry <url>] [--access public|private|shared] [--shared-with a@b.com,c@d.com] [--publish-now]
|
|
52
52
|
ht-skills submit <skillDir> [--registry <url>] [--submitter <name>] [--visibility public|private|shared] [--shared-with a@b.com,c@d.com] [--publish-now]
|
|
53
53
|
ht-skills install <slug[@version]> [more-skills...] [--registry <url>] [--target <dir>] [--tool codex|claude|vscode]
|
|
54
54
|
ht-skills add [registry] --skill <slug[@version]>,<slug[@version]> [--tool codex|claude|vscode]
|
|
@@ -57,6 +57,7 @@ Examples:
|
|
|
57
57
|
ht-skills search openai
|
|
58
58
|
ht-skills login
|
|
59
59
|
ht-skills publish .
|
|
60
|
+
ht-skills publish . --access private
|
|
60
61
|
ht-skills submit ./examples/hello-skill
|
|
61
62
|
ht-skills install hello-skill@1.0.0 --tool codex
|
|
62
63
|
ht-skills install hello-skill repo-bug-analyze --tool codex
|
|
@@ -238,6 +239,32 @@ function summarizePreviewErrors(preview) {
|
|
|
238
239
|
return errors.slice(0, 4).join("; ");
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
async function promptToOpenBrowser(url, deps = {}) {
|
|
243
|
+
const ask = deps.ask || null;
|
|
244
|
+
if (ask) {
|
|
245
|
+
await ask(`Press Enter to open the browser for:\n${url}\n`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const isInteractive = typeof deps.isInteractive === "boolean"
|
|
250
|
+
? deps.isInteractive
|
|
251
|
+
: Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
252
|
+
|
|
253
|
+
if (!isInteractive) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const rl = readline.createInterface({
|
|
258
|
+
input: process.stdin,
|
|
259
|
+
output: process.stdout,
|
|
260
|
+
});
|
|
261
|
+
try {
|
|
262
|
+
await rl.question(`Press Enter to open the browser for:\n${url}\n`);
|
|
263
|
+
} finally {
|
|
264
|
+
rl.close();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
241
268
|
function formatInstallError(error, { slug, version = null, registry, stage = "resolve" }) {
|
|
242
269
|
const rawMessage = String(error?.message || "Install failed").trim();
|
|
243
270
|
const status = Number(error?.status || 0);
|
|
@@ -431,16 +458,89 @@ function normalizeInlineText(value) {
|
|
|
431
458
|
return String(value || "").replace(/\s+/g, " ").trim();
|
|
432
459
|
}
|
|
433
460
|
|
|
461
|
+
const ANSI_PATTERN = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
462
|
+
|
|
463
|
+
function stripAnsi(value) {
|
|
464
|
+
return String(value || "").replace(ANSI_PATTERN, "");
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function isFullwidthCodePoint(codePoint) {
|
|
468
|
+
if (!Number.isFinite(codePoint)) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return codePoint >= 0x1100 && (
|
|
473
|
+
codePoint <= 0x115f ||
|
|
474
|
+
codePoint === 0x2329 ||
|
|
475
|
+
codePoint === 0x232a ||
|
|
476
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
477
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
478
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
479
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
480
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
481
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
482
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
483
|
+
(codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
|
|
484
|
+
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
|
485
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function getCharacterWidth(character) {
|
|
490
|
+
const codePoint = character.codePointAt(0);
|
|
491
|
+
if (!Number.isFinite(codePoint)) {
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
if (
|
|
495
|
+
codePoint === 0 ||
|
|
496
|
+
(codePoint >= 0x0000 && codePoint <= 0x001f) ||
|
|
497
|
+
(codePoint >= 0x007f && codePoint <= 0x009f)
|
|
498
|
+
) {
|
|
499
|
+
return 0;
|
|
500
|
+
}
|
|
501
|
+
return isFullwidthCodePoint(codePoint) ? 2 : 1;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function getDisplayWidth(value) {
|
|
505
|
+
let width = 0;
|
|
506
|
+
for (const character of stripAnsi(value)) {
|
|
507
|
+
width += getCharacterWidth(character);
|
|
508
|
+
}
|
|
509
|
+
return width;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function sliceTextByDisplayWidth(value, maxWidth) {
|
|
513
|
+
const safeWidth = Math.max(1, Number(maxWidth || 0));
|
|
514
|
+
const characters = [...String(value || "")];
|
|
515
|
+
let width = 0;
|
|
516
|
+
let index = 0;
|
|
517
|
+
|
|
518
|
+
while (index < characters.length) {
|
|
519
|
+
const characterWidth = getCharacterWidth(characters[index]);
|
|
520
|
+
if (width + characterWidth > safeWidth) {
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
width += characterWidth;
|
|
524
|
+
index += 1;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
slice: characters.slice(0, index).join(""),
|
|
529
|
+
rest: characters.slice(index).join(""),
|
|
530
|
+
width,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
434
534
|
function truncateText(value, maxLength) {
|
|
435
535
|
const text = normalizeInlineText(value);
|
|
436
536
|
const safeMaxLength = Math.max(1, Number(maxLength || 0));
|
|
437
|
-
if (text
|
|
537
|
+
if (getDisplayWidth(text) <= safeMaxLength) {
|
|
438
538
|
return text;
|
|
439
539
|
}
|
|
440
540
|
if (safeMaxLength <= 1) {
|
|
441
541
|
return "…";
|
|
442
542
|
}
|
|
443
|
-
return `${text
|
|
543
|
+
return `${sliceTextByDisplayWidth(text, safeMaxLength - 1).slice.trimEnd()}…`;
|
|
444
544
|
}
|
|
445
545
|
|
|
446
546
|
function wrapText(value, maxWidth) {
|
|
@@ -448,37 +548,37 @@ function wrapText(value, maxWidth) {
|
|
|
448
548
|
const safeWidth = Math.max(1, Number(maxWidth || 0));
|
|
449
549
|
if (!text) return [""];
|
|
450
550
|
|
|
451
|
-
const words = text.split(" ");
|
|
452
551
|
const lines = [];
|
|
453
|
-
let
|
|
552
|
+
let remaining = text;
|
|
454
553
|
|
|
455
|
-
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
lines.push(truncateText(word, safeWidth));
|
|
462
|
-
continue;
|
|
554
|
+
while (remaining) {
|
|
555
|
+
if (getDisplayWidth(remaining) <= safeWidth) {
|
|
556
|
+
lines.push(remaining);
|
|
557
|
+
break;
|
|
463
558
|
}
|
|
464
559
|
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
560
|
+
const characters = [...remaining];
|
|
561
|
+
let width = 0;
|
|
562
|
+
let index = 0;
|
|
563
|
+
let lastBreakIndex = -1;
|
|
470
564
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
565
|
+
while (index < characters.length) {
|
|
566
|
+
const character = characters[index];
|
|
567
|
+
const characterWidth = getCharacterWidth(character);
|
|
568
|
+
if (width + characterWidth > safeWidth) {
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
width += characterWidth;
|
|
572
|
+
index += 1;
|
|
573
|
+
if (character === " ") {
|
|
574
|
+
lastBreakIndex = index;
|
|
575
|
+
}
|
|
477
576
|
}
|
|
478
|
-
}
|
|
479
577
|
|
|
480
|
-
|
|
481
|
-
|
|
578
|
+
const breakIndex = lastBreakIndex > 0 ? lastBreakIndex : Math.max(1, index);
|
|
579
|
+
const line = characters.slice(0, breakIndex).join("").trimEnd();
|
|
580
|
+
lines.push(line);
|
|
581
|
+
remaining = characters.slice(breakIndex).join("").trimStart();
|
|
482
582
|
}
|
|
483
583
|
|
|
484
584
|
return lines.length ? lines : [""];
|
|
@@ -563,7 +663,7 @@ function formatSearchResult(item, { index = 0, colorize = (text) => text, width
|
|
|
563
663
|
}
|
|
564
664
|
|
|
565
665
|
const separator = " ";
|
|
566
|
-
const availableDescriptionWidth = width - plainPrefix
|
|
666
|
+
const availableDescriptionWidth = width - getDisplayWidth(plainPrefix) - getDisplayWidth(separator);
|
|
567
667
|
if (availableDescriptionWidth < minDescriptionWidth) {
|
|
568
668
|
return truncateText(`${plainPrefix}${separator}${descriptionPart}`, width);
|
|
569
669
|
}
|
|
@@ -578,25 +678,26 @@ function formatSearchCard(item, { index = 0, colorize = (text) => text, width =
|
|
|
578
678
|
const slugPart = `${item.slug}@${item.latestVersion}`;
|
|
579
679
|
const namePart = normalizeInlineText(item.name || "");
|
|
580
680
|
const descriptionPart = normalizeInlineText(item.description || "");
|
|
581
|
-
const maxInnerWidth = Math.max(
|
|
681
|
+
const maxInnerWidth = Math.max(16, width - 6);
|
|
582
682
|
const titleText = `${slugPart}${namePart ? ` (${namePart})` : ""}`;
|
|
583
683
|
const detailLines = wrapText(descriptionPart || "-", maxInnerWidth);
|
|
584
684
|
const contentWidth = Math.max(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
685
|
+
16,
|
|
686
|
+
getDisplayWidth(ordinal) + 1,
|
|
687
|
+
Math.min(maxInnerWidth, getDisplayWidth(titleText)),
|
|
688
|
+
...detailLines.map((line) => Math.min(maxInnerWidth, getDisplayWidth(line))),
|
|
588
689
|
);
|
|
589
|
-
const titleSuffix = "─".repeat(Math.max(0, contentWidth - ordinal
|
|
690
|
+
const titleSuffix = "─".repeat(Math.max(0, contentWidth - getDisplayWidth(ordinal) - 1));
|
|
590
691
|
const title = `${colorize("◇", "success")} ${colorize(ordinal, "accent")} ${colorize(titleSuffix, "muted")}╮`;
|
|
591
692
|
const blank = `│ ${" ".repeat(contentWidth)} │`;
|
|
592
693
|
const firstLineText = truncateText(titleText, contentWidth);
|
|
593
|
-
const firstLine = `│ ${colorize(firstLineText, "accent")}${" ".repeat(Math.max(0, contentWidth - firstLineText
|
|
694
|
+
const firstLine = `│ ${colorize(firstLineText, "accent")}${" ".repeat(Math.max(0, contentWidth - getDisplayWidth(firstLineText)))} │`;
|
|
594
695
|
const detailRendered = detailLines.map((line) => {
|
|
595
696
|
const text = truncateText(line, contentWidth);
|
|
596
|
-
return `│ ${text}${" ".repeat(Math.max(0, contentWidth - text
|
|
697
|
+
return `│ ${text}${" ".repeat(Math.max(0, contentWidth - getDisplayWidth(text)))} │`;
|
|
597
698
|
});
|
|
598
699
|
const bottom = `╰${"─".repeat(contentWidth + 2)}╯`;
|
|
599
|
-
return [title, blank, firstLine, ...detailRendered, bottom].join("\n");
|
|
700
|
+
return [title, blank, firstLine, ...detailRendered, blank, bottom].join("\n");
|
|
600
701
|
}
|
|
601
702
|
|
|
602
703
|
function printFallbackIntro({ registry, slug, version, skillName, skillDescription, installTargets }, log = console.log) {
|
|
@@ -1178,6 +1279,9 @@ async function cmdLogin(flags, deps = {}) {
|
|
|
1178
1279
|
throw new Error("registry did not return a usable CLI login flow");
|
|
1179
1280
|
}
|
|
1180
1281
|
|
|
1282
|
+
log(`Complete CLI sign-in here:\n${browserUrl}`);
|
|
1283
|
+
await promptToOpenBrowser(browserUrl, deps);
|
|
1284
|
+
|
|
1181
1285
|
try {
|
|
1182
1286
|
await Promise.resolve(openBrowser(browserUrl));
|
|
1183
1287
|
log(`Opened browser for ${registry}. Complete sign-in to continue...`);
|
|
@@ -1254,12 +1358,11 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1254
1358
|
throw new Error(summarizePreviewErrors(preview));
|
|
1255
1359
|
}
|
|
1256
1360
|
|
|
1361
|
+
const visibility = String(flags.access || flags.visibility || "public").trim().toLowerCase() || "public";
|
|
1257
1362
|
const body = {
|
|
1258
1363
|
preview_token: preview.preview_token,
|
|
1364
|
+
visibility,
|
|
1259
1365
|
};
|
|
1260
|
-
if (flags.visibility) {
|
|
1261
|
-
body.visibility = String(flags.visibility);
|
|
1262
|
-
}
|
|
1263
1366
|
if (flags["shared-with"]) {
|
|
1264
1367
|
body.shared_with = String(flags["shared-with"])
|
|
1265
1368
|
.split(",")
|