Skip to content

dsh-cardforge

Verified

dsh-cardforge · v0.31.0 · MIT · Web UI

ElecKoi 制卡工具(forge):教 AI 做卡的命令行工具,外加一个让 ElecKoi 里的 AI 直接跑它的宿主插件。

Install

dsh plugin add dsh-cardforge

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

制卡工具(forge)

给 ElecKoi(PC + Android)做的一套制卡工具: 从零开一张卡、改现有卡、校验、挂模块、换皮肤、组装卡内界面、打包、和酒馆(SillyTavern)来回搬。

三条约定:

  • spec.yaml 是唯一真相,card.json 是派生物。只手改 spec。
  • 内容住 source/,界面住 hud/,改哪层动哪层。
  • 颜色住 hud/主题.css,版式住 hud/hud.css,换皮肤碰不到机制,改机制不会把配色弄丢。

依赖只有一条:PyYAML,其余全是标准库(Python ≥ 3.9)。

装法

这是个文件夹工具:forge.py、forge/、assets/、samples/、向导/ 要待在一起。

python forge.py <命令>      # ① 直接跑(最省事)

python -m forge <命令>      # ② 当模块跑(等同 ①,给不想记文件名的人)

pip install -e .            # ③ 装成 `forge` 命令(推荐)
forge <命令>

为什么必须 -e:仓库里 CLI 的实体是根目录的 forge.py,而 forge/ 是它的内核包 —— 两个同名。普通 pip install . 只装走包、装不走 forge.py,forge 命令就找不到它; -e 把整个文件夹留在原地,命令和资源(assets/、samples/、向导/)都还在。 也正因为同名,import forge 拿到的是内核包 —— forge/cli.py 只做一件事:找到仓库根,把 forge.py 当脚本跑起来。

给别人怎么用(四种交法,别搞混)

预设和角色卡只认本机文件(安装来源只有「内建」和「用户导入」两种,没有网络装载通道)。 插件不一样 —— 包名、Git 地址、压缩包、本地绝对路径四种写法都吃;这个仓库本身就是一只能贴链接安装的插件(根目录的 package.json + cordis.patch.yml)。

对方想要什么 给他什么 他怎么用
做卡时被 AI 陪着 向导/制卡助手/成品/制卡助手.preset.png ElecKoi →「预设管理器」→「导入预设」→ 分类必须选「创作预设」(选错会提示「这不是创作预设。」)
在对话里问「怎么建卡」 向导/制卡助手/成品/制卡助手.card.png 当普通角色卡导入(手机端「导入角色」也认这张)
自己要做卡 仓库链接或出厂 zip 解压后 python forge.py plan 开工;也可以 pip install -e .
想常驻 ElecKoi 界面 插件名 dsh-cardforge 侧栏「插件」→「添加插件」→ 贴包名 dsh-cardforge → 安装 → 重启 ElecKoi(刚发版那几分钟镜像源可能还没抓到,等一会儿或把安装源切成官方源)

四条容易踩的:

  • 装完必须重启 ElecKoi(插件只在宿主启动时载入,不重启不生效)。装插件顺带把预设也装好了: 插件看有没有「制卡助手」这个创作预设,没有就自动导入,有就一个字不写;只有当前创作预设 还是官方默认那个时才切过去 —— 你自己挑过的一律不动。关掉:installPreset / activatePreset。 预设存在 ElecKoi 自己的数据里,卸载插件不会带走它(不自动删是因为你可能改过它)。
  • 「找不到 Python」不用配。 配置里 python 留空=自动找:先看 FORGE_PYTHON,再挨个试常见位置 与 PATH,真跑一次 import yaml,谁能跑通用谁 —— Windows 上「装了 Python」不等于它在 PATH 上。 都找不到会告诉你它找过哪些地方;机器上有好几个 Python 挑错了,才需要写死完整路径。
  • 卡做在哪个目录也不用每次说。 cwd 留空=<你的用户目录>\Documents\ElecKoi卡库(没有就自动建); 调用时给了 cwd 就听参数的(参数 > 配置 > 默认)。
  • 点安装报「连接 GitHub 超时」=这台机器到 github.com 不通。 预检只有 5 秒(git ls-remote), 直连实测 21 秒都连不上。① 给 git 配代理: git config --global http.https://github.com/.proxy http://127.0.0.1:你的代理端口; ② 或点「试试其他方式」,改成贴本机插件目录的绝对路径(出厂包里 插件\制卡工坊 那个目录),完全不碰网络。

