targetprocess

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2026 License: MIT Imports: 10 Imported by: 0

README

tp — Targetprocess CLI & Go SDK

A fast, no-nonsense command-line client and Go library for Targetprocess. Query work items, manage entities, and explore your instance — from the terminal or from Go code.

Built with LLM agents in mind. Works great with humans too.

Install

Homebrew:

brew install lifedraft/tap/tp

Go:

go install github.com/lifedraft/targetprocess-cli/cmd/tp@latest

Or grab a binary from the releases page.

Setup

tp config set domain https://your-instance.tpondemand.com
tp config set token your-access-token

Config is stored in ~/.config/tp/config.yaml. You can also use environment variables (TP_DOMAIN, TP_TOKEN) which take precedence over the file.

How it works

The CLI wraps both the v1 and v2 Targetprocess APIs behind a handful of commands:

  • tp show <id> — Show an entity by ID (auto-detects type).
  • tp search <type> — Search for entities with filters and presets.
  • tp create <type> <name> — Create a new entity.
  • tp update <id> — Update an existing entity.
  • tp comment — List, add, or delete comments on entities.
  • tp query — The power tool. Query any entity type using TP's v2 query language with filtering, projections, and aggregations.
  • tp inspect — Explore the API. List entity types, browse properties, discover what's available.
  • tp api — Escape hatch. Hit any API endpoint directly.
  • tp cheatsheet — Print a compact reference card with syntax and examples.
  • tp bug-report — Print diagnostic info for bug reports, or open a pre-filled GitHub issue.

Auto-resolution: Entity types are resolved automatically — userstory, UserStories, story, and us all resolve to UserStory. Common command synonyms also work: tp gettp show, tp findtp search, tp edittp update. You can even skip the subcommand entirely: tp 341079 is the same as tp show 341079.

Quick examples

# Show an entity by ID
tp show 341079
tp 341079                  # shorthand — same thing

# Search with presets (entity types auto-resolve)
tp search UserStory --preset open
tp search story --preset open     # alias works too
tp search bugs --preset highPriority

# Create a story
tp create UserStory "Implement dark mode" --project-id 42

# Update an entity
tp update 12345 --name "New title" --state-id 100

# Comments
tp comment list 341079
tp comment add 341079 "Looks good, @timo"

# Power queries with v2 syntax
tp query Bug -w 'entityState.isFinal!=true' -s 'id,name,priority.name as priority'
tp query Assignable -s 'id,name,entityType.name as type,entityState.name as state' \
  -w 'teamIteration!=null'
tp query Feature -s 'id,name,userStories.count as total,userStories.where(entityState.isFinal==true).count as done'

# Raw API access
tp api GET '/api/v1/Users?take=10'

All commands support --output json for structured output.

LLM agent support

This CLI was designed to be used by AI agents (Claude, GPT, etc.) as a tool for interacting with Targetprocess. A few things make this work well:

  • tp cheatsheet outputs a compact reference with full query syntax, entity types, and examples — perfect for stuffing into a system prompt. Add --output json for structured data.
  • tp inspect discover lets agents explore available entity types and their properties at runtime, so they don't need upfront knowledge of your TP schema.
  • Error messages are teaching moments. Common query mistakes (like writing is null instead of ==null) get caught with suggestions for the correct syntax.
  • --dry-run on queries shows the URL that would be called without executing it — useful for verification steps.
  • JSON output everywhere makes parsing straightforward.

Go SDK

The module doubles as an importable Go library with a typed client, generics, and structured entity types.

go get github.com/lifedraft/targetprocess-cli@latest
import tp "github.com/lifedraft/targetprocess-cli"
Quick start
// Create a client from config (~/.config/tp/config.yaml, env vars, keyring)
c, err := tp.NewClientFromConfig()

// Or provide credentials directly
c, err := tp.NewClient("yourcompany.tpondemand.com", "your-token")
Three layers of type safety

1. Typed CRUD with known entities — full struct types, compile-time safety:

// Get a bug by ID — returns *tp.Bug with typed fields
bug, err := tp.Get[tp.Bug](ctx, c, 12345)
fmt.Println(bug.Name)              // string
fmt.Println(bug.EntityState.Name)  // "Open"
fmt.Println(bug.Severity.Name)     // "Critical"

