Skip to content

Commit a108b46

Browse files
committed
fix(maker): install lua lsp in private venv
- Create a Maker-owned lua-lsp-venv before installing maker-lua-lsp. - Run pip install through the venv Python instead of uv-managed Python. - Resolve externally-managed-environment failures on uv Python 3.12. - Keep LSP setup best-effort and expose setup errors without blocking builds. - Update CLI/runtime tests and Maker docs for the venv installation flow. - Verified Jest Maker tests, format check, lint, and Maker build.
1 parent b1bd969 commit a108b46

8 files changed

Lines changed: 208 additions & 46 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ Maker 本地开发的默认路径是 CLI-first + PAT-first:
343343
- Maker 鉴权文件必须沿用线上已发布版本的原始本地保存路径,不要为 production 或 rnd 新建环境子目录;不要在用户文档或普通用户说明里暴露具体凭证缓存路径。
344344
- 用户可运行 `taptap-maker login` 主动刷新当前环境鉴权;`taptap-maker init` 和无参数 `taptap-maker pat set` 缺 PAT 时也走 CLI 登录。兼容写法 `taptap-maker pat set <PAT>``--pat PAT``--pat-stdin` 仅用于 CI / 应急联调,其中 argv 形式会让 PAT 进入 `ps`/shell history。
345345
- 本地研发环境配置只作为内部开发能力处理:CLI/MCP 按当前环境使用对应配置,显式 `--env``TAPTAP_MCP_ENV` 优先;项目目录级配置只读取 `.maker/taptap-maker.local.json`,不读取项目根目录散落的本地配置文件;不要把内部研发配置写入面向用户的 README/使用文档。
346-
- `taptap-maker init` 会检查 Git、Python 环境、maker-lua-lsp 本地 Lua 诊断环境、PAT、TapTap token、当前目录绑定状态、app 列表、AI dev kit,并在用户选择 app 后先记录 `.maker-mcp/config.json`,再 checkout 到当前目录;Python 未就绪时会自动尝试准备,最多 3 次,仍失败则暂停 init 且不继续 PAT、app、clone 或 MCP 配置;Python ready 后会 best-effort 安装/升级 `maker-lua-lsp` 并执行 `maker-lua-lsp install --ide codex,cursor,claude`,LSP 失败只提示错误且不阻塞远端构建。clone/fetch 失败后重复执行 init 会复用已记录 app,显式选择不同 app 会拒绝覆盖已有绑定。app 文本预览默认展示前 40 个;账号 app 很多时在 init 交互中输入 `all` 一次性展开全部,或单独跑 `taptap-maker apps --all`;`taptap-maker apps --json` 仅给 AI / 脚本解析使用。AI 转述时宽屏可用两列紧凑布局,窄屏保持单列;每个 app 保留 app_id,并在用户确认后选择 app。
346+
- `taptap-maker init` 会检查 Git、Python 环境、maker-lua-lsp 本地 Lua 诊断环境、PAT、TapTap token、当前目录绑定状态、app 列表、AI dev kit,并在用户选择 app 后先记录 `.maker-mcp/config.json`,再 checkout 到当前目录;Python 未就绪时会自动尝试准备,最多 3 次,仍失败则暂停 init 且不继续 PAT、app、clone 或 MCP 配置;Python ready 后会 best-effort 创建 Maker 私有 LSP venv,在其中安装/升级 `maker-lua-lsp` 并执行 `maker-lua-lsp install --ide codex,cursor,claude`,LSP 失败只提示错误且不阻塞远端构建。clone/fetch 失败后重复执行 init 会复用已记录 app,显式选择不同 app 会拒绝覆盖已有绑定。app 文本预览默认展示前 40 个;账号 app 很多时在 init 交互中输入 `all` 一次性展开全部,或单独跑 `taptap-maker apps --all`;`taptap-maker apps --json` 仅给 AI / 脚本解析使用。AI 转述时宽屏可用两列紧凑布局,窄屏保持单列;每个 app 保留 app_id,并在用户确认后选择 app。
347347
- AI dev kit 安装/更新按当前环境查询最新版本信息,按返回的 `current.version` 生成版本化下载 URL;版本检查失败时降级使用内置默认下载地址。安装成功后记录本地已安装版本,`taptap-maker doctor``maker://status``maker_status_lite` 输出当前版本、最新版本和是否可更新。
348348
- `taptap-maker init` 首次拉取默认使用 `git init` + `git fetch --depth=1 origin` + checkout;Git clone/fetch 会按错误内容判断是否自动重试:503、HTTP 5xx、超时、连接重置、RPC/HTTP2 中断等远端临时错误会重试;认证、权限、仓库不存在、远端拒绝和本地目录冲突不重试。
349349
- 首次 clone/fetch 前必须提示用户:Maker server 可能正在准备仓库,首次拉代码 20 秒以上是正常现象,请保持当前命令运行。

