@rayul/kordis 0.1.16 → 0.2.0

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 +176 -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(),
@@ -1374,18 +1392,21 @@ function parseFrontmatter(source) {
1374
1392
  parsed = parseYaml(raw);
1375
1393
  } catch (err) {
1376
1394
  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
- );
1395
+ 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';
1396
+ throw new FrontmatterParseError(`YAML frontmatter is malformed: ${msg}`, {
1397
+ cause: err,
1398
+ hint
1399
+ });
1382
1400
  }
1383
1401
  if (parsed === null || parsed === void 0) {
1384
1402
  return { frontmatter: {}, body: stripped.slice(match[0].length), bodyOffset: match[0].length };
1385
1403
  }
1386
1404
  if (typeof parsed !== "object" || Array.isArray(parsed)) {
1387
1405
  throw new FrontmatterParseError(
1388
- "YAML frontmatter must decode to an object, got " + typeof parsed
1406
+ "YAML frontmatter must decode to an object, got " + typeof parsed,
1407
+ {
1408
+ hint: "Frontmatter must be a YAML mapping (key: value pairs), not a list or scalar."
1409
+ }
1389
1410
  );
1390
1411
  }
1391
1412
  return {
@@ -1395,9 +1416,12 @@ function parseFrontmatter(source) {
1395
1416
  };
1396
1417
  }
1397
1418
  var FrontmatterParseError = class extends Error {
1419
+ /** Agent-actionable fix snippet, ready to render under the error. */
1420
+ hint;
1398
1421
  constructor(message, options) {
1399
1422
  super(message, options);
1400
1423
  this.name = "FrontmatterParseError";
1424
+ if (options?.hint) this.hint = options.hint;
1401
1425
  }
1402
1426
  };
1403
1427
 
@@ -1428,7 +1452,7 @@ function pushCommand() {
1428
1452
  const v = fm["name"];
1429
1453
  if (typeof v === "string") nameFromFm = v;
1430
1454
  } catch (err) {
1431
- throw new Error(err.message);
1455
+ throw err instanceof Error ? err : new Error(String(err));
1432
1456
  }
1433
1457
  let targetUser = null;
1434
1458
  let targetSlug = null;
@@ -1820,37 +1844,43 @@ function validatePackageSource(source) {
1820
1844
  rawFrontmatter = parsed.frontmatter;
1821
1845
  } catch (err) {
1822
1846
  if (err instanceof FrontmatterParseError) {
1823
- return earlyExit(flag("malformed_frontmatter", err.message));
1847
+ const extra = err.hint ? { hint: err.hint } : {};
1848
+ return earlyExit(flag("malformed_frontmatter", err.message, extra));
1824
1849
  }
1825
1850
  throw err;
1826
1851
  }
1827
1852
  if (!rawFrontmatter) {
1828
- return earlyExit(flag("missing_frontmatter", "package must start with a YAML frontmatter block"));
1853
+ return earlyExit(
1854
+ flag("missing_frontmatter", "package must start with a YAML frontmatter block", {
1855
+ hint: "Add a YAML block at the top:\n---\ntype: skill\nname: my-skill\ndescription: ...\n---"
1856
+ })
1857
+ );
1829
1858
  }
1830
1859
  const typeValue = rawFrontmatter["type"];
1831
1860
  if (typeof typeValue !== "string") {
1832
1861
  return earlyExit(
1833
- flag("missing_required_frontmatter", "`type` is required in frontmatter", { field: "type" })
1862
+ flag("missing_required_frontmatter", "`type` is required in frontmatter", {
1863
+ field: "type",
1864
+ hint: "Add type: skill (or one of: mcp-server, project-rules, custom-agent, hook)"
1865
+ })
1834
1866
  );
1835
1867
  }
1836
1868
  const typeParse = HarnessTypeSchema.safeParse(typeValue);
1837
1869
  if (!typeParse.success) {
1838
1870
  return earlyExit(
1839
- flag(
1840
- "unknown_type",
1841
- `type "${typeValue}" is not a recognised Harness type`,
1842
- { field: "type" }
1843
- )
1871
+ flag("unknown_type", `type "${typeValue}" is not a recognised Harness type`, {
1872
+ field: "type",
1873
+ hint: "Allowed types: skill, mcp-server, project-rules, custom-agent, hook"
1874
+ })
1844
1875
  );
1845
1876
  }
