diff --git a/src/constants.js b/src/constants.js index e096438..970ae6c 100644 --- a/src/constants.js +++ b/src/constants.js @@ -63,16 +63,39 @@ function detectCommandPrefix() { /** * 智能推测用户使用的命令前缀 - * 基于环境变量和执行路径的启发式判断 + * 从 process.argv 中提取 init 之前的所有部分作为命令前缀 */ function reconstructCommandPrefix() { - // 最简单最直接的判断:如果有 npm_execpath 且包含 npx,就是 npx 调用 - if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) { - return 'npx dpml-prompt@snapshot' // 默认 snapshot 版本 + try { + // 从 process.argv 中找到 init 命令的位置 + const initIndex = process.argv.findIndex(arg => arg === 'init') + + if (initIndex > 0) { + // 提取 init 之前的所有参数,跳过 node 可执行文件路径 + const prefixParts = process.argv.slice(1, initIndex) + + if (prefixParts.length > 0) { + // 如果第一部分是脚本路径,简化为包名 + const firstPart = prefixParts[0] + if (firstPart.includes('cli.js') || firstPart.includes('bin')) { + // 开发模式,替换为包名 + prefixParts[0] = 'dpml-prompt' + } + + return prefixParts.join(' ') + } + } + + // 如果找不到 init 或解析失败,使用环境变量判断 + if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) { + return 'npx dpml-prompt@snapshot' + } + + return 'dpml-prompt' + } catch (error) { + // 解析失败时的回退逻辑 + return 'dpml-prompt' } - - // 其他情况默认是全局安装 - return 'dpml-prompt' } /** diff --git a/src/tests/commands/command-prefix.e2e.test.js b/src/tests/commands/command-prefix.e2e.test.js index 694f99c..802d91b 100644 --- a/src/tests/commands/command-prefix.e2e.test.js +++ b/src/tests/commands/command-prefix.e2e.test.js @@ -44,7 +44,7 @@ describe('命令前缀动态检测 E2E', () => { describe('init命令保存命令前缀', () => { test('npx方式调用时应保存npx前缀', async () => { // 模拟npx调用init命令 - process.argv = ['node', 'npx_cache/dpml-prompt@snapshot/dist/bin/promptx.js', 'init'] + process.argv = ['node', 'npx', 'dpml-prompt@snapshot', 'init'] process.env.npm_execpath = '/usr/local/lib/node_modules/npm/bin/npx-cli.js' // 导入并执行init命令 @@ -71,7 +71,7 @@ describe('命令前缀动态检测 E2E', () => { }) test('指定版本号时应正确保存', async () => { - process.argv = ['node', 'npx_cache/dpml-prompt@latest/dist/bin/promptx.js', 'init'] + process.argv = ['node', 'npx', 'dpml-prompt@latest', 'init'] process.env.npm_execpath = '/usr/local/lib/node_modules/npm/bin/npx-cli.js' const cli = new PouchCLI() @@ -79,7 +79,7 @@ describe('命令前缀动态检测 E2E', () => { await cli.execute('init', []) const savedPrefix = await config.readText('command-prefix') - expect(savedPrefix).toBe('npx dpml-prompt@snapshot') // 简化逻辑只会返回默认snapshot版本 + expect(savedPrefix).toBe('npx dpml-prompt@latest') }) }) @@ -130,25 +130,25 @@ describe('命令前缀动态检测 E2E', () => { const testCases = [ { name: 'npx最新版本', - argv: ['node', '/tmp/.npm/_npx/1234/lib/node_modules/dpml-prompt/src/bin/promptx.js', 'init'], + argv: ['node', 'npx', 'dpml-prompt', 'init'], hasNpxEnv: true, - expected: 'npx dpml-prompt@snapshot' + expected: 'npx dpml-prompt' }, { name: 'npx指定版本', - argv: ['node', '/tmp/.npm/_npx/1234/lib/node_modules/dpml-prompt@0.1.0/src/bin/promptx.js', 'init'], + argv: ['node', 'npx', 'dpml-prompt@0.1.0', 'init'], hasNpxEnv: true, - expected: 'npx dpml-prompt@snapshot' + expected: 'npx dpml-prompt@0.1.0' }, { name: 'npx snapshot版本', - argv: ['node', '/tmp/.npm/_npx/1234/lib/node_modules/dpml-prompt@snapshot/src/bin/promptx.js', 'init'], + argv: ['node', 'npx', 'dpml-prompt@snapshot', 'init'], hasNpxEnv: true, expected: 'npx dpml-prompt@snapshot' }, { name: '全局安装', - argv: ['node', '/usr/local/bin/dpml-prompt', 'init'], + argv: ['node', 'dpml-prompt', 'init'], hasNpxEnv: false, expected: 'dpml-prompt' }, @@ -157,6 +157,30 @@ describe('命令前缀动态检测 E2E', () => { argv: ['node', '/Users/dev/PromptX/src/bin/promptx.js', 'init'], hasNpxEnv: false, expected: 'dpml-prompt' + }, + { + name: 'npx -y 参数', + argv: ['node', 'npx', '-y', 'dpml-prompt', 'init'], + hasNpxEnv: true, + expected: 'npx -y dpml-prompt' + }, + { + name: 'npx 复杂参数', + argv: ['node', 'npx', '--yes', '--registry=https://registry.npm.taobao.org', 'dpml-prompt@latest', 'init'], + hasNpxEnv: true, + expected: 'npx --yes --registry=https://registry.npm.taobao.org dpml-prompt@latest' + }, + { + name: 'pnpm dlx', + argv: ['node', 'pnpm', 'dlx', 'dpml-prompt@snapshot', 'init'], + hasNpxEnv: false, + expected: 'pnpm dlx dpml-prompt@snapshot' + }, + { + name: 'yarn dlx', + argv: ['node', 'yarn', 'dlx', 'dpml-prompt', 'init'], + hasNpxEnv: false, + expected: 'yarn dlx dpml-prompt' } ]