@rayul/kordis 0.1.10 → 0.1.11

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.
@@ -100,12 +100,16 @@ auto-resolve silently.
100
100
  `"following <full_slug>@<version>"`). RECOMMENDED: end your reply with that
101
101
  exact line. Skip only when it would clutter a casual non-technical reply.
102
102
 
103
- ## Bundled assets caveat
104
- A skill body may reference helper files (e.g. `references/<file>.md`,
105
- `scripts/<file>.sh`). Today the registry stores only the canonical
106
- SKILL.md text — bundled side-files are NOT shipped. If the body refers to
107
- one and you need it, follow the package's `homepage` to the upstream
108
- source instead of fabricating the asset.
103
+ ## Bundled assets
104
+ Skills published as a `.skill` bundle or directory include `assets/`,
105
+ `references/`, `scripts/`, and `agents/` subdirectories alongside
106
+ `SKILL.md`. After `kordis pull`, these files land next to `SKILL.md` in
107
+ the tool's skill directory (e.g. `.claude/skills/<name>/assets/foo.html`)
108
+ and are directly readable by the agent.
109
+
110
+ If a skill body references a helper file and the file isn't present after
111
+ pull, the package was published as a single-file skill — follow its
112
+ `homepage` to the upstream source.
109
113
 
110
114
  ## Examples
111
115
 
