feat: Add openspec-sync-specs and openspec-verify-change skills

- Introduced `openspec-sync-specs` skill to sync delta specs to main specs, allowing intelligent merging of requirements.
- Added `openspec-verify-change` skill to verify implementation against change artifacts, ensuring completeness, correctness, and coherence before archiving.

docs: Create CLAUDE.md for project guidance

- Added CLAUDE.md to provide an overview of the PCLI project, including build, test commands, architecture, and resource addition guidelines.

chore: Add new change and design documents for project filter in status command

- Created `.openspec.yaml`, `design.md`, `proposal.md`, and `tasks.md` for the `add-project-filter-to-status` change.
- Updated specs for CLI commands and status command to include project filtering functionality.

feat: Expand board included parsing in API client

- Added parsing for `labels`, `cardLabels`, and `cardMemberships` in the `GetBoard` response.
- Updated `ListCardsByBoard` to enrich card output with label names, enhancing usability in kanban sync workflows.
This commit is contained in:
Steve Cliff
2026-02-18 21:27:02 +00:00
parent 94dffdf8fc
commit 22d5848e1a
44 changed files with 494 additions and 77 deletions
+36 -3
View File
@@ -9,21 +9,31 @@ import (
"git.franklin.lab/steve.cliff/pcli/model"
)
func (c *Client) GetCard(ctx context.Context, id string) (*model.Card, error) {
func (c *Client) GetCard(ctx context.Context, id string) (*model.CardDetail, error) {
data, err := c.DoNoBody(ctx, "GET", fmt.Sprintf("/api/cards/%s", id))
if err != nil {
return nil, err
}
var response struct {
Item model.Card `json:"item"`
Item model.Card `json:"item"`
Included struct {
TaskLists []model.TaskList `json:"taskLists"`
Tasks []model.Task `json:"tasks"`
} `json:"included"`
}
if err := json.Unmarshal(data, &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal card response: %w", err)
}
return &response.Item, nil
result := &model.CardDetail{
Card: response.Item,
TaskLists: response.Included.TaskLists,
Tasks: response.Included.Tasks,
}
return result, nil
}
func (c *Client) CreateCard(ctx context.Context, listId string, fields map[string]any) (*model.Card, error) {
@@ -195,11 +205,34 @@ func (c *Client) ListCardsByBoard(ctx context.Context, boardId string, limit int
listNames[list.ID] = name
}
// Build label ID -> name map
labelNames := make(map[string]string)
for _, label := range board.Labels {
name := ""
if label.Name != nil {
name = *label.Name
}
labelNames[label.ID] = name
}
// Build card ID -> label names map
cardLabelNames := make(map[string][]string)
for _, cl := range board.CardLabels {
if name, ok := labelNames[cl.LabelID]; ok {
cardLabelNames[cl.CardID] = append(cardLabelNames[cl.CardID], name)
}
}
var allCards []model.CardWithList
for _, card := range board.Cards {
labels := cardLabelNames[card.ID]
if labels == nil {
labels = []string{}
}
cardWithList := model.CardWithList{
Card: card,
ListName: listNames[card.ListID],
Labels: labels,
}
allCards = append(allCards, cardWithList)