Skip to content

feat(api): add LabelBadge type for two-part pill badges Introduce LabelBadge struct that renders a muted label followed by an emphasized value, supporting customizable colors, text colors, shapes, and optional icons. Implements Textable interface with String(), ANSI(), HTML(), and Markdown() methods. Add HTML React formatter support to convert LabelBadge to clicky-ui Badge component. Also add DetailLevel enum and Detailable interface to text.go for multi-section detail views with context-aware rendering.#99

Merged
moshloop merged 13 commits intomainfrom
pr/badges
Apr 27, 2026

Conversation

@moshloop
Copy link
Copy Markdown
Member

@moshloop moshloop commented Apr 27, 2026

Summary by CodeRabbit

  • New Features

    • Interactive terminal prompts (single/multi-select, text input, validation), links and command links, styled label badges, demo web UI with examples and E2E tests.
    • Task controls: cancel by ID and capture/stream output; format callbacks that receive request/context.
  • Improvements

    • Entity filtering with lookup metadata, shell completions, and richer OpenAPI metadata surface; HTTP HEAD/lookup support.
  • Tooling

    • Linter rule flags direct stdout/stderr writes.

moshloop added 10 commits April 23, 2026 09:31
…nd completions

Implement Filter interface and lookup functions to enable dynamic filter metadata exposure through the RPC executor. This allows UI components to display available and selected filter options with rich formatting (styles, tooltips) and supports dependent filter narrowing based on other filter selections.

Key additions:
- Filter[T] interface for defining filter behavior with Key(), Label(), Lookup(), and Options() methods
- buildLookupFunc() generates lookup metadata from filter definitions
- buildFilterCompletionBinder() registers flag completion functions for shell and UI
- resolveEntityOpts() applies filters to transform raw flag values into backend-canonical forms
- LookupFunc field on EntityOperation and BulkActionInfo for RPC exposure
- BindCompletions field for registering completion functions on generated commands
- New clicky_lookup.go with entityLookupResponse and clickyNode types for rich filter metadata
- RPC executor support for HEAD and GET __lookup=filters requests
- Comprehensive test coverage in entity_filters_test.go and rpc/filter_lookup_test.go
- End-to-end example in examples/entity/ demonstrating filters, admin entities, and executor-backed UI

Breaking changes: buildOpts() now returns (T, error) instead of T; callers must handle error return value.
…and text input

Introduce comprehensive prompt functionality for interactive terminal UIs:

- Vendor gumchoose from charmbracelet/gum for single and multi-select prompts
- Add PromptSelect, PromptMultiSelect, and PromptText APIs for interactive user input
- Implement terminal detection, PTY handling, and screen management
- Support customizable rendering, validation, and pagination
- Include extensive test coverage with PTY-based integration tests
- Handle terminal state restoration and cursor positioning

This enables rich interactive CLI experiences with proper terminal handling across macOS and Linux.
… ownership management

Implement exclusive ANSI terminal ownership control to allow prompts to safely take over the terminal from task renderers. Add new task-prompt demo command showcasing interactive select, multi-select, and text input prompts integrated with concurrent task execution.

Key changes:
- Add AcquirePromptTerminal() for prompt-to-task renderer handoff
- Implement ansiTerminalController to coordinate terminal access
- Add SetNoRender() to suppress all task output including final summaries
- Add StopTask(id) to cancel specific tasks by immutable UUID
- Add Task.ID() returning stable UUID independent of task name
- Update go.mod dependencies (Go 1.26.1, commons v1.50.3, gomplate v3.24.77)
- Add comprehensive tests for no-render and stop-task functionality

This enables building interactive CLI applications where prompts can safely interrupt task rendering and restore it afterward.
…/json+clicky

Change the primary content type for clicky JSON format from application/clicky+json to application/json+clicky to follow standard IANA media type conventions. The HTTP layer now accepts both the new standard format and the legacy alias for backward compatibility.

BREAKING CHANGE: The Content-Type header for clicky JSON responses is now application/json+clicky instead of application/clicky+json. Clients should update their expectations, though the legacy format is still accepted in Accept headers.
… navigation

Implement time range filtering with from/to parameters replacing the previous window-based approach. Add entity detail page with row action support and response body viewing. Update API to support both application/clicky+json and application/json+clicky content types. Add StopTask function to global task API for canceling specific tasks. Upgrade dependencies including Go 1.26.1 and charmbracelet UI libraries.

BREAKING CHANGE: stackWindowOpts now uses From and To time.Time fields instead of UpdatedSince and Window duration fields. Content-Type header for clicky-json format changed from application/clicky+json to application/json+clicky.

Refs: entity demo tests, example app routing, API response handling
…p-linking support

Introduce a new /links page demonstrating Link and LinkCommand usage patterns with various browser targets (dialog, hover, expand, _clicky, _self, _window, _tab). Add team entity with list/get operations. Refactor webapp from domain-based OperationCatalog to metadata-driven EntityExplorerApp. Implement deep-link command pages that accept query params as initialValues and support autoRun. Update stack and cluster entities to use new Columns/Row API with LinkCommand rendering. Add serveLinkExamples handler to serve Clicky document with link examples. Update vite config to resolve clicky-ui from local monorepo path.
… for OpenAPI

Introduce Link and LinkCommand types in the API package to support hyperlink rendering across multiple formats (HTML, Markdown, ANSI). Add comprehensive entity annotation system to capture semantic metadata about CLI commands, enabling the RPC/OpenAPI layer to expose entity operations without path guessing. This includes:

- New Link and LinkCommand types with fluent builder API supporting multiple output formats
- Entity annotation framework with CommandOpenAPIMeta for storing operation semantics
- OpenAPI x-clicky extension to expose entity surfaces and operation metadata
- Enhanced command generation to annotate all entity operations with verb, scope, and capability flags
- Support for lookup and filter mode capabilities in operations

The annotation system allows downstream tools to understand entity relationships and operation semantics from generated CLI commands.
Add a callback system that allows registering before/after hooks around formatting operations. This enables transforming input before formatting and output after rendering, with context forwarding for request-aware formatting.

Add Link and LinkCommand types with target options (Dialog, Hover, Expand, Clicky, Self, Window, Tab) to support interactive links and command execution in formatted output. Update HTML React formatter to convert these new types to clickyNode structures with proper serialization.

Add FormatWithContext variants throughout the formatting pipeline to pass caller context (e.g., *http.Request) to registered callbacks, enabling request-scoped data enrichment and transformation.

Breaking change: FormatWithOptions now delegates to FormatWithContext internally; custom FormatManager implementations should override FormatWithContext instead.
…an task rendering

Implement output capture and log serialization to prevent interleaving between task renderer frames and direct stdout/stderr writes.

Key changes:
- Add StartCapturingOutput/StopCapturingOutput to buffer bare fmt.Print writes while keeping the live renderer unaffected
- Install logSerializingWriter during render loop to serialize logger output against tick renders using shared mutex
- Add direct-stdout linter rule to flag fmt.Println/Printf and os.Stdout/Stderr writes that bypass the renderer
- Add Println/Printf/Fprintln convenience functions that route through the active logger output
- Widen ClearLines to account for log lines emitted between ticks, preventing stale frame lines from stacking
- Add SetForceInteractive/IsForceInteractive to enable interactive rendering in test environments when needed
- Guard all terminal writes with bufferMutex to prevent log serializer writes from splitting renderer output

This fixes the bug where direct writes to stdout/stderr would shift the cursor without updating lastLines, causing the next ClearLines to undercount and leave stale summary lines stacked above the live region.
Introduce LabelBadge struct that renders a muted label followed by an emphasized value, supporting customizable colors, text colors, shapes, and optional icons. Implements Textable interface with String(), ANSI(), HTML(), and Markdown() methods. Add HTML React formatter support to convert LabelBadge to clicky-ui Badge component. Also add DetailLevel enum and Detailable interface to text.go for multi-section detail views with context-aware rendering.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Warning

Rate limit exceeded

@moshloop has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 11 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c18cadb2-a3ff-465f-86f2-dffd082a2142

📥 Commits

Reviewing files that changed from the base of the PR and between c7af84e and ef6e5d8.

