Remove the redundant JSON-RPC initialize fallback from the consolidated MCP health-check batch. A routed 404 already proves the endpoint is reachable, and the real authenticated MCP call remains authoritative. Avoiding the fallback also prevents a stalled GET plus stalled POST from consuming twice the configured hook timeout.
The mcp-health-check preflight probes HTTP MCP servers with a bare GET.
Some Streamable HTTP servers route only POST /mcp and answer a bare GET
with 404 (Paper Desktop 0.5.3 is one). The probe scored that as down and
blocked every tool call for the server indefinitely, since the 30s
backoff just re-probes and re-fails.
A routed HTTP response of any status proves the endpoint is reachable,
which is all this preflight claims to check -- 400/401/403/405/406 are
already treated this way for the same reason. Add 404 to the set and let
the real MCP client validate the endpoint.
Adds a regression test that stands up a POST-only server (404 on GET,
200 on POST /mcp); it fails on the current code and passes with the fix.
The preflight probe in mcp-health-check only ever sent a bare GET to the
server URL. Some Streamable HTTP MCP servers route POST exclusively and
answer any GET with 404 — api.telnyx.com/v2/mcp is one — so the probe
failed permanently against a perfectly healthy server.
404 is not in HEALTHY_HTTP_CODES, so every probe failed, the backoff
compounded to the 10-minute ceiling, and the hook blocked every tool call
for that server before it left the machine while `claude mcp list` still
reported it Connected.
Replay a failed GET as a real JSON-RPC initialize POST and accept that as
proof of life. Whitelisting 404 was the alternative, but it would mask
genuine outages on every other server.
Adds a regression test with a POST-only server that 404s all GETs and
validates the initialize body; it fails without this change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cost-estimate.js carries its own copy of the stale Opus/Haiku/Sonnet rate
table already reported in #2574, but grepping every .js/.json/.md file
outside node_modules turns up zero callers besides its own test. It was
added in 940135e alongside the statusline observability hooks and never
wired into any of them.
The maintainer's comment on #2656 named two acceptable outcomes: remove
the unused duplicate, or share one rate source with the live tracker.
cost-tracker.js's own fix (#2574) has not landed yet, so sharing its
table now would import numbers that are still wrong. Removing the dead
file is the smaller, immediately-correct step.
Fixes#2656
cost-tracker.js: RATE_TABLE priced all Opus models at the legacy $15/$75
tier and routed Fable/Mythos 5 to Sonnet rates, overstating Opus 5
sessions ~3x and understating Fable ~3.3x in costs.jsonl. Adds fable
($10/$50) and current opus ($5/$25) tiers, keeps Opus 4.0/4.1/3 on the
legacy tier, updates haiku to 4.5 pricing ($1/$5).
session-end.js: extractSessionSummary included local-command echoes
(<local-command-caveat>, <command-name>, <local-command-stdout>),
system reminders, tool_result carrier turns and isMeta entries in the
Tasks list, so SessionStart reloaded noise instead of user asks. Adds
a noise filter.
Both test suites pass (10/10 cost-tracker, 1/1 session-end).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hasMarkerBlock previously swallowed every read/open error and returned
false, so an unreadable AGENTS.md (EACCES, EMFILE, EISDIR, ...) made
detectLegacyCodexSync report a clean Codex home instead of an
indeterminate inspection result. The fallback path could then skip
legacy cleanup and exit 0 with legacy artifacts still in place.
Restrict the catch to ENOENT (a missing file legitimately means no
marker block) and rethrow everything else. detectLegacyCodexSync
already propagates from hasMarkerBlock, so callers now see the actual
inspection error instead of a misleading 'no marker'.
Regression test in tests/lib/codex-legacy-sync.test.js makes a
detectLegacyCodexSync call against an unreadable AGENTS.md and asserts
that it throws something other than ENOENT, plus a sanity check that
a missing AGENTS.md still reads as no-marker.
Restrict the automatic `uninstall` legacy Codex sync fallback to homes
that have a legacy ownership manifest (`~/.codex/ecc/legacy-sync-state.json`).
Marker-only AGENTS.md files are no longer auto-detected as legacy installs,
so a normal `uninstall` will not silently modify user-owned instructions.
The explicit `--legacy-codex-sync` flag still handles marker-only and
manifest-backed cleanup.
Also:
- Track the AGENTS.md path in removedPaths when a marker block is removed.
- Refactor codex home resolution into a helper.
- Add regression tests for marker-only auto vs. explicit behavior.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Tighten uninstall auto-detection so it only falls back to the legacy
sync-ecc-to-codex.sh path when there is an ownership manifest
(~/.codex/ecc/legacy-sync-state.json) or an ECC marker block in
~/.codex/AGENTS.md. Previously a clean Codex home with unrelated prompt
files could be misclassified as a legacy install, causing uninstall to
skip normal install-state reconciliation and exit with a partial warning.
Also make the no-state fallback return 'not-found' when there is no
marker to remove and no candidate files to clean, and make explicit
--legacy-codex-sync report the same on a clean home.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When no install-state is found for the current context, `ecc uninstall`
now checks for the legacy `sync-ecc-to-codex.sh` ownership manifest under
`~/.codex/ecc/legacy-sync-state.json` and, if present, rolls back the
managed Codex artifacts it recorded. It restores previous `config.toml`
and `AGENTS.md` content instead of deleting them, removes generated
prompts/docs/copies, and leaves unrelated Codex conversation history and
user config keys untouched. A fallback `--legacy-codex-sync` flag still
forces the legacy path explicitly, and `--dry-run` previews the cleanup.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
OpenCode natively uses ~/.config/opencode per XDG conventions. The
install target was writing to ~/.opencode, which only worked on systems
where that path happened to be symlinked to ~/.config/opencode. The MCP
inventory reader already looked in ~/.config/opencode, so the installer
and reader were inconsistent.
Rebased onto origin/main (49128b576). Fixtures moved from
scripts/hooks/ to /tmp/ecc-pr2380-fixtures/ per reviewer feedback.
(Original commit b0e49036 was based on cc6724ee; main has since
refactored spawnShell to use a shellArgs variable and added
PowerShell .sh fallback paths. This rebase adapts the
const result = spawnSync(...) + __rawInput tagging pattern to
all three spawnSync call sites in spawnShell.)