@rayul/kordis 0.6.5 → 0.6.7

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 +185 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1204,6 +1204,7 @@ import { join as join2 } from "path";
1204
1204
  var SERVICE = "kordis";
1205
1205
  var plaintextWarningEmitted = false;
1206
1206
  async function loadKeytar() {
1207
+ if (process.env["KORDIS_NO_KEYCHAIN"] === "1") return null;
1207
1208
  try {
1208
1209
  const mod = await import("keytar");
1209
1210
  return mod.default ?? mod;
@@ -1583,29 +1584,53 @@ async function useClient() {
1583
1584
  baseUrl: cfg.apiUrl,
1584
1585
  userAgent: `kordis-cli/${VERSION} node/${process.version}`
1585
1586
  });
1587
+ const debug = process.env["KORDIS_DEBUG"] === "1";
1588
+ let pollResult = null;
1586
1589
  try {
1587
- const tokens = await bootstrapApi.pollDeviceFlow(pending.device_code);
1588
- const meClient = new KordisClient({
1589
- baseUrl: cfg.apiUrl,
1590
- userAgent: `kordis-cli/${VERSION} node/${process.version}`,
1591
- getAccessToken: () => tokens.access_token
1592
- });
1593
- const me = await meClient.me();
1594
- username = me.username;
1595
- currentTokens = {
1596
- access_token: tokens.access_token,
1597
- refresh_token: tokens.refresh_token,
1598
- session_id: tokens.session_id,
1599
- expires_at_epoch: Math.floor(Date.now() / 1e3) + tokens.expires_in
1600
- };
1601
- await keychain.save(username, currentTokens);
1602
- cfg = { ...cfg, username, sessionId: tokens.session_id };
1603
- await saveCliConfig(cfg);
1604
- await clearPending();
1590
+ pollResult = await bootstrapApi.pollDeviceFlow(pending.device_code);
1605
1591
  } catch (err) {
1606
1592
  const code = err.code;
1607
- if (code && code !== "authorization_pending") {
1593
+ if (code === "authorization_pending") {
1594
+ if (debug) process.stderr.write("[kordis] auto-finalize: authorization_pending (keeping pending)\n");
1595
+ } else {
1608
1596
  await clearPending();
1597
+ if (debug) {
1598
+ const msg = err instanceof Error ? err.message : String(err);
1599
+ process.stderr.write(`[kordis] auto-finalize: poll failed (code=${code ?? "none"}): ${msg}
1600
+ `);
1601
+ }
1602
+ }
1603
+ }
1604
+ if (pollResult) {
1605
+ try {
1606
+ const meClient = new KordisClient({
1607
+ baseUrl: cfg.apiUrl,
1608
+ userAgent: `kordis-cli/${VERSION} node/${process.version}`,
1609
+ getAccessToken: () => pollResult.access_token
1610
+ });
1611
+ const me = await meClient.me();
1612
+ username = me.username;
1613
+ currentTokens = {
1614
+ access_token: pollResult.access_token,
1615
+ refresh_token: pollResult.refresh_token,
1616
+ session_id: pollResult.session_id,
1617
+ expires_at_epoch: Math.floor(Date.now() / 1e3) + pollResult.expires_in
1618
+ };
1619
+ await keychain.save(username, currentTokens);
1620
+ cfg = { ...cfg, username, sessionId: pollResult.session_id };
1621
+ await saveCliConfig(cfg);
1622
+ await clearPending();
1623
+ if (debug) process.stderr.write(`[kordis] auto-finalize: persisted tokens for ${username}
1624
+ `);
1625
+ } catch (err) {
1626
+ await clearPending();
1627
+ currentTokens = null;
1628
+ username = null;
1629
+ const msg = err instanceof Error ? err.message : String(err);
1630
+ process.stderr.write(
1631
+ `[kordis] auto-finalize: server issued tokens but local persistence failed (${msg}). Run \`kordis login --emit-only\` again to retry.
1632
+ `
1633
+ );
1609
1634
  }
1610
1635
  }
1611
1636
  }
@@ -1894,14 +1919,47 @@ var SORT_MODES = [
1894
1919
  "rising"
1895
1920
  ];
1896
1921
  var TIME_WINDOWS = ["24h", "7d", "30d"];
1922
+ var TOOL_SLUGS = [
1923
+ "claude-code",
1924
+ "codex",
1925
+ "cursor",
1926
+ "antigravity",
1927
+ "windsurf",
1928
+ "continue",
1929
+ "copilot",
1930
+ "opencode",
1931
+ "gemini-cli"
1932
+ ];
1933
+ var TOOL_ALIASES = {
1934
+ claude: "claude-code",
1935
+ gemini: "gemini-cli"
1936
+ };
1897
1937
  function searchCommand() {
1898
- return new Command3("search").description("search the Kordis registry").argument("[query...]", "keywords to match against title/description").option("--type <type>", "filter by harness type (skill|mcp-server|project-rules|custom-agent|hook)").option("--tool <tool>", "filter by compatible tool (claude-code|codex|cursor|...)").option("--tier <tier>", "filter by portability tier (A|B|C|D)").option("--author <username>", "filter to a single author (server returns exact total when set)").option("--sort <mode>", `sort: ${SORT_MODES.join("|")}`).option("--trending", "list trending packages (overrides --sort)", false).option("--rising", "list rising packages by 7d acceleration (overrides --sort)", false).option("--window <w>", `time window for --trending: ${TIME_WINDOWS.join("|")}`, "24h").option("--limit <n>", "max results", (v) => parseInt(v, 10), 20).action(async (query, opts) => {
1938
+ return new Command3("search").description("search the Kordis registry").argument("[query...]", "keywords to match against title/description").option("--type <type>", "filter by harness type (skill|mcp-server|project-rules|custom-agent|hook)").option(
1939
+ "--tool <tool>",
1940
+ `filter by compatible tool \u2014 one of: ${TOOL_SLUGS.join("|")} (aliases: ${Object.entries(
1941
+ TOOL_ALIASES
1942
+ ).map(([alias, canonical]) => `${alias}\u2192${canonical}`).join(", ")})`
1943
+ ).option("--tier <tier>", "filter by portability tier (A|B|C|D)").option("--author <username>", "filter to a single author (server returns exact total when set)").option(
1944
+ "--cursor <token>",
1945
+ "pagination cursor \u2014 pass the next_cursor field from a prior search response to fetch the next page"
1946
+ ).option("--sort <mode>", `sort: ${SORT_MODES.join("|")}`).option("--trending", "list trending packages (overrides --sort)", false).option("--rising", "list rising packages by 7d acceleration (overrides --sort)", false).option("--window <w>", `time window for --trending: ${TIME_WINDOWS.join("|")}`, "24h").option("--limit <n>", "max results", (v) => parseInt(v, 10), 20).action(async (query, opts) => {
1899
1947
  if (opts.sort && !SORT_MODES.includes(opts.sort)) {
1900
1948
  throw new Error(`--sort must be one of ${SORT_MODES.join(", ")}`);
1901
1949
  }
1902
1950
  if (opts.window && !TIME_WINDOWS.includes(opts.window)) {
1903
1951
  throw new Error(`--window must be one of ${TIME_WINDOWS.join(", ")}`);
1904
1952
  }
1953
+ let resolvedTool;
1954
+ if (opts.tool) {
1955
+ const candidate = TOOL_ALIASES[opts.tool] ?? opts.tool;
1956
+ if (!TOOL_SLUGS.includes(candidate)) {
1957
+ throw new Error(
1958
+ `--tool must be one of ${TOOL_SLUGS.join(", ")} (you passed "${opts.tool}")`
1959
+ );
1960
+ }
1961
+ resolvedTool = candidate;
1962
+ }
1905
1963
  const bundle = await useClient();
1906
1964
  let res;
1907
1965
  if (opts.rising) {
@@ -1919,9 +1977,10 @@ function searchCommand() {
1919
1977
  res = await bundle.api.search({
1920
1978
  q: query.join(" ") || void 0,
1921
1979
  type: opts.type ? [opts.type] : void 0,
1922
- tool: opts.tool ? [opts.tool] : void 0,
1980
+ tool: resolvedTool ? [resolvedTool] : void 0,
1923
1981
  tier: opts.tier,
1924
1982
  ...opts.author ? { author: opts.author } : {},
1983
+ ...opts.cursor ? { cursor: opts.cursor } : {},
1925
1984
  sort: opts.sort ?? "relevance",
1926
1985
  limit: opts.limit
1927
1986
  });
@@ -3277,18 +3336,23 @@ function getTranslator(type, format) {
3277
3336
  import { homedir as homedir6 } from "os";
3278
3337
  import { join as join8 } from "path";
3279
3338
  import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
3280
- var LOCKFILE_PATH = join8(homedir6(), ".kordis", "installed.json");
3339
+ function kordisHome2() {
3340
+ return process.env["KORDIS_HOME"] ?? join8(homedir6(), ".kordis");
3341
+ }
3342
+ function lockfilePath() {
3343
+ return join8(kordisHome2(), "installed.json");
3344
+ }
3281
3345
  async function readLockfile() {
3282
3346
  try {
3283
- const raw = await readFile7(LOCKFILE_PATH, "utf8");
3347
+ const raw = await readFile7(lockfilePath(), "utf8");
3284
3348
  return JSON.parse(raw);
3285
3349
  } catch {
3286
3350
  return { packages: {} };
3287
3351
  }
3288
3352
  }
3289
3353
  async function writeLockfile(lf) {
3290
- await mkdir5(join8(homedir6(), ".kordis"), { recursive: true });
3291
- await writeFile5(LOCKFILE_PATH, JSON.stringify(lf, null, 2) + "\n", "utf8");
3354
+ await mkdir5(kordisHome2(), { recursive: true });
3355
+ await writeFile5(lockfilePath(), JSON.stringify(lf, null, 2) + "\n", "utf8");
3292
3356
  }
3293
3357
  async function recordInstalled(fullSlug, entry) {
3294
3358
  const lf = await readLockfile();
@@ -3808,6 +3872,8 @@ var KINDS = [
3808
3872
  "praise",
3809
3873
  "question"
3810
3874
  ];
3875
+ var LIST_SORTS = ["recent", "old"];
3876
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3811
3877
  function commentCommand() {
3812
3878
  return new Command10("comment").description("post a comment on a package").argument("<full_slug>", "package identifier {username}/{slug}").requiredOption("--body <text>", "comment body (1\u201310000 chars)").option("--kind <kind>", `top-level kind: ${KINDS.join("|")}`, "comment").option("--title <text>", "top-level post title (1\u2013140 chars)").action(
3813
3879
  async (rawSlug, opts) => {
@@ -3838,11 +3904,103 @@ function commentCommand() {
3838
3904
  if (res.permalink) {
3839
3905
  printHuman(c.gray(` view: ${res.permalink}`));
3840
3906
  }
3907
+ printHuman(c.gray(` delete: kordis uncomment ${res.id}`));
3841
3908
  }
3842
3909
  });
3843
3910
  }
3844
3911
  );
3845
3912
  }
3913
+ function commentsCommand() {
3914
+ return new Command10("comments").description("list comments on a package").argument("<full_slug>", "package identifier {username}/{slug}").option("--limit <n>", "max top-level comments (1\u201350)", (v) => parseInt(v, 10), 20).option("--cursor <token>", "pagination cursor returned by a prior call as next_cursor").option(
3915
+ "--kind <kind>",
3916
+ `filter top-level posts by kind: ${KINDS.join("|")}`
3917
+ ).option("--sort <mode>", `sort: ${LIST_SORTS.join("|")} (default: recent)`).action(
3918
+ async (rawSlug, opts) => {
3919
+ const [username, slug] = rawSlug.split("/");
3920
+ if (!username || !slug) {
3921
+ throw new Error('full_slug must be "{username}/{slug}"');
3922
+ }
3923
+ if (opts.kind && !KINDS.includes(opts.kind)) {
3924
+ throw new Error(`--kind must be one of ${KINDS.join(", ")}`);
3925
+ }
3926
+ if (opts.sort && !LIST_SORTS.includes(opts.sort)) {
3927
+ throw new Error(`--sort must be one of ${LIST_SORTS.join(", ")}`);
3928
+ }
3929
+ const bundle = await useClient();
3930
+ const res = await bundle.api.listComments(username, slug, {
3931
+ limit: opts.limit,
3932
+ ...opts.cursor ? { cursor: opts.cursor } : {},
3933
+ ...opts.kind ? { kind: opts.kind } : {},
3934
+ ...opts.sort ? { sort: opts.sort } : {}
3935
+ });
3936
+ printDual({
3937
+ json: res,
3938
+ human: () => {
3939
+ if (res.results.length === 0) {
3940
+ printHuman(c.yellow("(no comments)"));
3941
+ printHuman(
3942
+ c.gray(" \u2192 be the first: kordis comment " + rawSlug + ' --body "..."')
3943
+ );
3944
+ return;
3945
+ }
3946
+ printHuman(
3947
+ c.gray(
3948
+ `Showing ${res.results.length} of ${res.total} top-level comment${res.total === 1 ? "" : "s"}`
3949
+ )
3950
+ );
3951
+ printHuman("");
3952
+ for (const node of res.results) {
3953
+ renderCommentNode(node, 0);
3954
+ }
3955
+ if (res.next_cursor) {
3956
+ printHuman(
3957
+ c.gray(
3958
+ `(more available \u2014 kordis comments ${rawSlug} --cursor ${res.next_cursor})`
3959
+ )
3960
+ );
3961
+ }
3962
+ }
3963
+ });
3964
+ }
3965
+ );
3966
+ }
3967
+ function renderCommentNode(node, depth) {
3968
+ const indent = " ".repeat(depth);
3969
+ const tag = node.kind === "comment" ? "" : c.cyan(`[${node.kind}]`) + " ";
3970
+ const title = node.title ? c.bold(node.title) + " " : "";
3971
+ const author = `${node.author.username}${node.author.display_name ? ` (${node.author.display_name})` : ""}`;
3972
+ printHuman(`${indent}${tag}${title}${c.gray(`\u2014 ${author}`)}`);
3973
+ for (const line of node.body.split("\n")) {
3974
+ printHuman(`${indent} ${line}`);
3975
+ }
3976
+ const trail = [`id: ${node.id}`];
3977
+ if (node.reply_count > 0) trail.push(`${node.reply_count} repl${node.reply_count === 1 ? "y" : "ies"}`);
3978
+ if (node.is_edited) trail.push("edited");
3979
+ if (node.permalink) trail.push(`view: ${node.permalink}`);
3980
+ printHuman(c.gray(`${indent} ${trail.join(" \xB7 ")}`));
3981
+ printHuman("");
3982
+ for (const child of node.replies) {
3983
+ renderCommentNode(child, depth + 1);
3984
+ }
3985
+ }
3986
+ function uncommentCommand() {
3987
+ return new Command10("uncomment").description("delete one of your comments by id").argument("<id>", "comment id (uuid v4)").action(async (id) => {
3988
+ if (!UUID_RE.test(id)) {
3989
+ throw new Error(
3990
+ "comment id must be a uuid v4 \u2014 get it from `kordis comments <slug>` or the `id` field on `kordis comment`'s response"
3991
+ );
3992
+ }
3993
+ const bundle = await useClient();
3994
+ requireAuth(bundle);
3995
+ await bundle.authedRequest((api) => api.deleteComment(id));
3996
+ printDual({
3997
+ json: { status: "deleted", id },
3998
+ human: () => {
3999
+ printHuman(c.gray(`\u2717 deleted comment ${id}`));
4000
+ }
4001
+ });
4002
+ });
4003
+ }
3846
4004
 
3847
4005
  // src/commands/star.ts
3848
4006
  import { Command as Command11 } from "commander";
@@ -4440,6 +4598,8 @@ function buildProgram() {
4440
4598
  program.addCommand(readCommand());
4441
4599
  program.addCommand(infoCommand());
4442
4600
  program.addCommand(commentCommand());
4601
+ program.addCommand(commentsCommand());
4602
+ program.addCommand(uncommentCommand());
4443
4603
  program.addCommand(starCommand());
4444
4604
  program.addCommand(unstarCommand());
4445
4605
  program.addCommand(deleteCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayul/kordis",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
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",