Skip to content

fix: 让 npm 安装版的 comet eval 能够定位共享验证环境 #182

Description

@liuli195

🔎 Existing issue check

  • I have searched the existing issues.

🎯 Affected area

Eval (domains/eval)

📦 Comet version

0.4.0-beta.3

💥 Current behavior

按照官方文档通过 npm 全局安装 Comet 并运行 comet init 后,在普通项目中执行:

comet eval C:\path\to\skill --quick --collect

会在 Windows 上失败:

Error: spawnSync uv ENOENT

该错误并不是 uv 缺失。uv --version 可以正常执行。实际原因是 comet eval<project>/eval 作为工作目录启动 uv,但:

  • npm 发布包不包含仓库级 eval/ 验证环境;
  • comet init 不创建或下载验证环境;
  • comet update 也不提供验证环境;
  • 交互式 comet initcomet init --yes 使用同一安装路径,交互选项中也没有 Eval Harness。

只创建一个空 eval/ 目录后,ENOENT 消失且 uv 真正启动,但 pytest 随后因缺少验证入口、配置和插件而失败。

使用与当前版本匹配的 Comet 源码,并通过 --project 指向源码根目录后,同一命令能成功收集测试。

✅ Expected behavior

官方文档描述的 npm install -g @rpamis/cometcomet initcomet eval 应可直接运行,不应要求用户另外下载未在正常安装流程中说明的源码仓库。

如果外部共享验证环境是有意设计,Comet 应自动提供或定位它,并将“验证环境缺失”与“uv 缺失”报告为不同错误。