插件细说看三份文档:文档/插件-DSH.md(它是什么形式的插件 · 怎么装 · 五个配置键 · 怎么用 · 在纯 DSH 里怎么活)· 文档/插件-ElecKoi.md(装进电子爱多出来的那件事:自动装创作预设 · 验收三条 · 手动兜底两条路)· 文档/装完怎么验.md(装完照着点、一条条打勾的验收清单 + 一份「症状 → 多半是因为 → 怎么办」对照表)。

那两个 PNG 是可直接分发的单文件,对方不需要装 Python、不需要克隆仓库。要改内容再重新导出:

python forge.py coach                      # 重新生成规程 + 向导卡源
python forge.py preset 向导/制卡助手/source/制卡助手.spec.yaml --png
python forge.py png    向导/制卡助手/source/制卡助手.spec.yaml

三分钟上手

python forge.py plan                                   # ① 开工先落一份规划书(九节空模板 —— 填满再动手)
python forge.py new   我的世界                        # 开一张新卡(内容层 + 界面层一起生成)
python forge.py check 我的世界                         # 体检(含界面规矩、皮肤可读性)
python forge.py hud   我的世界 --写回                   # 改过 hud/ 之后重新组装界面
python forge.py build 我的世界/source/我的世界.spec.yaml 我的世界/成品/我的世界.card.json

python forge.py new   我的世界 --模块 骰子,备份        # 顺手接两块现成机制(条目+界面一起接)
python forge.py drop  备份 我的世界/source/我的世界.spec.yaml   # 后悔了就撤,接之前的样子还在账本里
python forge.py theme 墨夜 我的世界                     # 换皮肤(换完记得再 hud 一次)
python forge.py theme --巡览 我的世界                   # 五套皮肤摆一起对比
python forge.py bundle 我的世界                        # 打成一个工程包(分发 / 备份)
python forge.py import 我的世界.工程包.zip --到 同事的卡   # 收包:落到本地,成一张能接着改的工程
python forge.py new    深夜便利店 --从 同事的卡.card.json   # 接手现成的卡:界面层照抄骨架,内容层用它的
python forge.py theme  --导出 我的世界                   # 把这张卡调好的配色导成一份皮肤文件(可发人)
python forge.py png    我的世界/成品/我的世界.card.json  # 出卡图(.card.png,ElecKoi 手机端「导入角色」认的就是它)
python forge.py preset 我的世界/成品/我的世界.card.json --png  # 出「创作预设」(.preset.json / .preset.png)
python forge.py lint   我的世界/成品/我的世界.card.json  # 正文体检(防八股):只查句式骨架,不查词
python forge.py lint   -                               # 刚写完一轮?直接从标准输入丢进去查

往卡里加东西(以前只能改已有的,现在能从无到有):

python forge.py entry 我的世界 --新 "夜里的酒馆" --模板 关键词 --关键词 酒馆,打听
python forge.py entry 我的世界 --列                     # 看看条目长什么样
python forge.py entry 我的世界 --看 "核心 · 世界总纲"     # 读一条的全貌:正文全文 / 关键词 / 提示 / 触发…
python forge.py entry 我的世界 --改 "核心 · 世界总纲" --正文@世界总纲.txt   # 长正文写成文件再 @ 引
python forge.py group 我的世界 --新 支线                # 分个组
python forge.py var   我的世界 --新 玩家.声望 --类型 number --初值 0   # 变量三处一次写齐
python forge.py regex 我的世界 --新 "清理变量" --模式 '…' --替换 '…'   # 加正则(Git Bash 里先 export MSYS_NO_PATHCONV=1)
python forge.py opener 我的世界 --正文@开场白.txt       # 换开场白(末尾那三个零宽标记工具负责)
python forge.py opener 别人给的卡 --正文@开场白.txt      # 只有内容层的工程也认
python forge.py trash 我的世界                          # 删错了?都在这儿,--还原 1 放回最近一条

