Skip to content

fix: fall back to PATH when the baked agtermctl path is gone - #473

Merged
umputun merged 1 commit into
umputun:masterfrom
bot-rogerthat:fix/agtermctl-path-fallback
Aug 21, 2026
Merged

fix: fall back to PATH when the baked agtermctl path is gone#473
umputun merged 1 commit into
umputun:masterfrom
bot-rogerthat:fix/agtermctl-path-fallback

Conversation

@bot-rogerthat

Copy link
Copy Markdown
Contributor

Fixes #472.

The problem

AgentHooksInstaller.bakeAgtermctlPath writes Bundle.main's absolute agtermctl path into the two installed wrappers, and the wrappers call it as "${AGTERMCTL:-agtermctl}". That default only fires when AGTERMCTL is empty, so the PATH rung agterm-agent-status.sh documents as step 3 is unreachable in any wrapper that was baked — and nothing ever checks whether the baked path still exists.

A bundle that moves after the install therefore kills the hooks. Installing from the mounted DMG is the easy way in: Bundle.main is /Volumes/agterm/agterm.app then, and the baked path dies on eject. The wrapper suppresses stdout/stderr and exits 0 by design, so nothing surfaces — the sidebar glyphs just stop appearing. Mine had been dead for weeks.

The fix

Bake a block that tests the path before committing to it:

# >>> agterm agtermctl path (installer-baked) >>>
if [ -z "${AGTERMCTL:-}" ]; then
  AGTERMCTL='/Applications/agterm.app/Contents/MacOS/agtermctl'
  [ -x "$AGTERMCTL" ] || AGTERMCTL="$(command -v agtermctl || true)"
fi

Both the assignment and the test sit inside the unset branch, so an explicit AGTERMCTL override is never second-guessed and the documented order stays 1 > 2 > 3. A moved bundle now degrades to the CLI from Help ▸ Install Command Line Tool… instead of going silent. Re-running the installer still heals the path outright; this is what covers the window before anyone thinks to.

The transform moves into host-free AgentHooksInstall — the type the rules already put in charge of the marker and merge policy — leaving AgentHooksInstaller the file glue. That is what makes it testable at all: the app-side helpers were private in a target swift test cannot reach, which is why the bake/strip pair had no coverage. It also lets stripBakedBlock take its line count from the block being emitted rather than a literal skip = 1 that would have silently gone stale as the block grew from one line to four.

Tests

agtermCore, all new:

  • AgentHooksInstallTests — block lands after the shebang, carries the -x fallback inside the unset branch, and a re-bake replaces the block rather than stacking it (a second bake over a /Volumes one equals a first bake with the new path).
  • AgentStatusWrapperTests — bakes the shipped wrapper through the installer's own transform and runs it with three stub binaries that record which of them was invoked: the baked one wins when it exists, PATH wins when the baked bundle is gone, and an explicit AGTERMCTL wins over both.

The two fallback tests fail on the previous block (the run records nothing at all, which is the bug), and the other two pass either way, so they pin the behavior that must not change.

What I ran

  • cd agtermCore && swift test — green, 2607 tests.
  • swiftlint lint --strict — 0 violations in 344 files.
  • bash -n over both wrappers with the block baked in, and a standalone swiftc -typecheck -swift-version 6 -strict-concurrency=complete of the changed AgentHooksInstaller.swift against the built agtermCore.

I could not run make build or make test-app: this machine has Command Line Tools only, no Xcode 26, so scripts/setup.sh, xcodegen and the app target are out of reach here. The app-target change is the deletion of three private helpers and one call into agtermCore, but it has not been compiled in the real project and no XCUITests were run.

No user-facing action or control command changes, so no skill or site/ update. site/docs.html says the scripts are installed "with the bundled agtermctl's absolute path baked in so they work even without the CLI on your PATH", which stays true.

@bot-rogerthat
bot-rogerthat requested a review from umputun as a code owner August 20, 2026 21:31
@bot-rogerthat
bot-rogerthat force-pushed the fix/agtermctl-path-fallback branch from 49fbf82 to 6eb5c52 Compare August 20, 2026 21:35
The agent-status hooks bake the running bundle's absolute agtermctl path into
the installed wrappers, and the call site is "${AGTERMCTL:-agtermctl}", so the
PATH rung the wrapper documents is unreachable once anything is baked. A bundle
that moves after the install - installing from the mounted DMG bakes /Volumes,
dead on eject - leaves the wrapper pointing at nothing, and it suppresses output
and exits 0 by design, so every status update fails silently.

Bake a block that tests the path before committing to it, inside the unset
branch so an explicit override is still first. The transform moves to host-free
AgentHooksInstall, which makes it testable and lets the strip count come from
the block being emitted rather than a literal that has to be kept in step.

Fixes umputun#472
@bot-rogerthat
bot-rogerthat force-pushed the fix/agtermctl-path-fallback branch from 6eb5c52 to abb1ebb Compare August 20, 2026 21:36

@umputun umputun left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@umputun
umputun merged commit 58fae00 into umputun:master Aug 21, 2026
7 checks passed
umputun added a commit that referenced this pull request Aug 21, 2026
follow-up to #473, all maintainer cleanup. `agtermctlMarker` was public with no consumer outside
agtermCore, since both test suites reach it through `@testable`, and that re-opened surface
d54b50f had just closed in this same file. Internal now.

`stripBakedBlock`'s comment claimed that deriving the skip count from the emitted block meant
growing that block could not leave half the old one behind. It is the other way round: the count
measures what is being WRITTEN and is applied to what a previous install wrote, so any length
change mis-strips, over-eating the script when the new block is longer. Nothing reaches it because
`copyBundledFolder` re-copies the pristine wrapper before every bake, which is what the comment
now says instead.

The rest is trimming. #473's doc comment ran eight lines over a two-line body and repeated the
moved-bundle rationale on four surfaces; the wrapper's own header owns it, so the Swift side points
there and the restatement in `AgentHooksInstaller` is gone. Three test comments come back to one
line, keeping the `#472` reason a regression test is entitled to.

The agent-skill troubleshooting entry for a stuck glyph named neither the moved-bundle cause nor
either recovery, so an installed agent had nothing to tell a user whose baked path went dead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent-status hooks die silently when the app bundle moves after install

2 participants