// Search for user stories
stories, err := tp.Search[tp.UserStory](ctx, c, "Project.Name=='MyProject'",
    tp.WithTake(10),
    tp.WithOrderBy("createDate desc"),
)

// Create and update
story, err := tp.Create[tp.UserStory](ctx, c, tp.Entity{"Name": "New feature", "Project": map[string]any{"Id": 42}})
updated, err := tp.Update[tp.Feature](ctx, c, 789, tp.Entity{"Name": "Renamed"})

Available entity types: UserStory, Bug, Task, Feature, Epic, Request, Comment.

2. Typed v2 queries with custom structs — define your own result shape:

type SprintItem struct {
    ID       int     `json:"id"`
    Name     string  `json:"name"`
    State    string  `json:"state"`
    Effort   float64 `json:"effort"`
    Assignee string  `json:"assignee"`
}

result, err := tp.Query[SprintItem](ctx, c, "UserStory", tp.QueryParams{
    Where:  "TeamIteration.Name=='Sprint 42'",
    Select: "id,name,entityState.name as state,effort,owner.fullName as assignee",
})
for _, item := range result.Items {
    fmt.Println(item.Name, item.State)  // fully typed
}
fmt.Println(result.Next)  // pagination URL (if any)

3. Untyped for dynamic usemap[string]any when you don't know the type:

entity, err := c.Get(ctx, "UserStory", 12345)
fmt.Println(entity["Name"])

items, err := c.Search(ctx, "Assignable", "name.contains('login')",
    tp.WithSelect("id", "name", "entityType.name as type"),
    tp.WithTake(50),
)
Error handling
var apiErr *tp.APIError
if errors.As(err, &apiErr) {
    fmt.Println(apiErr.StatusCode)  // 404
    fmt.Println(apiErr.Body)        // raw error body
}
Entity type resolution
// Resolve aliases, plurals, case-insensitive
tp.NormalizeType("stories")    // "UserStory"
tp.NormalizeType("us")         // "UserStory"
tp.NormalizeType("bug")        // "Bug"
tp.NormalizeType("sprint")     // "Iteration"

// Resolve type from an ID via the API
typeName, err := c.ResolveType(ctx, 12345)  // "UserStory"
Metadata
// List all entity types
types, err := c.MetaTypes(ctx)
for _, t := range types {
    fmt.Println(t.Name, t.Description)
}

// Inspect fields of an entity type
fields, err := c.MetaFields(ctx, "UserStory")
for _, f := range fields {
    fmt.Printf("%s (%s, %s)\n", f.Name, f.Type, f.Kind)  // "Id (Int32, value)"
}
Raw API access
data, err := c.Raw(ctx, "GET", "/api/v1/Users?take=5", nil)
Client options
c, err := tp.NewClient("domain.tpondemand.com", "token",
    tp.WithHTTPClient(customHTTPClient),
    tp.WithDebug(true),
)

Uninstall

Homebrew:

brew uninstall tp

Go (manual):

rm "$(which tp)"

To also remove your config: rm -rf ~/.config/tp

Found a bug?

Run tp bug-report to grab your environment info, or go straight to filing an issue:

tp bug-report --mode open

This opens a GitHub issue with your environment details already filled in — you just describe what went wrong.

License

MIT

Documentation

Overview

Package targetprocess provides a typed Go client for the Targetprocess REST API.

The package offers three layers of type safety:

  • Generic functions with known entity types for full compile-time safety
  • Generic functions with user-defined structs for typed v2 queries
  • Untyped Client methods returning map[string]any for dynamic use

Quick start:

c, err := targetprocess.NewClient("yourcompany.tpondemand.com", "your-token")
story, err := targetprocess.Get[targetprocess.UserStory](ctx, c, 12345)
fmt.Println(story.Name, story.EntityState.Name)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Create

func Create[T Typed](ctx context.Context, c *Client, fields Entity) (*T, error)

Create creates a new entity of type T and returns the typed result.

func DefaultConfigPath

func DefaultConfigPath() string

DefaultConfigPath returns the default config file path (~/.config/tp/config.yaml).

func Get

func Get[T Typed](ctx context.Context, c *Client, id int, include ...string) (*T, error)