⛔ Files ignored due to path filters (3)
  • example/go.sum is excluded by !**/*.sum
  • examples/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (26)
  • .github/workflows/release.yml
  • .gitignore
  • api/labelbadge.go
  • e2e_integration_test.go
  • entity_filters_test.go
  • example/example-data-200.json
  • example/example-data.json
  • example/go.mod
  • example/main.go
  • example/order-schema.yaml
  • example/output.html
  • examples/go.mod
  • examples/order-schema.yaml
  • format_callbacks.go
  • go.mod
  • log.go
  • prompt_test.go
  • rpc/converter.go
  • rpc/openapi.go
  • task/force_interactive_test.go
  • task/manager.go
  • task/manager_lifecycle.go
  • task/render.go
  • task/render_test.go
  • task/ui/src/jsx.d.ts
  • text/tokenizer.go

Walkthrough

This pull request introduces a comprehensive interactive prompt system with filtering support for entities, adds rich text rendering for links and badges, implements context-aware formatting callbacks with HTTP integration, enhances task management with cancellation and output capture, adds direct stdout linting, and includes a complete entity demo application with E2E tests and a web UI.

Changes

Cohort / File(s) Summary
Rich Text Types
api/labelbadge.go, api/labelbadge_test.go, api/link.go, api/text.go, format.go
Added LabelBadge and Link/LinkCommand types with rendering methods (String, ANSI, HTML, Markdown); introduced DetailLevel enum and Detailable interface for context-aware detail views; exposed convenience constructors and LinkTarget aliases in public API.
Format/Render Callbacks
format_callback_test.go, format_callbacks.go, formatters/callbacks.go, formatters/manager.go, formatters/http/http.go, formatters/http/http_test.go
Implemented callback system allowing before/after formatting hooks with context propagation; extended FormatManager with context-aware FormatWithContext/FormatToFileWithContext methods; updated HTTP formatter to accept application/json+clicky media type and pass request context to formatting.
Clicky JSON Output
formatters/html_react_formatter.go, formatters/html_react_formatter_test.go, clicky_lookup.go
Extended Clicky JSON schema to include link metadata (Target, Command, Args, Flags, AutoRun) and badge fields; added converters for Link, LinkCommand, and LabelBadge types; implemented entity lookup response generation with filter metadata.
Entity Filters & Lookups
entity.go, entity_annotations.go, entity_filters_test.go, cobra_command.go, sub_command.go, sub_command_test.go
Added Filter abstraction and shell completion support; extended EntityInfo/EntityOperation/BulkActionInfo with LookupFunc and BindCompletions; implemented filter resolution and option building; added ValidArgs propagation to ID/admin subcommands; introduced OpenAPI command annotations for entity/operation metadata.
RPC/OpenAPI Lookup
rpc/converter.go, rpc/executor.go, rpc/filter_lookup_test.go, rpc/openapi.go, rpc/serve.go, rpc/types.go, rpc/serve_test.go
Added lookup operation routing (FindLookupOperation) with __lookup=filters query support; implemented handleLookupCommand for HEAD/GET requests; extended OpenAPI spec with x-clicky metadata including surfaces, entity info, and lookup capability flags; updated request handling to route through lookup functions when present.
Interactive Prompt System
prompt.go, prompt_test.go, prompt_terminal_ioctl_darwin_test.go, prompt_terminal_ioctl_linux_test.go, internal/gumchoose/choose.go, internal/gumchoose/choose_test.go, internal/gumchoose/LICENSE, testdata/prompt_test_helper.go
Implemented generic prompt functions (PromptChoose, PromptSelect, PromptMultiSelect, PromptText) with TTY management; created paginated list UI widget (gumchoose) supporting single/multi-select with ordering; added PTY-driven E2E tests with terminal state validation and escape sequence mocking.
Task Management Enhancements
task/manager.go, task/manager_lifecycle.go, task/manager_output.go, task/manager_wait.go, task/log_serializer.go, task/render.go, task/snapshot.go, task/task.go, task/terminal_owner.go, task/worker.go, task/options.go, task/group.go
Added NoRender and ForceInteractive flags to suppress/force rendering; implemented output capture with StartCapturingOutput/StopCapturingOutput; added StopTask(id) for task cancellation; introduced log serialization writer for concurrent safety; added global terminal ownership controller (AcquirePromptTerminal) coordinating task renderer and prompt access; added UUID-based task IDs.
Task Management Tests
task/force_interactive_test.go, task/log_serializer_test.go, task/manager_output_test.go, task/no_render_test.go, task/stop_test.go
Added comprehensive tests for force-interactive mode, output capture, task cancellation by ID, and render suppression; validated terminal flag preservation and concurrent log safety.
Linting for Direct Stdout
lint/analyzer.go, lint/analyzer_test.go, lint/direct_stdout.go, lint/testdata/src/directstdout/...
Implemented analyzer rule flagging direct os.Stdout/os.Stderr writes and fmt.Print* calls to those streams; added suppression for main packages, test files, and //clicky:allow-stdout directive; included test fixtures demonstrating allowed/disallowed patterns.
Clicky Public Logging API
log.go, log_test.go, global_task_api.go
Exposed Println, Printf, Fprintln functions routing to logger output; added StopTask, StartCapturingOutput, StopCapturingOutput to global task API; ensured output interleaves correctly with task renderer.
Parser/Common Types
api/parser.go
Updated StructToRow to honor json:",omitempty" tags, omitting empty fields from PrettyDataRow output.
Entity Demo Application
examples/enitity/main.go, examples/enitity/main_test.go, examples/enitity/webapp/src/App.tsx, examples/enitity/webapp/src/LinkExamplesPage.tsx, examples/enitity/webapp/src/api.ts, examples/enitity/webapp/src/index.css, examples/enitity/webapp/src/main.tsx, examples/enitity/webapp/tsconfig.tsbuildinfo, examples/enitity/webapp/vite.config.ts, examples/enitity/webapp/.gitignore
Created full CRUD entity demo (stack, cluster, team) with filtering, admin views, bulk actions, and restart mutation; implemented embedded Vite React SPA serving Clicky RPC/OpenAPI endpoints; added /api/examples/links endpoint for link documentation; configured Tailwind/TanStack Query integration.
Entity Demo E2E Tests
examples/enitity/e2e/playwright.config.ts, examples/enitity/e2e/tests/entity-demo.spec.ts, examples/enitity/e2e/.gitignore
Added Playwright E2E test suite validating stacks/clusters/admin views, filter application, command execution in API explorer, deep-link navigation, and state persistence; configured test server launch and PTY handling.
Uber Demo Enhancements
examples/uber_demo/main.go
Added task-prompt CLI command demonstrating interactive prompts for action selection, multi-select checks with limits, and text input validation.
Module Dependencies
go.mod, examples/go.mod, examples/uber_demo/go.mod, examples/README.md
Upgraded toolchain to Go 1.26.1; added Charmbracelet TUI libraries (bubbles, bubbletea, huh); bumped commons and gomplate versions; expanded indirect dependencies for XML, clipboard, and UI utilities; documented new example directory scope.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request has no description provided, whereas the template requires sections including Type of Change, Testing, Checklist, and other important information. Add a comprehensive PR description following the provided template, covering type of change, testing status, and completion of the provided checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main changes: introducing a LabelBadge type for two-part pill badges with Textable interface implementations, plus DetailLevel/Detailable additions for context-aware detail views.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr/badges
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch pr/badges

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 27, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedgolang/​github.com/​flanksource/​commons@​v1.42.3 ⏵ v1.50.371 -13100100100100
Updatedgolang/​github.com/​flanksource/​commons@​v1.42.3 ⏵ v1.51.071 -13100100100100
Updatedgolang/​github.com/​flanksource/​gomplate/​v3@​v3.24.60 ⏵ v3.24.7794 -2100100100100

View full report

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lint/analyzer_test.go (1)

25-53: ⚠️ Potential issue | 🟠 Major

Assertions don't validate the behavior the test names claim.

analysistest.Run always returns one *Result per analyzed package, regardless of whether any diagnostics were produced. So Expect(results).NotTo(BeEmpty()) is essentially a tautology — it only proves the analyzer ran. Concretely:

  • The directstdout/good, directstdout/allowed, and directstdout/mainpkg specs would pass even if the analyzer wrongly flagged every line in those fixtures.
  • The directstdout/bad spec would pass even if the analyzer produced zero diagnostics on disallowed code — i.e., if the rule is silently broken.

This same weakness already affects the pre-existing bad/good api.Text specs (lines 19–29), so this PR is propagating the pattern rather than introducing it, but the new fixtures make it more pronounced.

The idiomatic approach with analysistest is to embed // want "..." comments in the testdata for expected diagnostics and let analysistest.Run itself report mismatches (it already calls t.Errorf internally). For "allowed" cases, assert each Result.Diagnostics slice is empty:

♻️ Suggested assertion shape
-	It("flags direct stdout/stderr writes", func() {
-		testdata := analysistest.TestData()
-		results := analysistest.Run(GinkgoT(), testdata, lint.Analyzer, "directstdout/bad")
-		Expect(results).NotTo(BeEmpty())
-	})
+	It("flags direct stdout/stderr writes", func() {
+		testdata := analysistest.TestData()
+		// `// want "..."` markers in directstdout/bad/bad.go drive validation;
+		// also assert at least one diagnostic was produced.
+		results := analysistest.Run(GinkgoT(), testdata, lint.Analyzer, "directstdout/bad")
+		var total int
+		for _, r := range results {
+			total += len(r.Diagnostics)
+		}
+		Expect(total).To(BeNumerically(">", 0), "expected directstdout/bad to produce diagnostics")
+	})
+
+	It("allows legitimate os.Stdin / inspection / buffer writes", func() {
+		testdata := analysistest.TestData()
+		results := analysistest.Run(GinkgoT(), testdata, lint.Analyzer, "directstdout/good")
+		for _, r := range results {
+			Expect(r.Diagnostics).To(BeEmpty(), "directstdout/good should not be flagged")
+		}
+	})

Apply the same BeEmpty() shape to directstdout/allowed and directstdout/mainpkg.

Note: analysistest.Run invokes the test object's Errorf on // want mismatches. GinkgoT() adapts to that, so embedding // want lines in the bad fixtures is the most precise approach.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lint/analyzer_test.go` around lines 25 - 53, The tests currently assert only
that analysistest.Run(GinkgoT(), testdata, lint.Analyzer, ...) returned a
non-empty results slice, which merely proves the analyzer ran; instead, rely on
analysistest's embedded "// want" comments for bad fixtures and explicitly
assert that Result.Diagnostics are empty for allowed fixtures. Update the specs
that call analysistest.Run (using GinkgoT() and receiving results) so that "bad"
cases rely on // want comments in the testdata (remove
Expect(results).NotTo(BeEmpty()) for those), and for the allowed/skip cases
(e.g., directstdout/good, directstdout/allowed, directstdout/mainpkg and the
api.Text good/allowed cases) assert each returned results[i].Diagnostics slice
is empty (iterate results and check Result.Diagnostics for emptiness) rather
than asserting only that results is non-empty.
🟡 Minor comments (18)
task/manager_output_test.go-44-45 (1)

44-45: ⚠️ Potential issue | 🟡 Minor

Time-based wait may be flaky; no public flush mechanism available.

The 20ms time.Sleep creates a race condition. Under heavy CI load or with -race, the reader goroutines spawned by StartCapturingOutput() may not finish reading and buffering all three written lines into the manager's output buffer before StopCapturingOutput() is called. This can cause intermittent test failures where "charlie" or other lines are missing.

The implementation has an internal Flush() method on the buffering writer, but it is not exposed via the public API. To robustly wait for capture to complete, consider:

  1. Extending the public API with a sync/flush method (e.g., FlushCapturedOutput()) that waits for reader goroutines to drain.
  2. Alternatively, refactor the test to poll the captured output buffer (if made accessible) with a deadline, instead of a fixed sleep.

For now, if keeping the sleep approach, document the risk and consider increasing the timeout or adding comments explaining the race condition.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@task/manager_output_test.go` around lines 44 - 45, The time.Sleep(20 *
time.Millisecond) is flaky because reader goroutines started by
StartCapturingOutput() may not finish before StopCapturingOutput(); remove the
fixed sleep and either (A) add a public FlushCapturedOutput() method that calls
the buffering writer's internal Flush() and blocks until readers drain, then
call FlushCapturedOutput() in the test between writes and StopCapturingOutput(),
or (B) change the test to poll the captured output (via an accessor on the
manager or by exposing the buffer) with a deadline until all expected lines
(alice, bob, charlie) appear before calling StopCapturingOutput(); update
references to StartCapturingOutput(), StopCapturingOutput(), and the buffering
writer's Flush() in the implementation/test accordingly.
examples/README.md-8-8 (1)

8-8: ⚠️ Potential issue | 🟡 Minor

Likely typo: enitityentity.

The directory name enitity/ (referenced in the README, examples/enitity/webapp/..., e2e specs, etc.) appears to be a misspelling of entity. Since this propagates into import paths, server URLs, and documentation, it's worth fixing once now (rename the directory and update references) rather than after consumers start linking to it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/README.md` at line 8, Rename the misspelled examples/enitity
directory to examples/entity and update all references: change README.md line
entries referencing "enitity/" and any paths like examples/enitity/webapp/... to
examples/entity/..., update import paths in source files, e2e spec file
references and any server URLs or documentation strings that include "enitity",
and run/verify tests and example scripts to ensure no broken imports or paths
remain (search for the token "enitity" and replace with "entity" across the
repo).
examples/enitity/webapp/tsconfig.tsbuildinfo-1-1 (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Don't commit tsconfig.tsbuildinfo — it's a generated build artifact.

*.tsbuildinfo is the TS incremental build cache. Committing it causes noisy diffs on every build, can leak absolute paths from contributors' machines, and serves no purpose for other developers (TypeScript regenerates it locally). Please remove the file from the PR and add an ignore entry.

🛠 Suggested `.gitignore` addition
+# TypeScript incremental build cache
+*.tsbuildinfo

Also run:

git rm --cached examples/enitity/webapp/tsconfig.tsbuildinfo
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/enitity/webapp/tsconfig.tsbuildinfo` at line 1, Remove the generated
TypeScript build artifact examples/enitity/webapp/tsconfig.tsbuildinfo from the
commit, add a gitignore entry for *.tsbuildinfo (or tsconfig.tsbuildinfo) so it
isn’t tracked, and unstage/remove the cached file from the repo (e.g., run git
rm --cached for the file) before committing; ensure the updated .gitignore is
committed so future builds won’t add tsbuildinfo files.
clicky_lookup.go-51-61 (1)

51-61: ⚠️ Potential issue | 🟡 Minor

Add explicit cases for Link/LinkCommand/LabelBadge in toClickyNode to match html_react_formatter.go.

All filter Options() implementations currently return only api.Text, but the new api.LabelBadge type introduced in this PR (and existing api.Link/api.LinkCommand) lack explicit handling in toClickyNode. Without them, any future filter that includes badges/links will silently degrade to plain text nodes, losing Href, Target, command args, and badge styling. Add cases mirroring html_react_formatter.go's convertLink, convertLinkCommand, and convertLabelBadge to preserve metadata in lookup responses.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clicky_lookup.go` around lines 51 - 61, The toClickyNode function currently
falls through to a plain text node for unknown api types, causing api.Link,
api.LinkCommand, and api.LabelBadge to lose metadata; add explicit cases in
toClickyNode to handle api.Link (preserve Href, Target, Text), api.LinkCommand
(preserve command name/args and display text), and api.LabelBadge (preserve
badge text and style) following the conversions implemented in
html_react_formatter.go (see convertLink, convertLinkCommand, convertLabelBadge)
so lookup responses retain link hrefs/targets, command args, and badge styling
instead of degrading to plain text.
examples/enitity/webapp/src/index.css-2-3 (1)

2-3: ⚠️ Potential issue | 🟡 Minor

Fragile cross-repo path assumption affects both CSS and JS imports.

The relative paths ../../../../../clicky-ui/... in index.css (and similarly in main.tsx) assume clicky-ui is checked out as a sibling directory to clicky. This is confirmed by the vite.config.ts aliases pointing to the same sibling location, but the CSS imports bypass the package manager entirely, making the configuration brittle for CI builds and non-standard directory layouts.

The vite.config.ts already establishes @flanksource/clicky-ui aliases; consider using those in CSS via Tailwind configuration, or migrate both CSS and JS imports to reference a workspace dependency or published package so the paths are stable and environment-agnostic.

The stylelint warning on @source is a false positive — it's a valid Tailwind v4 directive.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/enitity/webapp/src/index.css` around lines 2 - 3, The CSS uses
brittle repo-relative imports in index.css (`@import
"../../../../../clicky-ui/...";` and `@source ...`) that bypass package
resolution; replace those with the package alias already defined in
vite.config.ts (use `@flanksource/clicky-ui` in both CSS and JS) or import the
published/workspace package entry instead, and update main.tsx to import styles
from the package rather than a relative path; keep the Tailwind v4 `@source`
directive as-is but ensure any CSS tooling (postcss/tailwind config) can resolve
the `@flanksource/clicky-ui` alias so CI and alternate repo layouts work
reliably.
lint/direct_stdout.go-28-33 (1)

28-33: ⚠️ Potential issue | 🟡 Minor

Doc comment overpromises vs. implementation.

Two scope claims here are not actually implemented:

  1. "clicky's own module (package path prefix github.com/flanksource/clicky)"skipDirectStdoutFile never inspects pass.Pkg.Path(), so this skip does not happen. Either add the check or drop the bullet.
  2. "on the enclosing file, function, or statement"hasAllowStdoutDirective only walks file.Comments at file scope (and lines 167-169 acknowledge this). The function/statement granularity advertised here misleads readers writing the directive next to a single statement.

Reconcile the doc with the implementation (or extend the implementation to match), so users don't expect inline opt-outs that silently don't work.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lint/direct_stdout.go` around lines 28 - 33, The docstring claims two scopes
that the code doesn't implement: update skipDirectStdoutFile to actually check
the package path by reading pass.Pkg.Path() and skip files whose package path
has the prefix "github.com/flanksource/clicky", or remove that bullet from the
comment; and either extend hasAllowStdoutDirective to search for comment groups
attached to function and statement AST nodes (not just file.Comments) so inline
`//clicky:allow-stdout` next to a function or statement is honored, or change
the doc to state the directive only works at file scope (update the comment near
hasAllowStdoutDirective and lines acknowledging file-scope behavior). Ensure you
reference the functions skipDirectStdoutFile and hasAllowStdoutDirective and the
use of pass.Pkg.Path() and file.Comments when making the change.
examples/enitity/webapp/src/api.ts-11-25 (1)

11-25: ⚠️ Potential issue | 🟡 Minor

delete remaining.args quietly drops a flag named args.

Inside the substitution loop, delete remaining.args fires every time any path segment is replaced — not just when the substituted key is args. This is presumably a workaround for the cobra "args" fallback positional name introduced by rpc/converter.go (see the extractParameterName(...) == "" branch in that file), so that when a real positional name like policyNumber is substituted into the path, the redundant args query param isn't also sent.

But:

  • It silently drops a user-defined flag legitimately named args.
  • It only fires when some path segment was substituted, and only for the first iteration that happens to match — readers can't see the cobra-coupling rationale from this file.

At minimum, a comment explaining the coupling would help; better, scope the deletion to the case where the substituted key was the positional name (i.e., the converter's "single positional → path param" path), or move this normalization server-side.

♻️ Suggested clarification
   for (const [key, value] of Object.entries(params)) {
     if (resolved.includes(`{${key}}`)) {
       resolved = resolved.replace(`{${key}}`, encodeURIComponent(value));
       delete remaining[key];
-      delete remaining.args;
+      // The Go converter falls back to the synthetic "args" positional
+      // when no real name is extractable from cobra `Use`. Once a real
+      // positional has been substituted into the path, "args" is the
+      // redundant fallback and must not also be sent as a query param.
+      delete remaining.args;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/enitity/webapp/src/api.ts` around lines 11 - 25, The loop in
substitutePath currently unconditionally deletes remaining.args whenever any
path param is substituted, which accidentally drops a legitimate user flag named
"args"; change the deletion so it only removes remaining.args when the
substituted key is literally "args" (i.e., inside the if
(resolved.includes(`{${key}}`)) branch, replace the unconditional delete with a
conditional delete when key === "args"), and add a short comment referencing the
cobra/rpc converter fallback (the extractor that uses "args" as a positional
fallback) so future readers understand the coupling.
lint/direct_stdout.go-170-179 (1)

170-179: ⚠️ Potential issue | 🟡 Minor

Tighten directive matching beyond strings.Contains.

strings.Contains(c.Text, allowStdoutDirective) will silence the rule for any comment in the file that mentions the directive substring — including prose such as // don't reach for //clicky:allow-stdout here or doc strings discussing the directive (e.g., this very file once it's merged into a docs blob). Go directive conventions are usually //directive with no leading space and the directive token at column 0 of the comment text.

♻️ Suggested tightening
 func hasAllowStdoutDirective(file *ast.File) bool {
 	for _, cg := range file.Comments {
 		for _, c := range cg.List {
-			if strings.Contains(c.Text, allowStdoutDirective) {
+			// Match `//clicky:allow-stdout` as a directive, not a substring
+			// of arbitrary prose. Mirrors how `//go:` directives are parsed.
+			text := strings.TrimPrefix(c.Text, "//")
+			text = strings.TrimSpace(text)
+			if text == allowStdoutDirective ||
+				strings.HasPrefix(text, allowStdoutDirective+" ") {
 				return true
 			}
 		}
 	}
 	return false
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lint/direct_stdout.go` around lines 170 - 179, Replace the loose substring
check in hasAllowStdoutDirective: instead of strings.Contains(c.Text,
allowStdoutDirective) require the directive to appear as a top-level comment
token. For line comments check strings.HasPrefix(c.Text,
"//"+allowStdoutDirective) and for block comments split c.Text into lines and
check each trimmed line for a prefix of "//"+allowStdoutDirective (and treat
directive end-of-token by ensuring the next rune is whitespace or EOF); use the
allowStdoutDirective constant and the hasAllowStdoutDirective function to locate
where to change the logic.
task/force_interactive_test.go-41-55 (1)

41-55: ⚠️ Potential issue | 🟡 Minor

Test does not actually verify the env-var → force-interactive path.

This test sets CLICKY_FORCE_INTERACTIVE=1, checks via os.Getenv that it is set, then manually calls SetForceInteractive(true). The success of IsForceInteractive() at line 52 is therefore due to the explicit SetForceInteractive(true) call, not the env var. Since init() runs before any test, you cannot retroactively re-trigger it from inside a test.

To actually exercise the env-var path, factor the env-var read out of init() into a helper (e.g. applyForceInteractiveFromEnv()) that the test can invoke after t.Setenv. As written, this test is a no-op tautology.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@task/force_interactive_test.go` around lines 41 - 55, The test currently sets
CLICKY_FORCE_INTERACTIVE and then calls SetForceInteractive(true) directly, so
it never exercises the env-var reading done in init(); extract the env-var logic
from init() into a new helper (e.g. applyForceInteractiveFromEnv()) that reads
CLICKY_FORCE_INTERACTIVE and calls SetForceInteractive accordingly, update
init() to call that helper, and change TestForceInteractiveEnvVarSetsFlag to
call t.Setenv("CLICKY_FORCE_INTERACTIVE","1") followed by
applyForceInteractiveFromEnv() and then assert IsForceInteractive(); reference
IsForceInteractive, SetForceInteractive and init() when making the change.
rpc/filter_lookup_test.go-79-104 (1)

79-104: ⚠️ Potential issue | 🟡 Minor

Global entity registration should use unique test-specific names to avoid interference.

clicky.RegisterEntity mutates package-global state. While rpc/entities_test.go correctly mitigates this by registering a uniquely-named entity ("rpc-entities-test"), this test uses the generic name "rpc-filter-entity" which could collide if the test runs in parallel or if other tests in the same package register with the same name. Adopt the same mitigation strategy: use a unique identifier in the entity name, such as "test-filter-lookup-rpc-filter-entity" or similar.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rpc/filter_lookup_test.go` around lines 79 - 104,
TestSwaggerServer_FilterLookupRoutes registers a package-global entity via
clicky.RegisterEntity using the non-unique Name "rpc-filter-entity", which can
collide with other tests; update the Name in the registration inside
TestSwaggerServer_FilterLookupRoutes to a test-scoped unique string (e.g. prefix
with "test-filter-lookup-") so the clicky.RegisterEntity call registers a
distinct entity, leaving the Filters, List, and BulkActions (and the function
names/closures) unchanged.
examples/enitity/main.go-909-913 (1)

909-913: ⚠️ Potential issue | 🟡 Minor

Unknown /api/* paths fall through to the SPA handler.

mux.Handle("/", uiHandler) is the catch-all, so any request that does not match a more specific registered API pattern (e.g. a typo in /api/v1/..., or an executor route that fails to register) will be served index.html with HTTP 200 instead of returning a proper 404. Clients (and tests) will then have to disambiguate API errors from SPA HTML by content-sniffing. Consider scoping the SPA handler under a non-/api prefix or short-circuiting requests whose path starts with /api/ before falling back to serveIndex.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/enitity/main.go` around lines 909 - 913, The SPA catch-all
mux.Handle("/", uiHandler) lets unknown /api/* paths fall through to index.html;
update routing so API paths are not swallowed by the SPA: either register the
SPA under a non-/api prefix (e.g. mount uiHandler at "/" but only after
explicitly registering all API routes) or add a short-circuit before uiHandler
to detect req.URL.Path starting with "/api/" and return a 404 (or pass to
mux.NotFound) instead; modify the main routing logic where newWebappHandler,
uiHandler, and mux are used to implement this check so unknown API endpoints no
longer get served the SPA.
prompt_test.go-213-229 (1)

213-229: ⚠️ Potential issue | 🟡 Minor

Time-based sync is likely to flake in CI.

Several tests use fixed time.Sleep to wait for the helper to reach the prompt before sending keystrokes (e.g. time.Sleep(500*time.Millisecond) here, time.Sleep(800*time.Millisecond) in TestPromptStopsTaskRenderingBeforePrompt). On a loaded CI runner the helper may not have rendered the prompt yet, causing the arrow key / Enter to be consumed before the bubbletea program is reading input — the test then fails or hangs.

TestPromptTextValidation already demonstrates the right pattern via waitForCapturedText. Switching the other PTY tests to wait for a prompt-specific marker (e.g. the prompt title or > cue) before writing keystrokes will make them deterministic.

Also applies to: 309-320

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prompt_test.go` around lines 213 - 229, Replace the fragile time.Sleep waits
in the PTY test that uses startPromptHelper("choose_confirm") with deterministic
waiting: use the existing waitForCapturedText helper to block until the
prompt-specific marker (e.g. the prompt title or the prompt cue like ">" or the
option text that appears before you send keystrokes) appears in
capture.String(), then send the arrow key and Enter. Update the test that
currently writes to ptmx after time.Sleep(500*time.Millisecond) (and the similar
case at lines 309-320) to call waitForCapturedText(capture, "expected prompt
marker") before ptmx.Write so the keystrokes are delivered only after the
bubbletea prompt is rendered; keep the rest of the assertions
(requireExitWithin, getTerminalFlags, assertFlagsMatch, and checking
clickytext.StripANSI output) unchanged.
prompt_test.go-415-435 (1)

415-435: ⚠️ Potential issue | 🟡 Minor

requireExitWithin silently passes on unexpected Wait errors.

If cmd.Wait() returns a non-*exec.ExitError (e.g. an I/O error while reading the child’s exit), the type assertion at line 428 fails and the function falls through to the end without calling t.Fatalf, so the test reports success despite the helper crashing. Recommend tightening:

♻️ Proposed change
 	case err := <-done:
 		if err == nil {
 			return
 		}
-		if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() != 0 {
-			t.Fatalf("helper exited with error: %v", err)
-		}
+		t.Fatalf("helper exited with error: %v", err)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prompt_test.go` around lines 415 - 435, The helper function requireExitWithin
silently ignores non-*exec.ExitError failures from cmd.Wait(); update
requireExitWithin to treat any non-nil error from cmd.Wait() as a test failure
by calling t.Fatalf with the error (and context) when err != nil, rather than
only handling *exec.ExitError; reference cmd.Wait(), the done channel handling,
and the exitErr type assertion so you replace the current fall-through behavior
with a generic failure path that logs unexpected error types.
rpc/serve.go-622-631 (1)

622-631: ⚠️ Potential issue | 🟡 Minor

Headers already flushed before encode error path runs.

w.Header().Set(...) + w.WriteHeader(http.StatusOK) commits the response, so the subsequent http.Error(...) on encode failure cannot change the status code or content-type — it will just append a plaintext error line after a partial JSON body, producing a malformed response that still claims 200 application/json+clicky. Either encode into a buffer first, or drop the http.Error and just log the error.

♻️ Proposed change
-	w.Header().Set("Content-Type", "application/json+clicky")
-	w.WriteHeader(http.StatusOK)
-
-	if strings.EqualFold(r.Method, http.MethodHead) {
-		return
-	}
-
-	if err := json.NewEncoder(w).Encode(data); err != nil {
-		http.Error(w, fmt.Sprintf("failed to encode lookup response: %v", err), http.StatusInternalServerError)
-	}
+	body, err := json.Marshal(data)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("failed to encode lookup response: %v", err), http.StatusInternalServerError)
+		return
+	}
+	w.Header().Set("Content-Type", "application/json+clicky")
+	w.WriteHeader(http.StatusOK)
+	if strings.EqualFold(r.Method, http.MethodHead) {
+		return
+	}
+	_, _ = w.Write(body)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rpc/serve.go` around lines 622 - 631, The response is committed before json
encoding, so on encode failure the handler can’t change status or headers; fix
by encoding into a buffer (e.g., use json.Marshal or encode into a bytes.Buffer)
and only call w.Header().Set(...) and w.WriteHeader(http.StatusOK) and write the
buffer to w after a successful encode, keeping the HEAD check in place; remove
or replace the http.Error(w, ...) call with logging if encode fails so you don't
attempt to mutate a committed response.
examples/enitity/webapp/src/LinkExamplesPage.tsx-224-238 (1)

224-238: ⚠️ Potential issue | 🟡 Minor

Comma-joined args can corrupt positional values that contain ,.

commandInitialValues joins remaining positional args with "," into values.args (line 228), which is then placed verbatim into a single query parameter. If any positional argument contains a comma, the receiver (LinkExampleCommandPage's initialValues) cannot recover the original tokens. Consider repeating the parameter (URLSearchParams.append) or using a delimiter that cannot appear in arg values, and document the contract on both sides.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/enitity/webapp/src/LinkExamplesPage.tsx` around lines 224 - 238, The
current assembly of remaining positional args into a comma-joined string
(values.args) corrupts tokens that contain commas; change the serialization so
the receiver can reconstruct the original tokens—either (preferred) emit
repeated query params by appending each remaining arg under the same key (use
URLSearchParams.append for key "args" when building the link) or else serialize
the array safely (e.g., JSON.stringify + encodeURIComponent) and update
LinkExampleCommandPage's initialValues to decode/JSON.parse; locate and update
the code that builds values.args (the loop over positionalParams and the
assignment to values.args) and update LinkExampleCommandPage.initialValues
decoding to match the chosen format.
rpc/openapi.go-450-452 (1)

450-452: ⚠️ Potential issue | 🟡 Minor

Admin description reads awkwardly.

When Admin == true, entry.surface.Title is "Admin — Foo"; the fallback then produces "Manage admin — foo resources.". Build the fallback from entry.baseKey (or the un-prefixed title) and prepend the admin qualifier separately, e.g. "Manage Foo resources (admin).".

🛠️ Suggested fix
-		if entry.surface.Description == "" {
-			entry.surface.Description = fmt.Sprintf("Manage %s resources.", strings.ToLower(entry.surface.Title))
-		}
+		if entry.surface.Description == "" {
+			noun := strings.ToLower(clickyTitleCase(entry.baseKey))
+			if entry.surface.Admin {
+				entry.surface.Description = fmt.Sprintf("Manage %s resources (admin).", noun)
+			} else {
+				entry.surface.Description = fmt.Sprintf("Manage %s resources.", noun)
+			}
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rpc/openapi.go` around lines 450 - 452, The fallback description generation
should use the un-prefixed resource name (e.g. entry.baseKey or strip the "Admin
— " prefix from entry.surface.Title) instead of entry.surface.Title so you don't
get "admin — foo" inside the sentence; update the block that sets
entry.surface.Description to build the base name from entry.baseKey (or remove
the admin prefix) and then output something like "Manage Foo resources (admin)."
when the entry denotes an admin surface (check the Admin flag or detect the
"Admin" prefix on entry.surface.Title) so the admin qualifier is appended
separately.
internal/gumchoose/choose.go-370-390 (1)

370-390: ⚠️ Potential issue | 🟡 Minor

Trailing newline is always emitted; i != m.height condition is never false.

In the loop at line 370, i iterates over m.items[start:end] where the slice size is determined by paginator.GetSliceBounds() with PerPage = m.height (line 179). Thus i ranges from 0 to height-1, and the condition if i != m.height at line 387 is always true. This means a newline is written after every item, including the visually last one on the page. The intended behavior is likely to omit the trailing newline after the final item on the page.

Suggested fix
-		if i != m.height {
-			s.WriteRune('\n')
-		}
+		if i < (end-start)-1 {
+			s.WriteRune('\n')
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/gumchoose/choose.go` around lines 370 - 390, The loop always emits a
trailing newline because the condition checks i != m.height while i is an index
into the paged slice; change the newline check to compare against the last index
of the current page slice instead (e.g., compute the slice := m.items[start:end]
or use the length of m.items[start:end] and only write '\n' when i !=
len(slice)-1). Update the loop around m.items[start:end], m.index, and m.height
so the newline is omitted after the final item on the current page.
prompt.go-454-475 (1)

454-475: ⚠️ Potential issue | 🟡 Minor

Strip ANSI escapes in promptRenderedLineCount to avoid miscalculating line height.

The function measures len([]rune(line)) directly, so ANSI SGR sequences in the title inflate the visible width and overestimate titleHeight, which then shrinks the page size via clampPromptPageSize on narrow terminals.

The text.StripANSI helper is available and already used throughout the codebase. Use it to strip sequences before measuring: text.StripANSI(line).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prompt.go` around lines 454 - 475, In promptRenderedLineCount, strip ANSI
sequences before measuring visible width to avoid inflated lengths: for each
line (in the loop over strings.Split(text, "\n")), call text.StripANSI(line) (or
assign stripped := text.StripANSI(line)) and base the empty check and the rune
length calculation on the stripped string (use len([]rune(stripped)) when
computing (lineLength-1)/width + 1); keep the rest of the logic unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b2d9f0d-de2e-4aaf-965e-9dd67174f743

📥 Commits

Reviewing files that changed from the base of the PR and between b073cb4 and c7af84e.

⛔ Files ignored due to path filters (3)
  • examples/go.sum is excluded by !**/*.sum
  • examples/uber_demo/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (81)
  • api/labelbadge.go
  • api/labelbadge_test.go
  • api/link.go
  • api/parser.go
  • api/text.go
  • clicky_lookup.go
  • cobra_command.go
  • entity.go
  • entity_annotations.go
  • entity_filters_test.go
  • examples/README.md
  • examples/enitity/e2e/.gitignore
  • examples/enitity/e2e/playwright.config.ts
  • examples/enitity/e2e/tests/entity-demo.spec.ts
  • examples/enitity/main.go
  • examples/enitity/main_test.go
  • examples/enitity/webapp/.gitignore
  • examples/enitity/webapp/src/App.tsx
  • examples/enitity/webapp/src/LinkExamplesPage.tsx
  • examples/enitity/webapp/src/api.ts
  • examples/enitity/webapp/src/index.css
  • examples/enitity/webapp/src/main.tsx
  • examples/enitity/webapp/tsconfig.tsbuildinfo
  • examples/enitity/webapp/vite.config.ts
  • examples/go.mod
  • examples/uber_demo/go.mod
  • examples/uber_demo/main.go
  • format.go
  • format_callback_test.go
  • format_callbacks.go
  • formatters/callbacks.go
  • formatters/html_react_formatter.go
  • formatters/html_react_formatter_test.go
  • formatters/http/http.go
  • formatters/http/http_test.go
  • formatters/manager.go
  • global_task_api.go
  • go.mod
  • internal/gumchoose/LICENSE
  • internal/gumchoose/choose.go
  • internal/gumchoose/choose_test.go
  • lint/analyzer.go
  • lint/analyzer_test.go
  • lint/direct_stdout.go
  • lint/testdata/src/directstdout/allowed/allowed.go
  • lint/testdata/src/directstdout/bad/bad.go
  • lint/testdata/src/directstdout/good/good.go
  • lint/testdata/src/directstdout/mainpkg/main.go
  • log.go
  • log_test.go
  • prompt.go
  • prompt_terminal_ioctl_darwin_test.go
  • prompt_terminal_ioctl_linux_test.go
  • prompt_test.go
  • rpc/converter.go
  • rpc/executor.go
  • rpc/filter_lookup_test.go
  • rpc/openapi.go
  • rpc/serve.go
  • rpc/serve_test.go
  • rpc/types.go
  • sub_command.go
  • sub_command_test.go
  • task/force_interactive_test.go
  • task/group.go
  • task/log_serializer.go
  • task/log_serializer_test.go
  • task/manager.go
  • task/manager_lifecycle.go
  • task/manager_output.go
  • task/manager_output_test.go
  • task/manager_wait.go
  • task/no_render_test.go
  • task/options.go
  • task/render.go
  • task/snapshot.go
  • task/stop_test.go
  • task/task.go
  • task/terminal_owner.go
  • task/worker.go
  • testdata/prompt_test_helper.go

