Coding Net

Query and operate on Tencent Coding DevOps platform (e.coding.net) data — iterations, issues (requirements/defects/tasks), and team members — including creat...

wangyin717

@wangyin717

What This Skill Does

Query and operate on Tencent Coding DevOps platform data including iterations, issues (requirements/defects/tasks), and team members. Supports creating requirements and defects through a Python script interface that requires a CODING_TOKEN environment variable.

Replaces manual navigation of the Coding.net web interface by providing programmatic access to iterations, issues, and team member data through a structured API.

When to Use It

  • List all iterations in a Coding.net project to track sprint progress
  • Query open defects assigned to a specific team member
  • Create a new requirement in the current iteration
  • Retrieve team member list with IDs for assignment operations
  • Get custom fields from issues for reporting or export
  • Describe a single issue's full details by its ID

Install

$ openclaw skills install @wangyin717/coding-net

Coding Open API Skill

Bootstrap (must run at the start of every conversation)

Step 1: Collect the token

Check whether the user's message already includes a token. If not, check the environment variable:

python3 -c "import os; print('set' if os.environ.get('CODING_TOKEN') else 'not set')"

If neither is available, ask the user: "Please provide your Coding personal access token (Bearer Token)."

Step 2: Validate the token and confirm the team

import sys; sys.path.insert(0, "path/to/coding-net/scripts")
from core import bootstrap
result = bootstrap(token="<token>")
if result["error"]:
    print("✗", result["error"])
else:
    t = result["team"]
    print(f"✓ Token valid  Team: {t['name']}  ({t['host']})")

Step 3: Confirm the project identifier

Let the user know the team has been verified, then ask: "Please provide the project URL or project identifier. The URL format is https://<team>.coding.net/p/<project-identifier>/, where the project identifier is the part after /p/ (e.g. biaopin-swiftagent)."

⚠️ Never guess the project name. Coding.net distinguishes between "display name" and "project identifier" — the API only accepts the identifier. The user saying "the project is called swiftagent" does NOT mean the identifier is swiftagent; always ask for the URL to confirm.

Step 4: Validate the project + show the iteration list for the user to choose from

result = bootstrap("biaopin-swiftagent", token="<token>")
if result["error"]:
    print("✗", result["error"])
else:
    print("Iterations in this project:")
    for it in result["iterations"]:
        print(f"  [{it['code']}] {it['name']}")

Show the iteration list to the user and ask: "Which iteration would you like to look at?"

Only after completing these four steps should you proceed with the user's actual query.


Environment configuration (optional — set these to skip passing parameters)

VariableDescription
CODING_TOKENBearer Token
CODING_DEFAULT_PROJECT_NAMEDefault project identifier (the part after /p/ in the URL)
CODING_DEFAULT_ITERATION_CODEDefault iteration Code (integer)

Script layout

scripts/
├── core.py        — HTTP client, token resolution, CodingAPIError
├── iterations.py  — Iteration API (depends on core)
├── issues.py      — Issue API (depends on core + iterations)
└── members.py     — Team member API (depends on core)

Usage in Python (the scripts already handle sys.path, so just import directly):

import sys
sys.path.insert(0, "path/to/scripts")
from iterations import get_iteration_list_code_and_name
from issues import describe_issue, describe_issue_list, create_issue, describe_defect_types, \
    extract_members_from_issue_list, get_custom_fields_from_issues
from members import get_team_members_id_and_name

Public function reference

iterations.py

get_iteration_list_code_and_name(project_name=None, *, token=None) -> [{'code': int, 'name': str}]

Paginates through and returns the full iteration list. The returned code is the value needed by describe_issue_list(iteration=...).

issues.py

