@rayul/kordis 0.2.2 → 0.3.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 +24 -8
- package/dist/index.js +65 -9
- package/package.json +1 -1
|
@@ -31,15 +31,22 @@ Publish a valid, AI-review-passing version. ALWAYS dry-run first.
|
|
|
31
31
|
|
|
32
32
|
## Deleting a package
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
- Web UI: `https://kordis.store/p/<full_slug>` (if a delete button is available)
|
|
36
|
-
- Registry owner: soft-delete via the prod Postgres
|
|
37
|
-
(`UPDATE packages SET deleted_at = NOW() WHERE full_slug = '...'`)
|
|
38
|
-
- Workaround: push a new version with `visibility: unlisted` or a
|
|
39
|
-
deprecation notice in the body
|
|
34
|
+
### `kordis delete <full_slug> [--force]`
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
Soft-deletes a package you own. Removes from search / info / read /
|
|
37
|
+
pull. The slug is **permanently reserved** after deletion (supply-chain
|
|
38
|
+
safety) — it cannot be reused even by you. There is **no CLI undo**;
|
|
39
|
+
the registry operator can restore from the soft-delete row if asked.
|
|
40
|
+
|
|
41
|
+
Without `--force` the CLI shows the package metadata and prompts for
|
|
42
|
+
confirmation. Use `--force` ONLY when the user has explicitly confirmed
|
|
43
|
+
the destructive action (e.g. they said "yes, delete it"). Never pass
|
|
44
|
+
`--force` on the user's behalf.
|
|
45
|
+
|
|
46
|
+
If the body contains third-party content, MUST preserve the original
|
|
47
|
+
copyright and license text inside the body and link to source via
|
|
48
|
+
`homepage` — even on the version you intend to delete, in case the
|
|
49
|
+
operator restores it.
|
|
43
50
|
|
|
44
51
|
## Commands
|
|
45
52
|
|
|
@@ -122,6 +129,15 @@ User: "release with a note that breaking-changes the API"
|
|
|
122
129
|
2. clean → drop --dry-run for the real push
|
|
123
130
|
</example>
|
|
124
131
|
|
|
132
|
+
<example>
|
|
133
|
+
User: "delete <full_slug>"
|
|
134
|
+
1. confirm with the user that they understand the slug is permanently
|
|
135
|
+
reserved (cannot be reused) and there is no CLI undo
|
|
136
|
+
2. kordis delete <full_slug> # interactive confirmation prompt
|
|
137
|
+
- if user already confirmed in prior turn, kordis delete <full_slug> --force
|
|
138
|
+
3. show the {status, full_slug} reply to the user
|
|
139
|
+
</example>
|
|
140
|
+
|
|
125
141
|
## Forking another Kordis package
|
|
126
142
|
|
|
127
143
|
When the user asks to fork / adapt / republish someone else's package
|
package/dist/index.js
CHANGED
|
@@ -175,6 +175,7 @@ var init_errors = __esm({
|
|
|
175
175
|
error: z3.object({
|
|
176
176
|
code: ErrorCodeSchema,
|
|
177
177
|
message: z3.string(),
|
|
178
|
+
hint: z3.string().optional(),
|
|
178
179
|
flags: z3.array(ReviewFlagSchema).optional(),
|
|
179
180
|
retry_after_seconds: z3.number().int().nonnegative().optional(),
|
|
180
181
|
request_id: z3.string().optional()
|
|
@@ -631,7 +632,17 @@ var init_api = __esm({
|
|
|
631
632
|
* KORDIS.md recommends but does not enforce.
|
|
632
633
|
*/
|
|
633
634
|
attribution: z9.string(),
|
|
634
|
-
read_at: z9.string().datetime({ offset: true })
|
|
635
|
+
read_at: z9.string().datetime({ offset: true }),
|
|
636
|
+
/**
|
|
637
|
+
* Paths (NOT contents) of bundled assets that ship with the package —
|
|
638
|
+
* `references/*.md`, `assets/*.html`, `scripts/*.py`, etc. Agents reading a
|
|
639
|
+
* skill that references its own bundled files (e.g. "use
|
|
640
|
+
* `assets/template.html`") need a structured signal to switch to
|
|
641
|
+
* `kordis pull`; without it, the only hint is in body prose. Optional in
|
|
642
|
+
* the schema for backwards compatibility with pre-0.2.3 servers; the
|
|
643
|
+
* current server always populates it (empty array when no assets).
|
|
644
|
+
*/
|
|
645
|
+
bundled_assets: z9.array(z9.string()).optional()
|
|
635
646
|
});
|
|
636
647
|
PullPackageResponseSchema = z9.object({
|
|
637
648
|
status: z9.enum(["installed", "already_installed", "skipped"]),
|
|
@@ -718,6 +729,7 @@ var init_errors2 = __esm({
|
|
|
718
729
|
KordisApiError = class extends Error {
|
|
719
730
|
code;
|
|
720
731
|
status;
|
|
732
|
+
hint;
|
|
721
733
|
flags;
|
|
722
734
|
retryAfter;
|
|
723
735
|
requestId;
|
|
@@ -726,6 +738,7 @@ var init_errors2 = __esm({
|
|
|
726
738
|
this.name = "KordisApiError";
|
|
727
739
|
this.code = envelope.error.code;
|
|
728
740
|
this.status = status;
|
|
741
|
+
this.hint = envelope.error.hint;
|
|
729
742
|
this.flags = envelope.error.flags ?? [];
|
|
730
743
|
this.retryAfter = envelope.error.retry_after_seconds;
|
|
731
744
|
this.requestId = envelope.error.request_id ?? requestId;
|
|
@@ -1041,7 +1054,7 @@ async function saveCliConfig(cfg) {
|
|
|
1041
1054
|
}
|
|
1042
1055
|
|
|
1043
1056
|
// src/keychain.ts
|
|
1044
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
1057
|
+
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
1045
1058
|
import { homedir as homedir2 } from "os";
|
|
1046
1059
|
import { join as join2 } from "path";
|
|
1047
1060
|
var SERVICE = "kordis";
|
|
@@ -1054,12 +1067,28 @@ async function loadKeytar() {
|
|
|
1054
1067
|
return null;
|
|
1055
1068
|
}
|
|
1056
1069
|
}
|
|
1070
|
+
function kordisHome() {
|
|
1071
|
+
return process.env["KORDIS_HOME"] ?? join2(homedir2(), ".kordis");
|
|
1072
|
+
}
|
|
1057
1073
|
function fallbackPath() {
|
|
1058
|
-
return join2(
|
|
1074
|
+
return join2(kordisHome(), ".tokens.json");
|
|
1075
|
+
}
|
|
1076
|
+
function warningSentinelPath() {
|
|
1077
|
+
return join2(kordisHome(), ".warned-no-keychain");
|
|
1078
|
+
}
|
|
1079
|
+
async function pathExists(p) {
|
|
1080
|
+
try {
|
|
1081
|
+
await access(p);
|
|
1082
|
+
return true;
|
|
1083
|
+
} catch {
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1059
1086
|
}
|
|
1060
1087
|
async function createKeychain() {
|
|
1061
1088
|
const keytar = await loadKeytar();
|
|
1062
1089
|
if (keytar) {
|
|
1090
|
+
await unlink(warningSentinelPath()).catch(() => {
|
|
1091
|
+
});
|
|
1063
1092
|
return {
|
|
1064
1093
|
async save(username, tokens) {
|
|
1065
1094
|
await keytar.setPassword(SERVICE, username, JSON.stringify(tokens));
|
|
@@ -1073,15 +1102,22 @@ async function createKeychain() {
|
|
|
1073
1102
|
}
|
|
1074
1103
|
};
|
|
1075
1104
|
}
|
|
1076
|
-
if (!plaintextWarningEmitted && process.env["KORDIS_JSON"] !== "1") {
|
|
1105
|
+
if (!plaintextWarningEmitted && process.env["KORDIS_JSON"] !== "1" && !await pathExists(warningSentinelPath())) {
|
|
1077
1106
|
plaintextWarningEmitted = true;
|
|
1078
1107
|
const yellow = "\x1B[33m";
|
|
1079
1108
|
const reset = "\x1B[0m";
|
|
1080
1109
|
process.stderr.write(
|
|
1081
1110
|
`${yellow}[kordis] WARNING: OS keychain unavailable; falling back to plaintext token storage at ~/.kordis/.tokens.json (mode 0600).
|
|
1082
|
-
[kordis] On macOS install Xcode Command Line Tools, on Linux install libsecret-1-dev to enable keytar
|
|
1111
|
+
[kordis] On macOS install Xcode Command Line Tools, on Linux install libsecret-1-dev to enable keytar.
|
|
1112
|
+
[kordis] (suppressing this warning on subsequent runs \u2014 delete ~/.kordis/.warned-no-keychain to re-enable)${reset}
|
|
1083
1113
|
`
|
|
1084
1114
|
);
|
|
1115
|
+
await mkdir2(kordisHome(), { recursive: true }).catch(() => {
|
|
1116
|
+
});
|
|
1117
|
+
await writeFile2(warningSentinelPath(), "", { mode: 384 }).catch(() => {
|
|
1118
|
+
});
|
|
1119
|
+
} else if (!plaintextWarningEmitted) {
|
|
1120
|
+
plaintextWarningEmitted = true;
|
|
1085
1121
|
}
|
|
1086
1122
|
const store = await readAll();
|
|
1087
1123
|
return {
|
|
@@ -1920,10 +1956,13 @@ function zodFail(issues) {
|
|
|
1920
1956
|
hint = "description must be 1\u20132048 chars; current is too long. Move detail into the body.";
|
|
1921
1957
|
} else if (field === "description" && msg.includes("small")) {
|
|
1922
1958
|
hint = "description must be 1\u20132048 chars; provide a one-paragraph summary.";
|
|
1959
|
+
} else if (field) {
|
|
1960
|
+
hint = `Fix the value of frontmatter field "${field}" to match the schema. See the per-type spec under packages/harness-schema/src/types/ for the exact shape.`;
|
|
1961
|
+
} else {
|
|
1962
|
+
hint = "Fix the frontmatter to match the schema. See the per-type spec under packages/harness-schema/src/types/ for the exact shape.";
|
|
1923
1963
|
}
|
|
1924
|
-
const extra = {};
|
|
1964
|
+
const extra = { hint };
|
|
1925
1965
|
if (field) extra.field = field;
|
|
1926
|
-
if (hint) extra.hint = hint;
|
|
1927
1966
|
return flag("invalid_frontmatter", i.message, extra);
|
|
1928
1967
|
})
|
|
1929
1968
|
};
|
|
@@ -2862,6 +2901,17 @@ function readCommand() {
|
|
|
2862
2901
|
}
|
|
2863
2902
|
process.stdout.write(res.body);
|
|
2864
2903
|
if (!res.body.endsWith("\n")) process.stdout.write("\n");
|
|
2904
|
+
const bundled = res.bundled_assets ?? [];
|
|
2905
|
+
if (bundled.length > 0) {
|
|
2906
|
+
process.stderr.write(
|
|
2907
|
+
`${c.gray("\u2014")} ${c.bold(`\u24D8 ${bundled.length} bundled asset(s)`)} \u2014 fetch with ${c.cyan(`kordis pull ${res.full_slug}`)}:
|
|
2908
|
+
`
|
|
2909
|
+
);
|
|
2910
|
+
for (const path of bundled) {
|
|
2911
|
+
process.stderr.write(` ${c.gray("-")} ${path}
|
|
2912
|
+
`);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2865
2915
|
process.stderr.write(
|
|
2866
2916
|
`${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)}
|
|
2867
2917
|
`
|
|
@@ -2944,7 +2994,7 @@ function logoutCommand() {
|
|
|
2944
2994
|
}
|
|
2945
2995
|
|
|
2946
2996
|
// src/commands/setup.ts
|
|
2947
|
-
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, access } from "fs/promises";
|
|
2997
|
+
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, access as access2 } from "fs/promises";
|
|
2948
2998
|
import { homedir as homedir4 } from "os";
|
|
2949
2999
|
import { join as join7, dirname as dirname3 } from "path";
|
|
2950
3000
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -2969,7 +3019,7 @@ var SKILL_TARGETS = [
|
|
|
2969
3019
|
];
|
|
2970
3020
|
async function exists(path) {
|
|
2971
3021
|
try {
|
|
2972
|
-
await
|
|
3022
|
+
await access2(path);
|
|
2973
3023
|
return true;
|
|
2974
3024
|
} catch {
|
|
2975
3025
|
return false;
|
|
@@ -3701,6 +3751,7 @@ function handleError(err) {
|
|
|
3701
3751
|
error: {
|
|
3702
3752
|
code: err.code,
|
|
3703
3753
|
message: err.message,
|
|
3754
|
+
...err.hint ? { hint: err.hint } : {},
|
|
3704
3755
|
flags: err.flags,
|
|
3705
3756
|
...err.retryAfter !== void 0 ? { retry_after_seconds: err.retryAfter } : {},
|
|
3706
3757
|
request_id: err.requestId
|
|
@@ -3708,6 +3759,11 @@ function handleError(err) {
|
|
|
3708
3759
|
});
|
|
3709
3760
|
} else {
|
|
3710
3761
|
printHuman(c.red(`\u2717 ${err.code}: ${err.message}`));
|
|
3762
|
+
if (err.hint) {
|
|
3763
|
+
for (const line of err.hint.split("\n")) {
|
|
3764
|
+
printHuman(c.gray(` \u2192 ${line}`));
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3711
3767
|
for (const f of err.flags) {
|
|
3712
3768
|
printHuman(c.red(` - ${f.code}${f.message ? `: ${f.message}` : ""}`));
|
|
3713
3769
|
if (f.hint) {
|