Get retrieves a single entity by ID with full type safety. The entity type is inferred from T (e.g., Get[UserStory] hits /api/v1/UserStorys/{id}). Optional include fields control which nested objects are returned.

func NormalizeType

func NormalizeType(input string) string

NormalizeType resolves a user-provided entity type string to its canonical Targetprocess API form. It handles case-insensitive matching, plural stripping, and common aliases (e.g., "stories" -> "UserStory", "bug" -> "Bug").

Unknown types pass through unchanged.

func Search[T Typed](ctx context.Context, c *Client, where string, opts ...SearchOption) ([]T, error)

Search queries entities of type T using the v2 API.

func Update

func Update[T Typed](ctx context.Context, c *Client, id int, fields Entity) (*T, error)

Update modifies an existing entity of type T and returns the typed result.

Types

type APIError

type APIError = api.APIError

APIError represents an HTTP error response from the Targetprocess API. Use errors.As to extract it from returned errors:

var apiErr *targetprocess.APIError
if errors.As(err, &apiErr) {
    fmt.Println(apiErr.StatusCode, apiErr.Body)
}

type BaseEntity

type BaseEntity struct {
	ID                  int             `json:"Id"`
	Name                string          `json:"Name,omitempty"`
	Description         string          `json:"Description,omitempty"`
	ResourceType        string          `json:"ResourceType,omitempty"`
	EntityState         *EntityStateRef `json:"EntityState,omitempty"`
	EntityType          *Ref            `json:"EntityType,omitempty"`
	Project             *Ref            `json:"Project,omitempty"`
	Priority            *PriorityRef    `json:"Priority,omitempty"`
	Owner               *UserRef        `json:"Owner,omitempty"`
	Creator             *UserRef        `json:"Creator,omitempty"`
	LastEditor          *UserRef        `json:"LastEditor,omitempty"`
	Team                *Ref            `json:"Team,omitempty"`
	TeamIteration       *Ref            `json:"TeamIteration,omitempty"`
	Iteration           *Ref            `json:"Iteration,omitempty"`
	Release             *Ref            `json:"Release,omitempty"`
	ResponsibleTeam     *Ref            `json:"ResponsibleTeam,omitempty"`
	Tags                string          `json:"Tags,omitempty"`
	CreateDate          string          `json:"CreateDate,omitempty"`
	ModifyDate          string          `json:"ModifyDate,omitempty"`
	StartDate           string          `json:"StartDate,omitempty"`
	EndDate             string          `json:"EndDate,omitempty"`
	PlannedStartDate    string          `json:"PlannedStartDate,omitempty"`
	PlannedEndDate      string          `json:"PlannedEndDate,omitempty"`
	LastStateChangeDate string          `json:"LastStateChangeDate,omitempty"`
	Effort              float64         `json:"Effort,omitempty"`
	EffortCompleted     float64         `json:"EffortCompleted,omitempty"`
	EffortToDo          float64         `json:"EffortToDo,omitempty"`
	TimeSpent           float64         `json:"TimeSpent,omitempty"`
	TimeRemain          float64         `json:"TimeRemain,omitempty"`
	Progress            float64         `json:"Progress,omitempty"`
	NumericPriority     float64         `json:"NumericPriority,omitempty"`
	EntityVersion       int64           `json:"EntityVersion,omitempty"`
	Units               string          `json:"Units,omitempty"`
	CustomFields        []CustomField   `json:"CustomFields,omitempty"`
}

BaseEntity contains fields common to most Targetprocess work items. Known entity types embed this struct.

func (BaseEntity) GetID

func (e BaseEntity) GetID() int

GetID returns the entity's numeric ID.

type Bug

type Bug struct {
	BaseEntity
	Severity  *Ref `json:"Severity,omitempty"`
	UserStory *Ref `json:"UserStory,omitempty"`
}

Bug represents a Targetprocess bug.

func (Bug) TPResourceType

func (Bug) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the Targetprocess API client.

func NewClient

func NewClient(domain, token string, opts ...Option) (*Client, error)

NewClient creates a new Targetprocess API client. The domain should be your Targetprocess subdomain (e.g., "yourcompany.tpondemand.com").

func NewClientFromConfig