docs/MAKER.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,13 @@ CLI 命令:
150150
- `taptap-maker python setup`:自动准备本地 Lua 诊断环境。缺少可用系统 Python 时,CLI 会先把
151151
`uv` 安装到 `~/.taptap-maker/bin/`,再用 uv 安装 managed Python 到
152152
`~/.taptap-maker/python/uv/`,不修改系统 Python 或全局 PATH;Python ready 后会 best-effort
153-
安装/升级 `maker-lua-lsp` 并执行 `maker-lua-lsp install --ide codex,cursor,claude`
153+
创建 Maker 私有 LSP venv,在其中安装/升级 `maker-lua-lsp` 并执行
154+
`maker-lua-lsp install --ide codex,cursor,claude`
154155
- `taptap-maker python path`:输出 Maker 诊断脚本应使用的真实 Python 可执行文件路径。
155156
- `taptap-maker lua-lsp doctor`:检查 `maker-lua-lsp` 是否已经可用于本地 Lua 诊断。
156-
- `taptap-maker lua-lsp setup`:使用当前 Maker Python 执行
157-
`python -m pip install --upgrade maker-lua-lsp`,然后配置 Codex、Cursor 和 Claude。
157+
- `taptap-maker lua-lsp setup`:使用当前 Maker Python 创建
158+
`~/.taptap-maker/lua-lsp-venv/`,在 venv 中安装/升级 `maker-lua-lsp`
159+
然后配置 Codex、Cursor 和 Claude。
158160

159161
Python 运行时策略:
160162

@@ -163,8 +165,8 @@ Python 运行时策略:
163165
- Maker Lua 诊断脚本的 Python 最低要求是 3.8;低于 3.8 会被标记为
164166
`version_unsupported`,并提示运行 `taptap-maker python setup`
165167
- Python 3.8 到 3.11 满足最低要求,可以继续使用;状态中会提示推荐使用 3.12 或更新版本。
166-
- `taptap-maker python setup` 使用 uv 安装 Python 3.12,满足推荐版本要求;随后继续准备
167-
`maker-lua-lsp`。LSP 安装成功或失败都会在命令输出和状态中展示,但失败不阻塞远端构建。
168+
- `taptap-maker python setup` 使用 uv 安装 Python 3.12,满足推荐版本要求;随后创建
169+
Maker 私有 LSP venv 并继续准备 `maker-lua-lsp`。LSP 安装成功或失败都会在命令输出和状态中展示,但失败不阻塞远端构建。
168170
- Windows 不信任 `%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe` 这类 Microsoft Store
169171
app execution alias;检测到 alias 时会提示运行 `taptap-maker python setup`
170172
- macOS 不把 `/usr/bin/python3`、Xcode 或 Command Line Tools 自带 Python 作为 Maker 工具链,