看全部命令:python forge.py

在「不许删、不许改名」的环境里跑(比如 ElecKoi 里的 AI 直接跑命令):任何命令后面加 --沙箱, 或先 export FORGE_SANDBOX=1。forge 会切成「只创建、只覆盖」—— 落盘直接覆盖目标、回收站改用墓碑、 撤皮肤写空壳。代价是丢掉原子落盘,能不用就不用(自检第 42 项盯着这条)。

建议用一个装好 PyYAML 的独立 Python 环境(venv / conda 都行),别拿系统 Python 直接跑。

制卡向导(coach)—— 教 AI 怎么做卡

上面那些命令是给你用的。想让 AI 替你开新卡,缺的不是命令,是一份 AI 读得懂的制卡规程:

python forge.py coach            # 默认出到 ./向导/(--到 <目录> 换地方)

一份内容,两个出口:

产出 给谁 怎么用
向导/AI制卡规程.md 电脑上的 AI(Claude Code / Codex / Cursor 这类) 丢进对话,说「照这个给我做一张卡」
向导/SKILL.md 同上,带 frontmatter 拷进技能目录当技能用(如 ~/.claude/skills/eleckoi-card-maker/);规程更新后不用重装
向导/制卡助手/成品/制卡助手.card.json ElecKoi 里的 AI(陪你聊天那个) 导入挂上:它变成制卡师,在对话里一层层访谈你

两份出口共用同一套方法论九篇(机制 / 条目触发 / 预算 / 世界 / 故事 / 角色 / 写法 / 变量 / 界面)+ 一章红线(会被 ElecKoi 静默吃掉的写法)。

不想一次读完整份规程(一万七千字,塞进上下文既贵又冲淡重点):guide 先出目录,再按需取一段 ——

python forge.py guide                 # 目录:有哪些节、哪些篇、哪几篇必读、有哪几段「对话」
python forge.py guide --节 三          # 一节(一 开工流程 / 六 验收标准 / 九 红线 …)
python forge.py guide --篇 预算        # 一篇方法论 · `--对话 访谈` 一段在对话里做卡 · `--全文` 整份

认不出的名字当场报错并列出全部候选,撞名的报「不止一个」让你写全,绝不静默挑一个。 装在 ElecKoi 里的插件就是靠这条自己把知识取到手,不必等人把规程贴进对话。

向导卡自己就是范例:由 coach 生成,所以它自己也体检 0 错、常驻字数守在预算内。

一次落成(make)—— 想清楚了就别来回跑

new → group → entry × N → var → regex → opener → build → check 是七八次进程启动。 人用不累,AI 在对话里用就贵:每次调用都是一整轮。所以想清楚之后,一次落成:

python forge.py make --示例        # 先打出一张真能开的完整卡稿(长夜便利店)—— 照着改
python forge.py make --模板        # 或者只要骨架:有哪些键、怎么缩进
python forge.py make 我的卡.yaml   # 一趟:建工程+写条目+加变量正则+换开场白+出成品+体检

--模板 只回答「有哪些键」,--示例 连「话该写多细、哪句放常驻、哪句放关键词」一起示范 —— AI 最容易失手的不是键名,是「写多细算够」,一份好样板比十条规则管用。

稿子写的是这张卡该长什么样,不是「先做这步再做那步」:

  • 条目按标题认人 —— 卡里已有同名的就改它,没有才新建。想清掉骨架自带、稿里没提到的条目, 写一行 清空条目: true;想删掉某一条,写 删: true。
  • 给已有条目写 模板: / 分组: 也真生效(换档与挪组都会落下去);分组 / 变量对象缺了自动补。
  • 全程在临时目录里做,全绿了才整棵拷到目标位置 —— 稿子有毛病时目标位置一个字都不会被写, 静态能查的(模板名 / 类型 / 值域会不会被静默降级)一次报全。
  • --试 只验证不出;--覆盖 才允许把已有同名工程照稿重落。加 --沙箱 照样一趟落成。