func NewClientFromConfig(opts ...Option) (*Client, error)

NewClientFromConfig creates a Client using configuration from disk/environment. It loads config from the default path, consulting env vars and keyring.

func (*Client) Create

func (c *Client) Create(ctx context.Context, entityType string, fields Entity) (Entity, error)

Create creates a new entity of the given type.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, entityType string, id int) error

Delete removes an entity by type and ID.

func (*Client) Get

func (c *Client) Get(ctx context.Context, entityType string, id int, include ...string) (Entity, error)

Get retrieves a single entity by type and ID. Optional include fields control which nested objects are returned (v1 API).

func (*Client) MetaFields

func (c *Client) MetaFields(ctx context.Context, entityType string) ([]FieldInfo, error)

MetaFields returns the fields/properties of an entity type.

func (*Client) MetaTypes

func (c *Client) MetaTypes(ctx context.Context) ([]TypeInfo, error)

MetaTypes returns all entity types available in the Targetprocess instance.

func (*Client) Query

func (c *Client) Query(ctx context.Context, entityType string, params QueryParams) (json.RawMessage, error)

Query executes a v2 API query and returns raw JSON bytes.

func (*Client) QueryEntity

func (c *Client) QueryEntity(ctx context.Context, entityType string, id int, selectExpr string) (json.RawMessage, error)

QueryEntity queries a single entity by ID via the v2 API.

func (*Client) Raw

func (c *Client) Raw(ctx context.Context, method, path string, body io.Reader) ([]byte, error)

Raw makes a raw API request. The path should start with / and can include query parameters. This is an escape hatch for endpoints not covered by other methods.

func (*Client) ResolveType

func (c *Client) ResolveType(ctx context.Context, id int) (string, error)

ResolveType determines the entity type for a given ID via the API.

func (*Client) Search

func (c *Client) Search(ctx context.Context, entityType, where string, opts ...SearchOption) ([]Entity, error)

Search queries entities using the v2 API and returns untyped results.

func (*Client) Update

func (c *Client) Update(ctx context.Context, entityType string, id int, fields Entity) (Entity, error)

Update modifies an existing entity.

type Comment

type Comment struct {
	ID           int      `json:"Id"`
	Description  string   `json:"Description,omitempty"`
	CreateDate   string   `json:"CreateDate,omitempty"`
	Owner        *UserRef `json:"Owner,omitempty"`
	General      *Ref     `json:"General,omitempty"`
	ResourceType string   `json:"ResourceType,omitempty"`
}

Comment represents a Targetprocess comment on an entity.

func (Comment) GetID

func (c Comment) GetID() int

GetID returns the comment's numeric ID.

func (Comment) TPResourceType

func (Comment) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

type Config

type Config struct {
	Domain string
	Token  string
}

Config holds Targetprocess connection settings.

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig loads configuration from the given path (or default if empty). Environment variables TP_DOMAIN and TP_TOKEN override file values. The OS keyring is also consulted for the token.

type CustomField

type CustomField struct {
	Name  string `json:"Name"`
	Type  string `json:"Type,omitempty"`
	Value any    `json:"Value"`
}

CustomField represents a custom field value on an entity.

type Entity

type Entity = map[string]any

Entity represents a generic TP entity as a flexible map (untyped layer).

type EntityStateRef

type EntityStateRef struct {
	ID              int    `json:"Id"`
	Name            string `json:"Name,omitempty"`
	NumericPriority int    `json:"NumericPriority,omitempty"`
	ResourceType    string `json:"ResourceType,omitempty"`
}

EntityStateRef is a reference to an entity's workflow state.

type Epic

type Epic struct {
	BaseEntity
}

Epic represents a Targetprocess epic.

func (Epic) TPResourceType

func (Epic) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

type Feature

type Feature struct {
	BaseEntity
	Epic *Ref `json:"Epic,omitempty"`
}

Feature represents a Targetprocess feature.

func (Feature) TPResourceType

func (Feature) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

type FieldInfo

type FieldInfo struct {
	Name        string
	Type        string
	Kind        FieldKind
	Required    bool
	Readable    bool
	Writable    bool
	Description string
}

FieldInfo describes a field/property of an entity type.

type FieldKind

type FieldKind string

