Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ jobs:
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
npm run test:roslyn-gateway
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
npm run test:owner-death
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
node scripts/verify-owner-death.mjs --repomix
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
npm run test:manual-release
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Preserve bounded check report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand All @@ -63,5 +69,7 @@ jobs:
test-tmp/error-contracts/**/report.json
test-tmp/roslyn-host/**/report.json
test-tmp/roslyn-gateway/**/report.json
test-tmp/owner-death/**/report.json
test-tmp/manual-release/**/report.json
if-no-files-found: warn
retention-days: 7
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 0.14.0 (unreleased)

- Make stale/failed tray observations explicit, refresh passively before manual release, report registration failures and keep native listeners available after malformed registrations. Preserve warm Roslyn state across settings visibility and connection changes.
- Bind native Release source inputs and complete published outputs at build time; reject stale native artifacts even when regenerating the delivery manifest.

- Keep automatic Roslyn release off; add a reversible manual release that preserves Gateway, watcher, bounded cache and diagnostics. Reject busy/cleanup/workspace-switch races, invalidate old symbol locations and reuse the existing recovery gate if cleanup fails. Explicit searches can reload afterward.
- Add an optional independent Windows WinForms tray/settings entry with passive instance state, manual release and selected-instance shutdown. Enable each Gateway with `--tray`; start Tray manually. No idle timer, automatic startup, global settings file or additional MCP tools.
- Bound the local Named Pipe protocol, authenticate local user/session and client PID, retain disconnected/unknown states, and reconnect without replaying control actions. Closing settings or Tray does not stop MCP.
- Include the optional Tray in locked builds and delivery fingerprints. Add concurrency/IPC regression, actual WinForms with two isolated MCP fixtures, and ten actual Roslyn release/reload cycles with owned-process and resource evidence. Acceptance details and platform limits are recorded in the work log.

## 0.13.4 (unreleased)

- Retain startup checks for UIA platform/configuration/published files, while deferring the native health probe until explicit diagnosis. Actual UI requests execute directly and update the known observation from their response.
- Preserve unknown availability before any runtime observation and retain first-operation errors separately. Coalesce concurrent non-forced health probes using the existing mutex and cached observation.
- Add a three-sample runtime/startup measurement script and first-use/concurrency/recovery regression coverage. Cache, fingerprints, watchers and Roslyn initialization policies remain as before.

## 0.13.3 (unreleased)

- Verify each native Helper's owning Gateway through its actual ancestor chain, creation times and a held Windows process handle before project evaluation or UI access. The private launch environment supports development wrappers without identifying client applications by name.
- Cancel work when the owner exits, then terminate only the orphaned Helper after a two-second grace period even if native calls or cancellation callbacks block. Code Host retains its existing Job coverage for descendants; this does not cover a live but unresponsive Gateway or the separate Repomix process path.
- Preserve UIA stdin EOF as the request boundary and retain the Code Host protocol. Add isolated owner-lifetime, handle-disposal and multi-instance tests plus real initial-MSBuild Gateway-death acceptance and CI receipts.

## 0.13.2

- Exit the production stdio entry on EOF/closed pipes, unify shutdown events and terminate on process-level fatal errors. Connect transport before initialization so disconnect can cancel startup.
Expand Down
12 changes: 11 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ Reports and bounded stage logs are under `test-tmp/check/<run>/`. CI uploads onl

`npm run test:roslyn-host` and `npm run test:roslyn-gateway` use generated C# projects and an already installed SDK selected by `scripts/lib/dotnet.mjs`. The gateway check copies the entire published Code Host into a Chinese path with spaces and checks real overloads, stale identities and owned MSBuild descendants. This is not a clean-machine test or verification of the current Codex connection.

`npm run test:owner-death` kills only a generated Gateway during confirmed initial MSBuild work and checks all previously observed process identities for survivors. `node scripts/verify-owner-death.mjs --repomix` audits the actual adapter with a controlled Node CLI, without installing Repomix. `check:desktop` also runs the `--desktop` owner-death scenario against the isolated WPF fixture: the UIA Helper must exit while the target remains alive; the fixture is closed separately after recording that result. Reports are kept under `test-tmp/owner-death/`. These checks never identify client applications by process name.

