# ljdou-pi-work-agent

Pi 企业协作扩展，连接 **Jira / Confluence / GitLab / GitHub**，支持常用查询、更新和开发协作流程。

[English](README.md) · [功能审查与剩余限制](docs/CAPABILITIES.md)

## 安装与配置

```bash
npm install ljdou-pi-work-agent
```

安装脚本在首次安装时生成 `~/.pi/config/work-agent.yaml`，并注册全局扩展 wrapper。也可使用 pi 包管理方式：`pi install /absolute/path/to/package` 或 `pi install npm:ljdou-pi-work-agent`。凭据只放在私有配置中，不提交 Git。

用 `PI_WORK_AGENT_CONFIG` 指定其他 YAML/JSON 配置。当前公共配置加载器只支持简化 YAML：**不要写行内注释或行内数组**，注释放单独一行，数组使用多行列表；也可使用 JSON。

```yaml
datasources:
  jira-prod:
    type: jira
    url: https://company.atlassian.net
    username: user@example.com
    token: replace-with-token
    # Cloud 使用 3，Server/DC 使用 2（默认）
    apiVersion: "3"
    readOnly: false
    # Cloud 自定义富文本字段，先查询真实 ID
    # richTextFields:
    #   - customfield_12345
  confluence-prod:
    type: confluence
    url: https://company.atlassian.net/wiki
    username: user@example.com
    token: replace-with-token
    readOnly: false
  gitlab-prod:
    type: gitlab
    url: https://gitlab.company.com
    token: replace-with-token
    readOnly: false
  github-prod:
    type: github
    token: replace-with-token
    readOnly: false
    # GHES 可配置 url: https://github.company.com/api/v3
settings:
  defaultConnector: jira-prod
  queryTimeout: 30000
```

- `defaultConnector` 仅用于**相同系统类型**工具；其他系统选择其首个成功连接的连接器。显式 `connector` 优先；类型不符会拒绝，不会误选其他系统。
- `work_list_connections` 展示可用连接、类型、URL、只读状态和配置默认项，**不返回密码/Token**。
- Basic/Bearer 认证：Jira/Confluence 支持 `authType: bearer`；默认 Basic。GitLab/GitHub 使用 Bearer。
- 全部连接器使用全局 `settings.queryTimeout`，单连接配置可覆盖。

## 当前功能

共 **87 个工具**：Jira 26、Confluence 15、GitLab 23、GitHub 22、连接发现 1。某系统未连接成功时，其工具仍注册，但执行会报告明确连接错误。

### Jira：26 个工具

| 分类 | 工具 |
| --- | --- |
| 查询 | `jira_search_issue`、`jira_get_issue`、`jira_get_current_user` |
| 元数据 | `jira_get_fields`、`jira_get_edit_metadata`、`jira_get_create_metadata`、`jira_get_transitions`、`jira_get_link_types` |
| Issue 写入 | `jira_create_issue`、`jira_update_issue`、`jira_delete_issue`、`jira_transition_issue`、`jira_assign_issue` |
| 评论 | `jira_get_comments`、`jira_add_comment`、`jira_update_comment`、`jira_delete_comment` |
| 工时 | `jira_get_worklogs`、`jira_add_worklog`、`jira_update_worklog`、`jira_delete_worklog` |
| 关联/附件 | `jira_link_issues`、`jira_delete_issue_link`、`jira_upload_attachment`、`jira_delete_attachment` |
| 通用 API | `jira_rest_request`：Core `/rest/api/2|3`、Agile `/rest/agile/1.0` |

