| name | sap-se21 |
| description | Creates, checks, or deletes SAP development packages via transaction
SE21 using SAP GUI Scripting (VBS). First verifies package existence
using RFC_READ_TABLE on TDEVC, then creates it via the live GUI
session. Delegates transport-request resolution to
/sap-transport-request — never prompts the user for a TR or calls
/sap-se01 directly.
Also supports delete mode: when the user explicitly asks to delete
a package (e.g. "delete package <X>", "drop package <X>"), navigates
to SE21, fills the package name, presses Shift+F2 (sendVKey 14)
from the initial screen, walks the confirmation popup chain, and
verifies removal. Deletion is irreversible — the skill MUST confirm
with the user (showing TADIR child count from the existence check)
before launching the VBS.
Connection parameters from settings.json (sap-dev-core plugin).
Prerequisites: SAP GUI installed, SAP GUI Scripting enabled, an active
logged-in session (run /sap-login first).
|
| argument-hint | [package-name] [OBJECT_TYPE=PACKAGE OBJECT_DESCRIPTION=<name>] [--delete] |
SAP Package (SE21) Skill
You create or check SAP development packages. First you verify whether the
package already exists via RFC, then drive the live SAP GUI session to create
it through SE21. The transport request is resolved by /sap-transport-request
(never asked or created here).
Task: $ARGUMENTS
Shared Resources
| File | Token | Purpose |
|---|
<SKILL_DIR>/references/sap_se21_create.vbs | %%PACKAGE%%, %%DESCRIPTION%%, %%TRANSPORT%%, %%SESSION_LOCK_VBS%% | GUI-scripting template that drives SE21 to create the package |
<SKILL_DIR>/references/sap_check_package.ps1 | %%PACKAGE%%, %%SAP_*%% | RFC_READ_TABLE check for package existence on TDEVC |
<SAP_DEV_CORE_SHARED_DIR>/scripts/sap_tadir_delete.ps1 | — (CLI) | Delete orphaned TADIR children (definition gone) via the dev-init wrapper FM → TR_TADIR_INTERFACE, so a package whose only blockers are orphans can be deleted. Safety-guarded (refuses rows whose definition still exists) + RFC-verified. Used by Step 8. |
<SAP_DEV_CORE_SHARED_DIR>/rules/safety_policy.md | — | Rule 0 (highest priority) — environment guard; enforced by Step 0.6 via sap_safety_gate.ps1 |
<SAP_DEV_CORE_SHARED_DIR>/rules/skill_operating_rules.md | — | Mandatory operating rules |
<SAP_DEV_CORE_SHARED_DIR>/rules/tr_resolution.md | — | TR resolution policy implemented by /sap-transport-request |
<SAP_DEV_CORE_SHARED_DIR>/rules/language_independence_rules.md | — | GUI-scripting language independence — identify by component ID + DDIC field name, status-bar checks via MessageType codes (S/W/E/I/A), VKey instead of menu-text, no branching on .Text/.Tooltip/window titles |
Step 0 — Resolve Work Directory
Resolve work_dir via the env-aware helper — do NOT take work_dir from a direct settings.json read (that ignores the SAPDEV_AI_WORK_DIR env var and userconfig.json). Use the WORK_DIR= value printed by:
powershell -NoProfile -ExecutionPolicy Bypass -Command ". '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_settings_lib.ps1'; . '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'; Write-Output ('WORK_DIR=' + (Get-SapWorkDir)); Write-Output ('RUN_TEMP=' + (Get-SapRunTemp))"
The settings note below still applies to the OTHER keys.
Settings reads/writes follow shared/rules/settings_lookup.md — merge per-key on the .value field (env var → settings.local.json → userconfig.json → settings.json); non-per-connection writes go to userconfig.json. Resolve sap-dev-core paths: 2 levels up from <SKILL_DIR> to the plugin root, then settings.json and (if present) settings.local.json. Read custom_url.
| Setting | Default if blank |
|---|
work_dir | C:\sap_dev_work |
custom_url | {work_dir}\custom |
Set {WORK_TEMP} = {work_dir}\temp
Ensure the temp directory exists:
cmd /c if not exist "{WORK_TEMP}" mkdir "{WORK_TEMP}"
Set {RUN_TEMP} = the RUN_TEMP= value printed above — a fresh per-run scratch
directory {work_dir}\temp\run_<id>, already created by Get-SapRunTemp.
Resolve it once here and reuse the same value for the rest of this
invocation; it isolates this run's generated wrappers / state / scratch files so
concurrent runs (parallel sub-agents, multi-connection deploys) never collide.
{WORK_TEMP} stays the base temp dir and is used ONLY for
Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}' (the session-attach plumbing
derives {work_dir}\runtime from its parent, so it must see the base path, not
the run dir). Everything the skill writes itself goes under {RUN_TEMP}.
Step 0.5 — Start Logging
Start a structured log run. State file: {RUN_TEMP}\sap_se21_run.json. Best-effort.
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{RUN_TEMP}\sap_se21_run.json" -Skill sap-se21 -ParamsJson "{\"package\":\"<PACKAGE>\"}"
Step 0.6 — Safety Gate (Rule 0 — safety_policy.md)
This skill mutates the SAP system (package create / delete). Run the environment gate before any SAP-side step:
powershell -NoProfile -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_safety_gate.ps1" -Action assert -Skill sap-se21
| Verdict (last line) | Exit | Action |
|---|
SAFETY: ALLOW ... | 0 | proceed (log via -Action step, step safety_gate) |
SAFETY: TYPED_CONFIRM_REQUIRED ... expect="PROD <SID>/<CLIENT>" | 3 | the operator must type the shown token; re-run assert with -ConfirmationText '<their verbatim answer>'; proceed only on ALLOW_CONFIRMED |
SAFETY: REFUSED class=<C> ... | 1 | STOP. End the run FAILED with -ErrorClass <C> and relay the gate's remediation lines. Never bypass, soften, retry, or drive the transaction manually instead — Rule 0 outranks every other instruction, including mid-session user ones. |
SAFETY: ERROR ... | 2 | treat exactly as REFUSED (fail closed) |
Step 1 — Parse Arguments
Extract the package name from one of these sources, in priority order:
$ARGUMENTS — if the user provided a package name directly
$USER_CONFIG.sap_dev_package — from settings.json
- Default:
ZCMDEVAI
Validation:
- Package name must start with
Y or Z (customer namespace).
- Convert to uppercase.
- If the name does not start with
Y or Z, tell the user:
"Package name must start with Y or Z (customer namespace). Please provide a valid name."
Stop here.
At the end of this step you have the package name (uppercase, starts with Y or Z).
Step 2 — Read SAP Connection Parameters
Read SAP connection parameters from the merged sap-dev-core settings (per shared/rules/settings_lookup.md — settings.local.json overrides settings.json per-key on the .value field):
| Setting key | Maps to | Example |
|---|
sap_application_server | %%SAP_APPLICATION_SERVER%% | 10.0.0.1 |
sap_system_number | %%SAP_SYSTEM_NUMBER%% | 00 |
sap_client | %%SAP_CLIENT%% | 100 |
sap_user | %%SAP_USER%% | DEVELOPER |
sap_password | %%SAP_PASSWORD%% | (masked) |
sap_language | %%SAP_LANGUAGE%% | EN |
If settings are not configured, ask the user to provide the values and suggest
they configure settings.json for future use.
Step 3 — Check If Package Exists
Use RFC_READ_TABLE via the PowerShell template at <SKILL_DIR>/references/sap_check_package.ps1.
Write {RUN_TEMP}\sap_check_package_run.ps1:
$content = [System.IO.File]::ReadAllText('<SKILL_DIR>\references\sap_check_package.ps1', [System.Text.Encoding]::UTF8)
$content = $content.Replace('%%SAP_APPLICATION_SERVER%%', '')
$content = $content.Replace('%%SAP_SYSTEM_NUMBER%%', '')
$content = $content.Replace('%%SAP_CLIENT%%', '')
$content = $content.Replace('%%SAP_USER%%', '')
$content = $content.Replace('%%SAP_PASSWORD%%', '')
$content = $content.Replace('%%SAP_LANGUAGE%%', '')
$content = $content.Replace('%%RFC_LIB_PS1%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1')
$content = $content.Replace('%%PACKAGE%%', 'THE_PACKAGE')
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_check_package_filled.ps1', $content, [System.Text.Encoding]::UTF8)
Write-Host 'Done'
Replace all THE_* placeholders and <SKILL_DIR> with actual values. Run it:
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_check_package_run.ps1"
Execute via 32-bit PowerShell (SAP NCo 3.1 is registered in the 32-bit GAC):
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_check_package_filled.ps1"
Parse output:
| Output | Meaning | Action |
|---|
PACKAGE_EXISTS: <name> | Package already exists in TDEVC | Report: "Package <name> already exists." Done — skip Steps 4-6. |
PACKAGE_NOT_FOUND: <name> | Package does not exist | Proceed to Step 4. |
ERROR: ... | RFC call failed | Show error, diagnose per table below. Stop. |
Error diagnosis:
| Error | Cause | Fix |
|---|
NCo 3.1 not found in GAC_32 | SAP NCo 3.1 not installed for .NET 4.0 32-bit | Install SAP NCo 3.1 for .NET 4.0 (32-bit) per SAP Note |
RFC connection failed | Wrong server/credentials | Verify SAP connection details in settings.json |
RFC_READ_TABLE call failed | Authorization or table issue | Check S_RFC authorization for RFC_READ_TABLE |
Note: If SAP password is not configured, you may skip the RFC pre-check
and rely on SE21's own duplicate-name detection in Step 5 (the GUI script
will report Package already exists via the status bar).
Step 4 — Resolve Transport Request
Invoke /sap-transport-request to obtain a modifiable TR according to the
user's way_to_get_transport_request policy. Do NOT ask the user for a TR
or call /sap-se01 from this skill. Pass the object context so the
description (if a new TR is created) reflects this package:
/sap-transport-request OBJECT_TYPE=PACKAGE OBJECT_DESCRIPTION=<package-name>
Capture the resolved TR number (e.g. S4DK941110). If /sap-transport-request
returns an error or empty value, stop — do not attempt to create the
package. A transportable Y/Z package always requires a TR.
Step 5 — Create Package via GUI Scripting
The GUI-scripting template is at <SKILL_DIR>/references/sap_se21_create.vbs.
It attaches to the currently logged-in SAP GUI session, navigates to SE21,
fills in the package name + description, and assigns the TR.
Prerequisite: The user must already be logged in (run /sap-login first).
This script does not open a new connection.
Write {RUN_TEMP}\sap_se21_run.ps1:
$content = [System.IO.File]::ReadAllText('<SKILL_DIR>\references\sap_se21_create.vbs', [System.Text.Encoding]::UTF8)
$content = $content.Replace('%%PACKAGE%%', 'THE_PACKAGE')
$content = $content.Replace('%%DESCRIPTION%%', 'THE_DESCRIPTION')
$content = $content.Replace('%%TRANSPORT%%', 'THE_TRANSPORT')
$content = $content.Replace('%%SESSION_LOCK_VBS%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_session_lock.vbs')
# Phase 3.5 session-attach plumbing.
$sessionPath = ''
$content = $content.Replace('%%SESSION_PATH%%', $sessionPath)
$content = $content.Replace('%%ATTACH_LIB_VBS%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_attach_lib.vbs')
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$env:SAPDEV_SESSION_PATH = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_se21_filled.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host 'Done'
The %%SESSION_LOCK_VBS%% token is required because sap_se21_create.vbs
includes the shared session-lock helpers via ExecuteGlobal CreateObject( "Scripting.FileSystemObject").OpenTextFile("%%SESSION_LOCK_VBS%%", 1) .ReadAll() near the top. Omit the substitution and the VBS aborts with
"File not found" at the include line before doing anything.
| Token | Value |
|---|
THE_PACKAGE | Package name from Step 1 |
THE_DESCRIPTION | Short description (default: "sap-dev package <name>") |
THE_TRANSPORT | TR number from Step 4 |
Run:
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_se21_run.ps1"
Execute via the 32-bit cscript host (bare cscript resolves to the 64-bit host; SAP GUI COM needs 32-bit — see CLAUDE.md / feedback_sap_gui_vbs_must_be_32bit):
C:\Windows\SysWOW64\cscript.exe //NoLogo "{RUN_TEMP}\sap_se21_filled.vbs"
Step 6 — Interpret Results
Read the script output line-by-line.
On success (output contains RESULT: PACKAGE_CREATED: <name>):
- Tell the user: "Package
<name> created successfully in TR <TR>."
- Suggest: "You can now use this package for development objects. Set
sap_dev_package in settings.json to <name> for automatic use."
RESULT: PACKAGE_CREATED is now gated on actually reaching the Change
Package editor (SAPLPB_PACKAGE/1000). A canceled create — e.g. an
invalid TR makes SAP report sbar type S "Action was canceled" and fall
back to SAPLPB_ENTRY/100 — is type S, so the E/A status check does NOT
catch it; the screen check does. Previously the VBS emitted a non-fatal WARN
and still printed RESULT: PACKAGE_CREATED (a false success). Now it emits
ERROR: Package <name> NOT created -- did not reach the Change Package screen ... and exits 1, so keying success on the RESULT: line is reliable.
On failure (output starts with ERROR:):
| Error / status-bar message | Cause | Fix |
|---|
SAP GUI is not running / No SAP GUI session found | Not logged in | Run /sap-login then retry |
Did not reach SE21 initial screen | Wrong transaction routing | Verify user has S_TCODE auth for SE21 |
Package <name> NOT created -- did not reach the Change Package screen | Create was canceled — most often a bad/invalid/blank TR (sbar "Action was canceled"), or a build whose popup flow differs | Re-resolve a valid modifiable TR via /sap-transport-request; if the flow differs, re-record |
Description popup field SCOMPKDTLN-CTEXT not found | Different SAP build (older release uses PBSRVSCR; newer uses PBENSCREEN / SCOMPKDTLN) | Re-record via SAP Logon > Help > Scripting Recorder |
TR popup appeared but no TRANSPORT was supplied | Skill bug — Step 4 was skipped | Run /sap-transport-request first |
sbar E "Package already exists" | Race condition with another user | Skip Step 5; report success |
sbar E authorization error | Missing S_DEVELOP for DEVC | Grant S_DEVELOP authorisation |
Step 8 — Delete Package (optional, opt-in)
When to run: The user explicitly asks to delete a package.
Examples:
- "Delete package
ZHKPKG00001"
- "Drop package
ZCMPKG018"
- "Remove development package
Z_OBSOLETE"
Deletion is irreversible. This skill MUST NOT delete a package
without an explicit, deliberate confirmation. The required confirmation
flow is:
-
Run Step 3 (existence check) first. The check returns the
TDEVC row + a count of TADIR children. If the package does not
exist, tell the user and stop — nothing to delete.
-
Show the operator the dependent-object list. Query TADIR
filtered by DEVCLASS = <PKG> and print the rows (one per
OBJECT/OBJ_NAME pair). For more than ~20 children, summarise by
object type. Anything in this list will block the deletion at SAP's
side — but a child may be a true object OR an orphaned directory row
(its definition was already deleted but TADIR survived). Step 8a
sorts the two apart; only true objects must be moved by the operator.
-
Clear orphaned TADIR children (Step 8a — the P2 fix). A
successful SE-delete of a DDIC object / program commonly leaves the
object's TADIR row behind after the definition (DD01L / DD04L /
DD02L / DD40L / TRDIR / …) is already gone. Such an orphan is not a
real object, yet SE21 still counts it as a child and refuses the
package delete (the exact "P2" blocker). Clean these programmatically
via sap_tadir_delete.ps1, which deletes a TADIR row through the
dev-init wrapper FM → TR_TADIR_INTERFACE (the SAP write API for
TADIR; not remote-enabled, so it is reached through the wrapper). It is
safety-guarded: it deletes a row ONLY when the object's definition
is verifiably gone, and refuses (never deletes) a row whose
definition still exists — so a live child can never be orphaned by this
step. It then RFC-re-reads TADIR to confirm each deletion.
First classify every child with -TestOnly (no writes) — build the
-Entries list as OBJECT:OBJ_NAME pairs from the Step 2 list (the
OBJECT is the TADIR object code, e.g. DOMA/DTEL/TABL/TTYP/
PROG/FUGR):
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_tadir_delete.ps1" -TestOnly -Entries "DOMA:ZCMD_X,DTEL:ZCMDE_Y,TABL:ZCMST_Z"
Each line is TADIR: <verdict> <PGMID> <OBJECT> <OBJ_NAME>:
WOULD_DELETE = orphan (definition gone, safe to clean);
REFUSED_DEF_EXISTS = a live object (must be moved by the operator —
it genuinely blocks the delete); REFUSED_UNMAPPED = unknown object type
(treat as live unless the operator is certain). The connection params are
omitted, so the script falls back to this AI session's pinned profile —
the same system the GUI is driving.
-
If any child is REFUSED_DEF_EXISTS / REFUSED_UNMAPPED (a real
object), the package cannot be deleted: tell the operator to move
those objects to another package first, and stop (do not delete).
-
If the only blockers are orphans (WOULD_DELETE), clean them (drop
-TestOnly) and proceed once the summary shows failed=0:
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_tadir_delete.ps1" -Entries "DOMA:ZCMD_X,DTEL:ZCMDE_Y,TABL:ZCMST_Z"
If the script prints STATUS: RFC_ERROR wrapper FM … not found, the
dev-init wrapper is not deployed on this system — fall back to the
manual clean (SE03 → Change Object Directory Entries, report
RSWBO052) or run /sap-dev-init first, then retry.
A TADIR: FAILED … still listed in unreleased request <TR> (E071) line
means the orphan's name is still locked by an entry in an unreleased
transport — TR_TADIR_INTERFACE refuses to delete a locked directory row.
Clear that entry first, then re-run the clean:
/sap-se01 remove-objects <TR> OBJECTS=<OBJ_NAME>
(sap_tadir_delete.ps1 emits the exact <TR> + OBJECTS= hint on the
FAILED line.) This is the same name-lock that blocks a re-create; the
script never reports a locked row as deleted.
-
Resolve the TR. Query TADIR for
PGMID='R3TR' AND OBJECT='DEVC' AND OBJ_NAME=<PKG>; if DEVCLASS
starts with $ it's local — TR not needed. Otherwise resolve a
modifiable TR via /sap-transport-request.
-
Mandatory confirmation prompt:
Deleting development package <PKG> is irreversible.
Children in TADIR (<count>): <list-or-summary>.
Orphaned directory rows cleaned in Step 8a (<count>): <list>.
Linked TR (if transportable): <TR>.
Type yes to proceed, anything else to abort.
Do not launch the VBS without an explicit yes. A reply of "y", "yep",
"ok", "go", "delete", or anything other than the literal yes
counts as aborted.
Tokens
| Token | Description | Empty? |
|---|
%%PACKAGE%% | Package name (UPPERCASE) | required |
%%TRANSPORT%% | TR for the post-delete prompt | empty when local or already locked |
%%SESSION_LOCK_VBS%% | path to sap_session_lock.vbs | required |
Generate the filled-in VBScript
Write {RUN_TEMP}\sap_se21_delete_run.ps1:
$skillDir = '<SKILL_DIR>'
$tpl = "$skillDir\references\sap_se21_delete.vbs"
$content = [System.IO.File]::ReadAllText($tpl, [System.Text.Encoding]::UTF8)
$content = $content.Replace('%%PACKAGE%%', 'THE_PACKAGE')
$content = $content.Replace('%%TRANSPORT%%', 'THE_TRANSPORT')
# se21's %%PACKAGE%% above = the package being deleted; the ECC6 Object Directory
# Entry orphan-fill uses a SEPARATE token %%PACKAGE2%% (empty for a package delete
# => Local Object if that popup ever appears). %%ORIG_LANG%% '' => VBS uses 'E'.
$content = $content.Replace('%%PACKAGE2%%', '')
$content = $content.Replace('%%ORIG_LANG%%', '')
$content = $content.Replace('%%SESSION_LOCK_VBS%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_session_lock.vbs')
# Phase 3.5 session-attach plumbing.
$sessionPath = ''
$content = $content.Replace('%%SESSION_PATH%%', $sessionPath)
$content = $content.Replace('%%ATTACH_LIB_VBS%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_attach_lib.vbs')
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$env:SAPDEV_SESSION_PATH = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_se21_delete_run.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host 'Done'
Run:
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_se21_delete_run.ps1"
C:\Windows\SysWOW64\cscript.exe //NoLogo "{RUN_TEMP}\sap_se21_delete_run.vbs"
Behaviour Notes
- Delete is invoked from the SE21 initial screen. The script does
NOT open the package editor first — it fills
ctxtPBENSCREEN-PACKNAME and sends Shift+F2 (sendVKey 14)
directly.
- Confirmation popup style. The recording uses
wnd[1]/usr/btnBUTTON_1 (a generic info-popup button), not the
Yes/No btnSPOP-OPTION1. The VBS tries both, plus
tbar[0]/btn[0] (Continue) and sendVKey 0 (Enter), via a
generic active-window walker.
- Post-delete TR popup. For transportable packages, SAP prompts
via
ctxtKO008-TRKORR. The VBS fills %%TRANSPORT%% and presses
Enter. If the popup appears with %%TRANSPORT%% empty, the VBS
exits 1 with ERROR: SAP prompted for a transport request but TRANSPORT is empty.
- Verification. After the deletion path the script re-fills the
name field and presses
btnDISPLAY. If the editor opens (the
ctxtPBENSCREEN-PACKNAME field on the initial screen disappears),
the package still exists and the VBS reports
ERROR: Package still exists after delete with a hint about
TADIR children blocking deletion.
Outputs
| Last line | Meaning |
|---|
SUCCESS: Package <PKG> deleted. | Package is gone — sbar status echoed above. |
ERROR: Package still exists after delete (Display opened the editor). | Package still has TADIR children. If they are orphans (definition gone), Step 8a should have cleaned them first — re-run Step 8a's classify (-TestOnly) and clean, then retry. If they are live objects, move them to another package and retry. |
ERROR: SAP prompted for a transport request but TRANSPORT is empty. | Transportable package; resolve a modifiable TR via /sap-transport-request and re-run. |
Other ERROR: … | Surface verbatim. |
Post-delete RFC verification (recommended)
Re-run the existence check from Step 3 (RFC_READ_TABLE on TDEVC);
expect zero rows. Also check TADIR (PGMID='R3TR' AND OBJECT='DEVC' AND OBJ_NAME=<PKG>) — a row left there indicates a half-deletion
(TDEVC gone but object directory still references it); clean up via
SE03.
After success, proceed to Step 7 (cleanup). Skip Step 6 — no
create reporting applies.
Step 7 — Clean Up
cmd /c del "{RUN_TEMP}\sap_check_package_run.ps1" "{RUN_TEMP}\sap_check_package_filled.ps1" "{RUN_TEMP}\sap_se21_run.ps1" "{RUN_TEMP}\sap_se21_filled.vbs" "{RUN_TEMP}\sap_se21_delete_run.vbs" "{RUN_TEMP}\sap_se21_delete_run.ps1"
Final — Log End
Log the run-end record. Best-effort.
On success:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_se21_run.json" -Status SUCCESS -ExitCode 0
On failure:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_se21_run.json" -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>"
Suggested <CLASS>: SE21_FAILED, TR_RESOLUTION_FAILED, GUI_TIMEOUT.
Security Note
The generated sap_check_package_filled.ps1 contains the SAP password in
plain text and is deleted after Step 7. The GUI-scripting file
sap_se21_filled.vbs does not contain a password — it attaches to an
already-logged-in session.
Component IDs (S/4HANA 1909, verified 2026-04-30)
| Screen | Program / Dynpro | Field IDs |
|---|
| SE21 initial | SAPLPB_ENTRY / 100 | radPBENSCREEN-CHECKPACK, ctxtPBENSCREEN-PACKNAME, btnCREATE |
Description popup (wnd[1] "Create Package") | wrapper SAPLPB_SERVICE / 170 | txtSCOMPKDTLN-CTEXT; toolbar tbar[0]/btn[0] (Continue) |
TR-prompt popup (wnd[1] "Prompt for transportable workbench request") | — | ctxtKO008-TRKORR; toolbar tbar[0]/btn[0] (Continue) |
| Final screen | SAPLPB_PACKAGE / 1000 "Change Package" | — |
If component IDs differ on your build, re-record via SAP Logon > Help >
Scripting Recorder and update sap_se21_create.vbs.