1846
1877
  const type = typeParse.data;
1847
1878
  if (!PHASE_1_TYPES.includes(type)) {
1848
1879
  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
- )
1880
+ flag("type_not_phase1", `type "${type}" is planned for Phase 2+ and is not yet accepted`, {
1881
+ field: "type",
1882
+ hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook"
1883
+ })
1854
1884
  );
1855
1885
  return earlyExit(null, { flags, body });
1856
1886
  }
@@ -1858,22 +1888,69 @@ function validatePackageSource(source) {
1858
1888
  type: _discard_type,
1859
1889
  depends: _discard_depends,
1860
1890
  recommends: _discard_recommends,
1891
+ jit_eligible: rawJitEligible,
1892
+ executable: rawExecutable,
1861
1893
  ...typeSpecific
1862
1894
  } = rawFrontmatter;
1863
1895
  void _discard_type;
1864
1896
  void _discard_depends;
1865
1897
  void _discard_recommends;
1898
+ let jitOverride;
1899
+ let execOverride;
1900
+ if (rawJitEligible !== void 0) {
1901
+ if (typeof rawJitEligible !== "boolean") {
1902
+ flags.push(
1903
+ flag(
1904
+ "invalid_frontmatter",
1905
+ "jit_eligible must be a boolean (true / false)",
1906
+ {
1907
+ field: "jit_eligible",
1908
+ hint: "Example: jit_eligible: true"
1909
+ }
1910
+ )
1911
+ );
1912
+ } else {
1913
+ jitOverride = rawJitEligible;
1914
+ }
1915
+ }
1916
+ if (rawExecutable !== void 0) {
1917
+ if (typeof rawExecutable !== "boolean") {
1918
+ flags.push(
1919
+ flag("invalid_frontmatter", "executable must be a boolean (true / false)", {
1920
+ field: "executable",
1921
+ hint: "Example: executable: false"
1922
+ })
1923
+ );
1924
+ } else {
1925
+ execOverride = rawExecutable;
1926
+ }
1927
+ }
1866
1928
  const result = dispatchValidation(type, typeSpecific);
1867
1929
  if (!result.ok) {
1868
1930
  flags.push(...result.flags);
1869
1931
  return earlyExit(null, { flags, body });
1870
1932
  }
1933
+ const defaults = CAPABILITIES_OF_TYPE[type];
1934
+ const capabilities = {
1935
+ jit_eligible: jitOverride ?? defaults.jit_eligible,
1936
+ executable: execOverride ?? defaults.executable
1937
+ };
1871
1938
  const common = {
1872
1939
  title: typeof rawFrontmatter["title"] === "string" ? rawFrontmatter["title"] : typeof rawFrontmatter["name"] === "string" ? rawFrontmatter["name"] : null,
1873
1940
  description: typeof rawFrontmatter["description"] === "string" ? rawFrontmatter["description"] : null
1874
1941
  };
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"));
1942
+ if (!common.title)
1943
+ flags.push(
1944
+ flag("missing_title", "title (or name) is required", {
1945
+ hint: 'Add title: "Human Readable Title" (or rely on name: ... for skills)'
1946
+ })
1947
+ );
1948
+ if (!common.description)
1949
+ flags.push(
1950
+ flag("missing_description", "description is required", {
1951
+ hint: "Add a description field (1\u20132048 chars) explaining when to invoke this package."
1952
+ })
1953
+ );
1877
1954
  let depends = [];
1878
1955
  let recommends = [];
1879
1956
  try {
@@ -1883,7 +1960,12 @@ function validatePackageSource(source) {
1883
1960
  kind: DependencyKindSchema.parse("required")
1884
1961
  }));
1885
1962
  } catch (err) {
1886
- flags.push(flag("invalid_depends", err.message, { field: "depends" }));
1963
+ flags.push(
1964
+ flag("invalid_depends", err.message, {
1965
+ field: "depends",
1966
+ hint: 'Each entry must be "{username}/{slug}" optionally with "@^1.2.0". Example: alice/json-pretty@^1.0.0'
1967
+ })
1968
+ );
1887
1969
  }
