@rayul/kordis 0.1.17 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +183 -34
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ var init_primitives = __esm({
45
45
 
46
46
  // ../../packages/contracts/src/enums.ts
47
47
  import { z as z2 } from "zod";
48
- var HarnessTypeSchema, PHASE_1_TYPES, TierSchema, TIER_OF_TYPE, SortModeSchema, TimeWindowSchema, VisibilitySchema, ReviewStatusSchema, PublishStatusSchema, DependencyKindSchema, OAuthProviderSchema, UserRoleSchema, ReportReasonSchema;
48
+ var HarnessTypeSchema, PHASE_1_TYPES, TierSchema, TIER_OF_TYPE, CAPABILITIES_OF_TYPE, SortModeSchema, TimeWindowSchema, VisibilitySchema, ReviewStatusSchema, PublishStatusSchema, DependencyKindSchema, OAuthProviderSchema, UserRoleSchema, ReportReasonSchema;
49
49
  var init_enums = __esm({
50
50
  "../../packages/contracts/src/enums.ts"() {
51
51
  "use strict";
@@ -77,6 +77,16 @@ var init_enums = __esm({
77
77
  "mdc-rule": "C",
78
78
  plugin: "D"
79
79
  };
80
+ CAPABILITIES_OF_TYPE = {
81
+ skill: { jit_eligible: true, executable: false },
82
+ "project-rules": { jit_eligible: true, executable: false },
83
+ "custom-agent": { jit_eligible: true, executable: false },
84
+ "scoped-rule": { jit_eligible: true, executable: false },
85
+ "mdc-rule": { jit_eligible: true, executable: false },
86
+ "mcp-server": { jit_eligible: false, executable: true },
87
+ hook: { jit_eligible: false, executable: true },
88
+ plugin: { jit_eligible: false, executable: true }
89
+ };
80
90
  SortModeSchema = z2.enum([
81
91
  "stars",
82
92
  "downloads",
@@ -281,6 +291,14 @@ var init_package = __esm({
281
291
  tools_compatible: z6.array(ToolSlugSchema),
282
292
  tags: z6.array(TagSchema),
283
293
  agent_skills_compliant: z6.boolean().optional(),
294
+ /**
295
+ * Capability flags — explicit declaration of behavior. Defaults derive from
296
+ * `type` via `CAPABILITIES_OF_TYPE`; frontmatter MAY override. Optional in
297
+ * the schema so old API responses (pre-0.2.0) parse cleanly; consumers that
298
+ * need a value should fall back to `CAPABILITIES_OF_TYPE[type]`.
299
+ */
300
+ jit_eligible: z6.boolean().optional(),
301
+ executable: z6.boolean().optional(),
284
302
  review_status: ReviewStatusSchema,
285
303
  visibility: VisibilitySchema,
286
304
  installed_locally: z6.boolean().optional(),
@@ -1154,6 +1172,8 @@ import ora from "ora";
1154
1172
  var jsonMode = false;
1155
1173
  function setJsonMode(on) {
1156
1174
  jsonMode = on;
1175
+ if (on) process.env["KORDIS_JSON"] = "1";
1176
+ else delete process.env["KORDIS_JSON"];
1157
1177
  }
1158
1178
  function isJsonMode() {
1159
1179
  return jsonMode;
@@ -1374,18 +1394,21 @@ function parseFrontmatter(source) {
1374
1394
  parsed = parseYaml(raw);
1375
1395
  } catch (err) {
1376
1396
  const msg = err.message;
1377
- const hint = msg.includes("Nested mapping") || msg.includes("compact mapping") ? ' Hint: if a field value (e.g. description) contains a colon, wrap the entire value in quotes: description: "Use when... Trigger: X".' : "";
1378
- throw new FrontmatterParseError(
1379
- `YAML frontmatter is malformed: ${msg}${hint}`,
1380
- { cause: err }
1381
- );
1397
+ const hint = msg.includes("Nested mapping") || msg.includes("compact mapping") ? 'If the value contains ":" wrap it in quotes, e.g. description: "Use when X. Trigger: Y"' : 'Frontmatter must be valid YAML between two "---" fences. Validate at https://yamllint.com';
1398
+ throw new FrontmatterParseError(`YAML frontmatter is malformed: ${msg}`, {
1399
+ cause: err,
1400
+ hint
1401
+ });
1382
1402
  }
1383
1403
  if (parsed === null || parsed === void 0) {
1384
1404
  return { frontmatter: {}, body: stripped.slice(match[0].length), bodyOffset: match[0].length };
1385
1405
  }
1386
1406
  if (typeof parsed !== "object" || Array.isArray(parsed)) {
1387
1407
  throw new FrontmatterParseError(
1388
- "YAML frontmatter must decode to an object, got " + typeof parsed
1408
+ "YAML frontmatter must decode to an object, got " + typeof parsed,
1409
+ {
1410
+ hint: "Frontmatter must be a YAML mapping (key: value pairs), not a list or scalar."
1411
+ }
1389
1412
  );
1390
1413
  }
1391
1414
  return {
@@ -1395,9 +1418,12 @@ function parseFrontmatter(source) {
1395
1418
  };
1396
1419
  }
1397
1420
  var FrontmatterParseError = class extends Error {
1421
+ /** Agent-actionable fix snippet, ready to render under the error. */
1422
+ hint;
1398
1423
  constructor(message, options) {
1399
1424
  super(message, options);
1400
1425
  this.name = "FrontmatterParseError";
1426
+ if (options?.hint) this.hint = options.hint;
1401
1427
  }
1402
1428
  };
1403
1429
 
@@ -1428,7 +1454,7 @@ function pushCommand() {
1428
1454
  const v = fm["name"];
1429
1455
  if (typeof v === "string") nameFromFm = v;
1430
1456
  } catch (err) {
1431
- throw new Error(err.message);
1457
+ throw err instanceof Error ? err : new Error(String(err));
1432
1458
  }
1433
1459
  let targetUser = null;
1434
1460
  let targetSlug = null;
@@ -1820,37 +1846,43 @@ function validatePackageSource(source) {
1820
1846
  rawFrontmatter = parsed.frontmatter;
1821
1847
  } catch (err) {
1822
1848
  if (err instanceof FrontmatterParseError) {
1823
- return earlyExit(flag("malformed_frontmatter", err.message));
1849
+ const extra = err.hint ? { hint: err.hint } : {};
1850
+ return earlyExit(flag("malformed_frontmatter", err.message, extra));
1824
1851
  }
1825
1852
  throw err;
1826
1853
  }
1827
1854
  if (!rawFrontmatter) {
1828
- return earlyExit(flag("missing_frontmatter", "package must start with a YAML frontmatter block"));
1855
+ return earlyExit(
1856
+ flag("missing_frontmatter", "package must start with a YAML frontmatter block", {
1857
+ hint: "Add a YAML block at the top:\n---\ntype: skill\nname: my-skill\ndescription: ...\n---"
1858
+ })
1859
+ );
1829
1860
  }
1830
1861
  const typeValue = rawFrontmatter["type"];
1831
1862
  if (typeof typeValue !== "string") {
1832
1863
  return earlyExit(
1833
- flag("missing_required_frontmatter", "`type` is required in frontmatter", { field: "type" })
1864
+ flag("missing_required_frontmatter", "`type` is required in frontmatter", {
1865
+ field: "type",
1866
+ hint: "Add type: skill (or one of: mcp-server, project-rules, custom-agent, hook)"
1867
+ })
1834
1868
  );
1835
1869
  }
1836
1870
  const typeParse = HarnessTypeSchema.safeParse(typeValue);
1837
1871
  if (!typeParse.success) {
1838
1872
  return earlyExit(
1839
- flag(
1840
- "unknown_type",
1841
- `type "${typeValue}" is not a recognised Harness type`,
1842
- { field: "type" }
1843
- )
1873
+ flag("unknown_type", `type "${typeValue}" is not a recognised Harness type`, {
1874
+ field: "type",
1875
+ hint: "Allowed types: skill, mcp-server, project-rules, custom-agent, hook"
1876
+ })
1844
1877
  );
1845
1878
  }
1846
1879
  const type = typeParse.data;
1847
1880
  if (!PHASE_1_TYPES.includes(type)) {
1848
1881
  flags.push(
1849
- flag(
1850
- "type_not_phase1",
1851
- `type "${type}" is planned for Phase 2+ and is not yet accepted`,
1852
- { field: "type" }
1853
- )
1882
+ flag("type_not_phase1", `type "${type}" is planned for Phase 2+ and is not yet accepted`, {
1883
+ field: "type",
1884
+ hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook"
1885
+ })
1854
1886
  );
1855
1887
  return earlyExit(null, { flags, body });
1856
1888
  }
@@ -1858,22 +1890,69 @@ function validatePackageSource(source) {
1858
1890
  type: _discard_type,
1859
1891
  depends: _discard_depends,
1860
1892
  recommends: _discard_recommends,
1893
+ jit_eligible: rawJitEligible,
1894
+ executable: rawExecutable,
1861
1895
  ...typeSpecific
1862
1896
  } = rawFrontmatter;
1863
1897
  void _discard_type;
1864
1898
  void _discard_depends;
1865
1899
  void _discard_recommends;
1900
+ let jitOverride;
1901
+ let execOverride;
1902
+ if (rawJitEligible !== void 0) {
1903
+ if (typeof rawJitEligible !== "boolean") {
1904
+ flags.push(
1905
+ flag(
1906
+ "invalid_frontmatter",
1907
+ "jit_eligible must be a boolean (true / false)",
1908
+ {
1909
+ field: "jit_eligible",
1910
+ hint: "Example: jit_eligible: true"
1911
+ }
1912
+ )
1913
+ );
1914
+ } else {
1915
+ jitOverride = rawJitEligible;
1916
+ }
1917
+ }
1918
+ if (rawExecutable !== void 0) {
1919
+ if (typeof rawExecutable !== "boolean") {
1920
+ flags.push(
1921
+ flag("invalid_frontmatter", "executable must be a boolean (true / false)", {
1922
+ field: "executable",
1923
+ hint: "Example: executable: false"
1924
+ })
1925
+ );
1926
+ } else {
1927
+ execOverride = rawExecutable;
1928
+ }
1929
+ }
1866
1930
  const result = dispatchValidation(type, typeSpecific);
1867
1931
  if (!result.ok) {
1868
1932
  flags.push(...result.flags);
1869
1933
  return earlyExit(null, { flags, body });
1870
1934
  }
1935
+ const defaults = CAPABILITIES_OF_TYPE[type];
1936
+ const capabilities = {
1937
+ jit_eligible: jitOverride ?? defaults.jit_eligible,
1938
+ executable: execOverride ?? defaults.executable
1939
+ };
1871
1940
  const common = {
1872
1941
  title: typeof rawFrontmatter["title"] === "string" ? rawFrontmatter["title"] : typeof rawFrontmatter["name"] === "string" ? rawFrontmatter["name"] : null,
1873
1942
  description: typeof rawFrontmatter["description"] === "string" ? rawFrontmatter["description"] : null
1874
1943
  };
1875
- if (!common.title) flags.push(flag("missing_title", "title (or name) is required"));
1876
- if (!common.description) flags.push(flag("missing_description", "description is required"));
1944
+ if (!common.title)
1945
+ flags.push(
1946
+ flag("missing_title", "title (or name) is required", {
1947
+ hint: 'Add title: "Human Readable Title" (or rely on name: ... for skills)'
1948
+ })
1949
+ );
1950
+ if (!common.description)
1951
+ flags.push(
1952
+ flag("missing_description", "description is required", {
1953
+ hint: "Add a description field (1\u20132048 chars) explaining when to invoke this package."
1954
+ })
1955
+ );
1877
1956
  let depends = [];
1878
1957
  let recommends = [];
1879
1958
  try {
@@ -1883,7 +1962,12 @@ function validatePackageSource(source) {
1883
1962
  kind: DependencyKindSchema.parse("required")
1884
1963
  }));
1885
1964
  } catch (err) {
1886
- flags.push(flag("invalid_depends", err.message, { field: "depends" }));
1965
+ flags.push(
1966
+ flag("invalid_depends", err.message, {
1967
+ field: "depends",
1968
+ hint: 'Each entry must be "{username}/{slug}" optionally with "@^1.2.0". Example: alice/json-pretty@^1.0.0'
1969
+ })
1970
+ );
1887
1971
  }
1888
1972
  try {
1889
1973
  recommends = normaliseDepList(rawFrontmatter["recommends"]).map((d) => ({
@@ -1892,13 +1976,26 @@ function validatePackageSource(source) {
1892
1976
  kind: DependencyKindSchema.parse("recommended")
1893
1977
  }));
1894
1978
  } catch (err) {
1895
- flags.push(flag("invalid_recommends", err.message, { field: "recommends" }));
1979
+ flags.push(
1980
+ flag("invalid_recommends", err.message, {
1981
+ field: "recommends",
1982
+ hint: 'Each entry must be "{username}/{slug}" optionally with "@^1.2.0".'
1983
+ })
1984
+ );
1896
1985
  }
1897
1986
  if (body.trim().length < 50) {
1898
- flags.push(flag("content_too_short", "body must be at least 50 non-whitespace characters"));
1987
+ flags.push(
1988
+ flag("content_too_short", "body must be at least 50 non-whitespace characters", {
1989
+ hint: 'Add an "## Instructions" section explaining when and how to use this package.'
1990
+ })
1991
+ );
1899
1992
  }
1900
1993
  if (body.length > 1e5) {
1901
- flags.push(flag("content_too_long", "body exceeds 100 kB cap"));
1994
+ flags.push(
1995
+ flag("content_too_long", "body exceeds 100 kB cap", {
1996
+ hint: "Split long examples or transcripts into asset files under references/."
1997
+ })
1998
+ );
1902
1999
  }
1903
2000
  const ok = flags.every((f) => !BLOCKING_CODES.has(f.code));
1904
2001
  return {
@@ -1908,6 +2005,7 @@ function validatePackageSource(source) {
1908
2005
  frontmatter: result.frontmatter,
1909
2006
  body,
1910
2007
  common,
2008
+ capabilities,
1911
2009
  depends,
1912
2010
  recommends,
1913
2011
  flags
@@ -1938,6 +2036,7 @@ function earlyExit(firstFlag, override = {}) {
1938
2036
  frontmatter: null,
1939
2037
  body: "",
1940
2038
  common: { title: null, description: null },
2039
+ capabilities: null,
1941
2040
  depends: [],
1942
2041
  recommends: [],
1943
2042
  flags: firstFlag ? [firstFlag] : [],
@@ -1976,7 +2075,10 @@ function dispatchValidation(type, raw) {
1976
2075
  ok: false,
1977
2076
  frontmatter: null,
1978
2077
  flags: [
1979
- flag("type_not_phase1", `type "${type}" not accepted in Phase 1`, { field: "type" })
2078
+ flag("type_not_phase1", `type "${type}" not accepted in Phase 1`, {
2079
+ field: "type",
2080
+ hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook"
2081
+ })
1980
2082
  ]
1981
2083
  };
1982
2084
  }
@@ -1985,16 +2087,29 @@ function zodFail(issues) {
1985
2087
  return {
1986
2088
  ok: false,
1987
2089
  frontmatter: null,
1988
- flags: issues.map(
1989
- (i) => flag("invalid_frontmatter", i.message, { field: i.path.join(".") || void 0 })
1990
- )
2090
+ flags: issues.map((i) => {
2091
+ const field = i.path.join(".") || void 0;
2092
+ const msg = i.message.toLowerCase();
2093
+ let hint;
2094
+ if (field === "name" && msg.includes("lowercase")) {
2095
+ hint = 'name must be lowercase-with-hyphens, e.g. "my-cool-skill" not "MyCoolSkill"';
2096
+ } else if (field === "description" && (msg.includes("big") || msg.includes("large") || msg.includes("long"))) {
2097
+ hint = "description must be 1\u20132048 chars; current is too long. Move detail into the body.";
2098
+ } else if (field === "description" && msg.includes("small")) {
2099
+ hint = "description must be 1\u20132048 chars; provide a one-paragraph summary.";
2100
+ }
2101
+ const extra = {};
2102
+ if (field) extra.field = field;
2103
+ if (hint) extra.hint = hint;
2104
+ return flag("invalid_frontmatter", i.message, extra);
2105
+ })
1991
2106
  };
1992
2107
  }
1993
2108
 
1994
2109
  // ../../packages/translators/src/skill/identity.ts
1995
2110
  function buildSkillFile(input, outputPath) {
1996
2111
  const body = input.body.trimStart();
1997
- const fm = input.frontmatter;
2112
+ const fm = input.frontmatter["version"] === void 0 && input.version ? { ...input.frontmatter, version: input.version } : input.frontmatter;
1998
2113
  const name = typeof fm["name"] === "string" ? fm["name"] : input.fullSlug.split("/")[1];
1999
2114
  const frontmatter = ["---"];
2000
2115
  for (const [k, v] of Object.entries(fm)) {
@@ -2628,6 +2743,7 @@ function readCommand() {
2628
2743
  }
2629
2744
 
2630
2745
  // src/commands/info.ts
2746
+ init_src();
2631
2747
  import { Command as Command7 } from "commander";
2632
2748
  function infoCommand() {
2633
2749
  return new Command7("info").description("show details about a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (fullSlug) => {
@@ -2635,6 +2751,9 @@ function infoCommand() {
2635
2751
  if (!username || !slug) throw new Error('full_slug must be "{username}/{slug}"');
2636
2752
  const bundle = await useClient();
2637
2753
  const detail = await bundle.api.getPackage(username, slug);
2754
+ const typeDefaults = CAPABILITIES_OF_TYPE[detail.type];
2755
+ const jitEligible = detail.jit_eligible ?? typeDefaults.jit_eligible;
2756
+ const executable = detail.executable ?? typeDefaults.executable;
2638
2757
  printDual({
2639
2758
  json: detail,
2640
2759
  human: () => {
@@ -2642,6 +2761,12 @@ function infoCommand() {
2642
2761
  printHuman(" " + detail.description);
2643
2762
  printHuman("");
2644
2763
  printHuman(` ${c.cyan("Type")}: ${detail.type} (tier ${detail.tier})`);
2764
+ printHuman(
2765
+ ` ${c.cyan("JIT-readable")}: ${jitEligible ? c.green("yes") : c.red("no")}`
2766
+ );
2767
+ printHuman(
2768
+ ` ${c.cyan("Executable")}: ${executable ? c.yellow("yes") : c.gray("no")}`
2769
+ );
2645
2770
  printHuman(` ${c.cyan("Author")}: ${detail.author.username}`);
2646
2771
  printHuman(` ${c.cyan("Stats")}: \u2605 ${detail.stars} \xB7 \u21E3 ${detail.downloads}`);
2647
2772
  if (detail.tools_compatible.length > 0) {
@@ -3370,6 +3495,11 @@ function diffCommand() {
3370
3495
  printHuman("");
3371
3496
  if (!diff.unified_diff) {
3372
3497
  printHuman(c.dim("(no content changes between these versions)"));
3498
+ printHuman(
3499
+ c.dim(
3500
+ " \u2192 likely a metadata-only bump: description, deps, license, or version field"
3501
+ )
3502
+ );
3373
3503
  return;
3374
3504
  }
3375
3505
  for (const line of diff.unified_diff.split("\n")) {
@@ -3441,6 +3571,11 @@ function handleError(err) {
3441
3571
  printHuman(c.red(`\u2717 ${err.code}: ${err.message}`));
3442
3572
  for (const f of err.flags) {
3443
3573
  printHuman(c.red(` - ${f.code}${f.message ? `: ${f.message}` : ""}`));
3574
+ if (f.hint) {
3575
+ for (const line of f.hint.split("\n")) {
3576
+ printHuman(c.gray(` \u2192 ${line}`));
3577
+ }
3578
+ }
3444
3579
  }
3445
3580
  if (err.retryAfter !== void 0) {
3446
3581
  printHuman(c.yellow(` retry after: ${err.retryAfter}s`));
@@ -3457,8 +3592,22 @@ function handleError(err) {
3457
3592
  process.exit(err.exitCode());
3458
3593
  }
3459
3594
  if (err instanceof Error) {
3460
- if (isJsonMode()) printJson({ error: { code: "generic_failure", message: err.message } });
3461
- else printHuman(c.red(`\u2717 ${err.message}`));
3595
+ const hint = err.hint;
3596
+ const hintStr = typeof hint === "string" ? hint : void 0;
3597
+ if (isJsonMode()) {
3598
+ printJson({
3599
+ error: {
3600
+ code: "generic_failure",
3601
+ message: err.message,
3602
+ ...hintStr ? { hint: hintStr } : {}
3603
+ }
3604
+ });
3605
+ } else {
3606
+ printHuman(c.red(`\u2717 ${err.message}`));
3607
+ if (hintStr) {
3608
+ for (const line of hintStr.split("\n")) printHuman(c.gray(` \u2192 ${line}`));
3609
+ }
3610
+ }
3462
3611
  process.exit(1);
3463
3612
  }
3464
3613
  if (isJsonMode()) printJson({ error: { code: "generic_failure", message: String(err) } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayul/kordis",
3
- "version": "0.1.17",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "Command-line client for the Kordis registry — discover, install, and publish agentic-coding packages.",
6
6
  "license": "MIT",