Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .claude/skills/create-plugin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ description: 创建 Covel 插件。通过对话了解需求,直接生成 PLUGI
- **同步 / 后台执行**(仅手动触发):`execution: sync`(默认,阻塞 turn)/ `background`(202 + `jobId`,框架在 `_jobs/<jobId>` 写状态,前端通过 `plugin-data.changed` SSE 感知)。
- 插件**禁止**主动写 `_jobs/*` / `_logs/*`,框架会覆盖。
- **事件链**:runtime 在返回里带 `events: [{topic, data}]`,下游 `trigger: {type: event, topic}` runtime 在同 turn 被拉起。
- **事件契约声明(统一事件层)**:消费方在 frontmatter 用 `events: [{topic, schema, description, advertise?}]` 声明契约(`schema` 为插件根相对 JSON Schema 路径,校验事件 payload;`advertise: false` = 仅插件内部信令,agent 不可发射)。发射方 agent 声明 `advertiseEvents: true` + `tools.builtin: [emit-event]`,prompt 会自动收到当前 session 所有已声明事件的目录,LLM 命中时调 `emit-event`(同 topic 每回合去重)。参考实现:`plugins/scene-stage/runtimes/resolver/PLUGIN.md`(`scene.set` 消费方)。
- **事件契约声明(统一事件层)**:消费方在 frontmatter 用 `events: [{topic, schema, description, advertise?}]` 声明契约(`schema` 为插件根相对 JSON Schema 路径,校验事件 payload;`advertise: false` = 仅插件内部信令,agent 不可发射)。发射方 agent 声明 `advertiseEvents: true` + `tools.builtin: [emit-event]`,prompt 会自动收到当前 session 所有已声明事件的目录,LLM 命中时调 `emit-event`(同 topic 每回合去重)。参考实现:`plugins/scene-stage/runtimes/resolver/PLUGIN.md`(`scene.set` 消费方)。**去重的设计后果**:预期一回合内多次回执的契约(逐次判定、逐条通知)必须把 payload 批量化成数组、整回合一次发射——逐次 emit 从第二次起会被静默丢弃,这是契约设计问题而非发射方 prompt 问题。批量范例:`plugins/dice-check`(`check.resolved` 的 `{ checks: [1..3] }`)。
- **`requireToolUse: true`**(仅 agent):唯一职责就是调某个工具的 runtime 容易漂移成续写正文——开启后零成功工具调用即收场时框架注入一条纠正消息重试一次(如 `scene-prompts`)。
- **存储**:runtime 返回里带 `pluginData: [{namespace, key, value}, ...]`,框架自动转成 `plugin.data` / `plugin.data.batch` Proposal,写到 `plugin_data` 表 `(sessionId, pluginId, namespace, key)`。也可以用 `ctx.pluginData.set(...)` 立即落库(前端立刻通过 SSE 看到),适合 placeholder。
- **多媒体(图像 / 音频 / 视频 / 文件)**:用 `ctx.media`(不是 `pluginData` 直接塞 bytes)。`ctx.media.put(bytes, mime, meta) → MediaRef`;`ctx.media.ingestUrl(url, {allowedMimes})` 从 URL 拉取到 MediaStore。把 ref 写进 `pluginData.value.ref`,并在 runtime output 返回 `assetGenerations: [{ref, modality, meta}]` 让框架 emit `asset.generate` proposal(`assets` 仍是兼容 alias)。前端用 `<Media as="auto" ref={…}>` 渲染(自动按 mime 选 `<img>/<audio>/<video>/<a>` 控件)。完整契约见 [`runtime-context.md`](references/runtime-context.md) §`ctx.media`。
Expand Down Expand Up @@ -193,6 +193,15 @@ pnpm --filter @covel/plugin-<id> test

> 仓库外插件(`~/.covel/plugins/`)优先用仓库根目录的 `pnpm test:runtime -- <plugin> --plugins-dir ~/.covel/plugins --pretty` 跑插件自带 `tests/runtime-cases.json`。只有要写独立 Vitest 单测时,才在插件目录补测试依赖。

### Bundled 插件(写进仓库 `plugins/`)的额外集成门禁

第三方插件到 step 5 即完成;bundled 插件还有四道,缺一即是不完整交付:

