Files
sap-cli-skill/SKILL.md
T
吴让宇 e786742bcb chore: 同步至源码 2.5.1 — SKILL.md 改为构建产物 + 工具代码刷新
SKILL.md(唯一来源 = sap-cli/skill-src/SKILL.md.tmpl):
- 本仓 SKILL.md 自此为构建产物,勿手工编辑;手改后再构建会被漂移护栏拦下
- 并入模板独有内容:调用方式节 + 补齐 parser 真实命令(clone/enhancement/
  history/unit-test/unlock/transport create,原表只 9 个,实际 31 个)
- 版本号改为 {{VERSION}} 注入(原字面量停在 v2.2.1/v2.3.0,与工具实际版本脱节)
- 修 1 处违反铁律 5 的示例(--path ./src → ./src/TMP/class/)

assets/(工具代码,此前严重过时):
- 9 个文件与源码不一致,scanner.py 尤甚:3818B → 6664B
- 修复前从本仓装出的工具**不支持三层 src 结构**,铁律 5 实际跑不通
- transport.py 反向漂移(分发版多 transport create,源码无)已随源码回迁对齐

VERSION 2.5.1;references/ 三份规则与源码一致(无差异)
2026-09-11 00:22:05 +08:00

352 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: sap-cli
description: "Use when you need to operate SAP ABAP development objects — syncing code, querying metadata, managing transports, reading tables, or any task involving SAP ADT REST API. Trigger on: ABAP objects, SAP development, transport requests, DDIC types, SE11/SE16N/SE38/SE80 equivalents, 'sync to SAP', 'download from SAP', 'activate ABAP', 'read SAP table'. NOT for developing sap-cli itself."
version: "2.5.1"
author: WuRangyu
license: MIT
triggers:
- "ABAP development"
- "sync to SAP"
- "download from SAP"
- "SAP transport request"
- "DDIC/SE11/SE16N/SE38/SE80"
- "SAP ADT REST API"
---
# sap-cli — SAP ABAP Development Object Management
Python CLI wrapping the SAP ADT REST API. Manages the full lifecycle of ABAP development objects from the terminal. Core loop: **download → local edit → sync back to SAP**.
## 调用方式
```bash
cd <SKILL_DIR>/assets && python main.py <command> [options]
```
全局参数(置于命令之前):
| 参数 | 说明 |
|------|------|
| `--profile <name>` | 使用指定配置 profile |
| `--verify-ssl` | 启用 SSL 验证(默认关闭) |
安装与配置见同目录 `INSTALL.md`
## Quick Start
```bash
# 1. Single object: download → edit → sync
# 注意:--path 必须给到「对象类型目录」(src/TMP/<对象类型>),见铁律 5
python main.py download --name ZMY_CLASS --type class --path ./src/TMP/class
# edit ./src/TMP/class/zmy_class.abap
python main.py sync --name ZMY_CLASS --type class --path ./src/TMP/class/zmy_class.abap --corr_nr DEVK901XXX
# 2. Batch project sync (auto dependency-ordered)
python main.py init --path ./my_project # add objects under src/
python main.py sync --all --path ./my_project --corr_nr DEVK901XXX
# 3. Table query: structure (SE11) + data (SE16N)
python main.py show-table --name ZMY_TABLE
python main.py read-table --name ZMY_TABLE --fields "F1,F2" --where "F1 = 'X'" --max-rows 50
```
## Command Reference
Optional params in `[ ]`. 各命令参数以 `python main.py <cmd> --help` 为准。
| Category | Command | Key Parameters |
|----------|---------|----------------|
| CRUD | `create` | `--name --type [--description --source <.abap> --definition <.json> --package <$TMP> --corr_nr]` |
| CRUD | `info` | `--name --type` [^1] |
| CRUD | `download` | `--name --type --path` |
| CRUD | `sync` | `--name --type --path --corr_nr` **(REQUIRED)** |
| CRUD | `delete` | `--name --type` |
| CRUD | `activate` | `--name --type --corr_nr` (built-in NW 7.40 double-activate) |
| CRUD | `upload` | `--name --type --path --corr_nr` (lock→write→unlock only; no check/activate) |
| CRUD | `syntax-check` | `--name --type` (remote object; no upload/activate) |
| CRUD | `unlock` | `--name --type --lock-handle` (释放 delete 后残留的 enqueue 锁) |
| Batch | `init` | `--path` |
| Batch | `refresh` | `--path` |
| Batch | `sync --all` | `--path --corr_nr [--dry-run]` |
| Batch | `analyze` | `--path` (dependency analysis) |
| Batch | `clone` | `--name --type --from <profile> --to <profile> [--corr_nr]` (跨系统克隆对象) |
| Query | `show-table` | `--name` (SE11 structure) |
| Query | `read-table` | `--name [--fields --where --max-rows]` (SE16N data) |
| Query | `list` | `--type [--package --prefix]` |
| Query | `whereused` | `--name --type` |
| Query | `search` | `--query` |
| Query | `diff` | `--name --type --path` (local vs SAP) |
| Query | `history` | `--name --type` (对象版本历史) |
| Transport | `transport list` | — |
| Transport | `transport info` | `--corr_nr` |
| Transport | `transport release` | `--corr_nr` |
| Transport | `transport objects` | `--corr_nr` |
| Transport | `transport create` | `--description` (新建传输请求) |
| Config | `config show` | — |
| Config | `config list-profiles` | — |
| Config | `config set` | `<key> <val>` |
| Config | `auth login` / `auth status` | password stored in keyring |
| Other | `run-program` | `--name` (SA38) |
| Other | `check` | `--name --type` (ATC) |
| Other | `format` | `--name --type` (Pretty Printer) |
| Other | `enhancement` | `--name --type` (增强) |
| Other | `unit-test` | `--name --type` (ABAP Unit 测试) |
| Other | `package create` | `--name` |
| Other | `cds download` | `--name --path` |
| Other | `scaffold` | `--name --template <t>` — alv-report, bapi-wrapper, interface-class, data-model |
[^1]: ⚠️ `info` 不返回 `package`/`packageRef` 字段。需通过 ADT API 查询:`GET /sap/bc/adt/programs/programs/<name>` 返回 XML 中含 `<adtcore:packageRef>`。也可用 `list --package <pkg>` 反向查找。
Batch sync dependency order: `domain(10) → dataelement(20) → table(30) → tabletype(40) → interface(50) → class(60) → function(70) → report(80)`.
Step-wise sync for lock-heavy automation — split the atomic `sync` when lock conflicts are frequent; upload now, verify, activate later:
```bash
python main.py upload --name ZMY_CLASS --type class --path ./src/TMP/class/zmy_class.abap --corr_nr DEVK901XXX # lock→write→unlock only
python main.py syntax-check --name ZDEPENDENT_REPORT --type report # verify dependents (no upload)
python main.py activate --name ZMY_CLASS --type class --corr_nr DEVK901XXX # activate once clean
```
### Supported Object Types (16)
| Category | Types | Has source |
|----------|-------|------------|
| Programs | `report`, `include` | ✅ |
| OOP | `class`, `interface` | ✅ |
| Functions | `function` | ✅ |
| DDIC base | `domain`, `dataelement`, `table`, `structure` | ✅ |
| DDIC ext | `cdsview`, `view` | ✅ |
| DDIC no-source | `tabletype`, `messageclass`, `searchhelp`, `lockobject` | ❌ |
| Groups | `functiongroup` | ❌ |
**Function naming**: must use `group/module`, e.g. `ZMY_FGROUP/Z_MY_FUNC`.
## ⚠️ Constraints (HARD RULES — DO NOT VIOLATE)
Full rules: `references/sap-tool-constraints.md`. Violations are intercepted by hooks.
质检标准: `references/abap-coding-rules.md`(38条规则,6大类:安全/性能/可维护性/错误处理/NW740兼容/风格。其中 E04/E05 强制:CALL FUNCTION 前必须核实被调对象存在、参数结构与真实签名一致,ADT 语法检查不覆盖被调对象,核实记录纳入交付物)。质检员在代码审计时以此文档为唯一标准,判定体系:✅合规 / ❌违反(必须修复) / ⚠️建议(推荐修复)。
1. **Only `python main.py <cmd>` touches SAP** — no `requests`/`urllib`, no curl to ADT/SOAP/RFC, no `python -c` inline, no importing `ADTClient` internals, no hand-built ADT XML. Exceptions: `run-program` custom reports, explicit user request, or developing sap-cli itself.
2. **Never modify SAP standard objects** (`CL_*`, `CX_*`, `IF_*`, `SAPL*`, or any non-user original system) — read-only only: download/info/list/search/whereused/show-table/read-table/diff. When unsure, ask the user.
3. **Never write system tables** (TADIR, E071, SEOCLASS, REPOSRC, D010SINF, T000, TDEVC, TSTC) — read-only SELECT is fine; any write needs explicit authorization.
4. **`sync` must carry `--corr_nr`** — otherwise it triggers an interactive transport-request prompt.
5. **Non-interactive `delete` needs piped confirm**`echo "yes" | python main.py delete ...` (else EOFError).
6. **DDIC `sync` failure on NW 7.40 (HTTP 406) is a system limit** — do not retry; report to the user for SE09.
## ⛔ 开发铁律(依赖顺序 + 强制闭环)
> 违反任一条的后果不是「效率低」,而是**后续全部代码必须返工**。2026-09-10 S4T 实测事故后固化。
### 铁律 1:DDIC 未完成,禁止开发任何引用它的代码
代码引用的每一个 DDIC 对象(域 / 数据元素 / 表 / 结构 / 表类型)**必须先完成「上传 → 语法检查通过 → 激活成功」三关**,才能开始写引用它的代码。
- 落地顺序:`domain → dataelement → table/structure → tabletype → interface → class → function → report`
- 「已完成」**以实测为准,不采信会话自述**:
- 结构 → `read-table DD02L``AS4LOCAL = 'A'` 才算激活)
- 表类型 → `read-table DD40L`
- 其他对象 → `info` / `read-table TADIR`
- 违反后果:后续每个引用它的对象语法检查报「类型 XXX 未知 / 结构不存在」,整批返工。
### 铁律 2:强制开发闭环(七步,不得跳步)
```
1. 本地编写 / 修改源码 ← 本地文件是唯一编辑入口,不在 SAP 端直接改
2. 同步到 SAP ← upload / sync --corr_nr
3. SAP 端语法检查 ← syntax-check
4. 有错?→ 回到第 1 步 ← 本地改 → 再同步 → 再检查,循环到 0 错误
5. 语法检查通过 → 激活 ← activate
6. 激活成功 → 才允许执行测试 ← run-program / unit-test
7. 测试不过 → 回到第 1 步
```
**禁止**
- ❌ 跳过语法检查直接 `activate`
- ❌ 未激活就 `run-program` / `unit-test`(跑的是旧版本,或报「对象不存在」)
- ❌ 把「语法检查通过」当成「功能正常」—— `syntax-check` 不校验被调对象的存在性与签名(见 `references/abap-coding-rules.md` E04/E05
- ❌ 把「S4T 演示机通过」表述为「CEM / 生产验证通过」(演示机缺 DDIC、无编号范围、权限不同)
### 铁律 3:引用前先核实存在性(E04/E05 落地)
`CALL FUNCTION` 或引用 `TYPE z...` 之前,必须先 `info` / `read-table DD02L|DD03L` 核实:对象存在、名称逐字符一致、签名与组件一致。核实记录(对象存在性 + 签名比对)纳入交付物。
### 铁律 4:同一任务同时只允许一个开发者
多会话并发操作同一批对象会相互覆盖。**派下一个会话前,先确认前一个已退出**(`ps aux | grep [h]ermes`)。长任务用 `background=true, notify=true`,等完成通知再派下一步。
### 铁律 5:src 目录三层结构(开发包 → 开发对象 → 代码)
源码仓 `src/` 下**只允许这一种层次**,三层缺一不可:
```
src/
└── TMP/ ← 第 1 层:开发包,本地开发固定用 TMP
└── <对象类型>/ ← 第 2 层:开发对象 = 对象类型,一个类型一个目录
└── zxxx.abap ← 第 3 层:代码文件,文件名 = 对象名(download 产出小写,扫描大小写不敏感)
```
- 第 1 层**固定写 `TMP`**(本地开发包):对应 SAP 端 `$TMP` 本地包,目录名去掉 `$` 以免 shell 把 `$TMP` 当变量展开。对象正式归入传输包时,该层目录改名为包名(如 `ZIDTR`),层次不变
- 第 2 层目录即开发对象类型的落地:`report` / `class` / `interface` / `function` / `domain` / `dataelement` / `table` / `structure` / `tabletype` / `include` …(完整清单见下方「📦 Gitee仓库规范 → 目录结构」)
- **禁止**`.abap` 平铺在 `src/` 根下;禁止跳过开发包层直接把对象类型目录放 `src/` 下;禁止在对象类型目录下再嵌套子目录(函数模块的命名按下方结构图 `function/ZGROUP_ZFUNC.abap`
- **`download --path` 要给到对象类型目录**:该命令只把文件写进给定目录,**不会自动建开发包层/对象层**。`--path ./src/TMP/class` 正确;`--path ./src` 会产出平铺目录,不合格
- 反例(2026-09-10 实测):`sap-cem-bip/src/` 25 个 `.abap` 平铺在根下,既无开发包层也无对象类型层 → 不合格,需归位
- 批量功能(`init` / `sync --all`**已支持三层结构**:扫描器(`sapcli/scanner.py`)会扫项目根下的类型目录,也会下钻 `src/<开发包>/` 找类型目录;目录名**单复数均接受、大小写不敏感**(`class/` = `classes/`)。旧的扁平布局仍兼容
- 函数模块两种命名均可:`function/<函数组>/<函数模块>.abap` 或 ADT 式 `function/<函数组>.fugr.<函数模块>.abap`,对象名统一解析为 `函数组/函数模块`
### 2026-09-10 S4T 事故问题总结
| # | 问题 | 后果 | 对策(已固化) |
|---|------|------|----------------|
| 1 | 用 `CALL FUNCTION` 但检查不校验被调函数是否存在、调用结构是否正确 | 本地语法全绿、运行期 `CALL_FUNCTION_NOT_FOUND` 转储 | 编码规则 E04/E05(强制核实存在与签名) |
| 2 | DDIC 未落地就开发引用代码 | 后续对象语法检查全挂 | 铁律 1(DDIC 先行 + 实测判定) |
| 3 | 跳过「语法检查 → 激活 → 测试」闭环,直接跑测试 | 跑的是旧版本 / 对象不存在,结论无效 | 铁律 2(七步闭环不得跳步) |
| 4 | 采信自述「函数组壳已存在」 | 实查 TFDIR 0 行,对象从未落地 | 铁律 1(实测判定)+ 铁律 3 |
| 5 | 批量生成 DDIC 的工具报表反复 500/转储 | 34 分钟会话耗尽,零交付 | 逐个落对象;`run-program` 不稳定见下方「DDIC 落地方案」 |
| 6 | 多个会话并发改同一批对象 | 相互覆盖,效率崩盘 | 铁律 4(单工作者) |
| 7 | 未读技能就试错(`delete` 交互确认) | 白撞 EOFError 一轮 | 动手前先读 SKILL.md 与 references |
| 8 | `download --path ./src` 平铺下载,不建开发包/对象层 | 25 个 .abap 堆在 `src/` 根下,结构不合格需返工归位 | 铁律 5(src 三层结构 + `--path` 指向对象类型目录) |
## 🔄 Standard WorkflowSAP 对象操作标准流程)
> 所有 SAP 对象修改操作必须按此顺序执行,不得跳过或调整步骤顺序。
```
步骤1 info — 确认程序存在、已激活、关联请求号
├─ 有请求号 → 记录
└─ 无请求号 → transport list 找老板的请求号推荐
→ 无合适的则帮老板新建(含请求描述)
步骤2 download — 源码下载到本地
步骤3 五角色流水线:研究员 → 创作者 → 质检 → 老板批准
步骤4 建 Gitee 仓库(ADT 层级:src/包/类型/对象/)+ 同步确认请求号
└─ 请求号写入 doc 文档
步骤5 工程师改代码(本地,不 commit)
步骤6 请求号绑定对象(如 SE09 未自动绑定)
步骤7 sync --corr_nr → 语法检查 → 迭代修复 → 激活成功
└─ 全程带请求号,不要到编译阶段才关联
步骤8 激活成功 → git commit + tag → push
└─ git = 细粒度变更追溯 / 请求号 = 系统大版本管理
```
**核心原则:**
- info 同时查请求号,不单独处理
- 建 Gitee 仓库同时确认请求号,不滞后
- sync 全程带 `--corr_nr`,不等编译阶段补
- 只有激活成功才 git commit,不是每次本地改就提交
- git 管变更细节,请求号管系统大版本
## 📦 Gitee仓库规范
### 目录结构(ADT 层级)
> ⛔ **本节即铁律 5 的唯一合法层次**`src/TMP/<对象类型>/<代码文件>`(本地开发固定 `TMP`,对象有正式包后用包名)。`.abap` 平铺在 `src/` 根下、或跳过开发包层,均为不合格。
每个 SAP 项目的 Gitee 仓库按 ADT 层级组织源码:
```
sap-<project-name>/
├── src/
│ └── <package>/ # SAP 包名(如 $ZIDTR
│ ├── report/ # 程序类型目录
│ │ └── ZXXX.abap # ABAP 源码(.abap 扩展名)
│ ├── class/
│ │ └── ZCL_XXX.abap
│ ├── interface/
│ │ └── ZIF_XXX.abap
│ ├── function/
│ │ └── ZGROUP_ZFUNC.abap
│ ├── domain/
│ ├── dataelement/
│ ├── table/
│ └── ...
├── doc/ # 文档(请求号记录、设计说明)
│ └── transport-<corr_nr>.md # 每次传输的请求号记录
├── references/ # 参照文档
└── log/ # 错误知识库
```
### 请求号同步确认
1. **创建仓库时**:确认传输请求号(`transport list``transport info --corr_nr DEVK9XXXXX`),写入 `doc/transport-<corr_nr>.md`
2. **每次 sync 前**:确认请求号仍有效(未释放、未满),检查 `transport objects --corr_nr DEVK9XXXXX` 确保对象已绑定
3. **激活成功后**:更新 `doc/` 记录本次变更摘要
4. **仓库命名建议**`sap-<package-name>`,小写、hyphen 分隔,如 `sap-zidtradmin`
### Git 提交时机
- **只有 `sync` 激活成功后才 git commit**,不是每次本地修改就提交
- commit message 包含:对象名 + 请求号 + 变更摘要
- tag 使用请求号命名:`DEVK9XXXXX`
- git 管变更细节追溯,请求号管系统大版本管理
## Error Handling (Quick Reference)
错误知识库: `log/` 目录(LLM-WIKI 格式),每个错误独立一页。**先查 log/,再查 `references/error-handling.md`**。新错误按 `log/SCHEMA.md` 规范入库。
Full flow: `references/error-handling.md`.
| Error Pattern | Action |
|---|---|
| HTTP 406 (DDIC Lock) | System limit — report to user → SE09 manual DDIC sync |
| HTTP 403 (Locked) | Residual enqueue lock — try `run-program` clear-locks report, then SM12 |
| HTTP 400 (SaveFailure) | Class DEFINITION mismatch — diff local vs SAP source |
| HTTP 404 (DDIC download/sync) | NW 7.40 has no `/source/main` endpoint (7.50+) — DDIC objects only create/info/delete |
| HTTP 423 (Transport lock) | Object bound to transport — report to user → SE09 |
| HTTP 500 (set_source) | Lock 成功但写入 500,传输请求中无对象 → SE09 手动添加对象到请求 → `log/adt-set-source-500.md` |
| Activate fails, still inactive | double-activate built-in; still fails → report → SE09 |
| Open SQL @ 转义不一致 | 语法检查 2 错误——一处用 `@` 则全局必须一致(`WHERE field = @lv_var``INTO TABLE @lt_data``log/adt-nw740-open-sql-consistency.md` |
| `WITH EMPTY KEY` dump | NW 7.40 unsupported — use `WITH NON-UNIQUE KEY` / `WITH DEFAULT KEY` |
| Function name error | Missing `/` separator — use `ZGROUP/Z_FUNC` |
| EOFError on delete | Non-interactive — prepend `echo "yes" \|` |
| Syntax: "此处不允许逗号" | NW 7.40 逗号陷阱(9种) → `log/adt-nw740-comma-traps.md` |
General flow: **diagnose** (params / file / transport / network / object existence) → **usage error**: fix & retry → **sap-cli limit**: report, never bypass → **SAP limit**: exhaust automation below → **confirmed un-automatable**: request SAP GUI op.
Automation fallbacks — exhaust in order before requesting any SAP GUI action:
1. **Adjust params** — different `--corr_nr`, or `--dry-run` to preview a batch before committing.
2. **`run-program` workaround** — sync + run a helper report (e.g. `ZSAPILOT_CLEAR_LOCKS`) to clear residual enqueue locks.
3. **`delete` + recreate** — only if the object needs a full rewrite; `delete` may fail with HTTP 423 (transport-bound) → then it is SE09.
4. **`activate` retry** — double-activate is built in; if it still fails, stop retrying and report — do not chase other activation strategies.
- **Never bypass sap-cli** (no requests / curl / ADTClient) — report the failure and its cause.
- **Give the complete change checklist at once** — do the full comparison first (`show-table` vs code field refs); never surface issues incrementally.
- **System-table writes** — 4-step authorization: explain *** → list exact statements → state risks & alternatives → await explicit approval.
## NW 7.40 Compatibility (E2E verified — 2.5.1)
Full matrix: `docs/NW740-COMPATIBILITY.md`.
| Availability | Types |
|---|---|
| Full (download → edit → sync closed loop) | `report`, `class`, `interface`, `function`, `functiongroup`, `include` |
| CRUD only (create/info/delete, no source edit) | `domain`, `dataelement` — DDIC stored as XML at object URI, no `/source/main` endpoint (7.50+ feature) |
| Endpoint missing (404/415) | `table`, `structure`, `tabletype`, `view`, `messageclass`, `searchhelp`, `lockobject`, `cdsview` |
NW 7.40 specifics:
- **DDIC Lock** — `delete` fixed (dedicated Accept header); `sync` still 406.
- **Activation false-negative** — first activate may report failure but actually succeed; double-activate is built in.
- **ABAP syntax limits** — no string templates `\|...\|`, no inline `DATA(...)`, no `WITH EMPTY KEY`.
- **Open SQL @ 转义一致性** — NW 7.40 要求一处用 `@` 则全 SQL 必须一致(`WHERE field = @lv_var``INTO TABLE @lt_data`),混用导致语法检查 2 错误。详见 `log/adt-nw740-open-sql-consistency.md`
- **set_source HTTP 500(传输请求无对象)** — Lock 成功但写入 500,根因:Lock API 创建锁句柄但未绑定对象到传输请求。NW 7.40 需 SE09 手动添加对象。详见 `log/adt-set-source-500.md`
- **CSRF token** — all writes require it; sap-cli handles automatically.
### DDIC 结构/表类型落地方案(NW 7.40 无 ADT 端点时)
`create --type structure|tabletype` 在 NW 7.40 报 404/415(端点缺失)。可用落地路径:自研落地报表 + `run-program`
1. `upload` 一个工具报表到 `$TMP`,内部调 `DDIF_TABL_PUT`(结构)/ `DDIF_TTYP_PUT`(表类型)写 DDIC,再调 `DDIF_TABL_ACTIVATE` / `DDIF_TTYP_ACTIVATE` 激活
2. `run-program --name <工具报表>`
3. `read-table DD02L`(结构)/ `DD40L`(表类型)实查确认 `AS4LOCAL = 'A'`
踩坑(S4T 实测):
- **`DD_OBJ_ACTIVATOR` 会转储** — 必须用 `DDIF_TABL_ACTIVATE` / `DDIF_TTYP_ACTIVATE`
- **`run-program` 端点不稳定** — 一次性批量生成全部结构的工具会反复 500/转储;**逐个落**才稳态
- **结构不写 TADIR** — 落地后的结构在 DD02L/DD40L 可见但 TADIR 查不到,清点必须查字典表,不能用 TADIR 代替
- **清理脚手架用 `delete`** — 需 `echo "yes" \|` 管道确认(见约束 5);删完用 TADIR/DD02L 独立复核,不采信 delete 回显