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