Forráskód Böngészése

refactor: 改造成 Claude Code 插件,准备 marketplace 分发

分发形态从「整目录复制」改为插件 + 自建 marketplace(决策 7 修正决策 4)。

布局:.claude/skills/wikipali-write/ → plugins/wikipali-write/,加
.claude-plugin/plugin.json,SKILL.md 等挪进 skills/write/。用 skills/
子目录而不是把 SKILL.md 放插件根,是因为后者的调用名会变成
wikipali-write:wikipali-write,且 P2 还要加读取和 sentpr 两个 skill。

仓库根留 symlink .claude/skills/wikipali-write → plugins/.../skills/write,
这样在 mint 里开发时(从任何子目录启动)skill 仍自动加载——实测普通
skill 的向上查找会跟随 symlink。

删掉 VERSION 文件:版本号只留 plugin.json 的 version 一处,两处必然
漂移;install.sh 改为从 manifest 读,并降级为不走 marketplace 时的后路。

marketplace 单独一个 8 KB 的仓库(wikipali-plugins),插件本体留在
mint 用 git-subdir 稀疏克隆。mint 不能自己当 marketplace:marketplace
是整仓 clone,packfile 580 MB 会撞上 120 秒的 git 超时。

本地已验证:plugin validate 通过,marketplace add → install → 组件清单
显示 Skills(1) write、常驻 ~230 tok,装出来的副本在无关目录下加载为
wikipali-write:write,缓存里的 wp.py 能跑并复用 ~/.wikipali 的凭据。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
visuddhinanda 2 hónapja
szülő
commit
c46cf64000

+ 1 - 0
.claude/skills/wikipali-write

@@ -0,0 +1 @@
+../../plugins/wikipali-write/skills/write

+ 0 - 1
.claude/skills/wikipali-write/VERSION

@@ -1 +0,0 @@
-2026.08.05

+ 42 - 31
docs/wikipali-write-skill-design.md

@@ -4,7 +4,7 @@
 >
 > 分发路径:在本仓库开发调试,成熟后以**整目录复制**方式装到其他项目(§6.7)。不做独立仓库——API 仍需频繁修改,Skill 契约必须与 `api-v13` 同仓演进。
 >
-> 状态:设计已定案;服务端 P0 已完成(§5.1 端点 + §5.2 abdefg);Skill P1 已完成(`.claude/skills/wikipali-write/`)并在开发机上端到端跑通(2026-08-05);线上四站尚未部署
+> 状态:设计已定案;服务端 P0 已完成(§5.1 端点 + §5.2 abdefg);Skill P1 已完成(`plugins/wikipali-write/`)并在开发机上端到端跑通(2026-08-05);线上四站尚未部署
 > 对应后端:`api-v13`(Laravel 13,路由前缀 `/api/v2`)
 > 决策定案:2026-08-04(见 §9)
 
@@ -312,34 +312,43 @@ public function show(Request $request, AiModel $aiModel)
 
 ### 6.1 目录结构
 
-**开发地点:本仓库。分发方式:整目录复制。**(§9 决策 4)
+**开发地点:本仓库。分发方式:Claude Code 插件(marketplace)。**(§9 决策 4、决策 7)
 
 理由:API 尚不完善,Skill 与服务端要同步改(§5 的每一项都会反映到 `references/api.md`)。放在 mint 仓库内,一次提交就能同时改 Laravel 代码和 Skill 契约;独立仓库会让两者版本漂移,且改 API 时无法在同一个 Claude Code 会话里读写后端代码。
 
