@rayul/kordis 0.9.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/kordis-publishing/SKILL.md +35 -1
- package/assets/kordis-using/SKILL.md +27 -1
- package/dist/index.js +266 -33
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kordis-publishing
|
|
3
|
-
description: Use whenever the user wants to publish, share, or push a package to Kordis — phrases like "publish this", "share this skill to Kordis", "push this rule", "release a new version of <slug>", "bump the version", "semver", "버전 올리기". Covers dry-run flow, frontmatter requirements, version bump strategies, semver rules, and the AI review pipeline.
|
|
3
|
+
description: Use whenever the user wants to publish, share, or push a package to Kordis — phrases like "publish this", "share this skill to Kordis", "push this rule", "release a new version of <slug>", "bump the version", "semver", "버전 올리기", "yank", "withdraw a version", "unpublish a release", "철회". Covers dry-run flow, frontmatter requirements, version bump strategies, semver rules, yank/unyank for retracting bad releases, and the AI review pipeline.
|
|
4
4
|
type: skill
|
|
5
5
|
allowed-tools: ["Bash(kordis:*)", "Read", "Edit"]
|
|
6
6
|
---
|
|
@@ -36,6 +36,40 @@ single `.md` file that doesn't match the SKILL.md filename pattern.
|
|
|
36
36
|
use only `--bump patch|minor|major` to control versioning. Having both
|
|
37
37
|
is the most common push failure.
|
|
38
38
|
|
|
39
|
+
## Yanking a bad version (0.10.0+)
|
|
40
|
+
|
|
41
|
+
When a single release goes wrong — leaked secret, broken on platform X,
|
|
42
|
+
superseded by an urgent fix — use **yank**, not delete.
|
|
43
|
+
|
|
44
|
+
### `kordis yank <full_slug>@<version> [--reason <text>] [--force]`
|
|
45
|
+
Soft-marks the specific version as "unrecommended". Author-only.
|
|
46
|
+
Versions yanked this way:
|
|
47
|
+
- Disappear from `kordis pull <slug>` (bare slug) and from `@^X.Y` /
|
|
48
|
+
`@X.x` / `@*` range resolution
|
|
49
|
+
- **Still install** when explicitly pinned to that exact version
|
|
50
|
+
(`kordis pull <slug>@1.2.3`) — but the consumer's CLI prints a
|
|
51
|
+
stderr warning naming the reason. This preserves reproducibility of
|
|
52
|
+
prior pinned installs.
|
|
53
|
+
- Reversible at any time with `kordis unyank`
|
|
54
|
+
|
|
55
|
+
`--reason` is plain text capped at 280 chars — shown verbatim to
|
|
56
|
+
consumers in `info` / `versions` / pull warning. Examples that work
|
|
57
|
+
well: `"leaked AWS key"`, `"broken on Node 18"`, `"superseded by
|
|
58
|
+
0.5.0 — security fix"`.
|
|
59
|
+
|
|
60
|
+
### `kordis unyank <full_slug>@<version>`
|
|
61
|
+
Reverses a yank. Idempotent. After unyank, the version re-enters
|
|
62
|
+
range resolution and `current_version` recomputes if appropriate.
|
|
63
|
+
|
|
64
|
+
### Yank vs delete — pick the right one
|
|
65
|
+
- **`kordis yank <slug>@<version>`** — one bad release. Reversible.
|
|
66
|
+
Per-version. Preserves pinned-install reproducibility.
|
|
67
|
+
- **`kordis delete <slug>`** — the whole package should disappear.
|
|
68
|
+
Permanent + slug reservation forever (no successor can republish
|
|
69
|
+
under the same name). Last-resort.
|
|
70
|
+
|
|
71
|
+
If you just pushed a wrong version, the answer is almost always yank.
|
|
72
|
+
|
|
39
73
|
## Deleting a package
|
|
40
74
|
|
|
41
75
|
### `kordis delete <full_slug> [--force]`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kordis-using
|
|
3
|
-
description: Use whenever the user wants to consult or install a Kordis package — including any `<username>/<slug>` reference, "install this skill", "use the X skill", "fetch the Y rule", "set this repo up with…", "pin to version", "specific version", "버전 고정", "전이 의존성", "transitive dependency". Covers JIT read vs pull, the five Kordis package types, dependencies, version pinning, range syntax, and attribution. Load eagerly when in doubt.
|
|
3
|
+
description: Use whenever the user wants to consult or install a Kordis package — including any `<username>/<slug>` reference, "install this skill", "use the X skill", "fetch the Y rule", "set this repo up with…", "pin to version", "specific version", "버전 고정", "전이 의존성", "transitive dependency", "yanked", "version withdrawn", "철회", "unrecommended version". Covers JIT read vs pull, the five Kordis package types, dependencies, version pinning, range syntax, yank semantics, and attribution. Load eagerly when in doubt.
|
|
4
4
|
type: skill
|
|
5
5
|
allowed-tools: ["Bash(kordis:*)"]
|
|
6
6
|
---
|
|
@@ -111,6 +111,7 @@ Supported forms:
|
|
|
111
111
|
| `user/foo@1.0.0-beta.1` | exact prerelease (only matches if the literal exists) |
|
|
112
112
|
| `user/foo@^1.2` | caret — highest 1.x.y (x≥2). For 0.x.y, caret pins the minor |
|
|
113
113
|
| `user/foo@~1.2` | tilde — highest 1.2.y |
|
|
114
|
+
| `user/foo@0.1.x` | x-range — alias for `~0.1` (0.9.3+; semver-native) |
|
|
114
115
|
| `user/foo@*` | alias for `current_version` |
|
|
115
116
|
|
|
116
117
|
Comparator strings (`>=1 <2`, `||`) are NOT supported in 0.9.x —
|
|
@@ -131,6 +132,31 @@ transitive deps — read is a one-shot context inject, no install plan.
|
|
|
131
132
|
`kordis info user/foo@^1` shows a `Requested: 1.4.2 (current: 2.0.0)`
|
|
132
133
|
line above `Type` when the pin differs from the registry default.
|
|
133
134
|
|
|
135
|
+
## When a version is yanked (0.10.0+)
|
|
136
|
+
|
|
137
|
+
Authors can soft-mark a specific release as "yanked" via `kordis yank
|
|
138
|
+
<slug>@<version>` — usually to discourage installs of a broken or
|
|
139
|
+
unsafe version. Yank is reversible (`kordis unyank`), per-version (NOT
|
|
140
|
+
whole-package), and preserves reproducibility of prior pinned
|
|
141
|
+
installs.
|
|
142
|
+
|
|
143
|
+
Three install behaviours:
|
|
144
|
+
|
|
145
|
+
| Input | Behaviour |
|
|
146
|
+
| ---------------------------------- | ---------------------------------------------------------------- |
|
|
147
|
+
| `kordis pull <slug>` | Always picks the highest **non-yanked** version. Yanked versions are silently skipped — user sees the next-best release. |
|
|
148
|
+
| `kordis pull <slug>@^X.Y` | Range resolution skips yanked. If every match is yanked, server returns `not_found` with hint "every version satisfying X has been yanked". |
|
|
149
|
+
| `kordis pull <slug>@<exact>` (pinned to a yanked version) | Succeeds (exit 0) with a stderr warning: `⚠ pulled yanked version X of slug (reason: …) — author marked this as unrecommended on YYYY-MM-DD`. Stays installable so old lockfiles keep working. |
|
|
150
|
+
|
|
151
|
+
`kordis info <slug>` shows a red `⚠ Yanked YYYY-MM-DD` banner with the
|
|
152
|
+
reason when the resolved version is yanked. `kordis versions <slug>`
|
|
153
|
+
greys out yanked rows and suffixes their status with ` (yanked)`.
|
|
154
|
+
|
|
155
|
+
Transitive yank: if `A → B@^1` and the only `B@^1` is yanked, server
|
|
156
|
+
returns `dependency_conflict` (exit 35) with a chain link naming the
|
|
157
|
+
parent. Surface that chain to the user — fixing usually means pinning
|
|
158
|
+
A's requirement to a non-yanked range, or contacting B's author.
|
|
159
|
+
|
|
134
160
|
## Dependencies
|
|
135
161
|
`kordis pull` reports `depends` and `recommends` from the package metadata.
|
|
136
162
|
Surface the dependency tree to the user FIRST. If they accept,
|
package/dist/index.js
CHANGED
|
@@ -334,7 +334,14 @@ var init_package = __esm({
|
|
|
334
334
|
* concrete resolved version. Absent for bare-slug calls (where the
|
|
335
335
|
* response reflects `current_version`).
|
|
336
336
|
*/
|
|
337
|
-
requested_version: SemverSchema.optional()
|
|
337
|
+
requested_version: SemverSchema.optional(),
|
|
338
|
+
/**
|
|
339
|
+
* 0.10.0+ — yank status of the *displayed* version (requested_version
|
|
340
|
+
* if pinned, else current_version). Absent for non-yanked versions
|
|
341
|
+
* so pre-0.10 clients keep ignoring unknown fields.
|
|
342
|
+
*/
|
|
343
|
+
yanked_at: IsoDateTimeSchema.nullable().optional(),
|
|
344
|
+
yank_reason: z6.string().max(280).nullable().optional()
|
|
338
345
|
});
|
|
339
346
|
PackageVersionSchema = z6.object({
|
|
340
347
|
id: UuidSchema,
|
|
@@ -347,7 +354,15 @@ var init_package = __esm({
|
|
|
347
354
|
review_status: ReviewStatusSchema,
|
|
348
355
|
review_score: z6.number().nullable(),
|
|
349
356
|
review_flags: z6.array(ReviewFlagSchema),
|
|
350
|
-
published_at: IsoDateTimeSchema
|
|
357
|
+
published_at: IsoDateTimeSchema,
|
|
358
|
+
/**
|
|
359
|
+
* 0.10.0+ — when set, this version was soft-marked as yanked by the
|
|
360
|
+
* author (or an admin override). Excluded from range/`*` resolution
|
|
361
|
+
* but still installable when explicitly pinned (`@<exact>`). Null
|
|
362
|
+
* for active versions.
|
|
363
|
+
*/
|
|
364
|
+
yanked_at: IsoDateTimeSchema.nullable().optional(),
|
|
365
|
+
yank_reason: z6.string().max(280).nullable().optional()
|
|
351
366
|
});
|
|
352
367
|
PackageVersionSummarySchema = PackageVersionSchema.omit({ content: true });
|
|
353
368
|
VersionDiffSchema = z6.object({
|
|
@@ -500,7 +515,7 @@ var init_auth = __esm({
|
|
|
500
515
|
|
|
501
516
|
// ../../packages/contracts/src/api.ts
|
|
502
517
|
import { z as z9 } from "zod";
|
|
503
|
-
var SearchQuerySchema, SearchResponseSchema, TrendingQuerySchema, RisingQuerySchema, StarResponseSchema, AssetSchema, PublishPackageRequestSchema, PublishWaitQuerySchema, PublishPackageResponseSchema, PackagePathParamsSchema, ContentQuerySchema, PackageContentResponseSchema, JitReadRequestSchema, JitReadResponseSchema, PullPackageRequestSchema, PullPackageResponseSchema, ResolveVersionQuerySchema, ResolveVersionResponseSchema, GetPackageQuerySchema, ListVersionsResponseSchema, VersionDiffQuerySchema, ListCommentsQuerySchema, ListCommentsResponseSchema, UserProfileResponseSchema, UserPackagesQuerySchema, ListUserPackagesResponseSchema, ListUserStarsResponseSchema, FollowResponseSchema, ListFollowersQuerySchema, ListFollowersResponseSchema;
|
|
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;
|
|
504
519
|
var init_api = __esm({
|
|
505
520
|
"../../packages/contracts/src/api.ts"() {
|
|
506
521
|
"use strict";
|
|
@@ -692,6 +707,17 @@ var init_api = __esm({
|
|
|
692
707
|
installed_paths: z9.array(z9.string()),
|
|
693
708
|
tool_detected: ToolSlugSchema.nullable(),
|
|
694
709
|
format: z9.string(),
|
|
710
|
+
/**
|
|
711
|
+
* 0.10.0+ — true when the version being pulled was explicitly
|
|
712
|
+
* pinned (@version) AND has been yanked by its author. CLI uses this
|
|
713
|
+
* to print the stderr warning. Bare-slug / range pulls never set
|
|
714
|
+
* this flag (those paths silently skip yanked versions). Field is
|
|
715
|
+
* omitted entirely (not false) when not relevant, so pre-0.10
|
|
716
|
+
* clients ignore it cleanly.
|
|
717
|
+
*/
|
|
718
|
+
yanked: z9.boolean().optional(),
|
|
719
|
+
yank_reason: z9.string().max(280).nullable().optional(),
|
|
720
|
+
yanked_at: IsoDateTimeSchema.nullable().optional(),
|
|
695
721
|
dependencies: z9.object({
|
|
696
722
|
required: z9.array(
|
|
697
723
|
z9.object({
|
|
@@ -718,6 +744,16 @@ var init_api = __esm({
|
|
|
718
744
|
}),
|
|
719
745
|
recommends_hint: z9.string().nullable()
|
|
720
746
|
});
|
|
747
|
+
YankRequestSchema = z9.object({
|
|
748
|
+
reason: z9.string().max(280).optional()
|
|
749
|
+
});
|
|
750
|
+
YankResponseSchema = z9.object({
|
|
751
|
+
full_slug: FullSlugSchema,
|
|
752
|
+
version: SemverSchema,
|
|
753
|
+
yanked_at: IsoDateTimeSchema.nullable(),
|
|
754
|
+
yank_reason: z9.string().max(280).nullable(),
|
|
755
|
+
effective_current_version: SemverSchema
|
|
756
|
+
});
|
|
721
757
|
ResolveVersionQuerySchema = z9.object({
|
|
722
758
|
range: SemverRangeSchema
|
|
723
759
|
});
|
|
@@ -1101,6 +1137,8 @@ var init_client = __esm({
|
|
|
1101
1137
|
if (opts.body !== void 0) init.body = JSON.stringify(opts.body);
|
|
1102
1138
|
const method = (init.method ?? "GET").toUpperCase();
|
|
1103
1139
|
const retryable = method === "GET";
|
|
1140
|
+
const debug = typeof process !== "undefined" && process.env && (process.env["KORDIS_DEBUG"] === "1" || process.env["KORDIS_DEBUG"]?.toLowerCase() === "true");
|
|
1141
|
+
const debugStart = debug ? Date.now() : 0;
|
|
1104
1142
|
let lastErr;
|
|
1105
1143
|
for (let attempt = 0; attempt < (retryable ? 2 : 1); attempt++) {
|
|
1106
1144
|
try {
|
|
@@ -1110,12 +1148,22 @@ var init_client = __esm({
|
|
|
1110
1148
|
} catch (err) {
|
|
1111
1149
|
lastErr = err;
|
|
1112
1150
|
if (attempt === 0 && retryable) {
|
|
1151
|
+
if (debug) {
|
|
1152
|
+
console.error(
|
|
1153
|
+
`[kordis] api: ${method} ${url.pathname}${url.search} \u2192 fetch failed, retrying in 150ms (${err.message})`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1113
1156
|
await new Promise((resolve2) => setTimeout(resolve2, 150));
|
|
1114
1157
|
continue;
|
|
1115
1158
|
}
|
|
1116
1159
|
}
|
|
1117
1160
|
}
|
|
1118
1161
|
if (lastErr !== void 0) {
|
|
1162
|
+
if (debug) {
|
|
1163
|
+
console.error(
|
|
1164
|
+
`[kordis] api: ${method} ${url.pathname}${url.search} \u2192 ${lastErr.message} (${Date.now() - debugStart}ms)`
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1119
1167
|
throw new KordisNetworkError(
|
|
1120
1168
|
`cannot reach ${url.hostname}: ${lastErr.message}`,
|
|
1121
1169
|
lastErr
|
|
@@ -1123,6 +1171,11 @@ var init_client = __esm({
|
|
|
1123
1171
|
}
|
|
1124
1172
|
res = res;
|
|
1125
1173
|
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
1174
|
+
if (debug) {
|
|
1175
|
+
console.error(
|
|
1176
|
+
`[kordis] api: ${method} ${url.pathname}${url.search} \u2192 ${res.status} (${Date.now() - debugStart}ms${requestId ? `, req-id=${requestId}` : ""})`
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1126
1179
|
if (res.status === 204) return void 0;
|
|
1127
1180
|
const text = await res.text();
|
|
1128
1181
|
const parsed = text ? JSON.parse(text) : void 0;
|
|
@@ -1285,6 +1338,25 @@ var init_client = __esm({
|
|
|
1285
1338
|
`/api/packages/${username}/${slug}/versions`
|
|
1286
1339
|
);
|
|
1287
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* 0.10.0+ — soft-yank a specific version. Author-only by default;
|
|
1343
|
+
* pass `asAdmin: true` to use the admin override (server validates
|
|
1344
|
+
* the caller's role). Reason is plain text, 280 chars max.
|
|
1345
|
+
*/
|
|
1346
|
+
yankVersion(username, slug, version, body = {}, opts = {}) {
|
|
1347
|
+
const query = opts.asAdmin ? { as: "admin" } : void 0;
|
|
1348
|
+
return this.request(
|
|
1349
|
+
`/api/packages/${username}/${slug}/versions/${version}/yank`,
|
|
1350
|
+
{ method: "POST", body, ...query ? { query } : {} }
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
unyankVersion(username, slug, version, opts = {}) {
|
|
1354
|
+
const query = opts.asAdmin ? { as: "admin" } : void 0;
|
|
1355
|
+
return this.request(
|
|
1356
|
+
`/api/packages/${username}/${slug}/versions/${version}/yank`,
|
|
1357
|
+
{ method: "DELETE", ...query ? { query } : {} }
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1288
1360
|
diff(username, slug, from, to) {
|
|
1289
1361
|
return this.request(`/api/packages/${username}/${slug}/diff`, {
|
|
1290
1362
|
query: { from, to }
|
|
@@ -1413,7 +1485,7 @@ var init_src2 = __esm({
|
|
|
1413
1485
|
|
|
1414
1486
|
// src/index.ts
|
|
1415
1487
|
init_src2();
|
|
1416
|
-
import { Command as
|
|
1488
|
+
import { Command as Command25 } from "commander";
|
|
1417
1489
|
|
|
1418
1490
|
// src/commands/login.ts
|
|
1419
1491
|
import { Command } from "commander";
|
|
@@ -3842,7 +3914,12 @@ async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
|
|
|
3842
3914
|
installRoot,
|
|
3843
3915
|
written,
|
|
3844
3916
|
notes,
|
|
3845
|
-
plan: pullRes.dependencies
|
|
3917
|
+
plan: pullRes.dependencies,
|
|
3918
|
+
...pullRes.yanked ? {
|
|
3919
|
+
yanked: true,
|
|
3920
|
+
yankReason: pullRes.yank_reason ?? null,
|
|
3921
|
+
yankedAt: pullRes.yanked_at ?? null
|
|
3922
|
+
} : {}
|
|
3846
3923
|
};
|
|
3847
3924
|
}
|
|
3848
3925
|
function pullCommand() {
|
|
@@ -3856,10 +3933,10 @@ function pullCommand() {
|
|
|
3856
3933
|
).option(
|
|
3857
3934
|
"--no-with-required",
|
|
3858
3935
|
"skip transitive required dependencies (advanced; default is to install)"
|
|
3859
|
-
).action(
|
|
3936
|
+
).option("--pin <semver>", "pin to an exact version or range (overrides @suffix)").action(
|
|
3860
3937
|
async (fullSlugArg, opts) => {
|
|
3861
3938
|
const parsed = parseSlugAtVersion(fullSlugArg);
|
|
3862
|
-
const pin = parsed.version ?? parsed.range;
|
|
3939
|
+
const pin = opts.pin ?? parsed.version ?? parsed.range;
|
|
3863
3940
|
const userCwd = process.env["INIT_CWD"] ?? process.cwd();
|
|
3864
3941
|
const installRoot = opts.target ?? userCwd;
|
|
3865
3942
|
const detected = opts.tool ?? await detectTool(installRoot);
|
|
@@ -3911,6 +3988,11 @@ function pullCommand() {
|
|
|
3911
3988
|
tool_detected: result.tool,
|
|
3912
3989
|
installed_paths: result.written,
|
|
3913
3990
|
notes: result.notes,
|
|
3991
|
+
...result.yanked ? {
|
|
3992
|
+
yanked: true,
|
|
3993
|
+
yank_reason: result.yankReason,
|
|
3994
|
+
yanked_at: result.yankedAt
|
|
3995
|
+
} : {},
|
|
3914
3996
|
dependencies: dependencyInstalls.map((d) => ({
|
|
3915
3997
|
full_slug: d.fullSlug,
|
|
3916
3998
|
version: d.result.version,
|
|
@@ -3921,6 +4003,16 @@ function pullCommand() {
|
|
|
3921
4003
|
printHuman(
|
|
3922
4004
|
c.green(`\u2713 Installed ${c.bold(parsed.fullSlug)} v${result.version}`)
|
|
3923
4005
|
);
|
|
4006
|
+
if (result.yanked) {
|
|
4007
|
+
const reasonSuffix = result.yankReason ? ` (reason: ${result.yankReason})` : "";
|
|
4008
|
+
const dateSuffix = result.yankedAt ? ` on ${new Date(result.yankedAt).toISOString().slice(0, 10)}` : "";
|
|
4009
|
+
process.stderr.write(
|
|
4010
|
+
c.yellow(
|
|
4011
|
+
`\u26A0 pulled yanked version ${result.version} of ${parsed.fullSlug}${reasonSuffix} \u2014 author marked this as unrecommended${dateSuffix}
|
|
4012
|
+
`
|
|
4013
|
+
)
|
|
4014
|
+
);
|
|
4015
|
+
}
|
|
3924
4016
|
for (const w of result.written) printHuman(` ${c.gray("wrote")} ${w}`);
|
|
3925
4017
|
for (const n of result.notes) printHuman(c.yellow(` ! ${n}`));
|
|
3926
4018
|
for (const dep of dependencyInstalls) {
|
|
@@ -4043,6 +4135,15 @@ function infoCommand() {
|
|
|
4043
4135
|
printHuman(c.bold(detail.full_slug) + " " + c.gray(`v${headerVersion}`));
|
|
4044
4136
|
printHuman(" " + detail.description);
|
|
4045
4137
|
printHuman("");
|
|
4138
|
+
if (detail.yanked_at) {
|
|
4139
|
+
printHuman(
|
|
4140
|
+
c.red(" \u26A0 Yanked") + " " + c.gray(new Date(detail.yanked_at).toISOString().slice(0, 10))
|
|
4141
|
+
);
|
|
4142
|
+
if (detail.yank_reason) {
|
|
4143
|
+
printHuman(c.gray(` reason: ${detail.yank_reason}`));
|
|
4144
|
+
}
|
|
4145
|
+
printHuman("");
|
|
4146
|
+
}
|
|
4046
4147
|
if (detail.requested_version && detail.requested_version !== detail.current_version) {
|
|
4047
4148
|
printHuman(
|
|
4048
4149
|
` ${c.cyan("Requested")}: ${detail.requested_version} ${c.gray(`(current: ${detail.current_version})`)}`
|
|
@@ -4613,10 +4714,135 @@ function deleteCommand() {
|
|
|
4613
4714
|
});
|
|
4614
4715
|
}
|
|
4615
4716
|
|
|
4616
|
-
// src/commands/
|
|
4717
|
+
// src/commands/yank.ts
|
|
4617
4718
|
import { Command as Command13 } from "commander";
|
|
4719
|
+
import prompts3 from "prompts";
|
|
4720
|
+
function yankCommand() {
|
|
4721
|
+
return new Command13("yank").description("soft-mark a specific version of your package as yanked").argument(
|
|
4722
|
+
"<full_slug>",
|
|
4723
|
+
'package identifier with explicit version, e.g. "user/slug@1.2.3"'
|
|
4724
|
+
).option("--reason <text>", "optional reason shown to consumers (\u2264280 chars)").option("--force", "skip the confirmation prompt").action(
|
|
4725
|
+
async (fullSlugArg, opts) => {
|
|
4726
|
+
const parsed = parseSlugAtVersion(fullSlugArg);
|
|
4727
|
+
if (!parsed.version) {
|
|
4728
|
+
throw new Error(
|
|
4729
|
+
'yank requires an explicit version pin \u2014 use the form "user/slug@1.2.3" (range syntax not allowed)'
|
|
4730
|
+
);
|
|
4731
|
+
}
|
|
4732
|
+
const bundle = await useClient();
|
|
4733
|
+
const detail = await bundle.api.getPackage(parsed.username, parsed.slug);
|
|
4734
|
+
if (!opts.force) {
|
|
4735
|
+
printHuman(c.yellow(" \u26A0 You are about to yank:"));
|
|
4736
|
+
printHuman(
|
|
4737
|
+
` ${c.bold(detail.full_slug)} v${parsed.version}`
|
|
4738
|
+
);
|
|
4739
|
+
if (opts.reason) {
|
|
4740
|
+
printHuman(` reason: ${opts.reason}`);
|
|
4741
|
+
}
|
|
4742
|
+
printHuman(
|
|
4743
|
+
c.gray(
|
|
4744
|
+
" Yanked versions disappear from range/`*` resolution. Pinned installs (`@" + parsed.version + "`) still work. Reversible with `kordis unyank`."
|
|
4745
|
+
)
|
|
4746
|
+
);
|
|
4747
|
+
printHuman("");
|
|
4748
|
+
const { confirmed } = await prompts3({
|
|
4749
|
+
type: "confirm",
|
|
4750
|
+
name: "confirmed",
|
|
4751
|
+
message: `Yank ${c.bold(`${parsed.fullSlug}@${parsed.version}`)}?`,
|
|
4752
|
+
initial: false
|
|
4753
|
+
});
|
|
4754
|
+
if (!confirmed) {
|
|
4755
|
+
printHuman(c.gray("Aborted."));
|
|
4756
|
+
process.exit(0);
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
const res = await bundle.authedRequest(
|
|
4760
|
+
(api) => api.yankVersion(parsed.username, parsed.slug, parsed.version, {
|
|
4761
|
+
...opts.reason ? { reason: opts.reason } : {}
|
|
4762
|
+
})
|
|
4763
|
+
);
|
|
4764
|
+
printDual({
|
|
4765
|
+
json: res,
|
|
4766
|
+
human: () => {
|
|
4767
|
+
printHuman(
|
|
4768
|
+
c.green(
|
|
4769
|
+
`\u2713 Yanked ${c.bold(parsed.fullSlug)}@${parsed.version}`
|
|
4770
|
+
)
|
|
4771
|
+
);
|
|
4772
|
+
if (res.yank_reason) {
|
|
4773
|
+
printHuman(c.gray(` reason: ${res.yank_reason}`));
|
|
4774
|
+
}
|
|
4775
|
+
if (res.effective_current_version !== parsed.version) {
|
|
4776
|
+
printHuman(
|
|
4777
|
+
c.gray(
|
|
4778
|
+
` current_version is now ${c.bold(res.effective_current_version)} (was ${parsed.version})`
|
|
4779
|
+
)
|
|
4780
|
+
);
|
|
4781
|
+
} else {
|
|
4782
|
+
printHuman(
|
|
4783
|
+
c.gray(
|
|
4784
|
+
` current_version unchanged at ${c.bold(res.effective_current_version)} (no higher non-yanked version exists)`
|
|
4785
|
+
)
|
|
4786
|
+
);
|
|
4787
|
+
}
|
|
4788
|
+
printHuman(
|
|
4789
|
+
c.gray(
|
|
4790
|
+
` \u2192 Reverse with: kordis unyank ${parsed.fullSlug}@${parsed.version}`
|
|
4791
|
+
)
|
|
4792
|
+
);
|
|
4793
|
+
}
|
|
4794
|
+
});
|
|
4795
|
+
}
|
|
4796
|
+
);
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
// src/commands/unyank.ts
|
|
4800
|
+
import { Command as Command14 } from "commander";
|
|
4801
|
+
function unyankCommand() {
|
|
4802
|
+
return new Command14("unyank").description("reverse a yank on a specific version").argument(
|
|
4803
|
+
"<full_slug>",
|
|
4804
|
+
'package identifier with explicit version, e.g. "user/slug@1.2.3"'
|
|
4805
|
+
).action(async (fullSlugArg) => {
|
|
4806
|
+
const parsed = parseSlugAtVersion(fullSlugArg);
|
|
4807
|
+
if (!parsed.version) {
|
|
4808
|
+
throw new Error(
|
|
4809
|
+
'unyank requires an explicit version pin \u2014 use "user/slug@1.2.3"'
|
|
4810
|
+
);
|
|
4811
|
+
}
|
|
4812
|
+
const bundle = await useClient();
|
|
4813
|
+
const res = await bundle.authedRequest(
|
|
4814
|
+
(api) => api.unyankVersion(parsed.username, parsed.slug, parsed.version)
|
|
4815
|
+
);
|
|
4816
|
+
printDual({
|
|
4817
|
+
json: res,
|
|
4818
|
+
human: () => {
|
|
4819
|
+
printHuman(
|
|
4820
|
+
c.green(
|
|
4821
|
+
`\u2713 Unyanked ${c.bold(parsed.fullSlug)}@${parsed.version}`
|
|
4822
|
+
)
|
|
4823
|
+
);
|
|
4824
|
+
if (res.effective_current_version !== parsed.version) {
|
|
4825
|
+
printHuman(
|
|
4826
|
+
c.gray(
|
|
4827
|
+
` current_version is ${c.bold(res.effective_current_version)} (highest non-yanked)`
|
|
4828
|
+
)
|
|
4829
|
+
);
|
|
4830
|
+
} else {
|
|
4831
|
+
printHuman(
|
|
4832
|
+
c.gray(
|
|
4833
|
+
` current_version is now ${c.bold(res.effective_current_version)}`
|
|
4834
|
+
)
|
|
4835
|
+
);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
});
|
|
4839
|
+
});
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
// src/commands/list.ts
|
|
4843
|
+
import { Command as Command15 } from "commander";
|
|
4618
4844
|
function listCommand() {
|
|
4619
|
-
return new
|
|
4845
|
+
return new Command15("list").description("list packages \u2014 your own (no arg) or a public user\u2019s (with <username>)").argument("[username]", "kordis username to list (omit for self)").option("--limit <n>", "max results per page", (v) => parseInt(v, 10), 20).option("--page <n>", "self-mode page number (1-based)", (v) => parseInt(v, 10), 1).option("--cursor <token>", "public-mode pagination cursor").option("--sort <mode>", "sort: latest|popular (self) or stars|downloads|recent|updated (public)", "latest").action(async (rawUsername, opts) => {
|
|
4620
4846
|
const bundle = await useClient();
|
|
4621
4847
|
if (rawUsername === void 0) {
|
|
4622
4848
|
requireAuth(bundle);
|
|
@@ -4687,9 +4913,9 @@ function listCommand() {
|
|
|
4687
4913
|
}
|
|
4688
4914
|
|
|
4689
4915
|
// src/commands/versions.ts
|
|
4690
|
-
import { Command as
|
|
4916
|
+
import { Command as Command16 } from "commander";
|
|
4691
4917
|
function versionsCommand() {
|
|
4692
|
-
return new
|
|
4918
|
+
return new Command16("versions").description("list all versions of a package").argument("<full_slug>", "package identifier {username}/{slug}").action(async (fullSlug) => {
|
|
4693
4919
|
const [username, slug] = fullSlug.split("/");
|
|
4694
4920
|
if (!username || !slug) {
|
|
4695
4921
|
throw new Error('full_slug must be "{username}/{slug}"');
|
|
@@ -4706,12 +4932,17 @@ function versionsCommand() {
|
|
|
4706
4932
|
);
|
|
4707
4933
|
printHuman("");
|
|
4708
4934
|
for (const v of res.versions) {
|
|
4709
|
-
const
|
|
4935
|
+
const isYanked = Boolean(v.yanked_at);
|
|
4936
|
+
const reviewLabel = isYanked ? c.gray(`${v.review_status} (yanked)`) : v.review_status === "passed" ? c.green(v.review_status) : v.review_status === "rejected" ? c.red(v.review_status) : c.yellow(v.review_status);
|
|
4710
4937
|
const age = relativeTime(v.published_at);
|
|
4711
4938
|
const isCurrent = v.version === res.current_version;
|
|
4939
|
+
const versionLabel = isYanked ? c.gray(v.version) : c.bold(v.version);
|
|
4712
4940
|
printHuman(
|
|
4713
|
-
` ${
|
|
4941
|
+
` ${versionLabel}${isCurrent ? c.cyan(" (current)") : ""} ${reviewLabel} ${c.gray(age)}`
|
|
4714
4942
|
);
|
|
4943
|
+
if (isYanked && v.yank_reason) {
|
|
4944
|
+
printHuman(` ${c.gray(`reason: ${v.yank_reason}`)}`);
|
|
4945
|
+
}
|
|
4715
4946
|
}
|
|
4716
4947
|
}
|
|
4717
4948
|
});
|
|
@@ -4743,13 +4974,13 @@ function relativeTime(isoString) {
|
|
|
4743
4974
|
}
|
|
4744
4975
|
|
|
4745
4976
|
// src/commands/outdated.ts
|
|
4746
|
-
import { Command as
|
|
4977
|
+
import { Command as Command17 } from "commander";
|
|
4747
4978
|
import semver from "semver";
|
|
4748
4979
|
function effectiveRange(entry) {
|
|
4749
4980
|
return entry.requestedRange ?? `^${entry.version}`;
|
|
4750
4981
|
}
|
|
4751
4982
|
function outdatedCommand() {
|
|
4752
|
-
return new
|
|
4983
|
+
return new Command17("outdated").description("check installed packages for available updates").action(async () => {
|
|
4753
4984
|
const lf = await readLockfile();
|
|
4754
4985
|
const slugs = Object.keys(lf.packages);
|
|
4755
4986
|
if (slugs.length === 0) {
|
|
@@ -4847,13 +5078,13 @@ function outdatedCommand() {
|
|
|
4847
5078
|
}
|
|
4848
5079
|
|
|
4849
5080
|
// src/commands/update.ts
|
|
4850
|
-
import { Command as
|
|
5081
|
+
import { Command as Command18 } from "commander";
|
|
4851
5082
|
import semver2 from "semver";
|
|
4852
5083
|
function effectiveRange2(entry) {
|
|
4853
5084
|
return entry.requestedRange ?? `^${entry.version}`;
|
|
4854
5085
|
}
|
|
4855
5086
|
function updateCommand() {
|
|
4856
|
-
return new
|
|
5087
|
+
return new Command18("update").description("update installed packages to their latest versions").argument("[full_slug]", "package identifier {username}/{slug} (omit to update all)").option("--dry-run", "show what would be updated without doing it", false).action(async (fullSlugArg, opts) => {
|
|
4857
5088
|
const lf = await readLockfile();
|
|
4858
5089
|
let slugs = Object.keys(lf.packages);
|
|
4859
5090
|
if (slugs.length === 0) {
|
|
@@ -4987,9 +5218,9 @@ function updateCommand() {
|
|
|
4987
5218
|
}
|
|
4988
5219
|
|
|
4989
5220
|
// src/commands/diff.ts
|
|
4990
|
-
import { Command as
|
|
5221
|
+
import { Command as Command19 } from "commander";
|
|
4991
5222
|
function diffCommand() {
|
|
4992
|
-
return new
|
|
5223
|
+
return new Command19("diff").description("show changes between two versions of a package").argument("<full_slug>", "package identifier: {username}/{slug}, or {username}/{slug}@{from}..{to}").argument("[from]", "older version (omit to use second-latest)").argument("[to]", "newer version (omit to use latest)").action(async (fullSlugArg, fromArg, toArg) => {
|
|
4993
5224
|
let fullSlug = fullSlugArg;
|
|
4994
5225
|
let from = fromArg;
|
|
4995
5226
|
let to = toArg;
|
|
@@ -5060,13 +5291,13 @@ function diffCommand() {
|
|
|
5060
5291
|
}
|
|
5061
5292
|
|
|
5062
5293
|
// src/commands/recommend.ts
|
|
5063
|
-
import { Command as
|
|
5064
|
-
import
|
|
5294
|
+
import { Command as Command20 } from "commander";
|
|
5295
|
+
import prompts4 from "prompts";
|
|
5065
5296
|
function recommendCommand() {
|
|
5066
|
-
return new
|
|
5297
|
+
return new Command20("recommend").description("show personalized package recommendations").option("--limit <n>", "max items to return (1-50)", (v) => parseInt(v, 10), 10).option("--verbose", "include score_breakdown per item", false).option("--no-context", "skip the X-Kordis-Context header for this call").action(async (opts) => {
|
|
5067
5298
|
const cfg = await loadCliConfig();
|
|
5068
5299
|
if (cfg.personalization === "undecided" && process.stdin.isTTY && !isJsonMode() && opts.context !== false) {
|
|
5069
|
-
const { enable } = await
|
|
5300
|
+
const { enable } = await prompts4({
|
|
5070
5301
|
type: "confirm",
|
|
5071
5302
|
name: "enable",
|
|
5072
5303
|
message: "Personalize recommendations using anonymous local signals (installed agent tools, language stats, frameworks)? Server never stores them \u2014 only re-ranks results in-flight.",
|
|
@@ -5171,9 +5402,9 @@ function renderItem(idx, it, verbose) {
|
|
|
5171
5402
|
}
|
|
5172
5403
|
|
|
5173
5404
|
// src/commands/profile.ts
|
|
5174
|
-
import { Command as
|
|
5405
|
+
import { Command as Command21 } from "commander";
|
|
5175
5406
|
function profileCommand() {
|
|
5176
|
-
return new
|
|
5407
|
+
return new Command21("profile").description("inspect a user\u2019s public profile").argument("<username>", "kordis username").action(async (raw) => {
|
|
5177
5408
|
const username = parseUsername(raw);
|
|
5178
5409
|
const bundle = await useClient();
|
|
5179
5410
|
const profile = await bundle.api.getUserProfile(username);
|
|
@@ -5208,9 +5439,9 @@ function profileCommand() {
|
|
|
5208
5439
|
}
|
|
5209
5440
|
|
|
5210
5441
|
// src/commands/stars.ts
|
|
5211
|
-
import { Command as
|
|
5442
|
+
import { Command as Command22 } from "commander";
|
|
5212
5443
|
function starsCommand() {
|
|
5213
|
-
return new
|
|
5444
|
+
return new Command22("stars").description("list packages a user has starred").argument("<username>", "kordis username").option("--limit <n>", "max results per page (1-100, default 50)", (v) => parseInt(v, 10), 50).option("--cursor <token>", "pagination cursor returned by a prior response").action(async (raw, opts) => {
|
|
5214
5445
|
const username = parseUsername(raw);
|
|
5215
5446
|
const bundle = await useClient();
|
|
5216
5447
|
const res = await bundle.api.listUserStars(username, {
|
|
@@ -5242,9 +5473,9 @@ function starsCommand() {
|
|
|
5242
5473
|
}
|
|
5243
5474
|
|
|
5244
5475
|
// src/commands/follow.ts
|
|
5245
|
-
import { Command as
|
|
5476
|
+
import { Command as Command23 } from "commander";
|
|
5246
5477
|
function followCommand() {
|
|
5247
|
-
return new
|
|
5478
|
+
return new Command23("follow").description("follow a kordis user").argument("<username>", "kordis username").action(async (raw) => {
|
|
5248
5479
|
const username = parseUsername(raw);
|
|
5249
5480
|
const bundle = await useClient();
|
|
5250
5481
|
requireAuth(bundle);
|
|
@@ -5259,7 +5490,7 @@ function followCommand() {
|
|
|
5259
5490
|
});
|
|
5260
5491
|
}
|
|
5261
5492
|
function unfollowCommand() {
|
|
5262
|
-
return new
|
|
5493
|
+
return new Command23("unfollow").description("stop following a kordis user").argument("<username>", "kordis username").action(async (raw) => {
|
|
5263
5494
|
const username = parseUsername(raw);
|
|
5264
5495
|
const bundle = await useClient();
|
|
5265
5496
|
requireAuth(bundle);
|
|
@@ -5275,9 +5506,9 @@ function unfollowCommand() {
|
|
|
5275
5506
|
}
|
|
5276
5507
|
|
|
5277
5508
|
// src/commands/followers.ts
|
|
5278
|
-
import { Command as
|
|
5509
|
+
import { Command as Command24 } from "commander";
|
|
5279
5510
|
function makeListing(name) {
|
|
5280
|
-
return new
|
|
5511
|
+
return new Command24(name).description(
|
|
5281
5512
|
name === "followers" ? "list users following <username>" : "list users that <username> follows"
|
|
5282
5513
|
).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) => {
|
|
5283
5514
|
const username = parseUsername(raw);
|
|
@@ -5322,7 +5553,7 @@ function followingCommand() {
|
|
|
5322
5553
|
|
|
5323
5554
|
// src/index.ts
|
|
5324
5555
|
function buildProgram() {
|
|
5325
|
-
const program = new
|
|
5556
|
+
const program = new Command25("kordis").version(VERSION).description("Kordis \u2014 registry for agentic-coding packages").option("--json", "emit JSON output to stdout (default: human)", false).hook("preAction", (thisCommand) => {
|
|
5326
5557
|
const opts = thisCommand.optsWithGlobals();
|
|
5327
5558
|
if (opts.json) setJsonMode(true);
|
|
5328
5559
|
}).addHelpText("after", `
|
|
@@ -5355,6 +5586,8 @@ Environment variables (see README.md "Privacy & environment variables"):
|
|
|
5355
5586
|
program.addCommand(followersCommand());
|
|
5356
5587
|
program.addCommand(followingCommand());
|
|
5357
5588
|
program.addCommand(deleteCommand());
|
|
5589
|
+
program.addCommand(yankCommand());
|
|
5590
|
+
program.addCommand(unyankCommand());
|
|
5358
5591
|
program.addCommand(listCommand());
|
|
5359
5592
|
program.addCommand(versionsCommand());
|
|
5360
5593
|
program.addCommand(outdatedCommand());
|