FieldKind categorizes a field as a value, reference, or collection.

const (
	FieldKindValue      FieldKind = "value"
	FieldKindReference  FieldKind = "reference"
	FieldKindCollection FieldKind = "collection"
)

type Identifiable

type Identifiable interface {
	GetID() int
}

Identifiable is implemented by any entity that has a numeric ID.

type Option

type Option func(*clientConfig)

Option configures a Client.

func WithDebug

func WithDebug(debug bool) Option

WithDebug enables debug logging to stderr.

func WithHTTPClient

func WithHTTPClient(c *http.Client) Option

WithHTTPClient sets a custom HTTP client for the API client.

type PriorityRef

type PriorityRef struct {
	ID           int    `json:"Id"`
	Name         string `json:"Name,omitempty"`
	Importance   int    `json:"Importance,omitempty"`
	ResourceType string `json:"ResourceType,omitempty"`
}

PriorityRef is a reference to a priority level.

type QueryParams

type QueryParams struct {
	Where   string
	Select  string
	OrderBy string
	Take    int
	Skip    int
}

QueryParams holds parameters for a v2 API query.

type Ref

type Ref struct {
	ID           int    `json:"Id"`
	Name         string `json:"Name,omitempty"`
	ResourceType string `json:"ResourceType,omitempty"`
}

Ref is a lightweight reference to a related entity.

type Request

type Request struct {
	BaseEntity
}

Request represents a Targetprocess request.

func (Request) TPResourceType

func (Request) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

type Result

type Result[T any] struct {
	Items []T    `json:"items"`
	Next  string `json:"next,omitempty"`
}

Result wraps a v2 API response containing a paginated collection of items.

func Query

func Query[T any](ctx context.Context, c *Client, entityType string, params QueryParams) (*Result[T], error)

Query executes a v2 query and deserializes items into the provided type T. T can be any struct — use struct tags matching the v2 select expression. Unlike other generic functions, entityType must be passed explicitly because T may be a user-defined struct that does not implement Typed.

Example:

type SprintItem struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    State string `json:"state"`
}
result, err := targetprocess.Query[SprintItem](ctx, c, "UserStory", targetprocess.QueryParams{
    Where:  "TeamIteration.Name=='Sprint 42'",
    Select: "id,name,entityState.name as state",
})

type SearchOption

type SearchOption func(*searchOpts)

SearchOption configures a Search request.

func WithOrderBy

func WithOrderBy(expr string) SearchOption

WithOrderBy sets the sort expression (e.g., "createDate desc").

func WithSelect

func WithSelect(fields ...string) SearchOption

WithSelect sets the fields to return in the v2 query select clause. Example: WithSelect("id", "name", "entityState.name as state")

func WithTake

func WithTake(n int) SearchOption

WithTake sets the maximum number of results to return.

type Task

type Task struct {
	BaseEntity
	UserStory *Ref `json:"UserStory,omitempty"`
}

Task represents a Targetprocess task.

func (Task) TPResourceType

func (Task) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

type TypeInfo

type TypeInfo struct {
	Name        string
	Description string
}

TypeInfo describes an entity type available in the Targetprocess API.

type Typed

type Typed interface {
	Identifiable
	TPResourceType() string
}

Typed is implemented by entities that know their Targetprocess resource type. Generic functions use this to infer the API path automatically.

type UserRef

type UserRef struct {
	ID           int    `json:"Id"`
	FirstName    string `json:"FirstName,omitempty"`
	LastName     string `json:"LastName,omitempty"`
	FullName     string `json:"FullName,omitempty"`
	Login        string `json:"Login,omitempty"`
	ResourceType string `json:"ResourceType,omitempty"`
}

UserRef is a reference to a Targetprocess user.

type UserStory

type UserStory struct {
	BaseEntity
	Feature *Ref `json:"Feature,omitempty"`
}

UserStory represents a Targetprocess user story.

func (UserStory) TPResourceType

func (UserStory) TPResourceType() string

TPResourceType returns the canonical Targetprocess resource type name.

Directories

Path Synopsis
cmd
tp command
tp-capture command
Command tp-capture records real Targetprocess API responses for use as test fixtures.
Command tp-capture records real Targetprocess API responses for use as test fixtures.
internal
api

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL