@mu-cabin/coding-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,145 @@
1
+ # coding-code
2
+
3
+ A curated playbook for the **代码托管 (09)** category — the 152 Git/MR actions.
4
+
5
+ ## 1. Identifiers — get the DepotId first
6
+
7
+ Almost every action keys off **`DepotId`** (a numeric repo id), *not* the repo name. `DepotPath` is an
8
+ optional companion on most calls. So the universal first step is "resolve repo name → DepotId" (§2).
9
+
10
+ - **`DepotId`** — numeric repository id, required by nearly all git/MR actions.
11
+ - **`MergeId`** — the merge-request id; the key for every per-MR action (detail, diff, reviewers,
12
+ merge, close). MR list items carry both `Id` and `MergeId` — use **`MergeId`** for subsequent calls.
13
+ - **`ReviewerGlobalKey`** — reviewers/mergers are addressed by **GlobalKey** (a string), not numeric id.
14
+ - **Branches** are addressed by name; create needs a `StartPoint` (a branch name or commit sha).
15
+ - **Files/commits** need a `Ref` (branch/tag/sha) and a `Path`.
16
+
17
+ ## 2. Resolve the repo → DepotId
18
+
19
+ ```bash
20
+ # repos the current user can read (simplest, no ids needed)
21
+ coding call DescribeMyDepots --param PageNumber=1 --param PageSize=50 \
22
+ --select 'Payload.Depots[].Id,Payload.Depots[].Name,Payload.Depots[].ProjectName,Payload.Depots[].DefaultBranch' --json
23
+
24
+ # all repos in a project (needs the numeric ProjectId)
25
+ coding call DescribeProjectDepotInfoList --param ProjectId=341 \
26
+ --select 'DepotData.Depots[].Id,DepotData.Depots[].Name,DepotData.Depots[].DefaultBranch' --json
27
+
28
+ # by exact name (needs team GlobalKey + project + depot name)
29
+ coding call DescribeDepotByNameInfo --param TeamGk=<team> --param ProjectName=demo --param DepotName=backend \
30
+ --select 'Depot.Id,Depot.DefaultBranch,Depot.HttpsUrl,Depot.SshUrl' --json
31
+ ```
32
+
33
+ Cache the `DepotId` (and default branch) for the session.
34
+
35
+ ## 3. Browse code
36
+
37
+ ```bash
38
+ # branches
39
+ coding call DescribeGitBranches --param DepotId=123 --param PageSize=50 \
40
+ --select 'Branches[].BranchName,Branches[].IsDefaultBranch,Branches[].IsProtected,Branches[].Sha' --json
41
+
42
+ # directory listing at a ref (Path defaults to repo root)
43
+ coding call DescribeGitFiles --param DepotId=123 --param Ref=master --param Path=src \
44
+ --select 'Items[].Name,Items[].Path,Items[].Mode' --json
45
+
46
+ # a file's content at a specific commit
47
+ coding call DescribeGitFileContent --param DepotId=123 --param CommitSha=<sha> --param Path=src/app.ts --json
48
+
49
+ # commit history on a ref (KeyWord / Path / date range filter the list)
50
+ coding call DescribeGitCommitInfos --param DepotId=123 --param Ref=master --param PageSize=20 \
51
+ --select 'Commits[].Sha,Commits[].ShortMessage,Commits[].AuthorName,Commits[].CommitDate' --json
52
+ coding call DescribeGitCommitDiff --param DepotId=123 --param Sha=<sha> --json # one commit's diff (param is Sha)
53
+ ```
54
+
55
+ ## 4. Branches & tags
56
+
57
+ ```bash
58
+ coding call CreateGitBranch --param DepotId=123 --param BranchName=feature/login --param StartPoint=master --json
59
+ coding call DescribeGitTags --param DepotId=123 --json
60
+ coding call CreateGitTag --data '{"DepotId":123,"TagName":"v1.2.0","StartPoint":"master","Message":"release"}' --json
61
+ # DeleteGitBranch / DeleteGitTag / DeleteGitMergedBranches are gated — see §7
62
+ ```
63
+
64
+ ## 5. Merge requests — the lifecycle
65
+
66
+ ```bash
67
+ # 5a. CREATE — two near-identical actions exist:
68
+ # CreateGitMergeReq → also accepts inline `Reviewers` (comma-separated GlobalKeys) ← prefer this
69
+ # CreateGitMergeRequest→ same minus Reviewers
70
+ coding call CreateGitMergeReq --data '{
71
+ "DepotId":123, "SrcBranch":"feature/login", "DestBranch":"master",
72
+ "Title":"Add SMS login", "Content":"…", "Reviewers":"userA,userB"}' --json
73
+
74
+ # 5b. LIST (Status filter ∈ OPEN|CLOSE|ALL|ACCEPTED; plus KeyWord, Source/TargetBranches, Reviewer/Creator/Merger…)
75
+ coding call DescribeDepotMergeRequests --param DepotId=123 --param Status=OPEN --param PageSize=20 \
76
+ --select 'Data.List[].MergeId,Data.List[].Title,Data.List[].Status,Data.List[].SourceBranch,Data.List[].TargetBranch' --json
77
+ # also: DescribeSelfMergeRequests (mine), DescribeProjectMergeRequests (whole project)
78
+
79
+ # 5c. DETAIL + diff + discussion
80
+ coding call DescribeMergeReqInfo --param DepotId=123 --param MergeId=45 --json
81
+ coding call DescribeGitMergeRequestDiffs --param DepotId=123 --param MergeId=45 --json # changed-file list
82
+ coding call DescribeGitMergeRequestDiffDetail --param DepotId=123 --param MergeId=45 --param Path=src/app.ts --json
83
+ coding call DescribeSingeMergeRequestNotes --param DepotPath=team/demo/backend --param MergeId=45 --json # comments (keys off DepotPath)
84
+
85
+ # 5d. REVIEWERS
86
+ coding call CreateMergeRequestReviewer --param DepotId=123 --param MergeId=45 --param ReviewerGlobalKey=userC --json
87
+ coding call DescribeMergeRequestReviewers --param DepotId=123 --param MergeId=45 --json
88
+
89
+ # 5e. COMMENT (NOTE: this action keys off DepotPath, not DepotId; ParentId 0 = root.
90
+ # Omit `Form` for an MR-level comment; supply it for a diff-line comment.)
91
+ coding call CreateMergeRequestNote --param DepotPath=team/demo/backend --param MergeId=45 \
92
+ --param ParentId=0 --param Content="LGTM" --json
93
+
94
+ # 5f. CAN-MERGE check (uses branch names, not MergeId)
95
+ coding call DescribeCanMerge --param DepotId=123 --param Source=feature/login --param Target=master --json
96
+
97
+ # 5g. MERGE — all flags required. Squash/IsFastForward/IsDelSourceBranch are booleans; Message is the commit msg.
98
+ coding call ModifyMergeMR --data '{"DepotId":123,"MergeId":45,"Message":"Merge !45",
99
+ "Squash":false,"IsFastForward":false,"IsDelSourceBranch":true}' --json
100
+
101
+ # title/description edits, rebase, or close instead of merge:
102
+ coding call ModifyGitMergeRequest --param DepotId=123 --param MergeId=45 --param Title="…" --json
103
+ coding call ModifyGitMergeRequestRebase --param DepotId=123 --param MergeId=45 --json
104
+ coding call ModifyCloseMR --param DepotId=123 --param MergeId=45 --json # close without merging (not destructive-gated)
105
+ ```
106
+
107
+ ## 6. Writing files / commits, releases (less common)
108
+
109
+ `CreateGitFiles` / `ModifyGitFiles` commit content directly — **both** require `Ref`, `Message`, and
110
+ `LastCommitSha` (the current head sha, for optimistic-lock conflict detection). `CreateGitCommit` makes
111
+ a multi-file commit (keys off `DepotPath`, not `DepotId`; also needs `LastCommitSha`).
112
+ `CreateGitRelease` / `DescribeGitReleases` manage releases. Prefer normal git over the API for bulk
113
+ content work; use these only when you must operate purely through the OpenAPI.
114
+
115
+ ## 7. Destructive actions (exit-10 gated)
116
+
117
+ `DeleteGitDepot`, `DeleteGitBranch`, `DeleteGitMergedBranches`, `DeleteGitFiles`, `DeleteGitTag`,
118
+ `DeleteGitRelease`, `DeleteGitDeployKey`, `DeleteMergeRequestReviewer`, `DeleteMergeRequestNote`,
119
+ `DeleteBranchProtection`(+`Member`), `DeleteGitProtectedTagRule`, `DeleteSshKey`/`DeleteMemberSshKey`.
120
+ (`ModifyGitDepotArchive` is **not** gated — indexed `write` — but archiving hides the repo; confirm
121
+ anyway.) Follow SKILL.md's protocol: preview with `--dry-run`, get explicit
122
+ user approval, then re-run the **same argv with `--yes`**. `DeleteGitMergedBranches` is bulk — it skips
123
+ protected branches but still deletes every merged branch; confirm scope before approving.
124
+
125
+ ```bash
126
+ coding call DeleteGitBranch --param DepotId=123 --param BranchName=feature/login --dry-run --json # show user
127
+ coding call DeleteGitBranch --param DepotId=123 --param BranchName=feature/login --yes --json # after approval
128
+ ```
129
+
130
+ > **Merging is not exit-10 gated** (`ModifyMergeMR` is classified `write`). Treat it as consequential
131
+ > anyway: confirm the MR id, source/target, and the squash/delete-branch flags with the user before merging.
132
+
133
+ ## 8. Gotchas
134
+
135
+ - **`DepotId` is the key, not the repo name** — always resolve it via §2 first. `DepotPath` is optional
136
+ on most calls, but a few use it **instead of** `DepotId`: `CreateMergeRequestNote`,
137
+ `DescribeSingeMergeRequestNotes`, and `CreateGitCommit` have **no `DepotId` param** (key off
138
+ `DepotPath`), and `DeleteGitMergedBranches` requires **both**. Check `coding schema` when unsure.
139
+ - **Reviewers/mergers use `GlobalKey` (string), not numeric user id.**
140
+ - **Two MR-create actions**: `CreateGitMergeReq` (supports inline `Reviewers`) and `CreateGitMergeRequest`.
141
+ Pick one; don't call both. The MR key for everything after is **`MergeId`** (not the list item's `Id`).
142
+ - **`ModifyMergeMR` requires all four control fields** (`Message`, `Squash`, `IsFastForward`,
143
+ `IsDelSourceBranch`) — a missing required field is a validation error (exit 11).
144
+ - Diffs and trees are large — `--select`/`--limit` aggressively, and prefer `DescribeGitMergeRequestDiffDetail`
145
+ for a single file over pulling the whole MR diff.
@@ -0,0 +1,42 @@
1
+ # coding-export
2
+
3
+ A curated playbook for the **导出任务 / Export jobs (17)** category — 3 actions implementing an async
4
+ "export pages → ZIP of Markdown" pipeline. **No action here is destructive** (all are `write` risk; none
5
+ trips the exit-10 gate) — but note these actions break the usual naming convention (verb is a *suffix*:
6
+ `DocumentJob{Create,Start,Status}`).
7
+
8
+ ## 1. The export pipeline (create → start → poll)
9
+
10
+ ```bash
11
+ # 1. create the job. Event is fixed; EventBody is an escaped-JSON STRING naming the subject.
12
+ # belongType ∈ project|team|program ; belongId is that subject's numeric id.
13
+ coding call DocumentJobCreate --data '{"Event":"PAGE_EXPORT_TO_ZIP_MD",
14
+ "EventBody":"{\"belongType\":\"project\",\"belongId\":341}"}' --select 'JobId' --json
15
+ # → top-level JobId (a string) — carry it through the next two calls
16
+
17
+ # 2. start the job (it does not run on create)
18
+ coding call DocumentJobStart --param JobId=<JobId> --json # → only RequestId
19
+
20
+ # 3. poll status until done; Result is a signed COS download URL (⚠️ valid ~30 min, then expires)
21
+ coding call DocumentJobStatus --param JobId=<JobId> --select 'Status,ProcessRate,Result' --json
22
+ ```
23
+
24
+ The three values that thread through: `DocumentJobCreate` yields the **`JobId`** (top-level); `…Start`
25
+ kicks it off; `…Status` returns **`Status`** (string), **`ProcessRate`** (a progress object), and once
26
+ finished **`Result`** — the signed download URL.
27
+
28
+ > ⚠️ **`Result` is a time-limited COS link (~30 min per the spec).** Download promptly. The CLI does not
29
+ > download the bytes — hand the `Result` URL to the user (or `curl` it out of band) before it expires.
30
+
31
+ ## 2. Notes
32
+
33
+ - **Two separate steps to run a job**: `DocumentJobCreate` only *registers* the job; it does not run
34
+ until `DocumentJobStart`. Poll `DocumentJobStatus` after starting.
35
+ - **`EventBody` is an escaped-JSON string**, not a nested object: `{"belongType":"…","belongId":…}`.
36
+ `belongType` is `project` | `team` | `program`; `belongId` is the matching numeric id (resolve a
37
+ project id via the `references/project.md` playbook, a program id via `references/program.md`).
38
+ - **Responses are top-level**, not under `Data`: `JobId` (create), `Status`/`ProcessRate`/`Result`
39
+ (status). Match `--select` accordingly.
40
+ - **`Event` is fixed** to `PAGE_EXPORT_TO_ZIP_MD` (the only documented export type).
41
+ - **Export requires team/project admin scope** — that's what an exit-8 `UnauthorizedOperation` means
42
+ here (a scope gap, not a wrong token).
@@ -0,0 +1,66 @@
1
+ # coding-files
2
+
3
+ A curated playbook for the **文件网盘 / File disk (14)** category — 3 actions implementing an upload
4
+ pipeline. **No action in this category is destructive** — there's no exit-10 gate here (and no
5
+ delete/list/download action either).
6
+
7
+ ## 1. The upload pipeline (3 steps, like the Wiki ZIP flow)
8
+
9
+ Uploading a file is **presign → upload bytes out-of-band → register**. The CLI does steps 1 and 3; the
10
+ actual byte transfer (step 2) is a direct PUT to Tencent COS / MinIO that the CLI does **not** perform.
11
+
12
+ ```bash
13
+ # 1. presign — get the upload URL + the keys you carry forward
14
+ coding call DescribePreSignUploadUrl --data '{"ProjectName":"demo","FileName":"report.csv",
15
+ "ContentType":"text/csv","FolderType":0,"FolderId":0}' \
16
+ --select 'Data.UploadLink,Data.StorageKey,Data.AuthToken,Data.Headers' --json
17
+
18
+ # 2. PUT the bytes to Data.UploadLink — OUT OF BAND (curl/SDK). Use Data.Headers (a JSON string of
19
+ # x-cos-security-token etc.) and the signed Authorization embedded in UploadLink. The CLI does not
20
+ # move bytes; this step happens in the user's shell.
21
+
22
+ # 3. register the uploaded object as a CODING file — carries StorageKey + AuthToken from step 1
23
+ coding call CreateFile --data '{"StorageKey":"<from step 1>","AuthToken":"<from step 1>"}' \
24
+ --select 'Data.Id,Data.Name' --json # → Data.Id is the file id
25
+ ```
26
+
27
+ The values that thread through: step 1 yields **`StorageKey`** (the stored object path), **`AuthToken`**
28
+ (identity proof for step 3), **`UploadLink`** (the signed PUT URL), and **`Headers`** (a JSON *string*
29
+ of upload headers). Step 3 (`CreateFile`) needs only `StorageKey` + `AuthToken` and returns the new
30
+ file's `Data.Id`.
31
+
32
+ ## 2. ⚠️ `FolderType` picks the destination — get it right
33
+
34
+ `DescribePreSignUploadUrl` has two distinct use cases controlled by **`FolderType`**:
35
+
36
+ | Goal | `FolderType` | `FolderId` |
37
+ |---|---|---|
38
+ | Upload to the **file disk (网盘)** | `0` (常规文件夹) | a real folder id from `CreateFolder` (or `0` for root) |
39
+ | Upload an **issue/项目协同 attachment** | `1` (隐藏文件夹) | `0` (not needed) |
40
+
41
+ For the attachment case: get the `Data.Id` from `CreateFile`, then pass it into an issue create/modify
42
+ as `FileIds:[<id>]` (that lives in the `references/issues.md` playbook, action `CreateIssue`/`ModifyIssue`).
43
+
44
+ ## 3. Folders (file disk only)
45
+
46
+ ```bash
47
+ # create a folder in the file disk. ParentId 0 = root. Returns Data.Id (use as FolderId in step 1).
48
+ coding call CreateFolder --data '{"ProjectName":"demo","FoldName":"个人资料","ParentId":0}' \
49
+ --select 'Data.Id,Data.Name' --json
50
+ ```
51
+
52
+ Note the param is **`FoldName`** (not `FolderName`) and **`ParentId`** (not `ParentFolderId`).
53
+
54
+ ## 4. Gotchas
55
+
56
+ - **The byte upload (step 2) is out of band** — the CLI presigns and registers but does not PUT the
57
+ file. Hand the user the `UploadLink` + `Headers` to upload (COS/MinIO PUT), then run `CreateFile`.
58
+ - **`FolderType`: `0` = file disk, `1` = issue attachment** (hidden folder). Picking the wrong one puts
59
+ the file in the wrong place. The `ContentType` should match what a browser would send (e.g.
60
+ `image/png`, `text/csv`).
61
+ - **`CreateFile` keys off `StorageKey` + `AuthToken`** from the presign step — no `ProjectName` on this
62
+ call. It returns `Data.Id` (int) — the handle you feed into `FileIds[]` when attaching to an issue.
63
+ - **Param spelling**: `CreateFolder` uses **`FoldName`** and **`ParentId`**; presign uses **`FolderId`**
64
+ (singular) + **`FolderType`**. Everything in this category wraps under **`Data`**.
65
+ - **No delete / list / download** here — this category only presigns, registers files, and creates
66
+ folders. For attachment-to-issue wiring, see the `references/issues.md` playbook.
@@ -0,0 +1,175 @@
1
+ # coding-issues
2
+
3
+ A curated playbook for the **项目协同 (08)** category — the 65 issue/iteration actions.
4
+
5
+ ## 1. Concepts & identifiers (resolve these before mutating)
6
+
7
+ - **ProjectName** — the project's name/identifier (the slug used in URLs, e.g. `demo-project`), **not**
8
+ its display label. Required by almost every action. If you only know the team or the display name,
9
+ list/resolve projects first (SKILL.md / `DescribeUserProjects` / `DescribeProjectByName`).
10
+ - **IssueCode** — the per-project issue number the user sees (e.g. `#42` → `42`). The key for
11
+ read/modify/delete of a single issue. **Not** the same as the global issue id.
12
+ - **Type** (the enum) vs **IssueTypeId** (a number):
13
+ - `Type` ∈ `REQUIREMENT` 需求 · `DEFECT` 缺陷 · `MISSION` 任务 · `EPIC` 史诗 · `SUB_TASK` 子任务 (for
14
+ `CreateIssue`). **List queries** (`DescribeIssueList`/`DescribeIssueListWithPage`) take an `IssueType`
15
+ of only `ALL`/`DEFECT`/`REQUIREMENT`/`MISSION`/`EPIC` — **no `SUB_TASK`**; reach sub-tasks via
16
+ `DescribeSubIssueList` on the parent.
17
+ - `IssueTypeId` is the numeric id of a *configured* type in this project (a project may have
18
+ several custom requirement/defect types). Get it from `DescribeProjectIssueTypeList`. `CreateIssue`
19
+ needs `Type`; `IssueTypeId` is only needed when the project has multiple types for that `Type`.
20
+ - **Priority** is a **string code**, not a label: `"0"`低 · `"1"`中 · `"2"`高 · `"3"`紧急.
21
+ - **StatusId** — status is an id, resolved per (project, IssueType) via `DescribeProjectIssueStatusList`.
22
+ You **transition** an issue by setting `StatusId` on `ModifyIssue`; there is no separate transition action.
23
+ - **Dates** are `"YYYY-MM-DD"` strings (`StartDate`/`DueDate`).
24
+
25
+ ## 2. Orient — the one-time lookups
26
+
27
+ ```bash
28
+ # what issue types exist in this project (gives Type + IssueTypeId + names)
29
+ coding call DescribeProjectIssueTypeList --param ProjectName=demo \
30
+ --select 'IssueTypes[].IssueType,IssueTypes[].Id,IssueTypes[].Name' --json
31
+
32
+ # the status pipeline for a given type (gives IssueStatusId you'll set on ModifyIssue)
33
+ coding call DescribeProjectIssueStatusList --param ProjectName=demo --param IssueType=REQUIREMENT \
34
+ --select 'ProjectIssueStatusList[].IssueStatusId,ProjectIssueStatusList[].IssueStatus.Name' --json
35
+ ```
36
+
37
+ Cache these for the session so you don't re-fetch them per issue.
38
+
39
+ ## 3. Find issues
40
+
41
+ ```bash
42
+ # the user's own to-dos across the team (no ProjectName needed)
43
+ coding call DescribeWorkbenchIssueList --param Type=REQUIREMENT --param PageSize=20 \
44
+ --select 'Data.IssueList[].Code,Data.IssueList[].Name,Data.IssueList[].Priority' --limit 20 --json
45
+
46
+ # filtered list within a project — IssueType required; filter with Conditions
47
+ coding call DescribeIssueList --data '{
48
+ "ProjectName":"demo", "IssueType":"ALL", "Offset":0, "Limit":20,
49
+ "SortKey":"UPDATED_AT", "SortValue":"DESC",
50
+ "Conditions":[
51
+ {"Key":"STATUS_TYPE","Value":"TODO,PROCESSING"},
52
+ {"Key":"ASSIGNEE","Value":"254"},
53
+ {"Key":"KEYWORD","Value":"login"}
54
+ ]
55
+ }' --select 'IssueList[].Code,IssueList[].Name,IssueList[].Priority,IssueList[].IssueStatusName' --json
56
+ ```
57
+
58
+ **Condition keys** (`Key`, with comma-separated `Value` for multi-select): `STATUS`, `STATUS_TYPE`
59
+ (`TODO`/`PROCESSING`/`COMPLETED`), `ASSIGNEE`/`CREATOR`/`WATCHER` (user ids), `PRIORITY` (`0`–`3`),
60
+ `LABEL`, `MODULE`, `ITERATION`, `KEYWORD` (name/code fuzzy), `ISSUE_TYPE`, `CREATED_AT`/`UPDATED_AT`/
61
+ `DUE_DATE`/`START_DATE` (range `"2020-08-01_2020-08-31"`), `WORKING_HOURS` (range `"0.1_5.0"`),
62
+ `CUSTOM` (also set `CustomFieldId`). Use `ConstValue:"UNSPECIFIC"` to match "unassigned/unset".
63
+ For paged results with totals use `DescribeIssueListWithPage`.
64
+
65
+ ## 4. Create an issue
66
+
67
+ ```bash
68
+ coding call CreateIssue --data '{
69
+ "ProjectName":"demo", "Type":"REQUIREMENT", "Name":"用户登录支持短信验证码",
70
+ "Priority":"2", "Description":"...", "AssigneeId":254,
71
+ "IterationCode":1001, "DueDate":"2026-06-01"
72
+ }' --json
73
+ ```
74
+
75
+ - `Name`, `Type`, `Priority`, `ProjectName` are the only required fields.
76
+ - `SUB_TASK` **requires** `ParentCode`; `EPIC`/`SUB_TASK` ignore `IterationCode`/`EpicCode`.
77
+ - Set `IssueTypeId` only if the project has multiple configured types for that `Type` (from §2).
78
+ - Defaults to the type's initial status; pass an optional `StatusId` (resolved in §2) to create it
79
+ in a specific status, or transition afterward via `ModifyIssue` (§6).
80
+
81
+ ## 5. Read one issue
82
+
83
+ ```bash
84
+ coding call DescribeIssue --param ProjectName=demo --param IssueCode=42 --json
85
+ coding call DescribeIssueCommentList --param ProjectName=demo --param IssueCode=42 \
86
+ --select 'CommentList[].CommentId,CommentList[].Content,CommentList[].CreatorId' --json
87
+ # activity log — ActionType + Target are BOTH required and combine (e.g. UPDATE+STATUS = status changes,
88
+ # UPDATE+ASSIGNEE = reassignments, CREATE+SELF = creation). Or use DescribeIssueStatusChangeLogList.
89
+ coding call DescribeIssueLogList --param ProjectName=demo --param IssueCode=42 \
90
+ --param ActionType=UPDATE --param Target=STATUS --json
91
+ coding call DescribeSubIssueList --param ProjectName=demo --param IssueCode=42 --json # children
92
+ ```
93
+
94
+ ## 6. Update — fields, assignee, status, iteration
95
+
96
+ `ModifyIssue` is the single write path; pass `ProjectName` + `IssueCode` plus only the fields you change.
97
+
98
+ ```bash
99
+ # transition status: resolve StatusId from §2, then set it
100
+ coding call ModifyIssue --param ProjectName=demo --param IssueCode=42 --param StatusId=44318126 --json
101
+
102
+ # reassign + bump priority
103
+ coding call ModifyIssue --param ProjectName=demo --param IssueCode=42 \
104
+ --param AssigneeId=254 --param Priority=3 --json
105
+
106
+ # move into / out of an iteration
107
+ coding call ModifyIssue --param ProjectName=demo --param IssueCode=42 --param IterationCode=1001 --json
108
+ ```
109
+
110
+ - Description has its own action: `ModifyIssueDescription` (long rich text).
111
+ - Labels/watchers/releases use add+`Del…`/`Update…` list params (e.g. `LabelIds` to add, `DelLabelIds`
112
+ to remove) — check `coding schema ModifyIssue` for the exact field.
113
+ - Use `--dry-run` first when the user wants to see the change before it lands.
114
+
115
+ ## 7. Comment & log work hours
116
+
117
+ ```bash
118
+ # top-level comment: ParentId is required — use 0 for a root comment, or a comment Id to reply
119
+ coding call CreateIssueComment --param ProjectName=demo --param IssueCode=42 \
120
+ --param ParentId=0 --param Content="Reviewed, looks good." --json
121
+
122
+ # log work hours (StartAt is an epoch-ms timestamp; hours are floats)
123
+ coding call CreateIssueWorkHours --data '{
124
+ "ProjectName":"demo","IssueCode":42,"SpendHour":3.5,"RemainingHour":1.0,
125
+ "StartAt":1716249600000,"WorkingDesc":"实现短信发送"}' --json
126
+ coding call DescribeIssueWorkLogList --param ProjectName=demo --param IssueCode=42 --json
127
+ ```
128
+
129
+ ## 8. Iterations (sprints) & releases
130
+
131
+ ```bash
132
+ coding call DescribeIterationList --param ProjectName=demo \
133
+ --select 'Data.List[].Code,Data.List[].Name,Data.List[].Status' --json
134
+ coding call CreateIteration --data '{"ProjectName":"demo","Name":"Sprint 12",
135
+ "StartAt":"2026-05-25","EndAt":"2026-06-07","Goal":"登录改版"}' --json
136
+
137
+ # batch-plan issues into an iteration
138
+ coding call PlanIterationIssue --data '{"ProjectName":"demo","IterationCode":1001,
139
+ "IssueCode":[42,43,44]}' --json
140
+
141
+ # start / complete / reopen a sprint — Operator ∈ start|complete|restart
142
+ coding call ModifyIterationStatus --param ProjectName=demo --param IterationCode=1001 \
143
+ --param Operator=start --param StartAt=2026-05-25 --json
144
+ # completing requires EndAt; JoinIterationCode optionally carries unfinished issues into another sprint
145
+ coding call ModifyIterationStatus --param ProjectName=demo --param IterationCode=1001 \
146
+ --param Operator=complete --param EndAt=2026-06-07 --json
147
+ ```
148
+
149
+ Releases (版本) mirror this: `DescribeReleaseList` / `CreateRelease` / `ModifyRelease`, and
150
+ `DescribeReleaseIssueList` for a release's scope.
151
+
152
+ ## 9. Destructive actions
153
+
154
+ `DeleteIssue`, `DeleteIteration`, `DeleteRelease`, `DeleteIssueWorkHours`, `DeleteIssueModule`,
155
+ `DeleteIssueBlock`, `DeleteRequirementDefectRelation` are **gated (exit 10)**. (Comments have no
156
+ delete action — only `CreateIssueComment`/`ModifyIssueComment`.)
157
+ Follow SKILL.md's confirmation protocol: preview with `--dry-run`, get explicit user approval,
158
+ then re-run the same argv with `--yes`. Resolve the exact `IssueCode`/`IterationCode` first — deletes
159
+ are not reversible.
160
+
161
+ ```bash
162
+ coding call DeleteIssue --param ProjectName=demo --param IssueCode=42 --dry-run --json # show the user
163
+ coding call DeleteIssue --param ProjectName=demo --param IssueCode=42 --yes --json # after approval
164
+ ```
165
+
166
+ ## 10. Gotchas
167
+
168
+ - **IssueCode is project-scoped**, not global — always pair it with `ProjectName`.
169
+ - **Priority is `"0"`–`"3"`**, never the Chinese label. **Status is an id**, resolved per type (§2).
170
+ - **Type vs IssueType vs IssueTypeId**: `CreateIssue` takes `Type` (the enum); **list queries take
171
+ `IssueType`** (same enum, different param name — there is no `Type` param on `DescribeIssueList`/
172
+ `DescribeIssueListWithPage`). Add the numeric `IssueTypeId` only when a project defines multiple
173
+ configured types for that enum.
174
+ - `CreateIssueComment.ParentId` is **required** (`0` = root). `CreateIssueWorkHours.StartAt` is **epoch ms**.
175
+ - Large boards: always `--select` the few fields you need and `--limit` arrays — issue payloads are huge.
@@ -0,0 +1,115 @@
1
+ # coding-org
2
+
3
+ A curated playbook for the **组织和成员 (02)** category — the 16 actions covering **team members** and
4
+ the **department tree**.
5
+
6
+ ## 1. Two id systems — don't cross them
7
+
8
+ | Context | Input id | Notes |
9
+ |---|---|---|
10
+ | **Team member** (team-level ops) | **`UserId`** (uint64) | what Delete/Lock/Unlock/DescribeTeamMember take. In *responses* the same value is the member's `Id`. `Name`/`GlobalKey`/`Email` are **not** input ids (except `DescribeTeamMemberByEmail`). |
11
+ | **Department member** (membership/assignee ops) | **`RefId`** / **`RefIds[]`** | a *different* integer (部门用户 refId), obtained from `DescribeDepartmentMembers.Data.DepartmentMembers[].RefId`. **Not** the UserId. |
12
+
13
+ **Member `Status` enum** (everywhere): `0` 不活跃 · `1` 活跃 · `-1` 被锁定 · `-2` 锁定登录 · `-3` 退出团队.
14
+
15
+ ## 2. Team members
16
+
17
+ ```bash
18
+ # list team members (paginated). ShowDepartment=true attaches each member's department membership.
19
+ coding call DescribeTeamMembers --param PageNumber=1 --param PageSize=50 \
20
+ --select 'Data.TeamMembers[].Id,Data.TeamMembers[].Name,Data.TeamMembers[].Email,Data.TeamMembers[].GlobalKey,Data.TeamMembers[].Status' --json
21
+
22
+ # admins only (leaner shape — Id/Name/GlobalKey, NO Email/Status/Roles)
23
+ coding call DescribeTeamAdminMembers --param PageNumber=1 --param PageSize=50 --select 'Data.Members[].Id,Data.Members[].Name,Data.Members[].GlobalKey' --json
24
+
25
+ # look up one member — by UserId, or by email. Both return a single object at `TeamMember` (not under Data).
26
+ coding call DescribeTeamMember --param UserId=254 --param ShowDepartment=true --select 'TeamMember.Id,TeamMember.Name,TeamMember.Email,TeamMember.Status,TeamMember.Roles' --json
27
+ coding call DescribeTeamMemberByEmail --param Email=alice@example.com --select 'TeamMember.Id,TeamMember.Name,TeamMember.GlobalKey' --json
28
+ ```
29
+
30
+ Lock / unlock are **two separate actions** (there is no lock flag), both keyed by `UserId`:
31
+
32
+ ```bash
33
+ coding call ModifyTeamMemberLocked --param UserId=254 --json # returns only RequestId; re-read via DescribeTeamMember to confirm Status -1
34
+ coding call ModifyTeamMemberUnlocked --param UserId=254 --json
35
+ ```
36
+
37
+ ## 3. Departments — the org tree
38
+
39
+ The tree is expressed via `ParentId` (a parent department's id; **`0` = top level**). Mind that the
40
+ department-id param name **changes per action** (see the table in §5).
41
+
42
+ ```bash
43
+ # read the tree from a node down. GetTree=true returns the recursive subtree. Result at `DepartmentTree`.
44
+ coding call DescribeDepartment --param DepartmentId=4001 --param GetTree=true \
45
+ --select 'DepartmentTree.Id,DepartmentTree.Name,DepartmentTree.Children' --json
46
+
47
+ # create a department (ParentId=0 for a top-level one). → Department.Id
48
+ coding call CreateDepartment --data '{"Name":"Platform","ParentId":0}' --json
49
+
50
+ # modify — ⚠️ the dept's own id is `Id` here (not DepartmentId); Name + ParentId are also required.
51
+ # Changing ParentId MOVES the department under a new parent.
52
+ coding call ModifyDepartment --data '{"Id":4002,"Name":"Platform Eng","ParentId":4001}' --json
53
+ ```
54
+
55
+ ## 4. Department members & assignees (负责人)
56
+
57
+ Both consume **`RefIds` (an array)** — refIds, not UserIds. Get refIds from `DescribeDepartmentMembers`.
58
+
59
+ ```bash
60
+ # list a department's members (paginated). Pointer=true → only DIRECT members (exclude sub-departments).
61
+ coding call DescribeDepartmentMembers --param DepartmentId=4001 --param PageNumber=1 --param PageSize=50 \
62
+ --select 'Data.DepartmentMembers[].RefId,Data.DepartmentMembers[].UserId,Data.DepartmentMembers[].Name,Data.DepartmentMembers[].UserStatus' --json
63
+
64
+ # add members to departments — DepartmentIds AND RefIds are BOTH arrays (members ↔ departments)
65
+ coding call ModifyDepartmentMember --data '{"DepartmentIds":[4001],"RefIds":[5501,5502]}' --json
66
+
67
+ # set a department's head(s) — DepartmentId is a SINGLE id here; RefIds is still an array
68
+ coding call ModifyDepartmentAssignee --data '{"DepartmentId":4001,"RefIds":[5501]}' --json
69
+ ```
70
+
71
+ `ModifyDepartmentMember` (which **departments a member belongs to**, plural `DepartmentIds`) vs
72
+ `ModifyDepartmentAssignee` (who **leads one department**, single `DepartmentId`; surfaces as
73
+ `Assignee:true` in a member's `Refs[]`). Different shapes — don't swap them.
74
+
75
+ ## 5. Third-party (LDAP) sync
76
+
77
+ ```bash
78
+ # fire-and-forget sync of third-party department members (currently LDAP). No params. Rate-limited to
79
+ # at most once per 2 minutes — don't loop on it.
80
+ coding call TriggerDepartmentSync --json
81
+ # poll status separately (no params; returns an opaque `Result`)
82
+ coding call DescribeDepartmentSyncStatus --select 'Result' --json
83
+ ```
84
+
85
+ ## 6. Destructive actions (exit-10 gated)
86
+
87
+ Two actions need `--yes` (preview with `--dry-run`, get explicit approval, re-run the **same argv with
88
+ `--yes`** — see SKILL.md's exit-10 protocol):
89
+
90
+ - **`DeleteTeamMember`** (`UserId`) — removes the member from the team.
91
+ - **`DeleteDepartment`** (`DepartmentId`) — deletes a department node.
92
+
93
+ ## 7. Department-id param name cheat-sheet (the #1 trap)
94
+
95
+ | Action | Dept id param | Shape |
96
+ |---|---|---|
97
+ | `CreateDepartment` | `ParentId` (parent) | scalar |
98
+ | `ModifyDepartment` | **`Id`** (self) + `ParentId` (parent) | scalar |
99
+ | `DeleteDepartment` / `DescribeDepartment` / `DescribeDepartmentMembers` / `ModifyDepartmentAssignee` | `DepartmentId` | scalar |
100
+ | `ModifyDepartmentMember` | **`DepartmentIds`** | **array** |
101
+
102
+ ## 8. Gotchas
103
+
104
+ - **`UserId` (team ops) vs `RefId` (department member/assignee ops)** are different integers — never
105
+ interchange them. Read a member's `RefId` from `DescribeDepartmentMembers`.
106
+ - **Dept-id param name varies** (§7); **`RefIds` and (for ModifyDepartmentMember) `DepartmentIds` are
107
+ arrays** even for a single value.
108
+ - **Lock/unlock are distinct actions**, no flag.
109
+ - **Result wrapping**: paginated lists → `Data.<X>[]` (`Data.TeamMembers`, `Data.Members`,
110
+ `Data.DepartmentMembers`); single member reads → `TeamMember`; the tree → `DepartmentTree`; sync status
111
+ → `Result`. Match `--select` accordingly.
112
+ - **`TriggerDepartmentSync` is rate-limited (≤1 / 2 min)** and returns nothing useful beyond `RequestId`
113
+ — poll `DescribeDepartmentSyncStatus`, don't re-trigger in a loop.
114
+ - **Team/department administration needs an org-admin-scoped token** — that's what an exit-8
115
+ `UnauthorizedOperation` is telling you here (a scope gap, not a wrong token).