@rayul/kordis 0.6.2 → 0.6.3

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.
@@ -45,17 +45,24 @@ re-ranking ONLY — never persisted (see CLAUDE.md "Telemetry / privacy").
45
45
 
46
46
  ## Personalization opt-in
47
47
 
48
- The user is asked ONCE — at first interactive `kordis setup` OR (if
49
- postinstall ran in `--silent` mode) at first `kordis recommend`. Answer
50
- persists in `~/.kordis/config.json` (`personalization: 'on' | 'off'`).
48
+ The user is asked ONCE. Three paths persist the answer to
49
+ `~/.kordis/config.json` as `personalization: 'on' | 'off'`:
51
50
 
52
- Override at runtime:
51
+ 1. **Interactive `kordis setup`** in a real TTY — Y/n prompt.
52
+ 2. **First `kordis recommend`** in a TTY (lazy fallback if postinstall
53
+ ran `--silent`).
54
+ 3. **`kordis setup --personalization on|off`** — non-interactive flag
55
+ (0.6.3+). The **agent-facing path**: works without a TTY, no prompt.
56
+ Use this when the user has confirmed consent in chat — see Step 2.5.
57
+
58
+ Override at runtime (does not change the persisted answer):
53
59
  - `KORDIS_NO_CONTEXT=1` env — no header for the entire shell session
54
60
  - `--no-context` flag — no header for this single call
55
61
 
56
62
  If the response field `personalized` is `false`, the server returned
57
- popularity-only results — surface this to the user so they know the
58
- recommendations aren't tailored.
63
+ popularity-only results — almost always because the user hasn't been
64
+ asked yet (agent contexts have no TTY, so paths 1 and 2 never fire).
65
+ Surface this AND ask the user before flipping the flag (Step 2.5).
59
66
 
60
67
  ## Menu envelope (one item)
61
68
 
@@ -92,8 +99,7 @@ explicitly asked you not to share their stack.)
92
99
 
93
100
  ### 2. Triage
94
101
  Filter mentally by `confidence`. If everything is `low` and `personalized`
95
- is `false`, tell the user personalization is off and they can enable it
96
- via `kordis setup` (re-asks the prompt on next interactive run).
102
+ is `false`, follow Step 2.5 to offer the user a one-click consent path.
97
103
 
98
104
  If the response carries `capped_by_author > 0`, ALSO surface a one-line
99
105
  note to the user — e.g. "N more candidates from the same publisher were
@@ -101,6 +107,30 @@ skipped by the diversity cap" — so they understand why the list is
101
107
  shorter than the requested `--limit`. Without this hint users frequently
102
108
  mistake a short menu for a sparse or broken registry.
103
109
 
