用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/naamfung/dsc --skill lua-plugin-creator命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
按 SKILL.md 明确列出的规则识别并改写写作中的 AI 痕迹。只能处理清单内的问题;未命中规则的文字必须原样保留,也不能改变文章框架。适用于写作完成后的成稿清理。 Remove AI writing tells using an explicit whitelist of rules; leaves unmatched text untouched.
執行全面的代碼審計,檢查安全漏洞、邏輯錯誤、性能問題和代碼規範。
按照仓库规范生成简洁、聚焦的 git 提交(单功能一次提交,提交信息说明"为什么"而非"是什么")。
正在显示 SKILL.md
| name | lua-plugin-creator |
| description | 极全面的 LUA 插件创造指南——教模型经 tool-lua-host 编写、注册、验证与交付新的 LUA 工具插件(dsc API 完整参考、类型注解、热加载、最佳实践)。 |
本技能指导你(DSC 的 Agent)自己创造 DSC LUA 插件:当现有工具无法满足需求时,编写一个 DSC 专属的 LUA 脚本,由 tool-lua-host 加载后注册为新工具,无需修改宿主主程序,无需请求开发人员介入。
你写文件: scripts/<脚本名>/main.lua
│ tool-lua-host(空壳插件)每秒轮询脚本目录,发现新脚本/变更自动加载或重载
▼
脚本执行: dsc.register_tool("my_tool", spec, handler)
│ 注册名自动加前缀 → 宿主可见工具名 = lua_my_tool
▼
宿主调用: Agent 或任意工具经宿主流水线调用 lua_my_tool → 转发到 tool-lua-host
│ → 在你的脚本 VM 上执行 handler(args),返回结果文本
关键特性:
os/io 库——脚本无法直接读写文件系统、执行进程、访问网络。需要这类能力时用 dsc.tool.call 调用宿主已有工具(如 shell、str_replace_editor)。main.lua 后无需重启,自动生效;删除脚本目录则工具自动卸载。| 情形 | 行动 |
|---|---|
现有工具组合可满足(如先 str_replace_editor 再 shell) | 优先组合,不创造 |
| 需要多步骤、有状态、可复用的流程 | 创造插件(脚本内可自由编排 dsc API) |
| 需要跨工具共享状态 | 用 dsc.store |
| 需要在宿主工具执行前后干预(拦截/改写参数/结果) | 用 dsc.hook |
| 需要后台长任务 | 用 dsc.job |
| 需要调用宿主 LLM 做推理/生成 | 用 dsc.llm.chat |
创造插件的回报:一次编写,全宿主可用——任何会话、任何 Agent、任何其他插件都能调用你注册的工具。
所有能力以全局 dsc 表暴露。参数/返回值均为 LUA 原生类型(table / string / number / boolean)。
dsc.register_tool(name: string, spec: table, handler: function) -> nil
name:工具名(小写、下划线分词)。对外暴露为 lua_<name>。spec:{ description = string, parameters = <JSON Schema> }。parameters 用 JSON Schema 描述参数(type=object、properties、required)。handler:function(args: table) -> string | table | nil。返回字符串直接作为工具结果;返回 table 会序列化为 JSON;返回 nil 结果为空。dsc.register_tool("greet", {
description = "生成问候语",
parameters = {
type = "object",
properties = { who = { type = "string", description = "问候对象" } },
required = { "who" }
}
}, function(args)
return "你好," .. args.who .. "!"
end)
-- 宿主侧工具名:lua_greet
dsc.llm.chat({ system?: string, user: string, max_tokens?: number }) -> string
system 可选;max_tokens 省略用服务端默认。local summary: string = dsc.llm.chat({
system = "你是一位简洁的摘要助手。",
user = "请摘要这段文本:..." ,
max_tokens = 200
})
dsc.tool.call(name: string, args?: table) -> string
dsc.tool.list() -> { {name, description}, ... }
local out: string = dsc.tool.call("shell", { command = "dir /b ." })
dsc.notify.emit(name: string, data?: table) -> nil
on_event 钩子)都能收到。name 建议用命名空间式:<脚本名>/<事件>(如 myplugin/done)。dsc.notify.emit("myplugin/done", { task = "sync", ok = true })
dsc.store.get(key: string) -> any | nil
dsc.store.set(key: string, value: any) -> nil
dsc.store.delete(key: string) -> nil
local n = dsc.store.get("count") or 0
dsc.store.set("count", n + 1)
dsc.hook.before_tool(fn) -- fn(name, args) -> (veto, error, new_args)
dsc.hook.after_tool(fn) -- fn(name, args, result, error) -> (new_result, new_error)
dsc.hook.on_event(fn) -- fn(name, data)
before_tool:veto=true 阻止执行并返回 error;返回第三个值 new_args 改写参数。after_tool:改写结果/错误(返回两个值)。dsc.tool.call)时,宿主回调钩子会跳过该脚本的钩子(避免 VM 重入死锁)。因此不要在钩子里依赖"自己工具内部调用的工具"会经过自己的 before 钩子。-- 拦截:禁止对系统盘执行 shell
dsc.hook.before_tool(function(name, args)
if name == "shell" and args and args.command and string.find(args.command, "format") then
return true, "format 命令被 LUA 钩子拦截", nil
end
return false, "", nil
end)
dsc.job.spawn(fn: function) -> job_id: string
dsc.job.status(job_id: string) -> string -- "running" | "completed: <结果>" | "failed: <错误>"
dsc.job.list() -> { [job_id] = "running"|"completed"|"failed", ... }
fn 在你的脚本 VM 上异步执行(与主执行串行化,不会并发冲突)。dsc.llm.chat)或定时汇报。dsc.job.status 查询。local job = dsc.job.spawn(function()
local r: string = dsc.llm.chat({ user = "总结当前进展。", max_tokens = 100 })
dsc.notify.emit("myplugin/job_done", { result = r })
return r
end)
脚本经 flow-sensitive 类型检查器校验。为变量、参数、返回值写类型注解,让类型错误在加载前暴露(否则只作为警告打印)。
local n: number = 42
local i: integer = 7
local s: string = "hello"
local b: boolean = true
local v: any = anything -- any = 任意类型(关闭检查)
local x: nil = nil
type Point = { x: number, y: number }
local p: Point = { x = 10, y = 20 }
local lookup: { [string]: number } = { a = 1 }
local arr: { number } = { 1, 2, 3 }
local maybe: string? = nil -- 可空
type Exit = { kind: "exit", code: number }
type Msg = { kind: "message", text: string }
type Event = Exit | Msg -- 联合类型
local function first<T>(arr: { T }): T?
return arr[1]
end
local function handle(e: Event)
if e.kind == "exit" then
print(e.code) -- 此处 e 已收窄为 Exit
else
print(e.text) -- 此处 e 已收窄为 Msg
end
end
local function summarize(args: SummaryArgs): string
...
end
scripts/
└── <插件名>/ # 目录名 = 插件名(小写,连字符分隔)
└── main.lua # 唯一入口,加载时执行(定义工具/钩子/初始化)
main.lua 顶层语句在加载时执行一次:定义函数 → dsc.register_tool → 可选注册钩子/初始化 store。type X = ...)建议放文件头部,便于阅读。read_skill 再动手。exit_plan_mode 呈现设计再写码。shell/str_replace_editor 创建 scripts/<插件名>/main.lua(相对宿主工作目录)。dsc.* 调用、类型注解、字符串拼接(number 需 tostring)、table 字段名。lua_<name> 自动出现在宿主工具目录。lua_<name> 做冒烟测试;复杂工具准备多组参数(正常/边界/错误)。main.lua → 2 秒后自动重载 → 重测。删除目录即卸载。验证要点:
error() 或返回错误文本);dsc.hook,确认钩子不影响无关工具。parameters 使用 JSON Schema(object 类型):
parameters = {
type = "object",
properties = {
prompt = { type = "string", description = "必填参数" },
count = { type = "integer", description = "可选参数" },
tags = { type = "array", items = { type = "string" } }
},
required = { "prompt" }
}
description 写清每个参数含义——这是调用方(LLM)理解你工具的窗口。required 只放必须项。error("..."):抛 LUA 错误,宿主记录为工具执行失败。return "错误说明文本":作为正常结果返回,适合"软失败"。error()。dsc.tool.call 调用的工具失败时抛错,可用 pcall 捕获后转为友好提示:local ok, out = pcall(function() return dsc.tool.call("shell", { command = cmd }) end)
if not ok then
return "执行失败: " .. tostring(out)
end
fn 内部抛错会记录为 failed: <错误>,可用 dsc.job.status 查询。args.foo or "default" 处理缺省。<name>/<event>);store 键带脚本前缀避免冲突。dsc.job。dsc.llm.chat 完成就别多次;max_tokens 按需设小。return { ... }(自动序列化),便于下游解析。tostring 陷阱:number 拼接字符串需显式 tostring(n)。| 误区 | 正解 |
|---|---|
| 想直接读文件/执行进程 | 沙箱无 os/io,用 dsc.tool.call("shell" / "str_replace_editor", ...) |
dsc.llm.chat 返回空 | 已内部走流式;确认 user 必填、system 可省 |
| handler 返回 table 期望原样 | table 自动 JSON 序列化;要纯文本就返回字符串 |
| 依赖"自己工具内部调用的工具"经过自己的 before 钩子 | 嵌套调用时本脚本钩子会跳过(防死锁) |
忘了 dsc.register_tool | 只定义函数不注册 = 无工具产生 |
| store 键名冲突 | 键加脚本前缀,如 myplugin_count |
| 热加载后旧状态 | store 保留(进程内),但脚本内局部变量重载后重置 |
-- scripts/translator/main.lua
type TransArgs = { text: string, target: string? }
local function translate(args: TransArgs): string
local target: string = args.target or "中文"
local r: string = dsc.llm.chat({
system = "你是专业翻译。只输出译文,不加解释。",
user = "把下面内容翻译成" .. target .. ":\n" .. args.text,
max_tokens = 500
})
return r
end
dsc.register_tool("translate", {
description = "把文本翻译成指定语言(经宿主 LLM)",
parameters = {
type = "object",
properties = {
text = { type = "string", description = "待翻译文本" },
target = { type = "string", description = "目标语言,默认中文" }
},
required = { "text" }
}
}, translate)
-- scripts/devops/main.lua
-- 收集目录信息 → 统计代码规模 → 汇报
local function code_stats(args: { dir: string }): string
local ok, out = pcall(function()
return dsc.tool.call("shell", { command = "dir /s /b " .. args.dir })
end)
if not ok then
return "统计失败: " .. tostring(out)
end
dsc.store.set("devops_last_dir", args.dir) -- 记住上次目录
local count = 0
for line in out:gmatch("[^\r\n]+") do
count = count + 1
end
dsc.notify.emit("devops/stats", { dir = args.dir, files = count })
return "目录 " .. args.dir .. " 共 " .. tostring(count) .. " 个文件。"
end
dsc.register_tool("code_stats", {
description = "统计目录下文件数量并发布事件",
parameters = {
type = "object",
properties = { dir = { type = "string", description = "目录路径" } },
required = { "dir" }
}
}, code_stats)
-- scripts/reporter/main.lua
-- 后台跑一次分析,完成后发事件;拦截危险 shell 命令
dsc.register_tool("start_analysis", {
description = "后台启动代码分析,返回任务 id",
parameters = { type = "object", properties = {} }
}, function()
local job = dsc.job.spawn(function()
local r: string = dsc.llm.chat({ user = "分析这个仓库的模块划分。", max_tokens = 300 })
dsc.notify.emit("reporter/analysis_done", { result = r })
return r
end)
return "分析任务 " .. job .. " 已启动,结果完成后发布 reporter/analysis_done 事件。"
end)
dsc.hook.before_tool(function(name: string, args: any): any
if name == "shell" and args and args.command
and (string.find(args.command, "rm -rf") or string.find(args.command, "format")) then
return true, "危险命令已被 LUA 钩子拦截", nil
end
return false, "", nil
end)
scripts/<插件名>/main.lua 已创建,命名符合规范description + 参数 Schemalua_<工具名> 可用,正常/边界/错误路径均验证过