feat(github): configurable quick-action presets and PR branch checkout (#689)
* feat(github): configurable quick-action presets and PR branch checkout - Add workspace-scoped action presets (icon, label, hint, prompt template) for PRs and Issues, editable from GitHub settings with reset-to-defaults. - When starting a task from a PR, check out the PR head branch directly in the worktree instead of branching off main. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(github): extract action-preset service to keep service.go under line limit Splits the preset CRUD methods out of the oversized service.go and applies prettier formatting to three frontend files flagged by CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(github): address review feedback on action presets - Prevent infinite refetch loop when fetching presets fails by tracking attempted workspaces in a ref instead of re-entering the effect on every loading-state change. - Drop unused savePR/saveIssue helpers from the hook; only save() is consumed. - Reorder the init spread in updateGitHubActionPresets/resetGitHubActionPresets so callers can't accidentally overwrite method/body. - Tighten the GitHubActionPreset.icon type with string & {} so known icon keys keep autocomplete. - Sharpen the eye-icon test to assert the exact IconEye mapping. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(github): address second round of review feedback - Seed preset editor drafts from current store state via lazy initializers so a warm mount shows the user's saved customisations instead of silently reverting to defaults when Save is clicked. - Guard Reset button with its own in-flight state to prevent double-firing. - Stop freezing resolved defaults into the stored row on a partial update; persist only what the store has for the untouched kind so future default improvements still reach those workspaces. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(github): tighten quick-actions editor UI Replace the stacked PR/Issue editors and multi-line preset cards with a tabbed layout and single-line rows. Each row shows icon · label · hint inline; an "Edit prompt" toggle reveals the template textarea only when needed. Drops the redundant section headings, up/down reorder buttons, and verbose template helper label (hint moved into the textarea placeholder). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(github): widen icon select trigger so the icon is visible The icon select trigger had a fixed 40px width which the SelectTrigger's built-in chevron, padding, and icon gap all competed for, clipping the icon out of view. Drop the width override so the trigger sizes to its content. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -133,6 +133,13 @@ const createTablesSQL = `
|
||||
created_at DATETIME NOT NULL,
|
||||
UNIQUE(issue_watch_id, repo_owner, repo_name, issue_number)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS github_action_presets (
|
||||
workspace_id TEXT PRIMARY KEY,
|
||||
pr_presets TEXT NOT NULL DEFAULT '[]',
|
||||
issue_presets TEXT NOT NULL DEFAULT '[]',
|
||||
updated_at DATETIME NOT NULL
|
||||
);
|
||||
`
|
||||
|
||||
func (s *Store) initSchema() error {
|
||||
@@ -888,3 +895,59 @@ func (s *Store) DeleteIssueWatchTask(ctx context.Context, id string) error {
|
||||
_, err := s.db.ExecContext(ctx, `DELETE FROM github_issue_watch_tasks WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// --- Action preset operations ---
|
||||
|
||||
// GetActionPresets returns stored PR/Issue presets for a workspace. Returns
|
||||
// (nil, nil) when no row exists yet so the caller can apply defaults.
|
||||
func (s *Store) GetActionPresets(ctx context.Context, workspaceID string) (*ActionPresets, error) {
|
||||
var row struct {
|
||||
PRJSON string `db:"pr_presets"`
|
||||
IssueJSON string `db:"issue_presets"`
|
||||
}
|
||||
err := s.ro.GetContext(ctx, &row,
|
||||
`SELECT pr_presets, issue_presets FROM github_action_presets WHERE workspace_id = ?`, workspaceID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
presets := &ActionPresets{WorkspaceID: workspaceID}
|
||||
if err := json.Unmarshal([]byte(row.PRJSON), &presets.PR); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal pr presets: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(row.IssueJSON), &presets.Issue); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal issue presets: %w", err)
|
||||
}
|
||||
return presets, nil
|
||||
}
|
||||
|
||||
// UpsertActionPresets stores PR/Issue presets for a workspace, replacing any
|
||||
// existing row.
|
||||
func (s *Store) UpsertActionPresets(ctx context.Context, presets *ActionPresets) error {
|
||||
prJSON, err := json.Marshal(presets.PR)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal pr presets: %w", err)
|
||||
}
|
||||
issueJSON, err := json.Marshal(presets.Issue)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal issue presets: %w", err)
|
||||
}
|
||||
_, err = s.db.ExecContext(ctx, `
|
||||
INSERT INTO github_action_presets (workspace_id, pr_presets, issue_presets, updated_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(workspace_id) DO UPDATE SET
|
||||
pr_presets = excluded.pr_presets,
|
||||
issue_presets = excluded.issue_presets,
|
||||
updated_at = excluded.updated_at`,
|
||||
presets.WorkspaceID, string(prJSON), string(issueJSON), time.Now().UTC())
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteActionPresets removes the stored overrides for a workspace so defaults
|
||||
// apply again.
|
||||
func (s *Store) DeleteActionPresets(ctx context.Context, workspaceID string) error {
|
||||
_, err := s.db.ExecContext(ctx, `DELETE FROM github_action_presets WHERE workspace_id = ?`, workspaceID)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user