| name | sap-change-package |
| description | Changes the package (TADIR-DEVCLASS) assignment of an SAP repository object
via the "Object Directory Entry" dialog (Goto > Object Directory Entry).
Routes by object type to SE38 / SE37 / SE24 / SE11 / SE91 / CMOD (the last
for enhancement projects — used by /sap-cmod). Handles three
flows automatically based on current vs new package locality:
(a) $TMP → transportable (Z*/Y*): resolves a modifiable TR via
/sap-transport-request, then enters the new package + TR.
(b) transportable A → transportable B: pre-checks E071/E070 to ensure
the object is NOT linked to a modifiable TR (would block the move).
(c) transportable → $TMP: confirms the move and presses "Local object".
Verifies via TADIR re-query.
Prerequisites: Active SAP GUI session (use /sap-login first).
|
| argument-hint | <OBJECT_TYPE> <OBJECT_NAME> <NEW_PACKAGE> |
SAP Change Package Skill
You change the package assignment (TADIR-DEVCLASS) of an SAP repository
object via the standard "Object Directory Entry" dialog.
Task: $ARGUMENTS
Shared Resources
| File | Purpose |
|---|
<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 — used when moving from $TMP to a transportable package |
<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 sap_user.
Set {WORK_TEMP} = {work_dir}\temp. Ensure it 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. The helper persists run_id in a state file
({RUN_TEMP}\sap_change_package_run.json) so subsequent steps and the
final log-end call append to the same run. Best-effort: silently no-ops
if userConfig.log_enabled=false or the lib can't load.
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{RUN_TEMP}\sap_change_package_run.json" -Skill sap-change-package -ParamsJson "{\"object_type\":\"<OBJECT_TYPE>\",\"object_name\":\"<OBJECT_NAME>\",\"new_package\":\"<NEW_PACKAGE>\"}"
Step 0.6 — Safety Gate (Rule 0 — safety_policy.md)
This skill mutates the SAP system (TADIR package reassignment). 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-change-package
| 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
Required:
| Arg | Description | Example |
|---|
OBJECT_TYPE | Object type (see routing table below) | REPORT |
OBJECT_NAME | Object name | ZHKTEST003 |
NEW_PACKAGE | Target package — $TMP (or any $*) for local, Z* / Y* for transportable | ZHKA011 |
If any argument is missing, ask:
Provide <OBJECT_TYPE> <OBJECT_NAME> <NEW_PACKAGE> (e.g. REPORT ZHKTEST003 ZHKA011).
Object type → transaction routing
| OBJECT_TYPE | Transaction | TADIR OBJECT |
|---|
REPORT / PROGRAM / FUGR | SE38 | PROG / FUGR |
FM (function module) | SE37 | FUNC |
CLASS / INTERFACE | SE24 | CLAS / INTF |
TABLE / VIEW / DTEL / STRUCTURE / TABLETYPE / TYPEGROUP / DOMAIN / SEARCHHELP / LOCKOBJECT | SE11 | matching DDIC type |
MSGCLASS (message class) | SE91 | MSAG |
CMOD / ENHANCEMENTPROJECT / ENHPROJ (enhancement project) | CMOD | CMOD |
Step 2 — Read Current Package from TADIR
Query TADIR via /sap-se16n:
SELECT
PGMID
OBJECT
OBJ_NAME
DEVCLASS
FILTER
PGMID EQ R3TR
OBJECT EQ <TADIR_OBJECT_FROM_TABLE>
OBJ_NAME EQ <OBJECT_NAME>
Capture the row's DEVCLASS as CURRENT_PACKAGE. If TADIR has no row → the
object does not exist in the system; report and stop.
Short-circuit: if CURRENT_PACKAGE = NEW_PACKAGE (case-insensitive),
nothing to do — report "already in package " and stop.
Step 3 — Decide the Flow
Pick a MODE from the locality of CURRENT_PACKAGE and NEW_PACKAGE.
A package is local if it starts with $ (e.g. $TMP, $VOL); otherwise
transportable (Z*/Y*/customer namespace).
| CURRENT | NEW | MODE | Notes |
|---|
$* | Z* / Y* | TMP_TO_TRANSPORT | Need a modifiable TR. |
Z* / Y* | Z* / Y* | TRANSPORT_TO_TRANSPORT | Object MUST NOT be on a modifiable TR (else SAP refuses with an Error popup: Object directory entry R3TR <OBJ> <NAME> locked for request/task <TR>). |
Z* / Y* | $* | TRANSPORT_TO_LOCAL | Confirm popup + "Local object" button. Object MUST NOT be on a modifiable TR — SAP refuses the move with the same lock Error popup as TRANSPORT_TO_TRANSPORT. The previously-linked TR record itself is unaffected. |
$* | $* | LOCAL_TO_LOCAL | Just enter new $* package + Enter. No TR. |
Step 4 — Pre-checks per Mode
TRANSPORT_TO_TRANSPORT and TRANSPORT_TO_LOCAL
Both modes require the object to NOT be linked to a modifiable task/TR.
Query E071 via /sap-se16n to check if the object is currently in any
open task / TR:
SELECT
TRKORR
PGMID
OBJECT
OBJ_NAME
FILTER
PGMID EQ R3TR
OBJECT EQ <TADIR_OBJECT>
OBJ_NAME EQ <OBJECT_NAME>
For each TRKORR returned, query E070 for TRSTATUS:
SELECT
TRKORR
TRFUNCTION
TRSTATUS
FILTER
TRKORR EQ <each-TR-from-E071>
If any TRSTATUS = D (Modifiable) or L (Locked, in test) — the move
will fail. Tell the user:
"Object <NAME> is currently linked to modifiable TR <TR>. Release it
first via /sap-se01 release <TR>, or move within the same TR's package
scope. Aborting."
If the only linked TRs are R (Released), proceed.
The VBS templates also detect this condition at runtime, locale-independently:
if SAP shows a message-only popup (probed by DDIC control id txtMESSTXT1, with
no TR-description field txtKO013-AS4TEXT and no Yes/No option
btnSPOP-OPTION1) after pressing Enter on the package field, the script treats
it as a refusal/lock error, reports ERROR: SAP refused the package change: <message> (message text echoed for diagnostics only) and exits 1. The popup is
never classified by window title — an earlier title-based check
(LCase(title)="error") silently dismissed the lock popup under ZH/JA logons
and let the run report DONE without the move.
TMP_TO_TRANSPORT
Resolve a modifiable TR by delegating to /sap-transport-request:
/sap-transport-request OBJECT_TYPE=<TADIR_OBJECT> OBJECT_DESCRIPTION=<OBJECT_NAME>
Capture the returned TRKORR as RESOLVED_TR. This honours
way_to_get_transport_request (DEFAULT / ASK / CREATE_NEW). If
/sap-transport-request reports ERROR, stop and surface it.
Also build a TR description for the "Create Request" popup the dialog may
show: use <OBJECT_TYPE>_<OBJECT_NAME>_pkg truncated to 60 chars, or honour
rule_of_tr_description if set.
LOCAL_TO_LOCAL
No TR resolution or lock check needed.
Confirmation prompt for any *_TO_LOCAL move
After pre-checks pass, confirm with the user (one prompt):
"Move <NAME> from <CURRENT_PACKAGE> to <NEW_PACKAGE> (local)?
The object will no longer be transported. Proceed? (yes / no)"
Only proceed on yes.
Step 5 — Ensure SAP GUI Login
Requires an active SAP GUI session. If not logged in, run /sap-login.
Step 6 — Run the Appropriate Change-Package VBS
| Transaction | Template |
|---|
| SE38 | ./references/sap_change_package_se38.vbs |
| SE37 | ./references/sap_change_package_se37.vbs |
| SE24 | ./references/sap_change_package_se24.vbs |
| SE11 | ./references/sap_change_package_se11.vbs |
| SE91 | ./references/sap_change_package_se91.vbs |
| CMOD | ./references/sap_change_package_cmod.vbs |
Tokens:
| Token | Used by |
|---|
%%OBJECT_NAME%% | all |
%%OBJECT_TYPE%% | SE11 only (radio + name field selection) |
%%NEW_PACKAGE%% | all |
%%TRANSPORT%% | TMP_TO_TRANSPORT mode (pre-resolved TR); empty otherwise |
%%TR_DESCRIPTION%% | TMP_TO_TRANSPORT mode (used if dialog asks for new-TR description) |
Token-replace into {RUN_TEMP}\sap_change_package_<TXN>_run.ps1:
$content = [System.IO.File]::ReadAllText('<SKILL_DIR>\references\sap_change_package_<TXN>.vbs', [System.Text.Encoding]::UTF8)
$content = $content -replace '%%OBJECT_NAME%%','THE_NAME'
$content = $content -replace '%%OBJECT_TYPE%%','THE_TYPE' # SE11 only
$content = $content -replace '%%NEW_PACKAGE%%','THE_PKG'
$content = $content -replace '%%TRANSPORT%%','THE_TR'
$content = $content -replace '%%TR_DESCRIPTION%%','THE_TR_DESC'
# 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_change_package_<TXN>_run.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host 'Done'
Run via 32-bit cscript:
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_change_package_<TXN>_run.ps1"
C:/Windows/SysWOW64/cscript.exe //NoLogo {RUN_TEMP}\sap_change_package_<TXN>_run.vbs
Each VBS emits a stable contract:
INFO: ... progress lines
STATUS_TYPE: <S|W|E|A|I> and STATUS_TEXT: <text> (final status bar)
VERIFY_HINT: confirm TADIR-DEVCLASS=<pkg> ... — the GUI move is
sbar-confirmed only; this line reminds the caller that the authoritative
confirmation is the TADIR re-query in Step 7.
- Final line:
DONE (success) or ERROR: ... (failure)
The SUCCESS gate inside each VBS requires both STATUS_TYPE not E/A
and no modal popup left on screen — a lingering popup (or a lock/refusal
message popup) exits 1. All popup classification is by DDIC control id +
sbar.MessageType; window titles are never used for control flow (they are
echoed for diagnostics only). Because DONE is sbar-confirmed only, Step 7's
TADIR re-query is the load-bearing verification — do not skip it.
Step 7 — Verify
Re-query TADIR (Step 2 query). Expect DEVCLASS = NEW_PACKAGE.
If MODE was TMP_TO_TRANSPORT, also verify the object is now linked to
RESOLVED_TR via E071 (one row with TRKORR = RESOLVED_TR).
Step 8 — Report
Changed package of <OBJECT_TYPE> <OBJECT_NAME>:
before : <CURRENT_PACKAGE>
after : <NEW_PACKAGE>
mode : <MODE>
TR : <RESOLVED_TR | n/a>
sbar : [<TYPE>] <TEXT>
TADIR : verified ✓ | mismatch ✗
Final — Log End
Log the run-end record. Best-effort: silently no-ops if logging disabled.
On success:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_change_package_run.json" -Status SUCCESS -ExitCode 0
On failure (substitute <CLASS> and short message):
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_change_package_run.json" -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>"
Suggested <CLASS>: CHANGE_PACKAGE_FAILED, OBJECT_LOCKED_IN_TR, TR_RESOLUTION_FAILED, GUI_TIMEOUT.
Component IDs (for reference)
Common (Object Directory Entry dialog — opens at wnd[1])
| Element | ID |
|---|
| Change button (display→edit) | wnd[1]/tbar[0]/btn[6] |
| Package field | wnd[1]/usr/ctxtKO007-L_DEVCLASS |
| Enter / OK | wnd[1]/tbar[0]/btn[0] |
| Local object button | wnd[1]/tbar[0]/btn[12] |
| Create Request button | wnd[1]/tbar[0]/btn[8] |
| TR field (when entering existing TR) | wnd[1]/usr/ctxtKO008-TRKORR |
| Confirm popup (info) | wnd[2]/tbar[0]/btn[0] |
| New-TR description field | wnd[2]/usr/txtKO013-AS4TEXT |
Per-transaction "Goto > Object Directory Entry" menu paths
| Transaction | Menu | Object name field |
|---|
| SE38 | mbar/menu[2]/menu[3] | wnd[0]/usr/ctxtRS38M-PROGRAMM |
| SE37 | mbar/menu[2]/menu[5] | wnd[0]/usr/ctxtRS38L-NAME |
| SE24 | mbar/menu[2]/menu[4] | wnd[0]/usr/ctxtSEOCLASS-CLSNAME |
| SE11 | mbar/menu[2]/menu[0] | ctxtRSRD1-<key>_VAL (radio-dependent) |
| SE91 | mbar/menu[2]/menu[0] | wnd[0]/usr/ctxtRSDAG-ARBGB (then btnANZTASTE Display first) |
| CMOD | mbar/menu[2]/menu[3] | wnd[0]/usr/ctxtMOD0-NAME (then sendVKey 0 to select the project first) |
Menu indices are version-specific (SAP GUI 7.60 / S/4HANA 1909). If a future
release renumbers the Goto menu, update the MENU_OBJECT_DIR constant in
each VBS.
TADIR / E070 / E071 Field Reference
| Table | Field | Meaning |
|---|
TADIR | PGMID | R3TR for repository objects |
TADIR | OBJECT | Object type (PROG / CLAS / TABL / DOMA / MSAG / FUNC / FUGR / CMOD …) |
TADIR | OBJ_NAME | Object name |
TADIR | DEVCLASS | Package — $* = local, otherwise transportable |
TADIR | MASTERLANG | Original language of the object |
E071 | TRKORR | TR / task that contains this object |
E070 | TRSTATUS | D Modifiable, L Locked-in-test, O Release started, R Released |
E070 | TRFUNCTION | K Workbench, W Customizing |
Limitations
- The "to-local" confirmation popup (wnd[2] btn[0]) shape can vary across
S/4HANA releases; the VBS handles the common case from the recordings.
If a different popup appears, the VBS will surface the sbar text.
- The current logic assumes the object exists in TADIR. For freshly-created
$TMP objects that haven't been saved yet, run the relevant deploy skill
(sap-se38/se37/se24/se11/se91) first.
- For
TRANSPORT_TO_TRANSPORT moves where the user wants to release the
blocking TR, suggest /sap-se01 release <TR> and re-run this skill.