`node scripts/measure-runtime-baseline.mjs` creates a small C# project, runs three fresh stdio clients, measures unused/cold/warm/exit phases, and profiles source Router startup separately. It records snapshots under `test-tmp/runtime-baseline/`, using the existing SDK. Run it without competing check jobs for a comparison; samples share OS/SDK caches and do not establish a p95, a clean-machine benchmark, or installed-client acceptance. Instrumented startup I/O counts cover the selected asynchronous Node filesystem methods and `child_process.spawn`, not all native/kernel I/O.

For opt-in fixed-profile TavernDesk acceptance, use `npm run test:tavern-context -- <repository> --ui-pid=<PID> --ui-hwnd=<HWND>` and `npm run test:product -- <repository> <PID> <HWND>`. The latter checks the fixed profile receipt and performs six navigation-to-source tasks without source filenames supplied in advance. Native candidate discovery is counted, source reads used only as the oracle are separate, and all returned bodies are checked against current file hashes. Source candidates remain distinct from verified runtime bindings. The scripts do not install prerequisites, launch the target application or use personal databases.

The delivery manifest covers Gateway JavaScript, all published Host files including dependency sidecars, four managed Skill documents, and package/SDK/Host lock configuration. It records the Git revision and toolchains. Timestamps and checkout paths do not participate in content identity. Hashes detect local mismatches; they are not signatures. Run a complete check after changing delivery inputs. Keep a complete previous checkout/artifact set for rollback; do not mix old DLLs with a new Gateway.
The delivery manifest covers Gateway JavaScript, all published Host files including dependency sidecars, four managed Skill documents, and package/SDK/Host lock configuration. It records the Git revision and toolchains. Timestamps and checkout paths do not participate in content identity. Native Release builds run through `scripts/publish-native.mjs host|codeHost|tray` after locked restore (automatically inside `check`). The build records repository source/shared files and build settings before compilation, verifies they stayed unchanged, and seals the complete publish output. Delivery verification rejects changed source or artifacts; regenerating a delivery manifest cannot bless old DLLs. Direct `dotnet publish` is suitable for isolated fixtures; use the canonical script for delivery. This is a repository input fingerprint, not an attestation of arbitrary external MSBuild imports or the SDK installation. Hashes detect local mismatches; they are not signatures. Run a complete check after changing delivery inputs. Keep a complete previous checkout/artifact set for rollback; do not mix old DLLs with a new Gateway.

Production startup uses `npm start` and the published Release Host. `npm run dev` explicitly enables Debug/dotnet-run fallback. `customHostPath` remains an explicit configuration override; an old helper must not be mistaken for a verified current release. Rebuilds do not replace a running client's MCP connection.