1888
1970
  try {
1889
1971
  recommends = normaliseDepList(rawFrontmatter["recommends"]).map((d) => ({
@@ -1892,13 +1974,26 @@ function validatePackageSource(source) {
1892
1974
  kind: DependencyKindSchema.parse("recommended")
1893
1975
  }));
1894
1976
  } catch (err) {
1895
- flags.push(flag("invalid_recommends", err.message, { field: "recommends" }));
1977
+ flags.push(
1978
+ flag("invalid_recommends", err.message, {
1979
+ field: "recommends",
1980
+ hint: 'Each entry must be "{username}/{slug}" optionally with "@^1.2.0".'
1981
+ })
1982
+ );
1896
1983
  }
1897
1984
  if (body.trim().length < 50) {
1898
- flags.push(flag("content_too_short", "body must be at least 50 non-whitespace characters"));
1985
+ flags.push(
1986
+ flag("content_too_short", "body must be at least 50 non-whitespace characters", {
1987
+ hint: 'Add an "## Instructions" section explaining when and how to use this package.'
1988
+ })
1989
+ );
1899
1990
  }
1900
1991
  if (body.length > 1e5) {
1901
- flags.push(flag("content_too_long", "body exceeds 100 kB cap"));
1992
+ flags.push(
1993
+ flag("content_too_long", "body exceeds 100 kB cap", {
1994
+ hint: "Split long examples or transcripts into asset files under references/."
1995
+ })
1996
+ );
1902
1997
  }
1903
1998
  const ok = flags.every((f) => !BLOCKING_CODES.has(f.code));
1904
1999
  return {
@@ -1908,6 +2003,7 @@ function validatePackageSource(source) {
1908
2003
  frontmatter: result.frontmatter,
1909
2004
  body,
1910
2005
  common,
2006
+ capabilities,
1911
2007
  depends,
1912
2008
  recommends,
1913
2009
  flags
@@ -1938,6 +2034,7 @@ function earlyExit(firstFlag, override = {}) {
1938
2034
  frontmatter: null,
1939
2035
  body: "",
1940
2036
  common: { title: null, description: null },
2037
+ capabilities: null,
1941
2038
  depends: [],
1942
2039
  recommends: [],
1943
2040
  flags: firstFlag ? [firstFlag] : [],
@@ -1976,7 +2073,10 @@ function dispatchValidation(type, raw) {
1976
2073
  ok: false,
1977
2074
  frontmatter: null,
1978
2075
  flags: [
1979
- flag("type_not_phase1", `type "${type}" not accepted in Phase 1`, { field: "type" })
2076
+ flag("type_not_phase1", `type "${type}" not accepted in Phase 1`, {
2077
+ field: "type",
2078
+ hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook"
2079
+ })
1980
2080
  ]
1981
2081
  };
1982
2082
  }
@@ -1985,9 +2085,22 @@ function zodFail(issues) {
1985
2085
  return {
1986
2086
  ok: false,
1987
2087
  frontmatter: null,
1988
- flags: issues.map(
1989
- (i) => flag("invalid_frontmatter", i.message, { field: i.path.join(".") || void 0 })
1990
- )
2088
+ flags: issues.map((i) => {
2089
+ const field = i.path.join(".") || void 0;
2090
+ const msg = i.message.toLowerCase();
2091
+ let hint;
2092
+ if (field === "name" && msg.includes("lowercase")) {
2093
+ hint = 'name must be lowercase-with-hyphens, e.g. "my-cool-skill" not "MyCoolSkill"';
2094
+ } else if (field === "description" && (msg.includes("big") || msg.includes("large") || msg.includes("long"))) {
2095
+ hint = "description must be 1\u20132048 chars; current is too long. Move detail into the body.";
2096
+ } else if (field === "description" && msg.includes("small")) {
2097
+ hint = "description must be 1\u20132048 chars; provide a one-paragraph summary.";
2098
+ }
2099
+ const extra = {};
2100
+ if (field) extra.field = field;
2101
+ if (hint) extra.hint = hint;
2102
+ return flag("invalid_frontmatter", i.message, extra);
2103
+ })
1991
2104
  };
1992
2105
  }
1993
2106
 
@@ -2628,6 +2741,7 @@ function readCommand() {
2628
2741
  }
2629
2742
 
2630
2743
  // src/commands/info.ts
2744
+ init_src();
2631
2745
  import { Command as Command7 } from "commander";
2632
2746
  function infoCommand() {
2633
2747
  return new Command7("info").description("show details about a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (fullSlug) => {
@@ -2635,6 +2749,9 @@ function infoCommand() {
2635
2749
  if (!username || !slug) throw new Error('full_slug must be "{username}/{slug}"');
2636
2750
  const bundle = await useClient();
2637
2751
  const detail = await bundle.api.getPackage(username, slug);
2752
+ const typeDefaults = CAPABILITIES_OF_TYPE[detail.type];
2753
+ const jitEligible = detail.jit_eligible ?? typeDefaults.jit_eligible;
2754
+ const executable = detail.executable ?? typeDefaults.executable;
2638
2755
  printDual({
2639
2756
  json: detail,
2640
2757
  human: () => {
@@ -2642,6 +2759,12 @@ function infoCommand() {
2642
2759
  printHuman(" " + detail.description);
2643
2760
  printHuman("");
2644
2761
  printHuman(` ${c.cyan("Type")}: ${detail.type} (tier ${detail.tier})`);
2762
+ printHuman(
2763
+ ` ${c.cyan("JIT-readable")}: ${jitEligible ? c.green("yes") : c.red("no")}`
2764
+ );
2765
+ printHuman(
2766
+ ` ${c.cyan("Executable")}: ${executable ? c.yellow("yes") : c.gray("no")}`
2767
+ );
2645
2768
  printHuman(` ${c.cyan("Author")}: ${detail.author.username}`);
2646
2769
  printHuman(` ${c.cyan("Stats")}: \u2605 ${detail.stars} \xB7 \u21E3 ${detail.downloads}`);
2647
2770
  if (detail.tools_compatible.length > 0) {
@@ -3329,7 +3452,7 @@ function diffCommand() {
3329
3452
  let fullSlug = fullSlugArg;
3330
3453
  let from = fromArg;
3331
3454
  let to = toArg;
3332
- const rangeMatch = /^(.+)@([^.]+)\.\.(.+)$/.exec(fullSlugArg);
3455
+ const rangeMatch = /^(.+)@(.+?)\.\.(.+)$/.exec(fullSlugArg);
3333
3456
  if (rangeMatch) {
3334
3457
  fullSlug = rangeMatch[1];
3335
3458
  from = rangeMatch[2];
@@ -3441,6 +3564,11 @@ function handleError(err) {
3441
3564
  printHuman(c.red(`\u2717 ${err.code}: ${err.message}`));
3442
3565
  for (const f of err.flags) {
3443
3566
  printHuman(c.red(` - ${f.code}${f.message ? `: ${f.message}` : ""}`));
3567
+ if (f.hint) {
3568
+ for (const line of f.hint.split("\n")) {
3569
+ printHuman(c.gray(` \u2192 ${line}`));
3570
+ }
3571
+ }
3444
3572
  }
3445
3573
  if (err.retryAfter !== void 0) {
3446
3574
  printHuman(c.yellow(` retry after: ${err.retryAfter}s`));
@@ -3457,8 +3585,22 @@ function handleError(err) {
3457
3585
  process.exit(err.exitCode());
3458
3586
  }
3459
3587
  if (err instanceof Error) {
3460
- if (isJsonMode()) printJson({ error: { code: "generic_failure", message: err.message } });
3461
- else printHuman(c.red(`\u2717 ${err.message}`));
3588
+ const hint = err.hint;
3589
+ const hintStr = typeof hint === "string" ? hint : void 0;
3590
+ if (isJsonMode()) {
3591
+ printJson({
3592
+ error: {
3593
+ code: "generic_failure",
3594
+ message: err.message,
3595
+ ...hintStr ? { hint: hintStr } : {}
3596
+ }
3597
+ });
3598
+ } else {
3599
+ printHuman(c.red(`\u2717 ${err.message}`));
3600
+ if (hintStr) {
3601
+ for (const line of hintStr.split("\n")) printHuman(c.gray(` \u2192 ${line}`));
3602
+ }
3603
+ }
3462
3604
  process.exit(1);
3463
3605
  }
3464
3606
  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.16",
3
+ "version": "0.2.0",
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",