Documentation
¶
Index ¶
- Constants
- func FormatComment(version string, rating int, goos, goarch, comment string) string
- func MigrateLegacyOptOut(s *State, currentVersion string) bool
- func MinDaysBeforeFirstPrompt() int
- func MinLaunchesBeforeFirstPrompt() int
- func PromptCooldownDays() int
- func RatingEmoji(rating int) string
- func RecordLaunch(s *State, now time.Time)
- func RecordOptOut(s *State, currentVersion string)
- func RecordRating(s *State, currentVersion string, rating int)
- func RecordShown(s *State, now time.Time)
- func SaveState(s *State) error
- func ShouldShow(s *State, currentVersion string, now time.Time) bool
- type Sender
- type State
Constants ¶
const DiscussionNodeID = "D_kwDOQh82-s4Alt9V"
DiscussionNodeID is the GitHub Discussion node ID for the Feedback Hub. Must be replaced with the real node ID before release. Retrieve after creating the Discussion at:
https://github.com/asheshgoplani/agent-deck/discussions
via: gh api graphql -f query='{ repository(owner:"asheshgoplani",name:"agent-deck") { discussions(first:5) { nodes { id title } } } }'
const DiscussionURL = "https://github.com/asheshgoplani/agent-deck/discussions"
DiscussionURL is the GitHub Discussions page for agent-deck feedback. Note: GitHub Discussions does NOT support ?body= URL parameter prefill (only GitHub Issues supports this). The browser fallback opens this URL and relies on the user pasting from clipboard into the Discussion form.
Variables ¶
This section is empty.
Functions ¶
func FormatComment ¶
FormatComment formats a feedback submission for posting to GitHub Discussions. Format: "**vVER** | **N/5** EMOJI | GOOS GOARCH\nCOMMENT" When comment is empty, the trailing newline and comment are omitted.
func MigrateLegacyOptOut ¶ added in v1.9.10
MigrateLegacyOptOut rewrites a pre-#967 forever-opt-out (FeedbackEnabled false with no OptOutVersion) into a per-series opt-out anchored at currentVersion. Idempotent: a state that already records an OptOutVersion or has feedback enabled is left untouched. Returns true if the caller should persist the result via SaveState.
Production wiring lives in cmd/agent-deck/main.go, which calls this from the TUI launch path right next to RecordLaunch — so the legacy migration happens exactly once per affected user, on their first TUI launch after upgrading to a binary that ships #967.
func MinDaysBeforeFirstPrompt ¶ added in v1.7.41
func MinDaysBeforeFirstPrompt() int
MinDaysBeforeFirstPrompt returns the configured floor (default 3).
func MinLaunchesBeforeFirstPrompt ¶ added in v1.7.41
func MinLaunchesBeforeFirstPrompt() int
MinLaunchesBeforeFirstPrompt returns the configured floor (default 7).
func PromptCooldownDays ¶ added in v1.7.41
func PromptCooldownDays() int
PromptCooldownDays returns the configured cooldown (default 14).
func RatingEmoji ¶
RatingEmoji maps a numeric rating (1-5) to an emoji. Returns "" for out-of-range values.
func RecordLaunch ¶ added in v1.7.41
RecordLaunch increments LaunchCount by 1 and seeds FirstSeenAt with now on the very first call. Subsequent calls never overwrite FirstSeenAt so pacing persists across version upgrades and state reloads. Does NOT save — caller must call SaveState.
func RecordOptOut ¶
RecordOptOut records a feedback dismissal scoped to the running release series. After #967 this is no longer a permanent kill-switch — the opt-out only suppresses prompts while the running version stays on the same MAJOR.MINOR line (e.g. "1.9.x"). The legacy FeedbackEnabled flag is still toggled off so external readers (config.toml sync, the explicit `agent-deck feedback` re-enable prompt) continue to see the user as opted out until something explicitly clears it. Does NOT save — caller must call SaveState.
func RecordRating ¶
RecordRating sets last_rated_version to currentVersion and resets shown_count to 0. Deliberately does NOT touch FirstSeenAt, LastPromptedAt, or LaunchCount — pacing signals survive a rating so the next version still paces against the user's real history. Does NOT save — caller must call SaveState.
func RecordShown ¶
RecordShown increments shown_count by 1 and stamps LastPromptedAt with now so the cooldown engages for subsequent calls. Does NOT save — caller must call SaveState.
func SaveState ¶
SaveState atomically writes the state to ~/.agent-deck/feedback-state.json. Uses tmp+rename to prevent partial writes (T-01-01).
func ShouldShow ¶
ShouldShow returns true only when every gate is clear:
- the user is not opted out for the current release series (#967 — opt-out is scoped to MAJOR.MINOR, not forever)
- last_rated_version does not match currentVersion
- shown_count < max_shows
- user has been around for at least MinDaysBeforeFirstPrompt days AND has launched agent-deck at least MinLaunchesBeforeFirstPrompt times
- no prompt was shown within the last PromptCooldownDays days
Pure: never mutates state. Callers that want to track first-seen should call RecordLaunch at process start.
Types ¶
type Sender ¶
type Sender struct {
// GhCmd runs the gh CLI with the given arguments.
// Real implementation: exec.Command("gh", args...).CombinedOutput().
GhCmd func(args ...string) error
// BrowserCmd opens the given URL in the default browser.
// Real implementation: runtime.GOOS switch (open/xdg-open).
BrowserCmd func(url string) error
// ClipboardCmd copies the given text to the system clipboard.
// Receives the formatted COMMENT BODY (not a URL).
// Real implementation: clipboard.Copy(text, false).
ClipboardCmd func(text string) error
// IsHeadlessFunc returns true when no graphical display is available.
// Real implementation: platform.IsHeadless().
IsHeadlessFunc func() bool
}
Sender holds the three-tier send mechanism for feedback submissions. All four function fields are injectable for testing.
func NewSender ¶
func NewSender() *Sender
NewSender returns a *Sender with all four fields populated with real implementations.
func (*Sender) Send ¶
Send submits feedback using a three-tier fallback chain:
- gh CLI via GraphQL mutation (primary)
- If gh fails and not headless: copy body to clipboard, then open Discussion URL in browser
- If gh fails and headless: copy body to clipboard only (no browser)
version, rating, goos, goarch, comment are used to build the formatted comment body via FormatComment. Send returns nil in all fallback cases — the caller should inform the user that clipboard/browser fallback was used.
type State ¶
type State struct {
LastRatedVersion string `json:"last_rated_version"`
FeedbackEnabled bool `json:"feedback_enabled"`
OptOutVersion string `json:"opt_out_version,omitempty"`
ShownCount int `json:"shown_count"`
MaxShows int `json:"max_shows"`
LaunchCount int `json:"launch_count,omitempty"`
FirstSeenAt time.Time `json:"first_seen_at,omitempty"`
LastPromptedAt time.Time `json:"last_prompted_at,omitempty"`
}
State holds the persisted feedback preferences for a user. File: ~/.agent-deck/feedback-state.json. Always serializes all fields (D-05).
v1.7.41 added LaunchCount, FirstSeenAt, LastPromptedAt to pace the first prompt for new users. Serialized via RFC3339 through time.Time's MarshalJSON.
#967 added OptOutVersion to scope the dismiss-feedback opt-out to a single release series (MAJOR.MINOR). A user who declined at v1.9.5 is silenced only while the running version is still on the 1.9.x line; the next release-series bump re-shows the prompt. Pre-#967 state files stored a permanent FeedbackEnabled=false with no OptOutVersion — those are migrated in-memory in ShouldShow by treating them as "dismissed at the current series" (suppresses the immediate launch but expires on the next series bump).