- 查询开发方案/自测场景真实 customfield ID 后再写入，**不能猜字段 ID**；先读 Issue 和编辑元数据。
- `fields.description` 替换原内容，不是追加；追加前先读取合并。`fields`/`update` 至少一个非空，同字段不能同时放在两者中。
- v2 保留文本/wiki；v3 将 description/environment、评论、工时备注纯文本转 ADF 段落。显式 ADF 原样传递；自定义富文本需配置 `richTextFields` 或显式传 ADF。自动转换不会解析 Markdown/wiki 格式。
- Cloud 搜索使用 `/search/jql` 与 `nextPageToken`；Server 使用 `startAt`。创建元数据使用现代分页接口，旧 Server 可用通用 REST 请求旧版接口。
- 状态更新需流转 ID，不直接修改 status；工时保留剩余估时可传 `query: {"adjustEstimate":"leave"}`。
- `deleteSubtasks: true` 删除全部子任务。附件最多 20MiB，确认路径/大小/SHA-256，上传同一份已确认字节快照。

### Confluence：15 个工具

| 分类 | 工具 |
| --- | --- |
| 查询 | `confluence_search`、`confluence_get_page`、`confluence_get_current_user`、`confluence_get_page_children`、`confluence_get_comments`、`confluence_get_labels` |
| 页面 | `confluence_create_page`、`confluence_update_page`、`confluence_delete_page` |
| 评论 | `confluence_add_comment`、`confluence_update_comment`、`confluence_delete_comment` |
| 标签 | `confluence_add_label`、`confluence_remove_label` |
| 通用 API | `confluence_rest_request`：legacy `/rest/api`、Cloud `/api/v2` |

专用工具实际使用 **legacy REST v1**，不是 v2；能否使用 legacy API 受部署版本影响。Cloud URL 含 `/wiki`；Server/DC 使用实际 context path。

页面/评论内容为 **storage XHTML，不是 Markdown**。更新页面需传完整 `title`、`body` 和读取到的**当前** `version`，提交 version+1。确认后不再隐藏读取/合并内容；版本冲突直接报告，不自动重试。删除/移入回收站及对子页面影响依部署规则而定，不自动永久清理或恢复。

### GitLab：23 个工具

| 分类 | 工具 |
| --- | --- |
| 查询 | `gitlab_search_code`、`gitlab_get_file`、`gitlab_search_issue`、`gitlab_get_issue`、`gitlab_get_issue_notes`、`gitlab_get_current_user` |
| Issue | `gitlab_create_issue`、`gitlab_update_issue`、`gitlab_delete_issue`、`gitlab_close_issue` |
| 评论 | `gitlab_add_issue_note`、`gitlab_update_issue_note`、`gitlab_delete_issue_note` |
| 仓库文件 | `gitlab_create_file`、`gitlab_update_file`、`gitlab_delete_file` |
| MR | `gitlab_list_merge_requests`、`gitlab_get_merge_request`、`gitlab_create_merge_request`、`gitlab_update_merge_request`、`gitlab_merge_merge_request` |
| 流水线 | `gitlab_create_pipeline` |
| 通用 API | `gitlab_rest_request`：`/api/v4` |

- Issue/MR 使用**项目内 IID**，不是全局 ID。`projectId` 支持数字、原始 `group/project` 或单次 URL 编码路径。
- 描述是 Markdown，更新会替换；重新打开 Issue 可传 `fields.state_event: "reopen"`。
- 搜索支持分页和真正的项目级接口。文件读取默认 `HEAD`（默认分支），不假设 main。
- 文件变更会创建 commit，必须明确分支和完整内容；建议 `options.last_commit_id` 防止并发覆盖。options 不会覆盖显式分支/内容/commit message。
- MR 合并必须传预期 head SHA，先检查 review/流水线；创建流水线可能部署。Token 写权限、项目角色和保护分支规则仍由 GitLab 强制执行。

### GitHub：22 个工具

| 分类 | 工具 |
| --- | --- |
| 查询 | `github_search_code`、`github_get_file`、`github_search_issue`、`github_get_current_user`、`github_get_issue`、`github_get_comments` |
| Issue | `github_create_issue`、`github_update_issue`（含关闭/重开） |
| 评论 | `github_add_comment`、`github_update_comment`、`github_delete_comment` |
| 仓库文件 | `github_create_file`、`github_update_file`、`github_delete_file` |
| PR | `github_list_pull_requests`、`github_get_pull_request`、`github_create_pull_request`、`github_update_pull_request`、`github_merge_pull_request`、`github_review_pull_request` |
| Actions | `github_dispatch_workflow` |
| 通用 API | `github_rest_request`：允许当前 host 的 `/repos`、`/user`、`/users`、`/orgs`、`/search`、`/notifications`、`/rate_limit` 路径 |