Expand All @@ -34,3 +38,9 @@ SDK policy follows [Microsoft global.json guidance](https://learn.microsoft.com/
Current implementation is described in the [architecture guide](WinCode-架构与数据流说明.md). Remaining work is maintained in the [active engineering plan](WinCode-下一轮工程化迭代计划书.md); completed work belongs in CHANGELOG and the append-only work log. For documentation-only changes, verify local links, commands, version claims and evidence boundaries; do not claim a new runtime regression without running it.

Node 22 CI runs `npm run test:error-contracts` and uploads its bounded report. It exercises protocol errors, matching tool-error text/structured payloads, real generated-file trash failures and workspace recovery; injected UI images test serialization only. Run it locally after changes to these boundaries.

`npm run test:manual-release` verifies ten actual Roslyn release/reload cycles in generated C# projects, old-location rejection, stable owned resources, retained cache/watcher, edits while cold and A/B workspace reuse. The optional Tray is built and version/fingerprint checked by `check`; `check:desktop` additionally runs `npm run test:tray`, exercising actual WinForms and secured Named Pipes with two isolated stdio MCP clients and simulated Roslyn lifetimes. Reports are under `test-tmp/manual-release/` and `test-tmp/tray/`. UI screenshots are local only; these checks do not enable autostart or alter installed MCP client configuration.

After a nontrivial test failure, investigate official documentation and relevant real GitHub implementations/issues before choosing a fix. Record the observed failure, applicability of the reference and actual rerun result; do not replace verification with copied examples or arbitrary longer sleeps. Obvious syntax, object-shape and path mistakes can be corrected directly.

`npm run test:tray-workflow` (also in `check:desktop`) uses two compiled stdio MCP instances, real C# fixtures, the native settings handlers and authenticated pipes. It checks warm-state continuity across spaced queries, busy refusal, targeted release/recovery and Tray exit. No active Codex configuration is changed.
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<p align="center">
<a href="#english">English</a> · <a href="#简体中文">简体中文</a><br>
<img src="https://img.shields.io/badge/Platform-Windows%20x64-0078D6" alt="Windows x64">
<img src="https://img.shields.io/badge/Platform-Windows%2011%20x64-0078D6" alt="Windows 11 x64">
<img src="https://img.shields.io/badge/MCP-stdio-black" alt="MCP stdio">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT license"></a>
</p>
Expand All @@ -22,7 +22,9 @@ WinCode is a local MCP server built for Windows and .NET engineering. It bridges
- **Inspect the running app:** Enumerate visible windows, query specific controls or subtrees, and capture numbered visual overlays without activating or stealing focus from the target.
- **Review with evidence:** Trace on-screen widgets back to literal XAML declaration tags, line numbers, and file hashes, with transparent reporting for ambiguity, truncation, or degraded upstreams.

Current source version: **0.13.2**. All UI tools are strictly read-only and non-destructive. See [CHANGELOG](CHANGELOG.md) for full version history.
Current source version: **0.14.0**. All UI tools are strictly read-only and non-destructive. See [CHANGELOG](CHANGELOG.md) for full version history.

**Platform and compatibility:** Windows 11 x64 is the baseline for this project's local development and testing. Identical functionality, behavior, and performance are not guaranteed on other operating systems, other Windows versions, or different dependency versions. macOS and Linux users are encouraged to **fork this repository and adapt and validate it locally** for their platform. Use the dependency versions documented and pinned in this repository as the reference environment.

### Quick start

Expand Down Expand Up @@ -84,6 +86,20 @@ Add each argument as a separate entry, without extra surrounding quotes even whe

For prompt engineering and token-efficient skill routing, refer to the optional [Skill and MCP setup guide](WinCode-Skill制作与MCP配置指南.md).

### Optional tray and manual memory release

Automatic Roslyn release is **off**. This version provides no idle timer or automatic-release switch. A loaded semantic workspace stays warm for successive Agent calls. To release it when you decide it is no longer needed:

1. Build with `npm run check`, then run `tools/WinCode.Tray/bin/Release/net10.0-windows/win-x64/publish/WinCode.Tray.exe --show` from the repository. It requires the .NET 10 Windows Desktop runtime and does not install itself or enable Windows startup.
2. Add `--tray` as a separate argument to each Gateway you want to see, then refresh that MCP connection. For example: `"args": ["C:/path/to/WinCode/dist/index.js", "--workspace", "C:/path/to/project", "--tray"]`. Keep your existing explicit `--roslyn-config` arguments if using Roslyn.
3. Open **设置 / 内存管理**, refresh the observed state, select an idle instance and click **释放 Roslyn 内存**. A busy instance refuses the action; it does not queue a release for later. Requests arriving after a release has started wait for it to finish.

“暂无在途请求” means no request is currently in flight, not that the Agent has finished its task. Failed refreshes or observations older than 30 seconds are shown as unknown; manual release first obtains a new passive status. Opening, refreshing, hiding, or reconnecting Tray never releases or reloads Roslyn. Registration errors are reported in settings and Gateway diagnostics.

Release closes only that instance's owned Roslyn Host and invalidates its symbol locations. The next explicit symbol search reloads the project; old `symbolLocation` values require a new search. Gateway, workspace watcher, bounded cache, and last diagnostics remain. Local-text instances have no Roslyn memory to release.

Tray and Gateway are independent. Hiding settings or exiting Tray leaves MCP running; **停止此实例** requests that selected Gateway's normal shutdown after confirmation. Start Tray manually when needed; it can connect before or after an opted-in Gateway. The current limit is eight connected Gateways per Windows user/session. Use the same Windows user and privilege level. State is observed on registration/open/refresh, not continuously polled; disconnected means unknown, and the connection count does not include old or unregistered instances. Remove `--tray` and reconnect to disable integration. Windows 11 is the tested platform; alternate permissions, Explorer recovery and other DPI configurations need separate validation.

### Practical walkthrough: Targeted control inspection

Query specific controls directly rather than dumping an entire window's visual tree (which can easily span thousands of nodes and exhaust context limits):
Expand Down Expand Up @@ -234,7 +250,9 @@ WinCode 是面向 Windows 与 .NET 工程研发的本地 MCP 服务。它将项
- **观察实际界面:**发现系统可见窗口,按条件定向查询目标控件或子树,并在不激活、不抢占前台焦点的前提下获取数字标注截图。
- **源码双向印证:**将运行时抓取的控件关联回 XAML 源码声明的起始行号、代码片段与文件哈希,清晰报告歧义、截断与降级状态。

当前源码版本为 **0.13.2**。所有 UI 取证工具均为纯只读与非侵入设计。版本历史见 [CHANGELOG](CHANGELOG.md)。
当前源码版本为 **0.14.0**。所有 UI 取证工具均为纯只读与非侵入设计。版本历史见 [CHANGELOG](CHANGELOG.md)。

**平台与兼容性说明:**本项目以 **Windows 11 x64** 为本地开发与测试基准。其他操作系统、其他 Windows 版本或不同依赖版本下,功能表现、运行行为与性能不保证完全一致。建议 **macOS、Linux 用户通过 fork 本仓库进行本地适配与验证**;请以本项目文档和锁定文件中列出的依赖版本作为参考环境。

### 快速上手

Expand Down Expand Up @@ -296,6 +314,20 @@ npm run delivery:verify

如需配合 Agent Skill 获得低 Token 开销的精准任务路由,请参阅可选的 [Skill 与 MCP 配置指南](WinCode-Skill制作与MCP配置指南.md)。

### 可选托盘与手动释放内存

**自动释放保持关闭**,本版没有 idle 定时器或自动释放开关。Roslyn 加载后会保留,优先保障 Agent 连续工作;确实不再需要时,由你在设置里主动释放。

1. 完成 `npm run check` 后,运行仓库内 `tools/WinCode.Tray/bin/Release/net10.0-windows/win-x64/publish/WinCode.Tray.exe --show`。使用已有 .NET 10 Windows Desktop 运行时,不安装服务,不设置 Windows 自启动。
2. 给需要管理的 MCP 启动参数单独加上 `--tray`,再刷新该 MCP 连接。例如 `"args": ["C:/path/to/WinCode/dist/index.js", "--workspace", "C:/path/to/project", "--tray"]`。已配置 Roslyn 时保留原有 `--roslyn-config` 参数。
3. 打开“设置 / 内存管理”,刷新状态、选择空闲实例,点击“释放 Roslyn 内存”。实例忙碌或仍在收尾时拒绝本次释放,不排队延后释放;释放开始后到来的请求等待其完成。

只关闭所选实例拥有的 Roslyn Host 并失效旧符号定位;下一次显式搜索才重新加载,旧 `symbolLocation` 必须重新搜索。Gateway、工作区 watcher、现有受限缓存和最后诊断保留。local-text 实例没有 Roslyn 内存可释放。

“暂无在途请求”不代表 Agent 已结束任务。刷新失败或观察超过 30 秒时显示状态未知;手动释放前先获取新状态,超时不会接着释放。打开、刷新、隐藏设置及托盘重连均不触发 Roslyn 启停。注册失败原因会显示在设置和 Gateway 诊断输出中。

关闭设置窗口会收回托盘;“退出托盘”不影响 MCP。“停止此实例”经确认后请求该 Gateway 正常退出,客户端可能重新建立一个新实例。托盘和 Gateway 可按任意顺序手动启动;每个 Windows 用户/登录会话目前最多连接八个 Gateway,应使用同一用户和权限级别。状态仅在注册、打开或手动刷新时更新,不持续轮询;失联表示未知,连接数不含旧版或未注册实例。移除 `--tray` 并刷新 MCP 连接即可禁用集成。其他权限、Explorer 重启和不同 DPI 仍需单独验证。

### 实战示例:精准定位并分析目标控件

大型桌面应用的完整控件树动辄包含成百上千个视觉节点。若直接全量导出,不仅耗尽 Agent 上下文,还会增加定位干扰。WinCode 支持按条件精准定位目标控件子树:
Expand Down
Loading