PLN-228: fix: improve install script error handling and diagnostics - #43
Merged
Merged
Conversation
Collaborator
|
bump build version and it LGTM! |
wongk
approved these changes
Apr 9, 2026
mikeangstadt
approved these changes
Apr 9, 2026
peterulsteen
force-pushed
the
symphony/pln-228
branch
from
April 9, 2026 18:36
212f64c to
ebb02ee
Compare
…messaging, and shell guard
Rework install.sh so `curl | bash` failures produce actionable output,
re-runs give meaningful per-item feedback, and non-bash invocations
fail fast with guidance.
- Surface real `claude` CLI errors by capturing stderr to per-run
tempfiles under `${TMPDIR:-/tmp}/closedloop-install.XXXXXX` (cleaned
via an EXIT trap) and displaying them through a BSD- and GNU-sed
portable `sanitize_stderr` helper (literal ESC via ANSI-C quoting
plus POSIX `tr` octal ranges).
- Make the marketplace registration idempotent via
`claude plugin marketplace list --json` — re-runs print
"Marketplace already registered: closedloop-ai" instead of
re-invoking `marketplace add`.
- Classify each plugin per run by diffing pre/post snapshots from
`claude plugin list --json`, printing exactly one of `Installed:`,
`Updated: <plugin> (<old> -> <new>)`, or `Already up to date:`, and
rendering a three-count summary that preserves the failed count.
- Add a POSIX-portable `BASH_VERSION` guard at the very top of the
script so `dash install.sh` (and similar) exit cleanly with a
single-line guidance message before any bash-only syntax runs.
- Promote `jq` from a soft warn-and-continue to a hard preflight
requirement with brew/apt install instructions, since the snapshot
and idempotency logic depend on it.
Verified on macOS (bash 3.2, BSD sed): fresh install, idempotent
re-run, bogus marketplace source, invalid plugin ref, synthesized
`Updated` version delta, `dash install.sh`, and `jq` hidden from
PATH all produce the expected output and exit codes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
peterulsteen
force-pushed
the
symphony/pln-228
branch
from
April 9, 2026 19:50
ebb02ee to
81c89bf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Improves
install.shto cover the full scope of PLN-228. Changes are confined toinstall.sh; no plugin files touched.Error surfacing (AC-002, AC-004, AC-005)
claudeCLI stderr to per-run tempfiles under${TMPDIR:-/tmp}/closedloop-install.XXXXXXand display the underlying error when marketplace registration or plugin install/update failssanitize_stderrstrips ANSI color escapes and non-printable control characters using bash ANSI-C quoting ($'\033') plustroctal ranges so it works under BSD sed (macOS) as well as GNU sedEXITtrapIdempotent re-run messaging (AC-001, AC-006, AC-007, AC-008, AC-009, AC-010)
claude plugin marketplace list --json+jq— re-runs printMarketplace already registered: closedloop-aiinstead of re-invokingmarketplace addclaude plugin list --json+jqclassify each plugin as one of three distinct states:Installed: <plugin>,Updated: <plugin> (<old> -> <new>), orAlready up to date: <plugin>6 installed, 0 updated, 0 already up to datevs.0 installed, 0 updated, 6 already up to date), preserving the failed countShell guard (AC-011)
BASH_VERSIONguard at the very top of the script, before any bash-only syntax, so invocations under dash/ash exit cleanly with a single-line guidance message pointing atbash install.shor the piped-curl invocationBASH_VERSINFO≥ 3.2 so macOS's default bash still worksjqhard prereq (AC-012)jqpromoted from soft warn-and-continue to a hard preflight requirement (needed for the snapshot/idempotency logic). Missingjqprintsbrew install jq/apt install jqand exits before any marketplace or plugin operations runTest plan
Verified end-to-end on macOS (bash 3.2, BSD sed):
Installed: <plugin>, summaryAll 6 plugins ready (6 installed, 0 updated, 0 already up to date)(AC-001, AC-002, AC-003, AC-007, AC-008)Marketplace already registered: closedloop-ai, 6×Already up to date: <plugin>, summaryAll 6 plugins ready (0 installed, 0 updated, 6 already up to date); output visibly different from fresh run (AC-006, AC-007, AC-008, AC-009)MARKETPLACE_SOURCE) — underlyingclaudeCLI error surfaced to user (Failed to add marketplace: …HTTPS authentication failed…) instead of a generic warn line (AC-005)nonexistent-plugin-xyzadded toPLUGINSarray) — both install and update attempts fail,sanitize_stderrsurfaces the real CLI error (Failed to update plugin "nonexistent-plugin-xyz@closedloop-ai": Plugin "nonexistent-plugin-xyz" not found),warnfires, FAILED counter increments, summary shows3 plugins processed: 0 installed, 0 updated, 2 already up to date, 1 failed(AC-003, AC-004)Updatedpath with a version delta (pre-seededSNAPSHOT_PREwith a fictitious0.0.1forbootstrap) — classification correctly emitsUpdated: bootstrap (0.0.1 -> 1.2.0), summary showsAll 6 plugins ready (0 installed, 1 updated, 5 already up to date)(AC-007 Updated branch, AC-008)dash install.sh— bash guard fires, exits 1 with the guidance message, noclaudecommands executed (AC-011)jqhidden fromPATH— preflight exits 1 with brew/apt install instructions before any marketplace or plugin operations run (AC-012)Bugs uncovered and fixed during verification
snapshot_versiontrippedset -eon an empty grep match inside the classification command substitution — fresh installs succeeded but the script errexited before printing any per-plugin status or the summary. Fixed with|| true.UP_TO_DATEbranch only incremented the counter with noinfoline, violating AC-007's per-plugin message requirement. Fixed by emittingAlready up to date: <plugin>.sanitize_stderrused GNU-sed-only\x1b/\x00-\x08escape syntax and errored withRE error: invalid character rangeon BSD sed (macOS), silently swallowing the CLI errors AC-002/AC-004/AC-005 require to be shown. Rewritten to use a literal ESC via bash ANSI-C quoting plustrwith POSIX octal ranges.🤖 Generated with Claude Code
Loop ID: 019d6e91-f371-75eb-84fa-93dfae76959e
Artifact: https://app.closedloop.ai/implementation-plans/PLN-228