diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a23162..5d9cd89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -134,3 +134,14 @@ jobs: pnpm exec vsce publish --packagePath *.vsix $PRE_FLAG env: VSCE_PAT: ${{ secrets.VSCE_PAT }} + # Open VSX 发布:由仓库变量 ENABLE_OVSX_PUBLISH 门控(与 ENABLE_MARKETPLACE_PUBLISH 对称)。 + # 需配置 OVSX_PAT(open-vsx.org 的 Access Token);覆盖 Cursor / Windsurf / VSCodium 等 Open VSX 系编辑器。 + # 与 vsce 同款 `pnpm exec ovsx`(项目锁定版本;其传递依赖 @vscode/vsce 的构建脚本 + # 已在 pnpm-workspace.yaml allowBuilds 审批)。注意:ovsx 对「已打包 VSIX」会忽略 --pre-release + # (预发布语义已由 package job 的 vsce package --pre-release 内嵌进 VSIX 元数据), + # 故此处不传 PRE_FLAG——与 vsce publish 的两端成对要求不同。 + - name: 发布到 Open VSX + if: ${{ vars.ENABLE_OVSX_PUBLISH == 'true' }} + run: pnpm exec ovsx publish --packagePath ./*.vsix + env: + OVSX_PAT: ${{ secrets.OVSX_PAT }} diff --git a/CHANGELOG.md b/CHANGELOG.md index edb208a..e1b2ca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,32 @@ ## [Unreleased] +## [0.0.18] - 2026-09-10 — Commit / Branches 标题栏重构 · 批量 Discard Changes · 分支级 Push · 双市场发布重启 + +自 v0.0.17 以来的积累(PR #119 / #120 / #121)。核心变更:**Commit 与 Branches 两视图的头部与标题栏整体重构**——Commit 头部两行 UI(Active Changelist 选择器、List/Tree 段控)上移 VS Code 标题栏,净省两行竖直空间,并新增以文件勾选集为范围的批量 Discard Changes;Branches 的 Push 下放分支右键(按分支推送的显式 refspec / `-u` 双路径)、Prune 上移标题栏、Merge… 收入「…」菜单。同时修复无效 codicon 致 Compare with Current Branch 图标渲染空白与文件列表树的 A11y 语义回归,并重启 Open VSX 发布通道(与 Marketplace 对称门控),覆盖 Cursor / Windsurf / VSCodium 等编辑器。完整用户视角叙述见 [Release Note v0.0.18](./docs/releases/v0.0.18.md)。 + +### Added + +- **Commit 视图标题栏新增 Discard Changes(最左 `$(discard)` 图标,批量撤销勾选文件)**:以文件列表勾选集为范围,单次 modal 确认(列示目标文件,超 10 个截断)后批量执行——未跟踪文件 `clean` 删除、已跟踪改动 `restore` 还原,语义与单文件右键 Discard Changes 一致;勾选集自 webview 单向同步 host 镜像(webview 仍为事实源),无勾选时给出提示,视图刷新经 `onDidChange → refreshAll` 驱动。同时 Set Active Changelist… 与 New Changelist… 两入口由标题栏图标位收入「…」菜单(`0_changelist` 组,居同步组之前)。 +- **Branches 视图分支右键新增 Push(推送指定分支)**:右键任一本地分支 → `hyperGit.pushBranch`——已配置上游时按显式 refspec 推送到追踪分支(本地名/上游名不一致亦正确),无上游时选定 remote 并以 `-u` 建立追踪(语义同全局 Push 的双路径),推送后刷新视图并提示。 + +### Changed + +- **Commit 视图头部两行 UI 上移 VS Code 标题栏(省两行竖直空间)**:① 「Active Changelist」下拉与 `⋯` 管理菜单合并为标题栏「…」菜单的 Set Active Changelist… 入口(New Changelist… 亦收入「…」)——点击弹出 QuickPick:各列表带文件计数、当前活动项预选,分隔线后并入 New / Rename / Delete Changelist… 操作(默认列表不可改名/删除);活动 changelist 名常驻视图副标题(标题「Commit」同行右侧,Default 活动列表时省略——无信息量不常驻)。② 文件列表 List ⇄ Tree 段控改为标题栏 `$(list-tree)`/`$(list-flat)` 互斥图标(交互同 Graph/Branches 切换范式),偏好移交 host `workspaceState` 按仓库持久化(`hyperGit.commit.dmode:`)——**原 webview state 中的旧 List/Tree 偏好一次性重置为默认 flat**(勾选集、目录折叠与提交草稿不受影响)。③ Select All 复选框吸顶于文件列表容器内首行(列表为空时随空态隐藏),原 `.cl-bar` 与 files-header 两行整体移除。 +- **Branches 视图标题栏重排**:① 原 Push 图标下放至分支右键菜单(见上 Added;全局 Push 仍可用 Commit 视图标题栏与命令面板)。② 「Prune Deleted Remote Branches」由「…」菜单上移至标题栏 `$(sync-ignored)` 图标(`navigation@4`,即原 Push 位;该图标全扩展唯一,既区别于同栏 `$(refresh)`/`$(sync)` 同步类,也不与右键删除类 `$(trash)` 混淆)。③ 「Merge…」由标题栏图标收入「…」菜单(同步组内,Update Project 与 New Tag… 之间),不再占用图标位。 +- **视图标题栏图标常驻引导**:VS Code 默认仅在 hover/聚焦视图时显示标题栏动作(平台行为,`ViewPane` 依 `workbench.view.alwaysShowHeaderActions` 切换 CSS 类 `actions-always-visible`,扩展贡献点无法覆盖)。现提供双入口:命令面板 **Hyper Git: Always Show View Toolbar Icons** 随时开启,以及首次激活的一次性提示(Always Show 写入全局设置并即时生效、Don't Show Again 不再打扰;设置已为 `true` 时不提示)。 +- **CI 发布渠道重启双市场**:`publish` job 增回 Open VSX 发布步骤(`pnpm exec ovsx publish --packagePath ./*.vsix`,由仓库变量 `ENABLE_OVSX_PUBLISH` 门控 + `OVSX_PAT` 凭证,复用 `package` job 的同一枚 VSIX、共享 production 审批门),覆盖 Cursor / Windsurf / VSCodium 等 Open VSX 系编辑器;README 安装渠道同步加回 Open VSX。发布决策沿革见 [发布策略调研](./docs/research/04-publishing-cicd.md)。 + +### Fixed + +- **Compare with Current Branch 菜单图标渲染为空白方块**:`$(compare)` 并非有效 codicon id(自 v0.0.17 起即如此),修正为 `$(git-compare)`。无效 codicon id 在 VS Code 中静默渲染为空白、无任何告警,现以 [`tests/unit/codicon-validity.test.ts`](./tests/unit/codicon-validity.test.ts) 白名单护栏(取自 `microsoft/vscode-codicons` 的 `mapping.json`)全量校验命令与 submenu 图标,杜绝同类漏出——该护栏在本版开发期即拦下另一处新引入的 `$(sync-ignore)`(已修正为 `$(sync-ignored)`,见 Changed 中的 Branches 视图标题栏重排)。 +- **Commit 文件列表树语义回归**:吸顶 Select All 行引入后打断了树语义——`role="tree"` 与 `aria-label` 回归保留于可聚焦的 `#files` 容器,吸顶行 `#files-selectall` 与行区容器 `#files-rows` 降为 `role="presentation"`;并补 `scroll-margin-top`,避免键盘导航的 `scrollIntoView` 把首行滚到吸顶行之下被遮挡。 + +### Docs + +- **`.agents` 文档去重**:移除仓库内 `docs/.agents/browser-validation.md` 与 `docs/.agents/reference-specifications.md` 两份副本,统一以全局 `~/.agents/docs/` 为单一事实源,并清理 [知识索引](./docs/.agents/knowledge-map.md) 与 [文档中心](./docs/README.md) 中的索引死链。 +- **特性与调研文档同步标题栏迁移口径**:[Commit 视图整合](./docs/features/commit-view-consolidation.md)(QuickPick 形态的 changelist 管理、`hyperGit.commit.dmode:` 持久化键、批量 Discard 验证步骤)、[变更文件目录树](./docs/features/file-list-group-by-directory.md);[发布策略调研](./docs/research/04-publishing-cicd.md) §2.3 由「单市场」再次修订为「双市场」,保留完整决策沿革。 + ## [0.0.17] - 2026-09-09 — Log 提交详情常驻面板 · VS Code 1.136 最佳实践对齐 · 稳定性与体验修复 自 v0.0.16 以来的积累(PR #115 / #116)。核心变更:**Log 提交详情由悬停浮层改为右侧常驻面板**(三区可拖拽分割线、窄视图自适应堆叠),并全面对齐 VS Code 1.136 控件 / 交互 / API 最佳实践(修复 9 项缺陷、Webview 控件全面主题化、能力声明与设置治理);新增长时 git 操作进度反馈、入门导览与默认键位、Stash / Worktrees 批量操作、分支 / 标签名即时校验等体验增强。完整用户视角叙述见 [Release Note v0.0.17](./docs/releases/v0.0.17.md)。 @@ -52,7 +78,7 @@ - 新增 [Log 提交详情面板](./docs/features/log-commit-detail-panel.md)(布局分栏 / 交互数据流 Mermaid、边缘 Case、`forHash` 守卫语义);移除被替代的 `docs/features/log-commit-tooltip.md`,同步更新 [知识索引](./docs/.agents/knowledge-map.md)、[文档中心](./docs/README.md) 与 [变更文件目录树](./docs/features/file-list-group-by-directory.md) 表述。 - - 2026-09-01 — 多根工作区仓库切换 · 分支分组树 · Agentic Git 预置 · 面板布局与 Diff 修复 +## [0.0.16] - 2026-09-01 — 多根工作区仓库切换 · 分支分组树 · Agentic Git 预置 · 面板布局与 Diff 修复 自 v0.0.14 以来的全量积累(承载 0.0.15 版本号预置——该版本号已就位但未单独发布,本版一并发布)。核心新特性:多根工作区多仓库切换、Branches 按 `/` 前缀分组树、Agentic Git 偏好配置与 Claude Code 配置预置(M5 前置铺垫)、Diff 缺失端修复与面板默认布局调整;工程侧升级 pnpm 12 与一批开发依赖。完整用户视角叙述见 [Release Note v0.0.16](./docs/releases/v0.0.16.md)。 diff --git a/README.md b/README.md index ff9a1f7..f6512c6 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ - **Manual (recommended for now)**: download `hyper-git-agentic-git-x.x.x.vsix` from [Releases](https://github.com/ThreeFish-AI/hyper-git/releases) → run `Extensions: Install from VSIX` in the Command Palette. - **VS Code Marketplace**: search for `Hyper Git - Agentic Git`. +- **Open VSX**: search for `Hyper Git - Agentic Git` (covers Cursor / Windsurf / VSCodium and other Open VSX-based editors). - **Requirements**: VS Code ≥ 1.85.0 with the built-in Git extension enabled (`vscode.git`, bundled by default). Local Git repositories only — virtual / Web workspaces are not supported. ## Known Limitations @@ -95,7 +96,7 @@ pnpm exec vsce publish ``` - **Layering**: `engine/` (pure logic) → `adapter/` (the sole layer touching the vscode API, including the hand-rendered `adapter/webview/` UI); `agent/` is injected into `engine/` via interfaces and never depends on the UI; `shared/protocol.ts` is the single source of truth for the Webview ↔ Host contract. -- **Release**: a `v*` tag triggers CI to produce a GitHub Release (with the `.vsix` attached; the body is drawn from [`docs/releases/`](./docs/releases/README.md)) and publish to the VS Code Marketplace (gated by the `ENABLE_MARKETPLACE_PUBLISH` variable; `rc` tags go to the pre-release channel). +- **Release**: a `v*` tag triggers CI to produce a GitHub Release (with the `.vsix` attached; the body is drawn from [`docs/releases/`](./docs/releases/README.md)) and publish the same VSIX to both the VS Code Marketplace (gated by the `ENABLE_MARKETPLACE_PUBLISH` variable) and Open VSX (gated by `ENABLE_OVSX_PUBLISH`); `rc` tags go to the pre-release channel. - Package management and scripts standardize on `pnpm` (per the [AGENTS.md](./AGENTS.md) engineering conventions). Version history is tracked in the [Changelog](./CHANGELOG.md). See the [documentation hub](./docs/README.md) for the full docs. --- diff --git a/docs/.agents/browser-validation.md b/docs/.agents/browser-validation.md deleted file mode 100644 index dd0f4f3..0000000 --- a/docs/.agents/browser-validation.md +++ /dev/null @@ -1,458 +0,0 @@ -# 浏览器验证协议 (Browser Validation Protocol) - -> **文档定位**:本文是 AI Agent 浏览器验证策略的**唯一详尽来源 (Single Source of Truth)**。[AGENTS.md › Browser Validation Protocol](../../AGENTS.md) 中仅保留摘要级约束,协议实体以本文为准。 - ---- - -## 1. 概述 - -### 1.1 适用范围 - -本协议规范 AI Agent(Claude Code、Codex 等)在以下浏览器操作场景中的行为准则: - -- **即时验证**:Agent 在开发过程中对 UI 功能、页面渲染、网络请求的实时校验; -- **E2E 回归测试**:通过 Playwright 等框架执行端到端自动化测试; -- **OAuth / SSO 链路验证**:涉及第三方 OAuth(如 Google OAuth)或内部 SSO 的认证流程检测。 - -### 1.2 核心不变量 (Invariant) - -> **唯一信任源:真实用户登录态。** -> -> Agent **不得**自行完成、绕过或模拟任何 OAuth / SSO 认证流程。所有登录态均来源于用户已认证的浏览器,登录动作由用户在自有浏览器内手动完成。 - -### 1.3 术语约定 - -| 术语 | 定义 | -| ------------------ | ------------------------------------------------------------------------------------------------------- | -| **用户浏览器** | 用户日常使用的 Chrome,包含已登录的 IdP 账号、扩展、书签等完整用户数据 | -| **Sandbox 浏览器** | 无用户数据的隔离浏览器实例(如 Playwright 默认 `chromium.launch()` 或新建空白 Chrome profile) | -| **A 类场景** | 依赖真实用户登录态的验证场景(OAuth / SSO / IdP 服务交互),**必须**通过 `claude-in-chrome` 执行 | -| **B 类场景** | 不依赖第三方 OAuth 的本地隔离场景(dev-cookie 注入、`storageState` 复用),可通过 Playwright 等框架执行 | -| **IdP** | Identity Provider,身份提供商(如 Google、GitHub、Okta 等),负责签发认证凭证 | -| **Tab Group** | `claude-in-chrome` 扩展为每个 Agent 会话创建的隔离标签组,用于管理浏览器标签页 | - ---- - -## 2. 问题域 - -当项目采用第三方 OAuth / SSO 认证流时,Agent 在 Sandbox 浏览器中访问受保护页面会被 IdP 重定向至其登录端点。由于 Sandbox 浏览器不持有任何有效会话,认证流程会被同意屏、风控策略或多因素验证拦截[[3]](#ref3),导致验证链路中断。 - -以下绕过手段均**不可行**且**被明确禁止**: - -| 手段 | 失败原因 | -| --------------------------- | ---------------------------------------------------------------------------------- | -| 自动填充密码 / 验证码 | IdP 风控基于设备指纹、IP、User-Agent 等多维信号检测异常会话[[2]](#ref2) | -| 跨浏览器复制 `storageState` | Cookie 与会话令牌绑定浏览器上下文,跨实例注入触发会话无效化 | -| Agent 代理完成 OAuth 同意屏 | 违反 AI Agent 安全准则中敏感凭证不入 chat 的硬性要求 | - ---- - -## 3. 设计约束 - -### 3.1 目标 - -所有依赖登录态的浏览器验证统一通过 `mcp__claude-in-chrome__*` 接入用户**常用 Chrome**,复用**真实用户**的已认证会话态。用户仅需在自有浏览器中手动完成一次登录。 - -### 3.2 非目标 - -- 不实现密码自动填充 / 验证码自动接收 / CAPTCHA 自动求解; -- 不在 Sandbox / 空白 profile / 模拟身份下处理 OAuth / SSO 登录跳转; -- 不在 CI / 共享环境中长期托管真实账号 `storageState`; -- 不为多账号热切换提前抽象 profile manager(YAGNI)。 - ---- - -## 4. 架构决策 - -### 4.1 驱动选型 - -**唯一驱动:`mcp__claude-in-chrome__*`(Claude in Chrome 扩展)。** - -选型依据: - -1. 通过 Chrome 扩展直接接入用户日常使用的 Chrome,共享完整设备指纹与登录历史,规避 IdP 风控拦截; -2. 无需用户手动添加 `--remote-debugging-port` 启动参数,扩展安装后自动建立连接,零额外配置; -3. 提供 Tab Group 隔离机制,每个 Agent 会话拥有独立标签组,互不干扰; -4. 同时支持视觉交互(`computer` 截图 + 鼠标键盘)与无障碍树(`read_page`)双通道,适应不同验证需求; -5. 内置批量操作(`browser_batch`)与 GIF 录制(`gif_creator`),提升验证效率与文档化能力。 - -> **A 类(交互)vs B 类(自治)的驱动分工**:上述"唯一驱动"针对 **A 类交互场景**——由开发者或 Agent 在**真人监督**的会话中验证 OAuth/SSO/认证态 UI,复用用户真实 Chrome 登录态。而 **B 类自治场景**([Routine](../concepts/039-the-routine-system.md) 后台 Claude Code 子进程:无真人、无桌面、headless)无法使用 claude-in-chrome(其需可见桌面浏览器 + 人工完成登录/CAPTCHA,且非 command/url 形态、无法注入 mcp_config)。为此,系统将 **Playwright MCP(`@playwright/mcp`,headless · isolated)内置为全系统默认浏览器操作 MCP**,经单一注入点 `builtin_tools(claude_code).config.mcp_config` provision 至所有 Routine 运行时,用于浏览器实机回归验证;鉴权回归复用本仓 dev-cookie storageState 旁路(§9,**非** IdP storageState 复制)。选型论证见 [浏览器操作 MCP 调研](../research/120-browser-automation-mcp.md),集成与使用见 [浏览器操作 MCP 集成方案](../concepts/design/browser-automation-mcp-integration.md)。 - -### 4.2 工具职责矩阵 - -| 维度 | `mcp__claude-in-chrome__*`(**唯一驱动**) | `mcp__playwright__*`(**仅限 B 类场景**) | -| ------------ | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -| 浏览器实例 | 用户常用 Chrome(扩展接入,共享真实 profile) | Playwright 自启动 Chromium(独立 Sandbox) | -| 登录态来源 | 用户原生已登录 Chrome;扩展自动共享会话 | 默认空 profile,需 `storageState` / `userDataDir` 注入 | -| OAuth 兼容性 | ✅ 高(同设备指纹 / 同登录历史) | ❌ 禁止(触发 IdP 风控拦截) | -| 适用场景 | A 类:Agent 即时验证、OAuth/SSO 链路、IdP 服务交互、UI 回归 | B 类:① E2E setup project 人工登录后 `storageState` 复用;② 本地 dev-cookie 注入 | -| 凭证安全语义 | 用户全程在自有浏览器内完成敏感操作,不持久化任何凭证副本 | `storageState` / `userDataDir` 仅落本地,须 `.gitignore` 保护 | -| 额外能力 | `gif_creator` 录制验证过程;`browser_batch` 批量操作;`find` 自然语言元素定位 | 完整 Playwright API(测试框架级控制) | - -### 4.3 核心工具速查 - -| 功能 | 工具 | 说明 | -| ---------- | ---------------------------------- | --------------------------------------------------------- | -| 连通性检查 | `tabs_context_mcp` | 获取当前 Tab Group 信息,`createIfEmpty: true` 可自动创建 | -| 新建标签页 | `tabs_create_mcp` | 在当前 Tab Group 中创建新标签 | -| 导航 | `navigate` | 跳转至指定 URL | -| 页面读取 | `read_page` | 获取无障碍树(结构化,适合交互操作) | -| 视觉截图 | `computer` (screenshot) | 截取当前页面或指定区域截图(适合视觉验证) | -| 元素查找 | `find` | 以自然语言描述查找页面元素 | -| 表单填写 | `form_input` / `browser_fill_form` | 设置表单元素值 | -| JS 执行 | `javascript_tool` | 在页面上下文执行 JavaScript | -| 点击交互 | `computer` (left_click) | 通过坐标点击页面元素 | -| 键盘输入 | `computer` (type) / `type_text` | 键入文本 | -| 控制台日志 | `read_console_messages` | 读取浏览器控制台输出 | -| 网络请求 | `read_network_requests` | 读取页面网络请求 | -| 批量操作 | `browser_batch` | 单次调用顺序执行多个操作 | -| GIF 录制 | `gif_creator` | 录制验证过程并导出 | -| 关闭标签 | `tabs_close_mcp` | 关闭指定标签 | - -### 4.4 场景路由决策 - -```mermaid -flowchart TD - Start["需要浏览器验证"] --> NeedAuth{"涉及登录态?
(OAuth / SSO / 真实用户)"} - - NeedAuth -->|"A 类"| CiC["mcp__claude-in-chrome__*
接入用户 Chrome"] - CiC --> Reachable{"tabs_context_mcp
可达?"} - Reachable -->|是| TabCreate["tabs_create_mcp
创建验证标签"] - TabCreate --> OK["✅ 直接使用
(已含真实登录态)"] - Reachable -->|否| Recover["引导用户:
1. 确认 Chrome 已启动
2. 确认 claude-in-chrome 扩展已安装启用
3. 点击扩展图标完成连接"] - Recover --> OK - - NeedAuth -->|"B 类"| BRoute{"隔离场景分流"} - BRoute --> E2E["E2E setup project
真人一次性登录 →
storageState 复用"] - BRoute --> DevCK["本地 dev-cookie 注入
(不接触 OAuth)"] - - NeedAuth -.->|"严禁"| Forbid["❌ Sandbox 浏览器
跳转 IdP 同意屏"] - - style Start fill:#0f172a,stroke:#60a5fa,color:#e2e8f0 - style CiC fill:#0b1f2a,stroke:#34d399,color:#ecfdf5 - style OK fill:#052e16,stroke:#22c55e,color:#dcfce7 - style Recover fill:#1f1d0a,stroke:#f59e0b,color:#fef3c7 - style E2E fill:#0b1f2a,stroke:#3b82f6,color:#e6edf3 - style DevCK fill:#0b1f2a,stroke:#3b82f6,color:#e6edf3 - style NeedAuth fill:#1f2937,stroke:#a78bfa,color:#ede9fe - style Reachable fill:#1f2937,stroke:#a78bfa,color:#ede9fe - style BRoute fill:#1f2937,stroke:#a78bfa,color:#ede9fe - style Forbid fill:#3b0a0a,stroke:#f87171,color:#fee2e2 -``` - ---- - -## 5. 安全模型 - -### 5.1 禁止行为 (Forbidden Actions) - -| 编号 | 禁止行为 | 违反原则 | -| ---- | ------------------------------------------------------------ | ---------------- | -| F-1 | 在 Sandbox 浏览器中跳转 IdP 同意屏 | 核心不变量 §1.2 | -| F-2 | 以模拟用户或第三方账号替代真实用户完成登录态验证 | 核心不变量 §1.2 | -| F-3 | 要求用户在 chat 中粘贴密码、Cookie 或一次性验证码 | 敏感信息保护准则 | -| F-4 | 读取、复制或传输用户密码 / 验证码 / Refresh Token | 敏感信息保护准则 | -| F-5 | 将 `storageState` / `cookies` / `userDataDir` 提交至版本控制 | 凭证泄漏防护 | - -### 5.2 凭证生命周期管理 - -```mermaid -flowchart LR - subgraph 生成["凭证生成"] - direction TB - A1["用户在自有 Chrome
手动完成 OAuth 登录"] - A2["E2E setup project
导出 storageState"] - A3["项目签名脚本
生成 dev-cookie"] - end - - subgraph 存储["本地存储"] - direction TB - B1["storageState JSON"] - B2["dev-cookie JSON"] - B3["Chrome 用户 profile
(claude-in-chrome 共享)"] - end - - subgraph 防护["泄漏防护"] - direction TB - C1[".gitignore 屏蔽"] - C2[".dockerignore 屏蔽"] - C3["禁止 CI 挂载
真实账号 storageState"] - end - - A1 --> B3 - A2 --> B1 - A3 --> B2 - B1 & B2 --> C1 & C2 - B3 --> C3 - - style 生成 fill:#0f172a,stroke:#60a5fa,color:#e2e8f0 - style 存储 fill:#1f2937,stroke:#a78bfa,color:#ede9fe - style 防护 fill:#052e16,stroke:#22c55e,color:#dcfce7 -``` - -### 5.3 事件响应 - -若怀疑会话凭证泄漏: - -1. 立即通过 IdP 的设备管理页面撤销相关会话(如 [Google 设备管理](https://myaccount.google.com/device-activity)); -2. 删除本地所有 `storageState` 与 `userDataDir` 产物; -3. 重新执行 §6 连通性自检流程。 - ---- - -## 6. 运维规程:连通性自检 - -每次会话**首次**需要登录态浏览前,Agent **必须**按以下顺序执行自检。任一步骤失败应**立即中止**并向用户报告现象,**禁止**以"换 Sandbox 重试"等方式暗箱降级。 - -### Step 1 — 扩展接入与 Tab Group 初始化 - -```ts -// 1) 获取 Tab Group 上下文(自动创建若不存在) -mcp__claude-in-chrome__tabs_context_mcp({ createIfEmpty: true }); - -// 2) 创建验证标签 -mcp__claude-in-chrome__tabs_create_mcp(); - -// 3) 导航至 IdP 账号页验证登录态(以 Google 为例) -mcp__claude-in-chrome__navigate({ url: "https://myaccount.google.com", tabId: }); -``` - -**验收条件**: - -- `tabs_context_mcp` 返回有效 Tab Group 信息(包含至少一个 tab ID); -- 导航至 IdP 账号页后,页面内容包含用户账号标识(如邮箱地址),可通过 `get_page_text` 或 `read_page` 确认。 - -**异常处理**: - -| 现象 | 处置 | -| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `tabs_context_mcp` 返回空或报错 | 引导用户确认:① Chrome 已启动并处于前台;② claude-in-chrome 扩展已安装且已启用;③ 点击扩展图标完成连接,**禁止**改用 Sandbox | -| 接入成功但 IdP 账号页未识别用户 | 引导用户在该 Chrome 中手动登录目标账号(Agent 不接触密码 / 验证码) | - -> [!NOTE] -> 若用户有多个 Chrome profile,需确保 claude-in-chrome 扩展安装在包含目标 IdP 账号的 profile 中。 - -### Step 2 — 应用 OAuth 链路打通 - -```ts -// 替换为项目实际的 OAuth 登录入口 -mcp__claude-in-chrome__navigate({ - url: "http://localhost:/", - tabId: , -}); -``` - -**前置条件**:本地 dev server(前端 + 后端)已启动。 - -**验收条件**:无需重新输入密码,自动回跳应用首页并完成会话写入。 - -**异常处理**:检查项目配置中 OAuth callback URL 是否与当前运行端口一致。 - -> [!IMPORTANT] -> Step 1 与 Step 2 之间应保持 **≥ 3 秒**间隔,避免短时高频跳转触发 IdP CAPTCHA 验证。 - ---- - -## 7. 常用操作模式 - -### 7.1 页面验证 - -```ts -// 方式 A:无障碍树读取(推荐用于交互操作) -mcp__claude-in-chrome__read_page({ tabId: }); - -// 方式 B:视觉截图(推荐用于 UI 验证) -mcp__claude-in-chrome__computer({ action: "screenshot", tabId: }); -``` - -### 7.2 元素交互 - -```ts -// 自然语言查找元素 -mcp__claude-in-chrome__find({ query: "登录按钮", tabId: }); - -// 通过坐标点击 -mcp__claude-in-chrome__computer({ action: "left_click", coordinate: [x, y], tabId: }); - -// 表单填写 -mcp__claude-in-chrome__form_input({ ref: "", value: "", tabId: }); -``` - -### 7.3 调试辅助 - -```ts -// 读取控制台日志 -mcp__claude-in-chrome__read_console_messages({ tabId: , pattern: "error|warning|" }); - -// 读取网络请求 -mcp__claude-in-chrome__read_network_requests({ tabId: , urlPattern: "/api/" }); - -// 执行 JavaScript -mcp__claude-in-chrome__javascript_tool({ action: "javascript_exec", text: "document.title", tabId: }); -``` - -### 7.4 批量操作 - -单次调用顺序执行多个操作,减少回合开销: - -```ts -mcp__claude-in-chrome__browser_batch({ - actions: [ - { name: "navigate", input: { url: "http://localhost:/login", tabId: } }, - { name: "computer", input: { action: "screenshot", tabId: } }, - { name: "find", input: { query: "登录按钮", tabId: } }, - { name: "computer", input: { action: "left_click", coordinate: [x, y], tabId: } }, - ], -}); -``` - -### 7.5 验证过程录制 - -对于需要留档或分享的验证流程,可使用 GIF 录制: - -```ts -// 开始录制 -mcp__claude-in-chrome__gif_creator({ action: "start_recording", tabId: }); - -// ... 执行验证操作 ... - -// 停止录制并导出 -mcp__claude-in-chrome__gif_creator({ - action: "export", - tabId: , - download: true, - filename: "validation-.gif", -}); -``` - ---- - -## 8. E2E 测试集成 - -### 8.1 会话复用工作流 - -Playwright E2E 测试通过 `setup` project 实现一次性人工登录后的会话复用[[1]](#ref1): - -```mermaid -sequenceDiagram - participant Dev as 开发者 - participant PW as Playwright Runner - participant Browser as Headed Chromium - participant FS as 本地文件系统 - - rect rgb(15, 23, 42) - Note over Dev,FS: Phase 1 — 一次性登录(有头模式) - Dev->>PW: 执行 setup project - PW->>Browser: 启动有头浏览器 - Browser-->>Dev: 弹出 IdP 登录窗口 - Dev->>Browser: 手动完成 OAuth 登录 - Browser-->>PW: page.context().storageState() - PW->>FS: 写入 .auth/user.json - end - - rect rgb(5, 46, 22) - Note over Dev,FS: Phase 2 — 测试执行(无头模式) - Dev->>PW: 执行测试 - PW->>Browser: 启动新 context(headless) - PW->>Browser: 注入 storageState - Browser->>Browser: 以已登录态访问应用 - end -``` - -### 8.2 会话失效与刷新 - -| 触发条件 | 应对措施 | -| ------------------------------- | -------------------------------------------------------------------- | -| IdP 会话过期 | 删除 `storageState` 文件后重跑 `setup` project(`--headed` 模式) | -| 后端会话存储重建 | 同上 | -| Cookie 域 / `SameSite` 属性变更 | 同上;建议 setup project 末尾增加 URL 断言确认登录成功(见 §10 R-2) | - -### 8.3 CI 环境注意事项 - -依赖真实登录态的集成测试需外部后端服务与有效认证密钥。CI 环境中建议: - -- 通过环境变量开关控制 authed project 的注册,缺失时自动跳过; -- 认证辅助模块不内联 secret(防止入库),env 缺失时 fail-fast 并指向本文档; -- 不依赖后端的 mocked spec 始终全跑,确保 CI 覆盖率。 - ---- - -## 9. Dev-Cookie 旁路方案 - -适用于**不经第三方 OAuth** 的本地 Agent 开发场景,通过项目自签 cookie 直接获取业务页面访问权限。 - -### 9.1 工作原理 - -项目提供签名脚本,基于共享密钥(如 `AUTH_TOKEN_SECRET`)生成合法的会话 cookie。该 cookie 与 OAuth 颁发的 cookie 格式相同,后端 token 解码逻辑不区分来源,因此可在本地开发中绕过 OAuth 流程。 - -### 9.2 注入方式 - -**方式 A — 通过 `claude-in-chrome` 运行时注入**(适用于 Agent 即时验证): - -```ts -// 1. 导航至应用同源页面(建立 cookie 作用域) -mcp__claude-in-chrome__navigate({ url: "http://localhost:/", tabId: }); - -// 2. 注入 dev-cookie -mcp__claude-in-chrome__javascript_tool({ - action: "javascript_exec", - text: `document.cookie = "=; path=/; SameSite=Lax"; document.cookie.includes("")`, - tabId: , -}); - -// 3. 导航至目标页面 -mcp__claude-in-chrome__navigate({ url: "http://localhost:/", tabId: }); -``` - -**方式 B — `storageState` 持久化**(推荐用于 spec 复用): - -```bash -# 1. 生成 storageState 文件 -node --storage-state - -# 2. 以 storageState 运行测试 -PLAYWRIGHT_STORAGE_STATE= \ -npx playwright test -``` - -### 9.3 注意事项 - -| 项目 | 说明 | -| -------------------------- | --------------------------------------------------------------------------------------------- | -| `localhost` vs `127.0.0.1` | 若 dev server 仅监听 `localhost`(IPv6),浏览器导航**必须**使用 `localhost` 而非 `127.0.0.1` | -| `httpOnly` 属性 | 通过 `document.cookie` 注入的 cookie 为非 httpOnly,但浏览器仍随后续同源请求发送 | -| Cookie 持久性 | 浏览器 context 在导航间保持 cookie,但 context 关闭后丢失 | - ---- - -## 10. 风险矩阵 - -| 编号 | 风险 | 影响 | 概率 | 对策 | -| ---- | ------------------- | ---------------------------------------------- | ---- | --------------------------------------------------------------------------- | -| R-1 | IdP 风控误报 | 短时高频跳转触发 CAPTCHA / 邮箱验证 | 中 | 自检各步骤间保持 ≥ 3s 间隔;避免自动化循环中重复触发 OAuth | -| R-2 | `storageState` 漂移 | Cookie 域 / `SameSite` 变更后旧 state 静默失效 | 低 | setup project 末尾增加 URL 断言确认登录成功(如排除 `/auth/login` 路径) | -| R-3 | 多账号干扰 | 浏览器同时登录多个 IdP 账号致 OAuth 选择器弹出 | 低 | 自检失败时将账号选择器纳入指引,由用户在浏览器内显式选择目标账号 | -| R-4 | 凭证泄漏 | `storageState` 文件意外提交至版本控制 | 低 | `.gitignore` + `.dockerignore` 双重屏蔽;CI 禁止挂载真实账号 `storageState` | -| R-5 | 扩展连接中断 | claude-in-chrome 扩展与 Agent 会话断连 | 低 | 引导用户点击扩展图标重新建立连接;必要时重启 Chrome | - ---- - -## 附录. 协议演进记录 - -| 日期 | 变更 | -| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| 2026-05-06 | 废弃浏览器扩展首选方案,统一收敛至 `mcp__chrome_devtools__*` 唯一驱动 | -| 2026-05-15 | 文档结构重组:引入术语约定、安全模型、风险矩阵;将项目特化案例移至附录 | -| 2026-05-15 | 协议泛化:移除项目特化描述,使协议可通用于所有需要浏览器验证的 Agent 行为规范 | -| 2026-05-16 | 驱动迁移:从 `mcp__chrome_devtools__*` 迁移至 `mcp__claude-in-chrome__*`,利用 Chrome 扩展实现零配置接入;新增常用操作模式章节;泛化所有项目特化引用 | -| 2026-06-06 | 厘清 A 类(claude-in-chrome 交互)/ B 类(自治)分工;将 **Playwright MCP(`@playwright/mcp`)内置为全系统默认浏览器操作 MCP**,经 `builtin_tools(claude_code).config.mcp_config` 单一注入点 provision 至所有 Routine 运行时,用于浏览器实机回归验证(见 [集成方案](../concepts/design/browser-automation-mcp-integration.md)) | - ---- - -## References (IEEE) - -[1] Microsoft, "Authentication," _Playwright Documentation_, 2025. [Online]. Available: https://playwright.dev/docs/auth. - -[2] OWASP Foundation, "Session Management Cheat Sheet," _OWASP Cheat Sheet Series_, 2024. [Online]. Available: https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html. - -[3] D. Hardt, "The OAuth 2.0 Authorization Framework," _IETF RFC 6749_, Oct. 2012, doi: 10.17487/RFC6749. diff --git a/docs/.agents/knowledge-map.md b/docs/.agents/knowledge-map.md index ff492da..5f46f6b 100644 --- a/docs/.agents/knowledge-map.md +++ b/docs/.agents/knowledge-map.md @@ -11,8 +11,7 @@ ## Agents 知识库(本目录) - [Issue 记录](./issue.md) — 跨上下文问题处理经验(表因 / 根因 / 处理 / 防范)。 -- [引用规范 IEEE](./reference-specifications.md) — 文献引用格式与上标锚定。 -- [浏览器验证协议](./browser-validation.md) — OAuth/SSO 红线与 E2E 验证协议。 +- 引用规范(IEEE)与浏览器验证协议:以全局 `~/.agents/docs/` 为单一事实源(`reference-specifications.md` / `browser-validation.md`),仓库内不再留副本。 ## 项目文档(docs/) - [文档中心](../docs/README.md) — 文档与调研资产总索引。 @@ -29,7 +28,7 @@ - [工程实施方案](../docs/architecture/engineering-plan.md) — 路径 B 架构 + M0-M5 里程碑(**开发蓝图**)。 - [Git 功能矩阵](../docs/requirements/idea-feature-matrix.md) — 56 功能点 / 8 组(**验收基线**,参考 IDEA 等成熟实现)。 - [调研报告](../docs/research/README.md) — SCM 集成 / 工程蓝图 / 发布 CI / AI 接缝四路循证报告。 -- [发布说明](../releases/README.md) — 各正式版 Release Notes(GitHub Release 正文单一事实源;最新 [v0.0.17](../releases/v0.0.17.md))。 +- [发布说明](../releases/README.md) — 各正式版 Release Notes(GitHub Release 正文单一事实源;最新 [v0.0.18](../releases/v0.0.18.md))。 ## 架构分层(src/) > 依赖方向单向:`UI → Adapter → Engine`;`Agent` 以接口注入 `Engine`/`CommitPipeline`,不反向依赖 UI。 diff --git a/docs/.agents/reference-specifications.md b/docs/.agents/reference-specifications.md deleted file mode 100644 index 896b866..0000000 --- a/docs/.agents/reference-specifications.md +++ /dev/null @@ -1,16 +0,0 @@ -# Reference Specifications (IEEE) - -> **模版准则**:[编号] 作者缩写. 姓, "文章标题," _刊名/会议名缩写 (斜体)_, 卷号, 期数, 页码, 年份. - -```latex -[1] A. Author, B. Author, and C. Author, "Title of paper," *Abbrev. Title of Journal*, vol. X, no. Y, pp. XX–XX, Year. -``` - -**引用实践** - -- **文内锚定**:采用标准上标链接形式:`描述内容[[1]](#ref1)`。 -- **文献索引**:底层采用 HTML 锚点 `id` 实现跳转稳定性。 - -```latex -[1] A. Vaswani et al., "Attention is all you need," Adv. Neural Inf. Process. Syst., vol. 30, pp. 5998–6008, 2017. -``` diff --git a/docs/README.md b/docs/README.md index e43d718..e311948 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,14 +14,14 @@ - [Agentic Git 偏好配置](./features/agentic-git-preferences.md) — Agentic Git 预置:Base Branch + Commit / Create PR / Review 三项 agent 指令偏好(多行文本域 + 内置默认模板;M5 前置铺垫)。 ## 发布说明 -- [Release Notes 目录](./releases/README.md) — 各正式版发布说明(GitHub Release 正文单一事实源);最新:[v0.0.17 Log 提交详情常驻面板 · VS Code 1.136 最佳实践对齐](./releases/v0.0.17.md)。 +- [Release Notes 目录](./releases/README.md) — 各正式版发布说明(GitHub Release 正文单一事实源);最新:[v0.0.18 Commit / Branches 标题栏重构 · 批量 Discard Changes · 分支级 Push](./releases/v0.0.18.md)。 ## 调研报告(循证依据) - [02 · VS Code SCM API 与 vscode.git 集成路径](./research/02-vscode-scm-integration.md) — 路径 B 决策依据、SCM 稳定/proposed API 边界、changelist 模型映射。 - [03 · VS Code 扩展工程蓝图](./research/03-extension-blueprint.md) — 技术栈决策、工程骨架、IDEA→VS Code UI 表面映射表。 -- [04 · 发布策略 + CI/CD](./research/04-publishing-cicd.md) — VS Code Marketplace 发布、CI 矩阵、版本治理、安全。 +- [04 · 发布策略 + CI/CD](./research/04-publishing-cicd.md) — 双市场(Marketplace + Open VSX)发布、CI 矩阵、版本治理、安全。 - [05 · AI Agent 架构预留](./research/05-ai-agent-seams.md) — AI 接缝(ILlmProvider 等)+ 借鉴 JetBrains CheckinHandler 责任链设计 + 渐进式引入路线。 ## 协作与规范 - [AGENTS.md](../AGENTS.md) — 协作协议与工程行为准则。 -- [知识索引](./.agents/knowledge-map.md) · [Issue 记录](./.agents/issue.md) · [引用规范 IEEE](./.agents/reference-specifications.md)。 +- [知识索引](./.agents/knowledge-map.md) · [Issue 记录](./.agents/issue.md)。引用规范(IEEE)与浏览器验证协议以全局 `~/.agents/docs/` 为准(见 AGENTS.md)。 diff --git a/docs/features/commit-view-consolidation.md b/docs/features/commit-view-consolidation.md index e3ec225..fa0680c 100644 --- a/docs/features/commit-view-consolidation.md +++ b/docs/features/commit-view-consolidation.md @@ -8,8 +8,8 @@ |---|---| | 文件单击看 Diff | 文件行单击(勾选框除外)→ `commit/openFile` → 复用 `hyperGit.openDiff` | | 单文件右键菜单 | 文件行右键 → `commit/fileMenu` → 原生 `showQuickPick`:Open Diff / Move to Changelist… / Show History / Stage·Unstage Hunks… / Add to .gitignore / Discard Changes(复用既有命令,含 discard 确认框) | -| Changelist 管理 | 头部 `` + `⋯` 菜单行已上移,省一行竖直空间) | +| Git 操作工具栏 | 标题栏(`view/title`)承接 refresh / push / pull / fetch / pushDialog / updateProject / createPatch / applyPatch;最左 `$(discard)` 图标 Discard Changes 批量撤销勾选文件(`discardChanges` 单/多统一路径);New Changelist… 收入「…」菜单;去掉与 webview 内 Commit·Commit&Push 按钮重复的两项 | | 活动栏未提交数角标 | 迁至 Commit `WebviewView.badge`(容器图标角标 = 各视图 badge 之和,总数不变) | ## 关键设计(复用与最小回归) @@ -22,16 +22,16 @@ ## 交互范围取舍 -Commit 视图文件区以**活动 Changelist** 为提交目标(勾选集即提交范围);非活动 Changelist 通过头部下拉切换查看。相较旧树「同屏并列多 changelist」少了一处低频便利,换取窄侧栏下更聚焦的提交工作流。 +Commit 视图文件区以**活动 Changelist** 为提交目标(勾选集即提交范围);非活动 Changelist 通过标题栏「…」菜单 Set Active Changelist… 弹 QuickPick 切换,活动列表名常驻 `view.description` 副标题(Default 省略)。相较旧树「同屏并列多 changelist」少了一处低频便利,换取窄侧栏下更聚焦的提交工作流。 ## 实现 - 视图与菜单:[`package.json`](../../package.json)(删 `hyperGit.changes` 视图 / viewsWelcome / 全部 `view == hyperGit.changes` 菜单;`view/title` 迁至 `view == hyperGit.commit`)。 -- 视图主体:[`adapter/webview/commit-webview.ts`](../../src/adapter/webview/commit-webview.ts)(groups/switcher/文件交互/菜单/角标/目录树)。 -- 命令:[`adapter/commands.ts`](../../src/adapter/commands.ts)(`resolveChange` + 签名重构)。 +- 视图主体:[`adapter/webview/commit-webview.ts`](../../src/adapter/webview/commit-webview.ts)(groups/文件交互/菜单/角标/目录树;changelist 切换与管理、List/Tree 切换已上移标题栏,mode 以 host 为事实源按仓库持久化)。 +- 命令:[`adapter/commands.ts`](../../src/adapter/commands.ts)(`resolveChange` + 签名重构;`setActiveChangelist` QuickPick 承载切换与管理;`discardChanges` 单/多统一路径:归一数组 + modal 批量确认 + clean/restore 各一次调用)。 - 装配:[`extension.ts`](../../src/extension.ts)(移除 tree/changesView,角标改由 `commitView.updateBadge`)。 -- 协议:[`shared/protocol.ts`](../../src/shared/protocol.ts)(`CommitChangelistItem`、`commit/openFile|fileMenu|setActive|changelistMenu`)。 +- 协议:[`shared/protocol.ts`](../../src/shared/protocol.ts)(`commit/openFile|fileMenu|checkedChanged`;changelist 操作消息已随标题栏上移移除,`checkedChanged` 为勾选集 webview → host 单向镜像,供标题栏批量 Discard)。 ## 验证 -`pnpm run check-types && pnpm run lint && pnpm run test:unit` 全绿;Extension Development Host(F5):无 CHANGES 视图;Commit 顶部可切换/管理 changelist;文件单击开 Diff、右键出菜单(含 discard 确认);标题栏 Git 动作可用;活动栏角标随未提交数增减;无 Git 仓库时降级不崩溃。 +`pnpm run check-types && pnpm run lint && pnpm run test:unit` 全绿;Extension Development Host(F5):无 CHANGES 视图;标题栏最左 `$(discard)` 图标按 modal 确认批量撤销勾选文件(未跟踪删除、已跟踪还原,无勾选时仅提示);「…」菜单可切换/管理 changelist(Set Active Changelist… QuickPick)与新建列表、副标题显示活动列表名(Default 时无副标题);文件单击开 Diff、右键出菜单(含 discard 确认);标题栏 Git 动作可用;活动栏角标随未提交数增减;无 Git 仓库时降级不崩溃。 diff --git a/docs/features/file-list-group-by-directory.md b/docs/features/file-list-group-by-directory.md index 1f3793b..733f215 100644 --- a/docs/features/file-list-group-by-directory.md +++ b/docs/features/file-list-group-by-directory.md @@ -29,14 +29,14 @@ flowchart LR | 建树路径 | 条目 `path`(仓库相对) | `CommitFileChange.path`(干净新路径,重命名归位到新目录) | | 复选框 | 有:叶子勾选 + **目录三态**级联;Select All 基于全量文件 | 无(只读浏览) | | 叶子单击 | 打开 Diff(`commit/openFile`) | 打开单文件 Diff(`log/openFile`,`data-path` 保持展示串不变) | -| 偏好持久化 | `setState({mode, collapsed, checked})` | host `workspaceState`(`hyperGit.log.dmode:`)+ webview `dcollapsed` 按仓库记忆 | -| 切换控件 | webview 内 List/Tree 段控 | Graph 标题栏 `$(list-tree)`/`$(list-flat)` 互斥图标(同 Branches 分组切换范式) | +| 偏好持久化 | host `workspaceState`(`hyperGit.commit.dmode:`)+ webview `setState` 按仓库记 `collapsed`/`checked` | host `workspaceState`(`hyperGit.log.dmode:`)+ webview `dcollapsed` 按仓库记忆 | +| 切换控件 | Commit 标题栏 `$(list-tree)`/`$(list-flat)` 互斥图标(context key `hyperGit.commit.tree`) | Graph 标题栏 `$(list-tree)`/`$(list-flat)` 互斥图标(同 Branches 分组切换范式) | ## 实现 - 引擎:[`engine/tree/file-tree.ts`](../../src/engine/tree/file-tree.ts)(+ [`tests/unit/file-tree.test.ts`](../../tests/unit/file-tree.test.ts),15 用例)。 - 协议:[`shared/protocol.ts`](../../src/shared/protocol.ts)(`FileTreeNode`;`CommitViewState.tree`;`log/commitFiles` payload `tree`)。 -- 渲染:[`commit-webview.ts`](../../src/adapter/webview/commit-webview.ts)(List/Tree 段控、`renderFlat`/`renderTree`、`makeLeafRow`、目录三态 `updateDirStates`)、[`log-webview.ts`](../../src/adapter/webview/log-webview.ts)(`renderDetails` 分派、`renderDetailNode`;Log 侧切换控件为 Graph 标题栏 `hyperGit.log.detailTree/detailFlat` 命令图标,模式经 `log/detailMode` 定向下发免整图重拉)。 +- 渲染:[`commit-webview.ts`](../../src/adapter/webview/commit-webview.ts)(`renderFlat`/`renderTree`、`makeLeafRow`、目录三态 `updateDirStates`;切换控件为 Commit 标题栏 `hyperGit.commit.detailTree/detailFlat` 命令图标,mode 以 host 为事实源随 `state` 整态下发)、[`log-webview.ts`](../../src/adapter/webview/log-webview.ts)(`renderDetails` 分派、`renderDetailNode`;Log 侧切换控件为 Graph 标题栏 `hyperGit.log.detailTree/detailFlat` 命令图标,模式经 `log/detailMode` 定向下发免整图重拉)。 ## 验证 diff --git a/docs/releases/README.md b/docs/releases/README.md index 6462bd7..246f858 100644 --- a/docs/releases/README.md +++ b/docs/releases/README.md @@ -8,6 +8,7 @@ | 版本 | 说明 | 日期 | |---|---|---| +| [v0.0.18](./v0.0.18.md) | Commit / Branches 标题栏重构 · 批量 Discard Changes · 分支级 Push · 双市场发布重启 | 2026-09-10 | | [v0.0.17](./v0.0.17.md) | Log 提交详情常驻面板 · VS Code 1.136 最佳实践对齐 · 稳定性与体验修复 | 2026-09-09 | | [v0.0.16](./v0.0.16.md) | 多根工作区仓库切换 · 分支前缀分组树 · Agentic Git 预置 · Diff 修复与面板布局优化 | 2026-09-01 | | [v0.0.14](./v0.0.14.md) | 修复 GRAPH 视图提交记录时间倒序(`--topo-order` → `--author-date-order`) | 2026-07-06 | diff --git a/docs/releases/v0.0.18.md b/docs/releases/v0.0.18.md new file mode 100644 index 0000000..bf383a7 --- /dev/null +++ b/docs/releases/v0.0.18.md @@ -0,0 +1,92 @@ +# Hyper Git - Agentic Git v0.0.18 — Commit / Branches 标题栏重构 · 批量 Discard Changes · 分支级 Push · 双市场发布重启 + +> 本版核心:**Commit 与 Branches 两视图的头部与标题栏整体重构**——Commit 头部两行 UI 上移 VS Code 标题栏(净省两行竖直空间),并新增以文件勾选集为范围的**批量 Discard Changes**;Branches 的 **Push 下放分支右键**(按分支推送)、Prune 上移标题栏、Merge… 收入「…」菜单。同时修复无效 codicon 致 Compare with Current Branch 图标渲染空白与文件列表树的 A11y 语义回归,并**重启 Open VSX 发布通道**,覆盖 Cursor / Windsurf / VSCodium 等编辑器。 + +--- + +## ✨ 亮点速览 + +- **Commit 头部两行 UI 上移标题栏**:Active Changelist 下拉与 `⋯` 管理菜单、List ⇄ Tree 段控全部迁入 VS Code 标题栏,Select All 改为文件列表内吸顶首行——净省两行竖直空间,文件列表可视区随之变长。 +- **批量 Discard Changes**:Commit 标题栏最左 `$(discard)` 图标,以文件列表勾选集为范围一次性撤销——单次确认对话框列示目标文件,未跟踪文件删除、已跟踪改动还原,语义与单文件右键 Discard Changes 完全一致。 +- **分支级 Push**:右键 Branches 视图中任一本地分支即可单独推送——已配置上游时按显式 refspec 推送(本地名与上游名不一致亦正确),无上游时选定 remote 并以 `-u` 建立追踪。 +- **Branches 标题栏重排**:Push 图标下放右键菜单,「Prune Deleted Remote Branches」上移标题栏 `$(sync-ignored)` 图标位,「Merge…」收入「…」菜单。 +- **活动 Changelist 管理改走 QuickPick**:各列表带文件计数、当前活动项预选,分隔线后并入 New / Rename / Delete;活动列表名常驻视图副标题(Default 时省略——无信息量不常驻)。 +- **标题栏图标常驻引导**:VS Code 默认仅在 hover / 聚焦视图时显示标题栏动作,现提供命令面板开关与首次激活的一次性提示,一键让图标常驻。 +- **图标渲染空白修复**:Compare with Current Branch 的 `$(compare)` 并非有效 codicon id,修正为 `$(git-compare)`;并新增图标名白名单护栏测试,杜绝同类漏出。 +- **双市场发布重启**:同一枚 VSIX 同时发布至 VS Code Marketplace 与 Open VSX,Cursor / Windsurf / VSCodium 等 Open VSX 系编辑器可直接搜索安装。 + +--- + +## 🧩 特性详述 + +### 1. Commit 视图头部两行 UI 上移标题栏(#120) + +原视图头部的两行控件整体迁入 VS Code 标题栏,净省两行竖直空间: + +- **Active Changelist 选择器**:原下拉框与 `⋯` 管理菜单合并为一个 **Set Active Changelist…** 入口(位于标题栏「…」菜单)——点击弹出 QuickPick,各列表带文件计数、当前活动项预选,分隔线后并入 New / Rename / Delete Changelist… 操作(默认列表不可改名 / 删除);活动列表名常驻视图副标题(标题「Commit」同行右侧),**活动列表为 Default 时省略**; +- **List ⇄ Tree 切换**:原段控改为标题栏 `$(list-tree)` / `$(list-flat)` 互斥图标(交互范式与 Graph / Branches 一致),偏好移交扩展宿主 `workspaceState` **按仓库持久化**(`hyperGit.commit.dmode:`); +- **Select All 吸顶**:复选框改为文件列表容器内的吸顶首行(列表为空时随空态隐藏),原 `.cl-bar` 与 files-header 两行整体移除。 + +完整叙述见 [Commit 视图整合](../features/commit-view-consolidation.md) 与 [变更文件目录树](../features/file-list-group-by-directory.md)。 + +### 2. Commit 标题栏批量 Discard Changes(#121) + +新增 **Discard Changes** 标题栏动作(最左 `$(discard)` 图标),以文件列表**勾选集**为范围批量撤销: + +- 单次 modal 确认,正文列示目标文件(超 10 个截断为 `… and N more`); +- 确认后批量执行——未跟踪文件 `clean` 删除、已跟踪改动 `restore` 还原,与单文件右键 Discard Changes 走**同一条统一路径**(单 / 多选语义一致); +- 勾选集自 webview 单向同步宿主镜像(webview 仍为事实源),无勾选时给出提示而非静默无操作。 + +同时 **Set Active Changelist…** 与 **New Changelist…** 两入口由标题栏图标位收入「…」菜单(`0_changelist` 组,居同步组之前),把有限的图标位让给高频动作。 + +### 3. Branches 标题栏重排与分支级 Push(#121) + +- **分支右键 Push(新增)**:右键任一本地分支 → **Push**。已配置上游时按显式 refspec 推送到追踪分支(**本地名与上游名不一致亦正确**);无上游时选定 remote 并以 `-u` 建立追踪——与全局 Push 的双路径语义一致。未配置任何 remote 时给出明确告警;推送全程带进度反馈,完成后刷新视图并提示; +- **Push 图标下放**:标题栏不再占用图标位(全局 Push 仍可用 Commit 视图标题栏与命令面板); +- **Prune Deleted Remote Branches 上移**:由「…」菜单升至标题栏 `$(sync-ignored)` 图标(即原 Push 位)。该图标全扩展唯一,既区别于同栏 `$(refresh)` / `$(sync)` 同步类,也不与右键删除类 `$(trash)` 混淆; +- **Merge… 收入「…」菜单**:置于同步组内 Update Project 与 New Tag… 之间,不再占用图标位。 + +### 4. 标题栏图标常驻引导(#121) + +VS Code 默认仅在 hover / 聚焦视图时显示标题栏动作(平台行为:`ViewPane` 依 `workbench.view.alwaysShowHeaderActions` 切换 CSS 类,扩展贡献点无法覆盖)。本版提供双入口: + +- 命令面板 **Hyper Git: Always Show View Toolbar Icons** 随时开启; +- 首次激活的一次性提示——**Always Show** 写入全局设置并即时生效,**Don't Show Again** 不再打扰;该设置已为 `true` 时静默不提示。 + +### 5. 稳定性修复(#120 / #121) + +- **Compare with Current Branch 菜单图标渲染为空白方块**:`$(compare)` 并非有效 codicon id(自 v0.0.17 起即如此),修正为 `$(git-compare)`。无效 codicon id 在 VS Code 中**静默渲染为空白且无任何告警**,现以取自 `microsoft/vscode-codicons` 的图标名白名单护栏测试全量校验命令与 submenu 图标,杜绝同类漏出——该护栏在本版开发期即拦下另一处新引入的 `$(sync-ignore)`; +- **Commit 文件列表树语义回归**:吸顶 Select All 行引入后打断了树语义——`role="tree"` 与 `aria-label` 回归保留于可聚焦的文件列表容器,吸顶行与行区容器降为 `presentation`;并补 `scroll-margin-top`,避免键盘导航的 `scrollIntoView` 把首行滚到吸顶行之下被遮挡。 + +### 6. 发布通道:重启双市场(#119) + +CI `publish` job 增回 Open VSX 发布步骤(由仓库变量 `ENABLE_OVSX_PUBLISH` 门控,与 Marketplace 的 `ENABLE_MARKETPLACE_PUBLISH` 对称),**复用 `package` job 产出的同一枚 VSIX、共享同一道 production 审批门**——两个市场分发的是字节一致的构建产物。发布决策沿革见 [发布策略调研](../research/04-publishing-cicd.md)。 + +--- + +## ⚙️ 升级与兼容 + +- **Commit 文件列表 List / Tree 偏好一次性重置为默认 flat**:该偏好自 webview state 移交宿主 `workspaceState` 按仓库持久化(`hyperGit.commit.dmode:`),旧偏好不迁移;**勾选集、目录折叠状态与提交草稿均不受影响**; +- **Branches 标题栏不再有 Push 图标**:改为右键任一本地分支执行(新增的按分支推送能力);全局 Push 仍可用 Commit 视图标题栏与命令面板; +- **Branches 的 Merge… 由标题栏收入「…」菜单**;「Prune Deleted Remote Branches」反向由「…」菜单上移标题栏图标位; +- **Commit 的 Set Active Changelist… 与 New Changelist… 由标题栏图标位收入「…」菜单**;活动 changelist 为 Default 时视图副标题不再显示; +- **标题栏图标默认仅 hover 显示**属 VS Code 平台行为——若希望常驻,用命令面板的 **Hyper Git: Always Show View Toolbar Icons** 开启(或在首次激活的提示中点 Always Show); +- **无破坏性移除**:全部既有命令、视图与配置项保留;本版新增 `hyperGit.pushBranch`、`hyperGit.commit.discardSelected`、`hyperGit.commit.detailTree` / `detailFlat`、`hyperGit.alwaysShowHeaderActions` 五个命令。 + +--- + +## 📦 安装 + +- **VS Code Marketplace**:搜索 `Hyper Git - Agentic Git` 直接安装或更新。 +- **Open VSX**:搜索 `Hyper Git - Agentic Git` 直接安装或更新(覆盖 Cursor / Windsurf / VSCodium 等 Open VSX 系编辑器)。 +- **手动**:从 [Releases](https://github.com/ThreeFish-AI/hyper-git/releases/tag/v0.0.18) 下载 `hyper-git-agentic-git-0.0.18.vsix` → 命令面板 `Extensions: Install from VSIX`。 + +**系统要求**:VS Code ≥ 1.85.0,且启用内置 Git 扩展(`vscode.git`,默认随附)。仅支持本地 git 仓库,不支持虚拟 / Web 工作区。 + +--- + +## 💬 反馈 + +欢迎在 [GitHub Issues](https://github.com/ThreeFish-AI/hyper-git/issues) 反馈问题与建议。完整工程视角变更记录见 [CHANGELOG](../../CHANGELOG.md)。 + +许可证:[MIT](https://github.com/ThreeFish-AI/hyper-git/blob/master/LICENSE)。 diff --git a/docs/research/04-publishing-cicd.md b/docs/research/04-publishing-cicd.md index 5c3d03a..f48fe4e 100644 --- a/docs/research/04-publishing-cicd.md +++ b/docs/research/04-publishing-cicd.md @@ -4,6 +4,7 @@ > 所有事实论断均附 GitHub 路径或官方文档 URL;不确定项标注"待核实"。 > **决策更新(2026-07-04)**:本项目已将发布策略由「双市场(Marketplace + OpenVSX)」**收敛为 VS Code Marketplace 单市场**——移除 CI 的 OpenVSX 发布步骤与 `OVSX_PAT` 依赖(实践中 OpenVSX 命名空间与凭证未落地,且为降低维护面)。下文 §1.2、§2、§3、§5 及「下一步」中一切涉及 OpenVSX / ovsx 的内容,均保留为当时的循证背景(已被本决策取代);当前发布口径以 **§2.3(已修订)** 为准。 +> **决策更新(2026-09-10)**:Open VSX 命名空间与 `OVSX_PAT` 凭证已落地,发布策略**重启双市场(Marketplace + Open VSX)**——`publish` job 增回 Open VSX 发布步骤(`pnpm exec ovsx publish --packagePath ./*.vsix`,由仓库变量 `ENABLE_OVSX_PUBLISH` 门控),README 安装渠道同步加回;2026-07-04 的「单市场收敛」决策自此被取代,当前发布口径以 **§2.3(再次修订)** 为准。 --- @@ -71,15 +72,15 @@ - **Cursor 使用 Open VSX registry,而非 VS Code Marketplace**。来源:[Cursor 论坛官方回复](https://forum.cursor.com/t/cursor-marketplace-installs-offers-outdated-version-of-open-vsx-extension-despite-latest-version-being-available-upstream/159718)、[安全研究 mazinahmed.net](https://mazinahmed.net/blog/publishing-malicious-vscode-extensions/)(明确指出 OpenVSX 驱动 Cursor/Windsurf/Kiro 等 AI IDE)、[devclass 报道](https://www.devclass.com/development/2025/04/08/vs-code-extension-marketplace-wars-cursor-users-hit-roadblocks/1629343)。 - 后果:**只**发 Marketplace 时,Cursor/Windsurf 用户**装不到**(除非手动 `.vsix`)。 -### 2.3 决策(2026-07-04 修订):**VS Code Marketplace 单市场发布** +### 2.3 决策(2026-09-10 再次修订):**双市场发布(VS Code Marketplace + Open VSX)** -> 原方案为「双市场同时发布」(理由见上 §2.1/§2.2 循证背景:AI 受众主战场在 OpenVSX、边际成本低)。实践中因 OpenVSX 命名空间与 `OVSX_PAT` 凭证未落地、且为收敛维护面,**修订为仅发布 VS Code Marketplace**。 +> 原方案为「双市场同时发布」(理由见上 §2.1/§2.2 循证背景:AI 受众主战场在 OpenVSX、边际成本低);2026-07-04 因 OpenVSX 命名空间与 `OVSX_PAT` 凭证未落地、且为收敛维护面,临时收敛为 Marketplace 单市场;2026-09-10 命名空间与凭证落地后**重启双市场**。 当前口径: -1. **单市场**:仅经 `vsce publish` 发布到 VS Code Marketplace(原生 VS Code 用户主战场;`rc` tag 走 `--pre-release` 预发布通道)。 -2. **兜底安装**:每个 `v*` tag 均产出 GitHub Release 并附 `.vsix`,Cursor/Windsurf/VSCodium 等用户可下载后 `Install from VSIX`。 -3. **凭证**:仅需 `VSCE_PAT`(Azure DevOps PAT,scope Marketplace → Manage);由仓库变量 `ENABLE_MARKETPLACE_PUBLISH` 门控。 -4. **未来若重启 OpenVSX**:需先在 open-vsx.org 创建并 claim `ThreeFish-AI` 命名空间、配置有效 `OVSX_PAT`,再于 `publish` job 增回 `ovsx publish` 步骤(§2.1/§2.2 背景分析仍适用)。 +1. **双市场**:同一枚 VSIX(由 `package` job 统一打包)先后经 `vsce publish` 发 VS Code Marketplace、经 `ovsx publish` 发 Open VSX(覆盖 Cursor/Windsurf/VSCodium 等 AI IDE 用户,§2.2 循证)。`rc` tag 走预发布通道——Marketplace 端 `vsce` 打包/发布两端 `--pre-release` 成对;Open VSX 端语义由 `vsce package --pre-release` 内嵌进 VSIX 元数据,`ovsx publish` 无需该 flag。 +2. **兜底安装**:每个 `v*` tag 均产出 GitHub Release 并附 `.vsix`,可下载后 `Install from VSIX`。 +3. **凭证**:`VSCE_PAT`(Azure DevOps PAT,scope Marketplace → Manage,门控 `ENABLE_MARKETPLACE_PUBLISH`)+ `OVSX_PAT`(open-vsx.org Access Token,门控 `ENABLE_OVSX_PUBLISH`);两步骤同挂 `publish` job 的 `environment: production` 审批门。 +4. **Open VSX 安全前提**:`create-namespace` 不自动授予独占发布权,须另行 claim namespace ownership(§1.2 关键循证)。 --- diff --git a/package.json b/package.json index 0238a46..cd5e56e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "theme": "dark" }, "description": "Hyper Git brings IntelliJ IDEA's powerful Git & Commit modules to VS Code, enhanced with AI-powered autonomous agents for intelligent repository management. Experience Git in a higher dimension.", - "version": "0.0.17", + "version": "0.0.18", "publisher": "ThreeFish-AI", "license": "MIT", "preview": true, @@ -190,6 +190,11 @@ "title": "Show Version", "category": "Hyper Git" }, + { + "command": "hyperGit.alwaysShowHeaderActions", + "title": "Always Show View Toolbar Icons", + "category": "Hyper Git" + }, { "command": "hyperGit.refresh", "title": "Refresh", @@ -204,8 +209,9 @@ }, { "command": "hyperGit.setActiveChangelist", - "title": "Set Active Changelist", - "category": "Hyper Git" + "title": "Set Active Changelist…", + "category": "Hyper Git", + "icon": "$(checklist)" }, { "command": "hyperGit.renameChangelist", @@ -272,6 +278,24 @@ "category": "Hyper Git", "icon": "$(list-flat)" }, + { + "command": "hyperGit.commit.detailTree", + "title": "Group Changed Files by Directory", + "category": "Hyper Git", + "icon": "$(list-tree)" + }, + { + "command": "hyperGit.commit.detailFlat", + "title": "Show Changed Files as Flat List", + "category": "Hyper Git", + "icon": "$(list-flat)" + }, + { + "command": "hyperGit.commit.discardSelected", + "title": "Discard Changes", + "category": "Hyper Git", + "icon": "$(discard)" + }, { "command": "hyperGit.selectRepository", "title": "Select Repository…", @@ -432,7 +456,7 @@ "command": "hyperGit.pruneRemotes", "title": "Prune Deleted Remote Branches", "category": "Hyper Git", - "icon": "$(clear-all)" + "icon": "$(sync-ignored)" }, { "command": "hyperGit.cherryPick", @@ -484,7 +508,7 @@ "command": "hyperGit.compareWithCurrent", "title": "Compare with Current Branch", "category": "Hyper Git", - "icon": "$(compare)" + "icon": "$(git-compare)" }, { "command": "hyperGit.tagCreate", @@ -631,6 +655,12 @@ "category": "Hyper Git", "icon": "$(git-merge)" }, + { + "command": "hyperGit.pushBranch", + "title": "Push", + "category": "Hyper Git", + "icon": "$(arrow-up)" + }, { "command": "hyperGit.resolveConflicts", "title": "Resolve Conflicts (3-way Merge Editor)…", @@ -817,14 +847,34 @@ ], "view/title": [ { - "command": "hyperGit.refresh", + "command": "hyperGit.commit.discardSelected", "when": "view == hyperGit.commit", - "group": "navigation@1" + "group": "navigation@0.5" + }, + { + "command": "hyperGit.commit.detailTree", + "when": "view == hyperGit.commit && !hyperGit.commit.tree", + "group": "navigation@0.75" + }, + { + "command": "hyperGit.commit.detailFlat", + "when": "view == hyperGit.commit && hyperGit.commit.tree", + "group": "navigation@0.75" + }, + { + "command": "hyperGit.setActiveChangelist", + "when": "view == hyperGit.commit", + "group": "0_changelist@0" }, { "command": "hyperGit.newChangelist", "when": "view == hyperGit.commit", - "group": "navigation@2" + "group": "0_changelist@1" + }, + { + "command": "hyperGit.refresh", + "when": "view == hyperGit.commit", + "group": "navigation@1" }, { "command": "hyperGit.push", @@ -927,15 +977,10 @@ "group": "navigation@3" }, { - "command": "hyperGit.push", + "command": "hyperGit.pruneRemotes", "when": "view == hyperGit.branches", "group": "navigation@4" }, - { - "command": "hyperGit.mergeDialog", - "when": "view == hyperGit.branches", - "group": "navigation@5" - }, { "command": "hyperGit.branchesGroupByPrefix", "when": "view == hyperGit.branches && !hyperGit.branchesGrouping", @@ -951,11 +996,6 @@ "when": "view == hyperGit.branches", "group": "1_sync@1" }, - { - "command": "hyperGit.pruneRemotes", - "when": "view == hyperGit.branches", - "group": "1_sync@1.5" - }, { "command": "hyperGit.updateProject", "when": "view == hyperGit.branches", @@ -1000,6 +1040,11 @@ "command": "hyperGit.worktreePrune", "when": "view == hyperGit.worktrees", "group": "navigation@3" + }, + { + "command": "hyperGit.mergeDialog", + "when": "view == hyperGit.branches", + "group": "1_sync@2.5" } ], "view/item/context": [ @@ -1018,6 +1063,11 @@ "when": "view == hyperGit.branches && viewItem == hyperGit.remoteBranch", "group": "9_dangerous@2" }, + { + "command": "hyperGit.pushBranch", + "when": "view == hyperGit.branches && viewItem == hyperGit.branch && !listMultiSelection", + "group": "1_branch@2" + }, { "command": "hyperGit.mergeBranch", "when": "view == hyperGit.branches && viewItem =~ /^(hyperGit.branch|hyperGit.remoteBranch)$/ && !listMultiSelection", @@ -1153,7 +1203,7 @@ }, { "command": "hyperGit.setActiveChangelist", - "when": "false" + "when": "view == hyperGit.commit" }, { "command": "hyperGit.renameChangelist", @@ -1375,6 +1425,18 @@ "command": "hyperGit.log.detailFlat", "when": "view == hyperGit.log" }, + { + "command": "hyperGit.commit.detailTree", + "when": "view == hyperGit.commit" + }, + { + "command": "hyperGit.commit.detailFlat", + "when": "view == hyperGit.commit" + }, + { + "command": "hyperGit.commit.discardSelected", + "when": "view == hyperGit.commit" + }, { "command": "hyperGit.acceptOurs", "when": "hyperGit.hasConflicts" @@ -1382,6 +1444,10 @@ { "command": "hyperGit.acceptTheirs", "when": "hyperGit.hasConflicts" + }, + { + "command": "hyperGit.pushBranch", + "when": "false" } ] }, @@ -1504,6 +1570,7 @@ "esbuild": "^0.28.1", "eslint": "^10.9.1", "mocha": "^11.0.1", + "ovsx": "^1.1.1", "prettier": "^3.9.6", "rimraf": "^6.0.1", "typescript": "^6.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0beeb75..bb5440c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,10 +111,10 @@ importers: devDependencies: '@eslint/js': specifier: ^10.0.1 - version: 10.0.1(eslint@10.9.1) + version: 10.0.1(eslint@10.9.1(supports-color@8.1.1)) '@stylistic/eslint-plugin': specifier: ^5.10.0 - version: 5.10.0(eslint@10.9.1) + version: 5.10.0(eslint@10.9.1(supports-color@8.1.1)) '@types/node': specifier: ^26.4.0 version: 26.4.0 @@ -132,10 +132,13 @@ importers: version: 0.28.1 eslint: specifier: ^10.9.1 - version: 10.9.1 + version: 10.9.1(supports-color@8.1.1) mocha: specifier: ^11.0.1 version: 11.7.6 + ovsx: + specifier: ^1.1.1 + version: 1.1.1(@types/node@26.4.0)(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) prettier: specifier: ^3.9.6 version: 3.9.6 @@ -147,7 +150,7 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.68.0 - version: 8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3) + version: 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) vite: specifier: ^8.2.2 version: 8.2.2(@types/node@26.4.0)(esbuild@0.28.1) @@ -436,6 +439,140 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + + '@inquirer/checkbox@4.3.2': + resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.23': + resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.23': + resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} + + '@inquirer/input@4.3.1': + resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.23': + resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.23': + resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.10.1': + resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.11': + resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.2.2': + resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.4.2': + resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -443,6 +580,173 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@napi-rs/keyring-darwin-arm64@1.3.0': + resolution: {integrity: sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/keyring-darwin-x64@1.3.0': + resolution: {integrity: sha512-YcJtEV5LA3cvA4z3BurgxH5IhTsW1JfIvcAAcqcecwk06Si9F9NqkxbZVIfDwQ8oRHgaBmT3zZJnLAotCrVahw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/keyring-freebsd-x64@1.3.0': + resolution: {integrity: sha512-vlLf31TGhfRAaxLDBhg8b89ss0HHD/lyNmL5F3UjSaz5CUXElsJmKYq9fqA/B+cZKUEUcLHHGhF0I/CqcFdaVw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/keyring-linux-arm-gnueabihf@1.3.0': + resolution: {integrity: sha512-KiWdMMu/Inz/bHHIAGrnF7r54FZDYXuHO6UFF/rhIrshUsxbMG1Rl9lEymNtqqsVo927G0VYcb02FzWQ3iBQRQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/keyring-linux-arm64-gnu@1.3.0': + resolution: {integrity: sha512-eyKGpY40lm9Jvs1aD294XRH4y7+TlJM0YVAryZeXA6TX0mb4gMkxVXwSQv7MCwgah7raeUd0dKUb4BPAYIgcMg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/keyring-linux-arm64-musl@1.3.0': + resolution: {integrity: sha512-iIK6JWHXAJqDrEyLY3TmswwloVyt2vj+04TZnew+uSJ9gnDO8EwRbp3/iw3LpWaXiDO7VomGO6y8I0Id8uBZSw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/keyring-linux-riscv64-gnu@1.3.0': + resolution: {integrity: sha512-/PGqrwn6EwgtK6vccASSXJRfOSP4vN1F4ASsIQ+7MdrK6hNvAJ1FZPrIuD5gGGdxezo3F++To2Wq7DbuGIeuNQ==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/keyring-linux-x64-gnu@1.3.0': + resolution: {integrity: sha512-2PDK1WKWTu9lBGq9VvNEkSlQD3O7YwVpmnyN2M3cy4v7NJ/8gDMd9GXv3G+FVXN13uhp4gnnPBS+ScefmEeD2A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/keyring-linux-x64-musl@1.3.0': + resolution: {integrity: sha512-oJ2HkX8YUo46QBkn0pG+HuIKQNqr523q6vBobCn+P95s4C4K6/kLBqHY/1bg5J4ap31DzsznhnFKcfBNBsjCnw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/keyring-win32-arm64-msvc@1.3.0': + resolution: {integrity: sha512-tOd3c/uAaeoE4ycVlmAdSvygz0Zt3zdca6Y7gokBeIbaRDWpjDIUOpU3MvML59XAaqyuKGsVVu0F/DZb1lHPmw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/keyring-win32-ia32-msvc@1.3.0': + resolution: {integrity: sha512-sPSqeAFZMGqP1R++M2JTza7GQJJ/TpCo6JU6Vcd4jnebvOaEDs9b7eipakU1PJdSvhpC2yXMCNRk9gXfrhuwHQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/keyring-win32-x64-msvc@1.3.0': + resolution: {integrity: sha512-4DnCWXwDc0HRKwyRlG5y0VhKZW2tNRQfKKfyj6IX/KWfDNyq9hn4n+GL1auyDcOO/v8PwnhmYo2+rOOqCkvvOg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/keyring@1.3.0': + resolution: {integrity: sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA==} + engines: {node: '>= 10'} + + '@node-rs/crc32-android-arm-eabi@1.10.7': + resolution: {integrity: sha512-mWNghDkwgoc5uJhhPx/LbgLoNAxnq6Sfz4pTxi4NWG7s5l+dPe7K+L4kXb0oOfwl3Yq3Ro7CNPGLrU4cmTXhaA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@node-rs/crc32-android-arm64@1.10.7': + resolution: {integrity: sha512-lLUVm2H5HrSm/g2379JBRsSr5RCDqobP6I5sxZd+wSq6WBH2b6goilNYhxbss8gYQZLCJA09EvyFkZFFU9VuAQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@node-rs/crc32-darwin-arm64@1.10.7': + resolution: {integrity: sha512-8slhmrRa3B3/z+MnLgST+lR5T0Oic60YTQ3S9OIHP259GiktlmdYyebhAvy0bBcyCuScLDcW0EB3sXxWw1TCbQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@node-rs/crc32-darwin-x64@1.10.7': + resolution: {integrity: sha512-+RkQ2+jSWco0WqSPq7GWJlJwmJpIGA79OoToGTSAsqgypkls4i3PlexVTPrfepuHn3whpa4GrmjvLbRm5RXgCQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@node-rs/crc32-freebsd-x64@1.10.7': + resolution: {integrity: sha512-yqtsf23JCO1gYt3/Rkk5aoVn1rINk/J7//A9EKwGstNpBBlN3t0JKD3QpGzEnqc1vS72lEvIeO9sO3XiH+hJWw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@node-rs/crc32-linux-arm-gnueabihf@1.10.7': + resolution: {integrity: sha512-AB/I1GO9LDIIHiur2bexqPy30740AmBjEhO8ij9k9desVO41dN5/ddTU2+xbf9TYt2XXvku1qYbtf5YHAAVFAw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@node-rs/crc32-linux-arm64-gnu@1.10.7': + resolution: {integrity: sha512-0lPFuudkW+bpeXUZAqz6FSml6Z2JvF52UGSALvnDS8Qyg3wKBNWjcXy78Pw1wiIGao/AAFFr74I3OwCMzYujuQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@node-rs/crc32-linux-arm64-musl@1.10.7': + resolution: {integrity: sha512-lx5m7iCmdXPGigG7AI5NiawWsiJ1HwR/w6PV8XN48HNsgzdjar0ISB8TSLtJpq+jFcj10Wcz3Rt43RA72652Vg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@node-rs/crc32-linux-x64-gnu@1.10.7': + resolution: {integrity: sha512-If3ogA9D2XKKb8vHttu+jl3BTWT7UCTgmrXmUAwQABH45iK32AZ9OoMcrg2rWKKXgGpG6x3IslFBgzdOkObDnA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@node-rs/crc32-linux-x64-musl@1.10.7': + resolution: {integrity: sha512-XxuxZYJhQBlo2rRnyOOummZSyBdC+jHpXWM9XSWs1LWfsgc36IauKY+XXRAJwKwefx8lwmMjqp6T/Zq9frU87w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@node-rs/crc32-win32-arm64-msvc@1.10.7': + resolution: {integrity: sha512-Rs8TGuvxgpkteSLhPqAb3DOqqRO1jiYghdMxozl+P3Ftep1hTWL8+gwYF5dtu1BtdOJpz23A+8GehSuwl96MrQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@node-rs/crc32-win32-ia32-msvc@1.10.7': + resolution: {integrity: sha512-G/xNcmblMH6Z7KBX02XJPJj2UGo6eTgFa+r5ZU17O8V5P9iQLrKqs/T7Sz40otDxazD1maSXT/zA8Q7rBu2/3g==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@node-rs/crc32-win32-x64-msvc@1.10.7': + resolution: {integrity: sha512-1uXrMu17uz12WPdii84NXmoQsoPdmSQViyCMJPu0KQuwnhErQCA4PoGK+dih9jY0mrcLuzR1r5wXVaE049fJ6w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@node-rs/crc32@1.10.7': + resolution: {integrity: sha512-OwuyRAe9Lj0GoFVBFzTS6bTAV4i+Xd68sYbNUmLnI1GVkQIqVb9mzNrpKSZkBFiQD73CdMB/uKYsvk2bxq/eZg==} + engines: {node: '>= 10'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -954,6 +1258,9 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -968,6 +1275,9 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -976,6 +1286,10 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -999,12 +1313,21 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-keychain@1.1.0: + resolution: {integrity: sha512-244DWNdGepLKD5vEn3reZqwzZFiE/LD4U+XV9IaXQbtIXKvQkf0VkRaOj/9vPYauPdR12PSGB3U0cE7jJi3WTQ==} + engines: {node: '>=18'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1048,10 +1371,18 @@ packages: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1266,6 +1597,15 @@ packages: flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1325,6 +1665,10 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} @@ -1340,6 +1684,9 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -1379,6 +1726,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1407,6 +1758,10 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + is-ci@2.0.0: + resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} + hasBin: true + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1433,6 +1788,10 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} + is-it-type@5.1.3: + resolution: {integrity: sha512-AX2uU0HW+TxagTgQXOJY7+2fbFHemC7YFBwN1XqD8qQMKdtfbOC8OC3fUb4s5NU59a3662Dzwto8tWDdZYRXxg==} + engines: {node: '>=12'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1675,6 +2034,10 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1737,6 +2100,10 @@ packages: mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + nanoid@3.3.18: resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1770,6 +2137,10 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + obug@2.1.3: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} @@ -1793,6 +2164,11 @@ packages: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} + ovsx@1.1.1: + resolution: {integrity: sha512-tklsCzvGVWKlM91Vc9U8tNnaQ+XacPJ12SWHjDaHGUJB49oMhoAULsJGeefhHebPvvckbcWbKqKIXODMZah5SA==} + engines: {node: '>= 20'} + hasBin: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2051,6 +2427,10 @@ packages: simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-invariant@2.0.1: + resolution: {integrity: sha512-1sbhsxqI+I2tqlmjbz99GXNmZtr6tKIyEgGGnJw/MKGblalqk/XoOYYFJlBzTKZCxx8kLaD3FD5s9BEEjx5Pyg==} + engines: {node: '>=10'} + slash@5.1.0: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} @@ -2369,6 +2749,10 @@ packages: workerpool@9.3.4: resolution: {integrity: sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==} + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -2411,6 +2795,10 @@ packages: resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} + yauzl-promise@4.0.0: + resolution: {integrity: sha512-/HCXpyHXJQQHvFq9noqrjfa/WpQC2XYs3vI7tBiAi4QiIU1knvYhZGaO1QPjwIVMdqflxbmwgMXtYeaRiAE0CA==} + engines: {node: '>=16'} + yauzl@3.4.0: resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} engines: {node: '>=12'} @@ -2422,6 +2810,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + snapshots: '@azu/format-text@1.0.2': {} @@ -2434,34 +2826,34 @@ snapshots: dependencies: tslib: 2.8.1 - '@azure/core-auth@1.10.1': + '@azure/core-auth@1.10.1(supports-color@8.1.1)': dependencies: '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.13.1 + '@azure/core-util': 1.13.1(supports-color@8.1.1) tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/core-client@1.10.2': + '@azure/core-client@1.10.2(supports-color@8.1.1)': dependencies: '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.24.0 + '@azure/core-auth': 1.10.1(supports-color@8.1.1) + '@azure/core-rest-pipeline': 1.24.0(supports-color@8.1.1) '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 + '@azure/core-util': 1.13.1(supports-color@8.1.1) + '@azure/logger': 1.3.0(supports-color@8.1.1) tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/core-rest-pipeline@1.24.0': + '@azure/core-rest-pipeline@1.24.0(supports-color@8.1.1)': dependencies: '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 + '@azure/core-auth': 1.10.1(supports-color@8.1.1) '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.6 + '@azure/core-util': 1.13.1(supports-color@8.1.1) + '@azure/logger': 1.3.0(supports-color@8.1.1) + '@typespec/ts-http-runtime': 0.3.6(supports-color@8.1.1) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -2470,23 +2862,23 @@ snapshots: dependencies: tslib: 2.8.1 - '@azure/core-util@1.13.1': + '@azure/core-util@1.13.1(supports-color@8.1.1)': dependencies: '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.6 + '@typespec/ts-http-runtime': 0.3.6(supports-color@8.1.1) tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/identity@4.13.1': + '@azure/identity@4.13.1(supports-color@8.1.1)': dependencies: '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.2 - '@azure/core-rest-pipeline': 1.24.0 + '@azure/core-auth': 1.10.1(supports-color@8.1.1) + '@azure/core-client': 1.10.2(supports-color@8.1.1) + '@azure/core-rest-pipeline': 1.24.0(supports-color@8.1.1) '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 + '@azure/core-util': 1.13.1(supports-color@8.1.1) + '@azure/logger': 1.3.0(supports-color@8.1.1) '@azure/msal-browser': 5.15.0 '@azure/msal-node': 5.3.0 open: 10.2.0 @@ -2494,9 +2886,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/logger@1.3.0': + '@azure/logger@1.3.0(supports-color@8.1.1)': dependencies: - '@typespec/ts-http-runtime': 0.3.6 + '@typespec/ts-http-runtime': 0.3.6(supports-color@8.1.1) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -2598,19 +2990,19 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.9.1)': + '@eslint-community/eslint-utils@4.10.1(eslint@10.9.1(supports-color@8.1.1))': dependencies: - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@10.9.1)': + '@eslint-community/eslint-utils@4.9.1(eslint@10.9.1(supports-color@8.1.1))': dependencies: - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.23.5': + '@eslint/config-array@0.23.5(supports-color@8.1.1)': dependencies: '@eslint/object-schema': 3.0.5 debug: 4.4.3(supports-color@8.1.1) @@ -2626,9 +3018,9 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/js@10.0.1(eslint@10.9.1)': + '@eslint/js@10.0.1(eslint@10.9.1(supports-color@8.1.1))': optionalDependencies: - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) '@eslint/object-schema@3.0.5': {} @@ -2653,6 +3045,131 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@inquirer/ansi@1.0.2': {} + + '@inquirer/checkbox@4.3.2(@types/node@26.4.0)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@26.4.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/confirm@5.1.21(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/core@10.3.2(@types/node@26.4.0)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@26.4.0) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/editor@4.2.23(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/external-editor': 1.0.3(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/expand@4.0.23(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/external-editor@1.0.3(@types/node@26.4.0)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/figures@1.0.15': {} + + '@inquirer/input@4.3.1(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/number@3.0.23(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/password@4.0.23(@types/node@26.4.0)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/prompts@7.10.1(@types/node@26.4.0)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@26.4.0) + '@inquirer/confirm': 5.1.21(@types/node@26.4.0) + '@inquirer/editor': 4.2.23(@types/node@26.4.0) + '@inquirer/expand': 4.0.23(@types/node@26.4.0) + '@inquirer/input': 4.3.1(@types/node@26.4.0) + '@inquirer/number': 3.0.23(@types/node@26.4.0) + '@inquirer/password': 4.0.23(@types/node@26.4.0) + '@inquirer/rawlist': 4.1.11(@types/node@26.4.0) + '@inquirer/search': 3.2.2(@types/node@26.4.0) + '@inquirer/select': 4.4.2(@types/node@26.4.0) + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/rawlist@4.1.11(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/search@3.2.2(@types/node@26.4.0)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@26.4.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/select@4.4.2(@types/node@26.4.0)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@26.4.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 26.4.0 + + '@inquirer/type@3.0.10(@types/node@26.4.0)': + optionalDependencies: + '@types/node': 26.4.0 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -2664,6 +3181,113 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@napi-rs/keyring-darwin-arm64@1.3.0': + optional: true + + '@napi-rs/keyring-darwin-x64@1.3.0': + optional: true + + '@napi-rs/keyring-freebsd-x64@1.3.0': + optional: true + + '@napi-rs/keyring-linux-arm-gnueabihf@1.3.0': + optional: true + + '@napi-rs/keyring-linux-arm64-gnu@1.3.0': + optional: true + + '@napi-rs/keyring-linux-arm64-musl@1.3.0': + optional: true + + '@napi-rs/keyring-linux-riscv64-gnu@1.3.0': + optional: true + + '@napi-rs/keyring-linux-x64-gnu@1.3.0': + optional: true + + '@napi-rs/keyring-linux-x64-musl@1.3.0': + optional: true + + '@napi-rs/keyring-win32-arm64-msvc@1.3.0': + optional: true + + '@napi-rs/keyring-win32-ia32-msvc@1.3.0': + optional: true + + '@napi-rs/keyring-win32-x64-msvc@1.3.0': + optional: true + + '@napi-rs/keyring@1.3.0': + optionalDependencies: + '@napi-rs/keyring-darwin-arm64': 1.3.0 + '@napi-rs/keyring-darwin-x64': 1.3.0 + '@napi-rs/keyring-freebsd-x64': 1.3.0 + '@napi-rs/keyring-linux-arm-gnueabihf': 1.3.0 + '@napi-rs/keyring-linux-arm64-gnu': 1.3.0 + '@napi-rs/keyring-linux-arm64-musl': 1.3.0 + '@napi-rs/keyring-linux-riscv64-gnu': 1.3.0 + '@napi-rs/keyring-linux-x64-gnu': 1.3.0 + '@napi-rs/keyring-linux-x64-musl': 1.3.0 + '@napi-rs/keyring-win32-arm64-msvc': 1.3.0 + '@napi-rs/keyring-win32-ia32-msvc': 1.3.0 + '@napi-rs/keyring-win32-x64-msvc': 1.3.0 + optional: true + + '@node-rs/crc32-android-arm-eabi@1.10.7': + optional: true + + '@node-rs/crc32-android-arm64@1.10.7': + optional: true + + '@node-rs/crc32-darwin-arm64@1.10.7': + optional: true + + '@node-rs/crc32-darwin-x64@1.10.7': + optional: true + + '@node-rs/crc32-freebsd-x64@1.10.7': + optional: true + + '@node-rs/crc32-linux-arm-gnueabihf@1.10.7': + optional: true + + '@node-rs/crc32-linux-arm64-gnu@1.10.7': + optional: true + + '@node-rs/crc32-linux-arm64-musl@1.10.7': + optional: true + + '@node-rs/crc32-linux-x64-gnu@1.10.7': + optional: true + + '@node-rs/crc32-linux-x64-musl@1.10.7': + optional: true + + '@node-rs/crc32-win32-arm64-msvc@1.10.7': + optional: true + + '@node-rs/crc32-win32-ia32-msvc@1.10.7': + optional: true + + '@node-rs/crc32-win32-x64-msvc@1.10.7': + optional: true + + '@node-rs/crc32@1.10.7': + optionalDependencies: + '@node-rs/crc32-android-arm-eabi': 1.10.7 + '@node-rs/crc32-android-arm64': 1.10.7 + '@node-rs/crc32-darwin-arm64': 1.10.7 + '@node-rs/crc32-darwin-x64': 1.10.7 + '@node-rs/crc32-freebsd-x64': 1.10.7 + '@node-rs/crc32-linux-arm-gnueabihf': 1.10.7 + '@node-rs/crc32-linux-arm64-gnu': 1.10.7 + '@node-rs/crc32-linux-arm64-musl': 1.10.7 + '@node-rs/crc32-linux-x64-gnu': 1.10.7 + '@node-rs/crc32-linux-x64-musl': 1.10.7 + '@node-rs/crc32-win32-arm64-msvc': 1.10.7 + '@node-rs/crc32-win32-ia32-msvc': 1.10.7 + '@node-rs/crc32-win32-x64-msvc': 1.10.7 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2732,18 +3356,18 @@ snapshots: dependencies: '@secretlint/types': 10.2.2 - '@secretlint/config-loader@10.2.2': + '@secretlint/config-loader@10.2.2(supports-color@8.1.1)': dependencies: '@secretlint/profiler': 10.2.2 '@secretlint/resolver': 10.2.2 '@secretlint/types': 10.2.2 ajv: 8.20.0 debug: 4.4.3(supports-color@8.1.1) - rc-config-loader: 4.1.4 + rc-config-loader: 4.1.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@secretlint/core@10.2.2': + '@secretlint/core@10.2.2(supports-color@8.1.1)': dependencies: '@secretlint/profiler': 10.2.2 '@secretlint/types': 10.2.2 @@ -2752,11 +3376,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@secretlint/formatter@10.2.2': + '@secretlint/formatter@10.2.2(supports-color@8.1.1)': dependencies: '@secretlint/resolver': 10.2.2 '@secretlint/types': 10.2.2 - '@textlint/linter-formatter': 15.7.1 + '@textlint/linter-formatter': 15.7.1(supports-color@8.1.1) '@textlint/module-interop': 15.7.1 '@textlint/types': 15.7.1 chalk: 5.6.2 @@ -2768,11 +3392,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@secretlint/node@10.2.2': + '@secretlint/node@10.2.2(supports-color@8.1.1)': dependencies: - '@secretlint/config-loader': 10.2.2 - '@secretlint/core': 10.2.2 - '@secretlint/formatter': 10.2.2 + '@secretlint/config-loader': 10.2.2(supports-color@8.1.1) + '@secretlint/core': 10.2.2(supports-color@8.1.1) + '@secretlint/formatter': 10.2.2(supports-color@8.1.1) '@secretlint/profiler': 10.2.2 '@secretlint/source-creator': 10.2.2 '@secretlint/types': 10.2.2 @@ -2806,11 +3430,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.9.1)': + '@stylistic/eslint-plugin@5.10.0(eslint@10.9.1(supports-color@8.1.1))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(supports-color@8.1.1)) '@typescript-eslint/types': 8.62.1 - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -2818,7 +3442,7 @@ snapshots: '@textlint/ast-node-types@15.7.1': {} - '@textlint/linter-formatter@15.7.1': + '@textlint/linter-formatter@15.7.1(supports-color@8.1.1)': dependencies: '@azu/format-text': 1.0.2 '@azu/style-format': 1.0.1 @@ -2868,15 +3492,15 @@ snapshots: '@types/vscode@1.85.0': {} - '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/parser': 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.68.0 - '@typescript-eslint/type-utils': 8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/utils': 8.68.0(eslint@10.9.1)(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.68.0 - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -2884,19 +3508,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/parser@8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.68.0 '@typescript-eslint/types': 8.68.0 - '@typescript-eslint/typescript-estree': 8.68.0(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.68.0(supports-color@8.1.1)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.68.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.68.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.68.0(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.68.0(typescript@6.0.3) '@typescript-eslint/types': 8.68.0 @@ -2914,13 +3538,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.68.0 - '@typescript-eslint/typescript-estree': 8.68.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.68.0(eslint@10.9.1)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.68.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 10.9.1 + eslint: 10.9.1(supports-color@8.1.1) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -2930,9 +3554,9 @@ snapshots: '@typescript-eslint/types@8.68.0': {} - '@typescript-eslint/typescript-estree@8.68.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.68.0(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.68.0(typescript@6.0.3) + '@typescript-eslint/project-service': 8.68.0(supports-color@8.1.1)(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.68.0(typescript@6.0.3) '@typescript-eslint/types': 8.68.0 '@typescript-eslint/visitor-keys': 8.68.0 @@ -2945,13 +3569,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.68.0(eslint@10.9.1)(typescript@6.0.3)': + '@typescript-eslint/utils@8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(supports-color@8.1.1)) '@typescript-eslint/scope-manager': 8.68.0 '@typescript-eslint/types': 8.68.0 - '@typescript-eslint/typescript-estree': 8.68.0(typescript@6.0.3) - eslint: 10.9.1 + '@typescript-eslint/typescript-estree': 8.68.0(supports-color@8.1.1)(typescript@6.0.3) + eslint: 10.9.1(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2961,7 +3585,7 @@ snapshots: '@typescript-eslint/types': 8.68.0 eslint-visitor-keys: 5.0.1 - '@typespec/ts-http-runtime@0.3.6': + '@typespec/ts-http-runtime@0.3.6(supports-color@8.1.1)': dependencies: http-proxy-agent: 7.0.2(supports-color@8.1.1) https-proxy-agent: 7.0.6(supports-color@8.1.1) @@ -3061,8 +3685,8 @@ snapshots: '@vscode/vsce@3.9.2(supports-color@8.1.1)': dependencies: - '@azure/identity': 4.13.1 - '@secretlint/node': 10.2.2 + '@azure/identity': 4.13.1(supports-color@8.1.1) + '@secretlint/node': 10.2.2(supports-color@8.1.1) '@secretlint/secretlint-formatter-sarif': 10.2.2 '@secretlint/secretlint-rule-no-dotenv': 10.2.2 '@secretlint/secretlint-rule-preset-recommend': 10.2.2 @@ -3225,6 +3849,8 @@ snapshots: chalk@5.6.2: {} + chardet@2.2.0: {} + cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -3255,12 +3881,16 @@ snapshots: chownr@1.1.4: optional: true + ci-info@2.0.0: {} + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 cli-spinners@2.9.2: {} + cli-width@4.1.0: {} + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -3281,10 +3911,21 @@ snapshots: commander@12.1.0: {} + commander@6.2.1: {} + convert-source-map@2.0.0: {} core-util-is@1.0.3: {} + cross-keychain@1.1.0(@types/node@26.4.0): + dependencies: + '@inquirer/prompts': 7.10.1(@types/node@26.4.0) + meow: 14.1.0 + optionalDependencies: + '@napi-rs/keyring': 1.3.0 + transitivePeerDependencies: + - '@types/node' + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3326,8 +3967,20 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.1 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + delayed-stream@1.0.0: {} detect-libc@2.1.2: {} @@ -3455,11 +4108,11 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.9.1: + eslint@10.9.1(supports-color@8.1.1): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(supports-color@8.1.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.5 + '@eslint/config-array': 0.23.5(supports-color@8.1.1) '@eslint/config-helpers': 0.7.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 @@ -3569,6 +4222,10 @@ snapshots: flatted@3.4.4: {} + follow-redirects@1.16.0(debug@4.4.3(supports-color@8.1.1)): + optionalDependencies: + debug: 4.4.3(supports-color@8.1.1) + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -3644,6 +4301,11 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -3659,6 +4321,10 @@ snapshots: has-flag@4.0.0: {} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -3704,6 +4370,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: optional: true @@ -3722,6 +4392,10 @@ snapshots: ini@1.3.8: optional: true + is-ci@2.0.0: + dependencies: + ci-info: 2.0.0 + is-docker@3.0.0: {} is-extglob@2.1.1: {} @@ -3738,6 +4412,10 @@ snapshots: is-interactive@2.0.0: {} + is-it-type@5.1.3: + dependencies: + globalthis: 1.0.4 + is-number@7.0.0: {} is-path-inside@3.0.3: {} @@ -3956,6 +4634,8 @@ snapshots: mdurl@2.0.0: {} + meow@14.1.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -4024,6 +4704,8 @@ snapshots: mute-stream@0.0.8: {} + mute-stream@2.0.0: {} + nanoid@3.3.18: {} napi-build-utils@2.0.0: @@ -4056,6 +4738,8 @@ snapshots: object-inspect@1.13.4: {} + object-keys@1.1.1: {} + obug@2.1.3: {} once@1.4.0: @@ -4095,6 +4779,23 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 + ovsx@1.1.1(@types/node@26.4.0)(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1): + dependencies: + '@inquirer/prompts': 7.10.1(@types/node@26.4.0) + '@vscode/vsce': 3.9.2(supports-color@8.1.1) + commander: 6.2.1 + cross-keychain: 1.1.0(@types/node@26.4.0) + follow-redirects: 1.16.0(debug@4.4.3(supports-color@8.1.1)) + is-ci: 2.0.0 + leven: 3.1.0 + semver: 7.8.5 + tmp: 0.2.7 + yauzl-promise: 4.0.0 + transitivePeerDependencies: + - '@types/node' + - debug + - supports-color + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -4215,7 +4916,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - rc-config-loader@4.1.4: + rc-config-loader@4.1.4(supports-color@8.1.1): dependencies: debug: 4.4.3(supports-color@8.1.1) js-yaml: 4.2.0 @@ -4317,8 +5018,8 @@ snapshots: secretlint@10.2.2(supports-color@8.1.1): dependencies: '@secretlint/config-creator': 10.2.2 - '@secretlint/formatter': 10.2.2 - '@secretlint/node': 10.2.2 + '@secretlint/formatter': 10.2.2(supports-color@8.1.1) + '@secretlint/node': 10.2.2(supports-color@8.1.1) '@secretlint/profiler': 10.2.2 debug: 4.4.3(supports-color@8.1.1) globby: 14.1.0 @@ -4384,6 +5085,8 @@ snapshots: simple-concat: 1.0.1 optional: true + simple-invariant@2.0.1: {} + slash@5.1.0: {} slice-ansi@4.0.0: @@ -4549,13 +5252,13 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.8 - typescript-eslint@8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3): + typescript-eslint@8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/parser': 8.68.0(eslint@10.9.1)(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.68.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.68.0(eslint@10.9.1)(typescript@6.0.3) - eslint: 10.9.1 + '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/parser': 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.68.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.68.0(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + eslint: 10.9.1(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4649,6 +5352,12 @@ snapshots: workerpool@9.3.4: {} + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -4698,6 +5407,12 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yauzl-promise@4.0.0: + dependencies: + '@node-rs/crc32': 1.10.7 + is-it-type: 5.1.3 + simple-invariant: 2.0.1 + yauzl@3.4.0: dependencies: pend: 1.2.0 @@ -4707,3 +5422,5 @@ snapshots: buffer-crc32: 0.2.13 yocto-queue@0.1.0: {} + + yoctocolors-cjs@2.1.3: {} diff --git a/src/adapter/commands.ts b/src/adapter/commands.ts index 4a22f2d..f6f9bf5 100644 --- a/src/adapter/commands.ts +++ b/src/adapter/commands.ts @@ -21,7 +21,8 @@ function asId(arg: unknown): string | undefined { * 注册 Changes / Commit 相关命令(M1;原 Changes 树移除后由 Commit webview 复用)。 * * 文件级命令统一接受 `ChangeItem | 路径字符串`:webview 传路径,host 经 {@link resolveChange} - * 回落到 `service.getChanges()` 解析为 ChangeItem(单一事实源)。视图刷新由 registry/service + * 回落到 `service.getChanges()` 解析为 ChangeItem(单一事实源)。changelist 切换与管理由 + * 标题栏 $(checklist) 图标进入 setActiveChangelist QuickPick 承载。视图刷新由 registry/service * 的 onDidChange → extension.refreshAll → commitView.refresh() 驱动,命令内不再直接刷新视图。 */ export function registerChangesCommands( @@ -57,10 +58,48 @@ export function registerChangesCommands( ); subs.push( - vscode.commands.registerCommand('hyperGit.setActiveChangelist', (arg: unknown) => { + vscode.commands.registerCommand('hyperGit.setActiveChangelist', async (arg?: unknown) => { + // 带参 = 程序化切换;无参 = QuickPick(标题栏 $(checklist) 图标与命令面板共用入口)。 const id = asId(arg); if (id) { registry.setActive(id); + return; + } + const active = registry.activeChangelistId; + // 计数取 getGroups(含空列表,未显式分配项归活动列表——见 engine/changelist/grouper)。 + const groups = registry.getGroups(service.getChanges(), (c) => c.relativePath); + type SetActiveItem = + | { label: string; description: string; picked: boolean; setId: string } + | { label: string; kind: vscode.QuickPickItemKind } + | { label: string; op: 'new' | 'rename' | 'delete'; targetId?: string }; + const items: SetActiveItem[] = groups.map((g) => ({ + label: g.name, + description: `${g.items.length} file${g.items.length === 1 ? '' : 's'}`, + picked: g.id === active, // 预选当前活动项(对齐 selectRepository 的 picked 范式) + setId: g.id, + })); + // 分隔线后并入原 webview「⋯」菜单管理操作(default 不可改名/删除,镜像原 handleChangelistMenu 语义)。 + items.push({ label: 'Actions', kind: vscode.QuickPickItemKind.Separator }); + items.push({ label: '$(add) New Changelist…', op: 'new' }); + const def = active !== 'default' ? registry.getDef(active) : undefined; + if (def) { + items.push({ label: `$(edit) Rename "${def.name}"…`, op: 'rename', targetId: active }); + items.push({ label: `$(trash) Delete "${def.name}"…`, op: 'delete', targetId: active }); + } + const pick = await vscode.window.showQuickPick(items, { placeHolder: 'Select Active Changelist' }); + if (!pick) { + return; + } + if ('setId' in pick) { + registry.setActive(pick.setId); // 视图刷新经 registry.onDidChange → refreshAll 驱动 + } else if ('op' in pick) { + if (pick.op === 'new') { + await vscode.commands.executeCommand('hyperGit.newChangelist'); + } else if (pick.op === 'rename') { + await vscode.commands.executeCommand('hyperGit.renameChangelist', pick.targetId); + } else { + await vscode.commands.executeCommand('hyperGit.deleteChangelist', pick.targetId); + } } }), ); @@ -145,32 +184,51 @@ export function registerChangesCommands( ); subs.push( - vscode.commands.registerCommand('hyperGit.discardChanges', async (arg: ChangeItem | string) => { - const repo = service.repo; - const change = resolveChange(arg); - if (!repo || !change) { - return; - } - const choice = await vscode.window.showWarningMessage( - `Discard changes to "${change.relativePath}"? This action cannot be undone.`, - { modal: true }, - 'Discard', - ); - if (choice !== 'Discard') { - return; - } - try { - // 未跟踪文件用 clean(删除);已跟踪的改动用 restore(丢弃工作区改动)。 - // 视图刷新由 service.onDidChange → refreshAll 驱动。 - if (change.status === FileStatus.Untracked) { - await repo.clean([change.uri.fsPath]); - } else { - await repo.restore([change.uri.fsPath]); + vscode.commands.registerCommand( + 'hyperGit.discardChanges', + // 单文件(文件右键菜单)与批量(Commit 标题栏勾选集)统一路径:归一为数组后逐项解析过滤。 + async (arg: ChangeItem | string | readonly (ChangeItem | string)[]) => { + const repo = service.repo; + const changes = (Array.isArray(arg) ? arg : [arg]) + .map(resolveChange) + .filter((c): c is ChangeItem => Boolean(c)); + if (!repo || changes.length === 0) { + return; } - } catch (e) { - void vscode.window.showErrorMessage(`Failed to discard: ${e instanceof Error ? e.message : String(e)}`); - } - }), + const paths = changes.map((c) => c.relativePath); + const choice = await vscode.window.showWarningMessage( + paths.length === 1 + ? `Discard changes to "${paths[0]}"? This action cannot be undone.` + : `Discard changes to ${paths.length} selected files? This action cannot be undone.`, + // 批量时列示目标文件(超出 10 个截断),破坏性操作保持可见范围。 + { + modal: true, + detail: + paths.length > 1 + ? paths.slice(0, 10).join('\n') + (paths.length > 10 ? `\n… and ${paths.length - 10} more` : '') + : undefined, + }, + 'Discard', + ); + if (choice !== 'Discard') { + return; + } + try { + // 未跟踪文件用 clean(删除);已跟踪的改动用 restore(丢弃工作区改动),两类各一次调用。 + // 视图刷新由 service.onDidChange → refreshAll 驱动。 + const untracked = changes.filter((c) => c.status === FileStatus.Untracked).map((c) => c.uri.fsPath); + const tracked = changes.filter((c) => c.status !== FileStatus.Untracked).map((c) => c.uri.fsPath); + if (untracked.length > 0) { + await repo.clean(untracked); + } + if (tracked.length > 0) { + await repo.restore(tracked); + } + } catch (e) { + void vscode.window.showErrorMessage(`Failed to discard: ${e instanceof Error ? e.message : String(e)}`); + } + }, + ), ); return subs; diff --git a/src/adapter/history-commands.ts b/src/adapter/history-commands.ts index ef41e00..0a9b9af 100644 --- a/src/adapter/history-commands.ts +++ b/src/adapter/history-commands.ts @@ -314,6 +314,49 @@ export function registerHistoryCommands( }), ); + subs.push( + vscode.commands.registerCommand('hyperGit.pushBranch', async (node: BranchNode) => { + // 分支右键推送(原 Branches 标题栏 Push 下放):仅本地分支;上游取自 RawRef.upstream + // (%(upstream:short),非当前分支无 HEAD.upstream 可用),无上游路径与 hyperGit.push 一致。 + const repo = service.repo; + if (!repo || node?.kind !== 'branch' || node.remote) { + return; + } + const name = node.ref.shortName; + try { + const up = node.ref.upstream; + if (up) { + // 已配置上游:显式 refspec 推送(本地名/上游名不一致亦正确;remote 名约定不含 /)。 + const slash = up.indexOf('/'); + const remote = slash >= 0 ? up.slice(0, slash) : up; + const upstreamName = slash >= 0 ? up.slice(slash + 1) : name; + await runWithProgress('Pushing…', () => repo.push(remote, `${name}:${upstreamName}`)); + } else { + // 无上游:选定 remote 并以 -u 建立追踪(修复「Failed to execute git」根因)。 + const remotes = repo.state.remotes.map((r) => r.name); + if (remotes.length === 0) { + void vscode.window.showWarningMessage('No remote configured; cannot push'); + return; + } + const remote = + remotes.length === 1 + ? remotes[0] + : await vscode.window.showQuickPick(remotes, { + placeHolder: `Select a remote to push "${name}" to (will set upstream tracking -u)`, + }); + if (!remote) { + return; + } + await runWithProgress('Pushing…', () => repo.push(remote, name, true)); + } + branchesTree.refresh(); + void vscode.window.showInformationMessage(`Pushed ${name}`); + } catch (e) { + void showGitError(`Failed to Push: ${errMsg(e)}`); + } + }), + ); + subs.push( vscode.commands.registerCommand('hyperGit.fetch', async () => { const repo = service.repo; diff --git a/src/adapter/webview/commit-webview.ts b/src/adapter/webview/commit-webview.ts index 16b4c70..8f6b980 100644 --- a/src/adapter/webview/commit-webview.ts +++ b/src/adapter/webview/commit-webview.ts @@ -6,35 +6,93 @@ import type { CommitRequest } from '../commit/commit-service'; import type { ChangelistRegistry } from '../changelist-registry'; import type { ChangeItem, GitRepositoryService } from '../git-repository-service'; import type { - CommitChangelistItem, CommitFileItem, CommitViewState, HostToWebviewMessage, WebviewToHostMessage, } from '../../shared/protocol'; import type { CommitService } from '../commit/commit-service'; -import { getBaseStyles, ICON_CHEVRON_DOWN, ICON_ELLIPSIS } from './shared-styles'; +import { getBaseStyles, ICON_CHEVRON_DOWN } from './shared-styles'; import { getNonce } from './nonce'; /** * Commit 提交窗口(WebviewView,自绘提交面板)。 * - * 承载活动 changelist 文件列表(平铺 / 目录树两态可切)+ 文件单击看 diff + 单文件右键操作 + - * changelist 切换与管理(由原 Changes 视图平移而来)+ 多行 Commit Message 编辑器 + - * Amend/sign-off/skip-hooks 选项 + Commit/Commit and Push 按钮 + Conventional Commits 实时校验 + - * 最近消息复用。选中态由 webview 端管理(host 不回写,避免覆盖用户操作)。 + * 承载活动 changelist 文件列表(平铺 / 目录树两态,切换上移标题栏互斥图标)+ 文件单击看 diff + + * 单文件右键操作 + 多行 Commit Message 编辑器 + Amend/sign-off/skip-hooks 选项 + + * Commit/Commit and Push 按钮 + Conventional Commits 实时校验 + 最近消息复用。 + * changelist 切换与管理(New/Rename/Delete)由标题栏 $(checklist) 图标 → setActiveChangelist + * QuickPick 承载,活动列表名常驻 view.description 副标题(Default 省略——无信息量不常驻); + * 标题栏「…」菜单 Discard Changes 批量撤销勾选文件(勾选集经 checkedChanged 单向镜像至 host)。 + * Select All 吸顶于文件列表容器内首行。 + * 选中态由 webview 端管理(host 不回写,避免覆盖用户操作)。 * 注:活动栏未提交数角标已迁至隐藏的 hyperGit.changesBadge TreeView 承载(见 extension.ts)。 */ -export class CommitWebviewProvider implements vscode.WebviewViewProvider { +export class CommitWebviewProvider implements vscode.WebviewViewProvider, vscode.Disposable { public static readonly viewType = 'hyperGit.commit'; private view?: vscode.WebviewView; private currentMessage = ''; + /** 文件列表展示模式(List/Tree,标题栏图标切换):host 为事实源,随 state 整态下发。 */ + private detailMode: 'flat' | 'tree' = 'flat'; + /** 勾选集镜像(webview 事实源单向同步):标题栏「…」批量 Discard 数据源,切仓库时置空待重推。 */ + private checkedPaths: readonly string[] = []; + private readonly disposables: vscode.Disposable[] = []; + + /** List/Tree 偏好按仓库持久化 key(issue #107 同 log.dmode 范式;第三视图复用时提炼共享 helper)。 */ + private static dmodeKey(root: string): string { + return `hyperGit.commit.dmode:${root}`; + } + /** context key 同步(标题栏互斥按钮显隐由 when 子句驱动)。 */ + private static setCtx(key: string, value: string | boolean): void { + void vscode.commands.executeCommand('setContext', key, value); + } constructor( private readonly service: GitRepositoryService, private readonly registry: ChangelistRegistry, private readonly commit: CommitService, - ) {} + private readonly workspaceState: vscode.Memento, + ) { + // 激活即恢复当前仓库的 List/Tree 记忆并同步 context key(标题栏控件先于视图解析渲染); + // 活跃仓库切换(issue #107):恢复该仓库的记忆值(无记忆回退 'flat'), + // 文件列表刷新由 extension 的 onDidChange → refreshAll 驱动。 + this.loadRepoScopedPrefs(); + this.disposables.push( + service.onDidChangeRepository(() => { + this.loadRepoScopedPrefs(); + }), + ); + } + + dispose(): void { + for (const d of this.disposables) { + d.dispose(); + } + this.disposables.length = 0; + } + + /** 装载当前仓库的 List/Tree 偏好(memento → 内存 + context key);勾选镜像置空待 webview 重推。 */ + private loadRepoScopedPrefs(): void { + const root = this.service.repoRoot; + this.detailMode = + (root ? this.workspaceState.get<'flat' | 'tree'>(CommitWebviewProvider.dmodeKey(root)) : undefined) ?? 'flat'; + this.checkedPaths = []; + CommitWebviewProvider.setCtx('hyperGit.commit.tree', this.detailMode === 'tree'); + } + + /** 标题栏 List/Tree 互斥图标切换(同 Graph/Branches 范式):pushState 全同步且幂等,整态重发即达。 */ + setDetailMode(mode: 'flat' | 'tree'): void { + if (this.detailMode === mode) { + return; + } + const root = this.service.repoRoot; + if (root) { + void this.workspaceState.update(CommitWebviewProvider.dmodeKey(root), mode); + } + this.detailMode = mode; + CommitWebviewProvider.setCtx('hyperGit.commit.tree', mode === 'tree'); + this.pushState(); + } resolveWebviewView(view: vscode.WebviewView): void { this.view = view; @@ -52,6 +110,18 @@ export class CommitWebviewProvider implements vscode.WebviewViewProvider { this.pushState(); } + /** + * 标题栏「…」批量 Discard:范围 = 勾选集镜像(已由 webview 调和至当前活动 changelist 文件), + * 确认与执行统一复用 discardChanges(单/多同路径:modal 确认 + 未跟踪 clean / 已跟踪 restore)。 + */ + discardChecked(): void { + if (this.checkedPaths.length === 0) { + void vscode.window.showInformationMessage('No checked files to discard.'); + return; + } + void vscode.commands.executeCommand('hyperGit.discardChanges', this.checkedPaths); + } + private onMessage(msg: WebviewToHostMessage): void { switch (msg.type) { case 'requestState': @@ -74,11 +144,8 @@ export class CommitWebviewProvider implements vscode.WebviewViewProvider { case 'commit/fileMenu': void this.handleFileMenu(msg.payload.path); break; - case 'commit/setActive': - void vscode.commands.executeCommand('hyperGit.setActiveChangelist', msg.payload.id); - break; - case 'commit/changelistMenu': - void this.handleChangelistMenu(msg.payload.id); + case 'commit/checkedChanged': + this.checkedPaths = msg.payload.paths; break; } } @@ -93,7 +160,7 @@ export class CommitWebviewProvider implements vscode.WebviewViewProvider { if (!change) { return; } - // QuickPick label 支持 $(codicon) 内联图标(与 changelist 菜单对齐)。 + // QuickPick label 支持 $(codicon) 内联图标(与 changelist 标题栏 QuickPick 对齐)。 const actions: ReadonlyArray<{ readonly label: string; readonly command: string }> = [ { label: '$(diff) Open Diff', command: 'hyperGit.openDiff' }, { label: '$(symbol-enum) Move to Changelist…', command: 'hyperGit.moveChangelist' }, @@ -110,30 +177,6 @@ export class CommitWebviewProvider implements vscode.WebviewViewProvider { await vscode.commands.executeCommand(pick.command, change); } - /** changelist 头部 ⋯ 菜单:新建 / 重命名 / 删除(默认列表不可改名删除,复用既有命令)。 */ - private async handleChangelistMenu(id: string): Promise { - const def = this.registry.getDef(id); - const canModify = Boolean(def) && id !== 'default'; - const items: Array<{ label: string; op: 'new' | 'rename' | 'delete' }> = [ - { label: '$(add) New Changelist…', op: 'new' }, - ]; - if (canModify && def) { - items.push({ label: `$(edit) Rename "${def.name}"…`, op: 'rename' }); - items.push({ label: `$(trash) Delete "${def.name}"…`, op: 'delete' }); - } - const pick = await vscode.window.showQuickPick(items, { placeHolder: 'Changelist actions' }); - if (!pick) { - return; - } - if (pick.op === 'new') { - await vscode.commands.executeCommand('hyperGit.newChangelist'); - } else if (pick.op === 'rename') { - await vscode.commands.executeCommand('hyperGit.renameChangelist', id); - } else { - await vscode.commands.executeCommand('hyperGit.deleteChangelist', id); - } - } - private sendValidation(): void { this.post({ type: 'conventionalValidation', payload: this.commit.validateMessage(this.currentMessage) }); } @@ -169,21 +212,20 @@ export class CommitWebviewProvider implements vscode.WebviewViewProvider { const changes = this.service.getChanges(); const groups = this.registry.getGroups(changes, (c) => c.relativePath); const activeId = this.registry.activeChangelistId; - const changelists: CommitChangelistItem[] = groups.map((g) => ({ id: g.id, name: g.name, count: g.items.length })); const activeGroup = groups.find((g) => g.id === activeId) ?? groups.find((g) => g.active) ?? groups[0]; const files = (activeGroup?.items ?? []).map((c) => this.toFileItem(c)); const state: CommitViewState = { template: this.commit.getTemplate(), recentMessages: this.commit.getRecentMessages(), - activeChangelistName: this.registry.getDef(activeId)?.name ?? 'Default', - activeChangelistId: activeId, - changelists, + mode: this.detailMode, files, tree: buildFileTree(files.map((f) => f.path)), conventionalEnabled: this.commit.conventionalEnabled(), busy: false, repoRoot: this.service.repoRoot ?? '', }; + // 标题栏副标题 = 活动 changelist 名(原 webview 头部切换行上移;Default 省略——无信息量不常驻)。 + this.view.description = activeId === 'default' ? undefined : this.registry.getDef(activeId)?.name; this.post({ type: 'state', payload: state }); this.sendValidation(); } @@ -204,14 +246,9 @@ export class CommitWebviewProvider implements vscode.WebviewViewProvider { -
- Active Changelist: - - -
-