updose 0.3.0 → 0.4.0
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 +2 -2
- package/dist/index.cjs +24 -46
- package/dist/index.js +24 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,9 +130,9 @@ npx updose search --author james --target claude # james's Claude boilerpla
|
|
|
130
130
|
npx updose search --tag typescript --target codex # TypeScript boilerplates for Codex
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
Running `npx updose search` with no arguments returns popular boilerplates.
|
|
134
134
|
|
|
135
|
-
Results display the boilerplate name, version, author, description,
|
|
135
|
+
Results display the boilerplate name, version, author, description, download count, supported targets, and tags.
|
|
136
136
|
|
|
137
137
|
| Option | Description |
|
|
138
138
|
|-------- |------------- |
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
// src/index.ts
|
|
26
26
|
var import_commander = require("commander");
|
|
27
27
|
|
|
28
|
+
// src/commands/add.ts
|
|
29
|
+
var import_node_crypto = require("crypto");
|
|
30
|
+
|
|
28
31
|
// src/constants.ts
|
|
29
32
|
var USER_AGENT = "updose-cli";
|
|
30
33
|
var MANIFEST_FILENAME = "updose.json";
|
|
@@ -51,14 +54,18 @@ async function searchBoilerplates(query, filters) {
|
|
|
51
54
|
}
|
|
52
55
|
return await res.json();
|
|
53
56
|
}
|
|
54
|
-
async function recordDownload(repo, dir) {
|
|
57
|
+
async function recordDownload(repo, dir, projectHash) {
|
|
55
58
|
await fetch(`${API_BASE_URL}/download`, {
|
|
56
59
|
method: "POST",
|
|
57
60
|
headers: {
|
|
58
61
|
"Content-Type": "application/json",
|
|
59
62
|
"User-Agent": USER_AGENT
|
|
60
63
|
},
|
|
61
|
-
body: JSON.stringify({
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
repo,
|
|
66
|
+
dir: dir ?? null,
|
|
67
|
+
...projectHash ? { project_hash: projectHash } : {}
|
|
68
|
+
}),
|
|
62
69
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
63
70
|
});
|
|
64
71
|
}
|
|
@@ -357,35 +364,10 @@ function handleHttpError(res) {
|
|
|
357
364
|
);
|
|
358
365
|
}
|
|
359
366
|
}
|
|
360
|
-
var branchCache = /* @__PURE__ */ new Map();
|
|
361
|
-
async function getDefaultBranch(repo) {
|
|
362
|
-
const cached = branchCache.get(repo);
|
|
363
|
-
if (cached) return cached;
|
|
364
|
-
const { owner, name } = parseRepo(repo);
|
|
365
|
-
const res = await fetch(`${GITHUB_API_URL}/repos/${owner}/${name}`, {
|
|
366
|
-
headers: {
|
|
367
|
-
Accept: GITHUB_ACCEPT_HEADER,
|
|
368
|
-
"User-Agent": USER_AGENT,
|
|
369
|
-
...getAuthHeaders()
|
|
370
|
-
},
|
|
371
|
-
signal: createSignal()
|
|
372
|
-
});
|
|
373
|
-
if (res.status === 404) {
|
|
374
|
-
throw new Error(`Repository not found: ${repo}`);
|
|
375
|
-
}
|
|
376
|
-
handleHttpError(res);
|
|
377
|
-
if (!res.ok) {
|
|
378
|
-
throw new Error(`GitHub API error: ${res.status} ${res.statusText}`);
|
|
379
|
-
}
|
|
380
|
-
const data = await res.json();
|
|
381
|
-
branchCache.set(repo, data.default_branch);
|
|
382
|
-
return data.default_branch;
|
|
383
|
-
}
|
|
384
367
|
async function fetchFile(repo, path) {
|
|
385
368
|
const { owner, name } = parseRepo(repo);
|
|
386
|
-
const branch = await getDefaultBranch(repo);
|
|
387
369
|
const encodedPath = path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
388
|
-
const url = `${GITHUB_RAW}/${owner}/${name}/${
|
|
370
|
+
const url = `${GITHUB_RAW}/${owner}/${name}/HEAD/${encodedPath}`;
|
|
389
371
|
const res = await fetch(url, {
|
|
390
372
|
headers: { "User-Agent": USER_AGENT, ...getAuthHeaders() },
|
|
391
373
|
signal: createSignal()
|
|
@@ -417,8 +399,7 @@ async function fetchManifest(repo, dir) {
|
|
|
417
399
|
}
|
|
418
400
|
async function fetchRepoTree(repo) {
|
|
419
401
|
const { owner, name } = parseRepo(repo);
|
|
420
|
-
const
|
|
421
|
-
const url = `${GITHUB_API_URL}/repos/${owner}/${name}/git/trees/${branch}?recursive=1`;
|
|
402
|
+
const url = `${GITHUB_API_URL}/repos/${owner}/${name}/git/trees/HEAD?recursive=1`;
|
|
422
403
|
const res = await fetch(url, {
|
|
423
404
|
headers: {
|
|
424
405
|
Accept: GITHUB_ACCEPT_HEADER,
|
|
@@ -809,7 +790,8 @@ async function addCommand(repoInput, options) {
|
|
|
809
790
|
const summary = skillsInstalled > 0 ? `${installed} file(s) + ${skillsInstalled} skill(s)` : `${installed} file(s)`;
|
|
810
791
|
success(`Done! ${summary} installed, ${skipped} skipped.`);
|
|
811
792
|
if (installed + skillsInstalled > 0) {
|
|
812
|
-
|
|
793
|
+
const projectHash = (0, import_node_crypto.createHash)("sha256").update(cwd).digest("hex");
|
|
794
|
+
await recordDownload(repo, dir, projectHash).catch(() => {
|
|
813
795
|
});
|
|
814
796
|
}
|
|
815
797
|
} catch (err) {
|
|
@@ -1427,29 +1409,26 @@ function detectRepo(cwd) {
|
|
|
1427
1409
|
var import_chalk4 = __toESM(require("chalk"), 1);
|
|
1428
1410
|
async function searchCommand(query, options) {
|
|
1429
1411
|
try {
|
|
1430
|
-
if (!query && !options.target && !options.tag && !options.author) {
|
|
1431
|
-
error(
|
|
1432
|
-
"Please provide a search query or at least one filter (--target, --tag, --author)."
|
|
1433
|
-
);
|
|
1434
|
-
process.exitCode = 1;
|
|
1435
|
-
return;
|
|
1436
|
-
}
|
|
1437
1412
|
const filters = {};
|
|
1438
1413
|
if (options.target) filters.target = options.target;
|
|
1439
1414
|
if (options.tag) filters.tag = options.tag;
|
|
1440
1415
|
if (options.author) filters.author = options.author;
|
|
1441
|
-
const
|
|
1442
|
-
const label = query ? `"${query}"` : "the given filters";
|
|
1443
|
-
|
|
1416
|
+
const hasParams = !!(query || filters.target || filters.tag || filters.author);
|
|
1417
|
+
const label = query ? `"${query}"` : hasParams ? "the given filters" : "popular boilerplates";
|
|
1418
|
+
const response = await searchBoilerplates(query, filters);
|
|
1419
|
+
if (response.data.length === 0) {
|
|
1444
1420
|
info(`No boilerplates found for ${label}`);
|
|
1445
1421
|
return;
|
|
1446
1422
|
}
|
|
1447
1423
|
console.log();
|
|
1448
|
-
info(`Found ${
|
|
1424
|
+
info(`Found ${response.total} result(s) for ${label}:
|
|
1449
1425
|
`);
|
|
1450
|
-
for (const bp of
|
|
1426
|
+
for (const bp of response.data) {
|
|
1451
1427
|
formatResult(bp);
|
|
1452
1428
|
}
|
|
1429
|
+
console.log(
|
|
1430
|
+
import_chalk4.default.dim(` Browse more results and details at https://updose.dev/`)
|
|
1431
|
+
);
|
|
1453
1432
|
} catch (err) {
|
|
1454
1433
|
error(
|
|
1455
1434
|
err instanceof Error ? err.message : "An unexpected error occurred during search."
|
|
@@ -1463,10 +1442,9 @@ function formatResult(bp) {
|
|
|
1463
1442
|
if (bp.description) {
|
|
1464
1443
|
console.log(` ${bp.description}`);
|
|
1465
1444
|
}
|
|
1466
|
-
const rating = bp.avg_rating > 0 ? `${import_chalk4.default.yellow("\u2605")} ${bp.avg_rating}${bp.rating_count > 0 ? import_chalk4.default.dim(` (${bp.rating_count})`) : ""}` : import_chalk4.default.dim("\u2605 -");
|
|
1467
1445
|
const downloads = `${import_chalk4.default.green("\u2193")} ${bp.downloads.toLocaleString()}`;
|
|
1468
1446
|
const targets = import_chalk4.default.cyan(bp.targets.join(", "));
|
|
1469
|
-
console.log(` ${
|
|
1447
|
+
console.log(` ${downloads} ${targets}`);
|
|
1470
1448
|
if (bp.tags.length > 0) {
|
|
1471
1449
|
console.log(` ${bp.tags.map((t) => import_chalk4.default.dim(`#${t}`)).join(" ")}`);
|
|
1472
1450
|
}
|
|
@@ -1477,7 +1455,7 @@ function formatResult(bp) {
|
|
|
1477
1455
|
|
|
1478
1456
|
// src/index.ts
|
|
1479
1457
|
var program = new import_commander.Command();
|
|
1480
|
-
program.name("updose").description("AI coding tool boilerplate marketplace").version("0.
|
|
1458
|
+
program.name("updose").description("AI coding tool boilerplate marketplace").version("0.4.0");
|
|
1481
1459
|
program.command("add <repo>").description("Install a boilerplate").option("-y, --yes", "Skip all prompts and use defaults").option("--dry-run", "Preview install without writing files").action(addCommand);
|
|
1482
1460
|
program.command("search [query]").description("Search for boilerplates").option("--target <target>", "Filter by target (claude, codex, gemini)").option("--tag <tag>", "Filter by tag").option("--author <author>", "Filter by author").action(searchCommand);
|
|
1483
1461
|
program.command("init").description("Scaffold a new boilerplate repository").option("--dir <dir>", "Create boilerplate in a subdirectory").action(initCommand);
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
// src/commands/add.ts
|
|
7
|
+
import { createHash } from "crypto";
|
|
8
|
+
|
|
6
9
|
// src/constants.ts
|
|
7
10
|
var USER_AGENT = "updose-cli";
|
|
8
11
|
var MANIFEST_FILENAME = "updose.json";
|
|
@@ -29,14 +32,18 @@ async function searchBoilerplates(query, filters) {
|
|
|
29
32
|
}
|
|
30
33
|
return await res.json();
|
|
31
34
|
}
|
|
32
|
-
async function recordDownload(repo, dir) {
|
|
35
|
+
async function recordDownload(repo, dir, projectHash) {
|
|
33
36
|
await fetch(`${API_BASE_URL}/download`, {
|
|
34
37
|
method: "POST",
|
|
35
38
|
headers: {
|
|
36
39
|
"Content-Type": "application/json",
|
|
37
40
|
"User-Agent": USER_AGENT
|
|
38
41
|
},
|
|
39
|
-
body: JSON.stringify({
|
|
42
|
+
body: JSON.stringify({
|
|
43
|
+
repo,
|
|
44
|
+
dir: dir ?? null,
|
|
45
|
+
...projectHash ? { project_hash: projectHash } : {}
|
|
46
|
+
}),
|
|
40
47
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
41
48
|
});
|
|
42
49
|
}
|
|
@@ -335,35 +342,10 @@ function handleHttpError(res) {
|
|
|
335
342
|
);
|
|
336
343
|
}
|
|
337
344
|
}
|
|
338
|
-
var branchCache = /* @__PURE__ */ new Map();
|
|
339
|
-
async function getDefaultBranch(repo) {
|
|
340
|
-
const cached = branchCache.get(repo);
|
|
341
|
-
if (cached) return cached;
|
|
342
|
-
const { owner, name } = parseRepo(repo);
|
|
343
|
-
const res = await fetch(`${GITHUB_API_URL}/repos/${owner}/${name}`, {
|
|
344
|
-
headers: {
|
|
345
|
-
Accept: GITHUB_ACCEPT_HEADER,
|
|
346
|
-
"User-Agent": USER_AGENT,
|
|
347
|
-
...getAuthHeaders()
|
|
348
|
-
},
|
|
349
|
-
signal: createSignal()
|
|
350
|
-
});
|
|
351
|
-
if (res.status === 404) {
|
|
352
|
-
throw new Error(`Repository not found: ${repo}`);
|
|
353
|
-
}
|
|
354
|
-
handleHttpError(res);
|
|
355
|
-
if (!res.ok) {
|
|
356
|
-
throw new Error(`GitHub API error: ${res.status} ${res.statusText}`);
|
|
357
|
-
}
|
|
358
|
-
const data = await res.json();
|
|
359
|
-
branchCache.set(repo, data.default_branch);
|
|
360
|
-
return data.default_branch;
|
|
361
|
-
}
|
|
362
345
|
async function fetchFile(repo, path) {
|
|
363
346
|
const { owner, name } = parseRepo(repo);
|
|
364
|
-
const branch = await getDefaultBranch(repo);
|
|
365
347
|
const encodedPath = path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
366
|
-
const url = `${GITHUB_RAW}/${owner}/${name}/${
|
|
348
|
+
const url = `${GITHUB_RAW}/${owner}/${name}/HEAD/${encodedPath}`;
|
|
367
349
|
const res = await fetch(url, {
|
|
368
350
|
headers: { "User-Agent": USER_AGENT, ...getAuthHeaders() },
|
|
369
351
|
signal: createSignal()
|
|
@@ -395,8 +377,7 @@ async function fetchManifest(repo, dir) {
|
|
|
395
377
|
}
|
|
396
378
|
async function fetchRepoTree(repo) {
|
|
397
379
|
const { owner, name } = parseRepo(repo);
|
|
398
|
-
const
|
|
399
|
-
const url = `${GITHUB_API_URL}/repos/${owner}/${name}/git/trees/${branch}?recursive=1`;
|
|
380
|
+
const url = `${GITHUB_API_URL}/repos/${owner}/${name}/git/trees/HEAD?recursive=1`;
|
|
400
381
|
const res = await fetch(url, {
|
|
401
382
|
headers: {
|
|
402
383
|
Accept: GITHUB_ACCEPT_HEADER,
|
|
@@ -787,7 +768,8 @@ async function addCommand(repoInput, options) {
|
|
|
787
768
|
const summary = skillsInstalled > 0 ? `${installed} file(s) + ${skillsInstalled} skill(s)` : `${installed} file(s)`;
|
|
788
769
|
success(`Done! ${summary} installed, ${skipped} skipped.`);
|
|
789
770
|
if (installed + skillsInstalled > 0) {
|
|
790
|
-
|
|
771
|
+
const projectHash = createHash("sha256").update(cwd).digest("hex");
|
|
772
|
+
await recordDownload(repo, dir, projectHash).catch(() => {
|
|
791
773
|
});
|
|
792
774
|
}
|
|
793
775
|
} catch (err) {
|
|
@@ -1405,29 +1387,26 @@ function detectRepo(cwd) {
|
|
|
1405
1387
|
import chalk4 from "chalk";
|
|
1406
1388
|
async function searchCommand(query, options) {
|
|
1407
1389
|
try {
|
|
1408
|
-
if (!query && !options.target && !options.tag && !options.author) {
|
|
1409
|
-
error(
|
|
1410
|
-
"Please provide a search query or at least one filter (--target, --tag, --author)."
|
|
1411
|
-
);
|
|
1412
|
-
process.exitCode = 1;
|
|
1413
|
-
return;
|
|
1414
|
-
}
|
|
1415
1390
|
const filters = {};
|
|
1416
1391
|
if (options.target) filters.target = options.target;
|
|
1417
1392
|
if (options.tag) filters.tag = options.tag;
|
|
1418
1393
|
if (options.author) filters.author = options.author;
|
|
1419
|
-
const
|
|
1420
|
-
const label = query ? `"${query}"` : "the given filters";
|
|
1421
|
-
|
|
1394
|
+
const hasParams = !!(query || filters.target || filters.tag || filters.author);
|
|
1395
|
+
const label = query ? `"${query}"` : hasParams ? "the given filters" : "popular boilerplates";
|
|
1396
|
+
const response = await searchBoilerplates(query, filters);
|
|
1397
|
+
if (response.data.length === 0) {
|
|
1422
1398
|
info(`No boilerplates found for ${label}`);
|
|
1423
1399
|
return;
|
|
1424
1400
|
}
|
|
1425
1401
|
console.log();
|
|
1426
|
-
info(`Found ${
|
|
1402
|
+
info(`Found ${response.total} result(s) for ${label}:
|
|
1427
1403
|
`);
|
|
1428
|
-
for (const bp of
|
|
1404
|
+
for (const bp of response.data) {
|
|
1429
1405
|
formatResult(bp);
|
|
1430
1406
|
}
|
|
1407
|
+
console.log(
|
|
1408
|
+
chalk4.dim(` Browse more results and details at https://updose.dev/`)
|
|
1409
|
+
);
|
|
1431
1410
|
} catch (err) {
|
|
1432
1411
|
error(
|
|
1433
1412
|
err instanceof Error ? err.message : "An unexpected error occurred during search."
|
|
@@ -1441,10 +1420,9 @@ function formatResult(bp) {
|
|
|
1441
1420
|
if (bp.description) {
|
|
1442
1421
|
console.log(` ${bp.description}`);
|
|
1443
1422
|
}
|
|
1444
|
-
const rating = bp.avg_rating > 0 ? `${chalk4.yellow("\u2605")} ${bp.avg_rating}${bp.rating_count > 0 ? chalk4.dim(` (${bp.rating_count})`) : ""}` : chalk4.dim("\u2605 -");
|
|
1445
1423
|
const downloads = `${chalk4.green("\u2193")} ${bp.downloads.toLocaleString()}`;
|
|
1446
1424
|
const targets = chalk4.cyan(bp.targets.join(", "));
|
|
1447
|
-
console.log(` ${
|
|
1425
|
+
console.log(` ${downloads} ${targets}`);
|
|
1448
1426
|
if (bp.tags.length > 0) {
|
|
1449
1427
|
console.log(` ${bp.tags.map((t) => chalk4.dim(`#${t}`)).join(" ")}`);
|
|
1450
1428
|
}
|
|
@@ -1455,7 +1433,7 @@ function formatResult(bp) {
|
|
|
1455
1433
|
|
|
1456
1434
|
// src/index.ts
|
|
1457
1435
|
var program = new Command();
|
|
1458
|
-
program.name("updose").description("AI coding tool boilerplate marketplace").version("0.
|
|
1436
|
+
program.name("updose").description("AI coding tool boilerplate marketplace").version("0.4.0");
|
|
1459
1437
|
program.command("add <repo>").description("Install a boilerplate").option("-y, --yes", "Skip all prompts and use defaults").option("--dry-run", "Preview install without writing files").action(addCommand);
|
|
1460
1438
|
program.command("search [query]").description("Search for boilerplates").option("--target <target>", "Filter by target (claude, codex, gemini)").option("--tag <tag>", "Filter by tag").option("--author <author>", "Filter by author").action(searchCommand);
|
|
1461
1439
|
program.command("init").description("Scaffold a new boilerplate repository").option("--dir <dir>", "Create boilerplate in a subdirectory").action(initCommand);
|