@rayul/kordis 0.6.0 → 0.6.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/dist/index.js +195 -101
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -904,8 +904,8 @@ var init_client = __esm({
|
|
|
904
904
|
if (this.config.userAgent) headers["user-agent"] = this.config.userAgent;
|
|
905
905
|
return headers;
|
|
906
906
|
}
|
|
907
|
-
async request(
|
|
908
|
-
const url = new URL(this.baseUrl +
|
|
907
|
+
async request(path2, opts = {}) {
|
|
908
|
+
const url = new URL(this.baseUrl + path2);
|
|
909
909
|
if (opts.query) {
|
|
910
910
|
for (const [k, v] of Object.entries(opts.query)) {
|
|
911
911
|
if (v === void 0) continue;
|
|
@@ -1367,11 +1367,11 @@ async function detectLanguages(cwd = process.cwd()) {
|
|
|
1367
1367
|
await walk(cwd, 4, counts);
|
|
1368
1368
|
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([tag]) => tag);
|
|
1369
1369
|
}
|
|
1370
|
-
async function walk(
|
|
1370
|
+
async function walk(dir2, depth, counts) {
|
|
1371
1371
|
if (depth < 0) return;
|
|
1372
1372
|
let entries;
|
|
1373
1373
|
try {
|
|
1374
|
-
entries = await readdir(
|
|
1374
|
+
entries = await readdir(dir2, { withFileTypes: true });
|
|
1375
1375
|
} catch {
|
|
1376
1376
|
return;
|
|
1377
1377
|
}
|
|
@@ -1380,7 +1380,7 @@ async function walk(dir, depth, counts) {
|
|
|
1380
1380
|
if (SKIP_DIRS.has(e.name)) continue;
|
|
1381
1381
|
}
|
|
1382
1382
|
if (SKIP_DIRS.has(e.name)) continue;
|
|
1383
|
-
const full = join3(
|
|
1383
|
+
const full = join3(dir2, e.name);
|
|
1384
1384
|
if (e.isDirectory()) {
|
|
1385
1385
|
await walk(full, depth - 1, counts);
|
|
1386
1386
|
} else if (e.isFile()) {
|
|
@@ -1509,12 +1509,46 @@ async function getCachedContextHeader(cwd = process.cwd()) {
|
|
|
1509
1509
|
return header;
|
|
1510
1510
|
}
|
|
1511
1511
|
|
|
1512
|
+
// src/lib/pending-login.ts
|
|
1513
|
+
import { readFile as readFile4, writeFile as writeFile3, unlink as unlink2, mkdir as mkdir3 } from "fs/promises";
|
|
1514
|
+
import { homedir as homedir4 } from "os";
|
|
1515
|
+
import { join as join4 } from "path";
|
|
1516
|
+
function dir() {
|
|
1517
|
+
return process.env["KORDIS_HOME"] ?? join4(homedir4(), ".kordis");
|
|
1518
|
+
}
|
|
1519
|
+
function path() {
|
|
1520
|
+
return join4(dir(), "pending-login.json");
|
|
1521
|
+
}
|
|
1522
|
+
async function readPending() {
|
|
1523
|
+
try {
|
|
1524
|
+
const raw = await readFile4(path(), "utf8");
|
|
1525
|
+
const parsed = JSON.parse(raw);
|
|
1526
|
+
if (!parsed.device_code || !parsed.expires_at_epoch) return null;
|
|
1527
|
+
return parsed;
|
|
1528
|
+
} catch {
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
async function savePending(file) {
|
|
1533
|
+
await mkdir3(dir(), { recursive: true });
|
|
1534
|
+
await writeFile3(path(), JSON.stringify(file, null, 2) + "\n", "utf8");
|
|
1535
|
+
}
|
|
1536
|
+
async function clearPending() {
|
|
1537
|
+
try {
|
|
1538
|
+
await unlink2(path());
|
|
1539
|
+
} catch {
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function isExpired(file) {
|
|
1543
|
+
return Math.floor(Date.now() / 1e3) >= file.expires_at_epoch;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1512
1546
|
// src/version.ts
|
|
1513
1547
|
import { readFileSync } from "fs";
|
|
1514
1548
|
import { fileURLToPath } from "url";
|
|
1515
|
-
import { dirname, join as
|
|
1549
|
+
import { dirname, join as join5 } from "path";
|
|
1516
1550
|
var __dir = dirname(fileURLToPath(import.meta.url));
|
|
1517
|
-
var pkgPath =
|
|
1551
|
+
var pkgPath = join5(__dir, "..", "package.json");
|
|
1518
1552
|
var VERSION = (() => {
|
|
1519
1553
|
try {
|
|
1520
1554
|
const raw = readFileSync(pkgPath, "utf8");
|
|
@@ -1527,10 +1561,48 @@ var VERSION = (() => {
|
|
|
1527
1561
|
|
|
1528
1562
|
// src/client.ts
|
|
1529
1563
|
async function useClient() {
|
|
1530
|
-
|
|
1564
|
+
let cfg = await loadCliConfig();
|
|
1531
1565
|
const keychain = await createKeychain();
|
|
1532
|
-
|
|
1566
|
+
let username = cfg.username ?? null;
|
|
1533
1567
|
let currentTokens = username ? await keychain.load(username) : null;
|
|
1568
|
+
if (!currentTokens) {
|
|
1569
|
+
const pending = await readPending();
|
|
1570
|
+
if (pending) {
|
|
1571
|
+
if (isExpired(pending)) {
|
|
1572
|
+
await clearPending();
|
|
1573
|
+
} else {
|
|
1574
|
+
const bootstrapApi = new KordisClient({
|
|
1575
|
+
baseUrl: cfg.apiUrl,
|
|
1576
|
+
userAgent: `kordis-cli/${VERSION} node/${process.version}`
|
|
1577
|
+
});
|
|
1578
|
+
try {
|
|
1579
|
+
const tokens = await bootstrapApi.pollDeviceFlow(pending.device_code);
|
|
1580
|
+
const meClient = new KordisClient({
|
|
1581
|
+
baseUrl: cfg.apiUrl,
|
|
1582
|
+
userAgent: `kordis-cli/${VERSION} node/${process.version}`,
|
|
1583
|
+
getAccessToken: () => tokens.access_token
|
|
1584
|
+
});
|
|
1585
|
+
const me = await meClient.me();
|
|
1586
|
+
username = me.username;
|
|
1587
|
+
currentTokens = {
|
|
1588
|
+
access_token: tokens.access_token,
|
|
1589
|
+
refresh_token: tokens.refresh_token,
|
|
1590
|
+
session_id: tokens.session_id,
|
|
1591
|
+
expires_at_epoch: Math.floor(Date.now() / 1e3) + tokens.expires_in
|
|
1592
|
+
};
|
|
1593
|
+
await keychain.save(username, currentTokens);
|
|
1594
|
+
cfg = { ...cfg, username, sessionId: tokens.session_id };
|
|
1595
|
+
await saveCliConfig(cfg);
|
|
1596
|
+
await clearPending();
|
|
1597
|
+
} catch (err) {
|
|
1598
|
+
const code = err.code;
|
|
1599
|
+
if (code && code !== "authorization_pending") {
|
|
1600
|
+
await clearPending();
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1534
1606
|
const personalizationEnabled = cfg.personalization === "on" && process.env["KORDIS_NO_CONTEXT"] !== "1" && !process.argv.includes("--no-context");
|
|
1535
1607
|
const api = new KordisClient({
|
|
1536
1608
|
baseUrl: cfg.apiUrl,
|
|
@@ -1608,12 +1680,14 @@ var c = kleur;
|
|
|
1608
1680
|
|
|
1609
1681
|
// src/commands/login.ts
|
|
1610
1682
|
function loginCommand() {
|
|
1611
|
-
return new Command("login").description("authenticate with the Kordis registry").option("--provider <name>", "OAuth provider", "discord").option("--no-browser", "don't auto-open the browser; print the URL instead").
|
|
1683
|
+
return new Command("login").description("authenticate with the Kordis registry").option("--provider <name>", "OAuth provider", "discord").option("--no-browser", "don't auto-open the browser; print the URL instead").option(
|
|
1684
|
+
"--emit-only",
|
|
1685
|
+
"start device flow, open browser, save pending state, exit immediately \u2014 for non-interactive (agent) contexts where the 10-min poll would exceed the bash timeout"
|
|
1686
|
+
).action(async (opts) => {
|
|
1612
1687
|
const bundle = await useClient();
|
|
1613
1688
|
const started = spinner("Requesting device code");
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
);
|
|
1689
|
+
const provider = opts.provider ?? "discord";
|
|
1690
|
+
const device = await bundle.api.startDeviceFlow(provider);
|
|
1617
1691
|
started.succeed();
|
|
1618
1692
|
printHuman("");
|
|
1619
1693
|
printHuman(c.bold("Confirm the code in your browser:"));
|
|
@@ -1633,6 +1707,29 @@ function loginCommand() {
|
|
|
1633
1707
|
}
|
|
1634
1708
|
printHuman("");
|
|
1635
1709
|
}
|
|
1710
|
+
if (opts.emitOnly) {
|
|
1711
|
+
await savePending({
|
|
1712
|
+
device_code: device.device_code,
|
|
1713
|
+
user_code: device.user_code,
|
|
1714
|
+
verification_url: device.verification_url,
|
|
1715
|
+
expires_at_epoch: Math.floor(Date.now() / 1e3) + device.expires_in,
|
|
1716
|
+
provider
|
|
1717
|
+
});
|
|
1718
|
+
printDual({
|
|
1719
|
+
json: {
|
|
1720
|
+
status: "pending",
|
|
1721
|
+
user_code: device.user_code,
|
|
1722
|
+
verification_url: device.verification_url,
|
|
1723
|
+
expires_in: device.expires_in
|
|
1724
|
+
},
|
|
1725
|
+
human: () => printHuman(
|
|
1726
|
+
c.gray(
|
|
1727
|
+
"\u2192 pending login saved. Click Authorize in the browser, then re-run any kordis command \u2014 the next call will claim the token automatically."
|
|
1728
|
+
)
|
|
1729
|
+
)
|
|
1730
|
+
});
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1636
1733
|
const poll = spinner("Waiting for browser confirmation");
|
|
1637
1734
|
const start = Date.now();
|
|
1638
1735
|
const deadline = start + device.expires_in * 1e3;
|
|
@@ -1682,14 +1779,25 @@ function loginCommand() {
|
|
|
1682
1779
|
|
|
1683
1780
|
// src/commands/whoami.ts
|
|
1684
1781
|
import { Command as Command2 } from "commander";
|
|
1782
|
+
|
|
1783
|
+
// src/lib/require-auth.ts
|
|
1784
|
+
init_src2();
|
|
1785
|
+
function requireAuth(bundle) {
|
|
1786
|
+
if (bundle.username) return;
|
|
1787
|
+
throw new KordisApiError(401, {
|
|
1788
|
+
error: {
|
|
1789
|
+
code: "not_authenticated",
|
|
1790
|
+
message: "authentication required",
|
|
1791
|
+
hint: "Run `kordis login --emit-only` to start browser sign-in (exits immediately, no blocking poll), then re-run this command \u2014 the next CLI call auto-claims the token. For interactive shells, plain `kordis login` (10-min poll) also works."
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
// src/commands/whoami.ts
|
|
1685
1797
|
function whoamiCommand() {
|
|
1686
1798
|
return new Command2("whoami").description("show the currently authenticated user").action(async () => {
|
|
1687
1799
|
const bundle = await useClient();
|
|
1688
|
-
|
|
1689
|
-
printHuman(c.yellow("Not logged in. Run `kordis login`."));
|
|
1690
|
-
process.exitCode = 10;
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1800
|
+
requireAuth(bundle);
|
|
1693
1801
|
const me = await bundle.authedRequest((api) => api.me());
|
|
1694
1802
|
printDual({
|
|
1695
1803
|
json: me,
|
|
@@ -1706,18 +1814,18 @@ function whoamiCommand() {
|
|
|
1706
1814
|
import { Command as Command3 } from "commander";
|
|
1707
1815
|
|
|
1708
1816
|
// src/lib/recommendation-cache.ts
|
|
1709
|
-
import { readFile as
|
|
1710
|
-
import { homedir as
|
|
1711
|
-
import { join as
|
|
1817
|
+
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1818
|
+
import { homedir as homedir5 } from "os";
|
|
1819
|
+
import { join as join6 } from "path";
|
|
1712
1820
|
function cacheDir() {
|
|
1713
|
-
return process.env["KORDIS_HOME"] ??
|
|
1821
|
+
return process.env["KORDIS_HOME"] ?? join6(homedir5(), ".kordis");
|
|
1714
1822
|
}
|
|
1715
1823
|
function cachePath() {
|
|
1716
|
-
return
|
|
1824
|
+
return join6(cacheDir(), "recommendations.json");
|
|
1717
1825
|
}
|
|
1718
1826
|
async function readCache() {
|
|
1719
1827
|
try {
|
|
1720
|
-
const raw = await
|
|
1828
|
+
const raw = await readFile5(cachePath(), "utf8");
|
|
1721
1829
|
const parsed = JSON.parse(raw);
|
|
1722
1830
|
if (!parsed.generated_at || !Array.isArray(parsed.items)) return null;
|
|
1723
1831
|
return parsed;
|
|
@@ -1726,23 +1834,23 @@ async function readCache() {
|
|
|
1726
1834
|
}
|
|
1727
1835
|
}
|
|
1728
1836
|
async function writeCache(items, generatedAt) {
|
|
1729
|
-
await
|
|
1837
|
+
await mkdir4(cacheDir(), { recursive: true });
|
|
1730
1838
|
const payload = {
|
|
1731
1839
|
generated_at: generatedAt,
|
|
1732
1840
|
items,
|
|
1733
1841
|
seen_at: null
|
|
1734
1842
|
};
|
|
1735
|
-
await
|
|
1843
|
+
await writeFile4(cachePath(), JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
1736
1844
|
}
|
|
1737
1845
|
async function markSeen() {
|
|
1738
1846
|
const existing = await readCache();
|
|
1739
1847
|
if (!existing) return;
|
|
1740
|
-
await
|
|
1848
|
+
await mkdir4(cacheDir(), { recursive: true });
|
|
1741
1849
|
const updated = {
|
|
1742
1850
|
...existing,
|
|
1743
1851
|
seen_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1744
1852
|
};
|
|
1745
|
-
await
|
|
1853
|
+
await writeFile4(cachePath(), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
1746
1854
|
}
|
|
1747
1855
|
|
|
1748
1856
|
// src/lib/recommendation-footer.ts
|
|
@@ -1869,8 +1977,8 @@ function searchCommand() {
|
|
|
1869
1977
|
|
|
1870
1978
|
// src/commands/push.ts
|
|
1871
1979
|
import { Command as Command4 } from "commander";
|
|
1872
|
-
import { readFile as
|
|
1873
|
-
import { resolve, join as
|
|
1980
|
+
import { readFile as readFile6, stat as stat2, readdir as readdir2 } from "fs/promises";
|
|
1981
|
+
import { resolve, join as join7, posix, sep } from "path";
|
|
1874
1982
|
import JSZip from "jszip";
|
|
1875
1983
|
|
|
1876
1984
|
// ../../packages/harness-schema/src/parser.ts
|
|
@@ -2435,9 +2543,9 @@ function pushCommand() {
|
|
|
2435
2543
|
"--slug <slug>",
|
|
2436
2544
|
'override slug; accepts "user/slug" to point at a specific package'
|
|
2437
2545
|
).option("--type <type>", "override type inference").option("--changelog <text>", "optional changelog entry").option("--dry-run", "validate only; do not publish").option("--no-wait", "return immediately without waiting for review").action(
|
|
2438
|
-
async (
|
|
2546
|
+
async (path2, opts) => {
|
|
2439
2547
|
const bundle = await useClient();
|
|
2440
|
-
const abs = resolve(process.cwd(),
|
|
2548
|
+
const abs = resolve(process.cwd(), path2);
|
|
2441
2549
|
const s = await stat2(abs);
|
|
2442
2550
|
let content;
|
|
2443
2551
|
let assets = [];
|
|
@@ -2448,7 +2556,7 @@ function pushCommand() {
|
|
|
2448
2556
|
({ content, assets } = await bundleSkillZip(abs));
|
|
2449
2557
|
content = ensureTypeField(content, "skill");
|
|
2450
2558
|
} else {
|
|
2451
|
-
content = await
|
|
2559
|
+
content = await readFile6(abs, "utf8");
|
|
2452
2560
|
}
|
|
2453
2561
|
let nameFromFm = null;
|
|
2454
2562
|
try {
|
|
@@ -2629,19 +2737,19 @@ function shouldExclude(relPath, isDir) {
|
|
|
2629
2737
|
return false;
|
|
2630
2738
|
}
|
|
2631
2739
|
async function walkDir(base, relDir, collected) {
|
|
2632
|
-
const entries = await readdir2(
|
|
2740
|
+
const entries = await readdir2(join7(base, relDir), { withFileTypes: true });
|
|
2633
2741
|
for (const entry of entries) {
|
|
2634
2742
|
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
2635
2743
|
if (shouldExclude(rel, entry.isDirectory())) continue;
|
|
2636
2744
|
if (entry.isDirectory()) {
|
|
2637
2745
|
await walkDir(base, rel, collected);
|
|
2638
2746
|
} else if (entry.isFile()) {
|
|
2639
|
-
collected.push({ rel, abs:
|
|
2747
|
+
collected.push({ rel, abs: join7(base, rel) });
|
|
2640
2748
|
}
|
|
2641
2749
|
}
|
|
2642
2750
|
}
|
|
2643
2751
|
async function readAsset(absPath) {
|
|
2644
|
-
const buf = await
|
|
2752
|
+
const buf = await readFile6(absPath);
|
|
2645
2753
|
const text = buf.toString("utf8");
|
|
2646
2754
|
const roundtrip = Buffer.from(text, "utf8");
|
|
2647
2755
|
if (roundtrip.equals(buf)) {
|
|
@@ -2651,14 +2759,14 @@ async function readAsset(absPath) {
|
|
|
2651
2759
|
printHuman(c.gray(` \u2192 encoding ${absPath.split(sep).pop()} as base64`));
|
|
2652
2760
|
return { content: b64, encoding: "base64" };
|
|
2653
2761
|
}
|
|
2654
|
-
async function bundleDirectory(
|
|
2762
|
+
async function bundleDirectory(dir2) {
|
|
2655
2763
|
const all = [];
|
|
2656
|
-
await walkDir(
|
|
2764
|
+
await walkDir(dir2, "", all);
|
|
2657
2765
|
const skillEntry = all.find((f) => f.rel === "SKILL.md");
|
|
2658
2766
|
if (!skillEntry) {
|
|
2659
|
-
throw new Error(`No SKILL.md found in directory ${
|
|
2767
|
+
throw new Error(`No SKILL.md found in directory ${dir2}. Push a directory that has SKILL.md at its root.`);
|
|
2660
2768
|
}
|
|
2661
|
-
const content = await
|
|
2769
|
+
const content = await readFile6(skillEntry.abs, "utf8");
|
|
2662
2770
|
const assets = [];
|
|
2663
2771
|
for (const f of all) {
|
|
2664
2772
|
if (f.rel === "SKILL.md") continue;
|
|
@@ -2668,7 +2776,7 @@ async function bundleDirectory(dir) {
|
|
|
2668
2776
|
return { content, assets };
|
|
2669
2777
|
}
|
|
2670
2778
|
async function bundleSkillZip(zipPath) {
|
|
2671
|
-
const buf = await
|
|
2779
|
+
const buf = await readFile6(zipPath);
|
|
2672
2780
|
const zip = await JSZip.loadAsync(buf);
|
|
2673
2781
|
const entries = Object.keys(zip.files).filter((k) => !zip.files[k].dir);
|
|
2674
2782
|
if (entries.length === 0) throw new Error(".skill file is empty");
|
|
@@ -2702,8 +2810,8 @@ async function bundleSkillZip(zipPath) {
|
|
|
2702
2810
|
|
|
2703
2811
|
// src/commands/pull.ts
|
|
2704
2812
|
import { Command as Command5 } from "commander";
|
|
2705
|
-
import { mkdir as
|
|
2706
|
-
import { dirname as dirname2, join as
|
|
2813
|
+
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
2814
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
2707
2815
|
|
|
2708
2816
|
// ../../packages/translators/src/skill/identity.ts
|
|
2709
2817
|
function buildSkillFile(input, outputPath) {
|
|
@@ -3145,21 +3253,21 @@ function getTranslator(type, format) {
|
|
|
3145
3253
|
}
|
|
3146
3254
|
|
|
3147
3255
|
// src/lockfile.ts
|
|
3148
|
-
import { homedir as
|
|
3149
|
-
import { join as
|
|
3150
|
-
import { readFile as
|
|
3151
|
-
var LOCKFILE_PATH =
|
|
3256
|
+
import { homedir as homedir6 } from "os";
|
|
3257
|
+
import { join as join8 } from "path";
|
|
3258
|
+
import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
3259
|
+
var LOCKFILE_PATH = join8(homedir6(), ".kordis", "installed.json");
|
|
3152
3260
|
async function readLockfile() {
|
|
3153
3261
|
try {
|
|
3154
|
-
const raw = await
|
|
3262
|
+
const raw = await readFile7(LOCKFILE_PATH, "utf8");
|
|
3155
3263
|
return JSON.parse(raw);
|
|
3156
3264
|
} catch {
|
|
3157
3265
|
return { packages: {} };
|
|
3158
3266
|
}
|
|
3159
3267
|
}
|
|
3160
3268
|
async function writeLockfile(lf) {
|
|
3161
|
-
await
|
|
3162
|
-
await
|
|
3269
|
+
await mkdir5(join8(homedir6(), ".kordis"), { recursive: true });
|
|
3270
|
+
await writeFile5(LOCKFILE_PATH, JSON.stringify(lf, null, 2) + "\n", "utf8");
|
|
3163
3271
|
}
|
|
3164
3272
|
async function recordInstalled(fullSlug, entry) {
|
|
3165
3273
|
const lf = await readLockfile();
|
|
@@ -3195,8 +3303,8 @@ async function doPullInstall(fullSlug, tool, installRoot, version) {
|
|
|
3195
3303
|
assets: contentRes.assets
|
|
3196
3304
|
});
|
|
3197
3305
|
for (const f of result.files) {
|
|
3198
|
-
const abs =
|
|
3199
|
-
await
|
|
3306
|
+
const abs = join9(installRoot, f.path);
|
|
3307
|
+
await mkdir6(dirname2(abs), { recursive: true });
|
|
3200
3308
|
await writeEmittedFile(abs, f.content, f.encoding);
|
|
3201
3309
|
written.push(abs);
|
|
3202
3310
|
}
|
|
@@ -3215,8 +3323,8 @@ async function doPullInstall(fullSlug, tool, installRoot, version) {
|
|
|
3215
3323
|
`Tool "${format}" is not supported for type "${parsed.type}"; installed canonical form instead.`
|
|
3216
3324
|
);
|
|
3217
3325
|
for (const f of result.files) {
|
|
3218
|
-
const abs =
|
|
3219
|
-
await
|
|
3326
|
+
const abs = join9(installRoot, f.path);
|
|
3327
|
+
await mkdir6(dirname2(abs), { recursive: true });
|
|
3220
3328
|
await writeEmittedFile(abs, f.content, f.encoding);
|
|
3221
3329
|
written.push(abs);
|
|
3222
3330
|
}
|
|
@@ -3293,9 +3401,9 @@ async function detectTool(root) {
|
|
|
3293
3401
|
[".windsurfrules", "windsurf"],
|
|
3294
3402
|
[".github/copilot-instructions.md", "copilot"]
|
|
3295
3403
|
];
|
|
3296
|
-
for (const [
|
|
3404
|
+
for (const [path2, tool] of checks) {
|
|
3297
3405
|
try {
|
|
3298
|
-
await stat3(
|
|
3406
|
+
await stat3(join9(root, path2));
|
|
3299
3407
|
return tool;
|
|
3300
3408
|
} catch {
|
|
3301
3409
|
}
|
|
@@ -3304,9 +3412,9 @@ async function detectTool(root) {
|
|
|
3304
3412
|
}
|
|
3305
3413
|
async function writeEmittedFile(abs, content, encoding) {
|
|
3306
3414
|
if (encoding === "base64") {
|
|
3307
|
-
await
|
|
3415
|
+
await writeFile6(abs, Buffer.from(content, "base64"));
|
|
3308
3416
|
} else {
|
|
3309
|
-
await
|
|
3417
|
+
await writeFile6(abs, content, "utf8");
|
|
3310
3418
|
}
|
|
3311
3419
|
}
|
|
3312
3420
|
|
|
@@ -3340,8 +3448,8 @@ function readCommand() {
|
|
|
3340
3448
|
`${c.gray("\u2014")} ${c.bold(`\u24D8 ${bundled.length} bundled asset(s)`)} \u2014 fetch with ${c.cyan(`kordis pull ${res.full_slug}`)}:
|
|
3341
3449
|
`
|
|
3342
3450
|
);
|
|
3343
|
-
for (const
|
|
3344
|
-
process.stderr.write(` ${c.gray("-")} ${
|
|
3451
|
+
for (const path2 of bundled) {
|
|
3452
|
+
process.stderr.write(` ${c.gray("-")} ${path2}
|
|
3345
3453
|
`);
|
|
3346
3454
|
}
|
|
3347
3455
|
}
|
|
@@ -3427,9 +3535,9 @@ function logoutCommand() {
|
|
|
3427
3535
|
}
|
|
3428
3536
|
|
|
3429
3537
|
// src/commands/setup.ts
|
|
3430
|
-
import { readFile as
|
|
3431
|
-
import { homedir as
|
|
3432
|
-
import { join as
|
|
3538
|
+
import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, access as access3 } from "fs/promises";
|
|
3539
|
+
import { homedir as homedir7 } from "os";
|
|
3540
|
+
import { join as join10, dirname as dirname3 } from "path";
|
|
3433
3541
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3434
3542
|
import { Command as Command9 } from "commander";
|
|
3435
3543
|
import prompts from "prompts";
|
|
@@ -3452,9 +3560,9 @@ var SKILL_TARGETS = [
|
|
|
3452
3560
|
{ tool: "Codex CLI", dir: ".codex", skillsDir: ".codex/skills" },
|
|
3453
3561
|
{ tool: "Gemini CLI", dir: ".gemini", skillsDir: ".gemini/skills" }
|
|
3454
3562
|
];
|
|
3455
|
-
async function exists(
|
|
3563
|
+
async function exists(path2) {
|
|
3456
3564
|
try {
|
|
3457
|
-
await access3(
|
|
3565
|
+
await access3(path2);
|
|
3458
3566
|
return true;
|
|
3459
3567
|
} catch {
|
|
3460
3568
|
return false;
|
|
@@ -3464,18 +3572,18 @@ function assetRoots() {
|
|
|
3464
3572
|
const __dir2 = dirname3(fileURLToPath2(import.meta.url));
|
|
3465
3573
|
return {
|
|
3466
3574
|
// Built binary: bin/kordis.js → assets/ is one level up
|
|
3467
|
-
bundled:
|
|
3575
|
+
bundled: join10(__dir2, "..", "assets"),
|
|
3468
3576
|
// Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
|
|
3469
|
-
source:
|
|
3577
|
+
source: join10(__dir2, "..", "..", "..", "..", "packages", "skills")
|
|
3470
3578
|
};
|
|
3471
3579
|
}
|
|
3472
3580
|
async function pickAssetRoot() {
|
|
3473
3581
|
const { bundled, source } = assetRoots();
|
|
3474
|
-
return await exists(
|
|
3582
|
+
return await exists(join10(bundled, "KORDIS.md")) ? bundled : source;
|
|
3475
3583
|
}
|
|
3476
3584
|
async function loadHubContent() {
|
|
3477
3585
|
const root = await pickAssetRoot();
|
|
3478
|
-
const raw = await
|
|
3586
|
+
const raw = await readFile8(join10(root, "KORDIS.md"), "utf-8");
|
|
3479
3587
|
if (raw.startsWith("---")) {
|
|
3480
3588
|
const end = raw.indexOf("\n---", 4);
|
|
3481
3589
|
if (end !== -1) return raw.slice(end + 4).trimStart();
|
|
@@ -3484,7 +3592,7 @@ async function loadHubContent() {
|
|
|
3484
3592
|
}
|
|
3485
3593
|
async function loadSubSkillContent(name) {
|
|
3486
3594
|
const root = await pickAssetRoot();
|
|
3487
|
-
return
|
|
3595
|
+
return readFile8(join10(root, name, "SKILL.md"), "utf-8");
|
|
3488
3596
|
}
|
|
3489
3597
|
async function injectBlock(filePath, content, dryRun) {
|
|
3490
3598
|
const block = `${MARKER_START}
|
|
@@ -3493,14 +3601,14 @@ ${MARKER_END}`;
|
|
|
3493
3601
|
let existing = "";
|
|
3494
3602
|
let fileExists = true;
|
|
3495
3603
|
try {
|
|
3496
|
-
existing = await
|
|
3604
|
+
existing = await readFile8(filePath, "utf-8");
|
|
3497
3605
|
} catch {
|
|
3498
3606
|
fileExists = false;
|
|
3499
3607
|
}
|
|
3500
3608
|
if (!fileExists) {
|
|
3501
3609
|
if (!dryRun) {
|
|
3502
|
-
await
|
|
3503
|
-
await
|
|
3610
|
+
await mkdir7(dirname3(filePath), { recursive: true });
|
|
3611
|
+
await writeFile7(filePath, block + "\n", "utf-8");
|
|
3504
3612
|
}
|
|
3505
3613
|
return "injected";
|
|
3506
3614
|
}
|
|
@@ -3511,31 +3619,31 @@ ${MARKER_END}`;
|
|
|
3511
3619
|
if (current === block) return "unchanged";
|
|
3512
3620
|
if (!dryRun) {
|
|
3513
3621
|
const updated = existing.slice(0, startIdx) + block + existing.slice(endIdx + MARKER_END.length);
|
|
3514
|
-
await
|
|
3622
|
+
await writeFile7(filePath, updated, "utf-8");
|
|
3515
3623
|
}
|
|
3516
3624
|
return "updated";
|
|
3517
3625
|
}
|
|
3518
3626
|
if (!dryRun) {
|
|
3519
|
-
await
|
|
3627
|
+
await writeFile7(filePath, existing.trimEnd() + "\n\n" + block + "\n", "utf-8");
|
|
3520
3628
|
}
|
|
3521
3629
|
return "injected";
|
|
3522
3630
|
}
|
|
3523
3631
|
async function copyBundledSkill(filePath, content, dryRun) {
|
|
3524
3632
|
let existing = null;
|
|
3525
3633
|
try {
|
|
3526
|
-
existing = await
|
|
3634
|
+
existing = await readFile8(filePath, "utf-8");
|
|
3527
3635
|
} catch {
|
|
3528
3636
|
}
|
|
3529
3637
|
if (existing === content) return "unchanged";
|
|
3530
3638
|
if (!dryRun) {
|
|
3531
|
-
await
|
|
3532
|
-
await
|
|
3639
|
+
await mkdir7(dirname3(filePath), { recursive: true });
|
|
3640
|
+
await writeFile7(filePath, content, "utf-8");
|
|
3533
3641
|
}
|
|
3534
3642
|
return existing === null ? "injected" : "updated";
|
|
3535
3643
|
}
|
|
3536
3644
|
function setupCommand() {
|
|
3537
3645
|
return new Command9("setup").description("inject kordis usage guide into global agent instruction files").option("--silent", "suppress errors \u2014 safe for postinstall").option("--dry-run", "detect installed tools without writing any files").action(async (opts) => {
|
|
3538
|
-
const home =
|
|
3646
|
+
const home = homedir7();
|
|
3539
3647
|
const dryRun = opts.dryRun ?? false;
|
|
3540
3648
|
let hubContent;
|
|
3541
3649
|
let subSkillContents;
|
|
@@ -3552,8 +3660,8 @@ function setupCommand() {
|
|
|
3552
3660
|
let anyFound = false;
|
|
3553
3661
|
const changedTools = /* @__PURE__ */ new Set();
|
|
3554
3662
|
for (const target of HUB_TARGETS) {
|
|
3555
|
-
const dirPath =
|
|
3556
|
-
const filePath =
|
|
3663
|
+
const dirPath = join10(home, target.dir);
|
|
3664
|
+
const filePath = join10(home, target.file);
|
|
3557
3665
|
if (!await exists(dirPath)) {
|
|
3558
3666
|
if (!opts.silent) {
|
|
3559
3667
|
printHuman(c.gray(` - ${target.tool.padEnd(12)} not installed, skipped`));
|
|
@@ -3580,10 +3688,10 @@ function setupCommand() {
|
|
|
3580
3688
|
}
|
|
3581
3689
|
}
|
|
3582
3690
|
for (const target of SKILL_TARGETS) {
|
|
3583
|
-
const dirPath =
|
|
3691
|
+
const dirPath = join10(home, target.dir);
|
|
3584
3692
|
if (!await exists(dirPath)) continue;
|
|
3585
3693
|
for (const subName of SUB_SKILLS) {
|
|
3586
|
-
const filePath =
|
|
3694
|
+
const filePath = join10(home, target.skillsDir, subName, "SKILL.md");
|
|
3587
3695
|
const dest = `~/${target.skillsDir}/${subName}/SKILL.md`;
|
|
3588
3696
|
try {
|
|
3589
3697
|
const result = await copyBundledSkill(filePath, subSkillContents[subName], dryRun);
|
|
@@ -3666,11 +3774,7 @@ function commentCommand() {
|
|
|
3666
3774
|
throw new Error('full_slug must be "{username}/{slug}"');
|
|
3667
3775
|
}
|
|
3668
3776
|
const bundle = await useClient();
|
|
3669
|
-
|
|
3670
|
-
printHuman(c.yellow("Not logged in. Run `kordis login`."));
|
|
3671
|
-
process.exitCode = 10;
|
|
3672
|
-
return;
|
|
3673
|
-
}
|
|
3777
|
+
requireAuth(bundle);
|
|
3674
3778
|
const res = await bundle.authedRequest(
|
|
3675
3779
|
(api) => api.createComment(username, slug, {
|
|
3676
3780
|
kind: opts.kind,
|
|
@@ -3701,11 +3805,7 @@ function starCommand() {
|
|
|
3701
3805
|
throw new Error('full_slug must be "{username}/{slug}"');
|
|
3702
3806
|
}
|
|
3703
3807
|
const bundle = await useClient();
|
|
3704
|
-
|
|
3705
|
-
printHuman(c.yellow("Not logged in. Run `kordis login`."));
|
|
3706
|
-
process.exitCode = 10;
|
|
3707
|
-
return;
|
|
3708
|
-
}
|
|
3808
|
+
requireAuth(bundle);
|
|
3709
3809
|
const res = await bundle.authedRequest((api) => api.star(username, slug));
|
|
3710
3810
|
printDual({
|
|
3711
3811
|
json: res,
|
|
@@ -3723,11 +3823,7 @@ function unstarCommand() {
|
|
|
3723
3823
|
throw new Error('full_slug must be "{username}/{slug}"');
|
|
3724
3824
|
}
|
|
3725
3825
|
const bundle = await useClient();
|
|
3726
|
-
|
|
3727
|
-
printHuman(c.yellow("Not logged in. Run `kordis login`."));
|
|
3728
|
-
process.exitCode = 10;
|
|
3729
|
-
return;
|
|
3730
|
-
}
|
|
3826
|
+
requireAuth(bundle);
|
|
3731
3827
|
const res = await bundle.authedRequest(
|
|
3732
3828
|
(api) => api.unstar(username, slug)
|
|
3733
3829
|
);
|
|
@@ -3786,9 +3882,7 @@ function listCommand() {
|
|
|
3786
3882
|
return new Command13("list").description("list your own packages (requires login)").option("--limit <n>", "max results per page", (v) => parseInt(v, 10), 20).option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10), 1).option("--sort <mode>", "sort: latest|popular", "latest").action(async (opts) => {
|
|
3787
3883
|
const sort = opts.sort === "popular" ? "popular" : "latest";
|
|
3788
3884
|
const bundle = await useClient();
|
|
3789
|
-
|
|
3790
|
-
throw new Error("Not logged in. Run `kordis login` first.");
|
|
3791
|
-
}
|
|
3885
|
+
requireAuth(bundle);
|
|
3792
3886
|
const spin = spinner("Fetching your packages");
|
|
3793
3887
|
const res = await bundle.authedRequest(
|
|
3794
3888
|
(api) => api.listMyPackages({ page: opts.page, limit: opts.limit, sort })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rayul/kordis",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Command-line client for the Kordis registry — discover, install, and publish agentic-coding packages.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"build": "tsup && rm -rf assets && mkdir -p assets && cp -R ../../packages/skills/. assets/",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
23
|
"test": "vitest run",
|
|
24
|
-
"postinstall": "node scripts/postinstall.mjs"
|
|
24
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
25
|
+
"prepublishOnly": "pnpm build"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"@iarna/toml": "^2.2.5",
|