@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.
Files changed (2) hide show
  1. package/dist/index.js +195 -101
  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;
@@ -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
- if (!bundle.username) {
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 readFile4, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1710
- import { homedir as homedir4 } from "os";
1711
- import { join as join5 } from "path";
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"] ?? join5(homedir4(), ".kordis");
1821
+ return process.env["KORDIS_HOME"] ?? join6(homedir5(), ".kordis");
1714
1822
  }
1715
1823
  function cachePath() {
1716
- return join5(cacheDir(), "recommendations.json");
1824
+ return join6(cacheDir(), "recommendations.json");
1717
1825
  }
1718
1826
  async function readCache() {
1719
1827
  try {
1720
- const raw = await readFile4(cachePath(), "utf8");
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 mkdir3(cacheDir(), { recursive: true });
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 writeFile3(cachePath(), JSON.stringify(payload, null, 2) + "\n", "utf8");
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 mkdir3(cacheDir(), { recursive: true });
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 writeFile3(cachePath(), JSON.stringify(updated, null, 2) + "\n", "utf8");
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 readFile5, stat as stat2, readdir as readdir2 } from "fs/promises";
1873
- import { resolve, join as join6, posix, sep } from "path";
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 (path, opts) => {
2546
+ async (path2, opts) => {
2439
2547
  const bundle = await useClient();
2440
- const abs = resolve(process.cwd(), path);
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 readFile5(abs, "utf8");
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(join6(base, relDir), { withFileTypes: true });
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: join6(base, rel) });
2747
+ collected.push({ rel, abs: join7(base, rel) });
2640
2748
  }
2641
2749
  }
2642
2750
  }
2643
2751
  async function readAsset(absPath) {
2644
- const buf = await readFile5(absPath);
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(dir) {
2762
+ async function bundleDirectory(dir2) {
2655
2763
  const all = [];
2656
- await walkDir(dir, "", all);
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 ${dir}. Push a directory that has SKILL.md at its root.`);
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 readFile5(skillEntry.abs, "utf8");
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 readFile5(zipPath);
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 mkdir5, writeFile as writeFile5 } from "fs/promises";
2706
- import { dirname as dirname2, join as join8 } from "path";
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 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");
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 readFile6(LOCKFILE_PATH, "utf8");
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 mkdir4(join7(homedir5(), ".kordis"), { recursive: true });
3162
- await writeFile4(LOCKFILE_PATH, JSON.stringify(lf, null, 2) + "\n", "utf8");
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 = join8(installRoot, f.path);
3199
- await mkdir5(dirname2(abs), { recursive: true });
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 = join8(installRoot, f.path);
3219
- await mkdir5(dirname2(abs), { recursive: true });
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 [path, tool] of checks) {
3404
+ for (const [path2, tool] of checks) {
3297
3405
  try {
3298
- await stat3(join8(root, path));
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 writeFile5(abs, Buffer.from(content, "base64"));
3415
+ await writeFile6(abs, Buffer.from(content, "base64"));
3308
3416
  } else {
3309
- await writeFile5(abs, content, "utf8");
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 path of bundled) {
3344
- process.stderr.write(` ${c.gray("-")} ${path}
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 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";
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(path) {
3563
+ async function exists(path2) {
3456
3564
  try {
3457
- await access3(path);
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: join9(__dir2, "..", "assets"),
3575
+ bundled: join10(__dir2, "..", "assets"),
3468
3576
  // Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
3469
- source: join9(__dir2, "..", "..", "..", "..", "packages", "skills")
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(join9(bundled, "KORDIS.md")) ? bundled : source;
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 readFile7(join9(root, "KORDIS.md"), "utf-8");
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 readFile7(join9(root, name, "SKILL.md"), "utf-8");
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 readFile7(filePath, "utf-8");
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 mkdir6(dirname3(filePath), { recursive: true });
3503
- await writeFile6(filePath, block + "\n", "utf-8");
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 writeFile6(filePath, updated, "utf-8");
3622
+ await writeFile7(filePath, updated, "utf-8");
3515
3623
  }
3516
3624
  return "updated";
3517
3625
  }
3518
3626
  if (!dryRun) {
3519
- await writeFile6(filePath, existing.trimEnd() + "\n\n" + block + "\n", "utf-8");
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 readFile7(filePath, "utf-8");
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 mkdir6(dirname3(filePath), { recursive: true });
3532
- await writeFile6(filePath, content, "utf-8");
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 = homedir6();
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 = join9(home, target.dir);
3556
- const filePath = join9(home, target.file);
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 = join9(home, target.dir);
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 = join9(home, target.skillsDir, subName, "SKILL.md");
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
- if (!bundle.username) {
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
- if (!bundle.username) {
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
- if (!bundle.username) {
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
- if (!bundle.username) {
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.0",
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",