@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/README.md +23 -19
- package/assets/KORDIS.md +19 -8
- package/assets/kordis-publishing/SKILL.md +85 -14
- package/assets/kordis-search/SKILL.md +38 -15
- package/assets/kordis-social/SKILL.md +23 -7
- package/assets/kordis-using/SKILL.md +94 -34
- package/dist/index.js +853 -232
- package/package.json +5 -5
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
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
1275
|
-
|
|
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
|
-
|
|
1286
|
-
|
|
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
|
-
|
|
1442
|
-
|
|
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
|
|
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", "
|
|
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
|
|
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 ?? "
|
|
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
|
-
|
|
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: "
|
|
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(
|
|
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(
|
|
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 ? "
|
|
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
|
|
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((
|
|
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
|
-
"--
|
|
3001
|
-
|
|
3002
|
-
|
|
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(
|
|
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 = [
|
|
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",
|
|
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
|
-
|
|
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
|
|
4778
|
+
import { Command as Command8 } from "commander";
|
|
4162
4779
|
function infoCommand() {
|
|
4163
|
-
return new
|
|
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
|
|
4844
|
+
import { Command as Command9 } from "commander";
|
|
4228
4845
|
function logoutCommand() {
|
|
4229
|
-
return new
|
|
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
|
|
4253
|
-
import { homedir as
|
|
4254
|
-
import { join as
|
|
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
|
|
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
|
|
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:
|
|
4906
|
+
bundled: join11(__dir2, "..", "assets"),
|
|
4290
4907
|
// Dev mode: src/commands/setup.ts → 4 levels up to repo root, then packages/skills/
|
|
4291
|
-
source:
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
4426
|
-
const filePath =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5301
|
+
import { Command as Command12 } from "commander";
|
|
4685
5302
|
function starCommand() {
|
|
4686
|
-
return new
|
|
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
|
|
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
|
|
5346
|
+
import { Command as Command13 } from "commander";
|
|
4730
5347
|
import prompts2 from "prompts";
|
|
4731
5348
|
function deleteCommand() {
|
|
4732
|
-
return new
|
|
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
|
|
5387
|
+
import { Command as Command14 } from "commander";
|
|
4771
5388
|
import prompts3 from "prompts";
|
|
4772
5389
|
function yankCommand() {
|
|
4773
|
-
return new
|
|
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
|
|
5471
|
+
import { Command as Command15 } from "commander";
|
|
4855
5472
|
function unyankCommand() {
|
|
4856
|
-
return new
|
|
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
|
|
5514
|
+
import { Command as Command16 } from "commander";
|
|
4898
5515
|
function listCommand() {
|
|
4899
|
-
return new
|
|
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
|
|
5589
|
+
import { Command as Command17 } from "commander";
|
|
4973
5590
|
function versionsCommand() {
|
|
4974
|
-
return new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5896
|
+
import { Command as Command20 } from "commander";
|
|
5280
5897
|
function diffCommand() {
|
|
5281
|
-
return new
|
|
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
|
|
5971
|
+
import { Command as Command21 } from "commander";
|
|
5355
5972
|
import prompts4 from "prompts";
|
|
5356
5973
|
function recommendCommand() {
|
|
5357
|
-
return new
|
|
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
|
|
6083
|
+
import { Command as Command22 } from "commander";
|
|
5467
6084
|
function profileCommand() {
|
|
5468
|
-
return new
|
|
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
|
|
6120
|
+
import { Command as Command23 } from "commander";
|
|
5504
6121
|
function starsCommand() {
|
|
5505
|
-
return new
|
|
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
|
|
6155
|
+
import { Command as Command24 } from "commander";
|
|
5539
6156
|
function followCommand() {
|
|
5540
|
-
return new
|
|
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
|
|
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
|
|
6188
|
+
import { Command as Command25 } from "commander";
|
|
5572
6189
|
function makeListing(name) {
|
|
5573
|
-
return new
|
|
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
|
|
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(
|
|
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());
|