Comment thread api/labelbadge.go
// Register the <iconify-icon> web component so clicky-ui's <Icon> glyphs
// render (it loads icons on-demand from the Iconify CDN at runtime).
import "iconify-icon";
import "../../../../../clicky-ui/packages/ui/src/styles/tokens.css";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether `@flanksource/clicky-ui` exports tokens.css (or a styles entry).
fd -t f 'package.json' -E 'node_modules' | xargs rg -l '"@flanksource/clicky-ui"' 2>/dev/null
echo '---'
# Check if a local copy of tokens.css exists in the example we could point at instead.
fd -t f 'tokens.css'

Repository: flanksource/clicky

Length of output: 66


🏁 Script executed:

# Check the package.json in the examples/enitity/webapp directory
cat "examples/enitity/webapp/package.json"

Repository: flanksource/clicky

Length of output: 131


🏁 Script executed:

# Check the directory structure at the repo root to see what exists
ls -la | head -20

Repository: flanksource/clicky

Length of output: 1238


🏁 Script executed:

# Look for importmap or vite/build config
fd -t f -E 'node_modules' 'vite.config|importmap|index.html'

Repository: flanksource/clicky

Length of output: 124


🏁 Script executed:

# Check if there's any reference to tokens.css in the entire repo (not just package.json)
rg 'tokens\.css' --type-list | head -5 && rg 'tokens\.css' -E 'node_modules|dist|build'

