@rayul/kordis 0.8.1 → 0.9.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/assets/kordis-publishing/SKILL.md +26 -1
- package/assets/kordis-using/SKILL.md +41 -7
- package/dist/index.js +330 -107
- package/package.json +3 -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>". Covers dry-run flow, frontmatter requirements, version bump strategies, 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", "버전 올리기". Covers dry-run flow, frontmatter requirements, version bump strategies, semver rules, and the AI review pipeline.
|
|
4
4
|
type: skill
|
|
5
5
|
allowed-tools: ["Bash(kordis:*)", "Read", "Edit"]
|
|
6
6
|
---
|
|
@@ -107,6 +107,31 @@ non-patch update.
|
|
|
107
107
|
four to the user. If `status: "pending"`, the AI reviewer hasn't decided
|
|
108
108
|
yet — re-check with `kordis info <full_slug> --json` (or wait and re-run
|
|
109
109
|
`kordis info`); the `pending` page is not yet served on the public URL.
|
|
110
|
+
|
|
111
|
+
## Versioning your push (0.9.0+)
|
|
112
|
+
|
|
113
|
+
- **First publish** derives the version from the frontmatter `version:`
|
|
114
|
+
field if present, else defaults to `0.1.0`. Passing `--bump` on a
|
|
115
|
+
first publish now returns a clear `usage_error` (pre-0.9 silently
|
|
116
|
+
ignored it) — drop `--bump` or set the frontmatter version explicitly.
|
|
117
|
+
- **Subsequent pushes** default to `--bump patch`. Explicit
|
|
118
|
+
`--bump minor` or `--bump major` ships the matching jump.
|
|
119
|
+
- **Frontmatter `version:` on an update** must match the result of
|
|
120
|
+
the bump strategy, else the server rejects with `usage_error` naming
|
|
121
|
+
both values. Easier to drop the frontmatter version once a package is
|
|
122
|
+
published and let `--bump` drive it.
|
|
123
|
+
- **Prereleases** (`1.0.0-beta.1`) ship via frontmatter on first publish
|
|
124
|
+
or via an explicit prerelease push. `--bump` strips prerelease
|
|
125
|
+
suffixes; `current_version` never gets set to a prerelease via bump,
|
|
126
|
+
so installers on a bare `kordis pull <slug>` skip prereleases by
|
|
127
|
+
default. Consumers who want a prerelease pin explicitly:
|
|
128
|
+
`kordis pull <slug>@1.0.0-beta.1`.
|
|
129
|
+
- **AI review runs per-version** — every push enters the review queue
|
|
130
|
+
even if a prior version was already passed. Re-pushed packages can
|
|
131
|
+
legitimately fail review if new content trips a Stage-1 rule.
|
|
132
|
+
|
|
133
|
+
`kordis versions <slug>` lists everything ever published, with each
|
|
134
|
+
row's `review_status` so consumers know which versions are pull-safe.
|
|
110
135
|
If `status: "rejected"`, surface `review.flags` to the user and DO NOT
|
|
111
136
|
silently retry — explain why.
|
|
112
137
|
|
|
@@ -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…". Covers JIT read vs pull, the five Kordis package types, dependencies, version pinning, 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". Covers JIT read vs pull, the five Kordis package types, dependencies, version pinning, range syntax, and attribution. Load eagerly when in doubt.
|
|
4
4
|
type: skill
|
|
5
5
|
allowed-tools: ["Bash(kordis:*)"]
|
|
6
6
|
---
|
|
@@ -42,14 +42,15 @@ immediately (e.g. to verify a package you just pushed), use `pull` with
|
|
|
42
42
|
Returns the full body (Markdown) of a text-bearing package, plus an
|
|
43
43
|
`attribution` string. `--pin <semver>` overrides any `@suffix` on the slug.
|
|
44
44
|
|
|
45
|
-
### `kordis pull <full_slug>[@<version>] [options]`
|
|
45
|
+
### `kordis pull <full_slug>[@<version|range>] [options]`
|
|
46
46
|
Writes files into the current project.
|
|
47
47
|
|
|
48
|
-
| flag
|
|
49
|
-
|
|
|
50
|
-
| `--tool <name>`
|
|
51
|
-
| `--target <path>`
|
|
52
|
-
| `--with-recommends`
|
|
48
|
+
| flag | meaning |
|
|
49
|
+
| -------------------------- | ------------------------------------------------------------- |
|
|
50
|
+
| `--tool <name>` | force a target tool format (default: auto-detect) |
|
|
51
|
+
| `--target <path>` | install into a sub-directory (default: cwd) |
|
|
52
|
+
| `--with-recommends` | also install depth-0 recommended dependencies (0.9.0+) |
|
|
53
|
+
| `--no-with-required` | skip transitive required deps (advanced; default installs) |
|
|
53
54
|
|
|
54
55
|
IMPORTANT: `pull` **fails fast** when the target tool can't be inferred from
|
|
55
56
|
the cwd (no `.claude/`, `.codex/`, `.cursor/`, `AGENTS.md`, `CLAUDE.md`,
|
|
@@ -97,6 +98,39 @@ machine.
|
|
|
97
98
|
- IMPORTANT: hooks execute arbitrary code. EXPLAIN in plain words what the
|
|
98
99
|
hook does, and confirm with the user BEFORE pulling.
|
|
99
100
|
|
|
101
|
+
## Pinning versions and ranges (0.9.0+)
|
|
102
|
+
|
|
103
|
+
Append `@<version|range>` to a slug on `kordis pull`, `kordis read`,
|
|
104
|
+
`kordis info` (and check what's available with `kordis versions`).
|
|
105
|
+
Supported forms:
|
|
106
|
+
|
|
107
|
+
| input | meaning |
|
|
108
|
+
| ---------------------------------- | ---------------------------------------------------------------- |
|
|
109
|
+
| `user/foo` (no @) | latest `current_version` (server's bumped default) |
|
|
110
|
+
| `user/foo@1.2.3` | exact version |
|
|
111
|
+
| `user/foo@1.0.0-beta.1` | exact prerelease (only matches if the literal exists) |
|
|
112
|
+
| `user/foo@^1.2` | caret — highest 1.x.y (x≥2). For 0.x.y, caret pins the minor |
|
|
113
|
+
| `user/foo@~1.2` | tilde — highest 1.2.y |
|
|
114
|
+
| `user/foo@*` | alias for `current_version` |
|
|
115
|
+
|
|
116
|
+
Comparator strings (`>=1 <2`, `||`) are NOT supported in 0.9.x —
|
|
117
|
+
server returns `usage_error` with the accepted-forms list. Deferred to
|
|
118
|
+
0.10.x if demand emerges.
|
|
119
|
+
|
|
120
|
+
`kordis pull` walks transitive `required` deps by default — each child
|
|
121
|
+
is resolved against the range its parent declared. **Conflict between
|
|
122
|
+
two parents requiring incompatible ranges = hard fail** (exit code
|
|
123
|
+
35 `dependency_conflict`); the CLI prints the conflict chain so users
|
|
124
|
+
can pin the offending one explicitly. Recommended deps are only
|
|
125
|
+
walked at depth 0 when `--with-recommends` is passed.
|
|
126
|
+
|
|
127
|
+
`kordis read @range` resolves to a concrete version (the stderr footer
|
|
128
|
+
shows it: `… @1.4.2 (resolved @^1 → 1.4.2)`) but does NOT walk
|
|
129
|
+
transitive deps — read is a one-shot context inject, no install plan.
|
|
130
|
+
|
|
131
|
+
`kordis info user/foo@^1` shows a `Requested: 1.4.2 (current: 2.0.0)`
|
|
132
|
+
line above `Type` when the pin differs from the registry default.
|
|
133
|
+
|
|
100
134
|
## Dependencies
|
|
101
135
|
`kordis pull` reports `depends` and `recommends` from the package metadata.
|
|
102
136
|
Surface the dependency tree to the user FIRST. If they accept,
|
package/dist/index.js
CHANGED
|
@@ -143,6 +143,13 @@ var init_errors = __esm({
|
|
|
143
143
|
*/
|
|
144
144
|
"jit_blocked",
|
|
145
145
|
// 33
|
|
146
|
+
/**
|
|
147
|
+
* dependency_conflict — `kordis pull` resolver hit two incompatible
|
|
148
|
+
* version ranges in the install plan. Server returns the conflict
|
|
149
|
+
* chain via `flags`; CLI exits 35.
|
|
150
|
+
*/
|
|
151
|
+
"dependency_conflict",
|
|
152
|
+
// 35
|
|
146
153
|
"conflict",
|
|
147
154
|
// 40 (slug taken, version exists)
|
|
148
155
|
"filesystem_error",
|
|
@@ -160,6 +167,7 @@ var init_errors = __esm({
|
|
|
160
167
|
rate_limited: 31,
|
|
161
168
|
ai_review_rejected: 32,
|
|
162
169
|
jit_blocked: 33,
|
|
170
|
+
dependency_conflict: 35,
|
|
163
171
|
conflict: 40,
|
|
164
172
|
filesystem_error: 50,
|
|
165
173
|
not_found: 1,
|
|
@@ -320,7 +328,13 @@ var init_package = __esm({
|
|
|
320
328
|
depends: z6.array(DependencySpecSchema),
|
|
321
329
|
recommends: z6.array(DependencySpecSchema),
|
|
322
330
|
categories: z6.array(z6.string()).optional().default([]),
|
|
323
|
-
verified_by_kordis: z6.boolean()
|
|
331
|
+
verified_by_kordis: z6.boolean(),
|
|
332
|
+
/**
|
|
333
|
+
* 0.9.0+ — when the caller pinned a `@version` or `@range`, the
|
|
334
|
+
* concrete resolved version. Absent for bare-slug calls (where the
|
|
335
|
+
* response reflects `current_version`).
|
|
336
|
+
*/
|
|
337
|
+
requested_version: SemverSchema.optional()
|
|
324
338
|
});
|
|
325
339
|
PackageVersionSchema = z6.object({
|
|
326
340
|
id: UuidSchema,
|
|
@@ -486,7 +500,7 @@ var init_auth = __esm({
|
|
|
486
500
|
|
|
487
501
|
// ../../packages/contracts/src/api.ts
|
|
488
502
|
import { z as z9 } from "zod";
|
|
489
|
-
var SearchQuerySchema, SearchResponseSchema, TrendingQuerySchema, RisingQuerySchema, StarResponseSchema, AssetSchema, PublishPackageRequestSchema, PublishWaitQuerySchema, PublishPackageResponseSchema, PackagePathParamsSchema, ContentQuerySchema, PackageContentResponseSchema, JitReadRequestSchema, JitReadResponseSchema, PullPackageResponseSchema, ListVersionsResponseSchema, VersionDiffQuerySchema, ListCommentsQuerySchema, ListCommentsResponseSchema, UserProfileResponseSchema, UserPackagesQuerySchema, ListUserPackagesResponseSchema, ListUserStarsResponseSchema, FollowResponseSchema, ListFollowersQuerySchema, ListFollowersResponseSchema;
|
|
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;
|
|
490
504
|
var init_api = __esm({
|
|
491
505
|
"../../packages/contracts/src/api.ts"() {
|
|
492
506
|
"use strict";
|
|
@@ -652,6 +666,25 @@ var init_api = __esm({
|
|
|
652
666
|
*/
|
|
653
667
|
bundled_assets: z9.array(z9.string()).optional()
|
|
654
668
|
});
|
|
669
|
+
PullPackageRequestSchema = z9.object({
|
|
670
|
+
/**
|
|
671
|
+
* Exact version (`"1.2.3"`) or range (`"^1.2"`, `"~1.2"`, `"*"`). When
|
|
672
|
+
* absent the resolver returns `current_version`. Unsupported syntax
|
|
673
|
+
* (`">="`, `"<"`, `"||"`) is rejected with usage_error.
|
|
674
|
+
*/
|
|
675
|
+
version: SemverRangeSchema.optional(),
|
|
676
|
+
/**
|
|
677
|
+
* Walk `required` deps transitively (DFS, conflict = hard-fail, max
|
|
678
|
+
* depth 8). Default true — matches the user expectation that "install
|
|
679
|
+
* X" means everything X needs to function.
|
|
680
|
+
*/
|
|
681
|
+
with_required: z9.boolean().optional().default(true),
|
|
682
|
+
/**
|
|
683
|
+
* Walk depth-0 `recommended` deps (transitive recommends NOT followed).
|
|
684
|
+
* Default false — opt-in noise.
|
|
685
|
+
*/
|
|
686
|
+
with_recommends: z9.boolean().optional().default(false)
|
|
687
|
+
});
|
|
655
688
|
PullPackageResponseSchema = z9.object({
|
|
656
689
|
status: z9.enum(["installed", "already_installed", "skipped"]),
|
|
657
690
|
full_slug: FullSlugSchema,
|
|
@@ -665,7 +698,11 @@ var init_api = __esm({
|
|
|
665
698
|
full_slug: FullSlugSchema,
|
|
666
699
|
version: SemverSchema,
|
|
667
700
|
status: z9.enum(["installed", "already_installed", "skipped", "failed"]),
|
|
668
|
-
paths: z9.array(z9.string()).optional()
|
|
701
|
+
paths: z9.array(z9.string()).optional(),
|
|
702
|
+
/** 0.9.0+ — range the parent asked for, e.g. `"^1.2"`. */
|
|
703
|
+
requested_range: SemverRangeSchema.optional(),
|
|
704
|
+
/** 0.9.0+ — which package required this one (null for root). */
|
|
705
|
+
requested_by: FullSlugSchema.nullable().optional()
|
|
669
706
|
})
|
|
670
707
|
),
|
|
671
708
|
recommended: z9.array(
|
|
@@ -673,12 +710,27 @@ var init_api = __esm({
|
|
|
673
710
|
full_slug: FullSlugSchema,
|
|
674
711
|
version: SemverSchema,
|
|
675
712
|
status: z9.enum(["installed", "skipped"]),
|
|
676
|
-
paths: z9.array(z9.string()).optional()
|
|
713
|
+
paths: z9.array(z9.string()).optional(),
|
|
714
|
+
requested_range: SemverRangeSchema.optional(),
|
|
715
|
+
requested_by: FullSlugSchema.nullable().optional()
|
|
677
716
|
})
|
|
678
717
|
)
|
|
679
718
|
}),
|
|
680
719
|
recommends_hint: z9.string().nullable()
|
|
681
720
|
});
|
|
721
|
+
ResolveVersionQuerySchema = z9.object({
|
|
722
|
+
range: SemverRangeSchema
|
|
723
|
+
});
|
|
724
|
+
ResolveVersionResponseSchema = z9.object({
|
|
725
|
+
full_slug: FullSlugSchema,
|
|
726
|
+
requested_range: SemverRangeSchema,
|
|
727
|
+
resolved_version: SemverSchema,
|
|
728
|
+
current_version: SemverSchema,
|
|
729
|
+
is_prerelease: z9.boolean()
|
|
730
|
+
});
|
|
731
|
+
GetPackageQuerySchema = z9.object({
|
|
732
|
+
version: SemverRangeSchema.optional()
|
|
733
|
+
});
|
|
682
734
|
ListVersionsResponseSchema = z9.object({
|
|
683
735
|
full_slug: FullSlugSchema,
|
|
684
736
|
current_version: SemverSchema,
|
|
@@ -1135,8 +1187,10 @@ var init_client = __esm({
|
|
|
1135
1187
|
body
|
|
1136
1188
|
});
|
|
1137
1189
|
}
|
|
1138
|
-
getPackage(username, slug) {
|
|
1139
|
-
return this.request(`/api/packages/${username}/${slug}
|
|
1190
|
+
getPackage(username, slug, opts = {}) {
|
|
1191
|
+
return this.request(`/api/packages/${username}/${slug}`, {
|
|
1192
|
+
query: opts
|
|
1193
|
+
});
|
|
1140
1194
|
}
|
|
1141
1195
|
deletePackage(username, slug) {
|
|
1142
1196
|
return this.request(`/api/packages/${username}/${slug}`, { method: "DELETE" });
|
|
@@ -1147,10 +1201,20 @@ var init_client = __esm({
|
|
|
1147
1201
|
{ query: opts }
|
|
1148
1202
|
);
|
|
1149
1203
|
}
|
|
1150
|
-
|
|
1204
|
+
/**
|
|
1205
|
+
* 0.9.0+ — resolve a range to a concrete version without committing
|
|
1206
|
+
* to a pull. Used by `kordis info @range` and `kordis read @range`.
|
|
1207
|
+
*/
|
|
1208
|
+
resolveVersion(username, slug, range) {
|
|
1209
|
+
return this.request(
|
|
1210
|
+
`/api/packages/${username}/${slug}/resolve`,
|
|
1211
|
+
{ query: { range } }
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
pull(username, slug, opts = {}) {
|
|
1151
1215
|
return this.request(
|
|
1152
1216
|
`/api/packages/${username}/${slug}/pull`,
|
|
1153
|
-
{ method: "POST" }
|
|
1217
|
+
{ method: "POST", body: opts }
|
|
1154
1218
|
);
|
|
1155
1219
|
}
|
|
1156
1220
|
/**
|
|
@@ -2904,6 +2968,22 @@ function pushCommand() {
|
|
|
2904
2968
|
throw err instanceof Error ? err : new Error(String(err));
|
|
2905
2969
|
}
|
|
2906
2970
|
if (opts.dryRun) {
|
|
2971
|
+
const explicitBump = opts.bump && opts.bump !== "patch" ? opts.bump : null;
|
|
2972
|
+
if (explicitBump && nameFromFm) {
|
|
2973
|
+
const slugForCheck = opts.slug?.includes("/") ? opts.slug.split("/", 2) : bundle.username ? [bundle.username, opts.slug ?? nameFromFm] : null;
|
|
2974
|
+
if (slugForCheck && slugForCheck[0] && slugForCheck[1]) {
|
|
2975
|
+
const [u, s2] = slugForCheck;
|
|
2976
|
+
try {
|
|
2977
|
+
await bundle.api.getPackage(u, s2);
|
|
2978
|
+
} catch (err) {
|
|
2979
|
+
if (err && typeof err === "object" && "code" in err && err.code === "not_found") {
|
|
2980
|
+
throw new Error(
|
|
2981
|
+
`--bump ${explicitBump} cannot apply to a first publish (no existing version to bump from). First publish derives version from frontmatter \`version:\` or defaults to "0.1.0". Drop --bump (or set frontmatter version explicitly), then re-run.`
|
|
2982
|
+
);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2907
2987
|
await runDryRun({ content, assets });
|
|
2908
2988
|
return;
|
|
2909
2989
|
}
|
|
@@ -3599,6 +3679,11 @@ function kordisHome2() {
|
|
|
3599
3679
|
function lockfilePath() {
|
|
3600
3680
|
return join8(kordisHome2(), "installed.json");
|
|
3601
3681
|
}
|
|
3682
|
+
function lockfileDisplayPath() {
|
|
3683
|
+
const path2 = lockfilePath();
|
|
3684
|
+
const home = process.env["HOME"];
|
|
3685
|
+
return home && path2.startsWith(home) ? "~" + path2.slice(home.length) : path2;
|
|
3686
|
+
}
|
|
3602
3687
|
async function readLockfile() {
|
|
3603
3688
|
try {
|
|
3604
3689
|
const raw = await readFile7(lockfilePath(), "utf8");
|
|
@@ -3617,16 +3702,80 @@ async function recordInstalled(fullSlug, entry) {
|
|
|
3617
3702
|
await writeLockfile(lf);
|
|
3618
3703
|
}
|
|
3619
3704
|
|
|
3705
|
+
// src/lib/parse-username.ts
|
|
3706
|
+
init_src();
|
|
3707
|
+
var SORT_KEYWORDS = ["latest", "popular", "recent", "updated", "stars", "downloads"];
|
|
3708
|
+
function parseUsername(raw) {
|
|
3709
|
+
if (SORT_KEYWORDS.includes(raw.toLowerCase())) {
|
|
3710
|
+
throw new Error(
|
|
3711
|
+
`"${raw}" is a sort keyword \u2014 did you mean \`--sort ${raw}\`? Username arguments must follow the Kordis username shape (lowercase alphanumeric, hyphens allowed).`
|
|
3712
|
+
);
|
|
3713
|
+
}
|
|
3714
|
+
const parsed = UsernameSchema.safeParse(raw);
|
|
3715
|
+
if (!parsed.success) {
|
|
3716
|
+
throw new Error(
|
|
3717
|
+
`invalid username "${raw}" \u2014 must be 3-32 chars, lowercase alphanumeric with single hyphens (e.g. "alice", "my-team")`
|
|
3718
|
+
);
|
|
3719
|
+
}
|
|
3720
|
+
return parsed.data;
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3723
|
+
// src/lib/parse-slug-version.ts
|
|
3724
|
+
var SUFFIX_CHARS = /[\^~*]/;
|
|
3725
|
+
function parseSlugAtVersion(raw) {
|
|
3726
|
+
if (!raw.includes("/")) {
|
|
3727
|
+
try {
|
|
3728
|
+
const username2 = parseUsername(raw);
|
|
3729
|
+
throw new Error(
|
|
3730
|
+
`"${raw}" looks like a username, not a package \u2014 try \`kordis profile ${username2}\` for user details, or pass a full slug like "{username}/{slug}".`
|
|
3731
|
+
);
|
|
3732
|
+
} catch (err) {
|
|
3733
|
+
if (err instanceof Error) throw err;
|
|
3734
|
+
throw new Error(`"${raw}" is missing the "{username}/{slug}" form`);
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
const atIdx = raw.indexOf("@");
|
|
3738
|
+
const slugPart = atIdx === -1 ? raw : raw.slice(0, atIdx);
|
|
3739
|
+
const versionPart = atIdx === -1 ? "" : raw.slice(atIdx + 1);
|
|
3740
|
+
const [username, slug] = slugPart.split("/");
|
|
3741
|
+
if (!username || !slug) {
|
|
3742
|
+
throw new Error('full_slug must be "{username}/{slug}"');
|
|
3743
|
+
}
|
|
3744
|
+
if (atIdx !== -1 && versionPart.length === 0) {
|
|
3745
|
+
throw new Error(
|
|
3746
|
+
`"${raw}" has a trailing "@" with no version \u2014 drop the "@" or pass a version/range after it`
|
|
3747
|
+
);
|
|
3748
|
+
}
|
|
3749
|
+
const out = {
|
|
3750
|
+
fullSlug: slugPart,
|
|
3751
|
+
username,
|
|
3752
|
+
slug
|
|
3753
|
+
};
|
|
3754
|
+
if (versionPart.length > 0) {
|
|
3755
|
+
if (SUFFIX_CHARS.test(versionPart)) {
|
|
3756
|
+
out.range = versionPart;
|
|
3757
|
+
} else {
|
|
3758
|
+
out.version = versionPart;
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
return out;
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3620
3764
|
// src/commands/pull.ts
|
|
3621
|
-
async function doPullInstall(fullSlug, tool, installRoot,
|
|
3765
|
+
async function doPullInstall(fullSlug, tool, installRoot, opts = {}) {
|
|
3622
3766
|
const [username, slug] = fullSlug.split("/");
|
|
3623
3767
|
if (!username || !slug) throw new Error('full_slug must be "{username}/{slug}"');
|
|
3624
3768
|
const bundle = await useClient();
|
|
3625
|
-
const
|
|
3626
|
-
|
|
3769
|
+
const pullRes = await bundle.api.pull(username, slug, {
|
|
3770
|
+
...opts.version ? { version: opts.version } : {},
|
|
3771
|
+
...opts.withRequired !== void 0 ? { with_required: opts.withRequired } : {},
|
|
3772
|
+
...opts.withRecommends !== void 0 ? { with_recommends: opts.withRecommends } : {}
|
|
3773
|
+
});
|
|
3627
3774
|
const contentRes = await bundle.api.getContent(username, slug, {
|
|
3628
3775
|
format: "canonical",
|
|
3629
|
-
|
|
3776
|
+
// Server resolved the range during pull; pass the concrete version
|
|
3777
|
+
// through to /content so we get the exact bytes recorded above.
|
|
3778
|
+
version: pullRes.version
|
|
3630
3779
|
});
|
|
3631
3780
|
const parsed = validatePackageSource(contentRes.content);
|
|
3632
3781
|
if (!parsed.ok || !parsed.type || !parsed.frontmatter) {
|
|
@@ -3679,21 +3828,34 @@ async function doPullInstall(fullSlug, tool, installRoot, version) {
|
|
|
3679
3828
|
version: installedVersion,
|
|
3680
3829
|
tool,
|
|
3681
3830
|
target: installRoot,
|
|
3682
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3831
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3832
|
+
...opts.requestedRange ? { requestedRange: opts.requestedRange } : {},
|
|
3833
|
+
...opts.installedBy ? { installedBy: opts.installedBy } : {}
|
|
3683
3834
|
});
|
|
3684
|
-
return {
|
|
3835
|
+
return {
|
|
3836
|
+
version: installedVersion,
|
|
3837
|
+
tool,
|
|
3838
|
+
installRoot,
|
|
3839
|
+
written,
|
|
3840
|
+
notes,
|
|
3841
|
+
plan: pullRes.dependencies
|
|
3842
|
+
};
|
|
3685
3843
|
}
|
|
3686
3844
|
function pullCommand() {
|
|
3687
|
-
return new Command5("pull").description("install a package into the current project").argument(
|
|
3845
|
+
return new Command5("pull").description("install a package into the current project").argument(
|
|
3846
|
+
"<full_slug>",
|
|
3847
|
+
"package identifier {username}/{slug} or {username}/{slug}@{version|range}"
|
|
3848
|
+
).option("--tool <name>", "target tool format (default: auto-detect)").option("--target <path>", "install into a subdirectory (default: cwd)").option(
|
|
3849
|
+
"--with-recommends",
|
|
3850
|
+
"also install recommended (depth-0) dependencies",
|
|
3851
|
+
false
|
|
3852
|
+
).option(
|
|
3853
|
+
"--no-with-required",
|
|
3854
|
+
"skip transitive required dependencies (advanced; default is to install)"
|
|
3855
|
+
).action(
|
|
3688
3856
|
async (fullSlugArg, opts) => {
|
|
3689
|
-
const
|
|
3690
|
-
const
|
|
3691
|
-
const fullSlug = hasPinnedVersion ? fullSlugArg.slice(0, atIdx) : fullSlugArg;
|
|
3692
|
-
const pinnedVersion = hasPinnedVersion ? fullSlugArg.slice(atIdx + 1) : void 0;
|
|
3693
|
-
const [username, slug] = fullSlug.split("/");
|
|
3694
|
-
if (!username || !slug) {
|
|
3695
|
-
throw new Error('full_slug must be "{username}/{slug}" or "{username}/{slug}@{version}"');
|
|
3696
|
-
}
|
|
3857
|
+
const parsed = parseSlugAtVersion(fullSlugArg);
|
|
3858
|
+
const pin = parsed.version ?? parsed.range;
|
|
3697
3859
|
const userCwd = process.env["INIT_CWD"] ?? process.cwd();
|
|
3698
3860
|
const installRoot = opts.target ?? userCwd;
|
|
3699
3861
|
const detected = opts.tool ?? await detectTool(installRoot);
|
|
@@ -3702,25 +3864,68 @@ function pullCommand() {
|
|
|
3702
3864
|
"Cannot detect target tool from the current directory.\n Pass --tool <name> explicitly. Valid: claude-code, codex, cursor,\n continue, windsurf, antigravity, copilot.\n Or run `kordis pull` inside a project that already has one of:\n .claude/, .codex/, .cursor/, AGENTS.md, CLAUDE.md, .cursorrules,\n .continue/, .windsurf/, .antigravity/, .github/copilot-instructions.md"
|
|
3703
3865
|
);
|
|
3704
3866
|
}
|
|
3705
|
-
const
|
|
3706
|
-
const spin = spinner(
|
|
3707
|
-
const result = await doPullInstall(fullSlug, detected, installRoot,
|
|
3867
|
+
const rootLabel = pin ? `${parsed.fullSlug}@${pin}` : parsed.fullSlug;
|
|
3868
|
+
const spin = spinner(`Pulling ${rootLabel}`);
|
|
3869
|
+
const result = await doPullInstall(parsed.fullSlug, detected, installRoot, {
|
|
3870
|
+
...pin ? { version: pin } : {},
|
|
3871
|
+
withRequired: opts.withRequired,
|
|
3872
|
+
withRecommends: opts.withRecommends,
|
|
3873
|
+
installedBy: "user",
|
|
3874
|
+
...pin ? { requestedRange: pin } : {}
|
|
3875
|
+
});
|
|
3708
3876
|
spin.succeed();
|
|
3877
|
+
const dependencyInstalls = [];
|
|
3878
|
+
const installPlan = [
|
|
3879
|
+
...result.plan?.required.map((n) => ({ ...n, kind: "required" })) ?? [],
|
|
3880
|
+
...opts.withRecommends ? result.plan?.recommended.map((n) => ({ ...n, kind: "recommended" })) ?? [] : []
|
|
3881
|
+
];
|
|
3882
|
+
for (const node of installPlan) {
|
|
3883
|
+
const subSpin = spinner(
|
|
3884
|
+
` \u21B3 ${node.kind === "required" ? "dep" : "rec"} ${node.full_slug}@${node.version}`
|
|
3885
|
+
);
|
|
3886
|
+
try {
|
|
3887
|
+
const subRes = await doPullInstall(node.full_slug, detected, installRoot, {
|
|
3888
|
+
version: node.version,
|
|
3889
|
+
withRequired: false,
|
|
3890
|
+
withRecommends: false,
|
|
3891
|
+
installedBy: "dependency",
|
|
3892
|
+
...node.requested_range ? { requestedRange: node.requested_range } : {}
|
|
3893
|
+
});
|
|
3894
|
+
subSpin.succeed();
|
|
3895
|
+
dependencyInstalls.push({ fullSlug: node.full_slug, result: subRes });
|
|
3896
|
+
} catch (err) {
|
|
3897
|
+
subSpin.fail();
|
|
3898
|
+
throw err;
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3709
3901
|
printDual({
|
|
3710
3902
|
json: {
|
|
3711
3903
|
status: "installed",
|
|
3712
|
-
full_slug: fullSlug,
|
|
3904
|
+
full_slug: parsed.fullSlug,
|
|
3713
3905
|
version: result.version,
|
|
3906
|
+
...pin ? { requested_range: pin } : {},
|
|
3714
3907
|
tool_detected: result.tool,
|
|
3715
3908
|
installed_paths: result.written,
|
|
3716
|
-
notes: result.notes
|
|
3909
|
+
notes: result.notes,
|
|
3910
|
+
dependencies: dependencyInstalls.map((d) => ({
|
|
3911
|
+
full_slug: d.fullSlug,
|
|
3912
|
+
version: d.result.version,
|
|
3913
|
+
installed_paths: d.result.written
|
|
3914
|
+
}))
|
|
3717
3915
|
},
|
|
3718
3916
|
human: () => {
|
|
3719
3917
|
printHuman(
|
|
3720
|
-
c.green(`\u2713 Installed ${c.bold(fullSlug)} v${result.version}`)
|
|
3918
|
+
c.green(`\u2713 Installed ${c.bold(parsed.fullSlug)} v${result.version}`)
|
|
3721
3919
|
);
|
|
3722
3920
|
for (const w of result.written) printHuman(` ${c.gray("wrote")} ${w}`);
|
|
3723
3921
|
for (const n of result.notes) printHuman(c.yellow(` ! ${n}`));
|
|
3922
|
+
for (const dep of dependencyInstalls) {
|
|
3923
|
+
printHuman(
|
|
3924
|
+
c.green(` \u21B3 Installed ${c.bold(dep.fullSlug)} v${dep.result.version}`)
|
|
3925
|
+
);
|
|
3926
|
+
for (const w of dep.result.written) printHuman(` ${c.gray("wrote")} ${w}`);
|
|
3927
|
+
}
|
|
3928
|
+
printHuman(c.gray(` lockfile: ${lockfileDisplayPath()}`));
|
|
3724
3929
|
}
|
|
3725
3930
|
});
|
|
3726
3931
|
const reminder = await maybeRenderReminder();
|
|
@@ -3765,18 +3970,22 @@ import { Command as Command6 } from "commander";
|
|
|
3765
3970
|
function readCommand() {
|
|
3766
3971
|
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(
|
|
3767
3972
|
async (rawSlug, opts) => {
|
|
3768
|
-
const
|
|
3769
|
-
const
|
|
3770
|
-
const inlineVersion = atIdx > 0 ? rawSlug.slice(atIdx + 1) : void 0;
|
|
3771
|
-
const version = opts.pin ?? inlineVersion;
|
|
3772
|
-
const [username, slug] = fullSlug.split("/");
|
|
3773
|
-
if (!username || !slug) {
|
|
3774
|
-
throw new Error('full_slug must be "{username}/{slug}" (optional "@version")');
|
|
3775
|
-
}
|
|
3973
|
+
const parsed = parseSlugAtVersion(rawSlug);
|
|
3974
|
+
const pin = opts.pin ?? parsed.version ?? parsed.range;
|
|
3776
3975
|
const bundle = await useClient();
|
|
3777
|
-
|
|
3976
|
+
let resolvedVersion;
|
|
3977
|
+
if (parsed.range && !opts.pin) {
|
|
3978
|
+
const resolved = await bundle.api.resolveVersion(
|
|
3979
|
+
parsed.username,
|
|
3980
|
+
parsed.slug,
|
|
3981
|
+
parsed.range
|
|
3982
|
+
);
|
|
3983
|
+
resolvedVersion = resolved.resolved_version;
|
|
3984
|
+
}
|
|
3985
|
+
const versionForRead = resolvedVersion ?? pin;
|
|
3986
|
+
const res = await bundle.api.jitRead(parsed.username, parsed.slug, {
|
|
3778
3987
|
allow_pending: opts.allowPending,
|
|
3779
|
-
...
|
|
3988
|
+
...versionForRead ? { version: versionForRead } : {}
|
|
3780
3989
|
});
|
|
3781
3990
|
if (isJsonMode()) {
|
|
3782
3991
|
printJson(res);
|
|
@@ -3795,8 +4004,9 @@ function readCommand() {
|
|
|
3795
4004
|
`);
|
|
3796
4005
|
}
|
|
3797
4006
|
}
|
|
4007
|
+
const resolvedNote = parsed.range && resolvedVersion ? ` ${c.gray("(resolved")} ${c.gray("@" + parsed.range)} ${c.gray("\u2192")} ${resolvedVersion}${c.gray(")")}` : "";
|
|
3798
4008
|
process.stderr.write(
|
|
3799
|
-
`${c.gray("\u2014")} ${c.bold(res.full_slug)}${c.gray("@")}${res.version} ${c.gray("\xB7")} ${res.type} (tier ${res.tier}) ${c.gray("\xB7")} ${c.cyan(res.attribution)}
|
|
4009
|
+
`${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)}
|
|
3800
4010
|
`
|
|
3801
4011
|
);
|
|
3802
4012
|
printHuman("");
|
|
@@ -3808,27 +4018,31 @@ function readCommand() {
|
|
|
3808
4018
|
init_src();
|
|
3809
4019
|
import { Command as Command7 } from "commander";
|
|
3810
4020
|
function infoCommand() {
|
|
3811
|
-
return new Command7("info").description("show details about a package").argument(
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
);
|
|
3818
|
-
}
|
|
3819
|
-
throw new Error('full_slug must be "{username}/{slug}"');
|
|
3820
|
-
}
|
|
4021
|
+
return new Command7("info").description("show details about a package").argument(
|
|
4022
|
+
"<full_slug>",
|
|
4023
|
+
"package identifier {username}/{slug} or {username}/{slug}@{version|range}"
|
|
4024
|
+
).option("--pin <semver>", "pin to an exact version or range (overrides @suffix)").action(async (fullSlug, opts) => {
|
|
4025
|
+
const parsed = parseSlugAtVersion(fullSlug);
|
|
4026
|
+
const pin = opts.pin ?? parsed.version ?? parsed.range;
|
|
3821
4027
|
const bundle = await useClient();
|
|
3822
|
-
const detail = await bundle.api.getPackage(username, slug
|
|
4028
|
+
const detail = await bundle.api.getPackage(parsed.username, parsed.slug, {
|
|
4029
|
+
...pin ? { version: pin } : {}
|
|
4030
|
+
});
|
|
3823
4031
|
const typeDefaults = CAPABILITIES_OF_TYPE[detail.type];
|
|
3824
4032
|
const jitEligible = detail.jit_eligible ?? typeDefaults.jit_eligible;
|
|
3825
4033
|
const executable = detail.executable ?? typeDefaults.executable;
|
|
3826
4034
|
printDual({
|
|
3827
4035
|
json: detail,
|
|
3828
4036
|
human: () => {
|
|
3829
|
-
|
|
4037
|
+
const headerVersion = detail.requested_version ?? detail.current_version;
|
|
4038
|
+
printHuman(c.bold(detail.full_slug) + " " + c.gray(`v${headerVersion}`));
|
|
3830
4039
|
printHuman(" " + detail.description);
|
|
3831
4040
|
printHuman("");
|
|
4041
|
+
if (detail.requested_version && detail.requested_version !== detail.current_version) {
|
|
4042
|
+
printHuman(
|
|
4043
|
+
` ${c.cyan("Requested")}: ${detail.requested_version} ${c.gray(`(current: ${detail.current_version})`)}`
|
|
4044
|
+
);
|
|
4045
|
+
}
|
|
3832
4046
|
printHuman(` ${c.cyan("Type")}: ${detail.type} (tier ${detail.tier})`);
|
|
3833
4047
|
printHuman(
|
|
3834
4048
|
` ${c.cyan("JIT-readable")}: ${jitEligible ? c.green("yes") : c.red("no")}`
|
|
@@ -4396,26 +4610,6 @@ function deleteCommand() {
|
|
|
4396
4610
|
|
|
4397
4611
|
// src/commands/list.ts
|
|
4398
4612
|
import { Command as Command13 } from "commander";
|
|
4399
|
-
|
|
4400
|
-
// src/lib/parse-username.ts
|
|
4401
|
-
init_src();
|
|
4402
|
-
var SORT_KEYWORDS = ["latest", "popular", "recent", "updated", "stars", "downloads"];
|
|
4403
|
-
function parseUsername(raw) {
|
|
4404
|
-
if (SORT_KEYWORDS.includes(raw.toLowerCase())) {
|
|
4405
|
-
throw new Error(
|
|
4406
|
-
`"${raw}" is a sort keyword \u2014 did you mean \`--sort ${raw}\`? Username arguments must follow the Kordis username shape (lowercase alphanumeric, hyphens allowed).`
|
|
4407
|
-
);
|
|
4408
|
-
}
|
|
4409
|
-
const parsed = UsernameSchema.safeParse(raw);
|
|
4410
|
-
if (!parsed.success) {
|
|
4411
|
-
throw new Error(
|
|
4412
|
-
`invalid username "${raw}" \u2014 must be 3-32 chars, lowercase alphanumeric with single hyphens (e.g. "alice", "my-team")`
|
|
4413
|
-
);
|
|
4414
|
-
}
|
|
4415
|
-
return parsed.data;
|
|
4416
|
-
}
|
|
4417
|
-
|
|
4418
|
-
// src/commands/list.ts
|
|
4419
4613
|
function listCommand() {
|
|
4420
4614
|
return new Command13("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) => {
|
|
4421
4615
|
const bundle = await useClient();
|
|
@@ -4545,16 +4739,9 @@ function relativeTime(isoString) {
|
|
|
4545
4739
|
|
|
4546
4740
|
// src/commands/outdated.ts
|
|
4547
4741
|
import { Command as Command15 } from "commander";
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
return [maj, min, pat];
|
|
4552
|
-
};
|
|
4553
|
-
const [aMaj, aMin, aPat] = parse(a);
|
|
4554
|
-
const [bMaj, bMin, bPat] = parse(b);
|
|
4555
|
-
if (bMaj !== aMaj) return bMaj > aMaj;
|
|
4556
|
-
if (bMin !== aMin) return bMin > aMin;
|
|
4557
|
-
return bPat > aPat;
|
|
4742
|
+
import semver from "semver";
|
|
4743
|
+
function effectiveRange(entry) {
|
|
4744
|
+
return entry.requestedRange ?? `^${entry.version}`;
|
|
4558
4745
|
}
|
|
4559
4746
|
function outdatedCommand() {
|
|
4560
4747
|
return new Command15("outdated").description("check installed packages for available updates").action(async () => {
|
|
@@ -4575,13 +4762,37 @@ function outdatedCommand() {
|
|
|
4575
4762
|
for (const fullSlug of slugs) {
|
|
4576
4763
|
const [username, slug] = fullSlug.split("/");
|
|
4577
4764
|
if (!username || !slug) continue;
|
|
4578
|
-
const
|
|
4765
|
+
const entry = lf.packages[fullSlug];
|
|
4766
|
+
const installed = entry.version;
|
|
4767
|
+
const requestedRange = effectiveRange(entry);
|
|
4579
4768
|
try {
|
|
4580
4769
|
const detail = await bundle.api.getPackage(username, slug);
|
|
4581
4770
|
const latest = detail.current_version;
|
|
4582
|
-
|
|
4771
|
+
let status;
|
|
4772
|
+
if (!semver.gt(latest, installed)) {
|
|
4773
|
+
status = "up_to_date";
|
|
4774
|
+
} else if (semver.satisfies(latest, requestedRange)) {
|
|
4775
|
+
status = "in_range_update";
|
|
4776
|
+
} else {
|
|
4777
|
+
status = "out_of_range";
|
|
4778
|
+
}
|
|
4779
|
+
entries.push({
|
|
4780
|
+
full_slug: fullSlug,
|
|
4781
|
+
installed,
|
|
4782
|
+
latest,
|
|
4783
|
+
requested_range: requestedRange,
|
|
4784
|
+
status,
|
|
4785
|
+
outdated: status !== "up_to_date"
|
|
4786
|
+
});
|
|
4583
4787
|
} catch {
|
|
4584
|
-
entries.push({
|
|
4788
|
+
entries.push({
|
|
4789
|
+
full_slug: fullSlug,
|
|
4790
|
+
installed,
|
|
4791
|
+
latest: installed,
|
|
4792
|
+
requested_range: requestedRange,
|
|
4793
|
+
status: "up_to_date",
|
|
4794
|
+
outdated: false
|
|
4795
|
+
});
|
|
4585
4796
|
}
|
|
4586
4797
|
}
|
|
4587
4798
|
spin.succeed();
|
|
@@ -4603,10 +4814,19 @@ function outdatedCommand() {
|
|
|
4603
4814
|
);
|
|
4604
4815
|
printHuman("");
|
|
4605
4816
|
for (const e of entries) {
|
|
4606
|
-
|
|
4607
|
-
|
|
4817
|
+
let label;
|
|
4818
|
+
let arrow = "";
|
|
4819
|
+
if (e.status === "up_to_date") {
|
|
4820
|
+
label = c.green("Up to date");
|
|
4821
|
+
} else if (e.status === "in_range_update") {
|
|
4822
|
+
label = c.yellow("Outdated");
|
|
4823
|
+
arrow = c.yellow(` \u2192 ${e.latest}`);
|
|
4824
|
+
} else {
|
|
4825
|
+
label = c.yellow(`Outdated ${c.gray(`(out of range ${e.requested_range})`)}`);
|
|
4826
|
+
arrow = c.yellow(` \u2192 ${e.latest}`);
|
|
4827
|
+
}
|
|
4608
4828
|
printHuman(
|
|
4609
|
-
` ${c.bold(e.full_slug)} ${c.gray(e.installed)}${arrow} ${
|
|
4829
|
+
` ${c.bold(e.full_slug)} ${c.gray(e.installed)}${arrow} ${label}`
|
|
4610
4830
|
);
|
|
4611
4831
|
}
|
|
4612
4832
|
printHuman("");
|
|
@@ -4623,16 +4843,9 @@ function outdatedCommand() {
|
|
|
4623
4843
|
|
|
4624
4844
|
// src/commands/update.ts
|
|
4625
4845
|
import { Command as Command16 } from "commander";
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
return [maj, min, pat];
|
|
4630
|
-
};
|
|
4631
|
-
const [aMaj, aMin, aPat] = parse(a);
|
|
4632
|
-
const [bMaj, bMin, bPat] = parse(b);
|
|
4633
|
-
if (bMaj !== aMaj) return bMaj > aMaj;
|
|
4634
|
-
if (bMin !== aMin) return bMin > aMin;
|
|
4635
|
-
return bPat > aPat;
|
|
4846
|
+
import semver2 from "semver";
|
|
4847
|
+
function effectiveRange2(entry) {
|
|
4848
|
+
return entry.requestedRange ?? `^${entry.version}`;
|
|
4636
4849
|
}
|
|
4637
4850
|
function updateCommand() {
|
|
4638
4851
|
return new Command16("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) => {
|
|
@@ -4660,15 +4873,18 @@ function updateCommand() {
|
|
|
4660
4873
|
const entry = lf.packages[fullSlug];
|
|
4661
4874
|
const [username, slug] = fullSlug.split("/");
|
|
4662
4875
|
if (!username || !slug) continue;
|
|
4876
|
+
const requestedRange = effectiveRange2(entry);
|
|
4663
4877
|
try {
|
|
4664
|
-
const
|
|
4665
|
-
if (
|
|
4878
|
+
const resolved = await bundle.api.resolveVersion(username, slug, requestedRange);
|
|
4879
|
+
if (semver2.gt(resolved.resolved_version, entry.version)) {
|
|
4666
4880
|
candidates.push({
|
|
4667
4881
|
fullSlug,
|
|
4668
4882
|
installed: entry.version,
|
|
4669
|
-
|
|
4883
|
+
target_version: resolved.resolved_version,
|
|
4884
|
+
requestedRange,
|
|
4670
4885
|
tool: entry.tool,
|
|
4671
|
-
target: entry.target
|
|
4886
|
+
target: entry.target,
|
|
4887
|
+
...semver2.gt(resolved.current_version, resolved.resolved_version) ? { outOfRangeMajor: resolved.current_version } : {}
|
|
4672
4888
|
});
|
|
4673
4889
|
}
|
|
4674
4890
|
} catch {
|
|
@@ -4686,7 +4902,7 @@ function updateCommand() {
|
|
|
4686
4902
|
const dryEntries = candidates.map((c2) => ({
|
|
4687
4903
|
full_slug: c2.fullSlug,
|
|
4688
4904
|
from: c2.installed,
|
|
4689
|
-
to: c2.
|
|
4905
|
+
to: c2.target_version,
|
|
4690
4906
|
status: "dry_run"
|
|
4691
4907
|
}));
|
|
4692
4908
|
printDual({
|
|
@@ -4699,8 +4915,9 @@ function updateCommand() {
|
|
|
4699
4915
|
);
|
|
4700
4916
|
printHuman("");
|
|
4701
4917
|
for (const cand of candidates) {
|
|
4918
|
+
const tail = cand.outOfRangeMajor ? c.gray(` (registry has ${cand.outOfRangeMajor}, out of range ${cand.requestedRange})`) : "";
|
|
4702
4919
|
printHuman(
|
|
4703
|
-
` ${c.bold(cand.fullSlug)} ${c.gray(cand.installed)} \u2192 ${c.green(cand.
|
|
4920
|
+
` ${c.bold(cand.fullSlug)} ${c.gray(cand.installed)} \u2192 ${c.green(cand.target_version)}${tail}`
|
|
4704
4921
|
);
|
|
4705
4922
|
}
|
|
4706
4923
|
}
|
|
@@ -4709,11 +4926,17 @@ function updateCommand() {
|
|
|
4709
4926
|
}
|
|
4710
4927
|
const summary = [];
|
|
4711
4928
|
for (const cand of candidates) {
|
|
4929
|
+
const tail = cand.outOfRangeMajor ? c.gray(` (registry has ${cand.outOfRangeMajor}, out of range ${cand.requestedRange})`) : "";
|
|
4712
4930
|
printHuman(
|
|
4713
|
-
` ${c.cyan("\u2191")} Updating ${c.bold(cand.fullSlug)} ${c.gray(cand.installed)} \u2192 ${c.green(cand.
|
|
4931
|
+
` ${c.cyan("\u2191")} Updating ${c.bold(cand.fullSlug)} ${c.gray(cand.installed)} \u2192 ${c.green(cand.target_version)}${tail}`
|
|
4714
4932
|
);
|
|
4715
4933
|
try {
|
|
4716
|
-
const result = await doPullInstall(cand.fullSlug, cand.tool, cand.target
|
|
4934
|
+
const result = await doPullInstall(cand.fullSlug, cand.tool, cand.target, {
|
|
4935
|
+
version: cand.target_version,
|
|
4936
|
+
withRequired: false,
|
|
4937
|
+
withRecommends: false,
|
|
4938
|
+
requestedRange: cand.requestedRange
|
|
4939
|
+
});
|
|
4717
4940
|
summary.push({
|
|
4718
4941
|
full_slug: cand.fullSlug,
|
|
4719
4942
|
from: cand.installed,
|
|
@@ -4727,7 +4950,7 @@ function updateCommand() {
|
|
|
4727
4950
|
summary.push({
|
|
4728
4951
|
full_slug: cand.fullSlug,
|
|
4729
4952
|
from: cand.installed,
|
|
4730
|
-
to: cand.
|
|
4953
|
+
to: cand.target_version,
|
|
4731
4954
|
status: "failed",
|
|
4732
4955
|
error: message
|
|
4733
4956
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rayul/kordis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Command-line client for the Kordis registry — discover, install, and publish agentic-coding packages.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"open": "^10.1.0",
|
|
33
33
|
"ora": "^8.1.1",
|
|
34
34
|
"prompts": "^2.4.2",
|
|
35
|
+
"semver": "^7.6.3",
|
|
35
36
|
"yaml": "^2.6.0",
|
|
36
37
|
"zod": "^3.23.8"
|
|
37
38
|
},
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
"@types/jszip": "^3.4.1",
|
|
48
49
|
"@types/node": "^22.9.0",
|
|
49
50
|
"@types/prompts": "^2.4.9",
|
|
51
|
+
"@types/semver": "^7.5.8",
|
|
50
52
|
"tsup": "^8.3.5",
|
|
51
53
|
"tsx": "^4.19.2",
|
|
52
54
|
"typescript": "^5.6.3",
|