Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions scripts/check-windows-footguns.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,14 @@ def print_rules() -> None:


def main(argv: list[str]) -> int:
# Windows terminals default to cp1252, which can't encode the ✓/✗
# characters used in the output. Reconfigure streams to UTF-8 so the
# script works correctly on the very platform it is designed to help.
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
if hasattr(sys.stderr, "reconfigure"):
sys.stderr.reconfigure(encoding="utf-8")

args = parse_args(argv)

if args.list:
Expand Down
84 changes: 83 additions & 1 deletion skills/productivity/linear/scripts/linear_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,54 @@ def _resolve_team_id(key_or_name: str) -> str | None:
return None


def _resolve_label_id(name: str, team_id: str) -> str | None:
"""Map a label name to UUID within a team (case-insensitive), paginating if needed."""
q = """query($id: String!, $after: String) {
team(id: $id) {
labels(first: 250, after: $after) {
nodes { id name }
pageInfo { hasNextPage endCursor }
}
}
}"""
nl = name.lower()
cursor = None
while True:
labels = gql(q, {"id": team_id, "after": cursor}).get("team", {}).get("labels", {})
for label in labels.get("nodes", []):
if label["name"].lower() == nl:
return label["id"]
page_info = labels.get("pageInfo", {})
if not page_info.get("hasNextPage"):
break
cursor = page_info.get("endCursor")
return None


def _resolve_member_id(name: str, team_id: str) -> str | None:
"""Map a member display name or username to UUID within a team (case-insensitive), paginating if needed."""
q = """query($id: String!, $after: String) {
team(id: $id) {
members(first: 250, after: $after) {
nodes { id name displayName }
pageInfo { hasNextPage endCursor }
}
}
}"""
nl = name.lower()
cursor = None
while True:
members = gql(q, {"id": team_id, "after": cursor}).get("team", {}).get("members", {})
for member in members.get("nodes", []):
if member["name"].lower() == nl or (member.get("displayName") or "").lower() == nl:
return member["id"]
page_info = members.get("pageInfo", {})
if not page_info.get("hasNextPage"):
break
cursor = page_info.get("endCursor")
return None


def cmd_list_projects(args: argparse.Namespace) -> None:
if args.team:
tid = _resolve_team_id(args.team)
Expand Down Expand Up @@ -229,7 +277,18 @@ def cmd_create_issue(args: argparse.Namespace) -> None:
inp["priority"] = args.priority
if args.parent:
inp["parentId"] = args.parent
# TODO: label + assignee name->id lookup (omitted for v1 brevity)
if args.label:
lid = _resolve_label_id(args.label, tid)
if lid is None:
sys.stderr.write(f"Label '{args.label}' not found in team '{args.team}'.\n")
sys.exit(1)
inp["labelIds"] = [lid]
if args.assignee:
aid = _resolve_member_id(args.assignee, tid)
if aid is None:
sys.stderr.write(f"Assignee '{args.assignee}' not found in team '{args.team}'.\n")
sys.exit(1)
inp["assigneeId"] = aid

q = """mutation($input: IssueCreateInput!) {
issueCreate(input: $input) {
Expand All @@ -247,6 +306,29 @@ def cmd_update_issue(args: argparse.Namespace) -> None:
inp["description"] = args.description
if args.priority is not None:
inp["priority"] = args.priority
if args.label or args.assignee:
# Need the team id to resolve label/member names
get_q = """query($id: String!) { issue(id: $id) { team { id } } }"""
issue = gql(get_q, {"id": args.identifier}).get("issue")
if not issue:
sys.stderr.write(f"Issue not found: {args.identifier}\n")
sys.exit(1)
tid = (issue.get("team") or {}).get("id")
if not tid:
sys.stderr.write(f"Could not determine team for issue: {args.identifier}\n")
sys.exit(1)
if args.label:
lid = _resolve_label_id(args.label, tid)
if lid is None:
sys.stderr.write(f"Label '{args.label}' not found in this issue's team.\n")
sys.exit(1)
inp["labelIds"] = [lid]
if args.assignee:
aid = _resolve_member_id(args.assignee, tid)
if aid is None:
sys.stderr.write(f"Assignee '{args.assignee}' not found in this issue's team.\n")
sys.exit(1)
inp["assigneeId"] = aid
if not inp:
sys.stderr.write("No update fields provided.\n")
sys.exit(1)
Expand Down