describe_issue_list(
    project_name=None, *,   # falls back to CODING_DEFAULT_PROJECT_NAME if omitted
    issue_type="ALL",       # ALL / REQUIREMENT / DEFECT / MISSION
    limit="2000",
    assignee_ids=None,      # [int] — server-side filter (unreliable; pair with filter_issues for a client-side re-filter)
    iteration=None,         # int  — falls back to CODING_DEFAULT_ITERATION_CODE if omitted; a client-side re-filter is already built in
    status_types=None,      # None→TODO+PROCESSING; []→no filter; ['TODO',...]→specific types
    base_issue_type=None,   # REQUIREMENT / DEFECT / MISSION
    token=None,
) -> dict
# Each entry in Response.IssueList contains:
#   Code, Name, Type, IssueStatusName, IssueStatusType, Priority,
#   Assignees([{"id": int, "name": str}]),  ← array of assignees (not Creator/Handler)
#   IterationCode, IterationName, StartDate, DueDate, CustomFields
filter_issues(
    items: list,            # the IssueList returned by describe_issue_list
    *,
    assignee_name=None,     # assignee name (fuzzy match, case-insensitive)
    assignee_id=None,       # assignee ID (exact match)
    iteration_code=None,    # secondary iteration Code filter (use when server-side filtering fails)
) -> list
describe_issue(project_name=None, issue_code=0, *, token=None) -> dict
# Returns {Name, Description, IssueStatusName, AssigneeName, CreatorName}
create_issue(
    project_name=None, *,
    name: str,                  # title (required)
    issue_type="REQUIREMENT",   # REQUIREMENT / DEFECT / MISSION
    description="",
    priority=2,                 # 0=Low 1=Medium 2=High(default) 3=Urgent (per API docs)
    assignee_id=None,           # int — member ID (see members.py, or look it up from the issue list)
    iteration=None,             # int — falls back to CODING_DEFAULT_ITERATION_CODE if omitted
    start_date=None,            # str 'YYYY-MM-DD' — required in some projects
    due_date=None,              # str 'YYYY-MM-DD' — required in some projects
    label_ids=None,             # [int] — required in some projects; missing value raises issue_project_label_required
    working_hours=None,         # float — estimated hours; required in some projects
    issue_type_id=None,         # int — issue category ID (not the defect subtype)
    defect_type_id=None,        # int — defect subtype ID, from describe_defect_types
    token=None,
) -> dict  # {Code, Name, IssueStatusName, AssigneeName, CreatorName}
describe_defect_types(project_name=None, *, token=None) -> [{'id': int, 'name': str}]
# Returns the list of defect subtypes, for use with create_issue(defect_type_id=...)
extract_members_from_issue_list(issues_result: dict) -> [{'id': int, 'name': str}]
# Extracts a deduplicated member list from the return value of describe_issue_list — a fallback for
# when the token lacks permission for DescribeTeamMembers
get_custom_fields_from_issues(
    project_name=None, *,
    issue_type="REQUIREMENT",   # sample by issue type
    sample=10,                  # number of items to sample
    token=None,
) -> [{'id': int, 'name': str}]
# Infers a project's custom fields by sampling existing issues
# (works around DescribeIssueCustomFieldsBoundToProject, which requires elevated permissions)
# Must be called before creating an issue, so any required fields can be passed to
# create_issue via custom_field_values

members.py

get_team_members_id_and_name(*, token=None) -> [{'id': int, 'name': str}]

Paginates through and returns the full team member list. id can be used to filter with describe_issue_list(assignee_ids=[...]). Note: Some tokens lack the DescribeTeamMembers permission, which causes an error — fall back to extract_members_from_issue_list in that case.

Common workflows

Query a specific assignee's requirements in a given iteration (standard flow):

import sys; sys.path.insert(0, "path/to/coding-net/scripts")
from issues import describe_issue_list, filter_issues

result = describe_issue_list(project_name, iteration=22904, issue_type="REQUIREMENT", status_types=[])
issues = result["Response"]["IssueList"]

# Filter by assignee client-side (server-side filtering is unreliable)
my_issues = filter_issues(issues, assignee_name="wangyin")
for it in my_issues:
    assignees = ", ".join(a["name"] for a in it["Assignees"])
    print(f"#{it['Code']} [{it['IssueStatusName']}] {it['Name']} — {assignees}")

Extract members from the issue list (fallback when DescribeTeamMembers is not permitted):

from issues import describe_issue_list, extract_members_from_issue_list

result = describe_issue_list(project_name, iteration=code, status_types=[])
members = extract_members_from_issue_list(result)
# [{'id': 9403993, 'name': 'wangyin'}, ...]

Query the details of a single issue (including description):

from issues import describe_issue
detail = describe_issue(project_name, issue_code=12345)

Create a requirement (must detect custom fields first):

⚠️ A project may have required custom fields configured (e.g. "test submission date"); creating without them raises issue_custom_field_required. First call get_custom_fields_from_issues() to infer the field list, then pass custom_field_values.

from issues import create_issue, get_custom_fields_from_issues

# Step 1: Detect the project's custom fields
custom_fields = get_custom_fields_from_issues(project_name, issue_type="REQUIREMENT")
# Example return: [{"id": 38589683, "name": "Test submission date"}, ...]
# Confirm each field's value with the user; required fields must be passed in

# Step 2: Create the requirement
issue = create_issue(
    project_name,
    name="Support feature XX",
    start_date="2026-06-17",
    due_date="2026-06-30",
    custom_field_values=[
        {"Id": 38589683, "Content": "2026-06-30"},  # Test submission date
        # other required custom fields...
    ],
)
print(issue["Code"], issue["Name"])

Create a defect (including project-level required fields):

from issues import describe_issue_list, extract_members_from_issue_list, describe_defect_types, create_issue, filter_issues

# 1. Look up member ID from the issue list
result = describe_issue_list(project_name, iteration=code, status_types=[])
members = extract_members_from_issue_list(result)
uid = next(m["id"] for m in members if m["name"] == "Zhang San")

# 2. Look up defect subtypes
defect_types = describe_defect_types(project_name)  # [{'id': 36666669, 'name': 'Functional defect'}, ...]

# 3. Create (whether start_date/due_date/label_ids/working_hours are required depends on project config)
issue = create_issue(
    project_name,
    name="Login page returns a 500 error",
    issue_type="DEFECT",
    priority=1,
    assignee_id=uid,
    iteration=code,
    start_date="2026-06-17",
    due_date="2026-06-20",
    label_ids=[123],
    working_hours=2.0,
    defect_type_id=defect_types[0]["id"],
)

Top skills in this category