@rayul/kordis 0.5.0 → 0.6.1
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 +175 -78
- 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;
|
|
@@ -1706,18 +1803,18 @@ function whoamiCommand() {
|
|
|
1706
1803
|
import { Command as Command3 } from "commander";
|
|
1707
1804
|
|
|
1708
1805
|
// src/lib/recommendation-cache.ts
|
|
1709
|
-
import { readFile as
|
|
1710
|
-
import { homedir as
|
|
1711
|
-
import { join as
|
|
1806
|
+
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1807
|
+
import { homedir as homedir5 } from "os";
|
|
1808
|
+
import { join as join6 } from "path";
|
|
1712
1809
|
function cacheDir() {
|
|
1713
|
-
return process.env["KORDIS_HOME"] ??
|
|
1810
|
+
return process.env["KORDIS_HOME"] ?? join6(homedir5(), ".kordis");
|
|
1714
1811
|
}
|
|
1715
1812
|
function cachePath() {
|
|
1716
|
-
return
|
|
1813
|
+
return join6(cacheDir(), "recommendations.json");
|
|
1717
1814
|
}
|
|
1718
1815
|
async function readCache() {
|
|
1719
1816
|
try {
|
|
1720
|
-
const raw = await
|
|
1817
|
+
const raw = await readFile5(cachePath(), "utf8");
|
|
1721
1818
|
const parsed = JSON.parse(raw);
|
|
1722
1819
|
if (!parsed.generated_at || !Array.isArray(parsed.items)) return null;
|
|
1723
1820
|
return parsed;
|
|
@@ -1726,23 +1823,23 @@ async function readCache() {
|
|
|
1726
1823
|
}
|
|
1727
1824
|
}
|
|
1728
1825
|
async function writeCache(items, generatedAt) {
|
|
1729
|
-
await
|
|
1826
|
+
await mkdir4(cacheDir(), { recursive: true });
|
|
1730
1827
|
const payload = {
|
|
1731
1828
|
generated_at: generatedAt,
|
|
1732
1829
|
items,
|
|
1733
1830
|
seen_at: null
|
|
1734
1831
|
};
|
|
1735
|
-
await
|
|
1832
|
+
await writeFile4(cachePath(), JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
1736
1833
|
}
|
|
1737
1834
|
async function markSeen() {
|
|
1738
1835
|
const existing = await readCache();
|
|
1739
1836
|
if (!existing) return;
|
|
1740
|
-
await
|
|
1837
|
+
await mkdir4(cacheDir(), { recursive: true });
|
|
1741
1838
|
const updated = {
|
|
1742
1839
|
...existing,
|
|
1743
1840
|
seen_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1744
1841
|
};
|
|
1745
|
-
await
|
|
1842
|
+
await writeFile4(cachePath(), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
1746
1843
|
}
|
|
1747
1844
|
|
|
1748
1845
|
// src/lib/recommendation-footer.ts
|
|
@@ -1869,8 +1966,8 @@ function searchCommand() {
|
|
|
1869
1966
|
|
|
1870
1967
|
// src/commands/push.ts
|
|
1871
1968
|
import { Command as Command4 } from "commander";
|
|
1872
|
-
import { readFile as
|
|
1873
|
-
import { resolve, join as
|
|
1969
|
+
import { readFile as readFile6, stat as stat2, readdir as readdir2 } from "fs/promises";
|
|
1970
|
+
import { resolve, join as join7, posix, sep } from "path";
|
|
1874
1971
|
import JSZip from "jszip";
|
|
1875
1972
|
|
|
1876
1973
|
// ../../packages/harness-schema/src/parser.ts
|
|
@@ -2435,9 +2532,9 @@ function pushCommand() {
|
|
|
2435
2532
|
"--slug <slug>",
|
|
2436
2533
|
'override slug; accepts "user/slug" to point at a specific package'
|
|
2437
2534
|
).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 (
|
|
2535
|
+
async (path2, opts) => {
|
|
2439
2536
|
const bundle = await useClient();
|
|
2440
|
-
const abs = resolve(process.cwd(),
|
|
2537
|
+
const abs = resolve(process.cwd(), path2);
|
|
2441
2538
|
const s = await stat2(abs);
|
|
2442
2539
|
let content;
|
|
2443
2540
|
let assets = [];
|
|
@@ -2448,7 +2545,7 @@ function pushCommand() {
|
|
|
2448
2545
|
({ content, assets } = await bundleSkillZip(abs));
|
|
2449
2546
|
content = ensureTypeField(content, "skill");
|
|
2450
2547
|
} else {
|
|
2451
|
-
content = await
|
|
2548
|
+
content = await readFile6(abs, "utf8");
|
|
2452
2549
|
}
|
|
2453
2550
|
let nameFromFm = null;
|
|
2454
2551
|
try {
|
|
@@ -2629,19 +2726,19 @@ function shouldExclude(relPath, isDir) {
|
|
|
2629
2726
|
return false;
|
|
2630
2727
|
}
|
|
2631
2728
|
async function walkDir(base, relDir, collected) {
|
|
2632
|
-
const entries = await readdir2(
|
|
2729
|
+
const entries = await readdir2(join7(base, relDir), { withFileTypes: true });
|
|
2633
2730
|
for (const entry of entries) {
|
|
2634
2731
|
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
2635
2732
|
if (shouldExclude(rel, entry.isDirectory())) continue;
|
|
2636
2733
|
if (entry.isDirectory()) {
|
|
2637
2734
|
await walkDir(base, rel, collected);
|
|
2638
2735
|
} else if (entry.isFile()) {
|
|
2639
|
-
collected.push({ rel, abs:
|
|
2736
|
+
collected.push({ rel, abs: join7(base, rel) });
|
|
2640
2737
|
}
|
|
2641
2738
|
}
|
|
2642
2739
|
}
|
|
2643
2740
|
async function readAsset(absPath) {
|
|
2644
|
-
const buf = await
|
|
2741
|
+
const buf = await readFile6(absPath);
|
|
2645
2742
|
const text = buf.toString("utf8");
|
|
2646
2743
|
const roundtrip = Buffer.from(text, "utf8");
|
|
2647
2744
|
if (roundtrip.equals(buf)) {
|
|
@@ -2651,14 +2748,14 @@ async function readAsset(absPath) {
|
|
|
2651
2748
|
printHuman(c.gray(` \u2192 encoding ${absPath.split(sep).pop()} as base64`));
|
|
2652
2749
|
return { content: b64, encoding: "base64" };
|
|
2653
2750
|
}
|
|
2654
|
-
async function bundleDirectory(
|
|
2751
|
+
async function bundleDirectory(dir2) {
|
|
2655
2752
|
const all = [];
|
|
2656
|
-
await walkDir(
|
|
2753
|
+
await walkDir(dir2, "", all);
|
|
2657
2754
|
const skillEntry = all.find((f) => f.rel === "SKILL.md");
|
|
2658
2755
|
if (!skillEntry) {
|
|
2659
|
-
throw new Error(`No SKILL.md found in directory ${
|
|
2756
|
+
throw new Error(`No SKILL.md found in directory ${dir2}. Push a directory that has SKILL.md at its root.`);
|
|
2660
2757
|
}
|
|
2661
|
-
const content = await
|
|
2758
|
+
const content = await readFile6(skillEntry.abs, "utf8");
|
|
2662
2759
|
const assets = [];
|
|
2663
2760
|
for (const f of all) {
|
|
2664
2761
|
if (f.rel === "SKILL.md") continue;
|
|
@@ -2668,7 +2765,7 @@ async function bundleDirectory(dir) {
|
|
|
2668
2765
|
return { content, assets };
|
|
2669
2766
|
}
|
|
2670
2767
|
async function bundleSkillZip(zipPath) {
|
|
2671
|
-
const buf = await
|
|
2768
|
+
const buf = await readFile6(zipPath);
|
|
2672
2769
|
const zip = await JSZip.loadAsync(buf);
|
|
2673
2770
|
const entries = Object.keys(zip.files).filter((k) => !zip.files[k].dir);
|
|
2674
2771
|
if (entries.length === 0) throw new Error(".skill file is empty");
|
|
@@ -2702,8 +2799,8 @@ async function bundleSkillZip(zipPath) {
|
|
|
2702
2799
|
|
|
2703
2800
|
// src/commands/pull.ts
|
|
2704
2801
|
import { Command as Command5 } from "commander";
|
|
2705
|
-
import { mkdir as
|
|
2706
|
-
import { dirname as dirname2, join as
|
|
2802
|
+
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
2803
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
2707
2804
|
|
|
2708
2805
|
// ../../packages/translators/src/skill/identity.ts
|
|
2709
2806
|
function buildSkillFile(input, outputPath) {
|
|
@@ -3145,21 +3242,21 @@ function getTranslator(type, format) {
|
|
|
3145
3242
|
}
|
|
3146
3243
|
|
|
3147
3244
|
// src/lockfile.ts
|
|
3148
|
-
import { homedir as
|
|
3149
|
-
import { join as
|
|
3150
|
-
import { readFile as
|
|
3151
|
-
var LOCKFILE_PATH =
|
|
3245
|
+
import { homedir as homedir6 } from "os";
|
|
3246
|
+
import { join as join8 } from "path";
|
|
3247
|
+
import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
3248
|
+
var LOCKFILE_PATH = join8(homedir6(), ".kordis", "installed.json");
|
|
3152
3249
|
async function readLockfile() {
|
|
3153
3250
|
try {
|
|
3154
|
-
const raw = await
|
|
3251
|
+
const raw = await readFile7(LOCKFILE_PATH, "utf8");
|
|
3155
3252
|
return JSON.parse(raw);
|
|
3156
3253
|
} catch {
|
|
3157
3254
|
return { packages: {} };
|
|
3158
3255
|
}
|
|
3159
3256
|
}
|
|
3160
3257
|
async function writeLockfile(lf) {
|
|
3161
|
-
await
|
|
3162
|
-
await
|
|
3258
|
+
await mkdir5(join8(homedir6(), ".kordis"), { recursive: true });
|
|
3259
|
+
await writeFile5(LOCKFILE_PATH, JSON.stringify(lf, null, 2) + "\n", "utf8");
|
|
3163
3260
|
}
|
|
3164
3261
|
async function recordInstalled(fullSlug, entry) {
|
|
3165
3262
|
const lf = await readLockfile();
|
|
@@ -3195,8 +3292,8 @@ async function doPullInstall(fullSlug, tool, installRoot, version) {
|
|
|
3195
3292
|
assets: contentRes.assets
|
|
3196
3293
|
});
|
|
3197
3294
|
for (const f of result.files) {
|
|
3198
|
-
const abs =
|
|
3199
|
-
await
|
|
3295
|
+
const abs = join9(installRoot, f.path);
|
|
3296
|
+
await mkdir6(dirname2(abs), { recursive: true });
|
|
3200
3297
|
await writeEmittedFile(abs, f.content, f.encoding);
|
|
3201
3298
|
written.push(abs);
|
|
3202
3299
|
}
|
|
@@ -3215,8 +3312,8 @@ async function doPullInstall(fullSlug, tool, installRoot, version) {
|
|
|
3215
3312
|
`Tool "${format}" is not supported for type "${parsed.type}"; installed canonical form instead.`
|
|
3216
3313
|
);
|
|
3217
3314
|
for (const f of result.files) {
|
|
3218
|
-
const abs =
|
|
3219
|
-
await
|
|
3315
|
+
const abs = join9(installRoot, f.path);
|
|
3316
|
+
await mkdir6(dirname2(abs), { recursive: true });
|
|
3220
3317
|
await writeEmittedFile(abs, f.content, f.encoding);
|
|
3221
3318
|
written.push(abs);
|
|
3222
3319
|
}
|
|
@@ -3293,9 +3390,9 @@ async function detectTool(root) {
|
|
|
3293
3390
|
[".windsurfrules", "windsurf"],
|
|
3294
3391
|
[".github/copilot-instructions.md", "copilot"]
|
|
3295
3392
|
];
|
|
3296
|
-
for (const [
|
|
3393
|
+
for (const [path2, tool] of checks) {
|
|
3297
3394
|
try {
|
|
3298
|
-
await stat3(
|
|
3395
|
+
await stat3(join9(root, path2));
|
|
3299
3396
|
return tool;
|
|
3300
3397
|
} catch {
|
|
3301
3398
|
}
|
|
@@ -3304,9 +3401,9 @@ async function detectTool(root) {
|
|
|
3304
3401
|
}
|
|
3305
3402
|
async function writeEmittedFile(abs, content, encoding) {
|
|
3306
3403
|
if (encoding === "base64") {
|
|
3307
|
-
await
|
|
3404
|
+
await writeFile6(abs, Buffer.from(content, "base64"));
|
|
3308
3405
|
} else {
|
|
3309
|
-
await
|
|
3406
|
+
await writeFile6(abs, content, "utf8");
|
|
3310
3407
|
}
|
|
3311
3408
|
}
|
|
3312
3409
|
|
|
@@ -3340,8 +3437,8 @@ function readCommand() {
|
|
|
3340
3437
|
`${c.gray("\u2014")} ${c.bold(`\u24D8 ${bundled.length} bundled asset(s)`)} \u2014 fetch with ${c.cyan(`kordis pull ${res.full_slug}`)}:
|
|
3341
3438
|
`
|
|
3342
3439
|
);
|
|
3343
|
-
for (const
|
|
3344
|
-
process.stderr.write(` ${c.gray("-")} ${
|
|
3440
|
+
for (const path2 of bundled) {
|
|
3441
|
+
process.stderr.write(` ${c.gray("-")} ${path2}
|
|
3345
3442
|
`);
|
|
3346
3443
|
}
|
|
3347
3444
|
}
|
|
@@ -3427,9 +3524,9 @@ function logoutCommand() {
|
|
|
3427
3524
|
}
|
|
3428
3525
|
|
|
3429
3526
|
// src/commands/setup.ts
|
|
3430
|
-
import { readFile as
|
|
3431
|
-
import { homedir as
|
|
3432
|
-
import { join as
|
|
3527
|
+
import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, access as access3 } from "fs/promises";
|
|
3528
|
+
import { homedir as homedir7 } from "os";
|
|
3529
|
+
import { join as join10, dirname as dirname3 } from "path";
|
|
3433
3530
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3434
3531
|
import { Command as Command9 } from "commander";
|
|
3435
3532
|
import prompts from "prompts";
|
|
@@ -3452,9 +3549,9 @@ var SKILL_TARGETS = [
|
|
|
3452
3549
|
{ tool: "Codex CLI", dir: ".codex", skillsDir: ".codex/skills" },
|
|
3453
3550
|
{ tool: "Gemini CLI", dir: ".gemini", skillsDir: ".gemini/skills" }
|
|
3454
3551
|
];
|
|
3455
|
-
async function exists(
|
|
3552
|
+
async function exists(path2) {
|
|
3456
3553
|
try {
|
|
3457
|
-
await access3(
|
|
3554
|
+
await access3(path2);
|
|
3458
3555
|
return true;
|
|
3459
3556
|
} catch {
|
|
3460
3557
|
return false;
|
|
@@ -3464,18 +3561,18 @@ function assetRoots() {
|
|
|
3464
3561
|
const __dir2 = dirname3(fileURLToPath2(import.meta.url));
|
|
3465
3562
|
return {
|
|
3466
3563
|
// Built binary: bin/kordis.js → assets/ is one level up
|
|
3467
|
-
bundled:
|
|
3564
|
+
bundled: join10(__dir2, "..", "assets"),
|
|
3468
3565
|
// Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
|
|
3469
|
-
source:
|
|
3566
|
+
source: join10(__dir2, "..", "..", "..", "..", "packages", "skills")
|
|
3470
3567
|
};
|
|
3471
3568
|
}
|
|
3472
3569
|
async function pickAssetRoot() {
|
|
3473
3570
|
const { bundled, source } = assetRoots();
|
|
3474
|
-
return await exists(
|
|
3571
|
+
return await exists(join10(bundled, "KORDIS.md")) ? bundled : source;
|
|
3475
3572
|
}
|
|
3476
3573
|
async function loadHubContent() {
|
|
3477
3574
|
const root = await pickAssetRoot();
|
|
3478
|
-
const raw = await
|
|
3575
|
+
const raw = await readFile8(join10(root, "KORDIS.md"), "utf-8");
|
|
3479
3576
|
if (raw.startsWith("---")) {
|
|
3480
3577
|
const end = raw.indexOf("\n---", 4);
|
|
3481
3578
|
if (end !== -1) return raw.slice(end + 4).trimStart();
|
|
@@ -3484,7 +3581,7 @@ async function loadHubContent() {
|
|
|
3484
3581
|
}
|
|
3485
3582
|
async function loadSubSkillContent(name) {
|
|
3486
3583
|
const root = await pickAssetRoot();
|
|
3487
|
-
return
|
|
3584
|
+
return readFile8(join10(root, name, "SKILL.md"), "utf-8");
|
|
3488
3585
|
}
|
|
3489
3586
|
async function injectBlock(filePath, content, dryRun) {
|
|
3490
3587
|
const block = `${MARKER_START}
|
|
@@ -3493,14 +3590,14 @@ ${MARKER_END}`;
|
|
|
3493
3590
|
let existing = "";
|
|
3494
3591
|
let fileExists = true;
|
|
3495
3592
|
try {
|
|
3496
|
-
existing = await
|
|
3593
|
+
existing = await readFile8(filePath, "utf-8");
|
|
3497
3594
|
} catch {
|
|
3498
3595
|
fileExists = false;
|
|
3499
3596
|
}
|
|
3500
3597
|
if (!fileExists) {
|
|
3501
3598
|
if (!dryRun) {
|
|
3502
|
-
await
|
|
3503
|
-
await
|
|
3599
|
+
await mkdir7(dirname3(filePath), { recursive: true });
|
|
3600
|
+
await writeFile7(filePath, block + "\n", "utf-8");
|
|
3504
3601
|
}
|
|
3505
3602
|
return "injected";
|
|
3506
3603
|
}
|
|
@@ -3511,31 +3608,31 @@ ${MARKER_END}`;
|
|
|
3511
3608
|
if (current === block) return "unchanged";
|
|
3512
3609
|
if (!dryRun) {
|
|
3513
3610
|
const updated = existing.slice(0, startIdx) + block + existing.slice(endIdx + MARKER_END.length);
|
|
3514
|
-
await
|
|
3611
|
+
await writeFile7(filePath, updated, "utf-8");
|
|
3515
3612
|
}
|
|
3516
3613
|
return "updated";
|
|
3517
3614
|
}
|
|
3518
3615
|
if (!dryRun) {
|
|
3519
|
-
await
|
|
3616
|
+
await writeFile7(filePath, existing.trimEnd() + "\n\n" + block + "\n", "utf-8");
|
|
3520
3617
|
}
|
|
3521
3618
|
return "injected";
|
|
3522
3619
|
}
|
|
3523
3620
|
async function copyBundledSkill(filePath, content, dryRun) {
|
|
3524
3621
|
let existing = null;
|
|
3525
3622
|
try {
|
|
3526
|
-
existing = await
|
|
3623
|
+
existing = await readFile8(filePath, "utf-8");
|
|
3527
3624
|
} catch {
|
|
3528
3625
|
}
|
|
3529
3626
|
if (existing === content) return "unchanged";
|
|
3530
3627
|
if (!dryRun) {
|
|
3531
|
-
await
|
|
3532
|
-
await
|
|
3628
|
+
await mkdir7(dirname3(filePath), { recursive: true });
|
|
3629
|
+
await writeFile7(filePath, content, "utf-8");
|
|
3533
3630
|
}
|
|
3534
3631
|
return existing === null ? "injected" : "updated";
|
|
3535
3632
|
}
|
|
3536
3633
|
function setupCommand() {
|
|
3537
3634
|
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 =
|
|
3635
|
+
const home = homedir7();
|
|
3539
3636
|
const dryRun = opts.dryRun ?? false;
|
|
3540
3637
|
let hubContent;
|
|
3541
3638
|
let subSkillContents;
|
|
@@ -3552,8 +3649,8 @@ function setupCommand() {
|
|
|
3552
3649
|
let anyFound = false;
|
|
3553
3650
|
const changedTools = /* @__PURE__ */ new Set();
|
|
3554
3651
|
for (const target of HUB_TARGETS) {
|
|
3555
|
-
const dirPath =
|
|
3556
|
-
const filePath =
|
|
3652
|
+
const dirPath = join10(home, target.dir);
|
|
3653
|
+
const filePath = join10(home, target.file);
|
|
3557
3654
|
if (!await exists(dirPath)) {
|
|
3558
3655
|
if (!opts.silent) {
|
|
3559
3656
|
printHuman(c.gray(` - ${target.tool.padEnd(12)} not installed, skipped`));
|
|
@@ -3580,10 +3677,10 @@ function setupCommand() {
|
|
|
3580
3677
|
}
|
|
3581
3678
|
}
|
|
3582
3679
|
for (const target of SKILL_TARGETS) {
|
|
3583
|
-
const dirPath =
|
|
3680
|
+
const dirPath = join10(home, target.dir);
|
|
3584
3681
|
if (!await exists(dirPath)) continue;
|
|
3585
3682
|
for (const subName of SUB_SKILLS) {
|
|
3586
|
-
const filePath =
|
|
3683
|
+
const filePath = join10(home, target.skillsDir, subName, "SKILL.md");
|
|
3587
3684
|
const dest = `~/${target.skillsDir}/${subName}/SKILL.md`;
|
|
3588
3685
|
try {
|
|
3589
3686
|
const result = await copyBundledSkill(filePath, subSkillContents[subName], dryRun);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rayul/kordis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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",
|