Repository: flanksource/clicky

Length of output: 513


🏁 Script executed:

# List the examples directory structure
find examples -type d | head -20

Repository: flanksource/clicky

Length of output: 327


🏁 Script executed:

# Read the vite.config.ts file to see how the build is configured
cat "examples/enitity/webapp/vite.config.ts"

Repository: flanksource/clicky

Length of output: 1450


🏁 Script executed:

# Search for tokens.css properly
rg 'tokens\.css' -E 'node_modules|dist|build' --invert-match

Repository: flanksource/clicky

Length of output: 123


🏁 Script executed:

# Read the actual main.tsx to confirm the import line
cat "examples/enitity/webapp/src/main.tsx"

Repository: flanksource/clicky

Length of output: 1076


🏁 Script executed:

# Check if there's a build/dist output and how it handles this
fd -t f 'package.json' examples/enitity/webapp

Repository: flanksource/clicky

Length of output: 44


🏁 Script executed:

# Verify if the path with 5 "../" actually resolves to a real file
test -f "../../../../../clicky-ui/packages/ui/src/styles/tokens.css" && echo "Path exists" || echo "Path does not exist"

Repository: flanksource/clicky

Length of output: 82


🏁 Script executed:

# Check from the webapp directory specifically where the import happens
cd "examples/enitity/webapp/src" && test -f "../../../../../clicky-ui/packages/ui/src/styles/tokens.css" && echo "Path resolves from src" || echo "Path does NOT resolve from src"