-放在仓库根的 `.claude/skills/` 下:
+放在仓库根的 `plugins/` 下,本身就是一个合法插件:
 
 ```
-.claude/skills/wikipali-write/
-├── SKILL.md              # 触发条件 + 流程说明(给模型读)
-├── VERSION               # 日历版本号,随 API 契约变更递增
-├── references/
-│   └── api.md            # 本文 §2 的精简版:端点、字段、陷阱
-├── scripts/
-│   ├── wp_login.py       # 交互式登录,仅此脚本接触密码
-│   └── wp.py             # 客户端:endpoint / whoami / ensure-model / revoke /
-│                         #         channels / grant / write
-└── install.sh            # 复制自身到目标项目或 ~/.claude/skills/
+plugins/wikipali-write/
+├── .claude-plugin/
+│   └── plugin.json       # 插件清单,version 是唯一的版本来源
+├── README.md             # 面向安装者:装之前它会动你哪些东西
+├── install.sh            # 不走 marketplace 时的后路
+└── skills/
+    └── write/            # → 调用名 wikipali-write:write
+        ├── SKILL.md      # 触发条件 + 流程说明(给模型读)
+        ├── references/
+        │   └── api.md    # 本文 §2 的精简版:端点、字段、陷阱
+        └── scripts/
+            ├── wp_login.py   # 交互式登录,仅此脚本接触密码
+            └── wp.py         # 客户端:endpoint / whoami / ensure-model /
+                              #         revoke / channels / grant / write
 ```
 
 实现时比原计划多了两个子命令:`whoami`(一屏看清当前站点、三种 token 及其到期时间——排查「为什么 401」的第一步)与 `revoke`(§2.3b 的撤销端点,安全能力做了就该有入口)。`wp_login.py` 通过 `import wp` 复用 HTTP 与凭据代码,两个文件仍在同一目录内,不违反自包含约束。
 
-注意放在**仓库根**而非 `api-v13/.claude/skills/`:后者已有 `laravel-best-practices` 等目录级 skill,只在编辑 `api-v13/` 下文件时激活;而本 Skill 是对线上 API 的客户端操作,与当前编辑哪个子目录无关。
+几个布局上的决定:
+
+- **用 `skills/write/` 而不是把 SKILL.md 放插件根**。后者也合法(单 skill 插件允许),但调用名会变成 `wikipali-write:wikipali-write`;而且 §9 后续规划里还有读取和 sentpr 两个 skill,`skills/` 布局才能容纳。
+- **`VERSION` 文件已删**。版本号只留 `plugin.json` 的 `version` 一处,两处必然漂移;`install.sh` 改为从 manifest 读。
+- **仓库根留一个 symlink** `.claude/skills/wikipali-write → ../../plugins/wikipali-write/skills/write`,这样在 mint 里开发时(无论从哪个子目录启动 Claude Code)skill 仍然自动加载。实测普通 skill 的向上查找会跟随 symlink;插件形态则用 `--plugin-dir ./plugins/wikipali-write` 测。
+- 放在**仓库根**而非 `api-v13/` 下:后者已有 `laravel-best-practices` 等目录级 skill,只在编辑 `api-v13/` 时激活;而本 Skill 是对线上 API 的客户端操作,与当前编辑哪个子目录无关。
 
 ### 6.1.1 可分发性约束
 
 「能复制给别的项目用」是硬需求,因此以下几条是**约束而非偏好**:
 
-1. **目录自包含**——不引用 `.claude/skills/wikipali-write/` 之外的任何路径。SKILL.md 里不能出现 `api-v13/...` 这类仓库内引用;需要的 API 事实全部落在 `references/api.md` 里。
+1. **目录自包含**——不引用 `plugins/wikipali-write/` 之外的任何路径。SKILL.md 里不能出现 `api-v13/...` 这类仓库内引用;需要的 API 事实全部落在 `references/api.md` 里。
 2. **零安装依赖,只用 Python 标准库**——用 `urllib.request` 而非 `requests`,`json` / `getpass` / `argparse` 均为内置。**不跟随 `ai-translate` 的 venv + `pip install -e` 模式**(`ai-translate/pyproject.toml` 依赖 `pika`/`requests`/`redis`/`openai`):那套在目标项目里要求用户先建虚拟环境,与「复制即用」冲突。代价是要自己处理 `urllib` 的 HTTPError/超时/JSON 编码,比 `requests` 啰嗦,但换来 `python3 scripts/wp.py` 开箱可跑。
 3. **API 地址不硬编码**——见 §6.1.2。复制到别的项目后无需改代码。
 4. **凭据与 Skill 解耦**——凭据在 `~/.wikipali/`(§6.2),多个项目里的 Skill 副本共用同一份登录态,登录一次即可。
