Skip to content

feat(plugins): 声明式版本管理器 toolchainSources,自动发现 mise 等安装的运行时并按项目版本选择 - #1581

Draft
FxRayHughes wants to merge 1 commit into
1lck:previewfrom
FxRayHughes:feat/plugin-toolchain-sources
Draft

FxRayHughes wants to merge 1 commit into
1lck:previewfrom
FxRayHughes:feat/plugin-toolchain-sources

Conversation

@FxRayHughes

Copy link
Copy Markdown

关联 Issue

Fixes #1514

相关:#778(SDKMAN 的 JDK 检测)、#815(按项目要求的 Java 版本选 JDK)、#1455(Dock 启动时 PATH 里没有 node)。

问题

从 Dock/Finder 启动的 Lithe 不继承 shell 的 PATH / JAVA_HOME,mise(以及 asdf 等)的 shims 不可见;宿主的固定扫描位置也不包含 ~/.local/share/mise/installs。结果是:

  • mise 装的 Maven 检测不到,只能手动指定;
  • mise 装的 JDK 检测不到,自动检测静默回退到 Homebrew 的 JDK,选错版本也不易察觉;
  • 项目里 mise.toml / .tool-versions 要求的版本(JDK、node、pnpm 等)不会被使用。

把 mise 的目录硬编码进宿主只能解决一个版本管理器,还要宿主自己理解每种版本文件。本 PR 改为新增一个通用的声明式插件能力 toolchainSources:插件声明“版本管理器把运行时装在哪里、每个工具有哪些可执行文件、哪些文件写着项目要的版本”,共享 Core 只读地解析,宿主接入运行时选择。mise 支持作为社区插件单独发布(见下方“配套”)。

改动

插件契约(shared/contracts/plugins/v1/)

  • 新增顶层 toolchainSources(pluginAPIVersion: 3,requires.toolchainSources: 1,权限 toolchains.read,不需要激活事件)。每个来源声明:
    • installRoot:按顺序取第一个可用位置(环境变量、环境变量 + 子路径、主目录相对路径);
    • tools:Lithe 的工具名(java 的版本目录是 JDK home,其他工具按可执行文件名接入)、子目录、版本文件里的名字(如 java、core:java)、home、可执行文件(单个路径分量内允许 *);
    • versionFiles(项目内,同一目录按声明顺序)与 globalVersionFiles,格式为 miseToml 或 toolVersions。
  • README 新增 “Toolchain sources” 一节;schema、fixture toolchain-sources.plugin.json、invalid-manifests.json 反例、rust-core-api.md 同步更新。
  • 能力表 capabilities::check_used 要求 requires 精确列出 toolchainSources,多列或漏列都以 plugin.manifest.invalid 拒绝。

Rust Core(rust/lithe-core/src/plugins/toolchain_sources.rs)

  • 清单校验:数量上限、路径只能是包外相对路径、不允许 .. 和 **、环境变量名格式、显示名长度等。
  • 新命令 plugins.declarative.toolchains({manifest, environment, homeDirectory, root?}):
    • 列出安装目录:跳过符号链接别名(21 -> 21.0.2)和点文件,纯数字版本按新到旧排序;
    • 读版本文件:从项目最深目录逐级向上(最多 64 层),同一目录按声明顺序,最后读全局文件;第一个写了该工具的文件生效;
    • 选择:精确版本、点号边界上的前缀(21 匹配 21.0.2,不匹配 210)、latest、prefix:、mise 建立的别名链接(lts)、path:(绝对、~/ 或相对声明文件)。system、ref: 等需要版本管理器运行时解析的写法不选择任何版本;
    • 安全边界:path: 规范化后落在项目内部时不选择任何版本——宿主会不经询问启动被选中的运行时,仓库不能借此指定自己携带的程序;
    • 返回 inputs(读过和探测过的路径),供宿主判断缓存是否失效。
  • 不运行 mise/asdf,不读 shims,不写任何文件。

