360 Secure SkillHub
360 安全技能中心

Coding Net

通过 Python 脚本调用 Coding 开放 API:先校验 CODING_TOKEN 与项目标识,再查询迭代列表、需求/缺陷/任务列表、单条详情和团队成员,并在检测项目自定义字段后创建需求或缺陷。适用于团队迭代与任务协同管理。

wangyin717
wangyin717
热度 40.09
最近更新 2026-07-23 18:00:35
分类:协作依赖:代码执行 / 第三方 API应用场景:任务管理 / API 集成 / 工作流 / DevOps

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add coding-net
Metadata
name
coding-net
version
1.1.0
description
Query and operate on Tencent Coding DevOps platform (e.coding.net) data — iterations, issues (requirements/defects/tasks), and team members — including creating requirements and defects. Triggered when the user's request involves Coding platform operations, such as "list iterations", "query issues", "requirements in the current iteration", "bugs on Coding", "team member list", "assignee", "create a requirement", "create a defect". All APIs require the CODING_TOKEN environment variable.

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)

Variable Description
CODING_TOKEN Bearer Token
CODING_DEFAULT_PROJECT_NAME Default project identifier (the part after /p/ in the URL)
CODING_DEFAULT_ITERATION_CODE Default 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"],
)