Releases: EstebanForge/construct-cli
Releases · EstebanForge/construct-cli
Release list
The Construct CLI 1.9.4
[1.9.4] - 2026-06-20
Added
- SSH Identity Pinning (
ssh_pin_identities): New[sandbox]config option that pins one SSH identity per host to avoidToo many authentication failureswhen the forwarded agent holds many keys. Two entry forms:"host=keyname"(simple) and"alias=hostname=keyname"(multi-account on same service). Pinned identities are serialized intoCONSTRUCT_SSH_PIN_IDENTITIESand consumed byensure_ssh_config()in the container entrypoint, which emitsIdentitiesOnly yes+ the named key for each configured host. - Per-Session SSH Proxy Sockets: Each
constructprocess now uses its own socat socket (/home/construct/.ssh/agent.<pid>.sock) rather than a shared/home/construct/.ssh/agent.sock. Concurrent sessions sharing one daemon no longer overwrite each other's agent proxy.Teardown()cleans up only the calling session's socket. - SSH Bridge Regression Tests: Table-driven tests for
sshPinIdentitiesEnv(10 cases including malformed-entry guards),sshProxySockForPID(distinct PIDs, stable same PID, exact path), bridge no-agent guard, full proxy integration, and Bitwarden vault lock/unlock recycle case. - Entrypoint
ensure_ssh_configBash Tests: Newinternal/templates/entrypoint_ssh_config_test.goextracts and executes the realensure_ssh_configshell function in an isolatedHOME. Seven cases: no hardcodedIdentityAgent, physical-keys-only, agent pin with.pub, alias three-field form, physical-key fallback, pin skipped when key missing, opt-out (# construct-managed: false) respected.
Fixed
- SSH Agent Forwarding in Container:
ensure_ssh_config()no longer emits phantomIdentityFilepaths for keys that do not exist on disk, eliminating false "no SSH key" reports from agents. Hardcoded~/.ssh/defaultand~/.ssh/personalare only emitted when the files are actually present. IdentityAgentOverride Removed: TheHost *block no longer writesIdentityAgent ~/.ssh/agent.sock, which was overriding the per-sessionSSH_AUTH_SOCKenv var and routing all agent requests to whichever session last wrote that socket. SSH now usesSSH_AUTH_SOCKdirectly, injected per-session by the engine.- Error Context on SSH Proxy Helpers (Go Mistakes #49): Both
ensureDaemonSSHProxyandwaitForDaemonSSHProxynow wrap errors with%w, including container name, socket path, and port, enablingerrors.Is/errors.Asunwrapping and actionable messages in multi-session scenarios.
The Construct CLI 1.9.3
[1.9.3] - 2026-06-18
Fixed
- Pi Update No Longer Updates Extensions: Pi changed the bare
pi updateto update only pi (self), withpi update --allrequired to update pi and its extensions together. Construct's system update (ct sys update) was still calling barepi update, so pi extensions were silently no longer updated on each run. All three invocation sites now usepi update --all: the generated topgrade config (packages.go), the statictopgrade.tomltemplate, and the manualupdate-all.shfallback.
The Construct CLI 1.9.2
[1.9.2] - 2026-06-18
Removed
- Worktrunk: Removed
worktrunkfrom the default Cargo install list (packages.toml). Upstreamworktrunk 0.59.0fails to build from a registry tarball (cargo install) because itsvergen-gitclbuild script cannot computeVERGEN_GIT_DESCRIBEoutside a git worktree, andsrc/cli/mod.rsuses a hard compile-timeenv!("VERGEN_GIT_DESCRIBE"). The Topgrade/Cargo update step aborted on every update run. The historical 0.11.1 addition entry is retained for accuracy.
The Construct CLI 1.9.1
[1.9.1] - 2026-06-15
Added
- SSH Agent Reachability Check in
sys doctor: The existing "SSH Agent" check only verifiedSSH_AUTH_SOCKwas set, which a stale/recycled socket (e.g. Bitwarden/1Password after lock) passes while every in-containerssh/gitop fails silently. The check now probes the agent directly viassh-add -land reports reachable (with key count), reachable-but-no-keys, not-reachable, or unknown (ssh-add missing). Extracted into a unit-testedcheckSSHAgenthelper.
Fixed
- Concurrent Setup Deadlock: Two
docker compose runsetups running at once (e.g. a user retrying because setup looked stuck) share the same home bind-mount and both write into npm's shared globalnode_modules+ cache, deadlocking on npm's cache/lock and hanging indefinitely. Setup now takes a non-blocking exclusiveflockon~/.config/construct-cli/setup.lockbefore spawning the compose run; a second instance refuses to start and tells the user to wait. The lock auto-releases on process exit, so no manual cleanup is needed after a crash. - Redundant npm Reinstalls During Setup: Every
npm install -gin the generated setup script used--force, re-fetching and re-linking all global packages on every setup run. This made setup slow enough to look stuck (the trigger for the retry that caused the deadlock above).--forceis dropped from the setup path so npm skips packages already at the target version.update-all.shstill uses--forcefor explicit@latestupgrades (intentional). - SSH Agent Bridge Silently Failed on Stale Port: The in-container
socatSSH-agent proxy (started byentrypoint.shat container creation) baked the host bridge port into its argv. The host bridge (StartSSHBridge) bound a random ephemeral port that changed across CLI invocations, so the stalesocatkept pointing at a dead host port and everyssh/gitop failed with "communication with agent failed". The v1.8.15 "restartsocaton each exec" fix did not actually work:ensureDaemonSSHProxybackgroundssocat(returns near-instantly, almost never errors) and both exec sites discarded the result ofwaitForDaemonSSHProxy(gated onif err == nil). The liveness probe wastest -S(socket file exists), which a leftover socket or stalesocatpasses. Now both exec sites check and log both errors, and the probe is a realUNIX-CONNECT(socket actually accepting connections). - Topgrade Brew Step Crash on Linux: The
homebrew/casktap (auto-tapped underHOMEBREW_NO_INSTALL_FROM_APImode) breaksbrew upgradeon Linux because casks use arch-conditionalsha256 arm:/intel:that resolves to nil on non-macOS systems (e.g.Casks/0/0-ad), aborting the whole update run and stalling all formula upgrades. Bothupdate-all.shandentrypoint.shnow defensively untaphomebrew/caskon Linux (idempotent). Casks are macOS-only and non-functional on the Linux box.
Changed
- Deterministic SSH Bridge Port per Box:
StartSSHBridgenow derives a stable TCP port from the box identity (hash of the container name) instead of an ephemeral random port, with fallback to ephemeral on bind failure. The same box now maps to the same host port across invocations, so a stalesocatbaked at container creation keeps pointing at the right port instead of aging out. Band sits below the Linux ephemeral range (32768+) to reduce OS collisions. Correctness is still guaranteed by the per-execsocatrestart regardless of port strategy.
The Construct CLI 1.9.0
[1.9.0] - 2026-06-10
Added
- Non-interactive Container Exec: New
construct sys exec -- <command>command allows running a single command inside a running Construct container without attaching to an interactive shell. Designed for LLM agents operating in headless environments that need to execute commands inside the container and capture output. Streams stdout/stderr separately to the host, returns the container process exit code. Supports both daemon and CWD-scoped containers. Requires a running container (start withconstruct sys shellorconstruct sys daemon start). - Daemon Name Constant: Canonical
DaemonNameconstant ininternal/constants/constants.goreplaces scattered string literals across agent engine and sys packages. - Container Naming Export:
CwdContainerName()moved frominternal/agent(unexported) tointernal/runtime(exported) for cross-package reuse. - Non-interactive Exec Primitive:
ExecNonInteractiveStream()ininternal/runtime/runtime.goexecutes commands in running containers without TTY allocation, streaming stdout/stderr separately, returning real exit codes.
Changed
MapDaemonWorkdirandReadKeyringEnvexported from agent package for reuse bysys exec.- Smart Migration Rebuilds: Container image rebuilds are now gated by per-template hash tracking. Version bumps that only change Go code (no template changes) skip the rebuild entirely, reducing update time from ~2 minutes to ~2 seconds. Templates are classified into tiers: image-baked (Dockerfile, entrypoint.sh, etc.) trigger a full rebuild; runtime-only (docker-compose.yml, agent-patch.sh) trigger a deferred restart; no changes means no rebuild.
The Construct CLI 1.8.15
[1.8.15] - 2026-06-03
Changed
- Cross-Platform SSH Agent Bridge: Replaced macOS-only SSH agent forwarding with a TCP bridge that works on both macOS and Linux. On macOS the bridge binds
127.0.0.1(Docker Desktop routes it); on Linux it binds0.0.0.0so containers reach it viahost.docker.internal. Removed directSSH_AUTH_SOCKsocket mounts and permission-fixing logic from entrypoint and compose overrides. - Dynamic SSH Agent Socket Re-reading: The TCP bridge now re-reads
SSH_AUTH_SOCKper connection, with up to 3 retry attempts (100ms backoff). Handles agents like Bitwarden that recycle socket paths on vault lock/unlock. - Daemon SSH Proxy Restart: Running containers now get their socat proxy restarted with the current bridge port on each
exec, preventing stale-port failures across sessions.
Fixed
- NPM Package Setup Failures: Added
--forceflag to global npm package installs and upgrades during construct provisioning. Prevents setup crashes caused by pre-existing symlink conflicts (e.g.EEXISTconflicts during@kilocode/cliinstallation) and cascadingtar TAR_ENTRY_ERROR ENOENTextraction errors (which blocked the installation of thepipackage).
The Construct CLI 1.8.14
[1.8.14] - 2026-06-02
Added
- Global Gitignore Mount: User's global gitignore file is now automatically mounted (read-only) into the container at
/home/construct/.config/git/ignore. Detects the file from four locations in priority order:git config core.excludesFile,$XDG_CONFIG_HOME/git/ignore,~/.gitignore, and~/.gitignore_global. Tilde paths and spaces in paths are handled correctly. Mount is included in the override hash for proper cache invalidation.
The Construct CLI 1.8.13
[1.8.13] - 2026-06-01
Added
- Global Gitignore Mount: User's global gitignore file is now automatically mounted (read-only) into the container at
/home/construct/.config/git/ignore. Detects the file from four locations in priority order:git config core.excludesFile,$XDG_CONFIG_HOME/git/ignore,~/.gitignore, and~/.gitignore_global. Tilde paths and spaces in paths are handled correctly. Mount is included in the override hash for proper cache invalidation.
The Construct CLI 1.8.12
[1.8.12] - 2026-05-20
Added
- CWD-Derived Container Naming: Each working directory now gets its own container (
construct-cli-<sha256[:8]>) instead of a shared singleton. Running agents from multiple terminals with different working directories no longer conflicts with "Container 'construct-cli' is already running." Same directory always hashes to the same container name, preserving attach semantics.
Fixed
- Daemon Killed by
sys doctor --fix:cleanupAgentContainerprefix-matchedconstruct-cli-daemonand killed it;recreateDaemonContainerthen saw it missing and no-op'd. Daemon is now excluded from session container cleanup. - Stopped Containers Returned by Network Manager:
runningSessionContainersuseddocker ps -aq(all states), causing spurious UFW rule warnings on stopped containers. Now filters by running state. - Legacy Singleton Missed by Migration:
collectSessionContainersonly discovered CWD-hash containers, missing pre-upgradeconstruct-clisingleton. Now includes exact-match discovery for the legacy name. - Stale Error Message:
sys doctor --fixsuggestion referenced olddocker rm -f construct-cliinstead of prefix-based cleanup. - Keyring Env Path Hardcoding:
readKeyringEnvusedos.UserHomeDir()instead ofconfig.GetConfigDir()for the keyring env file path.
Changed
- Container Discovery: All consumers of the static
"construct-cli"container name (doctor.go,migration.go,network/manager.go,reset-environment.sh) now discover containers by prefix"construct-cli-"viaruntime.ListContainersByPrefix(). - Architecture Docs: Updated
ARCHITECTURE-DESIGN.mdSections 4, 9.3, and 11.2.1 for the new naming pattern.
The Construct CLI 1.8.11
[1.8.11] - 2026-05-20
Added
- Antigravity Update Integration: Added
agy updateintegration to the dynamic Topgrade generator (packages.go),topgrade.tomltemplate, and the manual system update fallback script (update-all.sh).
Fixed
- Yolo Configuration for agy: Fixed yolo settings (
yolo_allandyolo_agents) to correctly apply the--dangerously-skip-permissionsflag when initializing theagyagent. - Agent Credential Persistence: Added
gnome-keyring,libsecret-1-0, anddbus-x11to the container image, with automatic daemon startup in the entrypoint. Agents (e.g.,agy) that rely on the OS keyring for OAuth tokens now persist credentials across container restarts. Previously, every session required a fresh login. - Keyring Daemon Startup: Fixed
gnome-keyring-daemoninvocation in the entrypoint. The--startand--unlockflags are mutually exclusive and caused the daemon to silently fail, leaving the keyring locked. Changed to--unlock --components=secretswhich both starts the daemon and unlocks the login keyring with a blank password. - Keyring Env Vars Not Reaching Agents:
docker execruns agents directly (no shell), so.bashrc/.profileare never sourced andGNOME_KEYRING_CONTROL/DBUS_SESSION_BUS_ADDRESSwere invisible to agent binaries. agy's keyring auth timed out after 1s, fell back to browser OAuth, and hung. Fixed by having the entrypoint write these vars to~/.construct-keyring-env, which the Go CLI reads from the host-side bind mount and injects via-eflags on everydocker exec.