macOS 宿主

  • PluginToolchainSources(端口)+ MacPluginToolchainSources(实现):按项目缓存 Core 结果,键包含插件列表(id + 规范化清单)和环境;用 inputs 的 stat 指纹判断版本文件或安装目录是否变化,未变化时不重复调用 Core。
  • 运行时发现(MacRuntimeDiscovery / MacRuntimeLocator):插件提供的 JDK home 加在 SDKMAN 之后,Maven 加在 /usr/bin/mvn 之后。
  • 选择顺序(ProjectRuntimeService):
    • JDK:显式覆盖 → 项目设置 → 版本文件要求的 JDK(显示“来自 mise”)→ JAVA_HOME → 自动检测;
    • Maven:显式覆盖 → 项目设置 → mvnw → 版本文件要求的 mvn → 系统 Maven → 插件提供的其他 mvn;
    • node / pnpm / go / python / gradle 等命令:LITHE_*_PATH 等显式前缀 → 版本文件要求的可执行文件 → 其他已发现的候选 → PATH → 插件提供的其他版本。.project 来源仍不会被语言服务器自动选用。
    • 远程工作区不使用本机版本管理器。
  • 首次解析在后台的运行时探测中预热(performRuntimeRefresh),主线程上的选择链只做缓存校验。
  • 新 RuntimeChoiceSource.toolchainSource(name) / RuntimeToolSource.toolchainSource,界面文案 “来自 %@” / “Version manager”。
  • 每个声明了工具链来源的插件派生应用级模块 <id>.toolchain-sources,只承载启用状态;安全模式下不读取。
  • 免重启:canActivateWithoutRestart 放行只含 debugAdapters 和/或 toolchainSources 模块、无后端、无其他贡献的插件;实时注册表扫描后 AppModel 调用 pluginToolchainsDidChange() 重新发现运行时。同时声明语言或其他贡献的插件仍等待重启。
  • 插件市场权限说明:toolchains.read →“查找版本管理器安装的运行时,以及项目要求的版本”。

文档、i18n、功能矩阵

  • 新 Agent Note .agents/notes/implemented/feature/2026-10-29-plugin-toolchain-sources.md;更新 2026-10-26-plugin-live-activation.md 的谓词和边界。
  • shared/i18n 新增 2 个词条(中英),已重新生成 Localizable.strings。
  • 新功能矩阵条目 plugin-toolchain-sources:macOS implemented / pending,Windows missing / not-applicable(Windows 宿主未接入;Core 校验通过不代表 Windows 可用)。

配套

验证

命令 / 场景 结果
cargo test -p lithe-core --lib(GIT_CONFIG_GLOBAL 指向 init.defaultBranch=master) 1351 passed(含 tests::toolchain_sources 7 项、plugins::toolchain_sources 单元测试 3 项,declarative_plugins 覆盖新 fixture 与 invalid-manifests.json 反例)
scripts/rust-core/verify-rust-core-crates.sh 通过(同上 git 配置;本机全局 init.defaultBranch=main 时 8 个既有 git_line_history_* 用例因 checkout master 失败,与本 PR 无关)
Rust Core Swift bridge:C ABI、LSP、test-git-execution.py、MacGitHubGitOperationsTests 等 4 个 bridge suite 通过(用 SDK 26.5 原生构建跑;脚本默认 SDK 27 的 SwiftTerm Metal 编译在本机失败,属环境问题)
cargo fmt --check -p lithe-core、scripts/rust-core/verify-rust-core-comments.sh 通过
Swift:PluginToolchainSources、MacRuntimeDiscovery、ProjectRuntimeSettings、PluginLiveActivation、DeclarativePluginContract、ProjectRuntimeLoading、BuildToolProviderResolution、JavaLanguageServerRuntime、RunConfigurationIntegration、MavenRuntime、ExecutionModule、MacBuildProviderSessions、BuildSystemService 等 424 tests / 19 suites 全部通过(rebase 到最新 preview 后)
完整 Swift 测试(并行) 部分 Git/Agent/UI 用例在并行负载下超时;单独重跑运行时、构建相关 suite 全部通过;AgentEditRestorerTests、AgentSessionSelector*、LitheTreeListTests 等在未修改的 origin/preview 上同样失败
scripts/architecture/verify-shared-contracts.sh、verify-service-boundaries.sh 通过
node scripts/i18n/generate-i18n.mjs --check、verify-app-language-localization.mjs 通过(9519/9519)
scripts/platform-feature-matrix/verify-platform-feature-matrix.sh、verify-platform-feature-matrix-change.sh origin/preview HEAD 通过
scripts/macos/verify/verify-runtime-bundle-immutability.sh 通过
./scripts/agent-notes/verify-agent-notes.sh 通过(167 篇)
清单 schema 校验(fixture 与 mise 插件 plugin.json) 通过
真实 mise 2026.9.8 对照:用 Core 解析 mise 插件清单,与 mise current / mise where 比较(全局配置、父目录 mise.toml、子目录 .tool-versions、mise.local.toml、temurin-21、lts、前缀 3/3.13) java / node / maven / python / go / pnpm 选中的目录与 mise 完全一致