docs/MAKER_CLI_MCP_SKILL_REWORK_OVERVIEW.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ CLI 负责所有与本机环境、账号、项目绑定相关的低频动作:
5454
`taptap-maker python setup` 才下载 uv,并用 uv 准备 `~/.taptap-maker/` 下的私有 Python;
5555
Python ready 后继续 best-effort 准备 `maker-lua-lsp`
5656
- `maker-lua-lsp` 是本地 Lua 诊断真正使用的能力。CLI 会执行
57-
`python -m pip install --upgrade maker-lua-lsp`
57+
`python -m venv ~/.taptap-maker/lua-lsp-venv`,再用 venv Python 执行
58+
`pip install --upgrade maker-lua-lsp`
5859
`maker-lua-lsp install --ide codex,cursor,claude`;失败只在状态和输出中提示,不阻塞远端构建。
5960
- Maker Lua 诊断的 Python 最低要求是 3.8,推荐 3.12 或更新;低于 3.8 会提示自动准备
6061
Maker 私有 Python,3.8 到 3.11 可用但会提示推荐升级。uv 自动准备的版本固定为 3.12。
6162
- Windows 不信任 `python.exe` 的 Microsoft Store app execution alias,检测优先使用 `py -3`
6263
自动准备路径不调用系统 `python`,避免触发商店安装。
6364
- macOS 不把 Apple/Xcode/Command Line Tools 自带 Python 当作 Maker 工具链运行时;这类 Python
64-
可能无法稳定安装诊断依赖,自动准备时会走 uv managed Python。
65+
可能无法稳定安装诊断依赖,自动准备时会走 uv managed Python。Lua LSP 依赖安装在 Maker
66+
私有 venv 中,不直接修改 uv-managed Python。
6567
- 本地分支测试可直接用 `node dist/maker.js`,不依赖 npm 发布。
6668
- Windows 下生成 MCP 配置时通过 `cmd.exe` 包装 `npx.cmd`,兼容无 shell 的 MCP 启动器。
6769
- 初始化失败时保留现场,返回可重试状态,不自动删除用户文件。

docs/MAKER_PYTHON_RUNTIME_WINDOWS_TEST.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Python 运行时,然后触发 `maker-lua-lsp` 安装和 IDE 配置;LSP 安
1818
- 如果没有可信 Python,就自动准备 Maker 私有 Python。
1919
- Python 最低支持版本是 3.8,推荐 3.12 或更新。
2020
- Python 缺失不能阻塞 Maker MCP 的主状态、提交、推送和远端构建流程。
21-
- Python ready 后需要 best-effort 执行 `pip install --upgrade maker-lua-lsp`
21+
- Python ready 后需要 best-effort 创建 Maker 私有 LSP venv,在其中执行
22+
`pip install --upgrade maker-lua-lsp`,再执行
2223
`maker-lua-lsp install --ide codex,cursor,claude`
2324
- `maker-lua-lsp` 安装失败需要暴露错误给用户和本地 AI,但不能阻塞远端构建。
2425

@@ -53,8 +54,9 @@ taptap-maker lua-lsp setup
5354
- 使用 uv 安装 Python 3.12 managed Python 到 `~/.taptap-maker/python/uv/`
5455
- 写入 `~/.taptap-maker/python.json`
5556
6. Python ready 后,CLI 会执行:
56-
- `<python> -m pip install --upgrade maker-lua-lsp`
57-
- `maker-lua-lsp install --ide codex,cursor,claude`
57+
- `<python> -m venv ~/.taptap-maker/lua-lsp-venv`
58+
- `<lua-lsp-venv-python> -m pip install --upgrade maker-lua-lsp`
59+
- `<lua-lsp-venv>/bin/maker-lua-lsp install --ide codex,cursor,claude`
5860
7. `maker://status``maker_status_lite` 会输出 `Python environment`
5961
`Lua LSP environment`
6062
8. `maker_build_current_directory` 的 tool description 会引导 AI 在本地 Lua 诊断需要环境时先运行 setup,但缺 Python 或 LSP 不阻塞远端构建。
@@ -127,6 +129,7 @@ npx -y -p C:\Temp\taptap-maker-0.0.0-python-runtime.0.tgz taptap-maker python se
127129

