@rayul/kordis 0.11.1 → 0.12.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.
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, CAPABILITIES_OF_TYPE, SortModeSchema, TimeWindowSchema, VisibilitySchema, ReviewStatusSchema, PublishStatusSchema, DependencyKindSchema, OAuthProviderSchema, UserRoleSchema, ReportReasonSchema;
48
+ var HarnessTypeSchema, PHASE_1_TYPES, TierSchema, TIER_OF_TYPE, CAPABILITIES_OF_TYPE, FamilySchema, LineageKindSchema, TrustTierSchema, 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";
@@ -55,6 +55,7 @@ var init_enums = __esm({
55
55
  "skill",
56
56
  "custom-agent",
57
57
  "hook",
58
+ "judgment-pattern",
58
59
  "scoped-rule",
59
60
  "mdc-rule",
60
61
  "plugin"
@@ -64,13 +65,17 @@ var init_enums = __esm({
64
65
  "project-rules",
65
66
  "skill",
66
67
  "custom-agent",
67
- "hook"
68
+ "hook",
69
+ "judgment-pattern"
68
70
  ];
69
71
  TierSchema = z2.enum(["A", "B", "C", "D"]);
70
72
  TIER_OF_TYPE = {
71
73
  "mcp-server": "A",
72
74
  "project-rules": "A",
73
75
  skill: "A",
76
+ // Judgment is the most portable unit of all — it carries no tool-specific
77
+ // wiring, only reasoning. Tier A so it surfaces in every read/search gate.
78
+ "judgment-pattern": "A",
74
79
  "custom-agent": "B",
75
80
  "scoped-rule": "B",
76
81
  hook: "C",
@@ -80,6 +85,8 @@ var init_enums = __esm({
80
85
  CAPABILITIES_OF_TYPE = {
81
86
  skill: { jit_eligible: true, executable: false },
82
87
  "project-rules": { jit_eligible: true, executable: false },
88
+ // A judgment is pure text-to-read — never installs or runs anything.
89
+ "judgment-pattern": { jit_eligible: true, executable: false },
83
90
  "custom-agent": { jit_eligible: true, executable: false },
84
91
  "scoped-rule": { jit_eligible: true, executable: false },
85
92
  "mdc-rule": { jit_eligible: true, executable: false },
@@ -87,6 +94,14 @@ var init_enums = __esm({
87
94
  hook: { jit_eligible: false, executable: true },
88
95
  plugin: { jit_eligible: false, executable: true }
89
96
  };
97
+ FamilySchema = z2.enum(["component", "judgment"]);
98
+ LineageKindSchema = z2.enum(["adaptation", "fork", "absorption", "remix"]);
99
+ TrustTierSchema = z2.enum([
100
+ "re-verified",
101
+ "community-adapted",
102
+ "author-reported",
103
+ "unsubstantiated"
104
+ ]);
90
105
  SortModeSchema = z2.enum([
91
106
  "stars",
92
107
  "downloads",
@@ -103,13 +118,7 @@ var init_enums = __esm({
103
118
  DependencyKindSchema = z2.enum(["required", "recommended"]);
104
119
  OAuthProviderSchema = z2.enum(["discord", "github", "google"]);
105
120
  UserRoleSchema = z2.enum(["user", "moderator", "admin"]);
106
- ReportReasonSchema = z2.enum([
107
- "spam",
108
- "harmful",
109
- "incorrect",
110
- "copyright",
111
- "other"
112
- ]);
121
+ ReportReasonSchema = z2.enum(["spam", "harmful", "incorrect", "copyright", "other"]);
113
122
  }
114
123
  });
115
124
 
@@ -329,6 +338,14 @@ var init_package = __esm({
329
338
  recommends: z6.array(DependencySpecSchema),
330
339
  categories: z6.array(z6.string()).optional().default([]),
331
340
  verified_by_kordis: z6.boolean(),
341
+ /**
342
+ * v2.1+ — derived evidence trust tier. Non-null only for the `judgment`
343
+ * family (a judgment's value rests on "this works"); null for component
344
+ * types. Strongest-wins over {verified_by_kordis, distinct-author lineage
345
+ * adopters, presence of an `evidence` frontmatter field}. See TrustTierSchema.
346
+ * Optional so pre-0.12 responses parse cleanly.
347
+ */
348
+ trust_tier: TrustTierSchema.nullable().optional(),
332
349
  /**
333
350
  * 0.9.0+ — when the caller pinned a `@version` or `@range`, the
334
351
  * concrete resolved version. Absent for bare-slug calls (where the
@@ -387,13 +404,7 @@ var init_comment = __esm({
387
404
  "use strict";
388
405
  init_primitives();
389
406
  init_user();
390
- CommentKindSchema = z7.enum([
391
- "comment",
392
- "tip",
393
- "report",
394
- "praise",
395
- "question"
396
- ]);
407
+ CommentKindSchema = z7.enum(["comment", "tip", "report", "praise", "question"]);
397
408
  CommentStatusSchema = z7.enum(["open", "resolved"]);
398
409
  CommentSchema = z7.object({
399
410
  id: UuidSchema,
@@ -417,7 +428,7 @@ var init_comment = __esm({
417
428
  is_deleted: z7.boolean(),
418
429
  created_at: IsoDateTimeSchema,
419
430
  /**
420
- * Web permalink (`https://kordis.store/p/<full_slug>#comment-<id>`). Server
431
+ * Web permalink (`https://kordis.store/<full_slug>#comment-<id>`). Server
421
432
  * populates whenever it has the package slug at serialization time —
422
433
  * always on create + list paths; absent on update/status paths that don't
423
434
  * carry the slug. Old API responses (pre-0.6.4) omit it; new clients use
@@ -474,7 +485,7 @@ var init_auth = __esm({
474
485
  from: z8.enum(["web", "cli"]).default("web")
475
486
  });
476
487
  DeviceCodeRequestSchema = z8.object({
477
- provider: OAuthProviderSchema.default("discord")
488
+ provider: OAuthProviderSchema.default("google")
478
489
  });
479
490
  DeviceCodeResponseSchema = z8.object({
480
491
  device_code: z8.string().min(16),
@@ -515,7 +526,7 @@ var init_auth = __esm({
515
526
 
516
527
  // ../../packages/contracts/src/api.ts
517
528
  import { z as z9 } from "zod";
518
- var SearchQuerySchema, SearchResponseSchema, TrendingQuerySchema, RisingQuerySchema, StarResponseSchema, AssetSchema, PublishPackageRequestSchema, PublishWaitQuerySchema, PublishPackageResponseSchema, PackagePathParamsSchema, ContentQuerySchema, PackageContentResponseSchema, JitReadRequestSchema, JitReadResponseSchema, PullPackageRequestSchema, PullPackageResponseSchema, YankRequestSchema, YankResponseSchema, ResolveVersionQuerySchema, ResolveVersionResponseSchema, GetPackageQuerySchema, ListVersionsResponseSchema, VersionDiffQuerySchema, ListCommentsQuerySchema, ListCommentsResponseSchema, UserProfileResponseSchema, UserPackagesQuerySchema, ListUserPackagesResponseSchema, ListUserStarsResponseSchema, FollowResponseSchema, ListFollowersQuerySchema, ListFollowersResponseSchema;
529
+ var SearchQuerySchema, SearchResponseSchema, TrendingQuerySchema, RisingQuerySchema, StarResponseSchema, AssetSchema, LineageEdgeInputSchema, LineageRelativeSchema, LineageStatsSchema, LineageGraphResponseSchema, PublishPackageRequestSchema, PublishWaitQuerySchema, PublishPackageResponseSchema, PackagePathParamsSchema, ContentQuerySchema, PackageContentResponseSchema, JitReadRequestSchema, JitReadResponseSchema, PullPackageRequestSchema, PullPackageResponseSchema, YankRequestSchema, YankResponseSchema, ResolveVersionQuerySchema, ResolveVersionResponseSchema, GetPackageQuerySchema, ListVersionsResponseSchema, VersionDiffQuerySchema, ListCommentsQuerySchema, ListCommentsResponseSchema, UserProfileResponseSchema, UserPackagesQuerySchema, ListUserPackagesResponseSchema, ListUserStarsResponseSchema, FollowResponseSchema, ListFollowersQuerySchema, ListFollowersResponseSchema;
519
530
  var init_api = __esm({
520
531
  "../../packages/contracts/src/api.ts"() {
521
532
  "use strict";
@@ -556,9 +567,41 @@ var init_api = __esm({
556
567
  });
557
568
  AssetSchema = z9.object({
558
569
  path: z9.string().min(1).max(255),
559
- content: z9.string(),
570
+ // Schema-level backstop only: publish-service enforces the real 2 MB
571
+ // BYTE cap (utf8 chars can be multi-byte, base64 inflates 4/3). The
572
+ // char ceiling here just keeps non-core-api consumers of the contract
573
+ // from accepting unbounded strings.
574
+ content: z9.string().max(3e6),
560
575
  encoding: z9.enum(["utf8", "base64"]).default("utf8")
561
576
  });
577
+ LineageEdgeInputSchema = z9.object({
578
+ to_full_slug: FullSlugSchema,
579
+ to_version: SemverSchema.optional(),
580
+ kind: LineageKindSchema
581
+ });
582
+ LineageRelativeSchema = z9.object({
583
+ full_slug: FullSlugSchema,
584
+ title: z9.string(),
585
+ type: HarnessTypeSchema,
586
+ kind: LineageKindSchema,
587
+ /** Version snapshot recorded on the edge (the side facing this relative). */
588
+ version: SemverSchema.nullable(),
589
+ declared: z9.boolean()
590
+ });
591
+ LineageStatsSchema = z9.object({
592
+ /** Longest ancestor chain depth (capped traversal). */
593
+ lineage_depth: z9.number().int(),
594
+ /** Distinct packages derived FROM this one (the trust count). */
595
+ descendant_count: z9.number().int(),
596
+ /** Descendants whose edge kind is `absorption`. */
597
+ absorption_count: z9.number().int()
598
+ });
599
+ LineageGraphResponseSchema = z9.object({
600
+ full_slug: FullSlugSchema,
601
+ ancestors: z9.array(LineageRelativeSchema),
602
+ descendants: z9.array(LineageRelativeSchema),
603
+ stats: LineageStatsSchema
604
+ });
562
605
  PublishPackageRequestSchema = z9.object({
563
606
  /**
564
607
  * Raw source content — YAML frontmatter + markdown body. Server parses and
@@ -585,6 +628,12 @@ var init_api = __esm({
585
628
  changelog: z9.string().max(2e3).optional(),
586
629
  depends: z9.array(z9.string()).optional(),
587
630
  recommends: z9.array(z9.string()).optional(),
631
+ /**
632
+ * Declared provenance: sources this package was adapted / forked / absorbed /
633
+ * remixed from (`kordis push --adapted-from`). Recorded as `lineage_edges`;
634
+ * unresolved sources are skipped fail-open, never blocking the publish.
635
+ */
636
+ lineage: z9.array(LineageEdgeInputSchema).max(20).optional(),
588
637
  visibility: VisibilitySchema.optional(),
589
638
  /**
590
639
  * Skip draft validation (preview mode) — only allowed in dev builds.
@@ -621,6 +670,15 @@ var init_api = __esm({
621
670
  version_from: SemverSchema.nullable(),
622
671
  version_to: SemverSchema
623
672
  }).nullable(),
673
+ /**
674
+ * Declared-lineage outcome, present only when the publish supplied `lineage`.
675
+ * `skipped` lists sources that could not be resolved (recorded fail-open, not
676
+ * an error) so the agent can fix a typo or publish the source first.
677
+ */
678
+ lineage: z9.object({
679
+ recorded: z9.number().int().nonnegative(),
680
+ skipped: z9.array(FullSlugSchema)
681
+ }).optional(),
624
682
  published_at: z9.string().datetime()
625
683
  });
626
684
  PackagePathParamsSchema = z9.object({
@@ -679,7 +737,23 @@ var init_api = __esm({
679
737
  * the schema for backwards compatibility with pre-0.2.3 servers; the
680
738
  * current server always populates it (empty array when no assets).
681
739
  */
682
- bundled_assets: z9.array(z9.string()).optional()
740
+ bundled_assets: z9.array(z9.string()).optional(),
741
+ /**
742
+ * 0.11.x+ — soft-yank signal. An explicitly pinned yanked version is
743
+ * still served (reproducibility, mirroring pull), but the reading agent
744
+ * must be told so it can decide whether to trust the content. Optional
745
+ * for backwards compatibility with older servers; the current server
746
+ * always populates `yanked`.
747
+ */
748
+ yanked: z9.boolean().optional(),
749
+ yank_reason: z9.string().nullable().optional(),
750
+ /**
751
+ * v2.1+ — derived evidence trust tier (see TrustTierSchema). Non-null only
752
+ * for the `judgment` family, so a reading agent can weight a self-reported
753
+ * claim differently from a community-corroborated one. Optional for
754
+ * backwards compatibility with pre-0.12 servers.
755
+ */
756
+ trust_tier: TrustTierSchema.nullable().optional()
683
757
  });
684
758
  PullPackageRequestSchema = z9.object({
685
759
  /**
@@ -1088,6 +1162,9 @@ var init_errors2 = __esm({
1088
1162
  });
1089
1163
 
1090
1164
  // ../../packages/api-client/src/client.ts
1165
+ function seg(value) {
1166
+ return encodeURIComponent(value);
1167
+ }
1091
1168
  var KordisClient;
1092
1169
  var init_client = __esm({
1093
1170
  "../../packages/api-client/src/client.ts"() {
@@ -1139,10 +1216,11 @@ var init_client = __esm({
1139
1216
  const retryable = method === "GET";
1140
1217
  const debug = typeof process !== "undefined" && process.env && (process.env["KORDIS_DEBUG"] === "1" || process.env["KORDIS_DEBUG"]?.toLowerCase() === "true");
1141
1218
  const debugStart = debug ? Date.now() : 0;
1219
+ const timeoutMs = this.config.requestTimeoutMs ?? 6e4;
1142
1220
  let lastErr;
1143
1221
  for (let attempt = 0; attempt < (retryable ? 2 : 1); attempt++) {
1144
1222
  try {
1145
- res = await this.fetchImpl(url, init);
1223
+ res = await this.fetchImpl(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
1146
1224
  lastErr = void 0;
1147
1225
  break;
1148
1226
  } catch (err) {
@@ -1240,22 +1318,22 @@ var init_client = __esm({
1240
1318
  });
1241
1319
  }
1242
1320
  update(username, slug, body) {
1243
- return this.request(`/api/packages/${username}/${slug}`, {
1321
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}`, {
1244
1322
  method: "PUT",
1245
1323
  body
1246
1324
  });
1247
1325
  }
1248
1326
  getPackage(username, slug, opts = {}) {
1249
- return this.request(`/api/packages/${username}/${slug}`, {
1327
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}`, {
1250
1328
  query: opts
1251
1329
  });
1252
1330
  }
1253
1331
  deletePackage(username, slug) {
1254
- return this.request(`/api/packages/${username}/${slug}`, { method: "DELETE" });
1332
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}`, { method: "DELETE" });
1255
1333
  }
1256
1334
  getContent(username, slug, opts = {}) {
1257
1335
  return this.request(
1258
- `/api/packages/${username}/${slug}/content`,
1336
+ `/api/packages/${seg(username)}/${seg(slug)}/content`,
1259
1337
  { query: opts }
1260
1338
  );
1261
1339
  }
@@ -1265,15 +1343,15 @@ var init_client = __esm({
1265
1343
  */
1266
1344
  resolveVersion(username, slug, range) {
1267
1345
  return this.request(
1268
- `/api/packages/${username}/${slug}/resolve`,
1346
+ `/api/packages/${seg(username)}/${seg(slug)}/resolve`,
1269
1347
  { query: { range } }
1270
1348
  );
1271
1349
  }
1272
1350
  pull(username, slug, opts = {}) {
1273
- return this.request(
1274
- `/api/packages/${username}/${slug}/pull`,
1275
- { method: "POST", body: opts }
1276
- );
1351
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}/pull`, {
1352
+ method: "POST",
1353
+ body: opts
1354
+ });
1277
1355
  }
1278
1356
  /**
1279
1357
  * JIT read — fetch the canonical body into agent context. Plan v1.1.
@@ -1281,10 +1359,10 @@ var init_client = __esm({
1281
1359
  * (passed-only unless `allow_pending=true`).
1282
1360
  */
1283
1361
  jitRead(username, slug, body = {}) {
1284
- return this.request(
1285
- `/api/packages/${username}/${slug}/read`,
1286
- { method: "POST", body }
1287
- );
1362
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}/read`, {
1363
+ method: "POST",
1364
+ body
1365
+ });
1288
1366
  }
1289
1367
  search(query) {
1290
1368
  return this.request("/api/packages/search", {
@@ -1329,18 +1407,24 @@ var init_client = __esm({
1329
1407
  });
1330
1408
  }
1331
1409
  star(username, slug) {
1332
- return this.request(`/api/packages/${username}/${slug}/star`, {
1410
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}/star`, {
1333
1411
  method: "POST"
1334
1412
  });
1335
1413
  }
1336
1414
  unstar(username, slug) {
1337
- return this.request(`/api/packages/${username}/${slug}/star`, {
1415
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}/star`, {
1338
1416
  method: "DELETE"
1339
1417
  });
1340
1418
  }
1341
1419
  versions(username, slug) {
1342
1420
  return this.request(
1343
- `/api/packages/${username}/${slug}/versions`
1421
+ `/api/packages/${seg(username)}/${seg(slug)}/versions`
1422
+ );
1423
+ }
1424
+ /** Provenance graph (Phase 2): ancestors + direct descendants + trust stats. */
1425
+ lineage(username, slug) {
1426
+ return this.request(
1427
+ `/api/packages/${seg(username)}/${seg(slug)}/lineage`
1344
1428
  );
1345
1429
  }
1346
1430
  /**
@@ -1351,31 +1435,31 @@ var init_client = __esm({
1351
1435
  yankVersion(username, slug, version, body = {}, opts = {}) {
1352
1436
  const query = opts.asAdmin ? { as: "admin" } : void 0;
1353
1437
  return this.request(
1354
- `/api/packages/${username}/${slug}/versions/${version}/yank`,
1438
+ `/api/packages/${seg(username)}/${seg(slug)}/versions/${seg(version)}/yank`,
1355
1439
  { method: "POST", body, ...query ? { query } : {} }
1356
1440
  );
1357
1441
  }
1358
1442
  unyankVersion(username, slug, version, opts = {}) {
1359
1443
  const query = opts.asAdmin ? { as: "admin" } : void 0;
1360
1444
  return this.request(
1361
- `/api/packages/${username}/${slug}/versions/${version}/yank`,
1445
+ `/api/packages/${seg(username)}/${seg(slug)}/versions/${seg(version)}/yank`,
1362
1446
  { method: "DELETE", ...query ? { query } : {} }
1363
1447
  );
1364
1448
  }
1365
1449
  diff(username, slug, from, to) {
1366
- return this.request(`/api/packages/${username}/${slug}/diff`, {
1450
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}/diff`, {
1367
1451
  query: { from, to }
1368
1452
  });
1369
1453
  }
1370
1454
  // ─── comments ──────────────────────────────────────────────────────────
1371
1455
  listComments(username, slug, query = {}) {
1372
1456
  return this.request(
1373
- `/api/packages/${username}/${slug}/comments`,
1457
+ `/api/packages/${seg(username)}/${seg(slug)}/comments`,
1374
1458
  { query }
1375
1459
  );
1376
1460
  }
1377
1461
  createComment(username, slug, body) {
1378
- return this.request(`/api/packages/${username}/${slug}/comments`, {
1462
+ return this.request(`/api/packages/${seg(username)}/${seg(slug)}/comments`, {
1379
1463
  method: "POST",
1380
1464
  body
1381
1465
  });
@@ -1401,9 +1485,7 @@ var init_client = __esm({
1401
1485
  return this.request(`/api/comments/${id}`, { method: "DELETE" });
1402
1486
  }
1403
1487
  listReplies(id) {
1404
- return this.request(
1405
- `/api/comments/${id}/replies`
1406
- );
1488
+ return this.request(`/api/comments/${id}/replies`);
1407
1489
  }
1408
1490
  // ─── telemetry ─────────────────────────────────────────────────────────
1409
1491
  /**
@@ -1437,10 +1519,9 @@ var init_client = __esm({
1437
1519
  }
1438
1520
  /** Cursor-paginated packages that `username` has starred. */
1439
1521
  listUserStars(username, query = {}) {
1440
- return this.request(
1441
- `/api/users/${encodeURIComponent(username)}/stars`,
1442
- { query }
1443
- );
1522
+ return this.request(`/api/users/${encodeURIComponent(username)}/stars`, {
1523
+ query
1524
+ });
1444
1525
  }
1445
1526
  /** Auth-required. Idempotent (re-follow is a no-op). Returns post-mutation counter. */
1446
1527
  followUser(username) {
@@ -1490,7 +1571,7 @@ var init_src2 = __esm({
1490
1571
 
1491
1572
  // src/index.ts
1492
1573
  init_src2();
1493
- import { Command as Command25 } from "commander";
1574
+ import { Command as Command26 } from "commander";
1494
1575
 
1495
1576
  // src/commands/login.ts
1496
1577
  import { Command } from "commander";
@@ -1528,7 +1609,7 @@ async function loadCliConfig() {
1528
1609
  }
1529
1610
  async function saveCliConfig(cfg) {
1530
1611
  await mkdir(configDir(), { recursive: true });
1531
- await writeFile(configPath(), JSON.stringify(cfg, null, 2) + "\n", "utf8");
1612
+ await writeFile(configPath(), JSON.stringify(cfg, null, 2) + "\n", { encoding: "utf8", mode: 384 });
1532
1613
  }
1533
1614
 
1534
1615
  // src/keychain.ts
@@ -1952,7 +2033,7 @@ async function readPending() {
1952
2033
  }
1953
2034
  async function savePending(file) {
1954
2035
  await mkdir3(dir(), { recursive: true });
1955
- await writeFile3(path(), JSON.stringify(file, null, 2) + "\n", "utf8");
2036
+ await writeFile3(path(), JSON.stringify(file, null, 2) + "\n", { encoding: "utf8", mode: 384 });
1956
2037
  }
1957
2038
  async function clearPending() {
1958
2039
  try {
@@ -2110,16 +2191,31 @@ function spinner(text) {
2110
2191
  var c = kleur;
2111
2192
 
2112
2193
  // src/commands/login.ts
2194
+ function assertSafeVerificationUrl(raw) {
2195
+ let url;
2196
+ try {
2197
+ url = new URL(raw);
2198
+ } catch {
2199
+ throw new Error(`registry returned an invalid verification URL: ${JSON.stringify(raw)}`);
2200
+ }
2201
+ const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1";
2202
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
2203
+ throw new Error(
2204
+ `registry returned a verification URL with unexpected scheme "${url.protocol}" \u2014 refusing to open it. Check KORDIS_API_URL.`
2205
+ );
2206
+ }
2207
+ }
2113
2208
  function loginCommand() {
2114
- return new Command("login").description("authenticate with the Kordis registry").option("--provider <name>", "OAuth provider", "discord").option("--no-browser", "don't auto-open the browser; print the URL instead").option(
2209
+ return new Command("login").description("authenticate with the Kordis registry").option("--provider <name>", "OAuth provider", "google").option("--no-browser", "don't auto-open the browser; print the URL instead").option(
2115
2210
  "--emit-only",
2116
- "start device flow, open browser, save pending state, exit immediately \u2014 for non-interactive (agent) contexts where the 10-min poll would exceed the bash timeout"
2211
+ "start device flow, open the user's browser, save pending state, and exit immediately (no blocking poll) \u2014 THE login path an agent should drive on a user's behalf; the next CLI call auto-claims the token. Prefer this over plain `login`, whose 10-min poll exceeds an agent's bash timeout"
2117
2212
  ).action(async (opts) => {
2118
2213
  const bundle = await useClient();
2119
2214
  const started = spinner("Requesting device code");
2120
- const provider = opts.provider ?? "discord";
2215
+ const provider = opts.provider ?? "google";
2121
2216
  const device = await bundle.api.startDeviceFlow(provider);
2122
2217
  started.succeed();
2218
+ assertSafeVerificationUrl(device.verification_url);
2123
2219
  printHuman("");
2124
2220
  printHuman(c.bold("Confirm the code in your browser:"));
2125
2221
  printHuman(" " + c.yellow(device.user_code));
@@ -2192,7 +2288,10 @@ function loginCommand() {
2192
2288
  const cfg = await loadCliConfig();
2193
2289
  await saveCliConfig({ ...cfg, username: me.username, sessionId: tokens.session_id });
2194
2290
  printDual({
2195
- json: { status: "logged_in", username: me.username, session_id: tokens.session_id },
2291
+ // session_id intentionally omitted: stdout in JSON mode flows
2292
+ // into scripts, CI logs, and agent transcripts. Sessions stay
2293
+ // enumerable via GET /auth/sessions with the stored token.
2294
+ json: { status: "logged_in", username: me.username },
2196
2295
  human: () => printHuman(c.green(`
2197
2296
  \u2713 Logged in as ${c.bold(me.username)}.`))
2198
2297
  });
@@ -2219,7 +2318,7 @@ function requireAuth(bundle) {
2219
2318
  error: {
2220
2319
  code: "not_authenticated",
2221
2320
  message: "authentication required",
2222
- hint: "Run `kordis login --emit-only` to start browser sign-in (exits immediately, no blocking poll), then re-run this command \u2014 the next CLI call auto-claims the token. For interactive shells, plain `kordis login` (10-min poll) also works. If you DID login but auth keeps failing, run with `KORDIS_DEBUG=1` to surface auto-finalize stages (Round 12 F1 \u2014 diagnostic moment-of-need)."
2321
+ hint: "Authenticate first \u2014 and if you are an AGENT acting for a user, lead the login yourself: YOU run `kordis login --emit-only`. It opens the user's browser (they just click Authorize) and returns immediately with no blocking poll; then re-run this command and the token is auto-claimed. Do NOT tell the user to run a login command themselves, and do NOT run plain `kordis login` \u2014 its ~10-min poll will exceed your bash timeout. Plain `kordis login` is ONLY for a human typing at their own terminal. If the user already clicked Authorize but auth still fails, re-run with `KORDIS_DEBUG=1` to surface auto-finalize stages."
2223
2322
  }
2224
2323
  });
2225
2324
  }
@@ -2333,12 +2432,21 @@ var TOOL_ALIASES = {
2333
2432
  gemini: "gemini-cli"
2334
2433
  };
2335
2434
  function searchCommand() {
2336
- 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(
2435
+ return new Command3("search").description("search the Kordis registry").argument("[query...]", "keywords to match against title/description").option(
2436
+ "--type <type>",
2437
+ "filter by harness type (skill|mcp-server|project-rules|custom-agent|hook)"
2438
+ ).option(
2337
2439
  "--tool <tool>",
2338
2440
  `filter by compatible tool \u2014 one of: ${TOOL_SLUGS.join("|")} (aliases: ${Object.entries(
2339
2441
  TOOL_ALIASES
2340
2442
  ).map(([alias, canonical]) => `${alias}\u2192${canonical}`).join(", ")})`
2341
- ).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(
2443
+ ).option("--tier <tier>", "filter by portability tier (A|B|C|D)").option(
2444
+ "--tag <slug>",
2445
+ "filter by tag (e.g. aws, fastapi, llm); tags also boost free-text ranking now"
2446
+ ).option(
2447
+ "--author <username>",
2448
+ "filter to a single author (server returns exact total when set)"
2449
+ ).option(
2342
2450
  "--cursor <token>",
2343
2451
  "pagination cursor \u2014 pass the next_cursor field from a prior search response to fetch the next page"
2344
2452
  ).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) => {
@@ -2377,6 +2485,7 @@ function searchCommand() {
2377
2485
  type: opts.type ? [opts.type] : void 0,
2378
2486
  tool: resolvedTool ? [resolvedTool] : void 0,
2379
2487
  tier: opts.tier,
2488
+ ...opts.tag ? { tag: opts.tag } : {},
2380
2489
  ...opts.author ? { author: opts.author } : {},
2381
2490
  ...opts.cursor ? { cursor: opts.cursor } : {},
2382
2491
  sort: opts.sort ?? "relevance",
@@ -2384,7 +2493,7 @@ function searchCommand() {
2384
2493
  });
2385
2494
  }
2386
2495
  const queryProvided = query.length > 0;
2387
- const emptyHint = opts.type ? `no packages of type "${opts.type}" match. Try without --type, or list everything: kordis search --type ${opts.type} --sort recent --limit 25` : queryProvided ? "try `--type skill` (or mcp-server / project-rules / custom-agent / hook) to narrow, or broader keywords" : 'try keywords (e.g. `kordis search "topic"`) or `kordis search --trending`';
2496
+ const emptyHint = opts.type ? `no packages of type "${opts.type}" match. Try without --type, or list everything: kordis search --type ${opts.type} --sort recent --limit 25` : queryProvided ? "narrow with `--type skill|mcp-server|project-rules|custom-agent|hook|judgment-pattern` or `--tag <topic>` (e.g. aws, fastapi), broaden the keywords, or browse `--sort trending`" : 'try keywords (e.g. `kordis search "topic"`), `--tag <topic>`, or `kordis search --trending`';
2388
2497
  printDual({
2389
2498
  json: res.results.length === 0 ? { ...res, hint: emptyHint } : res,
2390
2499
  human: () => {
@@ -2420,10 +2529,10 @@ function searchCommand() {
2420
2529
  if (res.next_cursor) {
2421
2530
  printHuman(c.gray(`(more available \u2014 pass --cursor ${res.next_cursor})`));
2422
2531
  }
2423
- if (queryProvided && !opts.type && res.results.length > 3) {
2532
+ if (queryProvided && !opts.type && !opts.tag && res.results.length > 3) {
2424
2533
  printHuman(
2425
2534
  c.gray(
2426
- " \u2192 narrow further with `--type skill|mcp-server|project-rules|custom-agent|hook`"
2535
+ " \u2192 narrow with `--type skill|mcp-server|project-rules|custom-agent|hook|judgment-pattern` or `--tag <topic>`"
2427
2536
  )
2428
2537
  );
2429
2538
  }
@@ -2687,6 +2796,53 @@ var HookFrontmatterSchema = z18.object({
2687
2796
  license: z18.string().max(64).optional()
2688
2797
  }).strict();
2689
2798
 
2799
+ // ../../packages/harness-schema/src/types/judgment-pattern.ts
2800
+ init_src();
2801
+ import { z as z19 } from "zod";
2802
+ var FailureModeSchema = z19.object({
2803
+ symptom: z19.string().min(1).max(600).describe("the observable sign the judgment was ignored"),
2804
+ correction: z19.string().min(1).max(1200).describe("what to do once the symptom shows")
2805
+ }).strict();
2806
+ var AdaptHintsSchema = z19.object({
2807
+ /** Parts that transplant verbatim into ANY harness — the invariant core. */
2808
+ universal: z19.array(z19.string().min(1).max(400)).max(20).optional(),
2809
+ /** Parts the adapting agent must re-express in the host's stack/conventions. */
2810
+ context_specific: z19.array(z19.string().min(1).max(400)).max(20).optional()
2811
+ }).strict();
2812
+ var JudgmentPatternFrontmatterSchema = z19.object({
2813
+ name: z19.string().min(1).max(64).regex(
2814
+ /^[a-z0-9]+(-[a-z0-9]+)*$/,
2815
+ 'name must be lowercase-with-hyphens, e.g. "additive-only-migrations"'
2816
+ ),
2817
+ description: z19.string().min(1).max(2048),
2818
+ version: SemverSchema.optional(),
2819
+ title: z19.string().min(1).max(200).optional(),
2820
+ /** The recurring situation / trap this judgment addresses. */
2821
+ problem: z19.string().min(1).max(4e3),
2822
+ /**
2823
+ * The judgment itself — free-form Markdown. The agent is the interpreter,
2824
+ * so this stays unstructured prose (decision tree, heuristics, rationale).
2825
+ * This is the part that gets transplanted.
2826
+ */
2827
+ decision_logic: z19.string().min(1).max(2e4),
2828
+ /** Triggers — when this judgment should fire. At least one. */
2829
+ when_to_apply: z19.array(z19.string().min(1).max(400)).min(1).max(12),
2830
+ /** Counter-triggers — when applying it would be wrong. */
2831
+ when_not_to_apply: z19.array(z19.string().min(1).max(400)).max(12).optional(),
2832
+ /** How it fails in practice, and how to recover — the hard-won part. */
2833
+ failure_modes: z19.array(FailureModeSchema).max(12).optional(),
2834
+ /** Transplant steering — the field unique to judgment-pattern. */
2835
+ adapt_hints: AdaptHintsSchema.optional(),
2836
+ /** Where this judgment came from (project, incident, paper). Provenance. */
2837
+ origin: z19.string().min(1).max(600).optional(),
2838
+ /** Proof it works (anti-spam lever for AI review). */
2839
+ evidence: z19.string().min(1).max(2e3).optional(),
2840
+ // Kordis-forwarded cross-type fields (kept minimal, like skill).
2841
+ tags: z19.array(TagSchema).max(16).optional(),
2842
+ tools: z19.array(ToolSlugSchema).optional(),
2843
+ license: z19.string().max(64).optional()
2844
+ }).strict();
2845
+
2690
2846
  // ../../packages/harness-schema/src/validator.ts
2691
2847
  function flag(code, message, extra = {}) {
2692
2848
  return { code, message, ...extra };
@@ -2718,7 +2874,7 @@ function validatePackageSource(source) {
2718
2874
  return earlyExit(
2719
2875
  flag("missing_required_frontmatter", "`type` is required in frontmatter", {
2720
2876
  field: "type",
2721
- hint: "Add type: skill (or one of: mcp-server, project-rules, custom-agent, hook)"
2877
+ hint: "Add type: skill (or one of: mcp-server, project-rules, custom-agent, hook, judgment-pattern)"
2722
2878
  })
2723
2879
  );
2724
2880
  }
@@ -2727,7 +2883,7 @@ function validatePackageSource(source) {
2727
2883
  return earlyExit(
2728
2884
  flag("unknown_type", `type "${typeValue}" is not a recognised Harness type`, {
2729
2885
  field: "type",
2730
- hint: "Allowed types: skill, mcp-server, project-rules, custom-agent, hook"
2886
+ hint: "Allowed types: skill, mcp-server, project-rules, custom-agent, hook, judgment-pattern"
2731
2887
  })
2732
2888
  );
2733
2889
  }
@@ -2736,7 +2892,7 @@ function validatePackageSource(source) {
2736
2892
  flags.push(
2737
2893
  flag("type_not_phase1", `type "${type}" is planned for Phase 2+ and is not yet accepted`, {
2738
2894
  field: "type",
2739
- hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook"
2895
+ hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook, judgment-pattern"
2740
2896
  })
2741
2897
  );
2742
2898
  return earlyExit(null, { flags, body });
@@ -2925,6 +3081,11 @@ function dispatchValidation(type, raw) {
2925
3081
  if (!r.success) return zodFail(r.error.issues);
2926
3082
  return { ok: true, frontmatter: { type, data: r.data }, flags: [] };
2927
3083
  }
3084
+ case "judgment-pattern": {
3085
+ const r = JudgmentPatternFrontmatterSchema.safeParse(raw);
3086
+ if (!r.success) return zodFail(r.error.issues);
3087
+ return { ok: true, frontmatter: { type, data: r.data }, flags: [] };
3088
+ }
2928
3089
  default:
2929
3090
  return {
2930
3091
  ok: false,
@@ -2932,7 +3093,7 @@ function dispatchValidation(type, raw) {
2932
3093
  flags: [
2933
3094
  flag("type_not_phase1", `type "${type}" not accepted in Phase 1`, {
2934
3095
  field: "type",
2935
- hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook"
3096
+ hint: "Use one of the Phase 1 types: skill, mcp-server, project-rules, custom-agent, hook, judgment-pattern"
2936
3097
  })
2937
3098
  ]
2938
3099
  };
@@ -2967,7 +3128,7 @@ var ASSET_MAX_COUNT = 50;
2967
3128
  var SAFE_PATH_RE = /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/;
2968
3129
  function isAssetPathSafe(p) {
2969
3130
  if (!SAFE_PATH_RE.test(p)) return false;
2970
- return p.split("/").every((seg) => seg.length > 0 && seg !== "." && seg !== "..");
3131
+ return p.split("/").every((seg2) => seg2.length > 0 && seg2 !== "." && seg2 !== "..");
2971
3132
  }
2972
3133
  function validateAssets(assets) {
2973
3134
  const flags = [];
@@ -2992,14 +3153,211 @@ function validateAssets(assets) {
2992
3153
  return { ok: flags.length === 0, flags };
2993
3154
  }
2994
3155
 
3156
+ // ../../packages/harness-schema/src/sections.ts
3157
+ var JUDGMENT_FIELDS = [
3158
+ { key: "problem", label: "Problem" },
3159
+ { key: "decision_logic", label: "Decision logic" },
3160
+ { key: "when_to_apply", label: "When to apply" },
3161
+ { key: "when_not_to_apply", label: "When NOT to apply" },
3162
+ { key: "failure_modes", label: "Failure modes" },
3163
+ { key: "adapt_hints", label: "Adapt hints" },
3164
+ { key: "origin", label: "Origin" },
3165
+ { key: "evidence", label: "Evidence" }
3166
+ ];
3167
+ function slugifyHeading(h) {
3168
+ return h.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
3169
+ }
3170
+ function fieldToMarkdown(key, value) {
3171
+ if (value === null || value === void 0) return null;
3172
+ if (typeof value === "string") return value.trim() || null;
3173
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
3174
+ if (Array.isArray(value)) {
3175
+ if (value.length === 0) return null;
3176
+ if (key === "failure_modes") {
3177
+ return value.map((fm) => {
3178
+ const o = fm ?? {};
3179
+ const symptom = typeof o["symptom"] === "string" ? o["symptom"] : "";
3180
+ const correction = typeof o["correction"] === "string" ? o["correction"] : "";
3181
+ return `- **${symptom}**
3182
+ \u2192 ${correction}`;
3183
+ }).join("\n");
3184
+ }
3185
+ return value.map((v) => `- ${String(v)}`).join("\n");
3186
+ }
3187
+ if (typeof value === "object") {
3188
+ const o = value;
3189
+ const parts = [];
3190
+ if (Array.isArray(o["universal"]) && o["universal"].length > 0) {
3191
+ parts.push("**Universal**\n" + o["universal"].map((v) => `- ${String(v)}`).join("\n"));
3192
+ }
3193
+ if (Array.isArray(o["context_specific"]) && o["context_specific"].length > 0) {
3194
+ parts.push(
3195
+ "**Context-specific**\n" + o["context_specific"].map((v) => `- ${String(v)}`).join("\n")
3196
+ );
3197
+ }
3198
+ return parts.length > 0 ? parts.join("\n\n") : null;
3199
+ }
3200
+ return null;
3201
+ }
3202
+ function splitByHeadings(body) {
3203
+ const lines = body.split("\n");
3204
+ const sections = [];
3205
+ const intro = [];
3206
+ let cur = null;
3207
+ const flush = () => {
3208
+ if (cur) {
3209
+ sections.push({
3210
+ name: slugifyHeading(cur.label) || `section-${sections.length + 1}`,
3211
+ label: cur.label,
3212
+ body: cur.body.join("\n").trim()
3213
+ });
3214
+ }
3215
+ };
3216
+ for (const line of lines) {
3217
+ const m = /^(#{2,3})\s+(.+?)\s*$/.exec(line);
3218
+ if (m) {
3219
+ flush();
3220
+ cur = { label: m[2], body: [] };
3221
+ } else if (cur) {
3222
+ cur.body.push(line);
3223
+ } else {
3224
+ intro.push(line);
3225
+ }
3226
+ }
3227
+ flush();
3228
+ return { intro: intro.join("\n").trim(), sections };
3229
+ }
3230
+ function outlineOf(source) {
3231
+ let fm = null;
3232
+ let body = source;
3233
+ try {
3234
+ const parsed = parseFrontmatter(source);
3235
+ fm = parsed.frontmatter;
3236
+ body = parsed.body;
3237
+ } catch {
3238
+ }
3239
+ const description = fm && typeof fm["description"] === "string" ? fm["description"] : null;
3240
+ const type = fm && typeof fm["type"] === "string" ? fm["type"] : null;
3241
+ if (type === "judgment-pattern" && fm) {
3242
+ const sections2 = [];
3243
+ for (const { key, label } of JUDGMENT_FIELDS) {
3244
+ const md = fieldToMarkdown(key, fm[key]);
3245
+ if (md !== null) sections2.push({ name: key, label, body: md });
3246
+ }
3247
+ return { description, type, intro: body.trim(), sections: sections2 };
3248
+ }
3249
+ const { intro, sections } = splitByHeadings(body);
3250
+ return { description, type, intro, sections };
3251
+ }
3252
+ function sectionOf(source, name) {
3253
+ const q = name.trim().toLowerCase();
3254
+ const { sections } = outlineOf(source);
3255
+ return sections.find((s) => s.name === q) ?? sections.find((s) => s.label.toLowerCase() === q) ?? sections.find((s) => s.name === slugifyHeading(name)) ?? null;
3256
+ }
3257
+
3258
+ // src/lib/parse-username.ts
3259
+ init_src();
3260
+ var SORT_KEYWORDS = ["latest", "popular", "recent", "updated", "stars", "downloads"];
3261
+ function usernameError(message, hint) {
3262
+ const e = new Error(message);
3263
+ e.hint = hint;
3264
+ return e;
3265
+ }
3266
+ function parseUsername(raw) {
3267
+ if (SORT_KEYWORDS.includes(raw.toLowerCase())) {
3268
+ throw usernameError(
3269
+ `"${raw}" is a sort keyword \u2014 did you mean \`--sort ${raw}\`? Username arguments must follow the Kordis username shape (lowercase alphanumeric, hyphens allowed).`,
3270
+ `Pass it as the sort flag instead, e.g. \`--sort ${raw}\`, or supply an actual username argument.`
3271
+ );
3272
+ }
3273
+ const parsed = UsernameSchema.safeParse(raw);
3274
+ if (!parsed.success) {
3275
+ throw usernameError(
3276
+ `invalid username "${raw}" \u2014 must be 3-32 chars, lowercase alphanumeric with single hyphens (e.g. "alice", "my-team")`,
3277
+ "Usernames are lowercase alphanumeric with single hyphens. Browse existing users with `kordis search` or look one up with `kordis profile <username>`."
3278
+ );
3279
+ }
3280
+ return parsed.data;
3281
+ }
3282
+
3283
+ // src/lib/parse-slug-version.ts
3284
+ var SUFFIX_CHARS = /[\^~*]/;
3285
+ function slugError(message, hint) {
3286
+ const e = new Error(message);
3287
+ e.hint = hint;
3288
+ return e;
3289
+ }
3290
+ function parseSlugAtVersion(raw) {
3291
+ if (!raw.includes("/")) {
3292
+ let username2;
3293
+ try {
3294
+ username2 = parseUsername(raw);
3295
+ } catch {
3296
+ username2 = void 0;
3297
+ }
3298
+ if (username2) {
3299
+ throw slugError(
3300
+ `"${raw}" looks like a username, not a package`,
3301
+ `Try \`kordis profile ${username2}\` for user details, or pass a full slug like "{username}/{slug}".`
3302
+ );
3303
+ }
3304
+ throw slugError(
3305
+ `"${raw}" is missing the "{username}/{slug}" form`,
3306
+ 'Use the form "{username}/{slug}", e.g. "alice/json-pretty".'
3307
+ );
3308
+ }
3309
+ const atIdx = raw.indexOf("@");
3310
+ const slugPart = atIdx === -1 ? raw : raw.slice(0, atIdx);
3311
+ const versionPart = atIdx === -1 ? "" : raw.slice(atIdx + 1);
3312
+ const [username, slug] = slugPart.split("/");
3313
+ if (!username || !slug) {
3314
+ throw slugError(
3315
+ 'full_slug must be "{username}/{slug}"',
3316
+ 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.'
3317
+ );
3318
+ }
3319
+ if (atIdx !== -1 && versionPart.length === 0) {
3320
+ throw slugError(
3321
+ `"${raw}" has a trailing "@" with no version`,
3322
+ `Drop the "@" or append a version/range, e.g. "${slugPart}@1.0.0" or "${slugPart}@^1".`
3323
+ );
3324
+ }
3325
+ const out = {
3326
+ fullSlug: slugPart,
3327
+ username,
3328
+ slug
3329
+ };
3330
+ if (versionPart.length > 0) {
3331
+ if (SUFFIX_CHARS.test(versionPart)) {
3332
+ out.range = versionPart;
3333
+ } else {
3334
+ out.version = versionPart;
3335
+ }
3336
+ }
3337
+ return out;
3338
+ }
3339
+
2995
3340
  // src/commands/push.ts
3341
+ var collect = (val, acc) => acc.concat(val);
3342
+ function toLineage(raws, kind) {
3343
+ return (raws ?? []).map((raw) => {
3344
+ const p = parseSlugAtVersion(raw);
3345
+ return {
3346
+ to_full_slug: `${p.username}/${p.slug}`,
3347
+ ...p.version ? { to_version: p.version } : {},
3348
+ kind
3349
+ };
3350
+ });
3351
+ }
2996
3352
  var ASSET_MAX_BYTES = 2 * 1024 * 1024;
2997
3353
  var BUNDLE_MAX_BYTES = 5 * 1024 * 1024;
2998
3354
  function pushCommand() {
2999
- 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(
3000
- "--slug <slug>",
3001
- 'override slug; accepts "user/slug" to point at a specific package'
3002
- ).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(
3355
+ 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("--slug <slug>", 'override slug; accepts "user/slug" to point at a specific package').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").option(
3356
+ "--adapted-from <slug>",
3357
+ "source this package was adapted from (repeatable); records lineage",
3358
+ collect,
3359
+ []
3360
+ ).option("--forked-from <slug>", "source this package was forked from (repeatable)", collect, []).option("--absorbs <slug>", "source this package absorbs (repeatable)", collect, []).option("--remixes <slug>", "source this package remixes (repeatable)", collect, []).action(
3003
3361
  async (path2, opts) => {
3004
3362
  const bundle = await useClient();
3005
3363
  const abs = resolve(process.cwd(), path2);
@@ -3081,6 +3439,12 @@ function pushCommand() {
3081
3439
  const spin = spinner(
3082
3440
  opts.dryRun ? "Validating" : targetFullSlug ? `Publishing update to ${targetFullSlug}` : "Publishing"
3083
3441
  );
3442
+ const lineage = [
3443
+ ...toLineage(opts.adaptedFrom, "adaptation"),
3444
+ ...toLineage(opts.forkedFrom, "fork"),
3445
+ ...toLineage(opts.absorbs, "absorption"),
3446
+ ...toLineage(opts.remixes, "remix")
3447
+ ];
3084
3448
  const res = await bundle.authedRequest(
3085
3449
  (api) => api.publish(
3086
3450
  {
@@ -3091,6 +3455,7 @@ function pushCommand() {
3091
3455
  target_full_slug: targetFullSlug,
3092
3456
  type: opts.type,
3093
3457
  changelog: opts.changelog,
3458
+ lineage: lineage.length > 0 ? lineage : void 0,
3094
3459
  dry_run: !!opts.dryRun
3095
3460
  },
3096
3461
  {
@@ -3109,6 +3474,21 @@ function pushCommand() {
3109
3474
  printHuman(c.gray(` + ${assets.length} bundled file(s)`));
3110
3475
  }
3111
3476
  printHuman(` ${c.cyan(res.url)}`);
3477
+ if (res.lineage) {
3478
+ printHuman(c.gray(` lineage: ${res.lineage.recorded} edge(s) recorded`));
3479
+ if (res.lineage.skipped.length > 0) {
3480
+ printHuman(
3481
+ c.yellow(
3482
+ ` ! lineage source(s) not found, skipped: ${res.lineage.skipped.join(", ")}`
3483
+ )
3484
+ );
3485
+ printHuman(
3486
+ c.gray(
3487
+ " Publish the source first or fix the slug, then re-push to record it."
3488
+ )
3489
+ );
3490
+ }
3491
+ }
3112
3492
  if (res.status === "pending") {
3113
3493
  printHuman(
3114
3494
  c.yellow(
@@ -3179,9 +3559,7 @@ async function runDryRun(args) {
3179
3559
  );
3180
3560
  }
3181
3561
  if (assets.length > 0) printHuman(c.gray(` + ${assets.length} bundled file(s)`));
3182
- printHuman(
3183
- c.gray(" (AI review only runs on the real push; not performed here)")
3184
- );
3562
+ printHuman(c.gray(" (AI review only runs on the real push; not performed here)"));
3185
3563
  return;
3186
3564
  }
3187
3565
  printHuman(c.red(`\u2717 dry-run validation failed (${allFlags.length} flag(s))`));
@@ -3245,7 +3623,9 @@ async function bundleDirectory(dir2) {
3245
3623
  await walkDir(dir2, "", all);
3246
3624
  const skillEntry = all.find((f) => f.rel === "SKILL.md");
3247
3625
  if (!skillEntry) {
3248
- throw new Error(`No SKILL.md found in directory ${dir2}. Push a directory that has SKILL.md at its root.`);
3626
+ throw new Error(
3627
+ `No SKILL.md found in directory ${dir2}. Push a directory that has SKILL.md at its root.`
3628
+ );
3249
3629
  }
3250
3630
  const content = await readFile6(skillEntry.abs, "utf8");
3251
3631
  const assets = [];
@@ -3273,9 +3653,7 @@ async function bundleSkillZip(zipPath) {
3273
3653
  })).filter((f) => f.rel.length > 0 && !shouldExclude(f.rel, false));
3274
3654
  const skillEntry = stripped.find((f) => f.rel === "SKILL.md");
3275
3655
  if (!skillEntry) {
3276
- const e = new Error(
3277
- `.skill ZIP has no SKILL.md at its root (prefix="${prefix}")`
3278
- );
3656
+ const e = new Error(`.skill ZIP has no SKILL.md at its root (prefix="${prefix}")`);
3279
3657
  e.hint = "SKILL.md must sit at <skill-name>/SKILL.md at the archive root. Re-package so the single top-level directory contains SKILL.md (skill-creator does this by default).";
3280
3658
  throw e;
3281
3659
  }
@@ -3301,6 +3679,7 @@ async function bundleSkillZip(zipPath) {
3301
3679
  import { Command as Command5 } from "commander";
3302
3680
  import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
3303
3681
  import { dirname as dirname2, join as join9, resolve as resolve2, sep as sep2 } from "path";
3682
+ init_src();
3304
3683
 
3305
3684
  // ../../packages/translators/src/skill/identity.ts
3306
3685
  function buildSkillFile(input, outputPath) {
@@ -3535,7 +3914,7 @@ var mcpToCodex = (input) => {
3535
3914
  var mcpToContinue = (input) => {
3536
3915
  const name = getName(input);
3537
3916
  const t = getTransport(input);
3538
- const lines = [`name: ${name}`];
3917
+ const lines = [`name: ${JSON.stringify(name)}`];
3539
3918
  if (t.type === "stdio") {
3540
3919
  lines.push("type: stdio", `command: ${JSON.stringify(t.command)}`);
3541
3920
  if (t.args && t.args.length > 0) {
@@ -3594,9 +3973,13 @@ var customAgentToClaude = (input) => {
3594
3973
  const context = typeof fm["context"] === "string" ? fm["context"] : null;
3595
3974
  const allowed = Array.isArray(fm["allowed_tools"]) ? fm["allowed_tools"] : [];
3596
3975
  const disallowed = Array.isArray(fm["disallowed_tools"]) ? fm["disallowed_tools"] : [];
3597
- const lines = ["---", `name: ${name}`, `description: ${JSON.stringify(description)}`];
3976
+ const lines = [
3977
+ "---",
3978
+ `name: ${JSON.stringify(name)}`,
3979
+ `description: ${JSON.stringify(description)}`
3980
+ ];
3598
3981
  if (model) lines.push(`model: ${JSON.stringify(model)}`);
3599
- if (context) lines.push(`context: ${context}`);
3982
+ if (context) lines.push(`context: ${JSON.stringify(context)}`);
3600
3983
  if (allowed.length > 0) {
3601
3984
  lines.push("allowed_tools:");
3602
3985
  for (const t of allowed) lines.push(` - ${JSON.stringify(t)}`);
@@ -3679,6 +4062,70 @@ var hookCanonical = (input) => {
3679
4062
  return res;
3680
4063
  };
3681
4064
 
4065
+ // ../../packages/translators/src/judgment-pattern/identity.ts
4066
+ function yamlScalar(v) {
4067
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
4068
+ return JSON.stringify(String(v));
4069
+ }
4070
+ function emitNode(key, value, indent) {
4071
+ if (value === void 0 || value === null) return [];
4072
+ const pad = " ".repeat(indent);
4073
+ if (Array.isArray(value)) {
4074
+ if (value.length === 0) return [];
4075
+ const lines = [`${pad}${key}:`];
4076
+ for (const item of value) {
4077
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
4078
+ const entries = Object.entries(item).filter(
4079
+ ([, v]) => v !== void 0 && v !== null
4080
+ );
4081
+ if (entries.length === 0) {
4082
+ lines.push(`${pad} - {}`);
4083
+ continue;
4084
+ }
4085
+ entries.forEach(([k, v], i) => {
4086
+ const prefix = i === 0 ? `${pad} - ` : `${pad} `;
4087
+ lines.push(`${prefix}${k}: ${yamlScalar(v)}`);
4088
+ });
4089
+ } else {
4090
+ lines.push(`${pad} - ${yamlScalar(item)}`);
4091
+ }
4092
+ }
4093
+ return lines;
4094
+ }
4095
+ if (typeof value === "object") {
4096
+ const entries = Object.entries(value).filter(
4097
+ ([, v]) => v !== void 0 && v !== null
4098
+ );
4099
+ if (entries.length === 0) return [];
4100
+ const lines = [`${pad}${key}:`];
4101
+ for (const [k, v] of entries) lines.push(...emitNode(k, v, indent + 1));
4102
+ return lines;
4103
+ }
4104
+ if (typeof value === "string" && value.includes("\n")) {
4105
+ const lines = [`${pad}${key}: |`];
4106
+ for (const ln of value.replace(/\n+$/, "").split("\n")) lines.push(`${pad} ${ln}`);
4107
+ return lines;
4108
+ }
4109
+ return [`${pad}${key}: ${yamlScalar(value)}`];
4110
+ }
4111
+ function buildJudgmentFile(input, outputPath) {
4112
+ const fm = input.frontmatter["version"] === void 0 && input.version ? { ...input.frontmatter, version: input.version } : input.frontmatter;
4113
+ const name = typeof fm["name"] === "string" ? fm["name"] : input.fullSlug.split("/")[1] ?? input.fullSlug;
4114
+ const lines = ["---", "type: judgment-pattern"];
4115
+ for (const [k, v] of Object.entries(fm)) lines.push(...emitNode(k, v, 0));
4116
+ lines.push("---");
4117
+ const body = input.body.trim();
4118
+ const content = lines.join("\n") + (body.length > 0 ? "\n\n" + body + "\n" : "\n");
4119
+ const resolvedPath = outputPath.replace("{name}", String(name));
4120
+ return {
4121
+ files: [{ path: resolvedPath, content, mergeable: false, format: "markdown" }],
4122
+ notes: [],
4123
+ format: "kordis-judgment-pattern"
4124
+ };
4125
+ }
4126
+ var judgmentToClaude = (input) => buildJudgmentFile(input, ".claude/judgments/{name}.md");
4127
+ var judgmentCanonical = (input) => buildJudgmentFile(input, `kordis/judgment-patterns/${input.fullSlug.replace("/", "__")}.md`);
4128
+
3682
4129
  // ../../packages/translators/src/registry.ts
3683
4130
  var REGISTRY = {
3684
4131
  skill: {
@@ -3720,6 +4167,13 @@ var REGISTRY = {
3720
4167
  hook: {
3721
4168
  canonical: hookCanonical,
3722
4169
  "claude-code": hookToClaude
4170
+ },
4171
+ // Judgment is canonical Markdown — no per-tool wiring, so both targets are
4172
+ // identity. `read`/`adapt` (JIT) is the primary path; this is the local-copy
4173
+ // fallback. `toolsForType` will report only 'claude-code' as compatible.
4174
+ "judgment-pattern": {
4175
+ canonical: judgmentCanonical,
4176
+ "claude-code": judgmentToClaude
3723
4177
  }
3724
4178
  };
3725
4179
  REGISTRY["project-rules"]["gemini-cli"] = projectRulesToGemini;
@@ -3766,7 +4220,10 @@ async function readLockfile() {
3766
4220
  }
3767
4221
  async function writeLockfile(lf) {
3768
4222
  await mkdir5(kordisHome2(), { recursive: true });
3769
- await writeFile5(lockfilePath(), JSON.stringify(lf, null, 2) + "\n", "utf8");
4223
+ await writeFile5(lockfilePath(), JSON.stringify(lf, null, 2) + "\n", {
4224
+ encoding: "utf8",
4225
+ mode: 384
4226
+ });
3770
4227
  }
3771
4228
  async function recordInstalled(fullSlug, entry) {
3772
4229
  const lf = await readLockfile();
@@ -3774,88 +4231,6 @@ async function recordInstalled(fullSlug, entry) {
3774
4231
  await writeLockfile(lf);
3775
4232
  }
3776
4233
 
3777
- // src/lib/parse-username.ts
3778
- init_src();
3779
- var SORT_KEYWORDS = ["latest", "popular", "recent", "updated", "stars", "downloads"];
3780
- function usernameError(message, hint) {
3781
- const e = new Error(message);
3782
- e.hint = hint;
3783
- return e;
3784
- }
3785
- function parseUsername(raw) {
3786
- if (SORT_KEYWORDS.includes(raw.toLowerCase())) {
3787
- throw usernameError(
3788
- `"${raw}" is a sort keyword \u2014 did you mean \`--sort ${raw}\`? Username arguments must follow the Kordis username shape (lowercase alphanumeric, hyphens allowed).`,
3789
- `Pass it as the sort flag instead, e.g. \`--sort ${raw}\`, or supply an actual username argument.`
3790
- );
3791
- }
3792
- const parsed = UsernameSchema.safeParse(raw);
3793
- if (!parsed.success) {
3794
- throw usernameError(
3795
- `invalid username "${raw}" \u2014 must be 3-32 chars, lowercase alphanumeric with single hyphens (e.g. "alice", "my-team")`,
3796
- "Usernames are lowercase alphanumeric with single hyphens. Browse existing users with `kordis search` or look one up with `kordis profile <username>`."
3797
- );
3798
- }
3799
- return parsed.data;
3800
- }
3801
-
3802
- // src/lib/parse-slug-version.ts
3803
- var SUFFIX_CHARS = /[\^~*]/;
3804
- function slugError(message, hint) {
3805
- const e = new Error(message);
3806
- e.hint = hint;
3807
- return e;
3808
- }
3809
- function parseSlugAtVersion(raw) {
3810
- if (!raw.includes("/")) {
3811
- let username2;
3812
- try {
3813
- username2 = parseUsername(raw);
3814
- } catch {
3815
- username2 = void 0;
3816
- }
3817
- if (username2) {
3818
- throw slugError(
3819
- `"${raw}" looks like a username, not a package`,
3820
- `Try \`kordis profile ${username2}\` for user details, or pass a full slug like "{username}/{slug}".`
3821
- );
3822
- }
3823
- throw slugError(
3824
- `"${raw}" is missing the "{username}/{slug}" form`,
3825
- 'Use the form "{username}/{slug}", e.g. "alice/json-pretty".'
3826
- );
3827
- }
3828
- const atIdx = raw.indexOf("@");
3829
- const slugPart = atIdx === -1 ? raw : raw.slice(0, atIdx);
3830
- const versionPart = atIdx === -1 ? "" : raw.slice(atIdx + 1);
3831
- const [username, slug] = slugPart.split("/");
3832
- if (!username || !slug) {
3833
- throw slugError(
3834
- 'full_slug must be "{username}/{slug}"',
3835
- 'Pass both parts, e.g. "alice/json-pretty". Browse with `kordis search` if unsure.'
3836
- );
3837
- }
3838
- if (atIdx !== -1 && versionPart.length === 0) {
3839
- throw slugError(
3840
- `"${raw}" has a trailing "@" with no version`,
3841
- `Drop the "@" or append a version/range, e.g. "${slugPart}@1.0.0" or "${slugPart}@^1".`
3842
- );
3843
- }
3844
- const out = {
3845
- fullSlug: slugPart,
3846
- username,
3847
- slug
3848
- };
3849
- if (versionPart.length > 0) {
3850
- if (SUFFIX_CHARS.test(versionPart)) {
3851
- out.range = versionPart;
3852
- } else {
3853
- out.version = versionPart;
3854
- }
3855
- }
3856
- return out;
3857
- }
3858
-
3859
4234
  // src/commands/pull.ts
3860
4235
  async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
3861
4236
  const [username, slug] = fullSlug.split("/");
@@ -3986,6 +4361,13 @@ function pullCommand() {
3986
4361
  ...opts.withRecommends ? result.plan?.recommended.map((n) => ({ ...n, kind: "recommended" })) ?? [] : []
3987
4362
  ];
3988
4363
  for (const node of installPlan) {
4364
+ if (!FullSlugSchema.safeParse(node.full_slug).success) {
4365
+ const e = new Error(
4366
+ `registry returned a malformed dependency slug: ${JSON.stringify(node.full_slug)}`
4367
+ );
4368
+ e.hint = "Refusing to install it. Report this package to the registry operator.";
4369
+ throw e;
4370
+ }
3989
4371
  const subSpin = spinner(
3990
4372
  ` \u21B3 ${node.kind === "required" ? "dep" : "rec"} ${node.full_slug}@${node.version}`
3991
4373
  );
@@ -4079,7 +4461,8 @@ async function detectTool(root) {
4079
4461
  return null;
4080
4462
  }
4081
4463
  function safeInstallPath(installRoot, relPath) {
4082
- if (relPath.startsWith("/") || resolve2(relPath) === relPath) {
4464
+ const windowsAbsolute = /^[A-Za-z]:/.test(relPath) || relPath.startsWith("\\\\");
4465
+ if (relPath.startsWith("/") || windowsAbsolute || resolve2(relPath) === relPath) {
4083
4466
  const e = new Error(
4084
4467
  `refusing to write absolute path "${relPath}" outside the install root`
4085
4468
  );
@@ -4109,7 +4492,7 @@ async function writeEmittedFile(abs, content, encoding) {
4109
4492
  // src/commands/read.ts
4110
4493
  import { Command as Command6 } from "commander";
4111
4494
  function readCommand() {
4112
- return new Command6("read").description("fetch a package body into the agent context (no install)").argument("<full_slug>", "package identifier {username}/{slug} or {username}/{slug}@{version}").option("--allow-pending", "allow versions whose AI review has not passed", false).option("--pin <semver>", "pin to an exact version (overrides @suffix)").action(
4495
+ return new Command6("read").description("fetch a package body into the agent context (no install)").argument("<full_slug>", "package identifier {username}/{slug} or {username}/{slug}@{version}").option("--allow-pending", "allow versions whose AI review has not passed", false).option("--pin <semver>", "pin to an exact version (overrides @suffix)").option("--outline", "print only the section map (skeleton) \u2014 cheap on context", false).option("--section <name>", "print only one section (see names with --outline)").action(
4113
4496
  async (rawSlug, opts) => {
4114
4497
  const parsed = parseSlugAtVersion(rawSlug);
4115
4498
  const pin = opts.pin ?? parsed.version ?? parsed.range;
@@ -4117,11 +4500,7 @@ function readCommand() {
4117
4500
  const bundle = await useClient();
4118
4501
  let resolvedVersion;
4119
4502
  if (pin && pinIsRange) {
4120
- const resolved = await bundle.api.resolveVersion(
4121
- parsed.username,
4122
- parsed.slug,
4123
- pin
4124
- );
4503
+ const resolved = await bundle.api.resolveVersion(parsed.username, parsed.slug, pin);
4125
4504
  resolvedVersion = resolved.resolved_version;
4126
4505
  }
4127
4506
  const versionForRead = resolvedVersion ?? pin;
@@ -4129,6 +4508,79 @@ function readCommand() {
4129
4508
  allow_pending: opts.allowPending,
4130
4509
  ...versionForRead ? { version: versionForRead } : {}
4131
4510
  });
4511
+ if (opts.outline) {
4512
+ const o = outlineOf(res.body);
4513
+ if (isJsonMode()) {
4514
+ printJson({
4515
+ full_slug: res.full_slug,
4516
+ version: res.version,
4517
+ type: res.type,
4518
+ tier: res.tier,
4519
+ trust_tier: res.trust_tier ?? null,
4520
+ description: o.description,
4521
+ sections: o.sections.map((s) => ({ name: s.name, label: s.label })),
4522
+ bundled_assets: res.bundled_assets ?? []
4523
+ });
4524
+ return;
4525
+ }
4526
+ process.stdout.write(
4527
+ `${c.bold(res.full_slug)}${c.gray("@")}${res.version} ${c.gray("\xB7")} ${res.type} (tier ${res.tier})
4528
+ `
4529
+ );
4530
+ if (o.description) process.stdout.write(`${o.description}
4531
+ `);
4532
+ process.stdout.write("\n");
4533
+ if (o.sections.length > 0) {
4534
+ process.stdout.write(
4535
+ `Sections \u2014 read one with ${c.cyan(`kordis read ${res.full_slug} --section <name>`)}:
4536
+ `
4537
+ );
4538
+ for (const s of o.sections) {
4539
+ process.stdout.write(` ${c.gray("-")} ${s.name}
4540
+ `);
4541
+ }
4542
+ } else {
4543
+ process.stdout.write(
4544
+ `No sub-sections \u2014 read the whole body with ${c.cyan(`kordis read ${res.full_slug}`)}.
4545
+ `
4546
+ );
4547
+ }
4548
+ const assets = res.bundled_assets ?? [];
4549
+ if (assets.length > 0) {
4550
+ process.stdout.write(
4551
+ `
4552
+ Bundled files \u2014 fetch with ${c.cyan(`kordis pull ${res.full_slug}`)}:
4553
+ `
4554
+ );
4555
+ for (const p of assets) process.stdout.write(` ${c.gray("-")} ${p}
4556
+ `);
4557
+ }
4558
+ return;
4559
+ }
4560
+ if (opts.section) {
4561
+ const sec = sectionOf(res.body, opts.section);
4562
+ if (!sec) {
4563
+ const names = outlineOf(res.body).sections.map((s) => s.name).join(", ");
4564
+ process.stderr.write(
4565
+ `${c.red("\u2717")} section "${opts.section}" not found. Available: ${names || "(none \u2014 this package has no sub-sections)"}
4566
+ See the map with ${c.cyan(`kordis read ${res.full_slug} --outline`)}.
4567
+ `
4568
+ );
4569
+ process.exitCode = 2;
4570
+ return;
4571
+ }
4572
+ if (isJsonMode()) {
4573
+ printJson({ full_slug: res.full_slug, version: res.version, section: sec });
4574
+ return;
4575
+ }
4576
+ process.stdout.write(sec.body);
4577
+ if (!sec.body.endsWith("\n")) process.stdout.write("\n");
4578
+ process.stderr.write(
4579
+ `${c.gray("\u2014")} ${c.bold(res.full_slug)}${c.gray("@")}${res.version} ${c.gray("\xA7")} ${sec.label} ${c.gray("\xB7")} ${c.cyan(res.attribution)}
4580
+ `
4581
+ );
4582
+ return;
4583
+ }
4132
4584
  if (isJsonMode()) {
4133
4585
  printJson(res);
4134
4586
  return;
@@ -4147,8 +4599,9 @@ function readCommand() {
4147
4599
  }
4148
4600
  }
4149
4601
  const resolvedNote = pinIsRange && resolvedVersion ? ` ${c.gray("(resolved")} ${c.gray("@" + pin)} ${c.gray("\u2192")} ${resolvedVersion}${c.gray(")")}` : "";
4602
+ const trustNote = res.trust_tier ? ` ${c.gray("\xB7")} trust: ${res.trust_tier}` : "";
4150
4603
  process.stderr.write(
4151
- `${c.gray("\u2014")} ${c.bold(res.full_slug)}${c.gray("@")}${res.version}${resolvedNote} ${c.gray("\xB7")} ${res.type} (tier ${res.tier}) ${c.gray("\xB7")} ${c.cyan(res.attribution)}
4604
+ `${c.gray("\u2014")} ${c.bold(res.full_slug)}${c.gray("@")}${res.version}${resolvedNote} ${c.gray("\xB7")} ${res.type} (tier ${res.tier})${trustNote} ${c.gray("\xB7")} ${c.cyan(res.attribution)}
4152
4605
  `
4153
4606
  );
4154
4607
  printHuman("");
@@ -4156,11 +4609,175 @@ function readCommand() {
4156
4609
  );
4157
4610
  }
4158
4611
 
4612
+ // src/commands/adapt.ts
4613
+ import { Command as Command7 } from "commander";
4614
+
4615
+ // src/context/harness-reader.ts
4616
+ import { access as access3, readFile as readFile8 } from "fs/promises";
4617
+ import { homedir as homedir8 } from "os";
4618
+ import { join as join10 } from "path";
4619
+ var PER_FILE_CAP = 16 * 1024;
4620
+ var TOTAL_CAP = 48 * 1024;
4621
+ async function pathExists3(p) {
4622
+ try {
4623
+ await access3(p);
4624
+ return true;
4625
+ } catch {
4626
+ return false;
4627
+ }
4628
+ }
4629
+ function capContent(raw, cap) {
4630
+ if (Buffer.byteLength(raw, "utf8") <= cap) return { content: raw, truncated: false };
4631
+ const lines = raw.split("\n");
4632
+ let acc = 0;
4633
+ let lastHeadingIdx = -1;
4634
+ let cutIdx = lines.length;
4635
+ for (let i = 0; i < lines.length; i++) {
4636
+ const line = lines[i] ?? "";
4637
+ const lineBytes = Buffer.byteLength(`${line}
4638
+ `, "utf8");
4639
+ if (acc + lineBytes > cap) {
4640
+ cutIdx = i;
4641
+ break;
4642
+ }
4643
+ acc += lineBytes;
4644
+ if (/^#{1,6}\s/.test(line)) lastHeadingIdx = i;
4645
+ }
4646
+ const cut = lastHeadingIdx > cutIdx / 2 ? lastHeadingIdx : cutIdx;
4647
+ const kept = lines.slice(0, cut).join("\n");
4648
+ return {
4649
+ content: `${kept}
4650
+
4651
+ [truncated \u2014 ${cut}/${lines.length} lines shown]`,
4652
+ truncated: true
4653
+ };
4654
+ }
4655
+ function extractHeadings(content) {
4656
+ return content.split("\n").filter((l) => /^#{2,3}\s/.test(l)).map((l) => l.replace(/^#{2,3}\s+/, "").trim()).slice(0, 40);
4657
+ }
4658
+ async function readHarnessContext(cwd = process.cwd(), home = homedir8()) {
4659
+ const candidates = [
4660
+ { path: join10(cwd, "CLAUDE.md"), display: "CLAUDE.md", scope: "project" },
4661
+ { path: join10(cwd, "AGENTS.md"), display: "AGENTS.md", scope: "project" },
4662
+ { path: join10(cwd, "GEMINI.md"), display: "GEMINI.md", scope: "project" },
4663
+ { path: join10(cwd, ".cursorrules"), display: ".cursorrules", scope: "project" },
4664
+ {
4665
+ path: join10(cwd, ".github", "copilot-instructions.md"),
4666
+ display: ".github/copilot-instructions.md",
4667
+ scope: "project"
4668
+ },
4669
+ { path: join10(cwd, ".claude", "CLAUDE.md"), display: ".claude/CLAUDE.md", scope: "project" },
4670
+ { path: join10(home, ".claude", "CLAUDE.md"), display: "~/.claude/CLAUDE.md", scope: "global" }
4671
+ ];
4672
+ const files = [];
4673
+ let totalBytes = 0;
4674
+ for (const cand of candidates) {
4675
+ if (totalBytes >= TOTAL_CAP) break;
4676
+ if (!await pathExists3(cand.path)) continue;
4677
+ let raw;
4678
+ try {
4679
+ raw = await readFile8(cand.path, "utf8");
4680
+ } catch {
4681
+ continue;
4682
+ }
4683
+ const cap = Math.min(PER_FILE_CAP, TOTAL_CAP - totalBytes);
4684
+ const { content, truncated } = capContent(raw, cap);
4685
+ totalBytes += Buffer.byteLength(content, "utf8");
4686
+ files.push({
4687
+ path: cand.display,
4688
+ scope: cand.scope,
4689
+ content,
4690
+ truncated,
4691
+ headings: extractHeadings(content)
4692
+ });
4693
+ }
4694
+ return { files, found: files.length > 0 };
4695
+ }
4696
+
4697
+ // src/commands/adapt.ts
4698
+ var TRANSPLANT_INSTRUCTIONS = `1. Extract the *judgment* from \xA71 \u2014 the decision logic / discipline it encodes, not its surface wording.
4699
+ 2. Fit it to the harness in \xA72: keep universal parts verbatim; re-express context-specific parts in THIS harness's own conventions, stack, and section structure. Never duplicate a rule the harness already has \u2014 reconcile instead.
4700
+ 3. For EVERY proposed change, write a plain-language explanation of what changes and WHY. The deliverable is understanding transfer, not a paste.
4701
+ 4. PROPOSE, do not apply. Present a reviewable diff/edit for the user to approve. Kordis wrote nothing to disk and neither should you without approval.
4702
+ 5. Cite attribution (the "following \u2026" line below) in whatever you add.
4703
+ 6. If the user approves and wants to share their variation back, offer to run \`kordis push --adapted-from <slug@version>\` so the lineage is recorded \u2014 do not do this silently.`;
4704
+ function renderBrief(res, harness) {
4705
+ const out = [];
4706
+ out.push("# TRANSPLANT BRIEF");
4707
+ out.push(
4708
+ "> You (the agent) fit the judgment below into the user's existing harness. Propose changes WITH explanations; do not apply them automatically."
4709
+ );
4710
+ out.push("");
4711
+ out.push(`## 1. THE JUDGMENT \u2014 ${res.full_slug}@${res.version} (${res.type}, tier ${res.tier})`);
4712
+ out.push("");
4713
+ out.push(res.body.trimEnd());
4714
+ out.push("");
4715
+ out.push("## 2. THE USER'S EXISTING HARNESS");
4716
+ if (!harness.found) {
4717
+ out.push("");
4718
+ out.push(
4719
+ "_No harness file found in this project or globally._ Propose a NEW minimal placement (e.g. create or extend `CLAUDE.md`) \u2014 do not invent structure that is not there."
4720
+ );
4721
+ } else {
4722
+ for (const f of harness.files) {
4723
+ out.push("");
4724
+ out.push(`### ${f.path} (${f.scope}${f.truncated ? ", truncated" : ""})`);
4725
+ if (f.headings.length > 0) out.push(`Sections: ${f.headings.join(" \xB7 ")}`);
4726
+ out.push("");
4727
+ out.push("```md");
4728
+ out.push(f.content.trimEnd());
4729
+ out.push("```");
4730
+ }
4731
+ }
4732
+ out.push("");
4733
+ out.push("## 3. TRANSPLANT INSTRUCTIONS");
4734
+ out.push(TRANSPLANT_INSTRUCTIONS);
4735
+ out.push("");
4736
+ out.push(`\u2014 ${res.attribution}`);
4737
+ out.push("");
4738
+ return out.join("\n");
4739
+ }
4740
+ function adaptCommand() {
4741
+ return new Command7("adapt").description(
4742
+ "fit a judgment pattern to your existing harness (prints a transplant brief; writes nothing)"
4743
+ ).argument("<full_slug>", "package identifier {username}/{slug} or {username}/{slug}@{version}").option("--allow-pending", "allow versions whose AI review has not passed", false).option("--pin <semver>", "pin to an exact version (overrides @suffix)").action(async (rawSlug, opts) => {
4744
+ const parsed = parseSlugAtVersion(rawSlug);
4745
+ const pin = opts.pin ?? parsed.version ?? parsed.range;
4746
+ const pinIsRange = pin ? /[\^~*]/.test(pin) : false;
4747
+ const bundle = await useClient();
4748
+ let resolvedVersion;
4749
+ if (pin && pinIsRange) {
4750
+ const resolved = await bundle.api.resolveVersion(parsed.username, parsed.slug, pin);
4751
+ resolvedVersion = resolved.resolved_version;
4752
+ }
4753
+ const versionForRead = resolvedVersion ?? pin;
4754
+ const res = await bundle.api.jitRead(parsed.username, parsed.slug, {
4755
+ allow_pending: opts.allowPending,
4756
+ ...versionForRead ? { version: versionForRead } : {}
4757
+ });
4758
+ const harness = await readHarnessContext();
4759
+ if (isJsonMode()) {
4760
+ printJson({
4761
+ full_slug: res.full_slug,
4762
+ version: res.version,
4763
+ type: res.type,
4764
+ tier: res.tier,
4765
+ attribution: res.attribution,
4766
+ judgment_body: res.body,
4767
+ host_harness: harness,
4768
+ transplant_instructions: TRANSPLANT_INSTRUCTIONS
4769
+ });
4770
+ return;
4771
+ }
4772
+ process.stdout.write(renderBrief(res, harness));
4773
+ });
4774
+ }
4775
+
4159
4776
  // src/commands/info.ts
4160
4777
  init_src();
4161
- import { Command as Command7 } from "commander";
4778
+ import { Command as Command8 } from "commander";
4162
4779
  function infoCommand() {
4163
- return new Command7("info").description("show details about a package").argument(
4780
+ return new Command8("info").description("show details about a package").argument(
4164
4781
  "<full_slug>",
4165
4782
  "package identifier {username}/{slug} or {username}/{slug}@{version|range}"
4166
4783
  ).option("--pin <semver>", "pin to an exact version or range (overrides @suffix)").action(async (fullSlug, opts) => {
@@ -4224,9 +4841,9 @@ function infoCommand() {
4224
4841
  }
4225
4842
 
4226
4843
  // src/commands/logout.ts
4227
- import { Command as Command8 } from "commander";
4844
+ import { Command as Command9 } from "commander";
4228
4845
  function logoutCommand() {
4229
- return new Command8("logout").description("revoke the current session and clear local credentials").action(async () => {
4846
+ return new Command9("logout").description("revoke the current session and clear local credentials").action(async () => {
4230
4847
  const bundle = await useClient();
4231
4848
  if (!bundle.username) {
4232
4849
  printHuman(c.yellow("Not logged in; nothing to do."));
@@ -4249,11 +4866,11 @@ function logoutCommand() {
4249
4866
  }
4250
4867
 
4251
4868
  // src/commands/setup.ts
4252
- import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, access as access3 } from "fs/promises";
4253
- import { homedir as homedir8 } from "os";
4254
- import { join as join10, dirname as dirname3 } from "path";
4869
+ import { readFile as readFile9, writeFile as writeFile7, mkdir as mkdir7, access as access4 } from "fs/promises";
4870
+ import { homedir as homedir9 } from "os";
4871
+ import { join as join11, dirname as dirname3 } from "path";
4255
4872
  import { fileURLToPath as fileURLToPath2 } from "url";
4256
- import { Command as Command9 } from "commander";
4873
+ import { Command as Command10 } from "commander";
4257
4874
  import prompts from "prompts";
4258
4875
  var MARKER_START = "<!-- kordis-start -->";
4259
4876
  var MARKER_END = "<!-- kordis-end -->";
@@ -4276,7 +4893,7 @@ var SKILL_TARGETS = [
4276
4893
  ];
4277
4894
  async function exists(path2) {
4278
4895
  try {
4279
- await access3(path2);
4896
+ await access4(path2);
4280
4897
  return true;
4281
4898
  } catch {
4282
4899
  return false;
@@ -4286,18 +4903,18 @@ function assetRoots() {
4286
4903
  const __dir2 = dirname3(fileURLToPath2(import.meta.url));
4287
4904
  return {
4288
4905
  // Built binary: bin/kordis.js → assets/ is one level up
4289
- bundled: join10(__dir2, "..", "assets"),
4906
+ bundled: join11(__dir2, "..", "assets"),
4290
4907
  // Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
4291
- source: join10(__dir2, "..", "..", "..", "..", "packages", "skills")
4908
+ source: join11(__dir2, "..", "..", "..", "..", "packages", "skills")
4292
4909
  };
4293
4910
  }
4294
4911
  async function pickAssetRoot() {
4295
4912
  const { bundled, source } = assetRoots();
4296
- return await exists(join10(bundled, "KORDIS.md")) ? bundled : source;
4913
+ return await exists(join11(bundled, "KORDIS.md")) ? bundled : source;
4297
4914
  }
4298
4915
  async function loadHubContent() {
4299
4916
  const root = await pickAssetRoot();
4300
- const raw = await readFile8(join10(root, "KORDIS.md"), "utf-8");
4917
+ const raw = await readFile9(join11(root, "KORDIS.md"), "utf-8");
4301
4918
  if (raw.startsWith("---")) {
4302
4919
  const end = raw.indexOf("\n---", 4);
4303
4920
  if (end !== -1) return raw.slice(end + 4).trimStart();
@@ -4306,7 +4923,7 @@ async function loadHubContent() {
4306
4923
  }
4307
4924
  async function loadSubSkillContent(name) {
4308
4925
  const root = await pickAssetRoot();
4309
- return readFile8(join10(root, name, "SKILL.md"), "utf-8");
4926
+ return readFile9(join11(root, name, "SKILL.md"), "utf-8");
4310
4927
  }
4311
4928
  async function injectBlock(filePath, content, dryRun) {
4312
4929
  const block = `${MARKER_START}
@@ -4315,7 +4932,7 @@ ${MARKER_END}`;
4315
4932
  let existing = "";
4316
4933
  let fileExists = true;
4317
4934
  try {
4318
- existing = await readFile8(filePath, "utf-8");
4935
+ existing = await readFile9(filePath, "utf-8");
4319
4936
  } catch {
4320
4937
  fileExists = false;
4321
4938
  }
@@ -4345,7 +4962,7 @@ ${MARKER_END}`;
4345
4962
  async function copyBundledSkill(filePath, content, dryRun) {
4346
4963
  let existing = null;
4347
4964
  try {
4348
- existing = await readFile8(filePath, "utf-8");
4965
+ existing = await readFile9(filePath, "utf-8");
4349
4966
  } catch {
4350
4967
  }
4351
4968
  if (existing === content) return "unchanged";
@@ -4356,14 +4973,14 @@ async function copyBundledSkill(filePath, content, dryRun) {
4356
4973
  return existing === null ? "injected" : "updated";
4357
4974
  }
4358
4975
  function setupCommand() {
4359
- return new Command9("setup").description("inject kordis usage guide into global agent instruction files").option("--silent", "suppress errors \u2014 safe for postinstall").option("--dry-run", "detect installed tools without writing any files").option(
4976
+ return new Command10("setup").description("inject kordis usage guide into global agent instruction files").option("--silent", "suppress errors \u2014 safe for postinstall").option("--dry-run", "detect installed tools without writing any files").option(
4360
4977
  "--personalization <choice>",
4361
4978
  "record opt-in non-interactively: 'on' | 'off' (skips the Y/n prompt; safe for agents that just received user consent in chat)"
4362
4979
  ).option(
4363
4980
  "--error-telemetry <choice>",
4364
4981
  "record opt-in non-interactively: 'on' | 'off' (skips the Y/n prompt; agent forwards consent received in chat)"
4365
4982
  ).action(async (opts) => {
4366
- const home = homedir8();
4983
+ const home = homedir9();
4367
4984
  const dryRun = opts.dryRun ?? false;
4368
4985
  if (opts.personalization !== void 0) {
4369
4986
  const v = opts.personalization.toLowerCase();
@@ -4422,8 +5039,8 @@ function setupCommand() {
4422
5039
  let anyFound = false;
4423
5040
  const changedTools = /* @__PURE__ */ new Set();
4424
5041
  for (const target of HUB_TARGETS) {
4425
- const dirPath = join10(home, target.dir);
4426
- const filePath = join10(home, target.file);
5042
+ const dirPath = join11(home, target.dir);
5043
+ const filePath = join11(home, target.file);
4427
5044
  if (!await exists(dirPath)) {
4428
5045
  if (!opts.silent) {
4429
5046
  printHuman(c.gray(` - ${target.tool.padEnd(12)} not installed, skipped`));
@@ -4450,10 +5067,10 @@ function setupCommand() {
4450
5067
  }
4451
5068
  }
4452
5069
  for (const target of SKILL_TARGETS) {
4453
- const dirPath = join10(home, target.dir);
5070
+ const dirPath = join11(home, target.dir);
4454
5071
  if (!await exists(dirPath)) continue;
4455
5072
  for (const subName of SUB_SKILLS) {
4456
- const filePath = join10(home, target.skillsDir, subName, "SKILL.md");
5073
+ const filePath = join11(home, target.skillsDir, subName, "SKILL.md");
4457
5074
  const dest = `~/${target.skillsDir}/${subName}/SKILL.md`;
4458
5075
  try {
4459
5076
  const result = await copyBundledSkill(filePath, subSkillContents[subName], dryRun);
@@ -4541,7 +5158,7 @@ function setupCommand() {
4541
5158
  }
4542
5159
 
4543
5160
  // src/commands/comment.ts
4544
- import { Command as Command10 } from "commander";
5161
+ import { Command as Command11 } from "commander";
4545
5162
  var KINDS = [
4546
5163
  "comment",
4547
5164
  "tip",
@@ -4552,7 +5169,7 @@ var KINDS = [
4552
5169
  var LIST_SORTS = ["recent", "old"];
4553
5170
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4554
5171
  function commentCommand() {
4555
- 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(
5172
+ return new Command11("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(
4556
5173
  async (rawSlug, opts) => {
4557
5174
  if (!KINDS.includes(opts.kind)) {
4558
5175
  throw new Error(`--kind must be one of ${KINDS.join(", ")}`);
@@ -4588,7 +5205,7 @@ function commentCommand() {
4588
5205
  );
4589
5206
  }
4590
5207
  function commentsCommand() {
4591
- 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(
5208
+ return new Command11("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(
4592
5209
  "--kind <kind>",
4593
5210
  `filter top-level posts by kind: ${KINDS.join("|")}`
4594
5211
  ).option("--sort <mode>", `sort: ${LIST_SORTS.join("|")} (default: recent)`).action(
@@ -4662,7 +5279,7 @@ function renderCommentNode(node, depth) {
4662
5279
  }
4663
5280
  }
4664
5281
  function uncommentCommand() {
4665
- return new Command10("uncomment").description("delete one of your comments by id").argument("<id>", "comment id (uuid v4)").action(async (id) => {
5282
+ return new Command11("uncomment").description("delete one of your comments by id").argument("<id>", "comment id (uuid v4)").action(async (id) => {
4666
5283
  if (!UUID_RE.test(id)) {
4667
5284
  throw new Error(
4668
5285
  "comment id must be a uuid v4 \u2014 get it from `kordis comments <slug>` or the `id` field on `kordis comment`'s response"
@@ -4681,9 +5298,9 @@ function uncommentCommand() {
4681
5298
  }
4682
5299
 
4683
5300
  // src/commands/star.ts
4684
- import { Command as Command11 } from "commander";
5301
+ import { Command as Command12 } from "commander";
4685
5302
  function starCommand() {
4686
- return new Command11("star").description("star a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (rawSlug) => {
5303
+ return new Command12("star").description("star a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (rawSlug) => {
4687
5304
  const [username, slug] = rawSlug.split("/");
4688
5305
  if (!username || !slug) {
4689
5306
  const e = new Error('full_slug must be "{username}/{slug}"');
@@ -4703,7 +5320,7 @@ function starCommand() {
4703
5320
  });
4704
5321
  }
4705
5322
  function unstarCommand() {
4706
- return new Command11("unstar").description("remove your star from a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (rawSlug) => {
5323
+ return new Command12("unstar").description("remove your star from a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (rawSlug) => {
4707
5324
  const [username, slug] = rawSlug.split("/");
4708
5325
  if (!username || !slug) {
4709
5326
  const e = new Error('full_slug must be "{username}/{slug}"');
@@ -4726,10 +5343,10 @@ function unstarCommand() {
4726
5343
  }
4727
5344
 
4728
5345
  // src/commands/delete.ts
4729
- import { Command as Command12 } from "commander";
5346
+ import { Command as Command13 } from "commander";
4730
5347
  import prompts2 from "prompts";
4731
5348
  function deleteCommand() {
4732
- return new Command12("delete").description("permanently delete a package you own").argument("<full_slug>", "package identifier {username}/{slug}").option("--force", "skip the confirmation prompt").action(async (fullSlug, opts) => {
5349
+ return new Command13("delete").description("permanently delete a package you own").argument("<full_slug>", "package identifier {username}/{slug}").option("--force", "skip the confirmation prompt").action(async (fullSlug, opts) => {
4733
5350
  const [username, slug] = fullSlug.split("/");
4734
5351
  if (!username || !slug) {
4735
5352
  const e = new Error('full_slug must be "{username}/{slug}"');
@@ -4767,10 +5384,10 @@ function deleteCommand() {
4767
5384
  }
4768
5385
 
4769
5386
  // src/commands/yank.ts
4770
- import { Command as Command13 } from "commander";
5387
+ import { Command as Command14 } from "commander";
4771
5388
  import prompts3 from "prompts";
4772
5389
  function yankCommand() {
4773
- return new Command13("yank").description("soft-mark a specific version of your package as yanked").argument(
5390
+ return new Command14("yank").description("soft-mark a specific version of your package as yanked").argument(
4774
5391
  "<full_slug>",
4775
5392
  'package identifier \u2014 bare slug yanks current version (e.g. "user/slug"), or pin: "user/slug@1.2.3"'
4776
5393
  ).option("--reason <text>", "optional reason shown to consumers (\u2264280 chars)").option("--force", "skip the confirmation prompt").action(
@@ -4851,9 +5468,9 @@ function yankCommand() {
4851
5468
  }
4852
5469
 
4853
5470
  // src/commands/unyank.ts
4854
- import { Command as Command14 } from "commander";
5471
+ import { Command as Command15 } from "commander";
4855
5472
  function unyankCommand() {
4856
- return new Command14("unyank").description("reverse a yank on a specific version").argument(
5473
+ return new Command15("unyank").description("reverse a yank on a specific version").argument(
4857
5474
  "<full_slug>",
4858
5475
  'package identifier with explicit version, e.g. "user/slug@1.2.3"'
4859
5476
  ).action(async (fullSlugArg) => {
@@ -4894,9 +5511,9 @@ function unyankCommand() {
4894
5511
  }
4895
5512
 
4896
5513
  // src/commands/list.ts
4897
- import { Command as Command15 } from "commander";
5514
+ import { Command as Command16 } from "commander";
4898
5515
  function listCommand() {
4899
- return new Command15("list").description("list packages \u2014 your own (no arg) or a public user\u2019s (with <username>)").argument("[username]", "kordis username to list (omit for self)").option("--limit <n>", "max results per page", (v) => parseInt(v, 10), 20).option("--page <n>", "self-mode page number (1-based)", (v) => parseInt(v, 10), 1).option("--cursor <token>", "public-mode pagination cursor").option("--sort <mode>", "sort: latest|popular (self) or stars|downloads|recent|updated (public)", "latest").action(async (rawUsername, opts) => {
5516
+ return new Command16("list").description("list packages \u2014 your own (no arg) or a public user\u2019s (with <username>)").argument("[username]", "kordis username to list (omit for self)").option("--limit <n>", "max results per page", (v) => parseInt(v, 10), 20).option("--page <n>", "self-mode page number (1-based)", (v) => parseInt(v, 10), 1).option("--cursor <token>", "public-mode pagination cursor").option("--sort <mode>", "sort: latest|popular (self) or stars|downloads|recent|updated (public)", "latest").action(async (rawUsername, opts) => {
4900
5517
  const bundle = await useClient();
4901
5518
  if (rawUsername === void 0) {
4902
5519
  requireAuth(bundle);
@@ -4969,9 +5586,9 @@ function listCommand() {
4969
5586
  }
4970
5587
 
4971
5588
  // src/commands/versions.ts
4972
- import { Command as Command16 } from "commander";
5589
+ import { Command as Command17 } from "commander";
4973
5590
  function versionsCommand() {
4974
- return new Command16("versions").description("list all versions of a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (fullSlug) => {
5591
+ return new Command17("versions").description("list all versions of a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (fullSlug) => {
4975
5592
  const [username, slug] = fullSlug.split("/");
4976
5593
  if (!username || !slug) {
4977
5594
  const e = new Error('full_slug must be "{username}/{slug}"');
@@ -5032,13 +5649,13 @@ function relativeTime(isoString) {
5032
5649
  }
5033
5650
 
5034
5651
  // src/commands/outdated.ts
5035
- import { Command as Command17 } from "commander";
5652
+ import { Command as Command18 } from "commander";
5036
5653
  import semver from "semver";
5037
5654
  function effectiveRange(entry) {
5038
5655
  return entry.requestedRange ?? `^${entry.version}`;
5039
5656
  }
5040
5657
  function outdatedCommand() {
5041
- return new Command17("outdated").description("check installed packages for available updates").action(async () => {
5658
+ return new Command18("outdated").description("check installed packages for available updates").action(async () => {
5042
5659
  const lf = await readLockfile();
5043
5660
  const slugs = Object.keys(lf.packages);
5044
5661
  if (slugs.length === 0) {
@@ -5136,13 +5753,13 @@ function outdatedCommand() {
5136
5753
  }
5137
5754
 
5138
5755
  // src/commands/update.ts
5139
- import { Command as Command18 } from "commander";
5756
+ import { Command as Command19 } from "commander";
5140
5757
  import semver2 from "semver";
5141
5758
  function effectiveRange2(entry) {
5142
5759
  return entry.requestedRange ?? `^${entry.version}`;
5143
5760
  }
5144
5761
  function updateCommand() {
5145
- return new Command18("update").description("update installed packages to their latest versions").argument("[full_slug]", "package identifier {username}/{slug} (omit to update all)").option("--dry-run", "show what would be updated without doing it", false).action(async (fullSlugArg, opts) => {
5762
+ return new Command19("update").description("update installed packages to their latest versions").argument("[full_slug]", "package identifier {username}/{slug} (omit to update all)").option("--dry-run", "show what would be updated without doing it", false).action(async (fullSlugArg, opts) => {
5146
5763
  const lf = await readLockfile();
5147
5764
  let slugs = Object.keys(lf.packages);
5148
5765
  if (slugs.length === 0) {
@@ -5276,9 +5893,9 @@ function updateCommand() {
5276
5893
  }
5277
5894
 
5278
5895
  // src/commands/diff.ts
5279
- import { Command as Command19 } from "commander";
5896
+ import { Command as Command20 } from "commander";
5280
5897
  function diffCommand() {
5281
- return new Command19("diff").description("show changes between two versions of a package").argument("<full_slug>", "package identifier: {username}/{slug}, or {username}/{slug}@{from}..{to}").argument("[from]", "older version (omit to use second-latest)").argument("[to]", "newer version (omit to use latest)").action(async (fullSlugArg, fromArg, toArg) => {
5898
+ return new Command20("diff").description("show changes between two versions of a package").argument("<full_slug>", "package identifier: {username}/{slug}, or {username}/{slug}@{from}..{to}").argument("[from]", "older version (omit to use second-latest)").argument("[to]", "newer version (omit to use latest)").action(async (fullSlugArg, fromArg, toArg) => {
5282
5899
  let fullSlug = fullSlugArg;
5283
5900
  let from = fromArg;
5284
5901
  let to = toArg;
@@ -5351,10 +5968,10 @@ function diffCommand() {
5351
5968
  }
5352
5969
 
5353
5970
  // src/commands/recommend.ts
5354
- import { Command as Command20 } from "commander";
5971
+ import { Command as Command21 } from "commander";
5355
5972
  import prompts4 from "prompts";
5356
5973
  function recommendCommand() {
5357
- return new Command20("recommend").description("show personalized package recommendations").option("--limit <n>", "max items to return (1-50)", (v) => parseInt(v, 10), 10).option("--verbose", "include score_breakdown per item", false).option("--no-context", "skip the X-Kordis-Context header for this call").action(async (opts) => {
5974
+ return new Command21("recommend").description("show personalized package recommendations").option("--limit <n>", "max items to return (1-50)", (v) => parseInt(v, 10), 10).option("--verbose", "include score_breakdown per item", false).option("--no-context", "skip the X-Kordis-Context header for this call").action(async (opts) => {
5358
5975
  const cfg = await loadCliConfig();
5359
5976
  if (cfg.personalization === "undecided" && process.stdin.isTTY && !isJsonMode() && opts.context !== false) {
5360
5977
  const { enable } = await prompts4({
@@ -5463,9 +6080,9 @@ function renderItem(idx, it, verbose) {
5463
6080
  }
5464
6081
 
5465
6082
  // src/commands/profile.ts
5466
- import { Command as Command21 } from "commander";
6083
+ import { Command as Command22 } from "commander";
5467
6084
  function profileCommand() {
5468
- return new Command21("profile").description("inspect a user\u2019s public profile").argument("<username>", "kordis username").action(async (raw) => {
6085
+ return new Command22("profile").description("inspect a user\u2019s public profile").argument("<username>", "kordis username").action(async (raw) => {
5469
6086
  const username = parseUsername(raw);
5470
6087
  const bundle = await useClient();
5471
6088
  const profile = await bundle.api.getUserProfile(username);
@@ -5500,9 +6117,9 @@ function profileCommand() {
5500
6117
  }
5501
6118
 
5502
6119
  // src/commands/stars.ts
5503
- import { Command as Command22 } from "commander";
6120
+ import { Command as Command23 } from "commander";
5504
6121
  function starsCommand() {
5505
- return new Command22("stars").description("list packages a user has starred").argument("<username>", "kordis username").option("--limit <n>", "max results per page (1-100, default 50)", (v) => parseInt(v, 10), 50).option("--cursor <token>", "pagination cursor returned by a prior response").action(async (raw, opts) => {
6122
+ return new Command23("stars").description("list packages a user has starred").argument("<username>", "kordis username").option("--limit <n>", "max results per page (1-100, default 50)", (v) => parseInt(v, 10), 50).option("--cursor <token>", "pagination cursor returned by a prior response").action(async (raw, opts) => {
5506
6123
  const username = parseUsername(raw);
5507
6124
  const bundle = await useClient();
5508
6125
  const res = await bundle.api.listUserStars(username, {
@@ -5535,9 +6152,9 @@ function starsCommand() {
5535
6152
  }
5536
6153
 
5537
6154
  // src/commands/follow.ts
5538
- import { Command as Command23 } from "commander";
6155
+ import { Command as Command24 } from "commander";
5539
6156
  function followCommand() {
5540
- return new Command23("follow").description("follow a kordis user").argument("<username>", "kordis username").action(async (raw) => {
6157
+ return new Command24("follow").description("follow a kordis user").argument("<username>", "kordis username").action(async (raw) => {
5541
6158
  const username = parseUsername(raw);
5542
6159
  const bundle = await useClient();
5543
6160
  requireAuth(bundle);
@@ -5552,7 +6169,7 @@ function followCommand() {
5552
6169
  });
5553
6170
  }
5554
6171
  function unfollowCommand() {
5555
- return new Command23("unfollow").description("stop following a kordis user").argument("<username>", "kordis username").action(async (raw) => {
6172
+ return new Command24("unfollow").description("stop following a kordis user").argument("<username>", "kordis username").action(async (raw) => {
5556
6173
  const username = parseUsername(raw);
5557
6174
  const bundle = await useClient();
5558
6175
  requireAuth(bundle);
@@ -5568,9 +6185,9 @@ function unfollowCommand() {
5568
6185
  }
5569
6186
 
5570
6187
  // src/commands/followers.ts
5571
- import { Command as Command24 } from "commander";
6188
+ import { Command as Command25 } from "commander";
5572
6189
  function makeListing(name) {
5573
- return new Command24(name).description(
6190
+ return new Command25(name).description(
5574
6191
  name === "followers" ? "list users following <username>" : "list users that <username> follows"
5575
6192
  ).argument("<username>", "kordis username").option("--limit <n>", "max results per page (1-100, default 50)", (v) => parseInt(v, 10), 50).option("--cursor <token>", "pagination cursor returned by a prior response").action(async (raw, opts) => {
5576
6193
  const username = parseUsername(raw);
@@ -5617,10 +6234,12 @@ function followingCommand() {
5617
6234
 
5618
6235
  // src/index.ts
5619
6236
  function buildProgram() {
5620
- const program = new Command25("kordis").version(VERSION).description("Kordis \u2014 registry for agentic-coding packages").option("--json", "emit JSON output to stdout (default: human)", false).hook("preAction", (thisCommand) => {
6237
+ const program = new Command26("kordis").version(VERSION).description("Kordis \u2014 registry for agentic-coding packages").option("--json", "emit JSON output to stdout (default: human)", false).hook("preAction", (thisCommand) => {
5621
6238
  const opts = thisCommand.optsWithGlobals();
5622
6239
  if (opts.json) setJsonMode(true);
5623
- }).addHelpText("after", `
6240
+ }).addHelpText(
6241
+ "after",
6242
+ `
5624
6243
  Environment variables (see README.md "Privacy & environment variables"):
5625
6244
  KORDIS_API_URL=<url> override registry endpoint (default: https://api.kordis.store)
5626
6245
  KORDIS_HOME=<dir> override config + token fallback dir (default: ~/.kordis)
@@ -5629,7 +6248,8 @@ Environment variables (see README.md "Privacy & environment variables"):
5629
6248
  KORDIS_NO_KEYCHAIN=1 force plaintext token storage at ~/.kordis/.tokens.json
5630
6249
  (escape hatch for macOS keychain-prompt fatigue)
5631
6250
  KORDIS_DEBUG=1 surface auto-finalize stage outcomes to stderr (diagnostic)
5632
- `);
6251
+ `
6252
+ );
5633
6253
  program.addCommand(loginCommand());
5634
6254
  program.addCommand(logoutCommand());
5635
6255
  program.addCommand(whoamiCommand());
@@ -5637,6 +6257,7 @@ Environment variables (see README.md "Privacy & environment variables"):
5637
6257
  program.addCommand(pushCommand());
5638
6258
  program.addCommand(pullCommand());
5639
6259
  program.addCommand(readCommand());
6260
+ program.addCommand(adaptCommand());
5640
6261
  program.addCommand(infoCommand());
5641
6262
  program.addCommand(commentCommand());
5642
6263
  program.addCommand(commentsCommand());