1. **先 `pnpm install`**——新 workspace 包未链接依赖前 `pnpm --filter` 测试跑不动。
2. **跑 golden 契约测试**:bundled 集合有仓库级 golden,新增/改动插件后必须 `pnpm --filter @covel/runtime test` + `pnpm --filter @covel/server test` 并更新失败的 golden。常见三处:`packages/runtime/tests/core-plugin-manifest-contract.test.ts`(narrator inject 清单等)、`packages/runtime/tests/normalize-observe-golden.test.ts`(post-turn 并行 runtime 清单)、`apps/server/tests/lib/world-data-session-import.test.ts`(bundled dataSchemas Ajv 清单)。`pnpm lint` 只有 tsc,抓不到 golden。
3. **`pnpm check:plugins` 必须过**:每个插件目录要 `README.md`;`displayName`/`description` zh+en;UI json / PLUGIN.md / handler 的玩家可见文案不得裸中文(I18nText)。
4. **文档同步(CLAUDE.md 规则,同 PR)**:`docs/reference/plugins.md`(目录 + 概览表 + 详细章节)、`tools.md`(本地工具概览行)、`ui-panels.md`(面板/消息块登记表)、`world-data.md`(若声明 acceptsWorldData namespace)。

### 6. 展示结果

给用户摘要:插件名 / runtime 列表(及各自 stage、依赖、触发方式、类型)/ 使用的工具 / UI 入口 / 玩家可调设置 / slot 依赖(如 `covel.image`)。问是否需要调整。
Expand Down
18 changes: 11 additions & 7 deletions .claude/skills/create-world/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ lore 解析链是 **`WORLD.<lang>.md` → `WORLD.md` → 空字符串**。`WORLD
- `openingScenario` 必须呈现即时的选择或紧张感
- 按玩法选 `pluginPolicy.preset`:传统叙事 `traditional-story`,对话/校园/群像 `dialogue-mode`,省 token `low-cost`
- **视觉小说世界**(对话模式的增强档):声明 `defaultViewMode: stage` 进全屏舞台(背景 + 立绘 + 打字机)。资产是**渐进增强**——没有立绘/场景图也能跑(回退世界头图 + 占位卡),后续可用 `scripts/generate-portraits.mjs` / `generate-scenes.mjs` 补。成品参考 `worlds/haruka-academy`
- **RPG 世界**(判定/任务/背包/好感玩法):`pluginPolicy.requiredPlugins` 拉起 `dice-check`、`core-quest`、`inventory`、`affinity` 四件套;worldData 预置三类种子——`plugin://core-quest/quests`(任务)、`plugin://inventory/items`(开局物资,货币 tag `currency`)、`plugin://affinity/affinity`(关键 NPC 初始好感),记录形状见 `docs/reference/world-data.md`「内置 RPG 玩法种子」;`characterAttributes` 声明 0-5 小整数属性作判定修正来源(描述里写明各自管哪类判定)。种子的 NPC/giver 必须与 lore 和角色蓝图同名对齐。成品参考 `worlds/emberback`
- **写任何插件 ID 之前先 `ls plugins/` 确认它存在**——schema 不校验插件 ID,拼错要拖到建会话时才暴露
- 避免泛化的奇幻套路,追求独特的世界设定
- 所有 ID 字段(world id、faction id、worldData source id)用 kebab-case 英文;其余内容用用户的语言(默认中文)
- 所有 ID 字段(world id、faction id、worldData source id)用 kebab-case 英文
- **world.yaml 的展示字段必须写 I18nText 双语对象**——`name`、`summary`、`characterAttributes[].name`/`.description` 等写 `{ zh: …, en: … }`。schema 虽接受裸 string,但仓库门禁 `check-plugin-i18n` 会扫 `worlds/*/world.yaml`,裸中文直接判违规。WORLD.md、lore、`data/` 种子内容用用户的语言即可(默认中文)

### 3. 验证

Expand All @@ -66,12 +68,14 @@ console.log('schema OK');

按需追加:

| 你写了什么 | 至少要跑哪几层 |
| -------------------------- | ------------------------------------------ |
| 最小 world.yaml + WORLD.md | **L1 schema**(必做) |
| 声明了 `worldData` | + **L1b descriptor 校验** |
| factions 含 `relations[]` | + **L2 引用一致性** |
| 准备对外发布 | + **L3 lore 覆盖度** + **L4 真实跑一回合** |
| 你写了什么 | 至少要跑哪几层 |
| -------------------------- | ------------------------------------------------------------------------------------------------------ |
| 最小 world.yaml + WORLD.md | **L1 schema**(必做) |
| 声明了 `worldData` | + **L1b descriptor 校验** |
| 预置了 `plugin:*` 种子 | + **Ajv 逐条校验**:用目标插件的 `plugins/<id>/schemas/*.json` 校验每条种子记录(L1b 只验 descriptor 形状不验记录;不提前挡,建会话 preflight 才会炸) |
| factions 含 `relations[]` | + **L2 引用一致性** |
| 世界写进仓库 `worlds/` | + `node scripts/check-plugin-i18n.mjs`(world.yaml 展示字段 I18nText 门禁) |
| 准备对外发布 | + **L3 lore 覆盖度** + **L4 真实跑一回合** |