规程是「走过一遍」的,不是「想出来」的。 它被拿去做过五轮盲跑:不看内部代码的 AI 只拿规程 照章施工,卡在哪就记下来。第一轮「从零建卡」修回 15 处,第二轮「接手别人给的卡」挖出 10 处, 第三轮补上了缺的搬运命令(new <卡名> --从 <卡>),第五轮端到端做出 11 条目 / 14 变量 / 6 对象、check 0 错。

图形工作台(已迁出)

原来那个「开在浏览器里给人点的界面」连代码一起搬到了 弃用/工作台/(2026-10-10,v0.27.0)。 主线只有两条:AI 照着规程敲命令,和给 ElecKoi 里的 AI 一个工具。 自检里原本盯着它的第 21 / 39 / 40 项一并移除,编号保留空位、不重排。

路线图

是什么 状态
A · 离线工程链(就是本工具) forge.py 命令 + 规程,AI 照着跑,最后交出卡 已可用
B · 卡内 HUD 卡自带的页面内界面(面板 / 悬浮球 / 建卡器),靠一条正则送进宿主页 骨架与模块齐了,具体卡自己长
C · ElecKoi 原生插件 在 ElecKoi 里给 AI 一个 cardforge 工具,直接跑 forge.py 已发版:插件/制卡工坊/,装法见上表第 4 行

为什么插件比「让 AI 用自带命令行工具」强:ElecKoi 自己的命令行工具跑在受限令牌沙箱里 (读得到、写得进,但不许删、不许改名,WinError 5),而 forge 每次落盘都靠改名(os.replace / os.rename / 回收站是「移动」)。宿主插件起的子进程不受那层沙箱管 —— 原子落盘、改名、回收站全都回来了。 一句话分工:能用插件就用插件(全功能);只能用命令行工具就加 --沙箱(丢掉原子落盘,但能干活)。

三十三个命令

分类 命令
开卡 plan 规划书(不填参数=九节空模板 · plan <卡> 从现成卡反推现状 · plan --查 规划书.md 体检:哪节还空着、谁没写弱点,当场说)· make <卡稿.yaml> 照一份卡稿一次落成整张卡(--示例 出一张真能开的完整卡稿 / --模板 出骨架 / --试 只验证不出 / --覆盖 重落)· new 开新卡(可 --模块 骰子,备份)· new <卡名> --从 <卡.json / 包.zip / spec.yaml / 工程目录> 接手现成的卡(界面层照抄骨架、内容层用它、名字归新卡)· name 改卡名(spec 与界面配置两处一起改)· mods [--工程 X] 看有哪些模块 / 这张卡接没接 · add 接模块 · drop 撤模块
内容 entry 条目(列 / 看 / 新 / 改 / 复制 / 删 / 挪组 —— 一串名字就是批量的:复制·删·挪·启停,--改 一串=批量改同一组字段)· group 分组(列 / 新 / 改名 / 删)· var 变量(列 / 新 / 删(一串=批量删,三处一起撤) / 新对象 / 改名(三处一起挪) / 换对象(挂靠+初值+Zod 一起搬) / 对齐 Zod)· regex 正则(列 / 新 / 删 / 启停)· opener 开场白(看 / 正文 / 补齐 / 写回)
撤回 trash 回收站(列 / --还原 / --清空)—— 上面删掉的东西都在这儿
皮肤 theme 列主题 · theme <名> <工程> 换皮肤 · theme --巡览 对照图 · theme --撤 回核心默认 · theme --导出/--导入/--装 皮肤出入库
界面 hud 组装卡内 HUD(出 预览.html,--写回 把正则写进 spec)
打包 unpack 卡→spec · build spec→卡 · bundle 整个工程打成 zip(附 sha256 清单)· import 收包:工程包/单卡 → 本地工程 · diff 语义比对
出成品 png 卡→.card.png(ElecKoi 原生导入认的图片卡;内嵌 eleckoi-card 块,可选 --图 封面.png)· preset 卡→.preset.json / --png 出 .preset.png(ElecKoi「创作预设」:把同一批条目当一条常驻规程带过去;--说明 换说明文字、--工具组 换能力组、--留引擎条目 保留扮演机件)
体检 check 校验(值域+一致性+护栏+界面守则+工程自洽,可传目录批量)· lint 正文体检(防八股):只查句式骨架不查词 —— 提示冒号+空行接列表 / 对立句式 ≤1 次 / 破折号 0 处三条硬判据(lint - 从标准输入读一轮正文 · lint --标准 打判据表)· domains 打印真值域表 · selftest 自检四十二项
互通 analyze 拆解任意卡 · to-eleckoi 酒馆→ElecKoi · to-tavern ElecKoi→酒馆
文档 manual 出人读手册(HTML)· index 出给 AI 的导航索引 · coach 出「怎么做一张卡」:给 AI 的规程 + 给 ElecKoi 的向导卡 · guide 按需取规程(不给参数=目录,--节 / --篇 / --对话 / --全文)

