konggu2077 / MealScript-
git clone https://github.com/konggu2077/MealScript-.git
MealScript 把「吃什么」写成一门语言,再把它编译成一份有官方价格证据的方案
—
stars, 24h
+34
stars, 7 days · #1,201
+34
stars, 30 days · #8,183
34
★ total · +34% this week
0
forks · 1 open issues
Star history starts 2026-10-20; the chart appears after the second daily snapshot.
LanguageTypeScript
License—
Created2026-10-15 (5d ago)
Last pushtoday
Watchers0
OwnerUser
Links
README excerpt
### 立刻试试 - **在线 Demo**:https://konggu2077.github.io/MealScript-/dist/mealscript.html —— 不用装任何东西,打开即用,断网也能跑 (仓库设置里开启 Pages:`Settings → Pages → Deploy from a branch → main / (root)` 后生效) - **下载单文件网页**:`dist/mealscript.html`(265 KB,双击即开,CSS/JS 全内联、零外部请求) ### 目录 - 界面一览 - 这不是一个聊天点餐助手 - 快速开始 - 目录结构 - DSL v0.1 语法 - 真实性规则(我们的红线) - Live 与 Replay 的区别 - 测试 - 与官方标准的偏差说明(如实记录) - 免责声明 > Code Your Meal. Compile Your Hunger. **MealScript 是一门面向真实麦当劳点餐场景的轻量 DSL,配一个真正自己写的编译器。** **自己写的编译器 · 官方 MCP 真实结算价 · 行列级可解释诊断** 你用可编译的约束语句描述预算与偏好,系统用麦当劳官方 MCP 查询真实门店、真实菜单与真实结算价,产出带调用证据的候选方案,以及能精确到行列的编译诊断。 [code] 打开页面后有 5 个内置示例,其中 3 个是**故意写错的**——用来看结构化错误长什么样。 ### 单文件离线版(双击即开,无需安装任何东西) [code] `dist/mealscript.html` 是一个**自包含的单文件网页**:CSS 与 JS 全部内联,**零外部请求、零凭据、离线可用**。 编译器、语义检查、组合搜索、价格校验全部在浏览器里真实运行;门店与价格来自一次真实会话的**脱敏录制样例**, 所以它的状态恒为 `REPLAY_RESULT`,永远不会伪装成 `BUILD_PASSED`。 编辑器带**自动补全**:输入字段名、枚举取值(`pickup` / `dry_run` …)时有候选弹窗; 在 `require_keyword` / `exclude_keywords` 的引号里输入时,会从菜单数据里实时联想**分类、商品、标签** (如 `小食`、`麦旋风`、`爆款`),↑↓ 选择、Tab/Enter 确认、Esc 关闭。 写多关键词时只按**最后一个分隔符之后的片段**联想,并自动把这一行里已经写过的关键词从候选里去掉。 它已提交进仓库,也可以直接从 GitHub 下载后双击打开。构建时 `@mealscript/mcp-adapter` 被强制解析为 `browser.ts`,因此 `node:fs`、MCP SDK 与任何 Token 都不可能进入产物(有测试守着这条边界)。 ### 打开 Live 模式(真实 MCP) [code] `.env` 已在 `.gitignore` 中,Token **不会**进入仓库。没有 Token 也能完整体验 Replay 模式。 ### 刷新产品目录与价格(录制新 fixture) 拿到 Token 后,一条命令就能把门店、菜单、分类、标签和官方价全部重新抓一遍: [code] 它做的事,以及它**不做**的事: - 走的就是管线用的那个**只读**适配器 —— 写操作(下单/领券/抽奖)在适配器里被硬阻断,一次都调不到 - 逐件调 `calculate-price` 核对官方价,**只记录「官方价 = 菜单价」的非优惠商品**;有折扣的商品直接跳过,不做猜测 - 递归脱敏:门店地址 / 经纬度 / 距离 / 图片 / traceId / 个人信息全部剔除,并在终端**逐字段打印删了什么** - 结束时打印与上一版的差异(商品数、新增、下架、价格变动),先 `git diff fixtures/` 复核再提交 `location` 的写法与 `query-nearby-stores` 的对应关系(编译期拆分,见 `packages/compiler/src/location.ts`): | `location` 写法 | 拆成 | |---|---| | `北京市海淀区五道口` | city=`北京市`,keyword=`海淀区五道口` | | `上海市浦东新区陆家嘴` | city=`上海市`,keyword=`浦东新区陆家嘴` | | `深圳市南山区科技园` | city=`深圳市`,keyword=`南山区科技园` | | `北京 五道口` | city=`北京`,keyword=`五道口` | 编辑器里输入 `location` 的引号时会给出真实地点候选(北京/上海/深圳/广州/成都/杭州共 14 个); 换地点只影响 Live 查询 —— Replay 的门店来自录制样例,改 `location` 不会换门店,trace 里仍会如实记录你请求的城市与关键词。 --- ## 目录结构 [code] --- ## DSL v0.1 语法 | 字段 | 类型 / 规则 | 违反时的错误码 | |---|---|---| | `location` | 字符串,门店发现用的位置文本 | 缺失或空 → E001 | | `mode` | v0.1 仅 `pickup` | `delivery` → E102 | | `budget` | ` `max_items` → E103 | | `exclude_keywords` | 字符串数组(元素内也可用逗号写多个) | 非法数组 → E001 | | `min_items` / `max_items` | 整数 1–3 | 越界或 `min > max` → E103 | | `optimize` | v0.1 仅 `min(total_price)` | `max(protein)` → E104 | | `execution` | v0.1 固定 `dry_run` | `place_order` → E105 | **省略时的默认值**:`mode=pickup`、`min_items=1`、`max_items=2`、`optimize=min(total_price)`、`execution=dry_run`。 ### 多关键词:AND 语义 `require_keyword` 可以写多个关键词,**每一个都必须命中一件商品**(AND)。三种写法完全等价: [code] 去空白、去重后得到 `["巨无霸牛鱼肉堡", "麦旋风"]`,编译器要求套餐里同时出现这两类商品各一件。 因为每个关键词都要占一个名额,关键词个数超过 `max_items` 时直接给 **E103** 并告诉你该把 `max_items` 调到多少, 而不是等到搜索阶段报一个含糊的「无可行组合」。 注意语义边界:`require_keyword` 指的是**至少一件**命中商品,其余名额可以来自任意可售品类 —— 所以 `require_keyword: "汉堡"` + `min_items: 2` 组出