校验失败则修复后重新写入。L1b/L2/L3/L4 的现成脚本见 `references/world-validation.md`(必读)。

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/create-world/references/example-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pluginPolicy:
preferTags: [mode:traditional-story, role:codex, role:world-rules]
avoidTags: [mode:dialogue]
recommendedPlugins:
- player-identity
- affinity

dimensions:
geography:
Expand Down
4 changes: 3 additions & 1 deletion .claude/skills/create-world/references/world-yaml-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Schema 使用 Zod strict 模式验证,不允许未定义字段。所有文本字段支持 I18nText(`string` 或 `Record<string, string>`)。

> **门禁注意**:I18nText 虽接受裸 `string`,但仓库的 `check-plugin-i18n` 门禁要求 `worlds/*/world.yaml` 的展示字段(`name`/`summary`/`characterAttributes[].name`/`.description` 等)必须是 `{ zh: …, en: … }` 对象——裸中文会挂 `pnpm check:plugins`。

## 根字段

| 字段 | 类型 | 必需 | 约束 |
Expand Down Expand Up @@ -79,7 +81,7 @@ pluginPolicy:
preset: traditional-story
preferTags: [mode:traditional-story, role:codex, role:retrieval, role:world-rules]
avoidTags: [mode:dialogue]
recommendedPlugins: [player-identity] # preset 之外额外想要的
recommendedPlugins: [affinity] # preset 之外额外想要的
```

对话 / 视觉小说模式(参考 `worlds/haruka-academy`):
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**English** · [简体中文](./README.zh-CN.md)

[![Version](https://img.shields.io/badge/version-v0.0.23-8b5cf6)](https://github.com/ackness/covel/releases/tag/v0.0.23)
[![Version](https://img.shields.io/badge/version-v0.0.24-8b5cf6)](https://github.com/ackness/covel/releases/tag/v0.0.24)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Stage](https://img.shields.io/badge/stage-early--access-orange)](<>)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ackness/covel)
Expand All @@ -13,14 +13,15 @@

Covel is an AI RPG where the world keeps running between your turns: NPCs track how they feel about you, lore accumulates as you play, and memory carries the thread across the session. Every mechanic behind that is an **autonomous agent shipped as a plugin** — disable one, swap one, or write your own.

> **Current public release: v0.0.23**, early access — APIs, data formats, and plugin frontmatter may change between versions. Prebuilt binaries target macOS Apple Silicon and Windows x64; other platforms build from source.
> **Current public release: v0.0.24**, early access — APIs, data formats, and plugin frontmatter may change between versions. Prebuilt binaries target macOS Apple Silicon and Windows x64; other platforms build from source.

## Highlights

- 🎭 **Stage mode** — a full-screen visual novel: scene backdrops, character sprites, typewriter dialog, and choice overlays. Backdrops for brand-new locations are generated on demand, mid-session.
- 🤖 **Multi-agent turns** — while the narrator writes the scene, other agents extract NPC relationships, grow the world codex, pick the on-stage cast, and maintain long-range memory — in parallel, every turn.
- 🧩 **Everything is a plugin** — 23 bundled agents. A plugin is a `PLUGIN.md`: YAML frontmatter for triggers/tools/events, markdown body as the agent's prompt.
- 🌍 **Two flagship worlds** — a dark-fantasy mystery and a visual-novel school romance, both hand-built and ready to fork.
- 🎲 **RPG mechanics built in** — pre-rolled dice checks with visible receipts, an auto-tracked quest log, a player-managed inventory, and per-NPC affinity meters. All optional plugins; worlds can seed quests, gear, and starting affinity.
- 🧩 **Everything is a plugin** — 26 bundled agents. A plugin is a `PLUGIN.md`: YAML frontmatter for triggers/tools/events, markdown body as the agent's prompt.
- 🌍 **Three flagship worlds** — a dark-fantasy mystery, a visual-novel school romance, and a post-apocalyptic RPG expedition, all hand-built and ready to fork.
- 🔌 **Bring your own model** — OpenAI / Anthropic / DeepSeek / Qwen model slots. Local-first: SQLite on disk, API keys never persisted server-side.

## Two ways to play
Expand Down Expand Up @@ -63,6 +64,7 @@ Open <http://localhost:5173> — debug tooling lives at `/debug`. PostgreSQL, in

- **Mistport Chronicles** (雾港·裂潮纪) — dark-fantasy mystery in traditional-story mode. A fog-shrouded port where every ebb bares different ruins; a guildmaster vanishes and four powers race for a key to what sleeps in the deep. Bilingual, with a seed cast and investigation-flavored memory.
- **Haruka Academy** (遥风学园) — school romance in stage mode. Clubs, exams, rumors, and quiet crushes at a seaside high school, told through a cast of eight — portraits and scene art included.
- **Emberback** (鳌背孤城·烬海纪) — post-apocalyptic RPG expedition in traditional-story mode. The last great turtle carries the last city across an ember sea — and halts, without warning, above an uncharted sunken spire. The showcase for the RPG suite: seeded quests, starting gear, NPC affinity, and five check attributes wired into the dice system, with a portrait-backed main cast.

## Create your own

Expand Down
10 changes: 6 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[English](./README.md) · **简体中文**

[![Version](https://img.shields.io/badge/version-v0.0.23-8b5cf6)](https://github.com/ackness/covel/releases/tag/v0.0.23)
[![Version](https://img.shields.io/badge/version-v0.0.24-8b5cf6)](https://github.com/ackness/covel/releases/tag/v0.0.24)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Stage](https://img.shields.io/badge/stage-early--access-orange)](<>)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ackness/covel)
Expand All @@ -13,14 +13,15 @@

Covel 是一款 AI 驱动的 RPG,回合之间世界仍在运转:NPC 记录着对你的态度、世界典籍随游玩积累、记忆贯穿整局。支撑这一切的每个机制都是一个**以插件形式分发的自主 agent** —— 禁用一个、替换一个,或者自己写一个。

> **当前公开版本:v0.0.23**,早期阶段 —— API、数据格式、插件 frontmatter 可能随版本变化。官方预编译包面向 macOS Apple Silicon 与 Windows x64,其余平台从源码构建。
> **当前公开版本:v0.0.24**,早期阶段 —— API、数据格式、插件 frontmatter 可能随版本变化。官方预编译包面向 macOS Apple Silicon 与 Windows x64,其余平台从源码构建。

## 亮点

- 🎭 **舞台模式** —— 全屏视觉小说:场景背景、角色立绘、打字机对话框与选择肢浮层。走到没画过的新地点时,背景图会在会话中按需生成。
- 🤖 **多 agent 回合** —— 叙事 agent 写场景的同时,其他 agent 并行抽取 NPC 关系、扩写世界典籍、决定在场角色、维护长程记忆 —— 每一回合都是如此。
- 🧩 **一切皆插件** —— 内置 23 个 agent。一个插件就是一份 `PLUGIN.md`:YAML frontmatter 声明触发/工具/事件,Markdown 正文就是 agent 的提示词。
- 🌍 **两个旗舰世界** —— 一个黑暗奇幻悬疑、一个视觉小说校园恋爱,都是精心手作、可直接 fork 的范例。
- 🎲 **内置 RPG 玩法** —— 预掷骰判定(可视化回执)、自动跟踪的任务日志、玩家可直接操作的行囊、逐 NPC 好感度。全部是可选插件;世界包可以预置任务、开局装备与初始好感。
- 🧩 **一切皆插件** —— 内置 26 个 agent。一个插件就是一份 `PLUGIN.md`:YAML frontmatter 声明触发/工具/事件,Markdown 正文就是 agent 的提示词。
- 🌍 **三个旗舰世界** —— 黑暗奇幻悬疑、视觉小说校园恋爱、末世 RPG 远征,都是精心手作、可直接 fork 的范例。
- 🔌 **自带模型** —— OpenAI / Anthropic / DeepSeek / Qwen 模型槽位。本地优先:SQLite 落盘,API 密钥绝不在服务端持久化。

## 两种玩法
Expand Down Expand Up @@ -63,6 +64,7 @@ pnpm dev # web :5173 + server :3001(SQLite)

- **雾港·裂潮纪(Mistport Chronicles)** —— 传统叙事模式,黑暗奇幻悬疑。被浓雾包裹的港口,每次退潮都露出不同的遗迹;公会长失踪,四方势力争夺一把通往深处之物的钥匙。中英双语,内置种子角色与调查向记忆维度。
- **遥风学园(Haruka Academy)** —— 舞台模式(stage mode),校园恋爱日常。海边高中的社团、考试、传闻与未说出口的喜欢,由八名角色的群像展开 —— 立绘与场景美术开箱即含。
- **鳌背孤城·烬海纪(Emberback)** —— 传统叙事模式,末世 RPG 远征。世上最后一头巨鳌驮着最后一座城横渡烬海——破晓时分,它毫无征兆地停在一座图上没有的沉城尖塔之上。RPG 玩法套件的展示世界:预置任务、开局装备、NPC 初始好感与接入骰子系统的五项判定属性,主要角色配有立绘。

## 创造你自己的

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@covel/desktop",
"version": "0.0.23",
"version": "0.0.24",
"private": true,
"type": "module",
"main": "dist/main.mjs",
Expand Down
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@covel/server",
"version": "0.0.23",
"version": "0.0.24",
"private": true,
"type": "module",
"files": [
Expand Down
Loading