128130
- 下载 uv 到 `$env:TAPTAP_MAKER_HOME\bin\uv.exe`
129131
- 下载 Python 3.12 managed Python 到 `$env:TAPTAP_MAKER_HOME\python\uv\`
132+
- 创建 Lua LSP venv 到 `$env:TAPTAP_MAKER_HOME\lua-lsp-venv\`
130133
- 输出 `environment.status: "ready"`
131134
- 输出 `environment.provider: "uv-managed"`
132135

skills/taptap-maker-local/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ Workflow:
183183
4. Python plus `maker-lua-lsp` is the local Lua diagnostics prerequisite for Maker local
184184
development. `taptap-maker init` checks Python before PAT, app list, clone, and MCP config
185185
installation. If Python is not ready, init tries `taptap-maker python setup` up to 3 total
186-
attempts. After Python is ready, setup best-effort installs/upgrades `maker-lua-lsp` and runs
186+
attempts. After Python is ready, setup best-effort creates a Maker private LSP venv,
187+
installs/upgrades `maker-lua-lsp` there, and runs
187188
`maker-lua-lsp install --ide codex,cursor,claude`; LSP failure should be reported but must not
188189
block remote build. If Python setup still fails, explain that init has paused before
189190
login/project clone/MCP config, then guide the user to retry `taptap-maker python setup` with

src/__tests__/makerCliCommands.test.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,17 +1576,12 @@ describe('Maker CLI commands', () => {
15761576

15771577
test('lua-lsp setup installs maker-lua-lsp for Codex Cursor and Claude', async () => {
15781578
process.env.TAPTAP_MAKER_PYTHON_BIN = '/opt/maker-python/bin/python3';
1579-
const scriptsDir = path.join(tempDir, 'python-bin');
1579+
const venvDir = path.join(tempDir, 'maker-home', 'lua-lsp-venv');
1580+
const venvPython = path.join(venvDir, 'bin', 'python');
1581+
const scriptsDir = path.join(venvDir, 'bin');
15801582
const lspCommand = path.join(scriptsDir, 'maker-lua-lsp');
1581-
fs.mkdirSync(scriptsDir, { recursive: true });
1582-
fs.writeFileSync(lspCommand, '');
15831583
spawnSyncMock.mockImplementation((command, args) => {
15841584
if (command === '/opt/maker-python/bin/python3' && args.includes('-c')) {
1585-
if (String(args.at(-1)).includes('sysconfig.get_path')) {
1586-
return { status: 0, stdout: `${scriptsDir}\n`, stderr: '' } as ReturnType<
1587-
typeof spawnSync
1588-
>;
1589-
}
15901585
return {
15911586
status: 0,
15921587
stdout: JSON.stringify({
@@ -1599,12 +1594,18 @@ describe('Maker CLI commands', () => {
15991594
if (command === '/opt/maker-python/bin/python3' && args.join(' ') === '-m pip --version') {
16001595
return { status: 0, stdout: 'pip 25.1\n', stderr: '' } as ReturnType<typeof spawnSync>;
16011596
}
1602-
if (
1603-
command === '/opt/maker-python/bin/python3' &&
1604-
args.join(' ') === '-m pip install --upgrade maker-lua-lsp'
1605-
) {
1597+
if (command === '/opt/maker-python/bin/python3' && args.join(' ') === `-m venv ${venvDir}`) {
1598+
fs.mkdirSync(scriptsDir, { recursive: true });
1599+
fs.writeFileSync(venvPython, '');
1600+
fs.writeFileSync(lspCommand, '');
1601+
return { status: 0, stdout: 'created venv\n', stderr: '' } as ReturnType<typeof spawnSync>;
1602+
}
1603+
if (command === venvPython && args.join(' ') === '-m pip install --upgrade maker-lua-lsp') {
16061604
return { status: 0, stdout: 'installed\n', stderr: '' } as ReturnType<typeof spawnSync>;
16071605
}
1606+
if (command === venvPython && args.includes('-c')) {
1607+
return { status: 0, stdout: `${scriptsDir}\n`, stderr: '' } as ReturnType<typeof spawnSync>;
1608+
}
16081609
if (command === lspCommand && args.join(' ') === 'install --ide codex,cursor,claude') {
16091610
return { status: 0, stdout: 'configured\n', stderr: '' } as ReturnType<typeof spawnSync>;
16101611
}
@@ -1628,6 +1629,11 @@ describe('Maker CLI commands', () => {
16281629
);
16291630
expect(spawnSyncMock).toHaveBeenCalledWith(
16301631
'/opt/maker-python/bin/python3',
1632+
['-m', 'venv', venvDir],
1633+
expect.any(Object)
1634+
);
1635+
expect(spawnSyncMock).toHaveBeenCalledWith(
1636+
venvPython,
16311637
['-m', 'pip', 'install', '--upgrade', 'maker-lua-lsp'],
16321638
expect.any(Object)
16331639
);
@@ -1640,11 +1646,10 @@ describe('Maker CLI commands', () => {
16401646

16411647
test('python setup includes non-blocking Lua LSP setup result', async () => {
16421648
process.env.TAPTAP_MAKER_PYTHON_BIN = '/opt/maker-python/bin/python3';
1649+
const venvDir = path.join(tempDir, 'maker-home', 'lua-lsp-venv');
1650+
const venvPython = path.join(venvDir, 'bin', 'python');
16431651
spawnSyncMock.mockImplementation((command, args) => {
16441652
if (command === '/opt/maker-python/bin/python3' && args.includes('-c')) {
1645-
if (String(args.at(-1)).includes('sysconfig.get_path')) {
1646-
return { status: 0, stdout: `${tempDir}\n`, stderr: '' } as ReturnType<typeof spawnSync>;
1647-
}
16481653
return {
16491654
status: 0,
16501655
stdout: JSON.stringify({
@@ -1657,10 +1662,12 @@ describe('Maker CLI commands', () => {
16571662
if (command === '/opt/maker-python/bin/python3' && args.join(' ') === '-m pip --version') {
16581663
return { status: 0, stdout: 'pip 25.1\n', stderr: '' } as ReturnType<typeof spawnSync>;
16591664
}
1660-
if (
1661-
command === '/opt/maker-python/bin/python3' &&
1662-
args.join(' ') === '-m pip install --upgrade maker-lua-lsp'
1663-
) {
1665+
if (command === '/opt/maker-python/bin/python3' && args.join(' ') === `-m venv ${venvDir}`) {
1666+
fs.mkdirSync(path.dirname(venvPython), { recursive: true });
1667+
fs.writeFileSync(venvPython, '');
1668+
return { status: 0, stdout: 'created venv\n', stderr: '' } as ReturnType<typeof spawnSync>;
1669+
}
1670+
if (command === venvPython && args.join(' ') === '-m pip install --upgrade maker-lua-lsp') {
16641671
return { status: 1, stdout: '', stderr: 'lsp package failed' } as ReturnType<
16651672
typeof spawnSync
16661673
>;

src/__tests__/makerLuaLspRuntime.test.ts

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,23 @@ describe('Maker Lua LSP runtime', () => {
7979
test('setup installs maker-lua-lsp with the selected Python and configures all IDEs', () => {
8080
const calls: SpawnCall[] = [];
8181
const python = path.join(tempDir, 'python', 'bin', 'python3');
82-
const scriptsDir = path.join(tempDir, 'python', 'bin');
82+
const venvDir = path.join(tempDir, 'maker-home', 'lua-lsp-venv');
83+
const venvPython = path.join(venvDir, 'bin', 'python');
84+
const scriptsDir = path.join(venvDir, 'bin');
8385
const lspCommand = path.join(scriptsDir, 'maker-lua-lsp');
84-
fs.mkdirSync(scriptsDir, { recursive: true });
85-
fs.writeFileSync(lspCommand, '');
8686

8787
const spawn = (command: string, args: string[], options?: { timeout?: number }) => {
8888
calls.push({ command, args, timeout: options?.timeout });
89-
if (command === python && args.join(' ') === '-m pip install --upgrade maker-lua-lsp') {
89+
if (command === python && args.join(' ') === `-m venv ${venvDir}`) {
90+
fs.mkdirSync(scriptsDir, { recursive: true });
91+
fs.writeFileSync(venvPython, '');
92+
fs.writeFileSync(lspCommand, '');
93+
return spawnResult(0, 'created venv\n');
94+
}
95+
if (command === venvPython && args.join(' ') === '-m pip install --upgrade maker-lua-lsp') {
9096
return spawnResult(0, 'installed maker-lua-lsp\n');
9197
}
92-
if (command === python && args.includes('-c')) {
98+
if (command === venvPython && args.includes('-c')) {
9399
return spawnResult(0, `${scriptsDir}\n`);
94100
}
95101
if (command === lspCommand && args.join(' ') === 'install --ide codex,cursor,claude') {
@@ -112,6 +118,10 @@ describe('Maker Lua LSP runtime', () => {
112118
expect.arrayContaining([
113119
expect.objectContaining({
114120
command: python,
121+
args: ['-m', 'venv', venvDir],
122+
}),
123+
expect.objectContaining({
124+
command: venvPython,
115125
args: ['-m', 'pip', 'install', '--upgrade', 'maker-lua-lsp'],
116126
}),
117127
expect.objectContaining({
@@ -120,18 +130,28 @@ describe('Maker Lua LSP runtime', () => {
120130
}),
121131
])
122132
);
123-
expect(calls.find((call) => call.command === python && call.args[0] === '-m')?.timeout).toBe(
133+
expect(calls.find((call) => call.command === python && call.args[1] === 'venv')?.timeout).toBe(
124134
120_000
125135
);
136+
expect(
137+
calls.find((call) => call.command === venvPython && call.args[1] === 'pip')?.timeout
138+
).toBe(120_000);
126139
expect(
127140
calls.find((call) => call.command === lspCommand && call.args[0] === 'install')?.timeout
128141
).toBe(120_000);
129142
});
130143

131144
test('setup reports LSP install failure without throwing', () => {
132145
const python = path.join(tempDir, 'python', 'bin', 'python3');
146+
const venvDir = path.join(tempDir, 'maker-home', 'lua-lsp-venv');
147+
const venvPython = path.join(venvDir, 'bin', 'python');
133148
const spawn = (command: string, args: string[]) => {
134-
if (command === python && args.join(' ') === '-m pip install --upgrade maker-lua-lsp') {
149+
if (command === python && args.join(' ') === `-m venv ${venvDir}`) {
150+
fs.mkdirSync(path.dirname(venvPython), { recursive: true });
151+
fs.writeFileSync(venvPython, '');
152+
return spawnResult(0, 'created venv\n');
153+
}
154+
if (command === venvPython && args.join(' ') === '-m pip install --upgrade maker-lua-lsp') {
135155
return spawnResult(1, '', 'network timeout');
136156
}
137157
return spawnResult(1, '', `unexpected command: ${command} ${args.join(' ')}`);
@@ -169,4 +189,32 @@ describe('Maker Lua LSP runtime', () => {
169189
expect(environment.ready).toBe(false);
170190
expect(environment.nextAction).toContain('taptap-maker lua-lsp setup');
171191
});
192+
193+
test('doctor accepts LSP command that supports help but not version', () => {
194+
const python = path.join(tempDir, 'python', 'bin', 'python3');
195+
const scriptsDir = path.join(tempDir, 'maker-home', 'lua-lsp-venv', 'bin');
196+
const lspCommand = path.join(scriptsDir, 'maker-lua-lsp');
197+
fs.mkdirSync(scriptsDir, { recursive: true });
198+
fs.writeFileSync(lspCommand, '');
199+
200+
const spawn = (command: string, args: string[]) => {
201+
if (command === lspCommand && args[0] === '--version') {
202+
return spawnResult(2, '', 'maker-lua-lsp: error: unrecognized arguments: --version');
203+
}
204+
if (command === lspCommand && args[0] === '--help') {
205+
return spawnResult(0, 'usage: maker-lua-lsp\n');
206+
}
207+
return spawnResult(1, '', `unexpected command: ${command} ${args.join(' ')}`);
208+
};
209+
210+
const environment = checkMakerLuaLspEnvironment({
211+
pythonEnvironment: readyPython(python),
212+
spawn,
213+
});
214+
215+
expect(environment.ready).toBe(true);
216+
expect(environment.status).toBe('ready');
217+
expect(environment.command).toBe(lspCommand);
218+
expect(environment.version).toBe('installed');
219+
});
172220
});

0 commit comments

Comments
 (0)