Repository: flanksource/clicky

Length of output: 93


🏁 Script executed:

# Check what the correct depth should be - count from webapp/src to repo root
cd "examples/enitity/webapp/src" && pwd && cd ../../../../../.. && pwd

Repository: flanksource/clicky

Length of output: 115


🏁 Script executed:

# Verify the actual path from root to the tokens.css in clicky-ui
ls -la "clicky-ui/packages/ui/src/styles/tokens.css" 2>/dev/null || echo "clicky-ui sibling doesn't exist in this checkout"

Repository: flanksource/clicky

Length of output: 111


🏁 Script executed:

# Check if using the configured alias would work by verifying the alias target
test -f "clicky-ui/packages/ui/dist/index.mjs" && echo "clicky-ui dist exists" || echo "clicky-ui dist does not exist"

Repository: flanksource/clicky

Length of output: 92


🏁 Script executed:

# Check the README to understand how this example is meant to be run
cat README.md | grep -A 20 -B 5 "example" | head -50

Repository: flanksource/clicky

Length of output: 1309


🏁 Script executed:

# Look for any CI/build configuration that might set up clicky-ui
find .github -type f -name "*.yaml" -o -name "*.yml" | xargs grep -l "clicky-ui" 2>/dev/null || echo "No clicky-ui references in CI"

