@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.
Files changed (2) hide show
  1. package/dist/index.js +175 -78
  2. 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(path, opts = {}) {
908
- const url = new URL(this.baseUrl + path);
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(dir, depth, counts) {
1370
+ async function walk(dir2, depth, counts) {
1371
1371
  if (depth < 0) return;
1372
1372
  let entries;
1373
1373
  try {
1374
- entries = await readdir(dir, { withFileTypes: true });
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(dir, e.name);
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 join4 } from "path";
1549
+ import { dirname, join as join5 } from "path";
1516
1550
  var __dir = dirname(fileURLToPath(import.meta.url));
1517
- var pkgPath = join4(__dir, "..", "package.json");
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
- const cfg = await loadCliConfig();
1564
+ let cfg = await loadCliConfig();
1531
1565
  const keychain = await createKeychain();
1532
- const username = cfg.username ?? null;
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").action(async (opts) => {
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 device = await bundle.api.startDeviceFlow(
1615
- opts.provider ?? "discord"
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 readFile4, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1710
- import { homedir as homedir4 } from "os";
1711
- import { join as join5 } from "path";
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"] ?? join5(homedir4(), ".kordis");
1810
+ return process.env["KORDIS_HOME"] ?? join6(homedir5(), ".kordis");
1714
1811
  }
1715
1812
  function cachePath() {
1716
- return join5(cacheDir(), "recommendations.json");
1813
+ return join6(cacheDir(), "recommendations.json");
1717
1814
  }
1718
1815
  async function readCache() {
1719
1816
  try {
1720
- const raw = await readFile4(cachePath(), "utf8");
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 mkdir3(cacheDir(), { recursive: true });
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 writeFile3(cachePath(), JSON.stringify(payload, null, 2) + "\n", "utf8");
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 mkdir3(cacheDir(), { recursive: true });
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 writeFile3(cachePath(), JSON.stringify(updated, null, 2) + "\n", "utf8");
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 readFile5, stat as stat2, readdir as readdir2 } from "fs/promises";
1873
- import { resolve, join as join6, posix, sep } from "path";
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 (path, opts) => {
2535
+ async (path2, opts) => {
2439
2536
  const bundle = await useClient();
2440
- const abs = resolve(process.cwd(), path);
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 readFile5(abs, "utf8");
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(join6(base, relDir), { withFileTypes: true });
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: join6(base, rel) });
2736
+ collected.push({ rel, abs: join7(base, rel) });
2640
2737
  }
2641
2738
  }
2642
2739
  }
2643
2740
  async function readAsset(absPath) {
2644
- const buf = await readFile5(absPath);
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(dir) {
2751
+ async function bundleDirectory(dir2) {
2655
2752
  const all = [];
2656
- await walkDir(dir, "", all);
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 ${dir}. Push a directory that has SKILL.md at its root.`);
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 readFile5(skillEntry.abs, "utf8");
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 readFile5(zipPath);
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 mkdir5, writeFile as writeFile5 } from "fs/promises";
2706
- import { dirname as dirname2, join as join8 } from "path";
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 homedir5 } from "os";
3149
- import { join as join7 } from "path";
3150
- import { readFile as readFile6, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
3151
- var LOCKFILE_PATH = join7(homedir5(), ".kordis", "installed.json");
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 readFile6(LOCKFILE_PATH, "utf8");
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 mkdir4(join7(homedir5(), ".kordis"), { recursive: true });
3162
- await writeFile4(LOCKFILE_PATH, JSON.stringify(lf, null, 2) + "\n", "utf8");
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 = join8(installRoot, f.path);
3199
- await mkdir5(dirname2(abs), { recursive: true });
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 = join8(installRoot, f.path);
3219
- await mkdir5(dirname2(abs), { recursive: true });
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 [path, tool] of checks) {
3393
+ for (const [path2, tool] of checks) {
3297
3394
  try {
3298
- await stat3(join8(root, path));
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 writeFile5(abs, Buffer.from(content, "base64"));
3404
+ await writeFile6(abs, Buffer.from(content, "base64"));
3308
3405
  } else {
3309
- await writeFile5(abs, content, "utf8");
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 path of bundled) {
3344
- process.stderr.write(` ${c.gray("-")} ${path}
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 readFile7, writeFile as writeFile6, mkdir as mkdir6, access as access3 } from "fs/promises";
3431
- import { homedir as homedir6 } from "os";
3432
- import { join as join9, dirname as dirname3 } from "path";
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(path) {
3552
+ async function exists(path2) {
3456
3553
  try {
3457
- await access3(path);
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: join9(__dir2, "..", "assets"),
3564
+ bundled: join10(__dir2, "..", "assets"),
3468
3565
  // Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
3469
- source: join9(__dir2, "..", "..", "..", "..", "packages", "skills")
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(join9(bundled, "KORDIS.md")) ? bundled : source;
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 readFile7(join9(root, "KORDIS.md"), "utf-8");
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 readFile7(join9(root, name, "SKILL.md"), "utf-8");
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 readFile7(filePath, "utf-8");
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 mkdir6(dirname3(filePath), { recursive: true });
3503
- await writeFile6(filePath, block + "\n", "utf-8");
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 writeFile6(filePath, updated, "utf-8");
3611
+ await writeFile7(filePath, updated, "utf-8");
3515
3612
  }
3516
3613
  return "updated";
3517
3614
  }
3518
3615
  if (!dryRun) {
3519
- await writeFile6(filePath, existing.trimEnd() + "\n\n" + block + "\n", "utf-8");
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 readFile7(filePath, "utf-8");
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 mkdir6(dirname3(filePath), { recursive: true });
3532
- await writeFile6(filePath, content, "utf-8");
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 = homedir6();
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 = join9(home, target.dir);
3556
- const filePath = join9(home, target.file);
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 = join9(home, target.dir);
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 = join9(home, target.skillsDir, subName, "SKILL.md");
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.5.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",