feat: sap-cli skill v2.1.0 — self-contained distributable package

- assets/: sap-cli source (v2.1.0, 26 commands, 16 object types)
- references/: tool constraints + error handling (self-contained)
- scripts/setup.py: one-click install/config/verify
- SKILL.md: full command reference + dual-platform install guide
- VERSION: 2.1.0

Built from D:/Codespace/sap-cli via scripts/pack_skill.py
This commit is contained in:
2026-06-13 20:52:24 +08:00
commit 1e39b6da88
49 changed files with 7172 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
"""目录扫描模块。
扫描项目根目录下按约定的子目录结构,识别 ABAP 开发对象。
"""
from __future__ import annotations
import logging
import os
from dataclasses import dataclass
logger = logging.getLogger("sapcli.scanner")
DIRECTORY_TYPE_MAP: dict[str, str] = {
"reports": "report",
"classes": "class",
"interfaces": "interface",
"functions": "function",
"domains": "domain",
"dataelements": "dataelement",
"tables": "table",
"structures": "structure",
"tabletypes": "tabletype",
"includes": "include",
"cdsviews": "cdsview",
"messageclasses": "messageclass",
"views": "view",
"searchhelps": "searchhelp",
"lockobjects": "lockobject",
}
FUNCTIONS_DIR = "functions"
@dataclass
class ScannedObject:
"""扫描到的对象。"""
name: str # 大写对象名,function 类型含 / (GROUP/FUNC)
type: str # 对象类型
file: str # 相对于项目根目录的路径
def scan_project(project_path: str) -> list[ScannedObject]:
"""扫描项目目录,返回识别到的所有对象列表。
遍历已知目录名,每个目录下找 .abap 文件。
functions/ 特殊处理:子目录 = 函数组名。
"""
results: list[ScannedObject] = []
for dir_name, obj_type in DIRECTORY_TYPE_MAP.items():
dir_path = os.path.join(project_path, dir_name)
if not os.path.isdir(dir_path):
logger.debug("目录不存在,跳过: %s", dir_path)
continue
if dir_name == FUNCTIONS_DIR:
_scan_functions(dir_path, results)
else:
_scan_flat_directory(dir_path, dir_name, obj_type, results)
logger.info("扫描完成: %d 个对象", len(results))
return results
def _scan_flat_directory(
dir_path: str,
dir_name: str,
obj_type: str,
results: list[ScannedObject],
) -> None:
"""扫描普通目录(一对一:文件名 = 对象名)。"""
for filename in sorted(os.listdir(dir_path)):
if not filename.endswith(".abap"):
continue
# 隐藏文件跳过
if filename.startswith("."):
continue
obj_name = filename[:-5].upper() # 去掉 .abap,转大写
rel_path = f"{dir_name}/{filename}"
results.append(ScannedObject(
name=obj_name,
type=obj_type,
file=rel_path,
))
logger.debug("扫描到: %s (%s) → %s", obj_name, obj_type, rel_path)
def _scan_functions(dir_path: str, results: list[ScannedObject]) -> None:
"""扫描 functions/ 目录(特殊处理:子目录 = 函数组名)。
目录结构: functions/{组名小写}/{模块名小写}.abap
对象名: {组名大写}/{模块名大写}
"""
for group_dir_name in sorted(os.listdir(dir_path)):
group_dir = os.path.join(dir_path, group_dir_name)
if not os.path.isdir(group_dir):
continue
if group_dir_name.startswith("."):
continue
group_name = group_dir_name.upper()
for filename in sorted(os.listdir(group_dir)):
if not filename.endswith(".abap"):
continue
if filename.startswith("."):
continue
func_name = filename[:-5].upper()
obj_name = f"{group_name}/{func_name}"
rel_path = f"{FUNCTIONS_DIR}/{group_dir_name}/{filename}"
results.append(ScannedObject(
name=obj_name,
type="function",
file=rel_path,
))
logger.debug("扫描到: %s (function) → %s", obj_name, rel_path)