未做:打包后的 app 实机 UI 验收(功能矩阵保持 pending)。

已知限制

  • 版本文件只支持 miseToml 和 toolVersions:不支持 mise 的 conf.d/*.toml、环境专属配置(mise.<env>.toml、MISE_ENV)和 [tools] 之外的模板/任务写法。
  • system、ref: 等需要版本管理器运行时解析的写法不选择任何版本,回落到 Lithe 原有的选择。
  • 只有已安装的版本会被选中;Lithe 不会安装或下载。
  • 远程工作区不使用本机版本管理器;Windows 宿主尚未接入。
  • 已经启动的进程(语言服务器、运行中的构建)不会因为版本文件变化而重启,下一次启动才使用新选择。

A plugin with pluginAPIVersion 3 and requires.toolchainSources 1 declares
a version manager such as mise: where it installs runtimes, which
executables each tool has, and which project and global files name a
project's versions. Rust Core lists the install directories and reads
the version files (it never runs the version manager) and returns the
installed runtimes, the requested version and the selected installation.

The macOS host adds those runtimes to JDK, Maven and tool discovery and
prefers the version the project requests over the inherited JAVA_HOME
and PATH; an explicit choice in Lithe still wins. A toolchain-only plugin
applies without a restart.
@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

技术方案评审:toolchainSources 的边界

先说明:PR 要解决的问题(Dock 启动看不到 mise 装的运行时、项目版本文件不生效)是真实的。安全边界(path: 落在工作区内不选版本)、按 inputs 缓存、只读、免重启、功能矩阵这些周边做得扎实,下面的意见不针对它们,针对的是方案本身的架构边界。

验收标准

新增一个版本管理器(例如 SDKMAN、nvm、asdf)只改插件,不改 Rust Core;宿主适配层只保留通用机制。这才是“声明式”的意义。

现方案没达到这个标准的地方

1. Core 在重新实现版本管理器的语义。
lts 别名、latest、prefix:、path:、全局文件的覆盖顺序都是 mise 自己的规则,PR 把它们写进了 rust/lithe-core/src/plugins/toolchain_sources.rs。“已知限制”那一节(conf.d、mise.<env>.toml、模板、system、ref:)其实就是与 mise 的分歧清单。develop-lithe 第二节写明“上游拥有的事实仍由上游负责,Core 里不再维护第二份真源”。

2. “新增版本管理器只需要一份清单”实际不成立。
versionFiles[].format 是封闭枚举 miseToml | toolVersions,Core 里已经有带 mise 名字的解析器。nvm(.nvmrc 是单工具纯文本,目录名带 v 前缀)、pyenv(.python-version)、SDKMAN(.sdkmanrc 是 properties 格式)都需要新增格式,也就是改 Core。

3. 宿主侧的工具知识是写死的,且注入面过宽。

  • PluginToolchainSources.swift 里 tool.tool == "java" 是魔法字符串,其余工具按文件名 executables(named:) 匹配。maven 能进 Maven 选择链,只是因为可执行文件恰好叫 mvn,宿主里没有 maven → mvn 的映射。
  • 任何来源都可以为任意同名命令注入候选(比如声明 {tool: "git", executables: ["git"]}),而版本文件点名的那个版本排在 PATH 前面。权限 toolchains.read 的描述是“查找运行时”,实际效果是“影响宿主启动哪个程序”。

4. 优先级和作用域。

  • JDK、Maven、其他命令是三条顺序各不相同的链,硬编码在 ProjectRuntimeService。
  • 没有“项目级”和“全局”的统一概念,只有各条链里各自的位置。
  • 版本文件要求的版本没装时静默回落,而这正是 PR 描述里“选错版本不易察觉”的那个问题。

建议的方案

三种贡献,宿主不认识任何工具

贡献 回答什么 形式 谁提供
工具定义 工具是什么:怎么识别、探测版本、导出环境、启动 清单里的静态声明 任何插件
提供方 装了什么、项目要什么、全局要什么 插件通过 API 返回事实 版本管理器插件
使用方 谁需要这个工具 已有(通过 tool ID 引用) 语言服务器、构建系统、运行配置

1. 工具定义(示意)

"toolDefinitions": [
  { "id": "java", "displayName": "JDK",
    "executables": ["bin/java"], "commands": ["java"],
    "version": { "arguments": ["-version"], "pattern": "(?:1\\.)?(\\d+(?:\\.\\d+)*)" },
    "exports": [{"name": "JAVA_HOME", "value": "${home}"},
                {"name": "LITHE_JAVA_PATH", "value": "${executable}"}],
    "pathPrepend": ["${home}/bin"],
    "launch": ["${executable}", "${runtimeArguments}", "-jar", "${entry}", "${arguments}"] },
  { "id": "maven", "displayName": "Maven",
    "executables": ["bin/mvn"], "commands": ["mvn"],
    "projectExecutables": ["mvnw"],
    "version": { "arguments": ["-v"] },
    "pathPrepend": ["${home}/bin"] }
]

这张“工具 ID 到宿主用途”的映射表就是适配层里本来要写死的东西,改成插件贡献。它取代的是:java 当 JDK home 的特判、maven → mvn 的文件名巧合、契约里写死的版本探测参数和 LITHE_<TOOL>_PATH 导出规则、launch.kind: runtime 里 tool 只能是 java 或 node 的限制,以及选择链里专门给 mvnw、gradlew 写的分支。

  • ID 归属。 插件自己的工具用 插件id:工具;不带前缀的短名(java、node、go、maven)是保留名,只有宿主内置或官方签名插件能定义。同一 ID 被重复定义时拒绝后来者,不允许覆盖。
  • 宿主内置的那几个用同一种格式的数据文件表达,放在 shared/ 下,各平台随应用打包,作为第一个贡献者注册。适配层里不出现任何工具名,以后各插件可以逐个接管自己的定义。
  • 安全校验由 Core 统一做,与具体工具无关。 环境变量值只能是 ${home}、${executable} 模板;禁止 PATH、DYLD_*、LD_*;pathPrepend 只能在 ${home} 之内;探测参数必须是 -x/--xx 形状。安装审查里列出每个定义会运行的程序和参数。

2. 提供方 API

清单只声明“我能回答哪些工具”,不描述目录结构和文件格式:

"toolchainProviders": [{ "id": "mise", "displayName": "mise",
                         "tools": ["java", "node", "maven", "pnpm", "go"] }],
"permissions": ["toolchains.provide"]

宿主向插件发 toolchains/query({operationID, workspace, tools},workspace 为空表示只问全局),插件返回:

{ "installations": [
    {"tool": "java", "version": "21.0.2", "home": "/…/installs/java/21.0.2"} ],
  "statements": [
    {"tool": "java", "scope": "project", "source": "/proj/mise.toml",
     "request": "21", "installation": "/…/java/21.0.2"},
    {"tool": "java", "scope": "global", "source": "~/.config/mise/config.toml",
     "request": "17", "installation": "/…/java/17.0.9"},
    {"tool": "node", "scope": "project", "source": "/proj/.tool-versions",
     "request": "lts", "installation": null} ],
  "watch": ["/proj/mise.toml", "/proj/.tool-versions", "…/installs/java"] }
  • 插件只能回答自己在 tools 里声明过的、且有已启用插件定义的 ID,其余丢弃。这样“任意同名文件被注入”的口子就堵上了。
  • 版本语义(哪个文件优先、lts 对应哪个目录)归插件,宿主不解释 request。
  • installation: null 表示项目要求了但没装。
  • watch 沿用 PR 里“缓存到输入变化”的思路:宿主只做 stat 指纹比较,没变就不再调用插件。
  • 沿用现有的 operationID、取消、超时和过期结果规则。
  • 返回的路径由 Core 统一校验:绝对路径、存在、可执行、不落在工作区内(保留 PR 现有的这条安全规则)、数量有上限。
  • .tool-versions、mise.toml 这类通用解析函数放进 lithe-plugin-docs 的 Node/Java SDK,不放进宿主。

3. 统一的选择规则

所有工具共用一条规则,按层从上到下取第一个有值的:

层 内容 范围
1 固定 用户在 Lithe 里为这个项目选的 项目
2 项目要求 提供方上报的 scope: project,以及 mvnw、gradlew 等项目自带的包装脚本 项目
3 全局默认 Lithe 里的全局默认 → 提供方上报的 scope: global → JAVA_HOME/PATH 全局
4 自动检测 满足最低版本的已安装最新版(现有的 Core 规则) —
  • 没装任何插件时,第 2 层为空,行为与现在完全一致。
  • Lithe 里固定的值失效:报错,不回落(与调试适配器那篇 Note 的做法一致)。
  • 项目要求了但没装:回落到下一层继续工作,但状态标成“未满足”,界面明确提示。
  • 界面上每个工具一行,显示“JDK 21 · 来自项目要求(mise.toml)”,展开后列出每层的值,被盖住的划掉。

对这个 PR 的建议

可以保留: 工作区内路径拒绝、按输入缓存、只读原则、i18n 词条、功能矩阵条目、RuntimeChoiceSource 的“来自 %@”展示。

需要重做: Core 里的 toolchain_sources.rs(管理器专属解析与选择语义)、清单 schema 里的目录与格式描述、ProjectRuntimeService 里三条各自的链。

建议拆分:

  1. 契约(工具定义、提供方、校验)加 Agent Note,先定契约再写代码;
  2. 适配层的查询客户端(缓存、取消、过期);
  3. 统一选择规则加运行时设置界面;
  4. mise 插件加 SDK 帮手,再写一个 SDKMAN 插件当验收,并把 MacRuntimeDiscovery 里写死的 SDKMAN 位置迁出去。

契约在 shared/contracts/plugins/v1/,合并后要在 lithe-plugin-docs 运行 node scripts/contract.mjs sync 同步。

待讨论

  1. API 方向。 上面是“宿主请求、插件返回事实”,不是“插件主动推送”。推送需要插件进程为每个打开的项目常驻;请求式有明确的超时和取消。需要时可以在同一协议上加 toolchains/changed 通知。
  2. 规则细节。 “固定 > 项目要求 > 全局默认”,以及“要求没装就回落并警告”,是建议值,欢迎提出不同意见。
  3. 保留名。 只允许官方签名的插件或宿主内置定义不带前缀的短名;若允许社区插件定义,冲突处理会复杂很多。
  4. 自举和免重启。
    • 提供运行时的插件,它自己的后端不能依赖它要发现的运行时,否则循环依赖。后端必须自带运行时或是单文件可执行程序(契约已有 packagedRuntime)。
    • PR 的 canActivateWithoutRestart 只放行“无后端”的插件,这个条件需要放宽。
    • 这两点我们还没有原型验证过。
  5. Core 的一次性改动。 加定义的校验,以及把 checkRuntimeVersion 里 Java 8 的 1.x 特例改成定义里的正则。之后新增工具或管理器都不再改 Core。

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

这个具体的先后顺序啥的我记得构建工具里面我之前就提过一个方案,按照优先级去维护一个顺序,你可以去看看,具体细节我记不清楚了哎

@FxRayHughes

Copy link
Copy Markdown
Author

感谢这么细的评审。结论先说:同意按「工具定义 / 提供方 / 使用方」重做,本 PR 不再往下推。 我对照代码核对了几处问题,都成立:

  • toolchain_sources.rs 里的 lts、latest、prefix: 和覆盖顺序,等于在 Core 里维护了第二份 mise 真源。「已知限制」那一节列的正是两边的分歧。
  • versionFiles[].format 是封闭枚举,nvm、pyenv、SDKMAN 都要改 Core。
  • 宿主里有 tool == "java" 特判,maven 能进选择链只是因为文件名恰好叫 mvn。任何来源都能给同名命令注入候选,还排在 PATH 前面。toolchains.read 的描述低估了实际影响:它决定的是 Lithe 启动哪个程序。
  • 要求的版本没装时静默回落,这正是 [Bug] macOS 下自动检测不到 mise/SDKMAN 等版本管理器安装的 Maven 和 JDK,必须手动指定路径 #1514 抱怨的「选错版本不易察觉」。

你说的「按优先级维护顺序」,我找到的是你在 #1420 的评审(#1420 (comment)):

  • 单一 executables 入口,数组顺序就是优先级,可选 priority / insertBefore / insertAfter;
  • ExecutableCandidateResolver 区分 skip / resolved / terminalFailure;
  • 明确要复用到 Language Server、Runtime Dependencies 等。

如果是这条,我打算把新方案的选择规则直接建在它上面,不再写第二套分层逻辑。下面是调整方案,请看方向对不对。

1. 选择规则:写成每个工具定义的默认候选数组

四层规则不再写成 ProjectRuntimeService 里的三条链,而是写成工具定义里 executables 的默认顺序,由同一个 resolver 解析。plugins::executables 只需要两处扩展:

  • 新增一种来源 kind: "toolchain",向提供方查询;
  • setting 可以只取项目层或全局层。

示意(Maven;Java 结构相同,把 MAVEN_HOME 换成 JAVA_HOME,并且没有 Wrapper):

{ "id": "maven", "displayName": "Maven", "commands": ["mvn"],
  "version": { "arguments": ["-v"] },
  "executables": [
    { "id": "pinned",         "kind": "setting",     "settingID": "executable", "layer": "project" },
    { "id": "wrapper",        "kind": "projectFile", "path": "mvnw" },
    { "id": "projectRequest", "kind": "toolchain",   "scope": "project" },
    { "id": "default",        "kind": "setting",     "settingID": "executable", "layer": "global" },
    { "id": "globalRequest",  "kind": "toolchain",   "scope": "global" },
    { "id": "home",           "kind": "environment", "variable": "MAVEN_HOME", "path": "bin/mvn" },
    { "id": "path",           "kind": "command",     "command": "mvn" },
    { "id": "detected",       "kind": "toolchain",   "scope": "installed" }
  ] }

各层在 resolver 里的语义:

  • 固定值失效:非空 setting 指向的程序不存在或不可用时返回 terminalFailure,报错,不回落。这是 feat(plugins): 统一可执行文件候选与宿主注入解析 #1420 已有的行为。
  • 项目要求没装:toolchain 候选返回 skip,继续往下找,但带上原因,例如「mise.toml 要求 node 99,未安装」。目前的 skip 不带原因,需要扩展;界面据此标出「未满足」。
  • 自动检测(scope: "installed"):在所有提供方报告的安装里取满足最低版本的最新版。宿主现有的系统发现(java_home、Homebrew)改成第一个内置提供方,SDKMAN 迁到插件。
  • 使用方调整顺序:构建系统等使用方只能在自己的集合里 insertBefore / insertAfter,与 feat(plugins): 统一可执行文件候选与宿主注入解析 #1420 的限制一致。不新增 projectExecutables 这类旁路字段。

这样三条链变成一条。Provider、模型加载和实际执行用同一个结果,#1420 已经保证了这一点。没装版本管理器插件时,所有 toolchain 候选都是 skip,除「自动检测」层外,行为与现在一致。

2. 提供方:mise 的语义交还给 mise

同意「宿主请求,插件返回事实」。mise 插件改成带后端,后端直接调用 mise 自己的机器可读输出,任何一方都不再重写 mise 的规则:

  • mise ls --current --json:每个工具的 requested_version、install_path、source.path、installed,直接映射为 statements;
  • mise ls --installed --json:映射为 installations。

本机验证(mise 2026.9.8):用 env -i HOME=… MISE_OFFLINE=1 模拟从 Dock 启动的环境,一次调用约 20 ms。

自举:后端做成单文件原生程序(Rust,按 darwin-arm64 / x64 分包,Store 已支持按架构下载),不依赖它要发现的 node 或 java。mise 本身的位置用 #1420 的候选声明:设置 → ~/.local/bin/mise → Homebrew → PATH。安装审查里列出它会运行的 mise ls --json。

SDKMAN 适合做第二个验收插件:sdk 是 shell 函数,没有可调用的 CLI,插件只能自己读 ~/.sdkman/candidates 和 .sdkmanrc。这正好检验「上游没有现成答案时,不改 Core 也能接入」。.tool-versions / .sdkmanrc 的解析帮手按你的建议放进 lithe-plugin-docs 的 SDK。用 Node / Java SDK 写的版本管理器插件必须用 packagedRuntime 自带运行时,这一点会写进 Note。

3. 对待讨论各点的意见

  1. API 方向:同意请求式。补充一点:项目里新建的版本文件不在上一次的 watch 里,所以提供方可以在声明里列出它关心的文件名(如 mise.toml、.tool-versions)。工作区里出现或删除这些文件时,宿主重新查询。
  2. 规则:同意「固定 > 项目要求 > 全局默认 > 自动检测」,以及「没装就回落并警告」。有两个细节想确认:
    • 同一层里 mvnw 和 mise 要求的 Maven 谁先?建议 Wrapper 先:它是项目自带的,更具体,也和现有 resolve_maven_executable「覆盖 → Wrapper → 机器安装」一致。Wrapper 属于执行仓库代码,仍按现有规则只在用户操作后启动。
    • 两个提供方对同一工具都给出项目要求时,建议不猜:按插件 ID 顺序取第一个,并在界面标出冲突。
  3. 保留名:同意不带前缀的短名只能由宿主内置或官方签名插件定义。需要确认的是回答:社区提供方(如 mise)应当可以回答 java、node 这类保留名工具,否则 mise 插件对 JDK 没有用。安全靠三层保证:Core 校验答案,安装审查列出影响,界面标明「来自 mise」。
  4. 自举和免重启:自举用单文件后端解决。免重启先不放宽:带后端的提供方在第一阶段装完需要重启,feat(plugins): 声明式版本管理器 toolchainSources,自动发现 mise 等安装的运行时并按项目版本选择 #1581 里「仅工具链来源免重启」的改动撤回。等查询客户端的撤回、取消做完,再按热激活 Note 的规则逐类放行。
  5. Core 的一次性改动:同意把 checkRuntimeVersion 里 Java 8 的 1.x 特例改成定义里的正则。toolchain 候选来源和答案校验(绝对路径、存在、可执行、不在工作区内、数量上限)也放在这次。

4. 拆分

按你的建议,先契约后代码,拆成 4 个 PR:

  1. 提案:proposed/ 下的 Agent Note,加上 shared/contracts/plugins/v1/ 的契约草案(工具定义、提供方、toolchain 候选、答案校验)和 fixture,不改代码。参照 docs(vcs): 统一 VCS 模型与 Provider 接入提案(#827) #1428 VCS 提案的方式先评审。
  2. Core 与查询客户端:
  3. 统一选择与运行时设置界面:ProjectRuntimeService 的三条链换成按工具定义解析。每个工具一行,展开后列出各层的值,被覆盖的划掉,未满足的给出提示。
  4. 插件:mise 0.2.0(后端调用 mise),SDKMAN 插件验收,并把 MacRuntimeDiscovery 里写死的 SDKMAN 位置迁出去。

你列的可以保留的部分(工作区内路径拒绝、按输入缓存、只读原则、i18n 词条、功能矩阵、「来自 %@」展示)会分别带进第 2、3 个 PR。第 1 个提案 PR 提交后关闭本 PR,分支保留作参考;配套的 Store 收录 PR(Lithe-IDEA/Lithe-store#32)同时关闭,等新契约落地后重新提交。

如果方向没问题,我先提第 1 个提案 PR。

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator
  1. 这个删除逻辑是可以的,让 core 去进行监控,但是监控文件类型还是需要声明式的。确实有道理比如修改 swift 里面的版本链文件项目级别控制的时候确实可能导致项目级别的工具链版本进行变更
  2. 查询模块麻烦进行内置,监听的文件列表只要有一个变化了整个都需要进行重载的
  3. 运行时这些东西要不就移动到插件的那个管理界面即可了吧,在那里进行修改吧,可以看看下图这种模式
image 就是说你可以弄一个界面去进行统一展示环境相关的内容,但是一定是需要插件去注入式的,这个很关键。我上面是一个例子呀,因为注入的东西不是通用的模板,这个插件采取的方式是在插件的设置页面注入的(如果你打算做插件的话,插件自己的配置都是需要在这个界面的——也就是你说的 PR4)

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

剩下的没啥问题了,你大胆去做吧

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: no-score ghfind author score missing (not zero)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants