Documentation
¶
Index ¶
- Variables
- func DetectCandidateKinds(key string) []string
- func DetectKind(key string) string
- func ExtractProject(key string) string
- func FormatMissingCreds(result CredResult, parsed *ParsedURL) string
- func IsURL(input string) bool
- func SectionToKind() map[string]string
- type Action
- type Assigner
- type AuditEntry
- type AuditProvider
- func (a *AuditProvider) AddComment(ctx context.Context, issueKey string, body string) (*Comment, error)
- func (a *AuditProvider) AssignIssue(ctx context.Context, key string, userID string) error
- func (a *AuditProvider) Close() error
- func (a *AuditProvider) CreateIssue(ctx context.Context, issue *Issue) (*Issue, error)
- func (a *AuditProvider) DeleteIssue(ctx context.Context, key string) error
- func (a *AuditProvider) EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
- func (a *AuditProvider) GetCurrentUser(ctx context.Context) (string, error)
- func (a *AuditProvider) GetIssue(ctx context.Context, key string) (*Issue, error)
- func (a *AuditProvider) ListComments(ctx context.Context, issueKey string) ([]Comment, error)
- func (a *AuditProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
- func (a *AuditProvider) ListStatuses(ctx context.Context, key string) ([]Status, error)
- func (a *AuditProvider) TransitionIssue(ctx context.Context, key string, targetStatus string) error
- type Category
- type Comment
- type Commenter
- type Creator
- type CredResult
- type CredSpec
- type CurrentUserGetter
- type Deleter
- type DestructiveEntry
- type DestructiveNotifier
- type DestructiveProvider
- func (d *DestructiveProvider) AddComment(ctx context.Context, issueKey string, body string) (*Comment, error)
- func (d *DestructiveProvider) AssignIssue(ctx context.Context, key string, userID string) error
- func (d *DestructiveProvider) Close() error
- func (d *DestructiveProvider) CreateIssue(ctx context.Context, issue *Issue) (*Issue, error)
- func (d *DestructiveProvider) DeleteIssue(ctx context.Context, key string) error
- func (d *DestructiveProvider) EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
- func (d *DestructiveProvider) GetCurrentUser(ctx context.Context) (string, error)
- func (d *DestructiveProvider) GetIssue(ctx context.Context, key string) (*Issue, error)
- func (d *DestructiveProvider) ListComments(ctx context.Context, issueKey string) ([]Comment, error)
- func (d *DestructiveProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
- func (d *DestructiveProvider) ListStatuses(ctx context.Context, key string) ([]Status, error)
- func (d *DestructiveProvider) TransitionIssue(ctx context.Context, key string, targetStatus string) error
- type EditOptions
- type Editor
- type FindResult
- type Getter
- type Instance
- type Issue
- type ListOptions
- type Lister
- type ParsedURL
- type Policy
- type PolicyConfig
- type PolicyProvider
- func (pp *PolicyProvider) AddComment(ctx context.Context, issueKey string, body string) (*Comment, error)
- func (pp *PolicyProvider) AssignIssue(ctx context.Context, key string, userID string) error
- func (pp *PolicyProvider) CreateIssue(ctx context.Context, issue *Issue) (*Issue, error)
- func (pp *PolicyProvider) DeleteIssue(ctx context.Context, key string) error
- func (pp *PolicyProvider) EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
- func (pp *PolicyProvider) GetCurrentUser(ctx context.Context) (string, error)
- func (pp *PolicyProvider) GetIssue(ctx context.Context, key string) (*Issue, error)
- func (pp *PolicyProvider) ListComments(ctx context.Context, issueKey string) ([]Comment, error)
- func (pp *PolicyProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
- func (pp *PolicyProvider) ListStatuses(ctx context.Context, key string) ([]Status, error)
- func (pp *PolicyProvider) TransitionIssue(ctx context.Context, key string, targetStatus string) error
- type Provider
- type SafeProvider
- func (s *SafeProvider) AddComment(ctx context.Context, issueKey string, body string) (*Comment, error)
- func (s *SafeProvider) AssignIssue(ctx context.Context, key string, userID string) error
- func (s *SafeProvider) CreateIssue(ctx context.Context, issue *Issue) (*Issue, error)
- func (s *SafeProvider) DeleteIssue(_ context.Context, _ string) error
- func (s *SafeProvider) EditIssue(_ context.Context, _ string, _ EditOptions) (*Issue, error)
- func (s *SafeProvider) GetCurrentUser(ctx context.Context) (string, error)
- func (s *SafeProvider) GetIssue(ctx context.Context, key string) (*Issue, error)
- func (s *SafeProvider) ListComments(ctx context.Context, issueKey string) ([]Comment, error)
- func (s *SafeProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
- func (s *SafeProvider) ListStatuses(ctx context.Context, key string) ([]Status, error)
- func (s *SafeProvider) TransitionIssue(_ context.Context, _ string, _ string) error
- type Status
- type StatusLister
- type TrackerStatus
- type Transitioner
Constants ¶
This section is empty.
Variables ¶
var CredSpecs = map[string]CredSpec{}
CredSpecs maps tracker kinds to their credential requirements. This is exported so the cmd layer can populate it with provider-specific knowledge, keeping internal/tracker free of provider-specific data. The cmd/cmdutil package populates it at init time.
var KindToSection = map[string]string{
"jira": "jiras",
"github": "githubs",
"gitlab": "gitlabs",
"linear": "linears",
"azuredevops": "azuredevops",
"shortcut": "shortcuts",
}
KindToSection maps tracker kinds to their .humanconfig YAML section names. This is the canonical mapping; use SectionToKind() for the inverse.
Functions ¶
func DetectCandidateKinds ¶
DetectCandidateKinds returns all tracker kinds whose key format matches the given key. The order is deterministic: azuredevops is checked before github/gitlab repo format since "Word/N" is a subset of "owner/repo".
func DetectKind ¶
DetectKind returns the tracker kind that can be unambiguously inferred from the key format. Returns "" when the key is ambiguous or unrecognised.
The ordering must agree with DetectCandidateKinds: "Project/42" is a valid Azure DevOps key AND a valid github/gitlab repo shape, so azuredevops has to be checked first, otherwise callers relying on DetectKind route Azure keys to GitHub.
func ExtractProject ¶
ExtractProject extracts the project identifier from a key.
"KAN-42" → "KAN" "octocat/repo#42" → "octocat/repo" "octocat/repo" → "octocat/repo" "Project/42" → "Project" "123" → ""
func FormatMissingCreds ¶
func FormatMissingCreds(result CredResult, parsed *ParsedURL) string
FormatMissingCreds returns a user-friendly message about which env vars to set.
func SectionToKind ¶
SectionToKind returns the mapping of .humanconfig section names to tracker kinds.
Types ¶
type AuditEntry ¶
type AuditEntry struct {
Timestamp string `json:"timestamp"`
Operation string `json:"operation"`
Tracker string `json:"tracker"`
Kind string `json:"kind"`
Key string `json:"key"`
DurationMs int64 `json:"duration_ms"`
Error string `json:"error,omitempty"`
}
AuditEntry represents a single audit log record written as a JSON line.
type AuditProvider ¶
type AuditProvider struct {
// contains filtered or unexported fields
}
AuditProvider wraps a Provider and logs every method call to a JSON Lines file.
func NewAuditProvider ¶
func NewAuditProvider(inner Provider, name, kind, logPath string) (*AuditProvider, error)
NewAuditProvider creates an AuditProvider that delegates to inner and appends audit entries to the file at logPath. The file is created if it does not exist.
func (*AuditProvider) AddComment ¶
func (*AuditProvider) AssignIssue ¶
func (*AuditProvider) Close ¶
func (a *AuditProvider) Close() error
Close closes the underlying log file if present. Nil defends against panics from struct-literal test builds and double-close.
func (*AuditProvider) CreateIssue ¶
func (*AuditProvider) DeleteIssue ¶
func (a *AuditProvider) DeleteIssue(ctx context.Context, key string) error
func (*AuditProvider) EditIssue ¶
func (a *AuditProvider) EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
func (*AuditProvider) GetCurrentUser ¶
func (a *AuditProvider) GetCurrentUser(ctx context.Context) (string, error)
func (*AuditProvider) ListComments ¶
func (*AuditProvider) ListIssues ¶
func (a *AuditProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
func (*AuditProvider) ListStatuses ¶
func (*AuditProvider) TransitionIssue ¶
type Category ¶
type Category string
Category is the fixed, cross-tracker semantic bucket a Status belongs to.
Every tracker exposes its own user-facing status names ("Ready for Review", "In QA", "Needs Design", …) that vary per team and per workflow. Category normalises those names into a small closed set the CLI can reason about uniformly — e.g. "is this issue done?", "is work in progress?", "what colour should this chip be in the TUI?".
Two concepts, on purpose:
- Status.Name = the label the user sees (tracker-specific, free-form).
- Status.Category = the semantic bucket (fixed enum, defined here).
Per-tracker mapping lives in each provider client:
Linear linearStateType() — internal/linear/client.go Azure DevOps adoCategoryToType() — internal/azuredevops/client.go ClickUp mapStatusType() — internal/clickup/client.go Shortcut passthrough from API workflow state type GitHub open→Started, closed→Closed (binary) GitLab opened→Started, closed→Closed (binary) Jira not populated (transitions are dynamic per issue)
Upstream trackers distinguish more buckets than we do (Linear has 5: Backlog, Unstarted, Started, Completed, Canceled; Azure DevOps has 5: Proposed, InProgress, Resolved, Completed, Removed). We collapse them to 4 because that is the minimum the CLI needs to drive behaviour. To add a new category, extend this enum first, then update each client's mapping — do not sneak new values past the enum as bare strings.
const ( CategoryUnknown Category = "" // not populated (e.g. Jira transitions) CategoryUnstarted Category = "unstarted" // work not yet begun; Linear Backlog+Todo, ADO Proposed, ClickUp open CategoryStarted Category = "started" // actively in progress; Linear Started, ADO InProgress, GitHub/GitLab open CategoryDone Category = "done" // completed successfully; Linear Completed, ADO Resolved+Completed, ClickUp done+closed CategoryClosed Category = "closed" // finished but not completed (cancelled/removed); Linear Canceled, ADO Removed, GitHub/GitLab closed )
type Commenter ¶
type Commenter interface {
ListComments(ctx context.Context, issueKey string) ([]Comment, error)
AddComment(ctx context.Context, issueKey string, body string) (*Comment, error)
}
Commenter manages issue comments.
type CredResult ¶
type CredResult struct {
Spec CredSpec
Available map[string]string // suffix → value, for env vars that are set
Missing []string // suffixes that are not set
Complete bool // true if all required vars are set
}
CredResult tells the caller what credentials are available.
func CheckCreds ¶
func CheckCreds(spec CredSpec) CredResult
CheckCreds checks which credentials are available in the environment. It checks global env vars (e.g. JIRA_KEY) for each required suffix.
func CheckCredsEnv ¶
func CheckCredsEnv(spec CredSpec, getenv func(string) string) CredResult
CheckCredsEnv is like CheckCreds but accepts a custom env lookup function.
type CredSpec ¶
type CredSpec struct {
Kind string // "jira", "github", etc.
EnvPrefix string // "JIRA", "GITHUB", etc.
Required []string // env var suffixes: ["KEY", "USER"] for Jira, ["TOKEN"] for GitHub
Label string // Human-readable name
HelpURL string // Where to generate tokens
}
CredSpec describes the credentials required for a tracker kind.
func CredSpecForKind ¶
CredSpecForKind returns the credential specification for a tracker kind.
type CurrentUserGetter ¶
CurrentUserGetter retrieves the authenticated user's identifier.
type DestructiveEntry ¶
type DestructiveEntry struct {
Timestamp string `json:"timestamp"`
Operation string `json:"operation"` // "DeleteIssue", "EditIssue", "TransitionIssue"
Tracker string `json:"tracker"` // instance name
Kind string `json:"kind"` // "jira", "linear", etc.
Key string `json:"key"` // issue key
Detail string `json:"detail,omitempty"` // e.g. target status for transition, changed fields for edit
Error string `json:"error,omitempty"`
}
DestructiveEntry represents a destructive operation log record.
type DestructiveNotifier ¶
type DestructiveNotifier interface {
NotifyDestructive(ctx context.Context, entry DestructiveEntry) // no error return -- fire-and-forget
}
DestructiveNotifier sends fire-and-forget notifications for destructive ops.
type DestructiveProvider ¶
type DestructiveProvider struct {
// contains filtered or unexported fields
}
DestructiveProvider wraps a Provider and logs destructive operations (DeleteIssue, EditIssue, TransitionIssue) to a dedicated log file. Optionally sends notifications via a DestructiveNotifier.
func NewDestructiveProvider ¶
func NewDestructiveProvider(inner Provider, name, kind, logPath string, notifier DestructiveNotifier) (*DestructiveProvider, error)
NewDestructiveProvider creates a DestructiveProvider that delegates to inner and logs destructive operations to the file at logPath. The notifier may be nil, in which case only logging occurs.
func (*DestructiveProvider) AddComment ¶
func (*DestructiveProvider) AssignIssue ¶
func (*DestructiveProvider) Close ¶
func (d *DestructiveProvider) Close() error
Close closes the underlying log file if present. Struct-literal construction in tests and composite wrappers can leave logFile nil, so the nil check defends against panics in those paths.
func (*DestructiveProvider) CreateIssue ¶
func (*DestructiveProvider) DeleteIssue ¶
func (d *DestructiveProvider) DeleteIssue(ctx context.Context, key string) error
func (*DestructiveProvider) EditIssue ¶
func (d *DestructiveProvider) EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
func (*DestructiveProvider) GetCurrentUser ¶
func (d *DestructiveProvider) GetCurrentUser(ctx context.Context) (string, error)
func (*DestructiveProvider) ListComments ¶
func (*DestructiveProvider) ListIssues ¶
func (d *DestructiveProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
func (*DestructiveProvider) ListStatuses ¶
func (*DestructiveProvider) TransitionIssue ¶
type EditOptions ¶
EditOptions specifies which fields to update on an issue. Nil pointer fields are left unchanged; non-nil fields are set (even if empty).
type Editor ¶
type Editor interface {
EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
}
Editor updates an existing issue's title and/or description.
type FindResult ¶
type FindResult struct {
Provider string `json:"provider"`
Project string `json:"project"`
Key string `json:"key"`
}
FindResult holds the outcome of FindTracker.
func FindTracker ¶
FindTracker determines which configured tracker owns the given key.
Resolution strategy:
- Match key format against regexes → candidate kinds
- Filter candidates against configured instances
- If one kind remains → return it (no API call)
- If ambiguous → probe each instance with GetIssue until one succeeds
type Instance ¶
type Instance struct {
Name string // config entry name ("work", "personal"), empty for CLI-flag instances
Kind string // "jira", "github", "linear"
URL string // display URL
User string // display user (Jira only)
Description string // optional human-readable description of what this tracker is for
Role string // "pm", "engineering", or empty (inferred from kind)
Safe bool // when true, destructive operations (deletes) are blocked
Projects []string // projects to index (e.g. ["KAN", "INFRA"])
Provider Provider
}
Instance represents a configured tracker backend ready for use.
func Resolve ¶
Resolve determines which tracker instance to use.
When name is provided it finds the single instance whose Name matches. When name is empty it auto-detects: if keyHint allows inferring the tracker kind it filters to that kind; otherwise if all instances share one Kind it returns the first; if multiple kinds exist it returns an error.
func ResolveByKind ¶
ResolveByKind returns the first instance matching the given tracker kind. When name is non-empty, it further filters to that named instance.
type Issue ¶
type Issue struct {
Key string `json:"key"`
Project string `json:"project"` // project key, e.g. "KAN"
Type string `json:"type"` // issue type, e.g. "Task", "Bug"
Title string `json:"title"`
Status string `json:"status"`
StatusType Category `json:"status_type,omitempty"` // semantic bucket, see Category
Priority string `json:"priority"`
Assignee string `json:"assignee"`
Reporter string `json:"reporter"`
Description string `json:"description"` // markdown
URL string `json:"url,omitempty"` // web URL for opening in browser
UpdatedAt time.Time `json:"updated_at"` // last modification timestamp
ParentKey string `json:"parent_key,omitempty"` // parent issue key (subtask support)
Labels []string `json:"labels,omitempty"` // tags/labels on the issue
}
Issue is a provider-agnostic issue representation.
func (Issue) IsBug ¶
IsBug reports whether this issue represents a defect, normalised across trackers. A match is any segment equal to "bug" (case-insensitive) in Type or in any label, after splitting on '/' and ':'. This covers Shortcut's story_type="bug", Azure DevOps's WorkItemType="Bug", and label conventions like "bug", "Bug", "kind/bug", "type:bug" on Linear/GitHub/GitLab. Segment equality is used (not substring) so "debug" and "bugfix" do not match.
type ListOptions ¶
type ListOptions struct {
Project string
MaxResults int
IncludeAll bool // when false, only open/active issues are returned
UpdatedSince time.Time // when non-zero, only return issues updated after this time
}
ListOptions controls issue listing behaviour.
type Lister ¶
type Lister interface {
ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
}
Lister lists issues for a project.
type ParsedURL ¶
type ParsedURL struct {
Kind string // "jira", "github", "gitlab", "linear", "azuredevops", "shortcut"
BaseURL string // API-compatible base URL (e.g., "https://amazingcto.atlassian.net")
Key string // issue key in CLI format (e.g., "HUM-4", "owner/repo#42")
Org string // Azure DevOps org or Shortcut org slug
}
ParsedURL holds the results of parsing a tracker URL.
type Policy ¶
type Policy struct {
// contains filtered or unexported fields
}
Policy evaluates operations against configured block/confirm rules.
func NewPolicy ¶
func NewPolicy(cfg PolicyConfig) *Policy
NewPolicy creates a Policy from the given config, parsing rule strings into structured rules for fast lookup.
type PolicyConfig ¶
type PolicyConfig struct {
Block []string `mapstructure:"block"`
Confirm []string `mapstructure:"confirm"`
}
PolicyConfig holds the policies section from .humanconfig.
func LoadPolicyConfig ¶
func LoadPolicyConfig(dir string) (*PolicyConfig, error)
LoadPolicyConfig reads the policies section from .humanconfig.yaml in dir. Returns (nil, nil) when the policies section is absent or empty.
type PolicyProvider ¶
type PolicyProvider struct {
// contains filtered or unexported fields
}
PolicyProvider wraps a Provider and evaluates policy rules before delegating to the inner provider. Read-only methods always pass through.
func NewPolicyProvider ¶
func NewPolicyProvider(inner Provider, instanceName string, policy *Policy, warnFn func(string)) *PolicyProvider
NewPolicyProvider creates a PolicyProvider that evaluates policy rules before delegating write operations to the inner provider.
func (*PolicyProvider) AddComment ¶
func (*PolicyProvider) AssignIssue ¶
func (*PolicyProvider) CreateIssue ¶
func (*PolicyProvider) DeleteIssue ¶
func (pp *PolicyProvider) DeleteIssue(ctx context.Context, key string) error
func (*PolicyProvider) EditIssue ¶
func (pp *PolicyProvider) EditIssue(ctx context.Context, key string, opts EditOptions) (*Issue, error)
func (*PolicyProvider) GetCurrentUser ¶
func (pp *PolicyProvider) GetCurrentUser(ctx context.Context) (string, error)
func (*PolicyProvider) ListComments ¶
func (*PolicyProvider) ListIssues ¶
func (pp *PolicyProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
func (*PolicyProvider) ListStatuses ¶
func (*PolicyProvider) TransitionIssue ¶
type Provider ¶
type Provider interface {
Lister
Getter
Creator
Commenter
Deleter
Transitioner
Assigner
CurrentUserGetter
Editor
StatusLister
}
Provider combines all tracker operations into a single interface.
type SafeProvider ¶
type SafeProvider struct {
// contains filtered or unexported fields
}
SafeProvider wraps a Provider and blocks mutating operations. DeleteIssue, EditIssue, and TransitionIssue are blocked; read operations and assignments delegate to the inner provider.
func NewSafeProvider ¶
func NewSafeProvider(inner Provider, instanceName string) *SafeProvider
NewSafeProvider creates a SafeProvider that delegates to inner and blocks DeleteIssue with a descriptive error.
func (*SafeProvider) AddComment ¶
func (*SafeProvider) AssignIssue ¶
func (*SafeProvider) CreateIssue ¶
func (*SafeProvider) DeleteIssue ¶
func (s *SafeProvider) DeleteIssue(_ context.Context, _ string) error
func (*SafeProvider) EditIssue ¶
func (s *SafeProvider) EditIssue(_ context.Context, _ string, _ EditOptions) (*Issue, error)
func (*SafeProvider) GetCurrentUser ¶
func (s *SafeProvider) GetCurrentUser(ctx context.Context) (string, error)
func (*SafeProvider) ListComments ¶
func (*SafeProvider) ListIssues ¶
func (s *SafeProvider) ListIssues(ctx context.Context, opts ListOptions) ([]Issue, error)
func (*SafeProvider) ListStatuses ¶
func (*SafeProvider) TransitionIssue ¶
type Status ¶
type Status struct {
Name string `json:"name"`
Category Category `json:"type,omitempty"` // JSON key stays "type" for wire compatibility
}
Status represents a workflow state an issue can be in.
Name is what the user sees — tracker-specific, potentially team-specific, free-form. Category is the fixed semantic bucket (see Category doc).
type StatusLister ¶
StatusLister lists available statuses for an issue. For Jira, only valid transitions from the current state are returned. For other trackers, all statuses for the project/workflow are returned.
type TrackerStatus ¶
type TrackerStatus struct {
Name string // config entry name, e.g. "work", "amazingcto"
Kind string // tracker kind, e.g. "linear", "jira"
Label string // human-readable name, e.g. "Linear", "Jira"
Working bool // true when all required credentials are present (and not vault refs)
VaultRef bool // true when credentials are vault references (e.g. 1pw://) — unverified
Missing []string // env var names for missing credentials (empty when Working)
}
TrackerStatus describes a configured tracker entry and its credential state.
func DiagnoseTrackers ¶
func DiagnoseTrackers(dir string, unmarshal func(dir, section string, target any) error, getenv func(string) string) []TrackerStatus
DiagnoseTrackers reads all configured tracker entries and checks whether their required credentials are present. unmarshal reads a YAML section, getenv looks up environment variables.