Merge branch 'main' into client
This commit is contained in:
@@ -34,7 +34,7 @@ For raw SQL access (rare), use the wrapper scripts:
|
||||
|
||||
### List tickets
|
||||
```bash
|
||||
db/connectors/ticket list [--status S] [--priority P] [--epic N] [--sprint N] [--assigned A]
|
||||
db/connectors/ticket list [--status S] [--priority P] [--epic N] [--sprint N] [--assigned A] [--team T]
|
||||
```
|
||||
|
||||
### Show ticket detail
|
||||
@@ -45,7 +45,7 @@ Returns full ticket with children, blockers, and dependents.
|
||||
|
||||
### Create ticket
|
||||
```bash
|
||||
db/connectors/ticket create <type> <title> [--parent N] [--priority P] [--decision D]
|
||||
db/connectors/ticket create <type> <title> [--parent N] [--priority P] [--decision D] [--team T]
|
||||
```
|
||||
Types: `initiative`, `epic`, `story`, `task`, `bug`
|
||||
Priorities: `critical`, `high`, `medium`, `low`
|
||||
@@ -63,6 +63,12 @@ db/connectors/ticket assign <id> <agent>
|
||||
db/connectors/ticket unassign <id>
|
||||
```
|
||||
|
||||
### Team assignment
|
||||
```bash
|
||||
db/connectors/ticket team <id> <teams>
|
||||
```
|
||||
Teams are comma-separated, e.g. `server`, `client`, `server,client`.
|
||||
|
||||
### Sprint management
|
||||
```bash
|
||||
db/connectors/ticket sprint [--active]
|
||||
|
||||
+3
-3
@@ -6,6 +6,9 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- `ticket team` command and `--team` filter — comma-separated team assignment for tickets (server, client, joint, content)
|
||||
|
||||
### Fixed
|
||||
- `start-sprint` skill uses `git rev-parse --show-toplevel` for worktree-safe absolute paths — fixes "No such file or directory" errors on team branches
|
||||
|
||||
@@ -126,12 +129,9 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
- Rejected alternatives R-004 through R-010 documented (pure Bevy, pure Godot, GDExtension, C++ GDExtension, Fyrox, custom framework, protobuf)
|
||||
|
||||
### Fixed
|
||||
<<<<<<< HEAD
|
||||
- create-skill references to nonexistent init_skill.py and package_skill.py scripts
|
||||
=======
|
||||
- SimBridge wire format: inputs now batch-encoded as Vec\<PlayerInput\> array per server protocol (was sending individual inputs per frame)
|
||||
- SimBridge server args: positional address format ("127.0.0.1:9876") matching server CLI, port default corrected to 9876
|
||||
>>>>>>> origin/client
|
||||
- Wire format mismatch: LocalBridge now uses rmp_serde::to_vec_named() (named maps) matching client Protocol.gd expectations
|
||||
- EOF on bridge read now returns BridgeError::Transport for disconnect detection instead of empty Vec
|
||||
- WalkabilityMap rewritten from flat Vec to chunk-based HashMap per D-012 architecture (Tyre review)
|
||||
|
||||
+32
-13
@@ -3,17 +3,18 @@
|
||||
Ticket CLI — ergonomic interface to the project ticketing database.
|
||||
|
||||
Usage:
|
||||
ticket list [--status S] [--priority P] [--epic N] [--sprint N] [--assigned A]
|
||||
ticket list [--status S] [--priority P] [--epic N] [--sprint N] [--assigned A] [--team T]
|
||||
ticket show <id>
|
||||
ticket done <id> [<id> ...]
|
||||
ticket status <id> <new_status>
|
||||
ticket assign <id> <agent>
|
||||
ticket unassign <id>
|
||||
ticket team <id> <teams>
|
||||
ticket sprint [--active]
|
||||
ticket sprint assign <id> <sprint_id>
|
||||
ticket deps <id>
|
||||
ticket search <keyword>
|
||||
ticket create <type> <title> [--parent N] [--priority P] [--decision D]
|
||||
ticket create <type> <title> [--parent N] [--priority P] [--decision D] [--team T]
|
||||
ticket epics [--status S]
|
||||
ticket children <id>
|
||||
ticket count [--status S]
|
||||
@@ -112,7 +113,7 @@ def parse_flags(args, known_flags):
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_list(conn, args):
|
||||
flags, _ = parse_flags(args, ["status", "priority", "epic", "sprint", "assigned"])
|
||||
flags, _ = parse_flags(args, ["status", "priority", "epic", "sprint", "assigned", "team"])
|
||||
conditions = []
|
||||
params = []
|
||||
if "status" in flags:
|
||||
@@ -130,9 +131,13 @@ def cmd_list(conn, args):
|
||||
if "assigned" in flags:
|
||||
conditions.append("t.assigned_to = ?")
|
||||
params.append(flags["assigned"])
|
||||
if "team" in flags:
|
||||
# Match exact team name within comma-separated list
|
||||
conditions.append("(',' || t.team || ',' LIKE '%,' || ? || ',%')")
|
||||
params.append(flags["team"])
|
||||
where = " AND ".join(conditions) if conditions else "1=1"
|
||||
sql = f"""SELECT t.id, t.type, t.title, t.status, t.priority, t.assigned_to,
|
||||
t.parent_id, t.sprint_id
|
||||
t.team, t.parent_id, t.sprint_id
|
||||
FROM tickets t WHERE {where}
|
||||
ORDER BY
|
||||
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1
|
||||
@@ -184,6 +189,8 @@ def _print_brief(tickets):
|
||||
parts = [f"{t['type']}", f"P:{t['priority']}", f"S:{t['status']}"]
|
||||
if t.get("assigned_to"):
|
||||
parts.append(f"@{t['assigned_to']}")
|
||||
if t.get("team"):
|
||||
parts.append(f"Team:{t['team']}")
|
||||
if t.get("parent_id"):
|
||||
parts.append(f"Epic:#{t['parent_id']} ({t.get('parent_title', '?')})")
|
||||
if t.get("sprint_id"):
|
||||
@@ -265,25 +272,26 @@ def cmd_deps(conn, ticket_id):
|
||||
|
||||
|
||||
def cmd_search(conn, keyword):
|
||||
rows = query(conn, """SELECT id, type, title, status, priority, assigned_to
|
||||
rows = query(conn, """SELECT id, type, title, status, priority, assigned_to, team
|
||||
FROM tickets WHERE title LIKE ? OR description LIKE ?
|
||||
ORDER BY id""", (f"%{keyword}%", f"%{keyword}%"))
|
||||
out({"ok": True, "count": len(rows), "rows": rows})
|
||||
|
||||
|
||||
def cmd_create(conn, args):
|
||||
flags, positional = parse_flags(args, ["parent", "priority", "decision"])
|
||||
flags, positional = parse_flags(args, ["parent", "priority", "decision", "team"])
|
||||
if len(positional) < 2:
|
||||
out({"ok": False, "error": "Usage: ticket create <type> <title> [--parent N] [--priority P] [--decision D]"})
|
||||
out({"ok": False, "error": "Usage: ticket create <type> <title> [--parent N] [--priority P] [--decision D] [--team T]"})
|
||||
return
|
||||
ticket_type = positional[0]
|
||||
title = " ".join(positional[1:])
|
||||
parent_id = int(flags["parent"]) if "parent" in flags else None
|
||||
priority = flags.get("priority", "medium")
|
||||
decision_ref = flags.get("decision")
|
||||
team = flags.get("team")
|
||||
conn.execute(
|
||||
"INSERT INTO tickets (type, title, parent_id, priority, decision_ref) VALUES (?, ?, ?, ?, ?)",
|
||||
(ticket_type, title, parent_id, priority, decision_ref))
|
||||
"INSERT INTO tickets (type, title, parent_id, priority, decision_ref, team) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(ticket_type, title, parent_id, priority, decision_ref, team))
|
||||
conn.commit()
|
||||
last_id = query(conn, "SELECT last_insert_rowid() as id")[0]["id"]
|
||||
out({"ok": True, "id": last_id, "title": title})
|
||||
@@ -297,7 +305,7 @@ def cmd_epics(conn, args):
|
||||
conditions.append("t.status = ?")
|
||||
params.append(flags["status"])
|
||||
where = " AND ".join(conditions)
|
||||
rows = query(conn, f"""SELECT t.id, t.title, t.status, t.priority, t.assigned_to,
|
||||
rows = query(conn, f"""SELECT t.id, t.title, t.status, t.priority, t.assigned_to, t.team,
|
||||
COUNT(c.id) as child_count,
|
||||
SUM(CASE WHEN c.status='done' THEN 1 ELSE 0 END) as done_count
|
||||
FROM tickets t LEFT JOIN tickets c ON c.parent_id = t.id
|
||||
@@ -308,11 +316,16 @@ def cmd_epics(conn, args):
|
||||
|
||||
|
||||
def cmd_children(conn, ticket_id):
|
||||
rows = query(conn, """SELECT id, type, title, status, priority, assigned_to
|
||||
rows = query(conn, """SELECT id, type, title, status, priority, assigned_to, team
|
||||
FROM tickets WHERE parent_id = ? ORDER BY id""", (int(ticket_id),))
|
||||
out({"ok": True, "count": len(rows), "parent_id": int(ticket_id), "rows": rows})
|
||||
|
||||
|
||||
def cmd_team(conn, ticket_id, teams):
|
||||
updated = execute(conn, "UPDATE tickets SET team=?, updated_at=datetime('now') WHERE id=?", (teams, int(ticket_id)))
|
||||
out({"ok": True, "updated": updated, "id": int(ticket_id), "team": teams})
|
||||
|
||||
|
||||
def cmd_count(conn, args):
|
||||
flags, _ = parse_flags(args, ["status"])
|
||||
if "status" in flags:
|
||||
@@ -329,17 +342,18 @@ def cmd_count(conn, args):
|
||||
HELP = """ticket — project ticket CLI
|
||||
|
||||
Usage:
|
||||
ticket list [--status S] [--priority P] [--epic N] [--sprint N] [--assigned A]
|
||||
ticket list [--status S] [--priority P] [--epic N] [--sprint N] [--assigned A] [--team T]
|
||||
ticket show [--brief] <id> [<id>...] Full ticket detail (--brief for summary)
|
||||
ticket done <id> [<id> ...] Mark tickets as done
|
||||
ticket status <id> <new_status> Change ticket status
|
||||
ticket assign <id> <agent> Assign ticket to agent/branch
|
||||
ticket unassign <id> Remove assignment
|
||||
ticket team <id> <teams> Set team(s) (comma-separated, e.g. server,client)
|
||||
ticket sprint [--active] List sprints
|
||||
ticket sprint assign <id> <sprint> Assign ticket to sprint
|
||||
ticket deps <id> Show ticket dependencies
|
||||
ticket search <keyword> Search tickets by title/description
|
||||
ticket create <type> <title> [--parent N] [--priority P] [--decision D]
|
||||
ticket create <type> <title> [--parent N] [--priority P] [--decision D] [--team T]
|
||||
ticket epics [--status S] List epics with child counts
|
||||
ticket children <id> List children of a ticket
|
||||
ticket count [--status S] Count tickets by status"""
|
||||
@@ -390,6 +404,11 @@ def main():
|
||||
out({"ok": False, "error": "Usage: ticket unassign <id>"})
|
||||
else:
|
||||
cmd_unassign(conn, args[0])
|
||||
elif cmd == "team":
|
||||
if len(args) < 2:
|
||||
out({"ok": False, "error": "Usage: ticket team <id> <teams>"})
|
||||
else:
|
||||
cmd_team(conn, args[0], args[1])
|
||||
elif cmd == "sprint":
|
||||
cmd_sprint(conn, args)
|
||||
elif cmd == "deps":
|
||||
|
||||
@@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS tickets (
|
||||
status TEXT NOT NULL DEFAULT 'backlog' CHECK(status IN ('backlog', 'ready', 'in_progress', 'review', 'done', 'cancelled')),
|
||||
priority TEXT DEFAULT 'medium' CHECK(priority IN ('critical', 'high', 'medium', 'low')),
|
||||
assigned_to TEXT,
|
||||
team TEXT, -- comma-separated team names, e.g. 'server', 'client,server'
|
||||
decision_ref TEXT, -- e.g. 'D-010' or 'Q-001'
|
||||
sprint_id INTEGER REFERENCES sprints(id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
@@ -58,6 +59,7 @@ CREATE INDEX IF NOT EXISTS idx_tickets_parent ON tickets(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_sprint ON tickets(sprint_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_decision ON tickets(decision_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
||||
CREATE INDEX IF NOT EXISTS idx_history_ticket ON ticket_history(ticket_id);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user