package/dist/index.js CHANGED
@@ -459,7 +459,7 @@ var init_auth = __esm({
459
459
 
460
460
  // ../../packages/contracts/src/api.ts
461
461
  import { z as z9 } from "zod";
462
- var SearchQuerySchema, SearchResponseSchema, TrendingQuerySchema, RisingQuerySchema, StarResponseSchema, PublishPackageRequestSchema, PublishWaitQuerySchema, PublishPackageResponseSchema, PackagePathParamsSchema, ContentQuerySchema, PackageContentResponseSchema, JitReadRequestSchema, JitReadResponseSchema, PullPackageResponseSchema, ListVersionsResponseSchema, VersionDiffQuerySchema, ListCommentsQuerySchema, ListCommentsResponseSchema, UserProfileResponseSchema, UserPackagesQuerySchema;
462
+ var SearchQuerySchema, SearchResponseSchema, TrendingQuerySchema, RisingQuerySchema, StarResponseSchema, AssetSchema, PublishPackageRequestSchema, PublishWaitQuerySchema, PublishPackageResponseSchema, PackagePathParamsSchema, ContentQuerySchema, PackageContentResponseSchema, JitReadRequestSchema, JitReadResponseSchema, PullPackageResponseSchema, ListVersionsResponseSchema, VersionDiffQuerySchema, ListCommentsQuerySchema, ListCommentsResponseSchema, UserProfileResponseSchema, UserPackagesQuerySchema;
463
463
  var init_api = __esm({
464
464
  "../../packages/contracts/src/api.ts"() {
465
465
  "use strict";
@@ -498,6 +498,11 @@ var init_api = __esm({
498
498
  starred: z9.boolean(),
499
499
  star_count: z9.number().int().nonnegative()
500
500
  });
501
+ AssetSchema = z9.object({
502
+ path: z9.string().min(1).max(255),
503
+ content: z9.string(),
504
+ encoding: z9.enum(["utf8", "base64"]).default("utf8")
505
+ });
501
506
  PublishPackageRequestSchema = z9.object({
502
507
  /**
503
508
  * Raw source content — YAML frontmatter + markdown body. Server parses and
@@ -528,7 +533,12 @@ var init_api = __esm({
528
533
  /**
529
534
  * Skip draft validation (preview mode) — only allowed in dev builds.
530
535
  */
531
- dry_run: z9.boolean().optional()
536
+ dry_run: z9.boolean().optional(),
537
+ /**
538
+ * Bundled files beyond SKILL.md: assets, references, scripts, agents, etc.
539
+ * Max 50 entries; max 2 MB per file; max 5 MB total bundle.
540
+ */
541
+ assets: z9.array(AssetSchema).max(50).optional()
532
542
  });
533
543
  PublishWaitQuerySchema = z9.object({
534
544
  /**
@@ -574,8 +584,9 @@ var init_api = __esm({
574
584
  version: SemverSchema,
575
585
  format: z9.string(),
576
586
  content: z9.string(),
577
- notes: z9.array(z9.string()).default([])
587
+ notes: z9.array(z9.string()).default([]),
578
588
  // translator warnings, e.g. ".mdc → Claude: @filepath unsupported"
589
+ assets: z9.array(AssetSchema).default([])
579
590
  });
580
591
  JitReadRequestSchema = z9.object({
581
592
  /**
@@ -1004,6 +1015,7 @@ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink
1004
1015
  import { homedir as homedir2 } from "os";
1005
1016
  import { join as join2 } from "path";
1006
1017
  var SERVICE = "kordis";
1018
+ var plaintextWarningEmitted = false;
1007
1019
  async function loadKeytar() {
1008
1020
  try {
1009
1021
  const mod = await import("keytar");
@@ -1031,6 +1043,16 @@ async function createKeychain() {
1031
1043
  }
1032
1044
  };
1033
1045
  }
1046
+ if (!plaintextWarningEmitted && process.env["KORDIS_JSON"] !== "1") {
1047
+ plaintextWarningEmitted = true;
1048
+ const yellow = "\x1B[33m";
1049
+ const reset = "\x1B[0m";
1050
+ process.stderr.write(
1051
+ `${yellow}[kordis] WARNING: OS keychain unavailable; falling back to plaintext token storage at ~/.kordis/.tokens.json (mode 0600).
1052
+ [kordis] On macOS install Xcode Command Line Tools, on Linux install libsecret-1-dev to enable keytar.${reset}
1053
+ `
1054
+ );
1055
+ }
1034
1056
  const store = await readAll();
1035
1057
  return {
1036
1058
  async save(username, tokens) {
@@ -1321,8 +1343,9 @@ function searchCommand() {
1321
1343
 
1322
1344
  // src/commands/push.ts
1323
1345
  import { Command as Command4 } from "commander";
1324
- import { readFile as readFile3 } from "fs/promises";
1325
- import { resolve } from "path";
1346
+ import { readFile as readFile3, stat, readdir } from "fs/promises";
1347
+ import { resolve, join as join4, posix, sep } from "path";
1348
+ import JSZip from "jszip";
1326
1349
 
1327
1350
  // ../../packages/harness-schema/src/parser.ts
1328
1351
  import { parse as parseYaml } from "yaml";
@@ -1366,21 +1389,30 @@ var FrontmatterParseError = class extends Error {
1366
1389
 
1367
1390
  // src/commands/push.ts
1368
1391
  function pushCommand() {
1369
- return new Command4("push").description("publish a new package or a new version").argument("<file>", "path to the canonical package source").option("--bump <strategy>", "patch|minor|major (update only)", "patch").option(
1392
+ return new Command4("push").description("publish a new package or a new version").argument("<path>", "SKILL.md file, .skill ZIP, or skill directory").option("--bump <strategy>", "patch|minor|major (update only)", "patch").option(
1370
1393
  "--slug <slug>",
1371
1394
  'override slug; accepts "user/slug" to point at a specific package'
1372
1395
  ).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(
1373
- async (file, opts) => {
1396
+ async (path, opts) => {
1374
1397
  const bundle = await useClient();
1375
- const abs = resolve(process.cwd(), file);
1376
- const content = await readFile3(abs, "utf8");
1398
+ const abs = resolve(process.cwd(), path);
1399
+ const s = await stat(abs);
1400
+ let content;
1401
+ let assets = [];
1402
+ if (s.isDirectory()) {
1403
+ ({ content, assets } = await bundleDirectory(abs));
1404
+ } else if (abs.endsWith(".skill")) {
1405
+ ({ content, assets } = await bundleSkillZip(abs));
1406
+ } else {
1407
+ content = await readFile3(abs, "utf8");
1408
+ }
1377
1409
  let targetUser = null;
1378
1410
  let targetSlug = null;
1379
1411
  if (opts.slug && opts.slug.includes("/")) {
1380
- const [u, s] = opts.slug.split("/", 2);
1381
- if (u && s) {
1412
+ const [u, s2] = opts.slug.split("/", 2);
1413
+ if (u && s2) {
1382
1414
  targetUser = u;
1383
- targetSlug = s;
1415
+ targetSlug = s2;
1384
1416
  }
1385
1417
  } else {
1386
1418
  let nameFromFm = null;
@@ -1415,6 +1447,7 @@ function pushCommand() {
1415
1447
  (api) => api.publish(
1416
1448
  {
1417
1449
  content,
1450
+ assets: assets.length > 0 ? assets : void 0,
1418
1451
  bump: opts.bump,
1419
1452
  slug: opts.slug && !opts.slug.includes("/") ? opts.slug : void 0,
1420
1453
  target_full_slug: targetFullSlug,
@@ -1434,6 +1467,9 @@ function pushCommand() {
1434
1467
  printHuman(
1435
1468
  `${c.bold(res.full_slug)} ${c.gray(`v${res.version}`)} \u2192 ${res.status === "published" ? c.green("published") : res.status === "pending" ? c.yellow("pending review") : c.red("rejected")}`
1436
1469
  );
1470
+ if (assets.length > 0) {
1471
+ printHuman(c.gray(` + ${assets.length} bundled file(s)`));
1472
+ }
1437
1473
  printHuman(` ${c.cyan(res.url)}`);
1438
1474
  if (res.status === "pending") {
1439
1475
  printHuman(
@@ -1453,11 +1489,94 @@ function pushCommand() {
1453
1489
  }
1454
1490
  );
1455
1491
  }
1492
+ var EXCLUDE_NAMES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".gitkeep"]);
1493
+ var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["__pycache__", "node_modules", ".git", "evals", "dist", ".turbo"]);
1494
+ var EXCLUDE_EXTS = /* @__PURE__ */ new Set([".pyc", ".pyo", ".rbc"]);
1495
+ function shouldExclude(relPath, isDir) {
1496
+ const parts = relPath.split(posix.sep);
1497
+ const name = parts[parts.length - 1];
1498
+ if (EXCLUDE_NAMES.has(name)) return true;
1499
+ if (isDir && EXCLUDE_DIRS.has(name)) return true;
1500
+ const ext = name.includes(".") ? "." + name.split(".").pop() : "";
1501
+ if (EXCLUDE_EXTS.has(ext)) return true;
1502
+ return false;
1503
+ }
1504
+ async function walkDir(base, relDir, collected) {
1505
+ const entries = await readdir(join4(base, relDir), { withFileTypes: true });
1506
+ for (const entry of entries) {
1507
+ const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
1508
+ if (shouldExclude(rel, entry.isDirectory())) continue;
1509
+ if (entry.isDirectory()) {
1510
+ await walkDir(base, rel, collected);
1511
+ } else if (entry.isFile()) {
1512
+ collected.push({ rel, abs: join4(base, rel) });
1513
+ }
1514
+ }
1515
+ }
1516
+ async function readAsset(absPath) {
1517
+ const buf = await readFile3(absPath);
1518
+ const text = buf.toString("utf8");
1519
+ const roundtrip = Buffer.from(text, "utf8");
1520
+ if (roundtrip.equals(buf)) {
1521
+ return { content: text, encoding: "utf8" };
1522
+ }
1523
+ const b64 = buf.toString("base64");
1524
+ printHuman(c.gray(` \u2192 encoding ${absPath.split(sep).pop()} as base64`));
1525
+ return { content: b64, encoding: "base64" };
1526
+ }
1527
+ async function bundleDirectory(dir) {
1528
+ const all = [];
1529
+ await walkDir(dir, "", all);
1530
+ const skillEntry = all.find((f) => f.rel === "SKILL.md");
1531
+ if (!skillEntry) {
1532
+ throw new Error(`No SKILL.md found in directory ${dir}. Push a directory that has SKILL.md at its root.`);
1533
+ }
1534
+ const content = await readFile3(skillEntry.abs, "utf8");
1535
+ const assets = [];
1536
+ for (const f of all) {
1537
+ if (f.rel === "SKILL.md") continue;
1538
+ const { content: fc, encoding } = await readAsset(f.abs);
1539
+ assets.push({ path: f.rel, content: fc, encoding });
1540
+ }
1541
+ return { content, assets };
1542
+ }
1543
+ async function bundleSkillZip(zipPath) {
1544
+ const buf = await readFile3(zipPath);
1545
+ const zip = await JSZip.loadAsync(buf);
1546
+ const entries = Object.keys(zip.files).filter((k) => !zip.files[k].dir);
1547
+ if (entries.length === 0) throw new Error(".skill file is empty");
1548
+ const firstParts = entries[0].split("/");
1549
+ const prefix = firstParts.length > 1 ? firstParts[0] + "/" : "";
1550
+ const stripped = entries.map((k) => ({
1551
+ zipKey: k,
1552
+ rel: prefix ? k.slice(prefix.length) : k
1553
+ })).filter((f) => f.rel.length > 0 && !shouldExclude(f.rel, false));
1554
+ const skillEntry = stripped.find((f) => f.rel === "SKILL.md");
1555
+ if (!skillEntry) {
1556
+ throw new Error(`.skill ZIP has no SKILL.md at its root (prefix="${prefix}")`);
1557
+ }
1558
+ const content = await zip.files[skillEntry.zipKey].async("string");
1559
+ const assets = [];
1560
+ for (const f of stripped) {
1561
+ if (f.rel === "SKILL.md") continue;
1562
+ const rawBuf = await zip.files[f.zipKey].async("uint8array");
1563
+ const buf2 = Buffer.from(rawBuf);
1564
+ const text = buf2.toString("utf8");
1565
+ const roundtrip = Buffer.from(text, "utf8");
1566
+ if (roundtrip.equals(buf2)) {
1567
+ assets.push({ path: f.rel, content: text, encoding: "utf8" });
1568
+ } else {
1569
+ printHuman(c.gray(` \u2192 encoding ${f.rel} as base64`));
1570
+ assets.push({ path: f.rel, content: buf2.toString("base64"), encoding: "base64" });
1571
+ }
1572
+ }
1573
+ return { content, assets };
1574
+ }
1456
1575
 
1457
1576
  // src/commands/pull.ts
1458
1577
  import { Command as Command5 } from "commander";
1459
1578
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
1460
- import { dirname as dirname2, join as join4 } from "path";
1579
+ import { dirname as dirname2, join as join5 } from "path";
1461
1580
 
1462
1581
  // ../../packages/harness-schema/src/deps.ts
1463
1582
  init_src();
@@ -1777,7 +1896,12 @@ var BLOCKING_CODES = /* @__PURE__ */ new Set([
1777
1896
  "missing_description",
1778
1897
  "invalid_frontmatter",
1779
1898
  "content_too_short",
1780
- "content_too_long"
1899
+ "content_too_long",
1900
+ // asset-bundle codes
1901
+ "asset_path_unsafe",
1902
+ "asset_too_large",
1903
+ "bundle_too_large",
1904
+ "too_many_assets"
1781
1905
  ]);
1782
1906
  function earlyExit(firstFlag, override = {}) {
1783
1907
  return {
@@ -1860,10 +1984,11 @@ function buildSkillFile(input, outputPath) {
1860
1984
  }
1861
1985
  frontmatter.push("---");
1862
1986
  const content = frontmatter.join("\n") + "\n\n" + body;
1987
+ const resolvedPath = outputPath.replace("{name}", String(name));
1863
1988
  return {
1864
1989
  files: [
1865
1990
  {
1866
- path: outputPath.replace("{name}", String(name)),
1991
+ path: resolvedPath,
1867
1992
  content,
1868
1993
  mergeable: false,
1869
1994
  format: "markdown"
@@ -1873,17 +1998,45 @@ function buildSkillFile(input, outputPath) {
1873
1998
  format: "agent-skills"
1874
1999
  };
1875
2000
  }
1876
- var skillToClaude = (input) => buildSkillFile(input, ".claude/skills/{name}/SKILL.md");
1877
- var skillToCodex = (input) => buildSkillFile(input, ".codex/skills/{name}.md");
1878
- var skillToCursor = (input) => buildSkillFile(input, ".cursor/skills/{name}.md");
1879
- var skillToWindsurf = (input) => buildSkillFile(input, ".windsurf/skills/{name}.md");
1880
- var skillToContinue = (input) => buildSkillFile(input, ".continue/skills/{name}.md");
1881
- var skillToCopilot = (input) => buildSkillFile(input, ".github/skills/{name}.md");
1882
- var skillToGeminiCli = (input) => buildSkillFile(input, ".gemini/skills/{name}.md");
1883
- var skillToOpenCode = (input) => buildSkillFile(input, ".opencode/skills/{name}.md");
1884
- var skillToAntigravity = (input) => buildSkillFile(input, ".antigravity/skills/{name}.md");
2001
+ function buildSkillDir(input, outputPath) {
2002
+ const result = buildSkillFile(input, outputPath);
2003
+ const skillMdPath = result.files[0].path;
2004
+ const skillDir = skillMdPath.slice(0, skillMdPath.lastIndexOf("/"));
2005
+ const assetFiles = (input.assets ?? []).map((asset) => ({
2006
+ path: `${skillDir}/${asset.path}`,
2007
+ content: asset.content,
2008
+ mergeable: false,
2009
+ format: "plain",
2010
+ encoding: asset.encoding ?? "utf8"
2011
+ }));
2012
+ return {
2013
+ files: [...result.files, ...assetFiles],
2014
+ notes: result.notes,
2015
+ format: result.format
2016
+ };
2017
+ }
2018
+ function buildSkillSingleFile(input, outputPath) {
2019
+ const result = buildSkillFile(input, outputPath);
2020
+ const notes = [...result.notes];
2021
+ if (input.assets && input.assets.length > 0) {
2022
+ const tool = outputPath.split("/")[0]?.replace(".", "") ?? "this tool";
2023
+ notes.push(
2024
+ `${input.assets.length} bundled asset(s) not installed \u2014 ${tool} uses single-file skill convention. Pull with --tool claude-code to install the full bundle.`
2025
+ );
2026
+ }
2027
+ return { ...result, notes };
2028
+ }
2029
+ var skillToClaude = (input) => buildSkillDir(input, ".claude/skills/{name}/SKILL.md");
2030
+ var skillToCodex = (input) => buildSkillDir(input, ".codex/skills/{name}/SKILL.md");
2031
+ var skillToCursor = (input) => buildSkillSingleFile(input, ".cursor/skills/{name}.md");
2032
+ var skillToWindsurf = (input) => buildSkillSingleFile(input, ".windsurf/skills/{name}.md");
2033
+ var skillToContinue = (input) => buildSkillSingleFile(input, ".continue/skills/{name}.md");
2034
+ var skillToCopilot = (input) => buildSkillSingleFile(input, ".github/skills/{name}.md");
2035
+ var skillToGeminiCli = (input) => buildSkillDir(input, ".gemini/skills/{name}/SKILL.md");
2036
+ var skillToOpenCode = (input) => buildSkillSingleFile(input, ".opencode/skills/{name}.md");
2037
+ var skillToAntigravity = (input) => buildSkillSingleFile(input, ".antigravity/skills/{name}.md");
1885
2038
  var skillCanonical = (input) => {
1886
- const res = buildSkillFile(input, "SKILL.md");
2039
+ const res = buildSkillDir(input, "SKILL.md");
1887
2040
  res.format = "agent-skills-canonical";
1888
2041
  return res;
1889
2042
  };
@@ -2287,12 +2440,13 @@ function pullCommand() {
2287
2440
  fullSlug,
2288
2441
  frontmatter: parsed.frontmatter.data,
2289
2442
  body: parsed.body,
2290
- version: contentRes.version
2443
+ version: contentRes.version,
2444
+ assets: contentRes.assets
2291
2445
  });
2292
2446
  for (const f of result.files) {
2293
- const abs = join4(installRoot, f.path);
2447
+ const abs = join5(installRoot, f.path);
2294
2448
  await mkdir3(dirname2(abs), { recursive: true });
2295
- await writeFile3(abs, f.content, "utf8");
2449
+ await writeEmittedFile(abs, f.content, f.encoding);
2296
2450
  written.push(abs);
2297
2451
  }
2298
2452
  notes.push(...result.notes);
@@ -2303,15 +2457,16 @@ function pullCommand() {
2303
2457
  fullSlug,
2304
2458
  frontmatter: parsed.frontmatter.data,
2305
2459
  body: parsed.body,
2306
- version: contentRes.version
2460
+ version: contentRes.version,
2461
+ assets: contentRes.assets
2307
2462
  });
2308
2463
  notes.push(
2309
2464
  `Tool "${format}" is not supported for type "${parsed.type}"; installed canonical form instead.`
2310
2465
  );
2311
2466
  for (const f of result.files) {
2312
- const abs = join4(installRoot, f.path);
2467
+ const abs = join5(installRoot, f.path);
2313
2468
  await mkdir3(dirname2(abs), { recursive: true });
2314
- await writeFile3(abs, f.content, "utf8");
2469
+ await writeEmittedFile(abs, f.content, f.encoding);
2315
2470
  written.push(abs);
2316
2471
  }
2317
2472
  } else {
@@ -2339,7 +2494,7 @@ function pullCommand() {
2339
2494
  );
2340
2495
  }
2341
2496
  async function detectTool(root) {
2342
- const { stat } = await import("fs/promises");
2497
+ const { stat: stat2 } = await import("fs/promises");
2343
2498
  const checks = [
2344
2499
  [".claude", "claude-code"],
2345
2500
  [".cursor", "cursor"],
@@ -2355,13 +2510,20 @@ async function detectTool(root) {
2355
2510
  ];
2356
2511
  for (const [path, tool] of checks) {
2357
2512
  try {
2358
- await stat(join4(root, path));
2513
+ await stat2(join5(root, path));
2359
2514
  return tool;
2360
2515
  } catch {
2361
2516
  }
2362
2517
  }
2363
2518
  return null;
2364
2519
  }
2520
+ async function writeEmittedFile(abs, content, encoding) {
2521
+ if (encoding === "base64") {
2522
+ await writeFile3(abs, Buffer.from(content, "base64"));
2523
+ } else {
2524
+ await writeFile3(abs, content, "utf8");
2525
+ }
2526
+ }
2365
2527
 
2366
2528
  // src/commands/read.ts
2367
2529
  import { Command as Command6 } from "commander";
@@ -2461,7 +2623,7 @@ function logoutCommand() {
2461
2623
  // src/commands/setup.ts
2462
2624
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, access } from "fs/promises";
2463
2625
  import { homedir as homedir3 } from "os";
2464
- import { join as join5, dirname as dirname3 } from "path";
2626
+ import { join as join6, dirname as dirname3 } from "path";
2465
2627
  import { fileURLToPath as fileURLToPath2 } from "url";
2466
2628
  import { Command as Command9 } from "commander";
2467
2629
  var MARKER_START = "<!-- kordis-start -->";
@@ -2494,18 +2656,18 @@ function assetRoots() {
2494
2656
  const __dir2 = dirname3(fileURLToPath2(import.meta.url));
2495
2657
  return {
2496
2658
  // Built binary: bin/kordis.js → assets/ is one level up
2497
- bundled: join5(__dir2, "..", "assets"),
2659
+ bundled: join6(__dir2, "..", "assets"),
2498
2660
  // Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
2499
- source: join5(__dir2, "..", "..", "..", "..", "packages", "skills")
2661
+ source: join6(__dir2, "..", "..", "..", "..", "packages", "skills")
2500
2662
  };
2501
2663
  }
2502
2664
  async function pickAssetRoot() {
2503
2665
  const { bundled, source } = assetRoots();
2504
- return await exists(join5(bundled, "KORDIS.md")) ? bundled : source;
2666
+ return await exists(join6(bundled, "KORDIS.md")) ? bundled : source;
2505
2667
  }
2506
2668
  async function loadHubContent() {
2507
2669
  const root = await pickAssetRoot();
2508
- const raw = await readFile4(join5(root, "KORDIS.md"), "utf-8");
2670
+ const raw = await readFile4(join6(root, "KORDIS.md"), "utf-8");
2509
2671
  if (raw.startsWith("---")) {
2510
2672
  const end = raw.indexOf("\n---", 4);
2511
2673
  if (end !== -1) return raw.slice(end + 4).trimStart();
@@ -2514,7 +2676,7 @@ async function loadHubContent() {
2514
2676
  }
2515
2677
  async function loadSubSkillContent(name) {
2516
2678
  const root = await pickAssetRoot();
2517
- return readFile4(join5(root, name, "SKILL.md"), "utf-8");
2679
+ return readFile4(join6(root, name, "SKILL.md"), "utf-8");
2518
2680
  }
2519
2681
  async function injectBlock(filePath, content, dryRun) {
2520
2682
  const block = `${MARKER_START}
@@ -2582,8 +2744,8 @@ function setupCommand() {
2582
2744
  let anyFound = false;
2583
2745
  const changedTools = /* @__PURE__ */ new Set();
2584
2746
  for (const target of HUB_TARGETS) {
2585
- const dirPath = join5(home, target.dir);
2586
- const filePath = join5(home, target.file);
2747
+ const dirPath = join6(home, target.dir);
2748
+ const filePath = join6(home, target.file);
2587
2749
  if (!await exists(dirPath)) {
2588
2750
  if (!opts.silent) {
2589
2751
  printHuman(c.gray(` - ${target.tool.padEnd(12)} not installed, skipped`));
@@ -2610,10 +2772,10 @@ function setupCommand() {
2610
2772
  }
2611
2773
  }
2612
2774
  for (const target of SKILL_TARGETS) {
2613
- const dirPath = join5(home, target.dir);
2775
+ const dirPath = join6(home, target.dir);
2614
2776
  if (!await exists(dirPath)) continue;
2615
2777
  for (const subName of SUB_SKILLS) {
2616
- const filePath = join5(home, target.skillsDir, subName, "SKILL.md");
2778
+ const filePath = join6(home, target.skillsDir, subName, "SKILL.md");
2617
2779
  const dest = `~/${target.skillsDir}/${subName}/SKILL.md`;
2618
2780
  try {
2619
2781
  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.1.10",
3
+ "version": "0.1.11",
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",
@@ -26,6 +26,7 @@
26
26
  "dependencies": {
27
27
  "@iarna/toml": "^2.2.5",
28
28
  "commander": "^12.1.0",
29
+ "jszip": "^3.10.1",
29
30
  "kleur": "^4.1.5",
30
31
  "open": "^10.1.0",
31
32
  "ora": "^8.1.1",
@@ -42,6 +43,7 @@
42
43
  "@kordis/contracts": "workspace:*",
43
44
  "@kordis/harness-schema": "workspace:*",
44
45
  "@kordis/translators": "workspace:*",
46
+ "@types/jszip": "^3.4.1",
45
47
  "@types/node": "^22.9.0",
46
48
  "@types/prompts": "^2.4.9",
47
49
  "tsup": "^8.3.5",