@@ -490,30 +499,31 @@ ChannelController::index() 的 'user-edit' 分支:
 
 注意 `SentenceController::store()` 对**逐句失败是静默跳过**的(`:341`),所以「HTTP 200」不等于「全部写入成功」,必须核对 `count`。
 
-### 6.7 打包与分发
+### 6.7 打包与分发(2026-08-05 改为插件,见 §9 决策 7)
 
-目标:Skill 在本仓库调通后,能整体复制给其他项目使用,且不脱离本仓库维护。
+**主路径:Claude Code 插件 + 自建 marketplace。**
 
-**安装方式**——`install.sh` 把自身目录复制到目标位置:
+```
+/plugin marketplace add visuddhinanda/wikipali-plugins
+/plugin install wikipali-write@wikipali
+```
 
-```bash
-# 装到某个项目(项目级,只在该项目激活)
-.claude/skills/wikipali-write/install.sh ~/work/other-project
+桌面版(Claude Desktop 的 **Code** 标签页)点 `+` → Plugins → Add plugin 装同一个 marketplace。注意插件只对本地/SSH 会话生效,Chat 标签页与云会话不加载插件。
 
-# 装到用户级(所有项目可用)
-.claude/skills/wikipali-write/install.sh --user
-```
+**两个仓库的分工**:
 
-行为:`cp -r` 自身到 `<target>/.claude/skills/wikipali-write/` 或 `~/.claude/skills/wikipali-write/`;若目标已存在,比对 `VERSION` 并要求 `--force` 才覆盖。不复制 `install.sh` 以外的任何仓库文件。
+| 仓库 | 内容 | 为什么 |
+|---|---|---|
+| `visuddhinanda/wikipali-plugins` | 只有 `.claude-plugin/marketplace.json` + README,8 KB | `/plugin marketplace add` 会**完整克隆** marketplace 仓库,没有稀疏优化 |
+| `visuddhinanda/mint` | 插件本体 `plugins/wikipali-write/` | 与 API 同仓演进(§6.1)。marketplace 用 `git-subdir` 源指过来,Claude Code **稀疏克隆**只取这一个子目录 |
 
-**版本与漂移**——`VERSION` 用日历版本(如 `2026.08.04`),**在每次 API 契约变更时递增**(§5 的任何一项落地都算)。副本里的 `VERSION` 是判断「这份拷贝是否过期」的唯一依据。
+反过来「mint 自己当 marketplace」是不行的:mint 的 packfile 580 MB、HEAD 快照 212 MB,而 Claude Code 的 git 操作超时是 120 秒,且后台自动更新失败时会整仓重新 clone。
 
-这是 copy-based 分发的固有代价:**副本不会自动更新**。API 一改,各处副本就静默过期,直到用户重新 `install.sh --force`。缓解手段:
+**版本与更新**——版本号只有 `plugin.json` 的 `version` 一处。marketplace 条目里可以再加 `sha` 钉到具体提交,那才是真正的「发版」:用户不会静默拿到 mint 上某个未验证的中间提交。改 API 契约时的动作是:改插件 → 提交 → 推 mint → 更新 marketplace.json 的 `sha`/`version` → 用户 `/plugin update`。
 
