Repository navigation
v1.5.6 — one-button installer
1.5.6 - 2026-05-02
One-button installer release. The shipped tdpilot.tox is now a
self-installing container COMP — drag it into any TD project and the
Install + Update panels do the rest. No Textport gymnastics, no manual
.tox drag into /local, no shell scripts.
Tool count unchanged at 101 (the installer is COMP-side Python, not new
MCP tools). API_VERSION stays at 1.5.3 (HTTP protocol unchanged).
The .tox itself is meaningfully different: it now embeds four new
source files (installer.py, installer_exec.py, autostart.py,
renderer.py) that the freshness gate tracks via SHA-256.
The work was split into four commit-sized phases on
feat/v1.5.6-installer, all merged for this release:
Phase A — Detection scaffold + plan doc
docs/v1.5.6/INSTALLER_PLAN.md— 11-section plan covering goals, the
three install paths drift problem, restart-TD update pattern, the
enableexternaltox=Truearchitecture decision, the auth-bypass
conflict resolution, and risks requiring sign-off.installer.detect_state()— pure read, never mutates: probes uv,
git,claudeCLI,~/.tdpilot/pyproject.toml, TD prefs autoload
state,installed_plugins.json,.tdpilot.envsecret presence,
~/.tdpilot_pathcontents.installer.refresh_status_params()— writes the live status into the
parent COMP's custom params (Installstatus,Updatestatus,
Installedversion).
Phase B — Threading + install primitives
- Lock-protected
_job_statedict shared between the bg daemon thread
and TD's main cook thread. Bg threads are forbidden from touching
TD ops directly; they raisepending_actionflags and wait for the
main thread to consume. _wait_for_main_thread_action("save_toe", timeout=30)— the
main-thread bridge forproject.save(). Polls every 50ms with the
lock held briefly enough not to stall the cook.- Late-binding paths:
install_dir(),config_file(),env_file(),
autoload_toe(),pyproject(),prefs_path()are now functions
that re-readTDPILOT_INSTALL_DIR/TDPILOT_CONFIG_FILEper call.
Enables sandbox redirection mid-session without module reload. install_python_wrapper()— probe uv → install uv (curl … | sh)
→ git clone (or zip fallback) → pin to latest release tag →uv sync→ write.tdpilot.envwithTD_MCP_REQUIRE_AUTH=0. Verified
end-to-end in/tmpsandbox: clean v1.5.3 install in 12.7 seconds.set_td_autoload()— write~/.tdpilot_path, updatepref.txt
(general.startupfilemode=2+startupfilename), then bridge to
the main thread viasave_toe.uninstall_all()— revert prefs, remove config + autoload toe +
install dir.TDPILOT_KEEP_INSTALL_DIR=1escape hatch for testing.- Critical bug-fix during testing:
autostart.pynow calls
installer.module.autoload_toe()(the function) not
installer.module.AUTOLOAD_TOE(the module-eval-time constant).
The constant didn't followTDPILOT_INSTALL_DIRoverrides set
later in the session, so a sandbox test clobbered the user's real
~/.tdpilot/tdpilot_default.toe. Function call re-reads env on
every call.
Phase C — Claude plugin install + bootstrap orchestrator
install_claude_plugin()— checksinstalled_plugins.jsonFIRST
so users who already have the plugin via.mcpbdrag-drop into
Claude Desktop get a clean "already installed" path without needing
theclaudeCLI on PATH. Recognizes both registration keys
(tdpilot@dreamrec-TDPilotfrom Claude Code CLI marketplace,
tdpilot@local-desktop-app-uploadsfrom.mcpbdrop). Only when
the plugin is missing AND the CLI is missing do we raise the
"install Claude Code first" actionable error with the manual
fallback command.bootstrap_all()— single-job orchestrator that calls the
underscored_do_*helpers directly inside one bg thread sharing
one progress callback, so the panel sees one progress stream
instead of three Phase-B job lifecycles colliding. Claude plugin
step is non-fatal: ifclaudeCLI is missing AND plugin install
not detected, the user still gets a working Python wrapper +
autoload (they can install plugin separately later). Wrapper or
autoload failures abort the whole job.
Phase D — Update + rollback with 24h cache
check_for_updates(force=False)— synchronous (no bg job) single
curlGET againstapi.github.com/repos/dreamrec/TDPilot/releases/latest
with 5s timeout. Why curl, noturllib: TD's bundled Python doesn't
ship a CA bundle, sourllib.request.urlopenonhttps://fails
withCERTIFICATE_VERIFY_FAILED./usr/bin/curluses the system
trust store and Just Works on macOS/Linux. 24h cache at
~/.tdpilot/last_check.json. Cache hit returns in ~0.4ms (vs ~300ms
network); refreshed installed-version is recomputed every call so
semver compare stays accurate even on cache hit. Failures are NOT
cached so the next call retries. Verified live: installed=1.5.2,
latest=1.5.3, update_available=True, release_url + first 80 chars
of notes returned correctly._semver_tuple— tolerant parser."1.5.6","v1.5.6",
"1.5.6-rc1","1"all parse to 3-tuples; failures return
(0,0,0)so an unreadable installed version compares as "anything0.0.0 = update available".
update_now()— bg job, ~30s wall-clock. (1)_smart_copytree
snapshot tobackups/<ts>/excluding.venv,.git,knowledge/,
memory/,*.db/sqlite,__pycache__,*_cache/,node_modules.
Backups exist to recover code+config;.venvrebuilds from
uv.lockand brain DBs are regenerable, so excluding them keeps
backups light (single-digit MB instead of hundreds). (2)git fetch --tags && checkout <latest tag>(zip fallback if.gitis
missing — backup is the safety net). (3)uv sync. (4)
_wait_for_main_thread_action("save_toe")— re-saves autoload
.toeso the externaltox link points at fresh content on next TD
launch.rollback()— find newest~/.tdpilot/backups/<ts>/, move current
install totarget.rollback-aside-<ts>, copytree the backup back.
If copy fails, swap the aside copy back so the user is never left
empty-handed. Cleanup aside on success. Re-runuv syncafter
restore (backup didn't include.venv).
Build pipeline
td_component/build_tdpilot_tox.py— the v1.5.6 successor to
build_export_mcp_tox.py. Constructs the full parenttdpilot
containerCOMP with custom param pages (Install + Update, 23 params
total), four installer DATs (installertextDAT,
installer_execparameterexecuteDAT withfromop=parent()
expression,autostartexecuteDAT,renderertextDAT), the
status_texttextTOP (Courier New 14pt left-top aligned, 16px
inset, 4px line-spacing, 55% white — matches the live design), and
the nestedmcp_serversub-COMP delegated to the legacy
_populate_componentso the v1.5.6.toxinherits every
MCP-server fix the legacy script accumulated. Reuses
_guess_repo_root,_read_repo_file,_set_first_par,
_create_with_fallback,_resolve_export_host,
_reset_or_create_comp, and_write_tox_source_hashfrom the
legacy script via direct import._TOX_SOURCE_FILESandSOURCE_FILESextended in
build_export_mcp_tox.pyandscripts/check_tox_freshness.pyto
hash the four new installer source files. CI's freshness gate now
fails when any of them change without a corresponding.tox
rebuild, same as for the existing four mcp_server sources.