一张卡工程长什么样

我的世界/
  source/我的世界.spec.yaml     内容层:世界观、条目、变量、正则   ← 改内容只改这里
  模块账本.json                  接过哪些机制、并进来了什么          ← 撤模块靠它,别手改
  .forge/回收站/                 删掉的条目 / 变量 / 正则存这儿        ← 可逐项还原
  hud/                          界面层:建卡器 / 面板 / 悬浮球     ← 改外观只改这里
    hud.conf.json                 卡名、id 前缀、HUD 全局名、要接几条正则、现在用哪套皮肤
    hud.core.js                   通用核心(把变量树画成面板 + 建卡器 + 悬浮球)
    hud.css                       版式(一个颜色都不写死,全从 --hud-* 槽取)
    主题.css                      皮肤(只有颜色槽覆盖;由 forge theme 写)  ← 可没有
    view_create.html              建卡器那一块的骨架
    view_card.html                面板那一块的骨架
    模块/<名>/<名>.js|.css        按需挂上的模块(悬停的机制,撤掉就删目录)
  测试/test_core.js             HUD 纯函数单测(node 跑,新卡自带)
  成品/我的世界.card.json        交付物(导入 ElecKoi 的就是它)
  文档/                          手册与索引(bundle 时生成)
  怎么开工.md                    这个工程怎么用
  预览.html                      界面在浏览器里的样子(forge hud 产出)
  皮肤巡览.html                  五套皮肤的对照图(forge theme --巡览 产出)

hud.core.js 不含任何世界观内容:你往 variables 里加一栏,面板上就多一行,不用改代码。 换卡只动 hud.conf.json。

目录