文件读取省略 ref 使用真实默认分支；完整 UTF-8 文件写入会自动 base64 编码并创建 commit，更新/删除需当前 blob SHA。合并需预期 PR head SHA，API `merged: false` 会报告失败，不误报成功。Review 绑定指定 commit SHA；Actions 需明确 workflow/ref。

**不虚构 GitHub Issue 删除 REST 接口**：关闭使用 `github_update_issue`。普通会话评论不是代码 diff review comment，后者可使用通用 REST。

## 开发方案/自测场景更新流程

1. `work_list_connections` 确认连接。
2. 读取 Issue；Jira 额外查询字段 ID/编辑元数据。
3. 在本地将原描述与开发方案/自测场景合并。
4. 调用更新工具，用户核对目标与参数后确认。
5. 再读取核验。

GitLab 示例：

```json
{
  "connector": "gitlab-prod",
  "projectId": "group/project",
  "issueIid": "123",
  "fields": {
    "description": "原需求内容\n\n开发方案：……\n自测场景：……"
  }
}
```

Confluence 页面更新示例（先读到当前 version=4）：

```json
{
  "connector": "confluence-prod",
  "pageId": "12345",
  "title": "开发方案",
  "body": "<h2>方案</h2><p>内容……</p><h2>自测</h2><p>内容……</p>",
  "version": 4
}
```

## 统一安全策略

- **每次外部写入必须由 TUI/RPC 用户确认**连接 URL、身份、操作、资源和参数。Print/JSON 拒绝写入，模型不能用 confirmed 标记跳过。
- 四个系统共用写队列，串行确认和提交，避免跨系统弹窗重叠；不是针对其他客户端的分布式锁。
- `readOnly: true` 在连接器层禁止所有写入（含通用 REST）。平台权限仍需满足。
- 通用 REST 使用原始 JSON，不自动转换富文本；所有非 GET 都需确认（包含 POST search）。批量/管理操作必须先明确实际影响范围。
- URL 不含凭据/query/fragment；拒绝重定向、路径穿越和嵌套编码。支持取消/超时，不自动重试写入；结果未知先查询核验。
- 删除/合并可能不可恢复，commit/CI 可能触发通知和部署；不保证跨系统事务/回滚。敏感文件提交/上传需要明确用户意图。
- 返回限制 48KB / 2000 行，完整 JSON 保存为私有临时文件；建议筛字段、分页，临时文件按本地生命周期清理。
- 覆盖常用工作流与 JSON REST 扩展，**不是所有产品的全部功能**：非 Jira multipart、二进制下载、GraphQL、JSM/插件特有 API 命名空间、完整 Confluence v2 专用工具等尚未实现。详见功能审查。

## 验证与生效

```bash
npm test --workspace=packages/pi-work-agent
npm exec --workspace=packages/pi-work-agent -- tsc --noEmit -p tsconfig.json
```

测试模拟 HTTP，不修改真实企业系统数据。构建后，本地加载包通过 `/reload` 或重启 pi 生效；独立安装的 npm 副本需要单独更新。本地修改不等于已发布新版本。

## 结构

- `index.ts`：初始化、工具注册、安全连接发现、关闭清理。
- `config/`：配置加载/验证和模板。
- `connectors/`：平台操作；`json.ts` 提供安全 JSON HTTP，Jira 另支持 multipart。
- `core/registry.ts`：类型安全和默认连接选择。
- `core/tools.ts`：跨系统用户确认队列、输出截断。
- `tools/`：Agent 参数 schema 与工具入口。
- `tests/`：模拟 HTTP/工具/入口回归测试。
- `docs/CAPABILITIES.md`：缺口审查和剩余边界。

MIT
