master
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
523ae21341 |
Release 0.7.19: fix what day one of PostHog telemetry showed (Windows mesh export, cad_file and cad_screenshot failures, crash noise, failure reasons) (#586)
**This PR is the 0.7.19 release** (`scripts/release/bump-version.sh
patch`): merging it runs Publish Release. Its receiver changes under
`apps/api` deploy on the same merge through Deploy API, minutes before
PyPI has 0.7.19, so schema 4 is read before any client sends it.
Fixes for what PostHog's first day of telemetry showed (2026-10-08
00:14Z to about 21:40Z: about 209 installs and 59 crash reports). It
covers three bugs people are hitting, crash reports that were not
cadgen's bugs, and gaps in what the receiver lets us see. There is one
commit per fix.
## Bugs
**1. Builds that export a mesh crashed on Windows** (7 installs, all
Windows, about 26 crashes). `mesh_export.py` ran the Node exporter with
`text=True` and no encoding, so Windows read its UTF-8 output in the
local code page. The exporter's JSON report names every output path, so
any output folder whose name the code page cannot read (for example
`Рабочий стол` under cp1252, or most Chinese text under cp936) made
CPython's Windows output reader die quietly. `proc.stdout` came back
`None`, and `.splitlines()` raised an `AttributeError`. The exporter now
reads `utf-8` with `errors="replace"`, which keeps the JSON line intact.
The same fix goes into `run_node_builder`, whose input was also silently
empty under cp1252. ffmpeg, `gz sdf` and `doctor` now read `utf-8` with
`errors="backslashreplace"`, and doctor's child process is set to
`PYTHONIOENCODING=utf-8`. The tests force subprocess's default encoding
to cp1252, and both fail without the fix.
**2. `cad_file` failed on 48 of 49 calls on Windows** (5 of 6 installs).
Codex for Windows names a file opened from its file tree as
`openai/resource.path = "/C:/Users/…"`, read from the desktop bundle.
Python 3.13's `ntpath.isabs("/C:/…")` is False, so every call answered
"not an absolute path". The `file.resourceUri` alongside it is a
`codex-resource://` handle, so the fallback never helped. A new
`local_path` drops the slash before a drive on Windows, both for file
URIs and for plain paths, for `cad_file`, `cad_open` and `cad_show`.
This most likely also explains Antigravity's `cad_show` failures on
Windows (7 of 12). The Windows CI job now passes the path the way Codex
spells it.
**3. `cad_screenshot` failed on 30% of calls** (11 of 19 installs). The
most likely cause is an agent capturing straight after build, show or
open, while the view is still loading or has not synced yet. The view
refused with "Wait for the displayed model revision to finish loading",
"That viewer is not open" or "No CAD viewer with a model is open", or a
large model ran past the fixed 10 s wait.
- The page now waits until the view shows the requested model, loaded
and drawn (`CAPTURE_SETTLE_MS`, 20 s).
- The server waits for a view it just opened to sync (`OPENING_SECONDS`,
15 s) within one budget for the whole capture (`CAPTURE_SECONDS`, 40 s).
- The capture's reply still goes on its own call (`void answer(event)`),
so no view call is held open.
## Crash reports that were not cadgen's bugs
- **Windows viewer disconnects.** `ConnectionAbortedError` (WinError
10053) made up most of the crash volume: 23 installs. The viewer caught
only `BrokenPipeError` and `ConnectionResetError`, and the header write
had no guard. Every write to the socket now treats any `ConnectionError`
as the page having left.
- **A model's own mistakes.** A build123d name that does not exist,
raised through the `cadgen.build123d` re-export, and a non-string passed
to `srgb()`. Both now raise deliberately, so the existing rule counts
them as the person's error, and `srgb` raises a `TypeError` naming what
it was given.
- **Stopped workers.** A worker stopped by SIGTERM, SIGINT or SIGHUP (a
person quitting it, a logout) now counts as cancelled, not crashed.
SIGSEGV, SIGABRT and SIGKILL are still reported.
## Telemetry: what we can now see
- **Why a tool call failed.** There is a new `tool_failure {tool,
reason, count}` event in batch schema 4, which PostHog receives as
`tool_failed`. The reason is one word from a fixed list (`no_path`,
`relative_path`, `no_file`, `not_cad`, `no_view`, `wrong_view`,
`bad_request`, `timeout`, `view_error`, `too_large`, `no_viewer`, `bug`,
`other`), chosen where the call fails and never taken from a message. A
test checks that every `ToolFailed` and `NoAnswer` names one.
- **Rollout: the receiver goes first.** The API is its own Vercel
project now (#587) and deploys on merge to `main`, so merging this PR
puts the schema 4 receiver live before any release sends schema 4. A
refused batch is dropped, as before; there is no fallback in the client.
- **Refused batches are logged.** Each 400, 403 or 415 is one
`console.warn` line naming the rule that failed and the cadgen version.
Values, install ids and service messages are never logged. Vercel's
per-status counts need Observability Plus, so this is the only way to
see a refusal. The privacy policy says so.
- **Errors are logged by name**, for example `TimeoutError` instead of
`23`. A `/v1/forget` timed out at 17:02Z, and the client retries it.
- **`$session_id`** is now set, so error tracking can count sessions.
Our ids are UUIDv4, so PostHog's sessions table leaves them out; error
tracking should still read them, which needs checking after deploy.
Privacy policy, README and `apps/api/README.md` are updated where what
is sent or logged changed.
## Not in this PR
- **Deduplicating a resent batch.** The sender rebuilds a failed window
instead of resending it, and a batch has no id, so there is nothing
stable to dedupe on yet. It needs a per-batch id from the sender.
- **Dashboard totals.** PostHog's error-tracking "occurrences" counts
events, not each event's `count`; for the mesh-export crash that is 5
against 22. That is fixed on the dashboard side (t2c-analytics).
- **5 of 15 DXF builds failed.** DXF builds don't go through Node, so
the encoding fix doesn't cover them and they still need a look.
## Needs a real host
- Windows Codex: open a `.step` from the file tree; capture from a tab
hidden behind another tab.
- Claude Desktop: capture right after `cad_show` on a large STEP, or
while the card waits on Allow.
- Antigravity on Windows: confirm the path spelling it sends.
## Tests
Full suites on this branch, in a provisioned worktree (`.venv` from
`requirements-dev.txt`, `npm ci`, `bundle.sh --check`,
`CADGEN_DAEMON=0`): all pass.
- `scripts/test/test-python.sh --keep-going`: 2,774 tests in 8 groups,
OK.
- `scripts/test/test-js.sh`: every group passes (core, ui, web, mcp).
- `scripts/test/test-docs.sh`: receiver tests 30/30 and the rest 16/16.
- `scripts/test/test-global.sh`: 210 tests, OK (1 skipped).
Each new regression test was run against the old code, and each fails
there.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
b16d4530a5 |
Release 0.7.18 (#585)
Releases 0.7.18: VERSION, the derived metadata and every cadgen pin. Opened by Prepare Release. Merging it releases it: Publish Release uploads the wheel to PyPI, then moves the latest branch. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
a6b4a2f5c1 |
Release 0.7.17: keep each process's last telemetry batch for the next, and wait on nothing at exit (#574)
Release 0.7.17: no telemetry is lost when a CAD app, viewer or daemon exits, and no exit waits on the network. ## The bug (0.7.16) A CAD Viewer asked to exit (`cadgen viewer stop`, a newer launch, SIGINT/SIGTERM) hard-exits 0.5 s later (`viewer/main.py`), but its last send only began once `serve_forever` had returned. Against production, the exit always won: the viewer sends every 5 minutes, so everything since its last batch was lost, crash reports included, and a viewer that ran under five minutes sent nothing at all. Found by the post-release check of 0.7.16 against `api.texttocad.dev`: the CAD app's batch arrived, the viewer's never did; with the viewer kept alive past its 5-minute send, its batch (view, files, Quick Edit, a page crash) arrived and was taken. ## The fix No exit sends any more (law 19: nothing waits on the network). - `Recorder.close` keeps the process's last batch, whole, in `telemetry-batches.jsonl` beside the settings: a local write, only while sharing is on, under the answer in force, capped like the spool (256 KiB). A file of its own, so a 0.7.16 still installed never reads it. - The next process that sends sends it in the background, `KEPT_SECONDS` (10 s) after it starts, and with every flush (`send_kept`), each batch as it was made. Only under the same answer: a no deletes the file, and a batch kept under an earlier answer is dropped. A batch the receiver did not take is kept again. - The viewer starts keeping its batch as soon as it is asked to exit; the hard exit frees the port at 0.5 s as before and waits at most 1 s for that local write. - The two-second exit wait (`CLOSE_SECONDS`) is gone from `cadgen mcp`, the viewer and the build daemon. Law 19 (`packages/cadgen/README.md`), the README's Telemetry section and the privacy policy say so; the one wait left is `cadgen telemetry off`, which waits for the deletion it asks for. Trade-off: a batch kept at exit arrives when the next cadgen process starts, so its events are stamped then rather than when the process exited. ## Tests - `test_a_stopped_viewer_keeps_its_last_batch_for_the_next_process` (real viewer process, a receiver that hangs): the viewer exits promptly, sends nothing, and the kept file holds its batch. - `test_an_exiting_process_waits_on_nothing_and_the_next_one_sends_its_last_batch`: close returns in well under a second with the network hanging; the next recorder sends the batch as made (its process, session, client), keeps it again when offline, and never twice. - `test_a_kept_batch_is_sent_only_under_the_answer_it_was_kept_under`: a no deletes it; nothing is kept under a no; a process with `CADGEN_TELEMETRY=0` leaves it; another answer since drops it. - `test_a_started_recorder_sends_what_was_kept_a_moment_after_it_starts`. - The daemon's last batch is kept and sent by the next process. - Local: global 210, viewer 369, MCP 81, daemon/telemetry/crash/doctor/updates 64 — all pass. ## Release `VERSION` 0.7.16 → 0.7.17 (`scripts/release/bump-version.sh patch`); merging releases it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e3d1fb9993 |
Release 0.7.16 (#572)
Releases 0.7.16: VERSION, the derived metadata and every cadgen pin. Opened by Prepare Release. Merging it releases it: Publish Release uploads the wheel to PyPI, then moves the latest branch. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
06d9a94ccb |
Release 0.7.15 (#559)
Releases 0.7.15: VERSION, the derived metadata and every cadgen pin. Opened by Prepare Release. Merging it releases it: Publish Release uploads the wheel to PyPI, then moves the latest branch. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
d749a1980d |
Release 0.7.14
Bumps VERSION, derived package/plugin metadata and every skill's cadgen pin to 0.7.14. Created by Prepare Release, which merges it into main immediately; the merge runs Publish Release. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
57fb1a998b |
Release 0.7.13
Bumps VERSION, derived package/plugin metadata and every skill's cadgen pin to 0.7.13. Created by Prepare Release, which merges it into main immediately; the merge runs Publish Release. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
ba35a95c88 |
Release 0.7.12
Bumps VERSION, derived package/plugin metadata and every skill's cadgen pin to 0.7.12. Created by Prepare Release, which merges it into main immediately; the merge runs Publish Release. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
cf7d992f85 |
One-message install, one cadgen installation, update notices, and views by absolute path (#521)
The plugin rearchitecture in one PR. It has all of the install work: #522 (one cadgen installation) and #523 (install channels and update notices) are folded in here and closed in favour of it, and #527 (the animated logos) is carried too. It also has views by absolute path: the explorer, the home and the app menu, one viewer per port, and no roots. Rebased onto `main` at 0.7.11, after #499 and #526. It ships as a patch release, 0.7.12. ## Before deploying - **Run `apps/docs/src/lib/api/schema.sql` on the analytics database before the release's docs deploy.** It only adds (a `channel` column defaulting to `unknown`, and `source` made optional), so any time before is fine, even now: the live receiver keeps writing meanwhile, and the dashboard's breakdown by `source` keeps working. Until it runs, `/v1/health` fails the deploy check. - **OpenAI's portal acceptance of `env` in the ZIP's `.mcp.json` is unverified.** Confirm it at the next upload. If the portal stripped it, the OpenAI copy would only show the update button. - **Codex tabs restored from an older CAD show "CAD was updated"** until they are closed and CAD is opened again: the page no longer relaunches an older build's launch (a hard cutover, like the protocol bump itself). - **Release right after merging** (patch, 0.7.12). Until then main's configs pin cadgen 0.7.11, which starts fine with them (tested), but it has none of this PR's behaviour. ## Install copy: the README and the docs site **Install opens with "Ask your agent (recommended)":** "Send this message to your agent and it will install text-to-cad for you." The message is `Install text-to-cad from https://github.com/earthtojake/text-to-cad`. **Then installing it yourself, worded for a person or their agent alike.** Nothing is addressed to agents: 1. CAD runs through uv: check that it is installed, and install it if it is missing. 2. Run the commands under your app's heading, or Other Agents if it has none. 3. Restart the app. **Every install path is a sub-section with three commands:** install, update, and remove. To reinstall, remove it and install it again. - Claude Code - Claude Desktop (README only): to update, change the pinned version. - Codex - Cursor - Grok Build - Gemini, which installs with `--consent --auto-update` - Other Agents: the Skills CLI. Its `add` is also its update and its reinstall, and `-g -a <agent> -y` skips its questions. - Request Plugin, which opens a new issue On the docs page, each app's update and remove commands sit under a collapsed **Update or reinstall**. That is where the update button's *Manual installation* link lands: `texttocad.dev/install` redirects to `/#install`. **AGENTS.md now opens by pointing an agent that clones the repository to the README's Install.** **The plugin description drops its "Give your agent CAD superpowers." opening everywhere it appears:** the manifests, the Claude marketplace entry, the README intro (now directly under the title) and the docs site's Overview. The tagline stays as the headline. Store listings pick it up on their own schedules: Cursor when this merges, claude.ai at the next release, OpenAI at the next ZIP upload. **Docs site:** - **Animated logos (#527):** the wordmark draws itself in. The README and the hero show it as a 146 KB SVG in place of the 303 KB PNG. - **Available for these agents**, under the hero. It scrolls skills.sh's logo set (copied into `apps/docs/public/agents/` with approval), plus Grok's glyph from Lobe Icons (MIT). The tiles blend into both themes, and each logo jumps to its install. The 21 logos come to 66 KB: Nous Research's embedded PNG went from 196 KB to 22 KB, at the size the tile shows it. - **Header:** the CAD wordmark is always at a phone's left; on desktop it slides in once the hero's wordmark scrolls away. - **Contributing** section. - Every Copy button is the same size. - The hero's file caption is plain text. **One copy:** - The site's copy lives in `src/lib/content.ts`, which the page and the new prerendered `/llms.txt` both render. - The README and the site share their structure, description, install message, and every install, update and remove command. `test_plugin_manifests.py` checks them, and AGENTS.md and `apps/docs/README.md` state the rule. - The README says more: numbered steps, and fuller notes under each install. ## One cadgen installation cadgen runs as one launch command everywhere: `uvx --no-config --managed-python --python 3.13 --from cadgen==<version> <tool>` (`cadgen._internal.launch`). - **One installation and one warm daemon.** uv keeps one installation per requirement, and cadgen names its daemon after the installation's folder. So every app's server, every Codex thread and every skill command share one installation and one daemon. `--managed-python --python 3.13` makes that installation identical on every machine. - **Server configs:** Claude's, Codex's, Cursor's and Gemini's, and the README's Desktop snippet, run the launch command. A test holds them to cadgen's. - **Skills:** each cadgen skill defines `cadgen` and `python` as the launch command. Their `requirements.txt` files are deleted, and a test forbids any other way of installing cadgen in a skill. - **`cadgen doctor <skill>`** reads the pin from the skill's SKILL.md and reports the installation and daemon in use. - **Playwright is a core dependency** (`playwright==1.63.0`); the `snapshot` extra is gone. - The first snapshot installs the headless shell itself. - On Linux, cadgen installs the browser's libraries when it has root, or prints the exact command. - CI installs the headless shell only, about 350 MB less per run. - **Release:** `sync-version.mjs` stamps every skill's launch pin, and `check-version.sh` checks them. - **Dev installs:** `dev_install.py` points the copied skills at the checkout's `.venv`. `--wheel` builds the checkout's wheel and runs everything through `uvx --from <wheel>`. ## Install channels, a daily version check and the update button **Each plugin's startup config names where its installs come from**, in the server's environment: `CADGEN_INSTALL_CHANNEL=<id>`. Plugins that something else keeps up to date also set `CADGEN_AUTO_UPDATED=1`. The viewer and daemon the server starts inherit both. | Package | Server environment | Told of a release | | --- | --- | --- | | `main`'s `claude.mcp.json`: Claude Code, Grok Build, and Cursor through Claude Code's plugins | `claude-github` | yes | | `main`'s `codex.mcp.json` | `codex-github` | yes | | the README's Claude Desktop config | `claude-desktop` | yes | | the `plugin` branch's `cursor.mcp.json`, a Cursor clone (`plugin_branch.py`) | `cursor-github` | yes | | `main`'s `gemini-extension.json` | `gemini-github`, auto-updated | no: Gemini updates it | | `main`'s `cursor.mcp.json`, which the Cursor Marketplace reads | `cursor-marketplace`, auto-updated | no: the store updates it | | the `plugin` branch's `claude.mcp.json`, the Claude directory (`plugin_branch.py`) | `claude-directory`, auto-updated | no: the store updates it | | the OpenAI ZIP (`plugin_zip.py`, replacing `--install store`) | `openai-directory`, auto-updated | no: the store updates it | | a dev install | `dev` | no | | a skill's command, or the viewer a skill opens | nothing (`unknown`) | the viewer only: a skills-only install | - cadgen never decides by a channel's name, since its core may not name a host (a test holds that). A new store needs only its settings, no cadgen release. - The analytics server lists the channel names. - Environment variables, never flags: main's configs pin the last release, and cadgen ignores a variable it doesn't know but refuses a flag. Released 0.7.11 refuses `cadgen mcp --channel`, so flags would have broken every server installed from main between merging and releasing. **Only a copy nothing else updates hears of a new release.** - An auto-updated copy never checks and is never told, and neither is a dev install. - Every other copy reads `api.texttocad.dev/v1/versions` at most once a day, in one anonymous request, and is told whenever it is behind `latest`. The check never runs in CI, and `CADGEN_UPDATE_CHECK=0` turns it off. - The feed is only `{latest}`: the per-store minimums are gone. **The notice** reads "A new version v0.9.0 of text-to-cad is available (currently on v0.8.1)". Its prompt is worded like the install message: "Update text-to-cad to 0.9.0 from https://github.com/earthtojake/text-to-cad". - **CAD app and browser viewer: a blue update button** (`@text-to-cad/ui/update`): first among the navbar's right-hand controls, and on the home a row of its own, labeled Update. Both apps know the notice before the page draws (a launch carries it; the web page reads it with the server's description), so it draws with the page, and it is read again when the person comes back to the page. It opens the update card in a popover: - **Send to agent** (↑, as in Quick Edit) posts the prompt to the chat where the host takes messages, with a Copy icon beside it; - elsewhere it is **Copy prompt**; - a **Manual installation** link goes to `texttocad.dev/install`. - Nothing dismisses the button: it stays until the install is current. - **Inline cards are now the whole viewer** (Claude Desktop and every other MCP Apps host): navbar with the update button, tools, view cube and Quick Edit. Full size moves into a named slot (`CadViewerProps.fullSize`), last in the navbar and the home's row, and appears only where the host can show a view full size. A card used to be a compact view of the model alone, with no navbar, so the update button could never appear inline. - **Text-only apps:** the line on the first `cad_show` result of each server process. In text mode `cad_show` now returns text only, because Claude Code shows a result's structured content in place of its text and dropped the line. - **Skill commands say nothing, and don't check.** The same skill files ship in every plugin, so a command can't tell which one it came with. The earlier once-a-day stderr line is gone. The browser viewer's GitHub release polling and its old navbar update button are gone. `/__cad/version` is GET only, and both apps read it (the CAD app through its tunnel). **The feed** is served by the docs app (`src/lib/analytics` renamed `src/lib/api`). `latest` is the stamped version. **Analytics** send `channel` instead of `source` (batch schema 2), and `cadgen mcp --install` is gone. **Every released cadgen keeps counting:** the receiver reads every schema a release sends, so 0.7.7–0.7.11 copies (schema 1) are stored with the channel `unknown` and their `source` kept, and `schema.sql` only ever adds (a rule in `apps/docs/README.md`). **Nothing waits on the network** (cadgen law 19): analytics and the version check run in the background and fail silently, and CAD works offline once installed. The audit found one breach, now fixed: `/__cad/version` read a due feed inside the request (up to 5 s on a network that drops packets). The feed is now read only in the background, as a server starts, on each launch and on each `/__cad/version`. Checked against an installed cadgen with every request refused, then hanging: builds, snapshots, the server's tools and the viewer's routes all answer at once; the one wait is the exit's last analytics send (2 s when requests hang). How cadgen is launched (uvx re-checks its index once its cache is stale) is outside the law. **`cad-mcp-setup` is retired, with no replacement.** Installing CAD is uv and the app's own plugin install: the `cad` skill names uv, and the README's section for each app carries its install, update and restart steps, which the update button's prompt links. Store copies need nothing run. The Codex manifest no longer names an onboarding skill. There is no `cad_update` tool. ## Views by absolute path: the explorer, the home and the app menu **No more roots.** A view shows one file, named by its absolute path, or the home, and browses from that file's folder. That holds in a Codex tab, a Claude card and the web viewer alike. Roots, the thread workspace, MCP roots, Codex's sandbox metadata and the `explore` flag are gone. Agents name models by absolute path only. **The navbar:** - **The C logo opens the app menu.** It has Back to files (the home), the person's settings (Share anonymous usage data, Quick edit), Send feedback, GitHub and Discord, then the version and "Made by @…". The Settings popover is gone. The logo, the file name and the ⋯ share one hover and the pointer, with no tooltips. - **The file's name opens the explorer.** It shows the file's folder one folder at a time, the last three folders as a breadcrumb with the rest under "…", and "Filter files..." to search everything under the folder. - **The ⋯ menu** has Copy path and Reveal. - **The right side** is empty unless there's something to show: the update button, a dismissed alert's icon, or Full size. - **Display and Preview sit on top of the view cube again**, and Preview has the Play icon again. Preview's own controls land where they sat. **The home and missing files:** - A view with no file shows the home: Recent Files, Open with the desktop's chooser where one exists, and GitHub, Discord and X under the wordmark (the home has no settings menu). - A missing file shows "File does not exist" with Go home. - *Open with CAD* in Codex shows its file alone, with no Back to files and no explorer. **Viewer (`cadgen viewer`):** - **Absolute paths only.** Every route names files by absolute path and nothing is scanned. The asset route still sends only CAD files and their sidecars. - **New routes:** the explorer has `/__cad/folder` and `/__cad/search` (200 results, 1.5 s, 24 folders deep; hidden and build folders skipped). The home has `/__cad/recents`, `/__cad/thumbnail` and `/__cad/pick`. A model joins the library (`POST /__cad/recents` `open`) once it is on screen, and is counted for analytics there. - **`/__cad/server` says only what a reader uses:** `app`, `identityToken`, `autoReload`, `platform` (which file manager Reveal opens), `user`, `start`, `pick`, `port`, `pid`. - **One viewer per port**, 3245 or `--port N`, started from anywhere: - a launch reuses this user's viewer when its code matches; - it replaces one running other code (`/__cad/shutdown`); - it refuses anything else. - **Development:** `--new` takes a free port. The registry and `cadgen viewer list` are gone. **CAD app (`cadgen mcp`):** - A launch is a model's absolute path or the home (protocol 5). A launch from another build shows "CAD was updated". - **The page has three tools: `cad_sync`, `cad_capture_reply` and `cad_http`.** Everything else it asks of the server is the viewer's own routes, sent over `cad_http` to one in-process viewer shared by every view, which keeps the server's library and analytics: the models, the library and its pictures, Open, Reveal, the analytics answer and features, and the update check. The page's own copies of those (`cad_consent`, `cad_features`, `cad_version`, `cad_launch`, `cad_pick_model`, `cad_recents`, `cad_reveal`) are gone. Only the clipboard stays the host's. - **A text client is listed no page tool, and cannot call one:** a call reaches only a tool this client was listed, so no agent there answers the analytics card for the person. **Web:** the bare URL is the home, and `?file=` takes an absolute path. A relative one resolves where the viewer started, for development. **Skills:** they run the viewer from any folder and link `url?file=<absolute path>`. Copied references are absolute paths. ## Cleanup after the rearchitecture What views by absolute path made redundant, removed: - **Both apps reach the server through the same client.** `@text-to-cad/core/client` has `recents`, `changeRecents`, `thumbnail`, `keepThumbnail`, `pick`, `reveal`, `consent`, `features`, `version` and `reportActivity`. The web app's four fetch adapters and the CAD app's seven page tools are gone; the file menu is the shared `createCadFileActions` in both. - **Shared UI:** the declared-panel column (no renderer declared one), the reload-on-change path every live renderer bypassed, the file source's `subscribe`, `ModelLibrary`'s `pictureFrom` (the viewer pictures cards with its own client and `onThumbnail`) and `failure`, and dead exports. The update hook no longer reads on mount: the host hands it the notice. - **Core:** the client's `workspaceId` (every client had the same one), references' `workspaceId`, and the prompt formatter's `resolvePath` (every caller passed the identity). - **Server:** the MCP transport's outgoing requests and notification handler (used only for MCP roots), the viewer's duplicate pick lock (the chooser refuses a second itself), unread server fields, and dead viewer code (`CadgenOps.shutdown`, `STEP_DESCRIPTOR_NAME`). `/__cad/folder` and `/__cad/search` take paths through the one absolute-path rule, so a UNC path is refused there too. - **Docs and wording:** "Settings" is the app menu everywhere (the privacy policy, the README, `cad_analytics`), and no doc describes a retired surface. ## `scripts/install/dev_install.py <host>` Installs this checkout into `claude`, `codex`, `cursor`, `grok`, `gemini` or `claude-desktop` as `text-to-cad@earthtojake-dev`, with its own version per install. Running it again replaces the install; `--uninstall` removes it. It refuses a host where another copy would load. Tested live in each host; Codex and Grok ran in isolated homes. ## Gemini CLI extension `gemini-extension.json` sits at the root and is stamped on each release. Gemini installs from the latest GitHub Release's source tarball. To list it in Gemini's gallery, add the `gemini-cli-extension` topic to the repo. ## Also - **CONTRIBUTING:** what each store reads, Test In Agent Apps, the install channels table, the version feed. - **Removed one-off scripts**, replaced by `dev_install.py` and `--print-weights`. - **`test.yml`** also runs on `.cursor-plugin/**`, `gemini-extension.json` and `cursor.mcp.json`. Deploy Docs also checks `/v1/versions`. ## Checks - **At 1bdfa29e9 (the follow-ups and the cleanup), all pass:** - Python: cadgen 2215 (viewer and CAD app included); skills 280, 19, 51, 77, 4, 6 and 6; global 169; - core 1281, the core client 15; shared UI: node 746 + browser 53, vitest 336; browser viewer 57; CAD app 34; docs 16; - type checks for core, the shared UI, the CAD app and the browser viewer; the dependency and kit boundaries; - the full bundle, `check-version.sh` and `check-builds.sh`; - the viewer launch smoke test and the real-backend browser suite (format, camera). - **End to end, on this branch's wheel as cadgen 0.7.103 through the plugins' launch line, with fresh state (43/43):** - the launcher on spare ports (start, reuse from another folder, replace other code, stop, refuse a port another program holds) and `/__cad/server`'s nine fields; - the routes by absolute path (catalog, folder, search, the asset gate, recents, the pick guard); - in a browser, from the wheel's own client: a STEP from a cold store draws; the navbar's logo menu, file name and ⋯; Display and Preview over the cube; the explorer in the file's folder, its filter, a pick that opens the file, closes the explorer and joins the library; the app menu; Back to files to the home and its GitHub, Discord and X; "File does not exist"; a relative `?file=`; no page errors; - the CAD app inline (the agent's four tools and the page's three; the tunnel's catalog, folder, library, pictures, Reveal, analytics answer and update check; the clipboard and shutdown refused), as Codex tabs (home, tab, `cad_open`, the tab reopening, *Open with CAD*), and to a text client (two tools, no page tool, a link to the model). - **After views by absolute path (db6a5419e), all pass:** - shared UI: node 750 + browser 53, vitest 342; - core 1280, bench 24, browser viewer 57, CAD app 35; - Python 2215, with one failure that only happens in a shell with `PYTHONDONTWRITEBYTECODE=1` (`test_no_stale_bytecode`; it passes without it); - global 168, and the dependency boundaries; - type checks for the shared UI, the CAD app and the browser viewer; - the full bundle; - the viewer launch smoke test (a cold surface display, reuse, stop); - the real-backend browser suite (format, camera). - **By hand:** - in the web viewer: - the home, the explorer and its search, "File does not exist", a relative `?file=`; - the logo menu, and Display above the cube; - the update button and its card; - an inline card in a local MCP Apps harness: the menu, Back to files, the explorer; - the launcher on a custom port: start, reuse from another folder, refuse a port another program holds, stop. - **Not run for views by absolute path:** Claude Desktop and Codex end to end. - **Full local run after the rebase onto #499 and #526, with the update button (all pass):** - bundle check; - Python: - cadgen 2309; - skills 280, 19, 51, 77, 4, 6 and 6; - global 168; - JS: - core 1282; - bench 24; - shared UI: node 764 + 63, vitest 350; - browser viewer 61; - CAD app 36; - type checks for the shared UI, the CAD app and the browser viewer; - browser viewer and CAD app builds; - docs lint, type check and production build; - `check-version.sh`, `sync-version.mjs --check` and `check-builds.sh`. - **After #527 and the install copy:** - docs check; - `test-docs.sh` (16, with #527's logo tests); - global 168; - `check-builds.sh`. - The docs page was checked in a browser: Update or reinstall at desktop and phone width, and `/install` landing on `#install`. - **End to end, on a simulated fresh machine:** - Setup: - this branch's wheel as cadgen 0.7.99, with the plugin files stamped to it as a release would; - an empty uv cache with no uv Python; - every server started by its package's exact config, every skill command by its SKILL.md's exact launch line. - 28/28 checks: - **Cold install:** first start from a desktop app's bare PATH downloaded uv's Python 3.13.14 and every package and answered the MCP handshake in 7 s. A warm start took 0.2 s. - **One installation:** the server and the skills' `python` and `cadgen`, from a terminal PATH and a bare PATH, all use one uv environment on uv's Python. `cadgen doctor` confirms the pin and the kernel. - **One daemon:** a build run by a skill (`python src/bracket.py`) starts the daemon, and `cadgen daemon status` from either PATH shows the same process, the one `doctor` names. - **All 8 packages:** main's 4 configs, the README's Claude Desktop config, the `plugin` branch's 2 and the OpenAI ZIP's. The told copies get the notice. The auto-updated ones don't. - **Text apps:** a store copy's `cad_show` has no line and its server-started viewer no button; a GitHub copy has both. - **Skill commands** print nothing and read no feed. A viewer a skill starts shows the button. - **Right after merge:** HEAD's real configs (pinned 0.7.11) start on PyPI's 0.7.11. - **Agent apps**, each in a throwaway home (real configs verified unchanged): - Claude Code 2.1.281: marketplace and plugin install, `claude mcp list` shows the server ✔ Connected. - Codex 0.153.4: marketplace and plugin install, with the server and its channel in `codex mcp list`. - Grok Build 1.0.40: install, then `grok mcp doctor` gives a handshake and 14 tools. - **Not run here:** Gemini CLI (not installed on this machine), and the Cursor, Claude Desktop and Codex desktop apps (GUI only, or in use). - **Nested Claude Code agents**, with stand-in CLIs and isolated config: - Sent the update prompt, a copy installed from GitHub ran `claude plugin marketplace update earthtojake` and `claude plugin update text-to-cad@earthtojake`, then said to restart. - A Claude directory copy changed nothing. The agent said the store updates it and offered the switch. - An update line in a CAD result was followed the same way. - **Earlier, against the bundled wheel run through uvx as users get it, with a local feed:** - store and dev copies never asking; - the line on a text client's `cad_show`; - `cad_version` for app clients; - the viewer showing the analytics card, then the update button. - **Not run:** Codex and Claude Desktop end to end, because Codex was in use by another thread. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4d420edd14 |
Release 0.7.11
Bumps VERSION, derived package/plugin metadata and every skill's cadgen pin to 0.7.11. Created by Prepare Release, which merges it into main immediately; the merge runs Publish Release. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
7acde7dade |
Cursor plugin, and one plugin branch for every directory (#520)
Makes text-to-cad a Cursor plugin and turns the Claude-only release
branch into a provider-neutral `plugin` branch.
**Cursor**
- `.cursor-plugin/plugin.json` (Cursor's own format): the skills, CAD's
server from `claude.mcp.json` (Cursor reads Claude's MCP format, so no
third copy of the command), and the Claude icon as its logo.
`sync-version.mjs` stamps its version.
- **Tested in Cursor:** the built plugin tree in
`~/.cursor/plugins/local/text-to-cad` loads (`loadUserLocalPlugin
text-to-cad`, 0 failures), Cursor starts the `cad` server as
`plugin-text-to-cad-cad`, and the server picks **inline views** because
Cursor's client (`cursor-vscode`) advertises
`io.modelcontextprotocol/ui`. Not yet exercised: asking the agent to
show a model in a chat.
**Manifest consistency** (tests in `test_plugin_manifests.py`)
- The Cursor manifest carries the same listing links as Claude's
(documentation, support, privacy policy, terms, author URL); Codex's
`interface` URLs must match too. Cursor reloads it with them, 0
failures.
- One long description across the Claude, Codex (both fields), Cursor
manifests and the marketplace entry.
- One short description, **"Design 3D models"**: Codex's
`shortDescription` and the Claude marketplace's description and metadata
(which said "Give your agent CAD superpowers."). Cursor's format has no
short field.
**The `plugin` branch**
- `claude_plugin_branch.py` → `plugin_branch.py` (test renamed with it);
the tree gains `.cursor-plugin/plugin.json` (100 files, still checked
against claude.ai's rules, the strictest).
- `Publish Release`'s job is now `plugin-branch` and pushes the commit
to `plugin`. The first run builds on `claude-plugin`, so both names
share history.
- **`claude-plugin` stays:** claude.ai's listing tracks it and the
portal refuses a tracked-branch change while a reviewer has the plugin,
so the same commit is pushed to both. A TODO in the workflow and a dev
note in CONTRIBUTING.md ("The plugin branch") say to switch the listing
to `plugin`, then drop that push and delete `claude-plugin`.
**`main` stays installable for every host**: every manifest
(`.claude-plugin/`, `.codex-plugin/`, `.cursor-plugin/`) and MCP config
is at its root, the README's install commands clone it, and
`test_every_provider_plugin_manifest_exists_at_the_repo_root` keeps it
that way. The README gains Cursor's install (clone into
`~/.cursor/plugins/local`, or a team marketplace import) and notes
`@plugin` / `--branch plugin` for a lean install. AGENTS.md,
CONTRIBUTING.md and scripts/README.md follow the rename.
Checks: global suite 166 tests OK, `sync-version --check`,
`check-version.sh`, `plugin_branch.py --check`, `plugin_zip.py --check`,
`claude plugin validate`.
After merge: the `plugin` branch appears at the next release. Then
submit the repo at https://cursor.com/marketplace/publish.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|