@rayul/kordis 0.2.2 → 0.3.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.
@@ -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
- There is **no `kordis delete` CLI command**. Options:
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
- If the body contains third-party content, MUST preserve the original copyright
42
- and license text inside the body and link to source via `homepage`.
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
@@ -631,7 +631,17 @@ var init_api = __esm({
631
631
  * KORDIS.md recommends but does not enforce.
632
632
  */
633
633
  attribution: z9.string(),
634
- read_at: z9.string().datetime({ offset: true })
634
+ read_at: z9.string().datetime({ offset: true }),
635
+ /**
636
+ * Paths (NOT contents) of bundled assets that ship with the package —
637
+ * `references/*.md`, `assets/*.html`, `scripts/*.py`, etc. Agents reading a
638
+ * skill that references its own bundled files (e.g. "use
639
+ * `assets/template.html`") need a structured signal to switch to
640
+ * `kordis pull`; without it, the only hint is in body prose. Optional in
641
+ * the schema for backwards compatibility with pre-0.2.3 servers; the
642
+ * current server always populates it (empty array when no assets).
643
+ */
644
+ bundled_assets: z9.array(z9.string()).optional()
635
645
  });
636
646
  PullPackageResponseSchema = z9.object({
637
647
  status: z9.enum(["installed", "already_installed", "skipped"]),
@@ -1041,7 +1051,7 @@ async function saveCliConfig(cfg) {
1041
1051
  }
1042
1052
 
1043
1053
  // src/keychain.ts
1044
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
1054
+ import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
1045
1055
  import { homedir as homedir2 } from "os";
1046
1056
  import { join as join2 } from "path";
1047
1057
  var SERVICE = "kordis";
@@ -1054,12 +1064,28 @@ async function loadKeytar() {
1054
1064
  return null;
1055
1065
  }
1056
1066
  }
1067
+ function kordisHome() {
1068
+ return process.env["KORDIS_HOME"] ?? join2(homedir2(), ".kordis");
1069
+ }
1057
1070
  function fallbackPath() {
1058
- return join2(process.env["KORDIS_HOME"] ?? join2(homedir2(), ".kordis"), ".tokens.json");
1071
+ return join2(kordisHome(), ".tokens.json");
1072
+ }
1073
+ function warningSentinelPath() {
1074
+ return join2(kordisHome(), ".warned-no-keychain");
1075
+ }
1076
+ async function pathExists(p) {
1077
+ try {
1078
+ await access(p);
1079
+ return true;
1080
+ } catch {
1081
+ return false;
1082
+ }
1059
1083
  }
1060
1084
  async function createKeychain() {
1061
1085
  const keytar = await loadKeytar();
1062
1086
  if (keytar) {
1087
+ await unlink(warningSentinelPath()).catch(() => {
1088
+ });
1063
1089
  return {
1064
1090
  async save(username, tokens) {
1065
1091
  await keytar.setPassword(SERVICE, username, JSON.stringify(tokens));
@@ -1073,15 +1099,22 @@ async function createKeychain() {
1073
1099
  }
1074
1100
  };
1075
1101
  }
1076
- if (!plaintextWarningEmitted && process.env["KORDIS_JSON"] !== "1") {
1102
+ if (!plaintextWarningEmitted && process.env["KORDIS_JSON"] !== "1" && !await pathExists(warningSentinelPath())) {
1077
1103
  plaintextWarningEmitted = true;
1078
1104
  const yellow = "\x1B[33m";
1079
1105
  const reset = "\x1B[0m";
1080
1106
  process.stderr.write(
1081
1107
  `${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.${reset}
1108
+ [kordis] On macOS install Xcode Command Line Tools, on Linux install libsecret-1-dev to enable keytar.
1109
+ [kordis] (suppressing this warning on subsequent runs \u2014 delete ~/.kordis/.warned-no-keychain to re-enable)${reset}
1083
1110
  `
1084
1111
  );
1112
+ await mkdir2(kordisHome(), { recursive: true }).catch(() => {
1113
+ });
1114
+ await writeFile2(warningSentinelPath(), "", { mode: 384 }).catch(() => {
1115
+ });
1116
+ } else if (!plaintextWarningEmitted) {
1117
+ plaintextWarningEmitted = true;
1085
1118
  }
1086
1119
  const store = await readAll();
1087
1120
  return {
@@ -2862,6 +2895,17 @@ function readCommand() {
2862
2895
  }
2863
2896
  process.stdout.write(res.body);
2864
2897
  if (!res.body.endsWith("\n")) process.stdout.write("\n");
2898
+ const bundled = res.bundled_assets ?? [];
2899
+ if (bundled.length > 0) {
2900
+ process.stderr.write(
2901
+ `${c.gray("\u2014")} ${c.bold(`\u24D8 ${bundled.length} bundled asset(s)`)} \u2014 fetch with ${c.cyan(`kordis pull ${res.full_slug}`)}:
2902
+ `
2903
+ );
2904
+ for (const path of bundled) {
2905
+ process.stderr.write(` ${c.gray("-")} ${path}
2906
+ `);
2907
+ }
2908
+ }
2865
2909
  process.stderr.write(
2866
2910
  `${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
2911
  `
@@ -2944,7 +2988,7 @@ function logoutCommand() {
2944
2988
  }
2945
2989
 
2946
2990
  // src/commands/setup.ts
2947
- import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, access } from "fs/promises";
2991
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5, access as access2 } from "fs/promises";
2948
2992
  import { homedir as homedir4 } from "os";
2949
2993
  import { join as join7, dirname as dirname3 } from "path";
2950
2994
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -2969,7 +3013,7 @@ var SKILL_TARGETS = [
2969
3013
  ];
2970
3014
  async function exists(path) {
2971
3015
  try {
2972
- await access(path);
3016
+ await access2(path);
2973
3017
  return true;
2974
3018
  } catch {
2975
3019
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayul/kordis",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
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",