查询和操作腾讯 Coding DevOps 平台(e.coding.net)的迭代、事项(需求/缺陷/任务)、团队成员等数据,包括创建需求和缺陷。当用户涉及 Coding 平台操作时触发,如「查迭代」「查事项」「当前迭代的需求」「Coding 上的 bug」「团队成员列表」「assignee」「新建需求」「创建缺...
Step 1: Collect the token
Check whether the user's message already includes a token. If not, check the environment variable:
bash
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
python
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
python
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.
VariableDescriptionCODING_TOKENBearer TokenCODING_DEFAULT_PROJECT_NAMEDefault project identifier (the part after /p/ in the URL)CODING_DEFAULT_ITERATION_CODEDefault iteration Code (integer)
text
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):
python
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
python
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=...).
python
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
python
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
python
describe_issue(project_name=None, issue_code=0, *, token=None) -> dict
# Returns {Name, Description, IssueStatusName, AssigneeName, CreatorName}
python
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}
python
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=...)
python
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
python
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
python
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.
Query a specific assignee's requirements in a given iteration (standard flow):
python
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):
python
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):
python
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 callget_custom_fields_from_issues()to infer the field list, then passcustom_field_values.
python
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):
python
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"],
)