110
+ ### 2.5. Offer personalization consent (when `personalized: false`)
111
+
112
+ If the response is `personalized: false` and the user is engaged in chat
113
+ (i.e., you can ask them a question), surface this naturally:
114
+
115
+ > "추천 결과가 popularity-only로 왔어요 (개인화 OFF). 익명 신호
116
+ > (설치된 에이전트 툴, 현재 디렉토리의 언어/프레임워크 힌트)로
117
+ > 추천을 맞춤화할 수 있는데 켜드릴까요? 서버는 신호를 저장하지
118
+ > 않고 응답 re-ranking에만 사용해요."
119
+
120
+ ONLY after the user explicitly says yes, run:
121
+
122
+ ```
123
+ kordis setup --personalization on
124
+ ```
125
+
126
+ Then re-run `kordis recommend --limit 10 --json` to get the personalized
127
+ menu. **Never run `--personalization on` without the user's confirmation
128
+ in chat** — it persists a consent flag, which is the user's choice.
129
+
130
+ If the user says no (or doesn't reply), don't push it — record opt-out
131
+ optionally via `kordis setup --personalization off` so the question
132
+ isn't asked again.
133
+
104
134
  ### 3. Present
105
135
  Surface 3-5 picks max. For each, show: full_slug, one-line `why_recommended`,
106
136
  confidence wording per the table above, and the next-step
@@ -127,7 +157,9 @@ User: "any new tools I should try?"
127
157
  <example>
128
158
  User: "추천 좀 해봐"
129
159
  1. kordis recommend --limit 10 --json
130
- 2. If personalized is false, tell the user briefly + suggest `kordis setup`
160
+ 2. If `personalized: false`, ask in chat (Step 2.5): "개인화 켜드릴까요? ..."
161
+ - if user says yes → `kordis setup --personalization on`, then re-run
162
+ - if user says no → surface popularity-only picks as-is
131
163
  3. Surface 3-5 in Korean using the why_recommended verbatim (English is fine
132
164
  — re-render the user-facing prose in Korean as needed)
133
165
  </example>
package/dist/index.js CHANGED
@@ -3642,9 +3642,33 @@ async function copyBundledSkill(filePath, content, dryRun) {
3642
3642
  return existing === null ? "injected" : "updated";
3643
3643
  }
3644
3644
  function setupCommand() {
3645
- return new Command9("setup").description("inject kordis usage guide into global agent instruction files").option("--silent", "suppress errors \u2014 safe for postinstall").option("--dry-run", "detect installed tools without writing any files").action(async (opts) => {
3645
+ return new Command9("setup").description("inject kordis usage guide into global agent instruction files").option("--silent", "suppress errors \u2014 safe for postinstall").option("--dry-run", "detect installed tools without writing any files").option(
3646
+ "--personalization <choice>",
3647
+ "record opt-in non-interactively: 'on' | 'off' (skips the Y/n prompt; safe for agents that just received user consent in chat)"
3648
+ ).action(async (opts) => {
3646
3649
  const home = homedir7();
3647
3650
  const dryRun = opts.dryRun ?? false;
3651
+ if (opts.personalization !== void 0) {
3652
+ const v = opts.personalization.toLowerCase();
3653
+ if (v !== "on" && v !== "off") {
3654
+ throw new Error("--personalization must be 'on' or 'off'");
3655
+ }
3656
+ if (!dryRun) {
3657
+ const cfg = await loadCliConfig();
3658
+ await saveCliConfig({ ...cfg, personalization: v });
3659
+ }
3660
+ if (!opts.silent) {
3661
+ if (v === "on") {
3662
+ printHuman(
3663
+ c.green("\u2713 personalization on") + c.gray(" \u2014 disable any time: `KORDIS_NO_CONTEXT=1` env, `--no-context` flag, or `kordis setup --personalization off`")
3664
+ );
3665
+ } else {
3666
+ printHuman(
3667
+ c.gray("\u2192 personalization off \u2014 recommendations use popularity only.")
3668
+ );
3669
+ }
3670
+ }
3671
+ }
3648
3672
  let hubContent;
3649
3673
  let subSkillContents;
3650
3674
  try {
@@ -4287,7 +4311,7 @@ function recommendCommand() {
4287
4311
  if (!enable) {
4288
4312
  printHuman(
4289
4313
  c.gray(
4290
- "\u2192 recommendations will use popularity only. Re-enable later: `kordis setup` (re-asks the opt-in)."
4314
+ "\u2192 recommendations will use popularity only. Re-enable later: `kordis setup --personalization on`."
4291
4315
  )
4292
4316
  );
4293
4317
  }
@@ -4335,7 +4359,7 @@ function recommendCommand() {
4335
4359
  if (!res.personalized) {
4336
4360
  printHuman(
4337
4361
  c.gray(
4338
- "\u2192 enable personalization for tailored picks: `kordis setup` (re-asks the opt-in)"
4362
+ "\u2192 enable personalization for tailored picks: `kordis setup --personalization on` (no Y/n prompt \u2014 agent-friendly; confirm with the user in chat first)"
4339
4363
  )
4340
4364
  );
4341
4365
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayul/kordis",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
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",