路径 是什么
forge.py 命令行入口
forge/ 内核:model domain pack validate zod content bridge export manual mods theme scaffold hud bundle dist sandbox coach draft guard
forge/domain.py 真值域表:拦下会被 ElecKoi 静默降级的字段(市面工具都没有这一段)
forge/content.py 内容编辑内核:往卡里加条目 / 变量 / 正则,删了进回收站;命令行内核(guide / make 的地基也是它)
forge/zod.py Zod 校验代码(schema_code)的保形拆解与拼回:每个叶子表达式原样保留,不加不减
forge/hud.py 界面组装器:拼整页 HTML → 转正则替换串 → 写回 spec → 出预览页
forge/export.py 反向导出:ElecKoi → 酒馆,并把原卡存档塞进 extensions 以便无损回迁
forge/mods.py 模块引擎 + 账本:条目/正则进 spec,脚本/样式进 hud/模块/;接过什么记一笔,所以撤得回来
forge/theme.py 皮肤:把颜色槽表编成一份 CSS;外带可读性体检(对比度)
forge/guard.py 只读禁区名单:三处任选 —— 环境变量 FORGE_READONLY / 工程根 只读名单.txt(一行一路径)/ 目录里放个 .只读 空文件;任何写操作落盘前先问它
forge/bundle.py 打包分发:重建 → 手册 → 索引 → zip(含 包信息.json)
forge/dist.py 收发两端:清单(每个文件的 sha256)与收包(逐条查路径,恶意包整包拒收)
forge/sandbox.py 沙箱友好模式:只创建、只覆盖 —— 不许删 / 不许改名时也能干活
forge/coach.py 制卡向导:一份内容两个出口(给 AI 的规程 + 给 ElecKoi 的向导卡)
forge/draft.py 卡稿:照着「这张卡该长什么样」一次落成(全程在临时目录做,全绿了才整棵拷过去 —— 坏稿一个字都不落)
插件/制卡工坊/ C 轨本体:ElecKoi 宿主插件,给会话里的 AI 一个 cardforge 工具(宿主执行,不受命令行沙箱限制)
assets/骨架/ 一张最小可跑卡的源,连界面层与 JS 单测一起
assets/模块/ 五个可挂模块:骰子 · 尺度 · 备份 · 点数池 · 代笔(各带版本号)
assets/主题/ 五套皮肤:羊皮卷 · 墨夜 · 霜白 · 朱砂 · 苔原
samples/ 演示工程(五个模块全挂)+ 一张最小酒馆 V2 卡
文档/使用说明.md 主文档:命令、结构、值域表、校验项、路线图
制卡工具·方案.md 立项方案:为什么这么做、三条路线怎么选

护城河是什么

九条,每条都对应一类「填了不报错、但事实上没生效」的坑(细目与例证见 文档/使用说明.md §五):

一句话
一 真值域表 —— ElecKoi 对不认识的字段值静默降级,本工具把反编译取证出来的真值域表变成了会拦截的代码(forge.py domains)
二 变量三处同步 —— objects / initial_state / schema_code 少任何一处都不报错,写入会被静默丢掉;check 会对一遍,var --对齐 能补回来
二·补 形状不猜 —— initial_state 里是几层对象是卡自己写的:对得上就写,对不上当场说是哪一格挡着,绝不猜着塞
三 界面层边界守则 —— HUD 整段注进宿主页面,check 逐文件扫三反引号 / <!-- / </script / position:fixed,且 CSS 每个选择器必须带 .hud 前缀
四 模块可撤 —— 模块是覆盖进 spec 的,所以每接一个都把「原来长什么样」存进账本;forge drop 才能精确撤回。你手写进 spec 的东西,工具一个字都不动
五 换皮肤不碰机制 —— 颜色全在 --hud-* 槽里,主题只写槽;外带对比度体检(按宿主纯白 / 近黑两路各算一遍取更差)
六 双向无损 —— 转酒馆时把变量、分组、未映射字段、条目 id 与顺序全存进 extensions,回来时走回迁路径取回;艾瑟兰 2.5MB 实卡往返语义完全一致
七 能力缺口的诚实交代 —— 酒馆没有「由 AI 自己选读」这一档,转过去的条目会变成「永不自动触发」,工具点数并说清根因,不假装成功
八 值域在写入那一刻就拦下 —— 会静默降级的字段当场拒收,并写明「会被降级成什么」,让人没机会「填了不生效」
九 工程层也要对得上 —— 模块目录没登记 / 账本没记 / 改了 spec 没重出成品 / hud/ 没写回 spec,这四种错一个都不报错;check 照着 PROJ-* 逐条查,组装时也会主动说一句

自检

python forge.py selftest

四十二项,含九项负向用例(故意写错,断言必须被拦下)与三次往返比对:值域拦截、常驻位置、 空关键词、模块 CSS 漏前缀、变量三处同步、主题写错槽、浅灰字压浅底、恶意包逃逸 … 逐项在查什么,见 文档/使用说明.md §七。

已验:艾瑟兰 2.5MB 实卡(174 条目 / 6 分组 / 49 变量 / 15 正则,全程只读)解包再打包、转酒馆再回来, 语义均完全一致;新卡开出来即 0 错、界面守则 0 错、挂五个模块后 JS 单测 187 项全过。