🔁 Steps to reproduce

  1. 在 Windows 中运行 npm install -g @rpamis/comet@0.4.0-beta.3
  2. 在普通项目中执行 comet init(交互或 --yes 均可)。
  3. 确认 uv --version 成功。
  4. 确认项目不存在 eval/local/tests/tasks/test_tasks.py
  5. 运行 comet eval C:\path\to\skill --quick --collect
  6. 观察 Error: spawnSync uv ENOENT
  7. 下载同版本 Comet 源码并运行:
    comet eval C:\path\to\skill `
      --project C:\path\to\comet-source `
      --quick `
      --collect
  8. 观察 pytest 成功收集测试。

🖥️ Environment

- OS: Windows 11 10.0.26200
- Shell: PowerShell 7.6.3
- Node: v24.15.0
- Package manager: npm 11.12.1
- Install scope: global
- Skill language: zh
- Target AI platform(s): Codex
- uv: 0.11.7

📋 Logs or output

> uv --version
uv 0.11.7 (9d177269e 2026-04-15 x86_64-pc-windows-msvc)

> comet eval C:\path\to\skill --quick --collect
Error: spawnSync uv ENOENT

🧩 Additional context

相关源码:

  • CLI 将 --project 定义为包含 eval/ 的仓库根目录:

    comet/app/cli/index.ts

    Lines 145 to 159 in da58122

    .command('eval')
    .description('Evaluate a Skill or eval manifest with one command')
    .argument('[target]', 'Local Skill directory, SKILL.md, or comet/eval.yaml')
    .option('--project <dir>', 'Repository root that contains eval/', '.')
    .option('--manifest <path>', 'Path to comet/eval.yaml')
    .option('--skill-path <path>', 'Local Skill directory or SKILL.md')
    .option('--skill-name <name>', 'Skill name used with --skill-path')
    .option('--profile <name>', 'Eval profile override')
    .option('--task <task>', 'Explicit eval task override')
    .option('--report-config <path>', 'JSON/YAML report output config')
    .option('--html', 'Enable HTML report output')
    .option('--quick', 'Use the default quick smoke task where applicable')
    .option('--collect', 'Collect targets without executing Claude or Docker workloads')
    .action(async (target, options) => {
    await evalFacadeCommand(target, options);
  • npm 发布白名单不包含根目录 eval/

    comet/package.json

    Lines 21 to 28 in da58122

    "files": [
    "dist",
    "bin",
    "assets",
    "scripts/install/postinstall.js",
    "!dist/**/*.test.js",
    "!dist/**/__tests__"
    ],
  • 命令以 evalRoot(options) 作为工作目录启动 uv

    comet/app/commands/eval.ts

    Lines 192 to 208 in da58122

    function assertUvAvailable(): void {
    try {
    execFileSync('uv', ['--version'], { stdio: 'pipe' });
    } catch {
    throw new Error(
    'uv is not installed or not in PATH.\n' +
    'Install it: https://docs.astral.sh/uv/getting-started/installation/',
    );
    }
    }
    function runEval(args: string[], options: EvalCommandOptions): void {
    assertUvAvailable();
    execFileSync('uv', args, {
    cwd: evalRoot(options),
    stdio: 'inherit',
    });
  • 交互和非交互安装只在选择阶段不同,最终共用同一安装路径:

    comet/app/commands/init.ts

    Lines 73 to 131 in da58122

    async function selectScope(options: InitOptions, lang: string): Promise<InstallScope> {
    if (options.scope) return options.scope;
    if (options.yes) return 'project';
    return select({
    message: t(lang, 'installScope'),
    choices: [
    { name: t(lang, 'scopeProject'), value: 'project' as const },
    { name: t(lang, 'scopeGlobal'), value: 'global' as const },
    ],
    });
    }
    async function selectLanguage(options: InitOptions): Promise<LanguageConfig> {
    if (options.language) {
    return LANGUAGES.find((l) => l.id === options.language) ?? LANGUAGES[0];
    }
    if (options.yes) return LANGUAGES[0];
    const langId = await select({
    message: t('en', 'languagePrompt'),
    choices: LANGUAGES.map((lang) => ({ name: lang.name, value: lang.id })),
    });
    return LANGUAGES.find((l) => l.id === langId) ?? LANGUAGES[0];
    }
    async function selectInstallMode(options: InitOptions, lang: string): Promise<InstallMode> {
    if (options.installMode) return options.installMode;
    if (options.yes) return 'copy';
    return select({
    message: t(lang, 'installMode'),
    choices: [
    { name: t(lang, 'installModeCopy'), value: 'copy' as const },
    { name: t(lang, 'installModeSymlink'), value: 'symlink' as const },
    ],
    });
    }
    async function selectPlatforms(
    detected: Set<string>,
    options: InitOptions,
    lang: string,
    ): Promise<string[]> {
    const choices = PLATFORMS.map((p) => ({
    name: `${p.name}${detected.has(p.id) ? ` (${t(lang, 'detected')})` : ''}`,
    summaryName: p.name,
    value: p.id,
    checked: detected.has(p.id),
    }));
    if (options.yes) {
    const selected = [...detected];
    return selected.length > 0 ? selected : PLATFORMS.map((p) => p.id);
    }
    return platformSelectPrompt({
    message: t(lang, 'selectPlatforms'),
  • project scope 最终只创建 .comet 和两个文档目录:
    async function createWorkingDirs(projectPath: string, language: string = 'en'): Promise<void> {
    const dirs = [
    path.join(projectPath, 'docs', 'superpowers', 'specs'),
    path.join(projectPath, 'docs', 'superpowers', 'plans'),
    path.join(projectPath, '.comet'),
    ];
    for (const dir of dirs) {
    await ensureDir(dir);
    }
    await mergeProjectConfig(projectPath, language);
    }

建议:

  1. 随 npm 包发布版本匹配的验证环境,或由 init/update 安装;
  2. comet eval 自动定位验证环境;
  3. 启动 uv 前检查 evalRoottest_tasks.py
  4. 区分“验证环境缺失”和“uv 缺失”;
  5. 增加基于实际 npm 发布包的 Windows 端到端测试。

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions