ht-skills 0.2.9 → 0.2.10
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/lib/cli.js +94 -74
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -841,8 +841,15 @@ function getPublishStepStatusLabel(status) {
|
|
|
841
841
|
}
|
|
842
842
|
}
|
|
843
843
|
|
|
844
|
-
function renderPublishFlowCard(flowState, {
|
|
845
|
-
|
|
844
|
+
function renderPublishFlowCard(flowState, {
|
|
845
|
+
colorize = (text) => text,
|
|
846
|
+
width = 80,
|
|
847
|
+
currentMessage = "",
|
|
848
|
+
} = {}) {
|
|
849
|
+
const contentWidth = Math.max(
|
|
850
|
+
28,
|
|
851
|
+
...PUBLISH_FLOW_STEPS.map((step, index) => getDisplayWidth(`${index + 1}. ${step.label} Pending`)),
|
|
852
|
+
);
|
|
846
853
|
const lines = PUBLISH_FLOW_STEPS.map((step, index) => {
|
|
847
854
|
const status = flowState?.[step.id] || "pending";
|
|
848
855
|
const symbol = status === "done"
|
|
@@ -859,24 +866,57 @@ function renderPublishFlowCard(flowState, { colorize = (text) => text, width = 8
|
|
|
859
866
|
: status === "error"
|
|
860
867
|
? colorize(step.label, "warn")
|
|
861
868
|
: step.label;
|
|
862
|
-
const statusLabel = colorize(
|
|
869
|
+
const statusLabel = colorize(
|
|
870
|
+
getPublishStepStatusLabel(status),
|
|
871
|
+
status === "pending" ? "muted" : status === "error" ? "warn" : status === "active" ? "accent" : "success",
|
|
872
|
+
);
|
|
863
873
|
const plainLine = `${index + 1}. ${step.label} ${getPublishStepStatusLabel(status)}`;
|
|
864
|
-
const paddedWidth = Math.max(0,
|
|
874
|
+
const paddedWidth = Math.max(0, contentWidth - getDisplayWidth(plainLine));
|
|
865
875
|
return `│ ${symbol} ${label}${" ".repeat(paddedWidth)} ${statusLabel} │`;
|
|
866
876
|
});
|
|
867
877
|
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
const normalizedLines = lines.map((line) => {
|
|
874
|
-
const plainWidth = getDisplayWidth(line);
|
|
875
|
-
const targetWidth = contentWidth + 4;
|
|
876
|
-
return plainWidth < targetWidth ? `${line.slice(0, -2)}${" ".repeat(targetWidth - plainWidth)} │` : line;
|
|
877
|
-
});
|
|
878
|
+
const titleSuffix = "─".repeat(Math.max(0, contentWidth - getDisplayWidth("Publish Flow") - 1));
|
|
879
|
+
const title = `◇ ${colorize("Publish Flow", "accent")} ${colorize(titleSuffix, "muted")}╮`;
|
|
880
|
+
const messageLines = currentMessage
|
|
881
|
+
? wrapText(currentMessage, contentWidth).map((line) => `│ ${colorize(line, "muted")}${" ".repeat(Math.max(0, contentWidth - getDisplayWidth(line)))} │`)
|
|
882
|
+
: [];
|
|
878
883
|
const bottom = `╰${"─".repeat(contentWidth + 2)}╯`;
|
|
879
|
-
return [title, ...
|
|
884
|
+
return [title, ...lines, ...messageLines, bottom].join("\n");
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function createLiveBlockRenderer(output = process.stdout) {
|
|
888
|
+
let renderedLines = 0;
|
|
889
|
+
|
|
890
|
+
function clearPrevious() {
|
|
891
|
+
if (!renderedLines || !output.isTTY) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
output.write(`\x1B[${renderedLines}F`);
|
|
895
|
+
for (let index = 0; index < renderedLines; index += 1) {
|
|
896
|
+
output.write("\x1B[2K");
|
|
897
|
+
if (index < renderedLines - 1) {
|
|
898
|
+
output.write("\x1B[1E");
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
if (renderedLines > 1) {
|
|
902
|
+
output.write(`\x1B[${renderedLines - 1}F`);
|
|
903
|
+
} else {
|
|
904
|
+
output.write("\r");
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return {
|
|
909
|
+
render(content) {
|
|
910
|
+
const text = String(content || "");
|
|
911
|
+
clearPrevious();
|
|
912
|
+
output.write(`${text}\n`);
|
|
913
|
+
renderedLines = text.split("\n").length;
|
|
914
|
+
},
|
|
915
|
+
clear() {
|
|
916
|
+
clearPrevious();
|
|
917
|
+
renderedLines = 0;
|
|
918
|
+
},
|
|
919
|
+
};
|
|
880
920
|
}
|
|
881
921
|
|
|
882
922
|
function printFallbackIntro({ registry, slug, version, skillName, skillDescription, installTargets }, log = console.log) {
|
|
@@ -1529,19 +1569,25 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1529
1569
|
inspect: "pending",
|
|
1530
1570
|
submit: "pending",
|
|
1531
1571
|
};
|
|
1572
|
+
let currentMessage = "Ready to publish";
|
|
1573
|
+
const flowRenderer = ui ? createLiveBlockRenderer(process.stdout) : null;
|
|
1532
1574
|
|
|
1533
1575
|
const renderPublishFlow = () => {
|
|
1534
|
-
if (!
|
|
1535
|
-
|
|
1536
|
-
console.log(renderPublishFlowCard(flowState, {
|
|
1576
|
+
if (!flowRenderer) return;
|
|
1577
|
+
flowRenderer.render(renderPublishFlowCard(flowState, {
|
|
1537
1578
|
colorize,
|
|
1538
1579
|
width: Math.max(44, Math.min(outputWidth - 4, 72)),
|
|
1580
|
+
currentMessage,
|
|
1539
1581
|
}));
|
|
1540
1582
|
};
|
|
1541
1583
|
const setFlowStatus = (stepId, status) => {
|
|
1542
1584
|
flowState[stepId] = status;
|
|
1543
1585
|
renderPublishFlow();
|
|
1544
1586
|
};
|
|
1587
|
+
const setFlowMessage = (message) => {
|
|
1588
|
+
currentMessage = message;
|
|
1589
|
+
renderPublishFlow();
|
|
1590
|
+
};
|
|
1545
1591
|
|
|
1546
1592
|
if (ui) {
|
|
1547
1593
|
// eslint-disable-next-line no-console
|
|
@@ -1561,42 +1607,37 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1561
1607
|
printFallbackPublishIntro({ registry, skillDir, archiveName, visibility }, log);
|
|
1562
1608
|
}
|
|
1563
1609
|
|
|
1610
|
+
setFlowMessage(`Checking login for ${registry}`);
|
|
1564
1611
|
setFlowStatus("auth", "active");
|
|
1565
1612
|
if (!ui) {
|
|
1566
1613
|
log(`Checking login for ${registry}...`);
|
|
1567
1614
|
}
|
|
1568
1615
|
const { token } = await ensureValidAuthToken(registry, flags, deps);
|
|
1616
|
+
setFlowMessage("Login confirmed");
|
|
1569
1617
|
setFlowStatus("auth", "done");
|
|
1570
1618
|
|
|
1571
|
-
|
|
1619
|
+
setFlowMessage(`Packing ${path.basename(skillDir) || skillDir}`);
|
|
1572
1620
|
setFlowStatus("pack", "active");
|
|
1573
|
-
if (
|
|
1574
|
-
packSpinner.start(`Packing ${path.basename(skillDir) || skillDir}`);
|
|
1575
|
-
} else {
|
|
1621
|
+
if (!ui) {
|
|
1576
1622
|
log(`Packing skill directory: ${skillDir}`);
|
|
1577
1623
|
}
|
|
1578
1624
|
let archiveBuffer;
|
|
1579
1625
|
try {
|
|
1580
1626
|
archiveBuffer = await createZipFromDirectory(skillDir);
|
|
1627
|
+
setFlowMessage(`Created ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1581
1628
|
setFlowStatus("pack", "done");
|
|
1582
|
-
if (
|
|
1583
|
-
packSpinner.stop(`Created ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1584
|
-
} else {
|
|
1629
|
+
if (!ui) {
|
|
1585
1630
|
log(`Created archive ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1586
1631
|
}
|
|
1587
1632
|
} catch (error) {
|
|
1633
|
+
setFlowMessage(`Packing failed: ${error.message}`);
|
|
1588
1634
|
setFlowStatus("pack", "error");
|
|
1589
|
-
if (packSpinner) {
|
|
1590
|
-
packSpinner.stop("Packing failed");
|
|
1591
|
-
}
|
|
1592
1635
|
throw error;
|
|
1593
1636
|
}
|
|
1594
1637
|
|
|
1595
|
-
|
|
1638
|
+
setFlowMessage("Uploading archive for package inspection");
|
|
1596
1639
|
setFlowStatus("inspect", "active");
|
|
1597
|
-
if (
|
|
1598
|
-
uploadSpinner.start("Uploading archive for package inspection");
|
|
1599
|
-
} else {
|
|
1640
|
+
if (!ui) {
|
|
1600
1641
|
log("Uploading archive for package inspection...");
|
|
1601
1642
|
}
|
|
1602
1643
|
let job;
|
|
@@ -1612,24 +1653,19 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1612
1653
|
},
|
|
1613
1654
|
);
|
|
1614
1655
|
} catch (error) {
|
|
1656
|
+
setFlowMessage(`Inspection upload failed: ${error.message}`);
|
|
1615
1657
|
setFlowStatus("inspect", "error");
|
|
1616
|
-
if (uploadSpinner) {
|
|
1617
|
-
uploadSpinner.stop("Inspection upload failed");
|
|
1618
|
-
}
|
|
1619
1658
|
throw error;
|
|
1620
1659
|
}
|
|
1621
1660
|
|
|
1622
1661
|
const jobId = String(job.job_id || "").trim();
|
|
1623
1662
|
if (!jobId) {
|
|
1663
|
+
setFlowMessage("Inspection upload failed: missing inspection job id");
|
|
1624
1664
|
setFlowStatus("inspect", "error");
|
|
1625
|
-
if (uploadSpinner) {
|
|
1626
|
-
uploadSpinner.stop("Inspection upload failed");
|
|
1627
|
-
}
|
|
1628
1665
|
throw new Error("registry did not return an inspection job id");
|
|
1629
1666
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
} else {
|
|
1667
|
+
setFlowMessage(`Inspection job created (${jobId})`);
|
|
1668
|
+
if (!ui) {
|
|
1633
1669
|
log(`Inspection job created: ${jobId}`);
|
|
1634
1670
|
}
|
|
1635
1671
|
|
|
@@ -1637,16 +1673,10 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1637
1673
|
let lastProgressKey = "";
|
|
1638
1674
|
let consecutivePollErrors = 0;
|
|
1639
1675
|
const publishDeadline = Date.now() + publishTimeoutMs;
|
|
1640
|
-
const inspectSpinner = ui ? ui.spinner() : null;
|
|
1641
|
-
if (inspectSpinner) {
|
|
1642
|
-
inspectSpinner.start("Inspecting archive");
|
|
1643
|
-
}
|
|
1644
1676
|
while (inspection.status !== "succeeded" && inspection.status !== "failed") {
|
|
1645
1677
|
if (Date.now() > publishDeadline) {
|
|
1678
|
+
setFlowMessage(`Inspection timed out after ${Math.round(publishTimeoutMs / 1000)}s`);
|
|
1646
1679
|
setFlowStatus("inspect", "error");
|
|
1647
|
-
if (inspectSpinner) {
|
|
1648
|
-
inspectSpinner.stop("Inspection timed out");
|
|
1649
|
-
}
|
|
1650
1680
|
throw new Error(`Inspection timed out after ${Math.round(publishTimeoutMs / 1000)}s`);
|
|
1651
1681
|
}
|
|
1652
1682
|
|
|
@@ -1662,13 +1692,12 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1662
1692
|
} catch (error) {
|
|
1663
1693
|
consecutivePollErrors += 1;
|
|
1664
1694
|
if (consecutivePollErrors >= pollErrorLimit) {
|
|
1695
|
+
setFlowMessage(`Inspection polling failed: ${error.message}`);
|
|
1665
1696
|
setFlowStatus("inspect", "error");
|
|
1666
|
-
if (inspectSpinner) {
|
|
1667
|
-
inspectSpinner.stop("Inspection polling failed");
|
|
1668
|
-
}
|
|
1669
1697
|
throw new Error(`Inspection polling failed ${consecutivePollErrors} times: ${error.message}`);
|
|
1670
1698
|
}
|
|
1671
|
-
|
|
1699
|
+
setFlowMessage(`Inspection polling retry ${consecutivePollErrors}/${pollErrorLimit}: ${error.message}`);
|
|
1700
|
+
if (!ui) {
|
|
1672
1701
|
log(`Inspection polling retry ${consecutivePollErrors}/${pollErrorLimit}: ${error.message}`);
|
|
1673
1702
|
}
|
|
1674
1703
|
continue;
|
|
@@ -1676,10 +1705,8 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1676
1705
|
|
|
1677
1706
|
const runtimeError = String(inspection.error?.message || inspection.error || "").trim();
|
|
1678
1707
|
if (runtimeError && inspection.status !== "succeeded") {
|
|
1708
|
+
setFlowMessage(`Inspection failed: ${runtimeError}`);
|
|
1679
1709
|
setFlowStatus("inspect", "error");
|
|
1680
|
-
if (inspectSpinner) {
|
|
1681
|
-
inspectSpinner.stop("Inspection failed");
|
|
1682
|
-
}
|
|
1683
1710
|
throw new Error(runtimeError);
|
|
1684
1711
|
}
|
|
1685
1712
|
|
|
@@ -1691,30 +1718,27 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1691
1718
|
const progressLabel = step
|
|
1692
1719
|
? `${step}${typeof percent === "number" ? ` ${percent}%` : ""}`
|
|
1693
1720
|
: inspection.status;
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
} else {
|
|
1721
|
+
setFlowMessage(`Inspecting archive (${progressLabel})`);
|
|
1722
|
+
if (!ui) {
|
|
1697
1723
|
log(`Inspection in progress: ${progressLabel}`);
|
|
1698
1724
|
}
|
|
1699
1725
|
}
|
|
1700
1726
|
}
|
|
1701
1727
|
|
|
1702
1728
|
if (inspection.status !== "succeeded") {
|
|
1729
|
+
setFlowMessage(`Inspection failed: ${inspection.error || "unknown error"}`);
|
|
1703
1730
|
setFlowStatus("inspect", "error");
|
|
1704
|
-
if (inspectSpinner) {
|
|
1705
|
-
inspectSpinner.stop("Inspection failed");
|
|
1706
|
-
}
|
|
1707
1731
|
throw new Error(inspection.error || "skill archive inspection failed");
|
|
1708
1732
|
}
|
|
1733
|
+
setFlowMessage("Inspection passed");
|
|
1709
1734
|
setFlowStatus("inspect", "done");
|
|
1710
|
-
if (
|
|
1711
|
-
inspectSpinner.stop("Inspection passed");
|
|
1712
|
-
} else {
|
|
1735
|
+
if (!ui) {
|
|
1713
1736
|
log("Inspection passed.");
|
|
1714
1737
|
}
|
|
1715
1738
|
|
|
1716
1739
|
const preview = inspection.result || {};
|
|
1717
1740
|
if (!preview.valid || !preview.preview_token) {
|
|
1741
|
+
setFlowMessage("Inspection failed: preview token missing");
|
|
1718
1742
|
setFlowStatus("inspect", "error");
|
|
1719
1743
|
throw new Error(summarizePreviewErrors(preview));
|
|
1720
1744
|
}
|
|
@@ -1736,11 +1760,9 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1736
1760
|
body.publish_now = true;
|
|
1737
1761
|
}
|
|
1738
1762
|
|
|
1739
|
-
|
|
1763
|
+
setFlowMessage(`Submitting review request (${visibility})`);
|
|
1740
1764
|
setFlowStatus("submit", "active");
|
|
1741
|
-
if (
|
|
1742
|
-
submitSpinner.start(`Submitting review request (${visibility})`);
|
|
1743
|
-
} else {
|
|
1765
|
+
if (!ui) {
|
|
1744
1766
|
log(`Submitting review request with access=${visibility}...`);
|
|
1745
1767
|
}
|
|
1746
1768
|
let result;
|
|
@@ -1752,17 +1774,14 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1752
1774
|
}, token),
|
|
1753
1775
|
body: JSON.stringify(body),
|
|
1754
1776
|
});
|
|
1777
|
+
setFlowMessage(`Review submission created (${result.submission_id || "pending"})`);
|
|
1755
1778
|
setFlowStatus("submit", "done");
|
|
1756
|
-
if (
|
|
1757
|
-
submitSpinner.stop(`Review submission created (${result.submission_id || "pending"})`);
|
|
1758
|
-
} else {
|
|
1779
|
+
if (!ui) {
|
|
1759
1780
|
log(`Review submission created: ${result.submission_id || "(no submission id returned)"}`);
|
|
1760
1781
|
}
|
|
1761
1782
|
} catch (error) {
|
|
1783
|
+
setFlowMessage(`Submit failed: ${error.message}`);
|
|
1762
1784
|
setFlowStatus("submit", "error");
|
|
1763
|
-
if (submitSpinner) {
|
|
1764
|
-
submitSpinner.stop("Submit failed");
|
|
1765
|
-
}
|
|
1766
1785
|
throw error;
|
|
1767
1786
|
}
|
|
1768
1787
|
|
|
@@ -1786,6 +1805,7 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1786
1805
|
].join("\n"),
|
|
1787
1806
|
"Published",
|
|
1788
1807
|
);
|
|
1808
|
+
setFlowMessage(`Submitted ${archiveName} for review`);
|
|
1789
1809
|
ui.outro(`Submitted ${ui.pc.cyan(archiveName)} for review.`);
|
|
1790
1810
|
} else {
|
|
1791
1811
|
log(JSON.stringify(summaryPayload, null, 2));
|