-- `wp.py` 启动时调 `GET /v2/auth/current`(或任一轻量端点),若服务端返回的错误形态与 `references/api.md` 记录的不符,提示「Skill 可能已过期,请重新安装」;
-- 不做自动更新——那需要副本知道 mint 仓库的位置,违反 §6.1.1 的自包含约束。
+**`install.sh` 降级为后路**:不走 marketplace 时,它把插件目录整个复制到 `<target>/.claude/skills/wikipali-write/`,因为带 `.claude-plugin/plugin.json` 的目录会被当作 `<name>@skills-dir` 插件就地加载。代价是不会自动更新。
 
-**先后顺序**:先在本仓库把流程跑通(P1 全部完成),再写 `install.sh`。过早打包会把未定型的 API 契约固化到副本里。
+**先后顺序**:先在本仓库把流程跑通(P1 全部完成),再打包。过早分发会把未定型的 API 契约固化到别人机器上——所以**线上四站部署 + 线上复测通过之前,不要把 marketplace 地址给别人**。
 
 2026-08-05 的实际情况:`install.sh` 已写好并验证(装出的副本能独立运行),但**分发要等到服务端部署 + 端到端实测通过之后**。打包机制本身不依赖 API 契约,先写好没有代价;真正会把未定型契约固化出去的是「复制给别的项目」这一步。
 
@@ -605,8 +615,9 @@ Skill 的验证不走 Pest——它是个纯客户端,测的是「对着服务
 | 1 | 模型记录挂个人还是 group studio | **个人 studio** | §5.1 用 `canEdit()`,不引入 `StudioApi::userCanManage`;(a) 的遗留项 2 关闭 |
 | 2 | 是否提供 token 撤销机制 | ~~不做~~ → **2026-08-05 推翻,改为做** | 加 `ai_models.token_version`,模型 token payload 增 `typ`/`ver`,TTL 从 365 天收到 30 天;旧模型 token 全部失效(见 §5.1、§7-2) |
 | 3 | channel uid 如何获取 | **Skill 交互式选择** | 用 `GET /v2/channel?view=user-edit`,见 §6.4 |
-| 4 | Skill 分发形态 | **在本仓库开发,以复制方式分发**;不做独立仓库 | 放仓库根 `.claude/skills/wikipali-write/`,目录自包含、零依赖,可整体复制到其他项目;见 §6.1、§6.7 |
+| 4 | Skill 分发形态 | **在本仓库开发,以复制方式分发**;不做独立仓库 | 放仓库根 `plugins/wikipali-write/`,目录自包含、零依赖,可整体复制到其他项目;见 §6.1、§6.7 |
 | 5 | 多站点(4 个线上 + 开发机)如何处理 | 四个线上地址**共享库与 `jwt_secrets_key`**,凭据只存一份(`online` / `local` 两桶),可任意切换与自动 fallback(2026-08-05 补) | 见 §6.1.2、§6.2。`.org`/`.cc` 是地区,`www`/`next` 是**代码版本**不是数据环境;随之而来的是 API 契约版本差,见 §6.1.2-4 |
 | 6 | 用户怎么切 endpoint | `--api` 一次性覆盖**不写回**;改默认只经 `wp.py endpoint` 子命令;fallback **提示后切换**不静默(2026-08-05 补) | 见 §6.1.2「用户如何切换」。三条都指向同一个原则:当前连的是哪个站点,任何时候都应当是用户明确知道的 |
+| 7 | 怎么发布给别人 | **Claude Code 插件 + 自建 marketplace**:目录文件放独立小仓库 `wikipali-plugins`,插件本体留在 mint,用 `git-subdir` 稀疏克隆(2026-08-05 补,修正决策 4 的「整目录复制」) | 见 §6.7。mint 不能直接当 marketplace——marketplace 是整仓 clone,580 MB 撞 120 秒超时。MCP server 形态排在插件跑通之后 |
 
 决策 2 原本是「不做」,理由是省掉 `token_version` 可以不动 `ai_models` 表结构、不改 `getUserToken` 的 payload。2026-08-05 推翻:趁 Skill 尚未分发、代码尚未部署、外面一份真实凭据都没有的时候补,代价最小;再往后每多一份副本,「已签出 token 全部失效」的破坏面就大一分。