Repository: flanksource/clicky

Length of output: 92


🏁 Script executed:

# Check if there's a monorepo setup (package.json in root with workspaces)
cat package.json 2>/dev/null | head -50

Repository: flanksource/clicky

Length of output: 44


🏁 Script executed:

# Check if there are any other imports of the alias in the codebase
rg '@flanksource/clicky-ui' examples/enitity/webapp/src --type ts --type tsx

Repository: flanksource/clicky

Length of output: 90


🏁 Script executed:

# Verify the vite.config alias one more time to understand the exact alias key
grep -A 2 '@flanksource/clicky-ui/styles' examples/enitity/webapp/vite.config.ts

Repository: flanksource/clicky

Length of output: 205


🏁 Script executed:

# Check if this is meant to be built/deployed or only used in dev mode
grep -r "serve\|dev" examples/enitity/webapp/package.json 2>/dev/null || echo "No package.json found"

Repository: flanksource/clicky

Length of output: 84


🏁 Script executed:

# Look for how the example is actually run/built in the Go code
rg "enitity|webapp" --type go | head -20

Repository: flanksource/clicky

Length of output: 841


🏁 Script executed:

# Check the exact alias syntax one more time and look for any other style imports
cat examples/enitity/webapp/vite.config.ts | grep -A 5 -B 5 'styles'

