From 87f6368213567c676ba1c9798934a5dbeba3e22b Mon Sep 17 00:00:00 2001
From: Eric Wang
Date: Sun, 18 Oct 2026 10:30:50 -0700
Subject: [PATCH] 0.2.1: show it working; fixes the recordings found
The README now leads with four clips of real sessions (dsh with
deepseek-flash driving SOLIDWORKS 2019): a flange modeled from one
paragraph, a face the user selected rounded on request, a shaft and the
flange assembled and checked, and the drawing exported. Captions are the
sessions' own calls, results and closing words; scripts/clips/ is how
they are made. Long text moved into collapsible sections.
Recording them found real problems, fixed here:
- "flange.slddrw" resolved to the part flange.sldprt, so sw_export
wrote the wrong document. Exact names win; a SOLIDWORKS extension only
matches its own document type.
- a cut sketched on a plane at the body's boundary removed nothing; with
no direction given it is now made the other way and says reversed.
- in documents the bridge made, builds and assemblies zoom to fit, so a
person watching sees the result.
- a bridge older than the plugin answers "run pair.cmd again" instead of
"unknown tool".
Also CONTRIBUTING.md and a bug report form. Live: 66 of 66 checks on
SOLIDWORKS 2016 SP1 and 2019 SP0 with the release exe (fd6ec61c...).
Co-Authored-By: Claude Opus 5.5
---
.github/ISSUE_TEMPLATE/bug.yml | 33 +++++
.github/ISSUE_TEMPLATE/config.yml | 5 +
CHANGELOG.md | 30 +++++
CONTRIBUTING.md | 42 ++++++
README.md | 151 ++++++++++++---------
README.zh.md | 96 ++++++++------
bridge/SwBridge.csproj | 2 +-
bridge/app.manifest | 2 +-
bridge/src/SelfTest.cs | 16 +++
bridge/src/Sw/Docs.cs | 35 +++--
bridge/src/Tools/AssemblyTools.cs | 1 +
bridge/src/Tools/ModelTools.cs | 36 ++++-
bridge/swbridge.exe.sha256 | 2 +-
docs/images/flange-drawing.png | Bin 0 -> 56261 bytes
docs/images/flange-views.png | Bin 0 -> 305643 bytes
docs/images/plate-drawing.png | Bin 29673 -> 0 bytes
docs/images/plate-views.png | Bin 246046 -> 0 bytes
docs/media/assemble.gif | Bin 0 -> 769630 bytes
docs/media/drawing.gif | Bin 0 -> 390380 bytes
docs/media/flange.gif | Bin 0 -> 436626 bytes
docs/media/select.gif | Bin 0 -> 577503 bytes
lib/index.js | 13 +-
lib/tools.js | 7 +-
llms.txt | 2 +-
package.json | 2 +-
protocol/tools.json | 4 +-
scripts/clips/README.md | 29 +++++
scripts/clips/compose.py | 209 ++++++++++++++++++++++++++++++
scripts/clips/record-window.ps1 | 48 +++++++
scripts/clips/stamp.mjs | 7 +
tests/claims.spec.mjs | 11 +-
tests/live/e2e.mjs | 18 ++-
tests/plugin.spec.mjs | 5 +
33 files changed, 680 insertions(+), 126 deletions(-)
create mode 100644 .github/ISSUE_TEMPLATE/bug.yml
create mode 100644 .github/ISSUE_TEMPLATE/config.yml
create mode 100644 CONTRIBUTING.md
create mode 100644 docs/images/flange-drawing.png
create mode 100644 docs/images/flange-views.png
delete mode 100644 docs/images/plate-drawing.png
delete mode 100644 docs/images/plate-views.png
create mode 100644 docs/media/assemble.gif
create mode 100644 docs/media/drawing.gif
create mode 100644 docs/media/flange.gif
create mode 100644 docs/media/select.gif
create mode 100644 scripts/clips/README.md
create mode 100644 scripts/clips/compose.py
create mode 100644 scripts/clips/record-window.ps1
create mode 100644 scripts/clips/stamp.mjs
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
new file mode 100644
index 0000000..448106e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -0,0 +1,33 @@
+name: Bug report
+description: Something the tools did wrong, or could not do
+labels: [bug]
+body:
+ - type: textarea
+ id: what
+ attributes:
+ label: What happened
+ description: What you asked dsh, what it called, and what came back. The tool's error text ("CODE: sentence") helps most.
+ validations:
+ required: true
+ - type: textarea
+ id: status
+ attributes:
+ label: swbridge status
+ description: On the Windows PC, run `%LOCALAPPDATA%\dsh-solidworks\swbridge.exe status` (or the bundled bin\swbridge.exe status) and paste the output.
+ render: text
+ validations:
+ required: true
+ - type: input
+ id: versions
+ attributes:
+ label: dsh and plugin versions
+ description: "`dsh --version` and the plugin version, e.g. dsh 0.2.0-rc.2, @dshworks/dsh-solidworks 0.2.1"
+ - type: dropdown
+ id: setup
+ attributes:
+ label: Setup
+ options:
+ - dsh and SOLIDWORKS on the same Windows PC
+ - dsh on a Mac, SOLIDWORKS in a Windows VM
+ - dsh and SOLIDWORKS on different machines
+ - another MCP client (swbridge.exe mcp)
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..b776509
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: true
+contact_links:
+ - name: Security report
+ url: https://github.com/dshworks/dsh-solidworks/security/advisories/new
+ about: Please report security problems privately.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eba58a2..686638f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,35 @@
# Changelog
+## 0.2.1 - 2026-10-18
+
+Fixes found by recording the README's clips: real sessions, watched.
+
+- Fixed: a document name with a SOLIDWORKS extension could resolve to another
+ document with the same stem. With a part `flange` and its drawing open,
+ `sw_export {doc: "flange.slddrw"}` exported the part, and the model spent
+ twenty calls working out why the PDF was wrong. An exact path, file name or
+ title now always wins, and a `.sldprt`, `.sldasm` or `.slddrw` name only
+ matches that kind of document.
+- `sw_build`: a cut sketched on a plane at the body's boundary points away from
+ the material and removed nothing; the model needed three calls to find
+ `reverse`. With no direction given, a cut that removes nothing is now made the
+ other way, and the step says `reversed`.
+- In documents the bridge opened or made, `sw_build` and `sw_assemble` zoom to
+ fit afterwards, so someone watching SOLIDWORKS sees what was built. A user's
+ own documents keep the view they set.
+- Plugin: when the bridge on another machine is older than the plugin (paired
+ before an update) and does not know a tool, the answer says to run `pair.cmd`
+ again, instead of "unknown tool".
+- `sw_build`'s description and the system-prompt guide give a concrete example
+ of Y-up (a 12 mm plate on the top plane has its top face at y = 12), after a
+ session put a top face at z = 12.
+- README leads with four clips of real sessions; how they are made is in
+ `scripts/clips/`. CONTRIBUTING.md and a bug report form.
+- Live: 66 of 66 checks on SOLIDWORKS 2016 SP1 and 2019 SP0 with this exe,
+ including the two fixes above.
+- Reproducible bridge build: `swbridge.exe` SHA-256
+ `fd6ec61c1e1200f3d45f618bdeed1a1140e78a88ecb68185bffe8a6b7fbd304b` (SDK 8.0.426).
+
## 0.2.0 - 2026-10-18
From reading models to making them: 8 new tools, 21 in all.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..a6935b2
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,42 @@
+# Contributing
+
+Bug reports with `swbridge status` pasted in are the most useful thing right
+now, especially from SOLIDWORKS releases and Windows versions this has not run
+on yet (2020 and later, Windows 10, x64 hardware). Use the bug report form.
+
+## The rules the code keeps
+
+- **The Windows side decides what may be written.** A change that lets dsh save
+ over a source file, write outside the allowed folders, delete a feature the
+ bridge did not build, or discard someone's unsaved work will not be merged.
+- **2016 is the floor.** Every SOLIDWORKS member the bridge calls is a string
+ literal listed in `bridge/compat/members.tsv`, and must exist in the 2016 SP1
+ interop (`bridge/compat/sw2016.tsv`). `npm test` checks both.
+- **One contract.** Tools are defined once, in `protocol/tools.json`; the bridge
+ embeds it and the plugin builds dsh tools from it. Keep it in its layout with
+ `node scripts/format-contract.mjs`.
+- **Pages may not say what the code does not do.** `tests/claims.spec.mjs`
+ checks the README's numbers, tables and links; change both together.
+
+## Running it
+
+```
+npm install && npm test # Linux or macOS is enough: plugin, contract, 2016 members, claims
+npm run build:bridge # .NET 8 SDK (version pinned in global.json); writes bin/swbridge.exe
+```
+
+`swbridge selftest` runs the bridge's own checks on Windows without SOLIDWORKS.
+
+The live checks need a Windows machine with SOLIDWORKS 2016 or later, copies of
+the stock samples (`box.sldprt`, `box.slddrw`, `bolt.sldprt`,
+`bolt-assembly.sldasm`) in one folder, and a bridge that can dial this machine:
+
+```
+SW_LINK_TOKEN= SW_SAMPLES='C:\samples' node tests/live/e2e.mjs
+swbridge connect :47321 --out C:\out # on Windows
+```
+
+They open only those copies, make their own documents, and close everything
+they opened or made; a final check confirms the documents that were open before
+are as they were. If the build changes `bin/swbridge.exe`, update
+`bridge/swbridge.exe.sha256`; CI rebuilds the exe and compares.
diff --git a/README.md b/README.md
index 5a20be8..265010f 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@
-A session ·
+Watch it ·
Get started ·
Tools ·
What it will not do ·
@@ -37,56 +37,56 @@
---
-You ask dsh for a part, and it models it in your SOLIDWORKS: sketches,
-extrudes, revolves, holes, fillets, chamfers, shells. It checks the result
-against the geometry it meant to make, looks at it, puts parts together with
-mates, checks them for collisions, counts them into a bill of materials, and
-makes a dimensioned drawing. It also works on the files you already have: it
-opens them read-only, answers from the model itself, tries new values in memory,
-and writes STEP, PDF or native copies back into your project. Your files on disk
-do not change unless you turn that on.
-
-## A session
-
-A real run (dsh 0.2.0-rc.2, `deepseek-flash`, SOLIDWORKS 2019 SP0 in a
-Parallels VM, dsh on the other side of the link). The prompt:
-
-> Make a new part: a 100 x 60 x 20 mm plate lying flat on the top plane,
-> centred on the origin, with a 20 mm hole through its centre and its four
-> vertical edges rounded at 5 mm. Check that its volume is what that geometry
-> should give. Show it from four sides in one picture and say what you see.
-> Save it as plate.sldprt, make a drawing of it and export the drawing as
-> plate.pdf. Then close everything you made.
-
-dsh called `sw_status`, `sw_new`, `sw_build` (one call: the sketch, a mid-plane
-extrude, the hole, and the fillets on four edges it picked by points on them),
-`sw_view` (four views on one picture), `sw_save`, `sw_drawing`, `sw_view` (the
-drawing), `sw_export`, `sw_close` twice and `sw_status`. Eleven calls, all
-completed. From its answer:
-
-> **Volume:** tool returned **113287.61102 mm³**. Checked against the geometry:
-> 100×60×20 = 120000; minus ⌀20 through hole π·10²·20 = 6283.185; minus four
-> 5 mm corner rounds, 4·(25 − π·25/4)·20 = 429.204 → expected **113287.611
-> mm³**. Exact match.
->
-> **Top:** 100 × 60 outline with all four corners rounded and the ⌀20 hole dead
-> centre. **Front:** plain 100 × 20 rectangle; the vertical hole is internal
-> from this direction, so no hole outline.
->
-> **Drawing:** third angle, scale 1:1, sheet 279.4 × 215.9 mm; front, top,
-> right and isometric views; 2 dimensions imported (R5 and 20.00 shown on the
-> sheet). **PDF:** local copy `solidworks/plate.pdf`. **Closed:** the drawing
-> (unsaved changes discarded) and `plate.sldprt`; documents: [].
-
-
-
+
-The model checked the volume with its own arithmetic, not by trusting the
-tool. What it says about each view comes from the picture: no number the tools
-returned says the front view hides the hole. The PDF arrived in the dsh
-workspace, checked against the bridge's SHA-256.
+**One paragraph in, a flange out.** You describe the part; dsh models it in the
+SOLIDWORKS on your desk, checks the volume against its own arithmetic
+(158,983.493 mm³ both ways), and looks at it from four sides. It can also put
+parts together, check them for collisions, count a bill of materials, make the
+drawing, and work on the files you already have, read-only unless you say
+otherwise.
+
+## Watch it
+
+Every clip is a real session: dsh 0.2.0-rc.2 with `deepseek-flash`, SOLIDWORKS
+2019 SP0 in a Parallels VM, recorded from the SOLIDWORKS window. The captions
+are the session's own tool calls and results, and its own closing words. Time
+spent thinking is fast-forwarded; nothing is cut or reordered.
+[How the clips are made](scripts/clips/README.md).
+
+### Point at it
+
+You select a face in SOLIDWORKS and say what to do with it. dsh reads your
+selection (the flange's top face, 8 edges), rounds every edge of it at 2 mm, and
+explains why the volume went down by 360.218 mm³ even though one fillet adds
+material.
+
+
+
+### Put parts together, check they fit
+
+A shaft is modeled, both parts saved, and assembled with the shaft through the
+bore and the flange 40 mm up. Interference: 0 pairs. dsh also counts faces that
+only touch, and points out that a 30 mm shaft in a 30 mm bore is a line-to-line
+fit. Then the bill of materials.
+
+
+
+### Draw it
+
+The drawing lays its views out in projection at the largest standard scale that
+fits, brings in the model's dimensions, and comes back to your workspace as a
+PDF.
+
+
+
+
+
+
+
+Left: the PDF as exported. Right: sw_view, four views on one picture, which is how dsh looks at what it made.
## Get started
@@ -150,6 +150,9 @@ the front plane at z = 0, top at y = 0, right at x = 0. Faces and edges are
picked by a point on them. `doc` is a path, file name or title of an open
document and defaults to the active one.
+
+All 21 tools
+
| Tool | What it does |
| --- | --- |
| `sw_status` | Release (2016 SP1, 2019 SP0, ...), every SOLIDWORKS instance, the open documents, and any dialog SOLIDWORKS is waiting on. Called first. |
@@ -174,6 +177,8 @@ document and defaults to the active one.
| `sw_close` | Close a document; unsaved changes are discarded only if this bridge made them. |
| `sw_view` | A PNG of the model: one standard view or up to six on one labelled picture, shaded, wireframe or hidden-line. The user's view is put back afterwards. |
+
+
## What it will not do
The rules are enforced on the Windows side, where the files are. dsh can ask
@@ -222,6 +227,9 @@ or a local one; across anything else, carry it through SSH or a VPN. See
+----- one contract, both halves ----- +----------------------------------+
```
+
+The design, point by point
+
- **One space for geometry.** Sketch points, picks, probes, component origins
and bounding boxes are all model-space millimetres. The bridge maps each
sketch point through the sketch's own transform, so a circle on a face goes
@@ -264,21 +272,26 @@ or a local one; across anything else, carry it through SSH or a VPN. See
dsh's own `createMcpToolDefinition`, so validation, error results and image
admission behave as for any MCP server.
+
+
## Proof
Every claim on this page was run, against real SOLIDWORKS, on 2026-10-18.
| What | Result |
| --- | --- |
-| Live end-to-end, SOLIDWORKS 2016 SP1 (24.1.0) | 64 of 64 checks, through the real dial-out link, with the release exe |
-| Live end-to-end, SOLIDWORKS 2019 SP0 (27.0.0) | 64 of 64 checks, same exe |
-| Real dsh 0.2.0-rc.2 + `deepseek-flash`, dsh on Linux, SOLIDWORKS 2019 in a VM (listen) | the session above: 11 tool calls, 11 completed; a part modeled from a sentence, its volume checked, a drawing exported and fetched |
+| Live end-to-end, SOLIDWORKS 2016 SP1 (24.1.0) | 66 of 66 checks, through the real dial-out link, with the release exe |
+| Live end-to-end, SOLIDWORKS 2019 SP0 (27.0.0) | 66 of 66 checks, same exe |
+| Real dsh 0.2.0-rc.2 + `deepseek-flash`, dsh on Linux, SOLIDWORKS 2019 in a VM (listen) | the four clips above: 12, 17, 23 and 13 tool calls; one refused (a BOM asked of a part: `WRONG_DOC_TYPE`, which the model corrected), the rest completed |
| Real dsh 0.2.0-rc.2 + `deepseek-flash`, dsh on the Windows PC itself (spawn) | run on 0.1.0, unchanged since: the plugin started its bundled bridge through dsh's subprocess service; 5 tool calls, 5 completed; no bridge left running after dsh exited |
| The published `swbridge.exe` | rebuilt from source by CI to the same SHA-256 (`bridge/swbridge.exe.sha256`) |
| Pairing from the generated `pair.cmd`, sign-in start, `uninstall` | run on 0.1.0, unchanged since: installed with no admin, one instance under the mutex, nothing left after uninstall |
| Unit tests (Linux, no SOLIDWORKS) | 48, including the 2016 member check and both-sides contract checks |
| `swbridge selftest` (Windows, no SOLIDWORKS) | paths, units, JSON, drawing layout and scale, picture letterboxing, CSV, HMAC vector shared with the plugin |
+
+What the live checks compare against, and how long things take
+
The end-to-end checks test each answer against something the tool did not say
about itself. The modelling ones compute it from the geometry asked for: the
plate above must come to 6000·20 − π·10²·20 − 4·25·(1 − π/4)·20 mm³ and its top
@@ -292,7 +305,10 @@ must have its four views at the sheet's scale and export as `%PDF-`. The
ownership checks build on a sketch the bridge did not make (saved, closed and
reopened, so it is forgotten): a failed build and a delete of what was built on
it must leave that sketch in place, and a sketch that fails on its second
-entity must leave the feature tree exactly as it was. The
+entity must leave the feature tree exactly as it was. A blind cut sketched on
+a plane under the body must reverse itself and remove exactly π·5²·4 mm³, and a
+drawing saved next to its part must be the document its `.slddrw` name exports.
+The
reading ones from 0.1.0 still run: the stock `box.sldprt` must reach 325,500
mm³ after `sw_edit` sets its depth to 150 mm, the fetched STEP must hash to the
bridge's SHA-256, the refusals must return their codes. On 2016 the run used a
@@ -300,14 +316,17 @@ SOLIDWORKS that someone else had a 58-document assembly open in; a check
confirms their documents, dirty flags, active document and the 57 parts their
assembly had loaded came back unchanged.
-Measured through the link in the two final runs, SOLIDWORKS under x64 emulation
-on Windows 11 ARM64, on VMs other work was using at the same time (earlier runs
-were up to twice as fast): the five-step plate build 4.2-6.8 s, a probe
-0.5-1.6 s, a four-view picture 2.7-4.5 s, a drawing 3.4-4.2 s, interference on
-a four-part assembly 1.5-3.0 s, open a part 1.8-2.3 s, export STEP 0.5-0.7 s.
+Measured through the link in the 0.2.1 runs, SOLIDWORKS under x64 emulation on
+Windows 11 ARM64 (the slower end is the 2016 SOLIDWORKS that also holds the
+58-document assembly): the five-step plate build 3.2-4.3 s, a probe 0.3-0.9 s, a
+four-view picture 2.5-3.4 s, a drawing 1.9-3.9 s, interference on a three-part
+assembly 1.1-3.2 s, open a part 1.3-1.5 s, export STEP 0.4-0.5 s.
+
+
Not verified yet: SOLIDWORKS 2020 and later, Windows 10, x64 hardware. Reports
-welcome; `swbridge status` is the first thing to paste.
+welcome: [open an issue](https://github.com/dshworks/dsh-solidworks/issues/new/choose)
+with `swbridge status` pasted in.
## When to skip it
@@ -327,6 +346,9 @@ welcome; `swbridge status` is the first thing to paste.
## Troubleshooting
+
+Error messages and what they mean
+
| You see | It means |
| --- | --- |
| `NOT_CONNECTED` | No bridge has paired with this dsh. Run `pair.cmd` in the VM; if it ran, check `%LOCALAPPDATA%\dsh-solidworks\bridge.log` there. |
@@ -336,9 +358,15 @@ welcome; `swbridge status` is the first thing to paste.
| `DIRTY ... was not opened by this bridge process` on a document dsh made | The bridge restarted and no longer knows it made that document. Close it in SOLIDWORKS. |
| `no face within 0.5 mm of [x, y, z]` | The point is not on the model where the call assumed; the message names the nearest face or edge and how far it is. Components move when mated: use where they are now. |
| `another bridge holds the link` (in the bridge log) | A second machine is paired with the same dsh; the first one connected keeps it. |
+| `the bridge on ... does not have sw_...` | The Windows PC still runs the bridge it was paired with before the plugin was updated. Run `pair.cmd` there again. |
+
+
## Configuration
+
+Plugin and bridge options
+
The plugin row, `id: dsh-solidworks`:
| Key | Default | |
@@ -354,6 +382,8 @@ The plugin row, `id: dsh-solidworks`:
The bridge, `swbridge.exe`: `mcp`, `connect`, `status`, `uninstall`, with
`--out`, `--allow-write`, `--allow-save`, `--pid`. `swbridge help` has the rest.
+
+
## Prior art
- [wzyn20051216/solidworks-automation-skill](https://github.com/wzyn20051216/solidworks-automation-skill)
@@ -382,6 +412,7 @@ npm run build:bridge # .NET 8 SDK; builds bin/swbridge.exe for n
SW_LINK_TOKEN=... node tests/live/e2e.mjs # live run against a bridge that dials this machine
```
-`bridge/compat/README.md` explains how `sw2016.tsv` is regenerated from an
-installed interop. `node scripts/format-contract.mjs` keeps
+[CONTRIBUTING.md](CONTRIBUTING.md) has the rest, including how to run the live
+checks against your own SOLIDWORKS. `bridge/compat/README.md` explains how
+`sw2016.tsv` is regenerated from an installed interop. `node scripts/format-contract.mjs` keeps
`protocol/tools.json` in its layout. MIT, see [LICENSE](LICENSE).
diff --git a/README.zh.md b/README.zh.md
index db24f15..82383ab 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -24,7 +24,7 @@
-一次会话 ·
+看它工作 ·
开始使用 ·
工具 ·
它不会做什么 ·
@@ -35,41 +35,45 @@
---
-你向 dsh 要一个零件,它就在你的 SOLIDWORKS 里把它建出来:草图、拉伸、旋转、打孔、圆角、倒角、抽壳。
-它会拿结果和它本想做出的几何对照、渲染出来看一眼,用配合把零件装起来、检查干涉、汇总成物料清单,
-再出一张带尺寸的工程图。它同样能处理你已有的文件:以只读方式打开,直接从模型本身作答,在内存中试新数值,
-把 STEP、PDF 或原生格式的副本写回你的项目。除非你主动开启,磁盘上的文件不会被改动。
+
+
+
+
+**一段话进去,一个法兰出来。** 你描述零件,dsh 就在你桌面上的 SOLIDWORKS 里把它建出来,用它自己的算术核对体积
+(两边都是 158,983.493 mm³),再从四个方向看一遍。它还能把零件装起来、检查干涉、汇总物料清单、出工程图,
+也能处理你已有的文件:除非你同意,一律只读。
+
+## 看它工作
+
+每个片段都是一次真实会话:dsh 0.2.0-rc.2 加 `deepseek-flash`,SOLIDWORKS 2019 SP0 跑在 Parallels 虚拟机里,
+画面录自 SOLIDWORKS 窗口。字幕是这次会话自己的工具调用和结果,以及模型自己的结束语。模型思考的时间被快进,
+没有剪掉或调换任何内容。[片段是怎么做的](scripts/clips/README.md)。
+
+### 指给它看
+
+你在 SOLIDWORKS 里选中一个面,告诉它要做什么。dsh 读取你的选择(法兰的上表面,8 条边),把这些边全部倒 2 mm
+圆角,并解释为什么体积减少了 360.218 mm³,尽管其中一个圆角是在加材料。
+
+
-## 一次会话
+### 装起来,检查是否合得上
-一次真实运行(dsh 0.2.0-rc.2、`deepseek-flash`,SOLIDWORKS 2019 SP0 跑在 Parallels 虚拟机里,
-dsh 在链路另一端)。提示词(原文为英文):
+建一根轴,两个零件都保存,再装配:轴穿过中心孔,法兰离轴端 40 mm。干涉:0 对。dsh 还把只是贴合的面也算了一遍,
+指出 30 mm 的轴配 30 mm 的孔是零间隙配合。最后给出物料清单。
-> 新建一个零件:一块 100 x 60 x 20 mm 的板,平放在上视基准面上、以原点为中心,中心有一个 20 mm 的通孔,
-> 四条竖直棱边倒 5 mm 圆角。检查它的体积是否等于这个几何应有的值。在一张图里从四个方向展示它,并说说你看到了什么。
-> 保存为 plate.sldprt,出一张工程图并导出为 plate.pdf。最后关闭你建的所有东西。
+
-dsh 依次调用了 `sw_status`、`sw_new`、`sw_build`(一次调用完成草图、两侧对称拉伸、打孔,并用棱边上的点选中四条边倒圆角)、
-`sw_view`(四个视图拼在一张图上)、`sw_save`、`sw_drawing`、`sw_view`(看工程图)、`sw_export`、两次 `sw_close` 和 `sw_status`。
-共 11 次调用,全部完成。它的回答节选(原文为英文):
+### 出图
-> **体积:** 工具返回 **113287.61102 mm³**。按几何核对:100×60×20 = 120000;减去 ⌀20 通孔 π·10²·20 = 6283.185;
-> 减去四个 5 mm 圆角 4·(25 − π·25/4)·20 = 429.204 → 应为 **113287.611 mm³**。完全一致。
->
-> **上视:** 100 × 60 的轮廓,四角倒圆,⌀20 的孔正在中心。**前视:** 一个 100 × 20 的矩形;从这个方向看,竖直的孔在内部,
-> 所以没有孔的轮廓。
->
-> **工程图:** 第三角投影,比例 1:1,图纸 279.4 × 215.9 mm;前视、上视、右视和等轴测四个视图;导入 2 个尺寸
-> (图纸上显示 R5 和 20.00)。**PDF:** 本地副本 `solidworks/plate.pdf`。**已关闭:** 工程图(未保存的修改已丢弃)和
-> `plate.sldprt`;documents: []。
+工程图按投影关系布置视图,取能放进图纸的最大标准比例,导入模型尺寸,并以 PDF 的形式回到你的工作区。
+
+
-
-
+
+
-
-体积是模型用自己的算术核对的,而不是相信工具。它对每个视图的描述来自那张图片:工具返回的任何数字都没说前视图里看不到孔。
-PDF 已落到 dsh 的工作区,并与 bridge 报告的 SHA-256 校验一致。
+左:导出的 PDF。右:sw_view 把四个视图拼在一张图上,dsh 就是这样看它做出来的东西。
## 开始使用
@@ -126,6 +130,9 @@ exe 在本包的 `bin/swbridge.exe`,每个 [GitHub release](https://github.com
上视是 y = 0,右视是 x = 0。面和边用其上的一个点来选取。`doc` 是已打开文档的路径、文件名或标题,
默认是当前活动文档。
+
+全部 21 个工具
+
| 工具 | 作用 |
| --- | --- |
| `sw_status` | 版本(2016 SP1、2019 SP0 ……)、所有 SOLIDWORKS 实例、已打开文档,以及 SOLIDWORKS 正在等待的对话框。首先调用。 |
@@ -150,6 +157,8 @@ exe 在本包的 `bin/swbridge.exe`,每个 [GitHub release](https://github.com
| `sw_close` | 关闭文档;只有这个 bridge 自己做的未保存修改才会被丢弃。 |
| `sw_view` | 渲染一张 PNG:一个标准视图,或最多六个视图拼在一张带标注的图上,着色、线框或消隐。之后恢复用户原来的视图。 |
+
+
## 它不会做什么
这些规则在 Windows 一侧、文件所在的地方执行。dsh 可以提出任何请求,最终由 bridge 决定。
@@ -174,6 +183,9 @@ HMAC-SHA256 证明自己持有令牌),但没有加密。它面向仅主机
## 工作原理
+
+逐条设计说明
+
- **几何只有一个坐标空间。** 草图点、选取点、探测点、零部件原点和包围盒都是模型空间的毫米值。bridge 用草图
自身的变换把每个草图点映射进去,所以面上的圆会出现在模型说的位置,不管 SOLIDWORKS 给那个面的草图取了什么轴。
选取时会让每个面或边给出离目标最近的点,所以结果不依赖视角、缩放或前面挡着什么,也从不改动你的选择。
@@ -200,36 +212,45 @@ HMAC-SHA256 证明自己持有令牌),但没有加密。它面向仅主机
- **一份契约。** `protocol/tools.json` 定义名称、参数结构、时间预算和错误码。bridge 内嵌它;插件用 dsh 自己的
`createMcpToolDefinition` 从它构建 dsh 工具,所以参数校验、错误结果和图片准入与任何 MCP server 的行为一致。
+
+
## 证据
本页的每一条说法都在 2026-10-18 对真实 SOLIDWORKS 跑过。
| 内容 | 结果 |
| --- | --- |
-| 端到端实测,SOLIDWORKS 2016 SP1(24.1.0) | 64 项检查全部通过,经由真实的主动连出链路,用的就是发布的 exe |
-| 端到端实测,SOLIDWORKS 2019 SP0(27.0.0) | 64 项检查全部通过,同一个 exe |
-| 真实 dsh 0.2.0-rc.2 + `deepseek-flash`,dsh 在 Linux、SOLIDWORKS 2019 在虚拟机(listen) | 即上面那次会话:11 次工具调用全部完成;一句话建出零件、核对体积、导出并取回工程图 |
+| 端到端实测,SOLIDWORKS 2016 SP1(24.1.0) | 66 项检查全部通过,经由真实的主动连出链路,用的就是发布的 exe |
+| 端到端实测,SOLIDWORKS 2019 SP0(27.0.0) | 66 项检查全部通过,同一个 exe |
+| 真实 dsh 0.2.0-rc.2 + `deepseek-flash`,dsh 在 Linux、SOLIDWORKS 2019 在虚拟机(listen) | 即上面四个片段:分别 12、17、23 和 13 次工具调用;其中一次被拒绝(对零件要 BOM:`WRONG_DOC_TYPE`,模型随即改正),其余全部完成 |
| 真实 dsh 0.2.0-rc.2 + `deepseek-flash`,dsh 就在那台 Windows PC 上(spawn) | 在 0.1.0 上跑过,此后未改动:插件通过 dsh 的子进程服务启动自带 bridge;5 次工具调用全部完成;dsh 退出后没有残留 bridge 进程 |
| 发布的 `swbridge.exe` | CI 从源码重新构建,得到同一个 SHA-256(`bridge/swbridge.exe.sha256`) |
| 用生成的 `pair.cmd` 配对、登录自启、`uninstall` | 在 0.1.0 上跑过,此后未改动:无需管理员即装好;互斥锁下只有一个实例;卸载后不留痕迹 |
| 单元测试(Linux,无需 SOLIDWORKS) | 48,包括 2016 成员检查和双方契约检查 |
| `swbridge selftest`(Windows,无需 SOLIDWORKS) | 路径、单位、JSON、工程图布局和比例、图片留边、CSV,以及与插件共用的 HMAC 测试向量 |
+
+实测检查的对照标准,以及各操作耗时
+
端到端检查用工具没有自己声明过的东西来检验每个回答。建模类检查的标准答案都由所要求的几何算出:上面那块板必须是
6000·20 − π·10²·20 − 4·25·(1 − π/4)·20 mm³,顶面面积是它除以 20;50 mm 立方体抽壳 2 mm、一面开口必须是
50³ − 46²·48 = 23,432 mm³;内外径 10/20 mm、长 30 mm 的旋转管加 1 mm 倒角必须是 π(20² − 10²)·30 − π·(20 − 1/3);
设计研究取 10 mm 和 40 mm 时必须是顶面面积乘以 10 和 40,结束后板厚回到 20;两块面对面配合的板不能干涉,第三块压进
两者各 10 mm 时,与每块的重叠必须正好是顶面面积乘以 10;BOM 必须数出 3 件;工程图必须有四个与图纸比例一致的视图,
并能导出为 `%PDF-`。归属类检查基于一个不是 bridge 建的草图(保存、关闭再打开,bridge 已不记得它):在它上面建模失败、
-以及删除在它上面建的特征,都必须让这个草图保留;第二个图元就失败的草图必须让特征树保持原样。0.1.0 的读取类检查也照常运行:样例 `box.sldprt` 在 `sw_edit` 把深度改成 150 mm 后必须是
+以及删除在它上面建的特征,都必须让这个草图保留;第二个图元就失败的草图必须让特征树保持原样。画在实体下方基准面上的给定深度切除必须自动反向,并正好去掉 π·5²·4 mm³;
+和零件同名保存的工程图,用它的 `.slddrw` 名字导出的必须是工程图本身。0.1.0 的读取类检查也照常运行:样例 `box.sldprt` 在 `sw_edit` 把深度改成 150 mm 后必须是
325,500 mm³,取回的 STEP 必须与 bridge 的 SHA-256 一致,各种拒绝必须返回对应的错误码。2016 上的那次运行用的是一个
别人开着 58 个文档的装配体的 SOLIDWORKS;有一项检查确认他们的文档、修改标记、活动文档,以及他们装配体加载的 57 个零件都原样恢复。
-经由链路在最后两次运行中实测,SOLIDWORKS 在 Windows 11 ARM64 上以 x64 仿真运行,虚拟机当时也在跑别的工作
-(更早的运行最多快一倍):五步建板 4.2-6.8 s,一次探测 0.5-1.6 s,四视图拼图 2.7-4.5 s,出一张工程图
-3.4-4.2 s,四零件装配体干涉检查 1.5-3.0 s,打开零件 1.8-2.3 s,导出 STEP 0.5-0.7 s。
+经由链路在 0.2.1 的实测中测得,SOLIDWORKS 在 Windows 11 ARM64 上以 x64 仿真运行(较慢的一端是同时开着那个 58 个文档
+装配体的 2016):五步建板 3.2-4.3 s,一次探测 0.3-0.9 s,四视图拼图 2.5-3.4 s,出一张工程图 1.9-3.9 s,三零件装配体
+干涉检查 1.1-3.2 s,打开零件 1.3-1.5 s,导出 STEP 0.4-0.5 s。
+
+
-尚未验证:SOLIDWORKS 2020 及以后、Windows 10、x64 实机。欢迎反馈,先贴 `swbridge status` 的输出。
+尚未验证:SOLIDWORKS 2020 及以后、Windows 10、x64 实机。欢迎反馈:[提一个 issue](https://github.com/dshworks/dsh-solidworks/issues/new/choose),
+并贴上 `swbridge status` 的输出。
## 何时不适合用
@@ -249,4 +270,5 @@ npm run build:bridge # 需要 .NET 8 SDK;为 net48 构建 bin/
SW_LINK_TOKEN=... node tests/live/e2e.mjs # 对一个连向本机的 bridge 做实测
```
-`node scripts/format-contract.mjs` 让 `protocol/tools.json` 保持既定排版。MIT 许可,见 [LICENSE](LICENSE)。
+[CONTRIBUTING.md](CONTRIBUTING.md) 有其余说明,包括如何对你自己的 SOLIDWORKS 跑实测。`node scripts/format-contract.mjs`
+让 `protocol/tools.json` 保持既定排版。MIT 许可,见 [LICENSE](LICENSE)。
diff --git a/bridge/SwBridge.csproj b/bridge/SwBridge.csproj
index 37f878d..29a1d9c 100644
--- a/bridge/SwBridge.csproj
+++ b/bridge/SwBridge.csproj
@@ -22,7 +22,7 @@
none
false
true
- 0.2.0
+ 0.2.1
dsh-solidworks bridge
dshworks
MIT License
diff --git a/bridge/app.manifest b/bridge/app.manifest
index 56ddd47..2c7a6e2 100644
--- a/bridge/app.manifest
+++ b/bridge/app.manifest
@@ -1,6 +1,6 @@
-
+
diff --git a/bridge/src/SelfTest.cs b/bridge/src/SelfTest.cs
index 3a326e8..056ff66 100644
--- a/bridge/src/SelfTest.cs
+++ b/bridge/src/SelfTest.cs
@@ -52,6 +52,7 @@ public static int Run()
Check("scale parse", SheetLayout.ParseScale("1:2") == (1, 2) && SheetLayout.ParseScale("1/2") == null && SheetLayout.ParseScale("0:2") == null, "");
Check("scale ratio", SheetLayout.Ratio(0.5) == "1:2" && SheetLayout.Ratio(2) == "2:1", SheetLayout.Ratio(0.5));
Layouts();
+ Names();
var root = Path.Combine(Path.GetTempPath(), "swbridge-selftest");
var b = new Bridge(new Options { OutDir = root });
@@ -90,6 +91,21 @@ static void Layouts()
Check("without a front view, views go in a row", row["isometric"] == (0, 0) && row["top"] == (1, 0), "");
}
+ /// Which open document a name means. The case that went wrong: a part "flange" and its drawing
+ /// "flange - Sheet1" open together, and "flange.slddrw" resolved to the part.
+ static void Names()
+ {
+ const string part = @"C:\out\flange.sldprt", drawing = @"C:\out\flange.slddrw";
+ Check("a drawing's file name never means the part", Docs.Match(part, "flange", K.DocPart, "flange.slddrw") == 0, "");
+ Check("a drawing's file name means the drawing", Docs.Match(drawing, "flange - Sheet1", K.DocDrawing, "flange.slddrw") == 2, "");
+ Check("a path is exact", Docs.Match(part, "flange", K.DocPart, @"c:\OUT\flange.SLDPRT") == 2, "");
+ Check("a bare name is the part titled so, and only loosely its drawing", Docs.Match(part, "flange", K.DocPart, "flange") == 2 &&
+ Docs.Match(drawing, "flange - Sheet1", K.DocDrawing, "flange") == 1, "");
+ Check("a title with its extension hidden still matches", Docs.Match(part, "flange", K.DocPart, "flange.sldprt") == 2, "");
+ Check("a new document matches its title", Docs.Match("", "Part7", K.DocPart, "part7") == 2, "");
+ Check("other names do not match", Docs.Match(part, "flange", K.DocPart, "shaft") == 0, "");
+ }
+
static void Denied(Bridge b, string path)
{
try
diff --git a/bridge/src/Sw/Docs.cs b/bridge/src/Sw/Docs.cs
index 4a84d08..2b9636b 100644
--- a/bridge/src/Sw/Docs.cs
+++ b/bridge/src/Sw/Docs.cs
@@ -23,8 +23,14 @@ public static object Resolve(object app, string name)
}
var want = name.Trim();
var docs = Objects(Call(app, "GetDocuments"));
+ object loose = null;
foreach (var d in docs)
- if (Matches(d, want)) return d;
+ {
+ var m = Match(Str(Call(d, "GetPathName")) ?? "", Str(Call(d, "GetTitle")) ?? "", Type(d), want);
+ if (m == 2) return d;
+ if (m == 1 && loose == null) loose = d;
+ }
+ if (loose != null) return loose;
var open = new List();
foreach (var d in docs)
@@ -37,17 +43,26 @@ public static object Resolve(object app, string name)
$"\"{want}\" is not open in SOLIDWORKS; open it with sw_open first. Open: {(open.Count == 0 ? "none" : string.Join(", ", open))}");
}
- static bool Matches(object doc, string want)
+ /// How well a document answers to a name: 2 exactly (its path, file name or title), 1 loosely
+ /// (the same name without extension, since titles follow Explorer's "hide extensions"), 0 not.
+ /// A name with a SOLIDWORKS extension only ever matches a document of that type: flange.slddrw
+ /// is never the part flange.sldprt (measured: an export of "flange.slddrw" wrote the part).
+ public static int Match(string path, string title, int type, string want)
{
- var path = Str(Call(doc, "GetPathName")) ?? "";
- var title = Str(Call(doc, "GetTitle")) ?? "";
- if (Same(path, want) || Same(title, want)) return true;
- if (path.Length > 0)
+ if (Same(path, want) || Same(title, want)) return 2;
+ if (path.Length > 0 && Same(Path.GetFileName(path), want)) return 2;
+ var wantType = Path.GetExtension(want).ToLowerInvariant() switch
{
- var file = Path.GetFileName(path);
- if (Same(file, want) || Same(Path.GetFileNameWithoutExtension(path), want)) return true;
- }
- return Same(Path.GetFileNameWithoutExtension(title), Path.GetFileNameWithoutExtension(want));
+ ".sldprt" => K.DocPart, ".sldasm" => K.DocAssembly, ".slddrw" => K.DocDrawing, _ => 0,
+ };
+ if (wantType != 0 && wantType != type) return 0;
+ var stem = wantType != 0 ? Path.GetFileNameWithoutExtension(want) : want;
+ if (path.Length > 0 && Same(Path.GetFileNameWithoutExtension(path), stem)) return 1;
+ // A drawing's title carries its sheet: "flange - Sheet1".
+ var bare = Path.GetFileNameWithoutExtension(title);
+ var sheet = type == K.DocDrawing ? title.LastIndexOf(" - ", StringComparison.Ordinal) : -1;
+ if (sheet > 0 && Same(title.Substring(0, sheet), stem)) return 1;
+ return Same(bare, stem) || Same(title, stem) ? 1 : 0;
}
static bool Same(string a, string b) => a.Length > 0 && string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
diff --git a/bridge/src/Tools/AssemblyTools.cs b/bridge/src/Tools/AssemblyTools.cs
index 779e479..81e155d 100644
--- a/bridge/src/Tools/AssemblyTools.cs
+++ b/bridge/src/Tools/AssemblyTools.cs
@@ -66,6 +66,7 @@ public static Obj Assemble(Bridge b, object app, Args a)
Call(asm, "ClearSelection2", true);
Call(asm, "EditRebuild3");
var rebuild = WriteTools.WhatsWrong(asm);
+ ModelTools.Frame(b, asm);
var o = new Obj().Add("ok", failure == null && (bool)rebuild["ok"]).Add("doc", Docs.Brief(asm, b)).Add("steps", results);
if (failure != null) o.Add("failed", failure).Add("note", "steps before the failed one stay applied");
diff --git a/bridge/src/Tools/ModelTools.cs b/bridge/src/Tools/ModelTools.cs
index 98c504a..0b8b0a1 100644
--- a/bridge/src/Tools/ModelTools.cs
+++ b/bridge/src/Tools/ModelTools.cs
@@ -171,6 +171,7 @@ public static Obj Build(Bridge b, object app, Args a)
}
Call(doc, "EditRebuild3");
var rebuild = WriteTools.WhatsWrong(doc);
+ Frame(b, doc);
var o = new Obj().Add("ok", failure == null && (bool)rebuild["ok"]).Add("doc", Docs.Brief(doc, b)).Add("steps", results);
if (failure != null)
@@ -185,6 +186,15 @@ public static Obj Build(Bridge b, object app, Args a)
return o.Add("model", Summary(doc)).Add("rebuild", rebuild);
}
+ /// In a document the bridge opened or made, zoom to fit after changing it, so someone
+ /// watching SOLIDWORKS sees what was built. A user's own document keeps the view they set.
+ public static void Frame(Bridge b, object doc)
+ {
+ if (!b.Owns(doc)) return;
+ try { Call(doc, "ViewZoomtofit2"); }
+ catch (BridgeException e) when (e.Code != "SW_GONE") { /* a view nobody can see is no failure */ }
+ }
+
/// What a part is now: solid bodies, volume and bounding box. Enough for a caller to check a
/// build against what it meant to make.
public static Obj Summary(object doc)
@@ -410,12 +420,28 @@ Obj Extrude(Args s, string what)
var twoWay = cond == K.EndThroughAllBoth;
var t1 = twoWay ? K.EndThroughAll : cond;
var t2 = twoWay ? K.EndThroughAll : K.EndBlind;
- var feature = cut
- ? Call(features, "FeatureCut3", !twoWay, false, reverse, t1, t2, depth, depth, draft != 0, false, draft < 0, false,
+ var merge = Json.Bool(s, "merge", true);
+ object Make(bool dir) => cut
+ ? Call(features, "FeatureCut3", !twoWay, false, dir, t1, t2, depth, depth, draft != 0, false, draft < 0, false,
draftRad, 0.0, false, false, false, false, false, true, true, false, false, false, K.StartSketchPlane, 0.0, false)
- : Call(features, "FeatureExtrusion2", !twoWay, false, reverse, t1, t2, depth, depth, draft != 0, false, draft < 0, false,
- draftRad, 0.0, false, false, false, false, Json.Bool(s, "merge", true), true, true, K.StartSketchPlane, 0.0, false);
- return Made(feature, s, what, cut ? "cut" : "extrude", $"{(cut ? "cut" : "extrude")} {sketch}");
+ : Call(features, "FeatureExtrusion2", !twoWay, false, dir, t1, t2, depth, depth, draft != 0, false, draft < 0, false,
+ draftRad, 0.0, false, false, false, false, merge, true, true, K.StartSketchPlane, 0.0, false);
+ var feature = Make(reverse);
+ // A cut sketched on a plane at the body's boundary points away from the material by
+ // default and removes nothing (measured: six bolt holes on the top plane under a
+ // disc, three failed calls). When the caller left the direction open, the other
+ // way is the only cut that means anything: try it once and say so.
+ var reversed = false;
+ if (feature == null && cut && !s.ContainsKey("reverse") && !twoWay && cond != K.EndMidPlane)
+ {
+ SelectSketch(s, what);
+ feature = Make(!reverse);
+ reversed = feature != null;
+ }
+ var o = Made(feature, s, what, cut ? "cut" : "extrude",
+ $"{(cut ? "cut" : "extrude")} {sketch}{(cut ? " in either direction; is the sketch over the body?" : "")}");
+ if (reversed) o.Add("reversed", true).Add("note", "the cut pointed away from the material, so it was made the other way");
+ return o;
}
Obj Revolve(Args s, string what)
diff --git a/bridge/swbridge.exe.sha256 b/bridge/swbridge.exe.sha256
index 79af4ef..0ca8544 100644
--- a/bridge/swbridge.exe.sha256
+++ b/bridge/swbridge.exe.sha256
@@ -1 +1 @@
-18716d1b5e45ee3c37001321930952301a6f7fa4b4b7adc0f9cf63c359e5d3b5
+fd6ec61c1e1200f3d45f618bdeed1a1140e78a88ecb68185bffe8a6b7fbd304b
diff --git a/docs/images/flange-drawing.png b/docs/images/flange-drawing.png
new file mode 100644
index 0000000000000000000000000000000000000000..d18c9e4008150c5950a93cd9fcb9446fce799e8d
GIT binary patch
literal 56261
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