fix(grunt/tui): proper hover-preview + click for #N ticket refs#14
Merged
Conversation
…aces ugly [label](url)) Refs hivemind anomalyco#233. Closes the 'ugly link being showed' regression Nik flagged immediately after the first hover-preview attempt. ## What changed Previous shipped path: pre-process text to inject markdown links `[anomalyco#229](http://localhost:5173/tasks/229)`. Opentui's markdown renderer displays inline links as the LITERAL bracket+label+paren+URL string (see node_modules/.bun/@OpenTui+core/index*.js renderInlineToken 'link' case). Even in conceal mode the URL leaks as `<label> (url)`. Result: colored but visually ugly, no hover, no click handler. New path: split assistant text on the ticket-ref regex BEFORE markdown sees it. Render non-matching segments through markdown as before (so formatting + code blocks keep working). Render each matched `#N` as a new <TicketRef> component (packages/opencode/src/cli/cmd/tui/component/ticket-ref.tsx). ## TicketRef component - Renders `anomalyco#229` colored (theme.markdownLink) + underlined inline - onMouseOver: lazy-fetches /api/tasks/<id> from hivemind-api (with module-level cache + in-flight dedup), shows a floating tooltip with title + status + priority + zone + owner + scope preview - onMouseUp: opens http://localhost:5173/tasks/<id> via 'open' npm pkg (same package the existing Link component uses) Trade-off: inline markdown features that span a #N boundary (rare — e.g. `**bold anomalyco#229 text**`) won't span the split. Acceptable for prose. ## Bundled fix: install-local.sh version stamping Working tree also contained an uncommitted update to scripts/install-local.sh (originated from a parallel agent during this session). It changes the default version label from `git describe --dirty` (which made local builds look version-ahead of any released gruntcode → broke brew-binary boot when local migrations had advanced the DB) to a semver build-metadata format like `1.15.10-grunt.7+local.<sha>`. The base matches the latest grunt tag; the +local.<sha> part is semver build-metadata that's ignored for ordering. Also adds ahead-of-tag detection that warns about new migrations. Including it here since it was needed for THIS install to not break on next brew upgrade.
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
terrxo
added a commit
that referenced
this pull request
May 27, 2026
…lyco#258) (#19) Add a smoke-test step that runs BEFORE the freshly-built binary replaces the brew Cellar binary. Catches the class of regression where the build succeeds at the bundler layer but the produced binary crashes at module-load / plugin-resolution / config-read — the surface that silently shipped a U.length crash 2026-05-27 (PR #14-17 era) and went undetected for ~1h because already-running tabs kept their mmap'd old binary. Two layers, both cheap (~0.7s total on a healthy binary): 1. `--version` — catches module-load + top-level import crashes 2. `debug info` — catches plugin-resolution + config-load regressions (exercises the same module graph the TUI mount path does) If either layer fails — non-zero exit, OR exit 0 with TypeError / ReferenceError / SyntaxError / fatal-error / U.length / 'Cannot read prop' on stderr — the install aborts and the working brew binary stays put. The build output is preserved at $SRC_BIN for inspection. `--skip-smoke` flag provided for emergency overrides (rebuild loops on a known-broken state where the new binary is wanted despite a smoke fail). `timeout` wrapper detection: macOS doesn't ship coreutils by default, so `timeout`/`gtimeout` are detected at runtime; absent both, the smoke runs unwrapped (the commands themselves exit fast, so the wrapper is belt-and-suspenders, not load-bearing). Verified locally with three integration cases against the in-script smoke block: - Healthy gruntcode binary → proceeds to install - Crashing fake-bin → refused + exit 1, install aborted - Stealth crash (exit 0 + TypeError on stderr) → refused + exit 1 - `--skip-smoke` → smoke skipped, install proceeds Help text + doc-comment header updated; new `Smoke-test (pre-install verification)` section explains what the smoke does + how to disable. Closes anomalyco#258.
terrxo
added a commit
that referenced
this pull request
May 28, 2026
…aces ugly [label](url)) (#14) Refs hivemind anomalyco#233. Closes the 'ugly link being showed' regression Nik flagged immediately after the first hover-preview attempt. ## What changed Previous shipped path: pre-process text to inject markdown links `[anomalyco#229](http://localhost:5173/tasks/229)`. Opentui's markdown renderer displays inline links as the LITERAL bracket+label+paren+URL string (see node_modules/.bun/@OpenTui+core/index*.js renderInlineToken 'link' case). Even in conceal mode the URL leaks as `<label> (url)`. Result: colored but visually ugly, no hover, no click handler. New path: split assistant text on the ticket-ref regex BEFORE markdown sees it. Render non-matching segments through markdown as before (so formatting + code blocks keep working). Render each matched `#N` as a new <TicketRef> component (packages/opencode/src/cli/cmd/tui/component/ticket-ref.tsx). ## TicketRef component - Renders `anomalyco#229` colored (theme.markdownLink) + underlined inline - onMouseOver: lazy-fetches /api/tasks/<id> from hivemind-api (with module-level cache + in-flight dedup), shows a floating tooltip with title + status + priority + zone + owner + scope preview - onMouseUp: opens http://localhost:5173/tasks/<id> via 'open' npm pkg (same package the existing Link component uses) Trade-off: inline markdown features that span a #N boundary (rare — e.g. `**bold anomalyco#229 text**`) won't span the split. Acceptable for prose. ## Bundled fix: install-local.sh version stamping Working tree also contained an uncommitted update to scripts/install-local.sh (originated from a parallel agent during this session). It changes the default version label from `git describe --dirty` (which made local builds look version-ahead of any released gruntcode → broke brew-binary boot when local migrations had advanced the DB) to a semver build-metadata format like `1.15.10-grunt.7+local.<sha>`. The base matches the latest grunt tag; the +local.<sha> part is semver build-metadata that's ignored for ordering. Also adds ahead-of-tag detection that warns about new migrations. Including it here since it was needed for THIS install to not break on next brew upgrade.
terrxo
added a commit
that referenced
this pull request
May 28, 2026
…lyco#258) (#19) Add a smoke-test step that runs BEFORE the freshly-built binary replaces the brew Cellar binary. Catches the class of regression where the build succeeds at the bundler layer but the produced binary crashes at module-load / plugin-resolution / config-read — the surface that silently shipped a U.length crash 2026-05-27 (PR #14-17 era) and went undetected for ~1h because already-running tabs kept their mmap'd old binary. Two layers, both cheap (~0.7s total on a healthy binary): 1. `--version` — catches module-load + top-level import crashes 2. `debug info` — catches plugin-resolution + config-load regressions (exercises the same module graph the TUI mount path does) If either layer fails — non-zero exit, OR exit 0 with TypeError / ReferenceError / SyntaxError / fatal-error / U.length / 'Cannot read prop' on stderr — the install aborts and the working brew binary stays put. The build output is preserved at $SRC_BIN for inspection. `--skip-smoke` flag provided for emergency overrides (rebuild loops on a known-broken state where the new binary is wanted despite a smoke fail). `timeout` wrapper detection: macOS doesn't ship coreutils by default, so `timeout`/`gtimeout` are detected at runtime; absent both, the smoke runs unwrapped (the commands themselves exit fast, so the wrapper is belt-and-suspenders, not load-bearing). Verified locally with three integration cases against the in-script smoke block: - Healthy gruntcode binary → proceeds to install - Crashing fake-bin → refused + exit 1, install aborted - Stealth crash (exit 0 + TypeError on stderr) → refused + exit 1 - `--skip-smoke` → smoke skipped, install proceeds Help text + doc-comment header updated; new `Smoke-test (pre-install verification)` section explains what the smoke does + how to disable. Closes anomalyco#258.
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.
Closes hivemind anomalyco#233 properly (replaces the v1 markdown-link approach that rendered ugly
[label](url)literals).Root cause of the ugliness
Opentui's markdown renderer renders inline links as the LITERAL bracket+label+paren+URL string. The relevant block in
node_modules/.bun/@opentui+core/index*.js:Even in conceal mode the URL leaks as
<label> (url). There's no inline-tokenrenderNodeoverride hook either. So markdown links can't give us the clean visual + hover + click experience Nik described.New path: bypass markdown for ticket refs
Split assistant text on the ticket-ref regex BEFORE markdown sees it. Render non-matching segments through markdown as before. Render each matched
#Nas a new<TicketRef>component.TicketRef(packages/opencode/src/cli/cmd/tui/component/ticket-ref.tsx):#229colored (theme.markdownLink) + underlined inline/api/tasks/<id>from hivemind-api (module-level cache + in-flight dedup), shows a floating tooltip with title + status + priority + zone + owner + scope previewhttp://localhost:5173/tasks/<id>via theopennpm package (same as existing<Link>component)Trade-off: inline markdown features spanning a
#Nboundary (e.g.**bold #229 text**) won't span the split. Acceptable for typical prose.Bundled: install-local.sh version stamping fix
Working tree contained an uncommitted update from a parallel agent during this session. Changes the default version label from
git describe --dirty(which made local builds look version-ahead of any released gruntcode → broke brew-binary boot when local migrations had advanced the DB) to a semver build-metadata format like1.15.10-grunt.7+local.<sha>. Includes ahead-of-tag detection that warns about new migrations.Included here because it was needed for THIS install to not break on next brew upgrade.
Verified
bun typecheckclean. Local install reports1.15.10-grunt.7+local.5573d8375.dirty.