+ 13 - 0
plugins/wikipali-write/.claude-plugin/plugin.json

@@ -0,0 +1,13 @@
+{
+  "name": "wikipali-write",
+  "description": "以 AI 模型身份把句子写入 WikiPali 句子库:登录、模型身份 token、channel 选择、access token、分批写入与 count 核对。",
+  "version": "0.1.0",
+  "author": {
+    "name": "visuddhinanda",
+    "url": "https://github.com/visuddhinanda"
+  },
+  "homepage": "https://github.com/visuddhinanda/mint/tree/development/.claude/skills/wikipali-write",
+  "repository": "https://github.com/visuddhinanda/mint",
+  "license": "MIT",
+  "keywords": ["wikipali", "pali", "translation", "api-client"]
+}

+ 84 - 0
plugins/wikipali-write/README.md

@@ -0,0 +1,84 @@
+# wikipali-write
+
+一个 Claude Code 插件:让 Claude 以 **AI 模型身份**把句子写入 [WikiPali](https://www.wikipali.org) 句子库。
+
+写入的句子 `editor_uid` 记为 AI 模型的 uid 而不是操作者本人,署名与审计因此是准确的——谁翻的就是谁翻的。
+
+## 安装
+
+```
+/plugin marketplace add visuddhinanda/wikipali-plugins
+/plugin install wikipali-write@wikipali
+```
+
+桌面版在 **Code** 标签页里点 `+` → **Plugins** → **Add plugin** 也可以装。
+
+不走 marketplace 的话,克隆本仓库后跑 `plugins/wikipali-write/install.sh --user`。
+
+## 装之前请知道它会做什么
+
+插件能在你的机器上执行代码,装之前你应当知道这一个具体会干什么:
+
+- **读写 `~/.wikipali/credentials.json`**(权限 0600),里面存你的 WikiPali 登录 token、AI 模型身份 token 和 channel access token;
+- **往 wikipali.org 写数据**。写入是覆盖式的:相同位置(book / paragraph / word_start / word_end / channel)的已有句子会被替换。插件在每次写入前会回显目标并要求确认;
+- **只用 Python 标准库**,不装任何依赖,不建虚拟环境。
+
+它**不会**接触你的密码:登录由 `wp_login.py` 完成,密码经 `getpass` 读入内存,不落盘、不进日志、不进对话。这个脚本必须由你本人在真正的终端里执行,Claude 不代跑。
+
+## 用法
+
+装好后直接对 Claude 说「把这些译文写进 WikiPali 的某某 channel」即可,它会自己走完流程。手工调用:
+
+```bash
+python3 <插件目录>/skills/write/scripts/wp.py whoami        # 看当前凭据状态
+python3 <插件目录>/skills/write/scripts/wp_login.py         # 登录(自己跑)
+python3 <插件目录>/skills/write/scripts/wp.py ensure-model --name <模型标识>
+python3 <插件目录>/skills/write/scripts/wp.py channels
+python3 <插件目录>/skills/write/scripts/wp.py write sents.json --channel <uid> --dry-run
+```
+
+句子文件的形状:
+
+```json
+{
+  "channel_uid": "<channel uid>",
+  "sentences": [
+    { "book_id": 1, "paragraph": 10, "word_start": 0, "word_end": 12,
+      "content": "译文", "content_type": "markdown" }
+  ]
+}
+```
+
+## 站点
+
+线上四个地址(`www` / `next` × `.org` / `.cc`)共享同一个数据库和密钥,凭据通用,可随时切换:
+
+```bash
+python3 .../wp.py endpoint          # 列出并标出当前
+python3 .../wp.py endpoint next     # 改默认
+python3 .../wp.py --api next ...    # 只影响这一次调用
+```
+
+`www` 是稳定版、`next` 是最新版**代码**,不是不同的数据环境。较新的端点在稳定版上返回 404,意思是「该站点代码版本还没到」。
+
+## 权限模型
+
+三种 token,职责不混:
+
+| Token | 代表谁 | 有效期 |
+|---|---|---|
+| 用户 token | 人类操作者 | 365 天 |
+| 模型 token | AI 模型身份,写句子时的 `Authorization` | 30 天,可撤销 |
+| access token | 被委托的 channel 编辑权,写句子时的 body 字段 | 7 天 |
+
+模型自身不是任何 channel 的 owner,它的全部写权限来自你签发的 access token,且受 book 范围约束——**你没有编辑权的 channel,签发阶段就会失败**。凭据泄漏时用 `wp.py revoke` 作废该模型已签出的全部 token。
+
+## 开发
+
+本插件在 [visuddhinanda/mint](https://github.com/visuddhinanda/mint) 的 `plugins/wikipali-write/` 下开发,与被调用的 Laravel API(`api-v13/`)同仓演进——API 契约一改,插件在同一个提交里跟上。设计文档在 `docs/wikipali-write-skill-design.md`。
+
+端点细节、返回形状与各处陷阱见 `skills/write/references/api.md`。
+
+## License
+
+MIT

+ 17 - 9
.claude/skills/wikipali-write/install.sh → plugins/wikipali-write/install.sh

@@ -1,12 +1,16 @@
 #!/bin/sh
-# 把本 Skill 整目录复制到目标项目或用户级 skills 目录。
+# 手工安装:把本插件整目录复制到目标项目或用户级 skills 目录。
 #
 #   ./install.sh ~/work/other-project   # 项目级,只在该项目激活
 #   ./install.sh --user                 # 用户级,所有项目可用
 #   ./install.sh --user --force         # 覆盖已存在的旧副本
 #
+# 首选方式是从 marketplace 装(见 README),那样能自动更新。本脚本是给
+# 不走 marketplace 的场景留的后路:复制过去的目录带 .claude-plugin/
+# manifest,会被当作 <name>@skills-dir 插件就地加载。
+#
 # 只复制本目录自身,不碰仓库里的任何其他文件。副本不会自动更新——
-# API 契约一改,旧副本就静默过期,靠 VERSION 判断是否该重装。
+# API 契约一改,旧副本就静默过期,靠 plugin.json 的 version 判断。
 
 set -eu
 
@@ -16,7 +20,7 @@ FORCE=0
 TARGET=""
 
 usage() {
-    sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'
+    sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'
     exit "${1:-1}"
 }
 
@@ -43,7 +47,12 @@ if [ -z "$TARGET" ]; then
 fi
 
 DEST="$TARGET/$NAME"
-VERSION=$(cat "$SRC/VERSION")
+# 版本号只有一处来源:plugin.json。VERSION 文件已废弃,两处版本必然漂移
+read_version() {
+    python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("version","(未标版本)"))' \
+        "$1/.claude-plugin/plugin.json" 2>/dev/null || echo "(读不到 plugin.json)"
+}
+VERSION=$(read_version "$SRC")
 
 if [ "$DEST" = "$SRC" ]; then
     echo "错误:源和目标是同一个目录。" >&2
@@ -51,8 +60,7 @@ if [ "$DEST" = "$SRC" ]; then
 fi
 
 if [ -d "$DEST" ]; then
-    OLD="(无 VERSION 文件)"
-    [ -f "$DEST/VERSION" ] && OLD=$(cat "$DEST/VERSION")
+    OLD=$(read_version "$DEST")
     if [ "$FORCE" -ne 1 ]; then
         echo "目标已存在:$DEST"
         echo "  已装版本:$OLD"
@@ -66,10 +74,10 @@ fi
 
 mkdir -p "$TARGET"
 cp -R "$SRC" "$DEST"
-rm -rf "$DEST/__pycache__" "$DEST/scripts/__pycache__"
-chmod +x "$DEST/scripts/wp.py" "$DEST/scripts/wp_login.py" "$DEST/install.sh"
+find "$DEST" -name __pycache__ -type d -exec rm -rf {} + 2>/dev/null || true
+chmod +x "$DEST/skills/write/scripts/wp.py" "$DEST/skills/write/scripts/wp_login.py" "$DEST/install.sh"
 
 echo "已安装 $NAME $VERSION 到 $DEST"
 echo
 echo "下一步(凭据在 ~/.wikipali/,多个副本共用,通常不必重新登录):"
-echo "  python3 $DEST/scripts/wp.py whoami"
+echo "  python3 $DEST/skills/write/scripts/wp.py whoami"

+ 9 - 4
.claude/skills/wikipali-write/SKILL.md → plugins/wikipali-write/skills/write/SKILL.md

@@ -14,12 +14,17 @@ metadata:
 - `scripts/wp_login.py` —— 唯一接触密码的脚本,**必须由用户本人执行**
 - `scripts/wp.py` —— 其余全部操作
 
-下面的命令都假设 cwd 在本 Skill 目录(`.claude/skills/wikipali-write/`)。
+下面的命令都写成 `python3 scripts/wp.py …`,其中 `scripts/` 是**本 SKILL.md 同目录下的** `scripts/`。执行前先把它换成实际路径:
+
+- 作为插件安装时:`${CLAUDE_PLUGIN_ROOT}/skills/write/scripts/wp.py`
+- 直接放在项目里时:`<skill 目录>/scripts/wp.py`
+
+拿不准就先 `ls` 确认,不要凭猜测拼路径。
 
 ## 铁律
 
 1. **永远不要向用户索要密码,也不要代跑 `wp_login.py`。** 需要登录时,请用户**另开一个真正的终端**执行
-   `python3 .claude/skills/wikipali-write/scripts/wp_login.py`。
+   `python3 <上面那个 scripts 路径>/wp_login.py`(把完整路径写给他们,别让他们自己找)。
    不要让他们用 Claude Code 的 `!` 前缀——那里没有交互式终端,密码提示无处输入;也不要建议把密码放进命令行参数或直接打在对话里。
 2. **写入前必须让用户确认。** `wp.py write` 默认会回显目标并等确认;只有用户已经明确同意本次写入时,才可以加 `-y`。
 3. **绝不打印 token 全文**(`~/.wikipali/credentials.json` 里的任何值)。脚本自己会打码,不要 `cat` 那个文件。
@@ -36,7 +41,7 @@ python3 scripts/wp.py whoami          # 先看缺什么
 
 ```bash
 # 1) 登录(用户自己在另一个终端里跑,不要用 ! 前缀,也不要代跑)
-python3 .claude/skills/wikipali-write/scripts/wp_login.py
+python3 scripts/wp_login.py
 
 # 2) 建立模型身份并取 token;--name 必须是你自己的模型标识
 python3 scripts/wp.py ensure-model --name claude-opus-5
@@ -102,4 +107,4 @@ python3 scripts/wp.py revoke
 
 ## 更多
 
-端点字段、返回形状与各处陷阱见 `references/api.md`。若脚本行为与该文件对不上,多半是这份 Skill 副本过期了(比对 `VERSION`),请重新安装。
+端点字段、返回形状与各处陷阱见 `references/api.md`。若脚本行为与该文件对不上,多半是这份副本过期了——插件用户跑 `/plugin update`,手工安装的用户重新装一遍。

+ 0 - 0
.claude/skills/wikipali-write/references/api.md → plugins/wikipali-write/skills/write/references/api.md


+ 0 - 0
.claude/skills/wikipali-write/scripts/wp.py → plugins/wikipali-write/skills/write/scripts/wp.py


+ 0 - 0
.claude/skills/wikipali-write/scripts/wp_login.py → plugins/wikipali-write/skills/write/scripts/wp_login.py