Repository: flanksource/clicky

Length of output: 741


🏁 Script executed:

# Look for how other files in the webapp import styles or use the alias
rg 'import.*@flanksource' examples/enitity/webapp/src

Repository: flanksource/clicky

Length of output: 172


Use the configured @flanksource/clicky-ui/styles.css alias instead of a broken relative import.

The current import ../../../../../clicky-ui/packages/ui/src/styles/tokens.css uses five ../ segments and doesn't resolve (it reaches outside the repo entirely). The vite.config.ts already defines an alias for this file:

"@flanksource/clicky-ui/styles.css" → "../../../../clicky-ui/packages/ui/src/styles/tokens.css"

Use the configured alias instead:

♻️ Suggested fix
-import "../../../../../clicky-ui/packages/ui/src/styles/tokens.css";
+import "@flanksource/clicky-ui/styles.css";
 import "./index.css";

This matches how other imports in the file use @flanksource/clicky-ui and works both in dev (via vite alias resolution) and build (since vite resolves the alias during bundling).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import "../../../../../clicky-ui/packages/ui/src/styles/tokens.css";
import "@flanksource/clicky-ui/styles.css";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/enitity/webapp/src/main.tsx` at line 9, Replace the broken relative
import in main.tsx that reads
"../../../../../clicky-ui/packages/ui/src/styles/tokens.css" with the configured
Vite alias "@flanksource/clicky-ui/styles.css"; locate the import statement at
the top of main.tsx and change it to use the alias so Vite's resolver (as
defined in vite.config.ts) handles the path during dev and build.

Comment thread examples/go.mod
module examples

go 1.25.1
go 1.26.1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check root go.mod and any CI workflow files for Go version alignment
fd -t f 'go.mod$' --exec sh -c 'echo "=== {} ==="; head -5 "{}"'
fd -t f -e yml -e yaml . .github 2>/dev/null | xargs -I{} sh -c 'echo "=== {} ==="; rg -n "go-version|setup-go" {} 2>/dev/null'

Repository: flanksource/clicky

Length of output: 890


🏁 Script executed:

# Search for GO_VERSION environment variable definition in workflow files
rg -n "GO_VERSION\s*:" .github/workflows/ -A 1 -B 1

# Check if there's an env block at the workflow level
rg -n "env:" .github/workflows/ -A 5

# Check root go.mod for toolchain directive
head -10 ./go.mod

# Search for any toolchain references
rg -n "toolchain" .

Repository: flanksource/clicky

Length of output: 2545


Update release.yml workflow to Go 1.26 or later, or downgrade examples/go.mod to match.

The examples/go.mod specifies go 1.26.1, but the release.yml CI workflow is configured with GO_VERSION: "1.25". This mismatch will cause build failures in the release pipeline. The lint.yml workflow correctly uses GO_VERSION: "1.26", which is compatible with Go 1.26.1.

Either update release.yml to use GO_VERSION: "1.26" (or later) to align with the required Go version, or downgrade the go directive in examples/go.mod to 1.25 or compatible version.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/go.mod` at line 3, The Go version mismatch between examples/go.mod
(go 1.26.1) and the release workflow (GO_VERSION: "1.25") will break CI; fix by
either updating the release workflow's GO_VERSION to "1.26" (or later) in the
release.yml CI config, or by lowering the go directive in examples/go.mod to
"1.25" to match release.yml — ensure the chosen change keeps lint.yml's
GO_VERSION compatible and update only the GO_VERSION variable in release.yml or
the "go" directive in examples/go.mod accordingly.

Comment thread examples/go.mod Outdated
Comment thread format_callbacks.go
Comment thread log.go
Comment thread prompt.go
Comment on lines +1 to +19
package clicky

