@rayul/kordis 0.1.10 → 0.1.12
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/assets/kordis-using/SKILL.md +10 -6
- package/dist/index.js +217 -43
- package/package.json +3 -1
|
@@ -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
|
|
104
|
-
|
|
105
|
-
`scripts
|
|
106
|
-
SKILL.md
|
|
107
|
-
|
|
108
|
-
|
|
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,32 @@ 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("<
|
|
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 (
|
|
1396
|
+
async (path, opts) => {
|
|
1374
1397
|
const bundle = await useClient();
|
|
1375
|
-
const abs = resolve(process.cwd(),
|
|
1376
|
-
const
|
|
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
|
+
content = ensureTypeField(content, "skill");
|
|
1405
|
+
} else if (abs.endsWith(".skill")) {
|
|
1406
|
+
({ content, assets } = await bundleSkillZip(abs));
|
|
1407
|
+
content = ensureTypeField(content, "skill");
|
|
1408
|
+
} else {
|
|
1409
|
+
content = await readFile3(abs, "utf8");
|
|
1410
|
+
}
|
|
1377
1411
|
let targetUser = null;
|
|
1378
1412
|
let targetSlug = null;
|
|
1379
1413
|
if (opts.slug && opts.slug.includes("/")) {
|
|
1380
|
-
const [u,
|
|
1381
|
-
if (u &&
|
|
1414
|
+
const [u, s2] = opts.slug.split("/", 2);
|
|
1415
|
+
if (u && s2) {
|
|
1382
1416
|
targetUser = u;
|
|
1383
|
-
targetSlug =
|
|
1417
|
+
targetSlug = s2;
|
|
1384
1418
|
}
|
|
1385
1419
|
} else {
|
|
1386
1420
|
let nameFromFm = null;
|
|
@@ -1415,6 +1449,7 @@ function pushCommand() {
|
|
|
1415
1449
|
(api) => api.publish(
|
|
1416
1450
|
{
|
|
1417
1451
|
content,
|
|
1452
|
+
assets: assets.length > 0 ? assets : void 0,
|
|
1418
1453
|
bump: opts.bump,
|
|
1419
1454
|
slug: opts.slug && !opts.slug.includes("/") ? opts.slug : void 0,
|
|
1420
1455
|
target_full_slug: targetFullSlug,
|
|
@@ -1434,6 +1469,9 @@ function pushCommand() {
|
|
|
1434
1469
|
printHuman(
|
|
1435
1470
|
`${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
1471
|
);
|
|
1472
|
+
if (assets.length > 0) {
|
|
1473
|
+
printHuman(c.gray(` + ${assets.length} bundled file(s)`));
|
|
1474
|
+
}
|
|
1437
1475
|
printHuman(` ${c.cyan(res.url)}`);
|
|
1438
1476
|
if (res.status === "pending") {
|
|
1439
1477
|
printHuman(
|
|
@@ -1453,11 +1491,104 @@ function pushCommand() {
|
|
|
1453
1491
|
}
|
|
1454
1492
|
);
|
|
1455
1493
|
}
|
|
1494
|
+
function ensureTypeField(content, defaultType) {
|
|
1495
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
1496
|
+
if (!fmMatch) return content;
|
|
1497
|
+
const fmBlock = fmMatch[1];
|
|
1498
|
+
if (/^type\s*:/m.test(fmBlock)) return content;
|
|
1499
|
+
const injected = fmMatch[0].replace(/^---\r?\n/, `---
|
|
1500
|
+
type: ${defaultType}
|
|
1501
|
+
`);
|
|
1502
|
+
return content.replace(fmMatch[0], injected);
|
|
1503
|
+
}
|
|
1504
|
+
var EXCLUDE_NAMES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".gitkeep"]);
|
|
1505
|
+
var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["__pycache__", "node_modules", ".git", "evals", "dist", ".turbo"]);
|
|
1506
|
+
var EXCLUDE_EXTS = /* @__PURE__ */ new Set([".pyc", ".pyo", ".rbc"]);
|
|
1507
|
+
function shouldExclude(relPath, isDir) {
|
|
1508
|
+
const parts = relPath.split(posix.sep);
|
|
1509
|
+
const name = parts[parts.length - 1];
|
|
1510
|
+
if (EXCLUDE_NAMES.has(name)) return true;
|
|
1511
|
+
if (isDir && EXCLUDE_DIRS.has(name)) return true;
|
|
1512
|
+
const ext = name.includes(".") ? "." + name.split(".").pop() : "";
|
|
1513
|
+
if (EXCLUDE_EXTS.has(ext)) return true;
|
|
1514
|
+
return false;
|
|
1515
|
+
}
|
|
1516
|
+
async function walkDir(base, relDir, collected) {
|
|
1517
|
+
const entries = await readdir(join4(base, relDir), { withFileTypes: true });
|
|
1518
|
+
for (const entry of entries) {
|
|
1519
|
+
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
1520
|
+
if (shouldExclude(rel, entry.isDirectory())) continue;
|
|
1521
|
+
if (entry.isDirectory()) {
|
|
1522
|
+
await walkDir(base, rel, collected);
|
|
1523
|
+
} else if (entry.isFile()) {
|
|
1524
|
+
collected.push({ rel, abs: join4(base, rel) });
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
async function readAsset(absPath) {
|
|
1529
|
+
const buf = await readFile3(absPath);
|
|
1530
|
+
const text = buf.toString("utf8");
|
|
1531
|
+
const roundtrip = Buffer.from(text, "utf8");
|
|
1532
|
+
if (roundtrip.equals(buf)) {
|
|
1533
|
+
return { content: text, encoding: "utf8" };
|
|
1534
|
+
}
|
|
1535
|
+
const b64 = buf.toString("base64");
|
|
1536
|
+
printHuman(c.gray(` \u2192 encoding ${absPath.split(sep).pop()} as base64`));
|
|
1537
|
+
return { content: b64, encoding: "base64" };
|
|
1538
|
+
}
|
|
1539
|
+
async function bundleDirectory(dir) {
|
|
1540
|
+
const all = [];
|
|
1541
|
+
await walkDir(dir, "", all);
|
|
1542
|
+
const skillEntry = all.find((f) => f.rel === "SKILL.md");
|
|
1543
|
+
if (!skillEntry) {
|
|
1544
|
+
throw new Error(`No SKILL.md found in directory ${dir}. Push a directory that has SKILL.md at its root.`);
|
|
1545
|
+
}
|
|
1546
|
+
const content = await readFile3(skillEntry.abs, "utf8");
|
|
1547
|
+
const assets = [];
|
|
1548
|
+
for (const f of all) {
|
|
1549
|
+
if (f.rel === "SKILL.md") continue;
|
|
1550
|
+
const { content: fc, encoding } = await readAsset(f.abs);
|
|
1551
|
+
assets.push({ path: f.rel, content: fc, encoding });
|
|
1552
|
+
}
|
|
1553
|
+
return { content, assets };
|
|
1554
|
+
}
|
|
1555
|
+
async function bundleSkillZip(zipPath) {
|
|
1556
|
+
const buf = await readFile3(zipPath);
|
|
1557
|
+
const zip = await JSZip.loadAsync(buf);
|
|
1558
|
+
const entries = Object.keys(zip.files).filter((k) => !zip.files[k].dir);
|
|
1559
|
+
if (entries.length === 0) throw new Error(".skill file is empty");
|
|
1560
|
+
const firstParts = entries[0].split("/");
|
|
1561
|
+
const prefix = firstParts.length > 1 ? firstParts[0] + "/" : "";
|
|
1562
|
+
const stripped = entries.map((k) => ({
|
|
1563
|
+
zipKey: k,
|
|
1564
|
+
rel: prefix ? k.slice(prefix.length) : k
|
|
1565
|
+
})).filter((f) => f.rel.length > 0 && !shouldExclude(f.rel, false));
|
|
1566
|
+
const skillEntry = stripped.find((f) => f.rel === "SKILL.md");
|
|
1567
|
+
if (!skillEntry) {
|
|
1568
|
+
throw new Error(`.skill ZIP has no SKILL.md at its root (prefix="${prefix}")`);
|
|
1569
|
+
}
|
|
1570
|
+
const content = await zip.files[skillEntry.zipKey].async("string");
|
|
1571
|
+
const assets = [];
|
|
1572
|
+
for (const f of stripped) {
|
|
1573
|
+
if (f.rel === "SKILL.md") continue;
|
|
1574
|
+
const rawBuf = await zip.files[f.zipKey].async("uint8array");
|
|
1575
|
+
const buf2 = Buffer.from(rawBuf);
|
|
1576
|
+
const text = buf2.toString("utf8");
|
|
1577
|
+
const roundtrip = Buffer.from(text, "utf8");
|
|
1578
|
+
if (roundtrip.equals(buf2)) {
|
|
1579
|
+
assets.push({ path: f.rel, content: text, encoding: "utf8" });
|
|
1580
|
+
} else {
|
|
1581
|
+
printHuman(c.gray(` \u2192 encoding ${f.rel} as base64`));
|
|
1582
|
+
assets.push({ path: f.rel, content: buf2.toString("base64"), encoding: "base64" });
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return { content, assets };
|
|
1586
|
+
}
|
|
1456
1587
|
|
|
1457
1588
|
// src/commands/pull.ts
|
|
1458
1589
|
import { Command as Command5 } from "commander";
|
|
1459
1590
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
1460
|
-
import { dirname as dirname2, join as
|
|
1591
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
1461
1592
|
|
|
1462
1593
|
// ../../packages/harness-schema/src/deps.ts
|
|
1463
1594
|
init_src();
|
|
@@ -1777,7 +1908,12 @@ var BLOCKING_CODES = /* @__PURE__ */ new Set([
|
|
|
1777
1908
|
"missing_description",
|
|
1778
1909
|
"invalid_frontmatter",
|
|
1779
1910
|
"content_too_short",
|
|
1780
|
-
"content_too_long"
|
|
1911
|
+
"content_too_long",
|
|
1912
|
+
// asset-bundle codes
|
|
1913
|
+
"asset_path_unsafe",
|
|
1914
|
+
"asset_too_large",
|
|
1915
|
+
"bundle_too_large",
|
|
1916
|
+
"too_many_assets"
|
|
1781
1917
|
]);
|
|
1782
1918
|
function earlyExit(firstFlag, override = {}) {
|
|
1783
1919
|
return {
|
|
@@ -1860,10 +1996,11 @@ function buildSkillFile(input, outputPath) {
|
|
|
1860
1996
|
}
|
|
1861
1997
|
frontmatter.push("---");
|
|
1862
1998
|
const content = frontmatter.join("\n") + "\n\n" + body;
|
|
1999
|
+
const resolvedPath = outputPath.replace("{name}", String(name));
|
|
1863
2000
|
return {
|
|
1864
2001
|
files: [
|
|
1865
2002
|
{
|
|
1866
|
-
path:
|
|
2003
|
+
path: resolvedPath,
|
|
1867
2004
|
content,
|
|
1868
2005
|
mergeable: false,
|
|
1869
2006
|
format: "markdown"
|
|
@@ -1873,17 +2010,45 @@ function buildSkillFile(input, outputPath) {
|
|
|
1873
2010
|
format: "agent-skills"
|
|
1874
2011
|
};
|
|
1875
2012
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
2013
|
+
function buildSkillDir(input, outputPath) {
|
|
2014
|
+
const result = buildSkillFile(input, outputPath);
|
|
2015
|
+
const skillMdPath = result.files[0].path;
|
|
2016
|
+
const skillDir = skillMdPath.slice(0, skillMdPath.lastIndexOf("/"));
|
|
2017
|
+
const assetFiles = (input.assets ?? []).map((asset) => ({
|
|
2018
|
+
path: `${skillDir}/${asset.path}`,
|
|
2019
|
+
content: asset.content,
|
|
2020
|
+
mergeable: false,
|
|
2021
|
+
format: "plain",
|
|
2022
|
+
encoding: asset.encoding ?? "utf8"
|
|
2023
|
+
}));
|
|
2024
|
+
return {
|
|
2025
|
+
files: [...result.files, ...assetFiles],
|
|
2026
|
+
notes: result.notes,
|
|
2027
|
+
format: result.format
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
function buildSkillSingleFile(input, outputPath) {
|
|
2031
|
+
const result = buildSkillFile(input, outputPath);
|
|
2032
|
+
const notes = [...result.notes];
|
|
2033
|
+
if (input.assets && input.assets.length > 0) {
|
|
2034
|
+
const tool = outputPath.split("/")[0]?.replace(".", "") ?? "this tool";
|
|
2035
|
+
notes.push(
|
|
2036
|
+
`${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.`
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
return { ...result, notes };
|
|
2040
|
+
}
|
|
2041
|
+
var skillToClaude = (input) => buildSkillDir(input, ".claude/skills/{name}/SKILL.md");
|
|
2042
|
+
var skillToCodex = (input) => buildSkillDir(input, ".codex/skills/{name}/SKILL.md");
|
|
2043
|
+
var skillToCursor = (input) => buildSkillSingleFile(input, ".cursor/skills/{name}.md");
|
|
2044
|
+
var skillToWindsurf = (input) => buildSkillSingleFile(input, ".windsurf/skills/{name}.md");
|
|
2045
|
+
var skillToContinue = (input) => buildSkillSingleFile(input, ".continue/skills/{name}.md");
|
|
2046
|
+
var skillToCopilot = (input) => buildSkillSingleFile(input, ".github/skills/{name}.md");
|
|
2047
|
+
var skillToGeminiCli = (input) => buildSkillDir(input, ".gemini/skills/{name}/SKILL.md");
|
|
2048
|
+
var skillToOpenCode = (input) => buildSkillSingleFile(input, ".opencode/skills/{name}.md");
|
|
2049
|
+
var skillToAntigravity = (input) => buildSkillSingleFile(input, ".antigravity/skills/{name}.md");
|
|
1885
2050
|
var skillCanonical = (input) => {
|
|
1886
|
-
const res =
|
|
2051
|
+
const res = buildSkillDir(input, "SKILL.md");
|
|
1887
2052
|
res.format = "agent-skills-canonical";
|
|
1888
2053
|
return res;
|
|
1889
2054
|
};
|
|
@@ -2287,12 +2452,13 @@ function pullCommand() {
|
|
|
2287
2452
|
fullSlug,
|
|
2288
2453
|
frontmatter: parsed.frontmatter.data,
|
|
2289
2454
|
body: parsed.body,
|
|
2290
|
-
version: contentRes.version
|
|
2455
|
+
version: contentRes.version,
|
|
2456
|
+
assets: contentRes.assets
|
|
2291
2457
|
});
|
|
2292
2458
|
for (const f of result.files) {
|
|
2293
|
-
const abs =
|
|
2459
|
+
const abs = join5(installRoot, f.path);
|
|
2294
2460
|
await mkdir3(dirname2(abs), { recursive: true });
|
|
2295
|
-
await
|
|
2461
|
+
await writeEmittedFile(abs, f.content, f.encoding);
|
|
2296
2462
|
written.push(abs);
|
|
2297
2463
|
}
|
|
2298
2464
|
notes.push(...result.notes);
|
|
@@ -2303,15 +2469,16 @@ function pullCommand() {
|
|
|
2303
2469
|
fullSlug,
|
|
2304
2470
|
frontmatter: parsed.frontmatter.data,
|
|
2305
2471
|
body: parsed.body,
|
|
2306
|
-
version: contentRes.version
|
|
2472
|
+
version: contentRes.version,
|
|
2473
|
+
assets: contentRes.assets
|
|
2307
2474
|
});
|
|
2308
2475
|
notes.push(
|
|
2309
2476
|
`Tool "${format}" is not supported for type "${parsed.type}"; installed canonical form instead.`
|
|
2310
2477
|
);
|
|
2311
2478
|
for (const f of result.files) {
|
|
2312
|
-
const abs =
|
|
2479
|
+
const abs = join5(installRoot, f.path);
|
|
2313
2480
|
await mkdir3(dirname2(abs), { recursive: true });
|
|
2314
|
-
await
|
|
2481
|
+
await writeEmittedFile(abs, f.content, f.encoding);
|
|
2315
2482
|
written.push(abs);
|
|
2316
2483
|
}
|
|
2317
2484
|
} else {
|
|
@@ -2339,7 +2506,7 @@ function pullCommand() {
|
|
|
2339
2506
|
);
|
|
2340
2507
|
}
|
|
2341
2508
|
async function detectTool(root) {
|
|
2342
|
-
const { stat } = await import("fs/promises");
|
|
2509
|
+
const { stat: stat2 } = await import("fs/promises");
|
|
2343
2510
|
const checks = [
|
|
2344
2511
|
[".claude", "claude-code"],
|
|
2345
2512
|
[".cursor", "cursor"],
|
|
@@ -2355,13 +2522,20 @@ async function detectTool(root) {
|
|
|
2355
2522
|
];
|
|
2356
2523
|
for (const [path, tool] of checks) {
|
|
2357
2524
|
try {
|
|
2358
|
-
await
|
|
2525
|
+
await stat2(join5(root, path));
|
|
2359
2526
|
return tool;
|
|
2360
2527
|
} catch {
|
|
2361
2528
|
}
|
|
2362
2529
|
}
|
|
2363
2530
|
return null;
|
|
2364
2531
|
}
|
|
2532
|
+
async function writeEmittedFile(abs, content, encoding) {
|
|
2533
|
+
if (encoding === "base64") {
|
|
2534
|
+
await writeFile3(abs, Buffer.from(content, "base64"));
|
|
2535
|
+
} else {
|
|
2536
|
+
await writeFile3(abs, content, "utf8");
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2365
2539
|
|
|
2366
2540
|
// src/commands/read.ts
|
|
2367
2541
|
import { Command as Command6 } from "commander";
|
|
@@ -2461,7 +2635,7 @@ function logoutCommand() {
|
|
|
2461
2635
|
// src/commands/setup.ts
|
|
2462
2636
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, access } from "fs/promises";
|
|
2463
2637
|
import { homedir as homedir3 } from "os";
|
|
2464
|
-
import { join as
|
|
2638
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
2465
2639
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2466
2640
|
import { Command as Command9 } from "commander";
|
|
2467
2641
|
var MARKER_START = "<!-- kordis-start -->";
|
|
@@ -2494,18 +2668,18 @@ function assetRoots() {
|
|
|
2494
2668
|
const __dir2 = dirname3(fileURLToPath2(import.meta.url));
|
|
2495
2669
|
return {
|
|
2496
2670
|
// Built binary: bin/kordis.js → assets/ is one level up
|
|
2497
|
-
bundled:
|
|
2671
|
+
bundled: join6(__dir2, "..", "assets"),
|
|
2498
2672
|
// Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
|
|
2499
|
-
source:
|
|
2673
|
+
source: join6(__dir2, "..", "..", "..", "..", "packages", "skills")
|
|
2500
2674
|
};
|
|
2501
2675
|
}
|
|
2502
2676
|
async function pickAssetRoot() {
|
|
2503
2677
|
const { bundled, source } = assetRoots();
|
|
2504
|
-
return await exists(
|
|
2678
|
+
return await exists(join6(bundled, "KORDIS.md")) ? bundled : source;
|
|
2505
2679
|
}
|
|
2506
2680
|
async function loadHubContent() {
|
|
2507
2681
|
const root = await pickAssetRoot();
|
|
2508
|
-
const raw = await readFile4(
|
|
2682
|
+
const raw = await readFile4(join6(root, "KORDIS.md"), "utf-8");
|
|
2509
2683
|
if (raw.startsWith("---")) {
|
|
2510
2684
|
const end = raw.indexOf("\n---", 4);
|
|
2511
2685
|
if (end !== -1) return raw.slice(end + 4).trimStart();
|
|
@@ -2514,7 +2688,7 @@ async function loadHubContent() {
|
|
|
2514
2688
|
}
|
|
2515
2689
|
async function loadSubSkillContent(name) {
|
|
2516
2690
|
const root = await pickAssetRoot();
|
|
2517
|
-
return readFile4(
|
|
2691
|
+
return readFile4(join6(root, name, "SKILL.md"), "utf-8");
|
|
2518
2692
|
}
|
|
2519
2693
|
async function injectBlock(filePath, content, dryRun) {
|
|
2520
2694
|
const block = `${MARKER_START}
|
|
@@ -2582,8 +2756,8 @@ function setupCommand() {
|
|
|
2582
2756
|
let anyFound = false;
|
|
2583
2757
|
const changedTools = /* @__PURE__ */ new Set();
|
|
2584
2758
|
for (const target of HUB_TARGETS) {
|
|
2585
|
-
const dirPath =
|
|
2586
|
-
const filePath =
|
|
2759
|
+
const dirPath = join6(home, target.dir);
|
|
2760
|
+
const filePath = join6(home, target.file);
|
|
2587
2761
|
if (!await exists(dirPath)) {
|
|
2588
2762
|
if (!opts.silent) {
|
|
2589
2763
|
printHuman(c.gray(` - ${target.tool.padEnd(12)} not installed, skipped`));
|
|
@@ -2610,10 +2784,10 @@ function setupCommand() {
|
|
|
2610
2784
|
}
|
|
2611
2785
|
}
|
|
2612
2786
|
for (const target of SKILL_TARGETS) {
|
|
2613
|
-
const dirPath =
|
|
2787
|
+
const dirPath = join6(home, target.dir);
|
|
2614
2788
|
if (!await exists(dirPath)) continue;
|
|
2615
2789
|
for (const subName of SUB_SKILLS) {
|
|
2616
|
-
const filePath =
|
|
2790
|
+
const filePath = join6(home, target.skillsDir, subName, "SKILL.md");
|
|
2617
2791
|
const dest = `~/${target.skillsDir}/${subName}/SKILL.md`;
|
|
2618
2792
|
try {
|
|
2619
2793
|
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.
|
|
3
|
+
"version": "0.1.12",
|
|
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",
|