vskill 0.2.28 → 0.2.30
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/dist/api/client.d.ts +1 -0
- package/dist/api/client.js +2 -1
- package/dist/api/client.js.map +1 -1
- package/dist/api/client.test.js +2 -1
- package/dist/api/client.test.js.map +1 -1
- package/dist/commands/add.js +55 -7
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +96 -1
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/find.js +58 -127
- package/dist/commands/find.js.map +1 -1
- package/dist/commands/find.test.js +69 -17
- package/dist/commands/find.test.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/output.d.ts +6 -0
- package/dist/utils/output.js +17 -0
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/output.test.d.ts +1 -0
- package/dist/utils/output.test.js +22 -0
- package/dist/utils/output.test.js.map +1 -0
- package/dist/utils/prompts.js +40 -5
- package/dist/utils/prompts.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/find.js
CHANGED
|
@@ -2,31 +2,9 @@
|
|
|
2
2
|
// vskill find -- search the verified-skill.com registry
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
import { searchSkills } from "../api/client.js";
|
|
5
|
-
import { bold,
|
|
6
|
-
/**
|
|
7
|
-
* Extract owner/repo from a URL and wrap it in a clickable OSC 8 hyperlink.
|
|
8
|
-
* Falls back to author name if no URL is available.
|
|
9
|
-
*/
|
|
10
|
-
function formatRepo(repoUrl, author) {
|
|
11
|
-
if (!repoUrl)
|
|
12
|
-
return author || "-";
|
|
13
|
-
try {
|
|
14
|
-
const ownerRepo = new URL(repoUrl).pathname
|
|
15
|
-
.replace(/^\//, "")
|
|
16
|
-
.replace(/\.git$/, "")
|
|
17
|
-
.replace(/\/$/, "");
|
|
18
|
-
return link(repoUrl, ownerRepo);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return repoUrl;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
5
|
+
import { bold, dim, cyan, red, link, formatInstalls } from "../utils/output.js";
|
|
24
6
|
/**
|
|
25
7
|
* Extract the base `owner/repo` slug from a repoUrl.
|
|
26
|
-
* Handles URLs like:
|
|
27
|
-
* https://github.com/owner/repo
|
|
28
|
-
* https://github.com/owner/repo/tree/main/path
|
|
29
|
-
* https://github.com/owner/repo.git
|
|
30
8
|
*/
|
|
31
9
|
function extractBaseRepo(repoUrl) {
|
|
32
10
|
if (!repoUrl)
|
|
@@ -35,49 +13,17 @@ function extractBaseRepo(repoUrl) {
|
|
|
35
13
|
return match ? match[1] : null;
|
|
36
14
|
}
|
|
37
15
|
/**
|
|
38
|
-
*
|
|
16
|
+
* Format as `owner/repo@skill-name`.
|
|
39
17
|
*/
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
let scoreCol;
|
|
44
|
-
if (r.isBlocked) {
|
|
45
|
-
displayTier = "BLOCKED";
|
|
46
|
-
tierColor = red;
|
|
47
|
-
const parts = [r.severity, r.threatType].filter(Boolean);
|
|
48
|
-
scoreCol = parts.length > 0 ? parts.join(" | ") : "blocked";
|
|
49
|
-
}
|
|
50
|
-
else if (r.isTainted) {
|
|
51
|
-
displayTier = "TAINTED";
|
|
52
|
-
tierColor = red;
|
|
53
|
-
scoreCol = String(r.score ?? "-");
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
displayTier = r.tier || "VERIFIED";
|
|
57
|
-
tierColor =
|
|
58
|
-
r.tier === "CERTIFIED"
|
|
59
|
-
? yellow
|
|
60
|
-
: r.tier === "VERIFIED"
|
|
61
|
-
? green
|
|
62
|
-
: dim;
|
|
63
|
-
scoreCol = String(r.score ?? "-");
|
|
64
|
-
}
|
|
65
|
-
const repo = formatRepo(r.repoUrl, r.author);
|
|
66
|
-
return [
|
|
67
|
-
r.isBlocked ? red(bold(r.name)) : bold(r.name),
|
|
68
|
-
repo,
|
|
69
|
-
tierColor(displayTier),
|
|
70
|
-
r.isBlocked ? red(scoreCol) : scoreCol,
|
|
71
|
-
];
|
|
18
|
+
function formatRepoSkill(repoUrl, skillName) {
|
|
19
|
+
const base = extractBaseRepo(repoUrl);
|
|
20
|
+
return base ? `${base}@${skillName}` : skillName;
|
|
72
21
|
}
|
|
73
22
|
/**
|
|
74
|
-
*
|
|
23
|
+
* Get the verified-skill.com URL for a skill.
|
|
75
24
|
*/
|
|
76
|
-
function
|
|
77
|
-
return
|
|
78
|
-
.split("\n")
|
|
79
|
-
.map((line) => prefix + line)
|
|
80
|
-
.join("\n");
|
|
25
|
+
function getSkillUrl(skillName) {
|
|
26
|
+
return `https://verified-skill.com/skills/${encodeURIComponent(skillName)}`;
|
|
81
27
|
}
|
|
82
28
|
export async function findCommand(query, opts) {
|
|
83
29
|
console.log(dim(`Searching for "${query}"...\n`));
|
|
@@ -99,102 +45,87 @@ export async function findCommand(query, opts) {
|
|
|
99
45
|
console.log(dim(`Try a broader search or visit ${cyan("https://verified-skill.com")}`));
|
|
100
46
|
return;
|
|
101
47
|
}
|
|
102
|
-
// Sort: non-blocked by
|
|
48
|
+
// Sort: non-blocked by installs descending, score as tiebreaker, blocked at end
|
|
103
49
|
results.sort((a, b) => {
|
|
104
50
|
if (a.isBlocked && !b.isBlocked)
|
|
105
51
|
return 1;
|
|
106
52
|
if (!a.isBlocked && b.isBlocked)
|
|
107
53
|
return -1;
|
|
54
|
+
const installDiff = (b.installs ?? 0) - (a.installs ?? 0);
|
|
55
|
+
if (installDiff !== 0)
|
|
56
|
+
return installDiff;
|
|
108
57
|
return (b.score ?? 0) - (a.score ?? 0);
|
|
109
58
|
});
|
|
110
|
-
// JSON output mode
|
|
59
|
+
// JSON output mode
|
|
111
60
|
if (opts?.json) {
|
|
112
61
|
console.log(JSON.stringify(results, null, 2));
|
|
113
62
|
return;
|
|
114
63
|
}
|
|
115
64
|
const blockedCount = results.filter((r) => r.isBlocked).length;
|
|
116
|
-
|
|
117
|
-
// ---------- Grouped TTY display -------------------------------------------
|
|
65
|
+
// ---------- TTY display: flat two-line entries ----------------------------
|
|
118
66
|
if (process.stdout.isTTY) {
|
|
119
|
-
// Group results by base repo
|
|
120
|
-
const groupOrder = [];
|
|
121
|
-
const groups = new Map();
|
|
122
67
|
for (const r of results) {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
68
|
+
const label = formatRepoSkill(r.repoUrl, r.name);
|
|
69
|
+
const url = getSkillUrl(r.name);
|
|
70
|
+
if (r.isBlocked) {
|
|
71
|
+
const parts = [r.severity, r.threatType].filter(Boolean);
|
|
72
|
+
const threatInfo = parts.length > 0 ? parts.join(" | ") : "blocked";
|
|
73
|
+
console.log(`${red(bold(label))} ${red(threatInfo)}`);
|
|
127
74
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const marketplaceKeys = groupOrder.filter((k) => k !== "__ungrouped__" && groups.get(k).length >= 2);
|
|
132
|
-
const singletonResults = [];
|
|
133
|
-
for (const key of groupOrder) {
|
|
134
|
-
const items = groups.get(key);
|
|
135
|
-
if (key === "__ungrouped__" || items.length < 2) {
|
|
136
|
-
singletonResults.push(...items);
|
|
75
|
+
else {
|
|
76
|
+
const installStr = `${formatInstalls(r.installs)} installs`;
|
|
77
|
+
console.log(`${bold(label)} ${dim(installStr)}`);
|
|
137
78
|
}
|
|
138
|
-
|
|
139
|
-
const hasMarketplaceGroups = marketplaceKeys.length > 0;
|
|
140
|
-
// Render marketplace groups
|
|
141
|
-
for (const baseRepo of marketplaceKeys) {
|
|
142
|
-
const items = groups.get(baseRepo);
|
|
143
|
-
console.log(bold(`Plugin Marketplace: ${baseRepo} (${items.length} skills)`) +
|
|
144
|
-
" " +
|
|
145
|
-
dim(`Install: npx vskill install ${baseRepo}`));
|
|
146
|
-
const rows = items.map((r) => buildRow(r));
|
|
147
|
-
console.log(indent(table(headers, rows), " "));
|
|
79
|
+
console.log(dim(` \u2514 ${link(url, url)}`));
|
|
148
80
|
console.log();
|
|
149
81
|
}
|
|
150
|
-
//
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
console.log(table(headers, rows));
|
|
161
|
-
}
|
|
82
|
+
// Footer
|
|
83
|
+
if (hasMore) {
|
|
84
|
+
const currentLimit = opts?.limit ?? 15;
|
|
85
|
+
const suggestedLimit = Math.min(currentLimit * 2, 50);
|
|
86
|
+
console.log(dim(`Showing ${results.length} results. Use --limit ${suggestedLimit} for more.`));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(dim(`${results.length} result${results.length === 1 ? "" : "s"} found.`));
|
|
162
90
|
}
|
|
163
|
-
const countText = `${results.length} result${results.length === 1 ? "" : "s"} found`;
|
|
164
|
-
console.log(dim(`\n${countText}${hasMore ? " (more available — browse at verified-skill.com)" : ""}`));
|
|
165
91
|
if (blockedCount > 0) {
|
|
166
|
-
console.log(red(`\n ${blockedCount} BLOCKED skill${blockedCount === 1 ? "" : "s"}
|
|
92
|
+
console.log(red(`\n ${blockedCount} BLOCKED skill${blockedCount === 1 ? "" : "s"} \u2014 known malicious, installation will be refused`));
|
|
167
93
|
}
|
|
168
|
-
// Install
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
const
|
|
173
|
-
console.log(dim(`\nInstall marketplace: `) + cyan(`npx vskill i ${firstMp}`));
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
const firstResult = results.find((r) => !r.isBlocked);
|
|
177
|
-
const repo = firstResult ? extractBaseRepo(firstResult.repoUrl) : null;
|
|
94
|
+
// Install hint
|
|
95
|
+
if (!opts?.noHint) {
|
|
96
|
+
const firstInstallable = results.find((r) => !r.isBlocked);
|
|
97
|
+
if (firstInstallable) {
|
|
98
|
+
const repo = extractBaseRepo(firstInstallable.repoUrl);
|
|
178
99
|
if (repo) {
|
|
179
|
-
console.log(dim(
|
|
100
|
+
console.log(dim("\nInstall: ") + cyan(`npx vskill i ${repo}`));
|
|
180
101
|
}
|
|
181
102
|
}
|
|
182
103
|
}
|
|
183
104
|
return;
|
|
184
105
|
}
|
|
185
|
-
// ---------- Non-TTY (piped)
|
|
186
|
-
const
|
|
187
|
-
|
|
106
|
+
// ---------- Non-TTY (piped) — tab-separated flat lines --------------------
|
|
107
|
+
for (const r of results) {
|
|
108
|
+
const label = formatRepoSkill(r.repoUrl, r.name);
|
|
109
|
+
if (r.isBlocked) {
|
|
110
|
+
console.log(`${label}\tBLOCKED\t${r.threatType ?? ""}`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(`${label}\t${r.installs}\t${r.tier}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
188
116
|
const pipeCountText = `${results.length} result${results.length === 1 ? "" : "s"} found`;
|
|
189
|
-
console.log(
|
|
117
|
+
console.log(`\n${pipeCountText}${hasMore ? " (more available)" : ""}`);
|
|
190
118
|
if (blockedCount > 0) {
|
|
191
|
-
console.log(
|
|
119
|
+
console.log(`\n ${blockedCount} BLOCKED skill${blockedCount === 1 ? "" : "s"} \u2014 known malicious, installation will be refused`);
|
|
192
120
|
}
|
|
193
|
-
|
|
194
|
-
if (!opts?.noHint && hasInstallable) {
|
|
121
|
+
if (!opts?.noHint) {
|
|
195
122
|
const firstInstallable = results.find((r) => !r.isBlocked);
|
|
196
|
-
|
|
197
|
-
|
|
123
|
+
if (firstInstallable) {
|
|
124
|
+
const repo = extractBaseRepo(firstInstallable.repoUrl);
|
|
125
|
+
if (repo) {
|
|
126
|
+
console.log(`\nInstall: npx vskill i ${repo}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
198
129
|
}
|
|
199
130
|
}
|
|
200
131
|
//# sourceMappingURL=find.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find.js","sourceRoot":"","sources":["../../src/commands/find.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wDAAwD;AACxD,8EAA8E;AAG9E,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"find.js","sourceRoot":"","sources":["../../src/commands/find.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,53 +1,105 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// Tests for find command
|
|
2
|
+
// Tests for find command
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
4
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
5
5
|
const mockSearchSkills = vi.hoisted(() => vi.fn());
|
|
6
6
|
vi.mock("../api/client.js", () => ({
|
|
7
7
|
searchSkills: mockSearchSkills,
|
|
8
8
|
}));
|
|
9
|
-
// Capture console.log output
|
|
10
9
|
const logs = [];
|
|
11
|
-
|
|
10
|
+
let origIsTTY;
|
|
12
11
|
import { findCommand } from "./find.js";
|
|
13
|
-
describe("findCommand
|
|
12
|
+
describe("findCommand", () => {
|
|
14
13
|
beforeEach(() => {
|
|
15
14
|
logs.length = 0;
|
|
16
15
|
vi.spyOn(console, "log").mockImplementation((...args) => {
|
|
17
16
|
logs.push(args.map(String).join(" "));
|
|
18
17
|
});
|
|
19
18
|
vi.spyOn(console, "error").mockImplementation(() => { });
|
|
19
|
+
origIsTTY = process.stdout.isTTY;
|
|
20
20
|
mockSearchSkills.mockResolvedValue({
|
|
21
21
|
results: [
|
|
22
|
-
{ name: "test-skill", author: "test", repoUrl: "https://github.com/test/test-skill", tier: "VERIFIED", score: 90 },
|
|
22
|
+
{ name: "test-skill", author: "test", repoUrl: "https://github.com/test/test-skill", tier: "VERIFIED", score: 90, installs: 1250 },
|
|
23
23
|
],
|
|
24
24
|
hasMore: false,
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
Object.defineProperty(process.stdout, "isTTY", { value: origIsTTY, configurable: true });
|
|
29
|
+
});
|
|
30
|
+
it("TC-030: non-TTY output includes tab-separated result", async () => {
|
|
30
31
|
Object.defineProperty(process.stdout, "isTTY", { value: false, configurable: true });
|
|
31
32
|
await findCommand("test", { json: false, noHint: false });
|
|
32
33
|
const output = logs.join("\n");
|
|
33
|
-
expect(output).toContain("
|
|
34
|
-
|
|
34
|
+
expect(output).toContain("test/test-skill@test-skill");
|
|
35
|
+
expect(output).toContain("1250");
|
|
35
36
|
});
|
|
36
37
|
it("TC-031: hint is suppressed with --json flag", async () => {
|
|
37
|
-
const origIsTTY = process.stdout.isTTY;
|
|
38
38
|
Object.defineProperty(process.stdout, "isTTY", { value: false, configurable: true });
|
|
39
39
|
await findCommand("test", { json: true, noHint: false });
|
|
40
40
|
const output = logs.join("\n");
|
|
41
|
-
expect(output).not.toContain("
|
|
42
|
-
Object.defineProperty(process.stdout, "isTTY", { value: origIsTTY, configurable: true });
|
|
41
|
+
expect(output).not.toContain("Install:");
|
|
43
42
|
});
|
|
44
43
|
it("TC-032: hint is suppressed with --no-hint flag", async () => {
|
|
45
|
-
const origIsTTY = process.stdout.isTTY;
|
|
46
44
|
Object.defineProperty(process.stdout, "isTTY", { value: false, configurable: true });
|
|
47
45
|
await findCommand("test", { json: false, noHint: true });
|
|
48
46
|
const output = logs.join("\n");
|
|
49
|
-
expect(output).not.toContain("
|
|
50
|
-
|
|
47
|
+
expect(output).not.toContain("Install:");
|
|
48
|
+
});
|
|
49
|
+
it("displays install counts in TTY mode", async () => {
|
|
50
|
+
Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true });
|
|
51
|
+
await findCommand("test");
|
|
52
|
+
const output = logs.join("\n");
|
|
53
|
+
expect(output).toContain("1.3K installs");
|
|
54
|
+
expect(output).toContain("test/test-skill@test-skill");
|
|
55
|
+
});
|
|
56
|
+
it("sorts results by installs descending", async () => {
|
|
57
|
+
Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true });
|
|
58
|
+
mockSearchSkills.mockResolvedValue({
|
|
59
|
+
results: [
|
|
60
|
+
{ name: "low-installs", author: "a", repoUrl: "https://github.com/a/b", tier: "VERIFIED", score: 90, installs: 10 },
|
|
61
|
+
{ name: "high-installs", author: "b", repoUrl: "https://github.com/b/c", tier: "VERIFIED", score: 50, installs: 5000 },
|
|
62
|
+
],
|
|
63
|
+
hasMore: false,
|
|
64
|
+
});
|
|
65
|
+
await findCommand("test");
|
|
66
|
+
const output = logs.join("\n");
|
|
67
|
+
const highIdx = output.indexOf("high-installs");
|
|
68
|
+
const lowIdx = output.indexOf("low-installs");
|
|
69
|
+
expect(highIdx).toBeLessThan(lowIdx);
|
|
70
|
+
});
|
|
71
|
+
it("shows --limit hint when truncated", async () => {
|
|
72
|
+
Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true });
|
|
73
|
+
mockSearchSkills.mockResolvedValue({
|
|
74
|
+
results: [
|
|
75
|
+
{ name: "skill-1", author: "a", repoUrl: "https://github.com/a/b", tier: "VERIFIED", score: 90, installs: 100 },
|
|
76
|
+
],
|
|
77
|
+
hasMore: true,
|
|
78
|
+
});
|
|
79
|
+
await findCommand("test");
|
|
80
|
+
const output = logs.join("\n");
|
|
81
|
+
expect(output).toContain("Use --limit");
|
|
82
|
+
});
|
|
83
|
+
it("shows blocked skills with threat info", async () => {
|
|
84
|
+
Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true });
|
|
85
|
+
mockSearchSkills.mockResolvedValue({
|
|
86
|
+
results: [
|
|
87
|
+
{ name: "bad-skill", author: "evil", repoUrl: "https://github.com/evil/bad", tier: "BLOCKED", score: 0, installs: 0, isBlocked: true, threatType: "credential-theft", severity: "critical" },
|
|
88
|
+
],
|
|
89
|
+
hasMore: false,
|
|
90
|
+
});
|
|
91
|
+
await findCommand("test");
|
|
92
|
+
const output = logs.join("\n");
|
|
93
|
+
expect(output).toContain("credential-theft");
|
|
94
|
+
});
|
|
95
|
+
it("JSON output includes installs field", async () => {
|
|
96
|
+
Object.defineProperty(process.stdout, "isTTY", { value: false, configurable: true });
|
|
97
|
+
await findCommand("test", { json: true });
|
|
98
|
+
// Skip the "Searching..." line — JSON starts at the second log entry
|
|
99
|
+
const jsonLine = logs.find((l) => l.trimStart().startsWith("["));
|
|
100
|
+
expect(jsonLine).toBeDefined();
|
|
101
|
+
const parsed = JSON.parse(jsonLine);
|
|
102
|
+
expect(parsed[0].installs).toBe(1250);
|
|
51
103
|
});
|
|
52
104
|
});
|
|
53
105
|
//# sourceMappingURL=find.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find.test.js","sourceRoot":"","sources":["../../src/commands/find.test.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,
|
|
1
|
+
{"version":3,"file":"find.test.js","sourceRoot":"","sources":["../../src/commands/find.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -70,7 +70,7 @@ program
|
|
|
70
70
|
.command("find <query>")
|
|
71
71
|
.alias("search")
|
|
72
72
|
.description("Search the verified-skill.com registry")
|
|
73
|
-
.option("--limit <n>", "Max results to return (default
|
|
73
|
+
.option("--limit <n>", "Max results to return (default 15, max 50)", parseInt)
|
|
74
74
|
.option("--json", "Output raw JSON")
|
|
75
75
|
.action(async (query, opts) => {
|
|
76
76
|
const { findCommand } = await import("./commands/find.js");
|
package/dist/utils/output.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ export declare function link(url: string, text: string): string;
|
|
|
16
16
|
* Column widths are auto-calculated from the widest cell.
|
|
17
17
|
*/
|
|
18
18
|
export declare function table(headers: string[], rows: string[][], gap?: number): string;
|
|
19
|
+
/**
|
|
20
|
+
* Format install count as human-readable string.
|
|
21
|
+
* 0 → "0", 999 → "999", 1000 → "1.0K", 1250 → "1.2K",
|
|
22
|
+
* 10000 → "10K", 999999 → "999K", 1000000 → "1.0M"
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatInstalls(n: number): string;
|
|
19
25
|
export declare function spinner(message: string): {
|
|
20
26
|
stop: (final?: string) => void;
|
|
21
27
|
};
|
package/dist/utils/output.js
CHANGED
|
@@ -68,6 +68,23 @@ function padRight(str, width, gap) {
|
|
|
68
68
|
const padding = Math.max(0, width - visible.length) + gap;
|
|
69
69
|
return str + " ".repeat(padding);
|
|
70
70
|
}
|
|
71
|
+
// ---- Formatting -----------------------------------------------------------
|
|
72
|
+
/**
|
|
73
|
+
* Format install count as human-readable string.
|
|
74
|
+
* 0 → "0", 999 → "999", 1000 → "1.0K", 1250 → "1.2K",
|
|
75
|
+
* 10000 → "10K", 999999 → "999K", 1000000 → "1.0M"
|
|
76
|
+
*/
|
|
77
|
+
export function formatInstalls(n) {
|
|
78
|
+
if (n < 1_000)
|
|
79
|
+
return String(n);
|
|
80
|
+
if (n < 10_000)
|
|
81
|
+
return `${(n / 1_000).toFixed(1)}K`;
|
|
82
|
+
if (n < 1_000_000)
|
|
83
|
+
return `${Math.floor(n / 1_000)}K`;
|
|
84
|
+
if (n < 10_000_000)
|
|
85
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
86
|
+
return `${Math.floor(n / 1_000_000)}M`;
|
|
87
|
+
}
|
|
71
88
|
// ---- Spinner --------------------------------------------------------------
|
|
72
89
|
export function spinner(message) {
|
|
73
90
|
const frames = ["|", "/", "-", "\\"];
|
package/dist/utils/output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { formatInstalls } from "./output.js";
|
|
3
|
+
describe("formatInstalls", () => {
|
|
4
|
+
it("returns raw number below 1000", () => {
|
|
5
|
+
expect(formatInstalls(0)).toBe("0");
|
|
6
|
+
expect(formatInstalls(999)).toBe("999");
|
|
7
|
+
});
|
|
8
|
+
it("formats thousands with one decimal", () => {
|
|
9
|
+
expect(formatInstalls(1000)).toBe("1.0K");
|
|
10
|
+
expect(formatInstalls(1250)).toBe("1.3K");
|
|
11
|
+
expect(formatInstalls(9999)).toBe("10.0K");
|
|
12
|
+
});
|
|
13
|
+
it("formats ten-thousands without decimal", () => {
|
|
14
|
+
expect(formatInstalls(10000)).toBe("10K");
|
|
15
|
+
expect(formatInstalls(999999)).toBe("999K");
|
|
16
|
+
});
|
|
17
|
+
it("formats millions", () => {
|
|
18
|
+
expect(formatInstalls(1000000)).toBe("1.0M");
|
|
19
|
+
expect(formatInstalls(10000000)).toBe("10M");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=output.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.test.js","sourceRoot":"","sources":["../../src/utils/output.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/utils/prompts.js
CHANGED
|
@@ -100,17 +100,49 @@ const KEY_SPACE = " ";
|
|
|
100
100
|
const KEY_ENTER_CR = "\r";
|
|
101
101
|
const KEY_ENTER_LF = "\n";
|
|
102
102
|
const KEY_CTRL_C = "\x03";
|
|
103
|
+
/**
|
|
104
|
+
* Queue-based keypress reader that prevents race conditions.
|
|
105
|
+
* A persistent listener buffers incoming data, so no keystrokes
|
|
106
|
+
* are lost between reads (e.g. during render cycles).
|
|
107
|
+
*/
|
|
108
|
+
const keyQueue = [];
|
|
109
|
+
const keyWaiters = [];
|
|
110
|
+
let keyListenerAttached = false;
|
|
111
|
+
function ensureKeyListener() {
|
|
112
|
+
if (keyListenerAttached)
|
|
113
|
+
return;
|
|
114
|
+
keyListenerAttached = true;
|
|
115
|
+
process.stdin.on("data", (data) => {
|
|
116
|
+
const key = data.toString();
|
|
117
|
+
const waiter = keyWaiters.shift();
|
|
118
|
+
if (waiter) {
|
|
119
|
+
waiter(key);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
keyQueue.push(key);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function detachKeyListener() {
|
|
127
|
+
if (!keyListenerAttached)
|
|
128
|
+
return;
|
|
129
|
+
process.stdin.removeAllListeners("data");
|
|
130
|
+
keyListenerAttached = false;
|
|
131
|
+
keyQueue.length = 0;
|
|
132
|
+
keyWaiters.length = 0;
|
|
133
|
+
}
|
|
103
134
|
/**
|
|
104
135
|
* Read a single keypress from stdin in raw mode.
|
|
105
136
|
* Returns the raw key string (may be multi-byte for arrow keys).
|
|
106
137
|
*/
|
|
107
138
|
function readKey() {
|
|
139
|
+
ensureKeyListener();
|
|
140
|
+
const buffered = keyQueue.shift();
|
|
141
|
+
if (buffered !== undefined) {
|
|
142
|
+
return Promise.resolve(buffered);
|
|
143
|
+
}
|
|
108
144
|
return new Promise((resolve) => {
|
|
109
|
-
|
|
110
|
-
process.stdin.removeListener("data", onData);
|
|
111
|
-
resolve(data.toString());
|
|
112
|
-
}
|
|
113
|
-
process.stdin.on("data", onData);
|
|
145
|
+
keyWaiters.push(resolve);
|
|
114
146
|
});
|
|
115
147
|
}
|
|
116
148
|
// ---------------------------------------------------------------------------
|
|
@@ -229,6 +261,7 @@ function createInteractivePrompter() {
|
|
|
229
261
|
}
|
|
230
262
|
}
|
|
231
263
|
finally {
|
|
264
|
+
detachKeyListener();
|
|
232
265
|
process.stdin.setRawMode(false);
|
|
233
266
|
process.stdin.pause();
|
|
234
267
|
write(SHOW_CURSOR);
|
|
@@ -291,6 +324,7 @@ function createInteractivePrompter() {
|
|
|
291
324
|
}
|
|
292
325
|
}
|
|
293
326
|
finally {
|
|
327
|
+
detachKeyListener();
|
|
294
328
|
process.stdin.setRawMode(false);
|
|
295
329
|
process.stdin.pause();
|
|
296
330
|
write(SHOW_CURSOR);
|
|
@@ -325,6 +359,7 @@ function createInteractivePrompter() {
|
|
|
325
359
|
}
|
|
326
360
|
}
|
|
327
361
|
finally {
|
|
362
|
+
detachKeyListener();
|
|
328
363
|
process.stdin.setRawMode(false);
|
|
329
364
|
process.stdin.pause();
|
|
330
365
|
}
|