import (
"errors"
"fmt"
"io"
"os"
"reflect"
"strings"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/flanksource/clicky/api"
"github.com/flanksource/clicky/internal/gumchoose"
"github.com/flanksource/clicky/task"
"golang.org/x/sys/unix"
"golang.org/x/term"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the repo claims Windows support and whether sibling
# platform-specific files / build tags exist for the prompt code path.
fd -t f 'prompt.*\.go$'
echo '---'
rg -nP '^//go:build' -g '*.go' | head -n 50
echo '---'
# Look for any existing GOOS=windows handling or stubs.
rg -nP 'GOOS|//go:build\s+(windows|!windows|unix)' -g '*.go' | head -n 50
echo '---'
# Confirm /dev/tty and unix package usage are confined to this file.
rg -nP '/dev/tty|golang\.org/x/sys/unix' -g '*.go'

Repository: flanksource/clicky

Length of output: 1761


Add build constraint to prompt.go to prevent Windows compilation failure.

This file imports golang.org/x/sys/unix and opens /dev/tty, both unavailable on Windows. It lacks a //go:build !windows constraint at the top. The test files (prompt_terminal_ioctl_linux_test.go, prompt_terminal_ioctl_darwin_test.go) and test logic in prompt_test.go confirm the feature is Unix-only, yet other Unix-specific code in the repo (e.g., exec/process_group_unix.go) already follows this pattern correctly. Add the build constraint to the top of the file to match the repo's conventions and prevent compilation failures on unsupported platforms.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prompt.go` around lines 1 - 19, This file (prompt.go) is Unix-only but lacks
a build constraint, causing Windows builds to fail due to imports like
"golang.org/x/sys/unix" and opening "/dev/tty"; add a build tag at the very top
of the file (before the package declaration) such as "//go:build !windows" (and
optionally the legacy "// +build !windows" line directly beneath it) so the
compiler excludes prompt.go from Windows builds and matches the repo's Unix-only
pattern used by other files like exec/process_group_unix.go.

Comment thread rpc/openapi.go Outdated
Comment thread rpc/serve_test.go
Comment on lines 442 to 446
func TestFormatToContentType_ClickyJSON(t *testing.T) {
assert.Equal(t, "application/clicky+json", formatToContentType("clicky-json"))
assert.Equal(t, "application/json+clicky", formatToContentType("clicky-json"))
assert.Equal(t, "text/html; charset=utf-8", formatToContentType("html-react"))
assert.Equal(t, "application/json", formatToContentType("json"))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

RFC 6839 structured syntax suffix +json convention application/something+json

💡 Result:

RFC 6839 defines the "+json" structured syntax suffix for media types whose representation follows the JSON format established by application/json (RFC 4627, updated by RFC 8259). The convention for "application/something+json" is to append "+json" to the subtype name (e.g., application/problem+json), indicating that the media type uses JSON syntax. This allows generic JSON processors to handle the underlying representation while specific processors use the full type semantics. Use the +json suffix when: - The media type identifies specific semantics (e.g., application/problem+json for HTTP error details per RFC 7807 / RFC 9457). - Generic JSON parsing is possible without special knowledge beyond standard JSON. Examples include: - application/problem+json (RFC 7807/9457): Problem details for HTTP APIs. - application/ld+json: JSON-LD. - application/vnd.api+json: JSON:API. - application/geo+json: GeoJSON. - application/activity+json: Activity Streams. Processors seeing "xxx/yyy+json" can parse as JSON generically (per RFC 6839 Section 3.1 fragment considerations). It is not a subtype of application/json for content negotiation; specific types like application/problem+json are distinct. The suffix is registered in IANA's Structured Syntax Suffix Registry. New media types register via RFC 6838 procedures.

Citations:


🏁 Script executed:

# First, let's check the file structure and locate the test file
find . -name "serve_test.go" -type f

Repository: flanksource/clicky

Length of output: 82


🏁 Script executed:

# Read the relevant portions of the test file
wc -l ./rpc/serve_test.go

Repository: flanksource/clicky

Length of output: 86


🏁 Script executed:

# Read the entire test file
cat -n ./rpc/serve_test.go

Repository: flanksource/clicky

Length of output: 15070


🏁 Script executed:

# Now let's look at the serve.go file to find the formatToContentType function
find . -name "serve.go" -type f | head -5

Repository: flanksource/clicky

Length of output: 77


🏁 Script executed:

# Let's search for the formatToContentType function in serve.go
rg -n "formatToContentType" ./rpc/serve.go -A 10

Repository: flanksource/clicky

Length of output: 669


🏁 Script executed:

# Let's see the full formatToContentType function
rg -n "formatToContentType" ./rpc/serve.go -A 20 | tail -30

Repository: flanksource/clicky

Length of output: 865


🏁 Script executed:

# Let's also check if there are any other references to these media types in the codebase
rg -n "clicky\+json|json\+clicky" --type go

Repository: flanksource/clicky

Length of output: 2060


🏁 Script executed:

# Let's check the git history or comments around the change to understand the reasoning
rg -n "json\+clicky|clicky\+json" ./rpc/serve.go -B 3 -A 3

Repository: flanksource/clicky

Length of output: 732


Non-standard outbound media type — application/clicky+json is the standards-compliant form per RFC 6839.

The structured syntax suffix +json (RFC 6839 / RFC 9110) follows the convention application/<subtype>+json (e.g., application/ld+json, application/problem+json). The current implementation outputs application/json+clicky, which does not match any registered suffix scheme and breaks content-negotiation libraries that specifically match the +json suffix.

Inbound parsing correctly accepts both forms (lines 401-417), but the outbound Content-Type header should use the RFC-compliant form. While the code comments refer to application/clicky+json as "legacy", the standards expect the opposite.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rpc/serve_test.go` around lines 442 - 446, The test reveals the outbound
Content-Type is using a non-standard suffix order; update the
formatToContentType implementation so that the "clicky-json" case returns the
RFC-compliant structured-syntax-suffix form "application/clicky+json" (change
the mapping in formatToContentType) and adjust the test
TestFormatToContentType_ClickyJSON to expect "application/clicky+json" instead
of "application/json+clicky"; ensure only the outbound mapping is changed
(inbound parsing can remain as-is).

Comment thread task/manager.go
Remove outdated example files (example-data.json, example-data-200.json, go.mod, go.sum, main.go, order-schema.yaml, output.html) from the example directory. Update examples/go.mod to use commons v1.51.0 and clean up unused dependencies. Minor formatting adjustments to order-schema.yaml for consistency.
…ollapsing

Refactor task rendering to prioritize problem tasks (failed/warning/cancelled) and running tasks, ensuring they are never collapsed into summaries. Success tasks are collapsed when they exceed available terminal height, while pending tasks remain limited to 5 visible items.

This change improves visibility of critical task states that require user attention while still managing screen real estate for less critical tasks.

Also updates dependencies: flanksource/commons v1.50.3 → v1.51.0, adds charmbracelet/x/conpty, charmbracelet/x/errors, charmbracelet/x/termios, charmbracelet/x/xpty, erikgeiser/coninput, and mattn/go-localereader. Removes unused dependencies including Snawoot/go-http-digest-auth-client and aws/aws-sdk-go-v2/service/route53.

Adds .gavel/ to .gitignore and includes JSX type definitions for iconify-icon component.
- prompt: set explicit PTY winsize and TERM=screen-256color so huh's
  text input renders under PTY without leaking OSC11 query response
  into stdin as user input.
- text/tokenizer: rewrite StripANSI to terminate CSI sequences on any
  byte in 0x40-0x7E (not just 'm') so cursor-movement codes like \x1b[3A
  no longer swallow text up to the next 'm' character.
- rpc/converter: skip the {id}/action path rewrite for top-level commands
  whose only "parent" is the cobra root, so commands like 'pretty <file>'
  resolve to /api/v1/pretty instead of /api/v1/{file}/pretty.
- format_callbacks: MustFormatWithContext now panics on error to match
  its documented Must* contract (was silently swallowing the error).
- api/labelbadge: HTML() now honors Shape (rounded/pill/square) and
  Icon fields the struct documents.
- log: loggerOutput() falls back to os.Stderr when the commons logger
  has no writer configured, preventing a nil-writer panic.
- task/manager: convert isInteractive to atomic.Bool so SetForceInteractive
  doesn't race with the render loop.
- rpc/openapi: drop duplicate Surface-key assignment loop.
- examples/go.mod: bump go-jose/v3 to v3.0.5 (GHSA-78h2-9frx-2jm8).
- release.yml: align GO_VERSION with examples/go.mod (1.26).
- e2e: update OpenAPI path expectation to match converter fix.
@moshloop moshloop merged commit db4f748 into main Apr 27, 2026
14 of 16 checks passed
@moshloop moshloop deleted the pr/badges branch April 27, 2026 08:12
flankbot pushed a commit that referenced this pull request Apr 27, 2026
## [1.21.6](v1.21.5...v1.21.6) (2026-04-27)

### ⚠ BREAKING CHANGES

* **formatters:** FormatWithOptions now delegates to FormatWithContext internally; custom FormatManager implementations should override FormatWithContext instead.
* **api,ui,example:** stackWindowOpts now uses From and To time.Time fields instead of UpdatedSince and Window duration fields. Content-Type header for clicky-json format changed from application/clicky+json to application/json+clicky.

Refs: entity demo tests, example app routing, API response handling
* **formatters:** The Content-Type header for clicky JSON responses is now application/json+clicky instead of application/clicky+json. Clients should update their expectations, though the legacy format is still accepted in Accept headers.

### ✨ Features

* **api,rpc,entity:** Add Link types and entity operation annotations for OpenAPI ([3bf7d81](3bf7d81))
* **api,ui,example:** add time range filtering and entity detail page navigation ([0b78123](0b78123))
* **api:** add LabelBadge type for two-part pill badges ([8874efb](8874efb))
* **entity-example:** add link and linkcommand examples page with deep-linking support ([d6d89b6](d6d89b6))
* **entity:** Add filter lookup support for dynamic filter metadata and completions ([168e9b2](168e9b2))
* **formatters:** add format callbacks and link/command support ([b19923a](b19923a))
* **formatters:** standardize clicky JSON content type to application/json+clicky ([a899d50](a899d50))
* **prompt:** Add interactive prompt APIs with select, multi-select, and text input ([ad6a55d](ad6a55d))
* **task,examples:** Add interactive prompt support and task terminal ownership management ([5218f72](5218f72))
* **task,lint,log:** add output capture and log serialization for clean task rendering ([6dc5e67](6dc5e67))
* **task:** improve task rendering with priority-based ordering and collapsing ([aaa5043](aaa5043))

### 🐛 Bug Fixes

* address PR [#99](#99) test failures and CodeRabbit review ([db4f748](db4f748))

### 🔧 Maintenance

* **examples:** remove legacy example files and update dependencies ([190395f](190395f))
@flankbot
Copy link
Copy Markdown

🎉 This PR is included in version 1.21.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants