abapgit-agent 1.15.3 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/abap/CLAUDE.md CHANGED
@@ -179,26 +179,27 @@ Anything else (SAP namespace object, or SAP-delivered package)?
179
179
 
180
180
  ---
181
181
 
182
- ### 4. Use Syntax Command Before Commit (for CLAS, INTF, PROG, DDLS)
182
+ ### 4. Use Syntax Command Before Commit (for CLAS, INTF, PROG, DDLS, FUGR)
183
183
 
184
184
  ```
185
185
  ❌ WRONG: Make changes → Commit → Push → Pull → Find errors → Fix → Repeat
186
186
  ✅ CORRECT: Make changes → Run syntax → Fix locally → Commit → Push → Pull → Done
187
187
  ```
188
188
 
189
- **For CLAS, INTF, PROG, DDLS files**: Run `syntax` command BEFORE commit to catch errors early.
189
+ **For CLAS, INTF, PROG, DDLS, FUGR files**: Run `syntax` command BEFORE commit to catch errors early.
190
190
 
191
191
  ```bash
192
192
  # Check syntax of local code (no commit/push needed)
193
193
  # Use the actual filename from your project (name comes from objects.local.md)
194
194
  abapgit-agent syntax --files src/<name>.clas.abap
195
195
  abapgit-agent syntax --files src/<name>.ddls.asddls
196
+ abapgit-agent syntax --files src/<name>.fugr.<fm_name>.abap
196
197
 
197
198
  # Check multiple INDEPENDENT files
198
199
  abapgit-agent syntax --files src/<name1>.clas.abap,src/<name2>.clas.abap
199
200
  ```
200
201
 
201
- **For other types (FUGR, TABL, etc.)**: Skip syntax, proceed to commit/push/pull.
202
+ **For other types (TABL, STRU, DCLS, etc.)**: Skip syntax, proceed to commit/push/pull.
202
203
 
203
204
  **Why use syntax command?**
204
205
  - Catches syntax errors BEFORE polluting git history with fix commits
@@ -424,7 +425,39 @@ After activating a class, stop and tell the user: `"Class is activated. Run with
424
425
 
425
426
  ---
426
427
 
427
- ### 10. Probe and PoC Objects Always Z/Y, Never in SAP Packages
428
+ ### 10. Never Run `drop` Command Without Explicit Permission
429
+
430
+ **Never call `abapgit-agent drop` unless the user explicitly confirms.** Dropping an object physically deletes it from the ABAP system — this is irreversible without a subsequent pull.
431
+
432
+ **When to SUGGEST `drop --pull`:**
433
+
434
+ When an object is in a broken or inconsistent state that cannot be resolved by re-pulling:
435
+ - `inspect` reports **"INCLUDE report ... not found"** (stale include in ABAP not present in git)
436
+ - Pull repeatedly fails with **"Activation cancelled"** and re-pulling doesn't fix it
437
+ - Object has a corrupt inactive version that permanently blocks activation
438
+
439
+ In these cases, suggest the fix and wait for confirmation:
440
+
441
+ ```
442
+ "The object ZCL_FOO has an inconsistent state (stale include in ABAP).
443
+ This can be fixed by dropping and re-pulling it:
444
+
445
+ abapgit-agent drop --files abap/zcl_foo.clas.abap --pull
446
+
447
+ This will delete ZCL_FOO from ABAP and immediately re-activate it from git.
448
+ Confirm when ready."
449
+ ```
450
+
451
+ **What `drop --pull` does:**
452
+ 1. Physically deletes the object from the ABAP system
453
+ 2. Immediately re-pulls and re-activates it from git (clean state)
454
+ 3. Does NOT touch the git repository file
455
+
456
+ **Constraint:** Only CLAS, INTF, PROG, TABL, TTYP are supported. DTEL (data elements) are rejected — edit the XML and use `pull` instead.
457
+
458
+ ---
459
+
460
+ ### 11. Probe and PoC Objects — Always Z/Y, Never in SAP Packages
428
461
 
429
462
  ```
430
463
  ❌ WRONG: Create a probe/PoC object with the project's SAP namespace prefix
@@ -450,7 +483,7 @@ Never assume — wait for the user's answer before proceeding.
450
483
 
451
484
  ---
452
485
 
453
- ### 11. Troubleshooting ABAP Issues
486
+ ### 12. Troubleshooting ABAP Issues
454
487
 
455
488
  | Symptom | Tool | When |
456
489
  |---|---|---|
@@ -461,7 +494,16 @@ Never assume — wait for the user's answer before proceeding.
461
494
 
462
495
  **Critical rules for `debug` sessions:**
463
496
 
464
- 1. **Always use `--json`** for all debug commands (`attach`, `vars`, `stack`, `step`) — human output is not machine-parseable
497
+ ```
498
+ ❌ WRONG: abapgit-agent debug attach
499
+ ❌ WRONG: abapgit-agent debug vars
500
+ ❌ WRONG: abapgit-agent debug step --type continue
501
+ ✅ CORRECT: abapgit-agent debug attach --json > /tmp/a.json 2>&1 &
502
+ ✅ CORRECT: abapgit-agent debug vars --json
503
+ ✅ CORRECT: abapgit-agent debug step --type continue --json
504
+ ```
505
+
506
+ 1. **Always use `--json`** for ALL debug commands (`attach`, `vars`, `stack`, `step`) — human output is not machine-parseable. This is non-negotiable.
465
507
  2. **Attach BEFORE trigger** — start `debug attach --json` in background first, wait for `"Listener active"`, THEN fire the trigger (`unit`/`pull`/`run`)
466
508
  3. **Never pull to trigger** if a simpler trigger works — use `unit` when a test exists, `run` for a class runner; use `pull` only when the bug is specifically in the pull flow
467
509
  4. **Never pass `--session`** to `step/vars/stack` — it bypasses the daemon and causes errors
@@ -469,7 +511,12 @@ Never assume — wait for the user's answer before proceeding.
469
511
 
470
512
  **Finding the right line number for a breakpoint:**
471
513
 
472
- Use `view --full --lines` to get ready-to-use `debug set` commands per method:
514
+ ```
515
+ ❌ WRONG: abapgit-agent debug set --objects ZCL_FOO:42 ← never guess a line number
516
+ ✅ CORRECT: abapgit-agent view --objects ZCL_FOO --full --lines ← get exact line from output
517
+ ```
518
+
519
+ Always run `view --full --lines` first — it prints a ready-to-use `debug set` command for every method. Never guess or estimate line numbers.
473
520
 
474
521
  ```bash
475
522
  abapgit-agent view --objects ZCL_FOO --full --lines
@@ -509,7 +556,8 @@ abapgit-agent debug set --objects LSUSRU04:50
509
556
 
510
557
  Minimal correct sequence:
511
558
  ```bash
512
- abapgit-agent debug set --objects ZCL_FOO:42 # 1. set breakpoint
559
+ abapgit-agent view --objects ZCL_FOO --full --lines # 0. get exact line number from output hint
560
+ abapgit-agent debug set --objects ZCL_FOO:42 # 1. set breakpoint (use line from step 0)
513
561
  abapgit-agent debug attach --json > /tmp/a.json 2>&1 & # 2. attach (background)
514
562
  until grep -q "Listener active" /tmp/a.json 2>/dev/null; do sleep 0.3; done
515
563
  abapgit-agent unit --files src/zcl_foo.clas.testclasses.abap > /tmp/t.json 2>&1 & # 3. trigger
@@ -522,7 +570,7 @@ abapgit-agent debug step --type continue --json # 4. release
522
570
 
523
571
  ---
524
572
 
525
- ### 12. abaplint — Static Analysis (Optional, Project-Controlled)
573
+ ### 13. abaplint — Static Analysis (Optional, Project-Controlled)
526
574
 
527
575
  abaplint is **optional**. Only run it if `.abaplint.json` exists in the project root.
528
576
  Each project defines its own rules — never assume which rules are active.
@@ -722,18 +770,18 @@ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
722
770
 
723
771
  ```
724
772
  Modified ABAP files?
725
- ├─ CLAS/INTF/PROG/DDLS files?
773
+ ├─ CLAS/INTF/PROG/DDLS/FUGR files?
726
774
  │ ├─ Independent files (no cross-dependencies)?
727
775
  │ │ └─ ✅ Use: syntax → [abaplint] → commit → push → pull --sync-xml
728
776
  │ └─ Dependent files (interface + class, class uses class)?
729
777
  │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml
730
- └─ Other types (FUGR, TABL, STRU, DTEL, TTYP, etc.)?
778
+ └─ Other types (TABL, STRU, DTEL, TTYP, etc.)?
731
779
  ├─ XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG)?
732
780
  │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files abap/ztable.tabl.xml --sync-xml
733
781
  ├─ DCLS (CDS access control)?
734
782
  │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files abap/zc_view.dcls.xml --sync-xml
735
783
  │ ⚠️ Pass the .xml file — pull --files does NOT accept .asdcls extensions
736
- └─ FUGR and other complex objects?
784
+ └─ Other complex objects?
737
785
  └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml → (if errors: inspect)
738
786
 
739
787
  [abaplint] = run abapgit-agent lint only if .abaplint.json exists in repo root
@@ -761,6 +809,7 @@ Modified ABAP files?
761
809
  | `ref --topic cds-testing` | CDS Testing (Test Double Framework) |
762
810
  | `ref --topic json` | JSON Handling |
763
811
  | `ref --topic common-errors` | Common ABAP Errors - Quick Fixes |
812
+ | `ref --topic string-template` | String Templates — syntax, escaping `\{` `\}`, JSON payloads |
764
813
  | `ref --topic abapgit` | abapGit XML Metadata — **use for CDS/DDLS XML**, also CLAS, INTF, PROG, DCLS, FUGR |
765
814
  | `ref --topic abapgit-xml-only` | abapGit XML Metadata — XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG) |
766
815
  | `ref --topic abapgit-fugr` | abapGit XML Metadata — Function Group (FUGR) details |
@@ -80,6 +80,41 @@ lv_response = lv_response && '"key":"val"}'.
80
80
  | `DATA(lv) = 'literal'.` followed by `&&` | ❌ | Infers `C LENGTH N`, truncates |
81
81
  | `DATA(lv) = 'literal'.` used only in `\|{ lv }\|` | ⚠️ | Technically works but misleading — prefer explicit type |
82
82
  | `DATA(lv) = 'X'.` used as abap_bool flag | ✅ | `C LENGTH 1` is correct for flags |
83
+ | `DATA(lv) = COND string( WHEN ... THEN str+off ELSE str ).` | ❌ | Offset notation on `string` inside COND raises `CX_SY_RANGE_OUT_OF_BOUNDS` at runtime |
84
+
85
+ ### The offset-notation-in-COND trap
86
+
87
+ Offset/length notation (`str+off` or `str(len)`) on a `string`-typed variable **cannot appear
88
+ as a result expression inside `COND`** — even when the `COND` is explicitly typed as `string`.
89
+ ABAP evaluates both branch types at generation time and the offset expression on a dynamic
90
+ string is invalid, causing a `CX_SY_RANGE_OUT_OF_BOUNDS` crash at runtime.
91
+
92
+ ```abap
93
+ DATA lv_file TYPE string VALUE 'src/foo.clas.abap'.
94
+ DATA(lv_pos) = find( val = lv_file sub = '/' occ = -1 ).
95
+
96
+ * WRONG — CX_SY_RANGE_OUT_OF_BOUNDS at runtime
97
+ DATA(lv_path) = COND string(
98
+ WHEN lv_pos > 0 THEN '/' && lv_file(lv_pos + 1) " ← offset on string = crash
99
+ ELSE '/' ).
100
+
101
+ * CORRECT — use substring() which returns string and is safe in COND
102
+ DATA(lv_path) = COND string(
103
+ WHEN lv_pos > 0 THEN '/' && substring( val = lv_file len = lv_pos + 1 )
104
+ ELSE '/' ).
105
+
106
+ * ALSO CORRECT — keep IF/ELSE with DATA x TYPE string
107
+ DATA lv_path TYPE string.
108
+ IF lv_pos > 0.
109
+ DATA(lv_len) = lv_pos + 1.
110
+ lv_path = '/' && lv_file(lv_len).
111
+ ELSE.
112
+ lv_path = '/'.
113
+ ENDIF.
114
+ ```
115
+
116
+ **Rule**: whenever the prefer_inline quickfix would produce `COND ... THEN str+off` or
117
+ `COND ... THEN str(len)`, use `substring()` instead, or keep the `IF/ELSE` form.
83
118
 
84
119
  ### Rule of thumb
85
120
 
@@ -8,7 +8,7 @@ grand_parent: ABAP Development
8
8
 
9
9
  # Common ABAP Errors - Quick Fixes
10
10
 
11
- **Searchable keywords**: error, syntax error, field mismatch, fixed point, comma, host variable, @ prefix, type incompatible
11
+ **Searchable keywords**: error, syntax error, field mismatch, fixed point, comma, host variable, @ prefix, type incompatible, string template, expression limiter, literal brace, http 500, xml metadata mismatch, interface description, descript, recompilation, offset notation, cond trap, cx_sy_range_out_of_bounds
12
12
 
13
13
  ## Fixed Point Arithmetic Error
14
14
 
@@ -119,6 +119,83 @@ or declare the variable explicitly with `TYPE string`.
119
119
 
120
120
  ---
121
121
 
122
+ ## Offset Notation on String in COND — CX_SY_RANGE_OUT_OF_BOUNDS
123
+
124
+ **Symptom**: Pull succeeds but ABAP crashes at runtime with `CX_SY_RANGE_OUT_OF_BOUNDS`
125
+ inside a method using `COND`.
126
+
127
+ **Root cause**: Offset/length notation on a `string` variable (`str+off`, `str(len)`)
128
+ **cannot be used as a result expression inside `COND`** — even with an explicit `COND string(...)` type.
129
+ The abaplint `prefer_inline` quickfix can introduce this automatically when converting
130
+ an `IF/ELSE` block that assigns from a string-variable offset.
131
+
132
+ ```abap
133
+ * WRONG — crashes at runtime with CX_SY_RANGE_OUT_OF_BOUNDS
134
+ DATA(lv_path) = COND string(
135
+ WHEN lv_pos > 0 THEN '/' && lv_file(lv_pos + 1) " ← string offset in COND
136
+ ELSE '/' ).
137
+
138
+ * CORRECT — use substring() instead
139
+ DATA(lv_path) = COND string(
140
+ WHEN lv_pos > 0 THEN '/' && substring( val = lv_file len = lv_pos + 1 )
141
+ ELSE '/' ).
142
+ ```
143
+
144
+ **Fix**: Replace `str+off` / `str(len)` inside `COND` results with `substring( val = str off = off )` or
145
+ `substring( val = str len = len )`, or revert to the `IF/ELSE` form with `DATA x TYPE string`.
146
+
147
+ → See `abaplint.md` for the full safe/unsafe table for `prefer_inline`.
148
+
149
+ ---
150
+
151
+ ## XML Metadata Mismatch — HTTP 500 on Pull
152
+
153
+ **Symptom**: `pull` returns HTTP 500 with no short dump (ST22) for the request user.
154
+ Other objects pull fine; only a specific interface or class fails.
155
+
156
+ **Root cause**: The `.intf.xml` or `.clas.xml` file in git has a metadata field (e.g. `DESCRIPT`,
157
+ `UNICODE`, `STATE`) that differs from the active ABAP system value. abapGit writes the git
158
+ value back to VSEOINTERF/VSEOCLASS, which forces the SAP class framework to recompile all
159
+ implementing/inheriting objects. If any implementing class has a syntax or generation
160
+ issue with the current active source, the recompilation crashes outside the normal exception
161
+ handler — producing HTTP 500 with no dump.
162
+
163
+ **Diagnosis**:
164
+ 1. No dump in ST22 for your user → not a user-code crash
165
+ 2. Only one specific INTF/CLAS fails → metadata delta in its XML triggers recompilation
166
+ 3. Check what differs: `abapgit-agent view --objects ZIF_MY_INTF --type INTF` and compare
167
+ `DESCRIPT` / other fields with the XML file in git
168
+
169
+ **Fix**: Sync the XML to match the ABAP system value. The easiest way:
170
+
171
+ ```bash
172
+ # Pull without --sync-xml first to let abapGit rewrite the XML from the system
173
+ abapgit-agent pull --files abap/zif_my_intf.intf.abap --sync-xml
174
+ ```
175
+
176
+ Or manually correct the field in the XML file to match the system value, then push and pull.
177
+
178
+ **Prevention**: Always use `--sync-xml` after pulling new objects so the XML stays in sync
179
+ with what the ABAP serializer produces.
180
+
181
+ ---
182
+
183
+ **Error**: `Expression limiter '{' in string template not followed by space`
184
+
185
+ Literal `{` and `}` (e.g. in JSON payloads) must be escaped as `\{` and `\}` inside `|...|`.
186
+
187
+ ```abap
188
+ " WRONG — unescaped { in JSON triggers parse error
189
+ rv_result = |{"success":"X","name":"{ lv_name }"}|.
190
+
191
+ " CORRECT
192
+ rv_result = |\{"success":"X","name":"{ lv_name }"\}|.
193
+ ```
194
+
195
+ → Full escaping rules and examples: `abapgit-agent ref --topic string-template`
196
+
197
+ ---
198
+
122
199
  ## See Also
123
200
  - **ABAP SQL** (sql.md) - for SQL syntax rules
124
201
  - **CDS Views** (cds.md) - for CDS selection patterns
@@ -0,0 +1,84 @@
1
+ ---
2
+ layout: default
3
+ title: String Templates
4
+ nav_order: 25
5
+ parent: ABAP Coding Guidelines
6
+ grand_parent: ABAP Development
7
+ ---
8
+
9
+ # String Templates
10
+
11
+ **Searchable keywords**: string template, pipe, `|...|`, literal brace, escape, expression limiter, JSON, `\{`, `\}`
12
+
13
+ ## Syntax
14
+
15
+ String templates are delimited by `|...|`. Embedded expressions use `{ expr }`:
16
+
17
+ ```abap
18
+ DATA(s) = |Hello { lv_name }!|.
19
+ DATA(s) = |User: { cl_abap_context_info=>get_user_technical_name( ) }|.
20
+ DATA(s) = |Length: { strlen( lv_text ) }|.
21
+ ```
22
+
23
+ ## Escaping Special Characters
24
+
25
+ Inside `|...|`, four characters have special meaning and must be escaped with `\`:
26
+
27
+ | Character | Escape as | Produces |
28
+ |-----------|-----------|---------|
29
+ | `{` | `\{` | literal `{` |
30
+ | `}` | `\}` | literal `}` |
31
+ | `\` | `\\` | literal `\` |
32
+ | `\|` | `\|` | literal `\|` |
33
+
34
+ ```abap
35
+ " Produces: \ | { }
36
+ DATA(s) = |\\ \| \{ \}|.
37
+ ```
38
+
39
+ ## JSON Payloads — The Most Common Mistake
40
+
41
+ JSON objects start with `{` — which ABAP treats as an expression delimiter.
42
+ **Always escape outer JSON braces as `\{` and `\}`.**
43
+
44
+ ```abap
45
+ " WRONG — "Expression limiter '{' in string template not followed by space"
46
+ rv_result = |{"success":"X","name":"{ lv_name }"}|.
47
+ " ^ unescaped { triggers parse error
48
+
49
+ " CORRECT — outer JSON braces escaped, expression { } left as-is
50
+ rv_result = |\{"success":"X","name":"{ lv_name }"\}|.
51
+
52
+ " CORRECT — error with method call
53
+ rv_result = |\{"success":"","error":"{ lx_error->get_text( ) }"\}|.
54
+
55
+ " CORRECT — multiple embedded fields
56
+ rv_result = |\{"success":"X","object":"{ lv_obj_name }","type":"{ lv_obj_type }"\}|.
57
+ ```
58
+
59
+ ## Control Characters
60
+
61
+ ```abap
62
+ DATA(s) = |line1\nline2|. " newline
63
+ DATA(s) = |col1\tcol2|. " tab
64
+ DATA(s) = |line1\r\nline2|. " CR+LF
65
+ ```
66
+
67
+ ## Chaining with `&&`
68
+
69
+ ```abap
70
+ DATA(s) = |{ lv_a }| && ` separator ` && |{ lv_b }|.
71
+ ```
72
+
73
+ ## Performance Note
74
+
75
+ A string template containing only literal text (no `{ }` expressions) is evaluated at
76
+ runtime. Prefer backquote literals for pure text:
77
+
78
+ ```abap
79
+ " Prefer this for literal-only strings
80
+ DATA(s) = `Hello World`.
81
+
82
+ " Not this (runtime overhead with no benefit)
83
+ DATA(s) = |Hello World|.
84
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abapgit-agent",
3
- "version": "1.15.3",
3
+ "version": "1.16.1",
4
4
  "description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
5
5
  "files": [
6
6
  "bin/",
@@ -36,6 +36,7 @@
36
36
  "test:cmd:preview": "node tests/run-all.js --cmd --command=preview",
37
37
  "test:cmd:tree": "node tests/run-all.js --cmd --command=tree",
38
38
  "test:cmd:dump": "node tests/run-all.js --cmd --command=dump",
39
+ "test:drop": "node tests/run-all.js --drop",
39
40
  "test:cmd:debug": "node tests/run-all.js --cmd --command=debug",
40
41
  "test:debug": "node tests/run-all.js --debug",
41
42
  "test:debug:scenarios": "bash tests/integration/debug-scenarios.sh",
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Drop command - Physically delete a single ABAP object from the ABAP system
3
+ */
4
+
5
+ const { printHttpError } = require('../utils/format-error');
6
+
7
+ // Map file extension (second-to-last part) to ABAP object type label
8
+ const EXT_TO_TYPE = {
9
+ clas: 'CLAS', intf: 'INTF', prog: 'PROG', fugr: 'FUGR',
10
+ tabl: 'TABL', dtel: 'DTEL', ttyp: 'TTYP', doma: 'DOMA',
11
+ ddls: 'DDLS', dcls: 'DCLS', msag: 'MSAG', stru: 'STRU'
12
+ };
13
+
14
+ /**
15
+ * Derive a display label for the object from the file path.
16
+ * e.g. "abap/zcl_foo.clas.abap" → { name: "ZCL_FOO", type: "CLAS" }
17
+ */
18
+ function objectFromFile(filePath) {
19
+ const base = filePath.split('/').pop();
20
+ const parts = base.split('.');
21
+ if (parts.length < 3) return null;
22
+ const name = parts[0].toUpperCase();
23
+ const typeExt = parts[1].toLowerCase();
24
+ const type = EXT_TO_TYPE[typeExt] || typeExt.toUpperCase();
25
+ return { name, type };
26
+ }
27
+
28
+ module.exports = {
29
+ name: 'drop',
30
+ description: 'Physically delete a single ABAP object from the ABAP system',
31
+ requiresAbapConfig: true,
32
+ requiresVersionCheck: true,
33
+
34
+ async execute(args, context) {
35
+ const { loadConfig, AbapHttp, gitUtils, getTransport, getConflictSettings } = context;
36
+
37
+ // Show help if requested
38
+ if (args.includes('--help') || args.includes('-h')) {
39
+ console.log(`
40
+ Usage:
41
+ abapgit-agent drop --files <file>
42
+ abapgit-agent drop --files <file> --transport <TRANSPORT>
43
+ abapgit-agent drop --files <file> --pull
44
+
45
+ Description:
46
+ Physically deletes a single ABAP object from the ABAP system using abapGit's
47
+ own object serializer. The object type and name are derived from the file path.
48
+
49
+ Use --pull to immediately re-activate the object from git after deletion
50
+ (useful to force a clean re-installation).
51
+
52
+ Parameters:
53
+ --files <file> Path to the ABAP source or XML file (required).
54
+ Accepted extensions: .abap, .asddls, .tabl.xml, etc.
55
+ The file must exist on disk.
56
+ --transport <TRANSPORT> Transport request (e.g. DEVK900001). Optional.
57
+ --pull Re-activate the object via pull after deletion.
58
+ --conflict-mode <mode> Conflict mode for --pull: abort (default) or ignore.
59
+
60
+ Examples:
61
+ abapgit-agent drop --files abap/zcl_foo.clas.abap
62
+ abapgit-agent drop --files abap/zcl_foo.clas.abap --pull
63
+ abapgit-agent drop --files abap/zmy_table.tabl.xml --transport DEVK900001
64
+ `);
65
+ return;
66
+ }
67
+
68
+ const filesArgIndex = args.indexOf('--files');
69
+ const transportArgIndex = args.indexOf('--transport');
70
+ const conflictModeArgIndex = args.indexOf('--conflict-mode');
71
+ const doPull = args.includes('--pull');
72
+
73
+ if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
74
+ console.error('❌ Error: --files is required');
75
+ console.error(' Usage: abapgit-agent drop --files <file>');
76
+ process.exit(1);
77
+ }
78
+
79
+ const filePath = args[filesArgIndex + 1].trim();
80
+
81
+ // Validate file exists on disk
82
+ const fs = require('fs');
83
+ if (!fs.existsSync(filePath)) {
84
+ console.error(`❌ Error: file not found: ${filePath}`);
85
+ process.exit(1);
86
+ }
87
+
88
+ // Validate file extension (same rules as pull --files)
89
+ const base = filePath.split('/').pop();
90
+ const parts = base.split('.');
91
+ const lastExt = parts[parts.length - 1].toLowerCase();
92
+ const ABAP_SOURCE_EXTS = new Set(['abap', 'asddls']);
93
+ const isXmlOnlyObject = parts.length === 3 && parts[0].length > 0 && lastExt === 'xml';
94
+ if (!ABAP_SOURCE_EXTS.has(lastExt) && !isXmlOnlyObject) {
95
+ console.error('❌ Error: --files must be an ABAP source file (.abap, .asddls) or an XML-only object file (name.type.xml).');
96
+ process.exit(1);
97
+ }
98
+
99
+ const obj = objectFromFile(filePath);
100
+
101
+ if (obj && obj.type === 'DTEL') {
102
+ console.error(`❌ drop does not support DTEL objects.`);
103
+ console.error(` Data elements cannot be re-activated after deletion due to SAP CBDA`);
104
+ console.error(` activation engine limitations. Edit the XML file and run pull instead.`);
105
+ process.exit(1);
106
+ }
107
+
108
+ const transportRequest = transportArgIndex !== -1 ? args[transportArgIndex + 1] : (getTransport ? getTransport() : null);
109
+ const conflictMode = conflictModeArgIndex !== -1 ? args[conflictModeArgIndex + 1] : (getConflictSettings ? getConflictSettings().mode : 'abort');
110
+
111
+ console.log(`\n🗑️ Dropping ${obj ? obj.type + ' ' + obj.name : filePath} from ABAP system...`);
112
+ if (transportRequest) {
113
+ console.log(` Transport: ${transportRequest}`);
114
+ }
115
+
116
+ const config = loadConfig();
117
+ const http = new AbapHttp(config);
118
+
119
+ try {
120
+ const csrfToken = await http.fetchCsrfToken();
121
+
122
+ const data = { file: filePath };
123
+ if (transportRequest) {
124
+ data.transport_request = transportRequest;
125
+ }
126
+
127
+ const result = await http.post('/sap/bc/z_abapgit_agent/drop', data, { csrfToken });
128
+
129
+ const success = result.SUCCESS || result.success;
130
+ const objectName = result.OBJECT || result.object;
131
+ const objectType = result.TYPE || result.type;
132
+ const message = result.MESSAGE || result.message;
133
+ const error = result.ERROR || result.error;
134
+
135
+ if (success === 'X' || success === true) {
136
+ console.log(`✅ Object deleted successfully.`);
137
+ if (objectName && objectType) {
138
+ console.log(` Object: ${objectName} (${objectType})`);
139
+ }
140
+ } else {
141
+ console.error(`❌ Failed to delete object`);
142
+ console.error(` Error: ${error || message || 'Unknown error'}`);
143
+ process.exit(1);
144
+ }
145
+ } catch (error) {
146
+ printHttpError(error, {});
147
+ process.exit(1);
148
+ }
149
+
150
+ // --pull: re-activate the object from git
151
+ if (doPull) {
152
+ console.log(`\n↩️ Re-pulling from git...`);
153
+ const pullCommand = require('./pull');
154
+ const gitUrl = gitUtils.getRemoteUrl();
155
+ const branch = gitUtils.getBranch();
156
+ if (!gitUrl) {
157
+ console.error('❌ Cannot re-pull: no git remote configured.');
158
+ process.exit(1);
159
+ }
160
+ try {
161
+ await pullCommand.pull(
162
+ gitUrl, branch, [filePath], transportRequest || null,
163
+ loadConfig, AbapHttp, false, undefined, conflictMode, false, false
164
+ );
165
+ } catch (pullError) {
166
+ // pull() already printed the error
167
+ process.exit(1);
168
+ }
169
+ }
170
+ }
171
+ };
@@ -196,6 +196,36 @@ module.exports = {
196
196
  async execute(args, context) {
197
197
  const { loadConfig, AbapHttp } = context;
198
198
 
199
+ if (args.includes('--help') || args.includes('-h')) {
200
+ console.log(`
201
+ Usage:
202
+ abapgit-agent dump [--user <user>] [--date <date>] [--time <HH:MM..HH:MM>]
203
+ [--program <prog>] [--error <error>] [--limit <n>] [--detail <n>] [--json]
204
+
205
+ Description:
206
+ Query short dumps (ST22) from the ABAP system.
207
+
208
+ Parameters:
209
+ --user <user> Filter by user name.
210
+ --date <date> Filter by date: TODAY, YESTERDAY, or YYYYMMDD or YYYYMMDD..YYYYMMDD.
211
+ --time <HH:MM..HH:MM> Filter by time range (e.g. 08:00..17:00).
212
+ --program <prog> Filter by program name.
213
+ --error <error> Filter by error type (e.g. RABAX_STATE).
214
+ --timezone <tz> Timezone for date/time filter (default: system timezone).
215
+ --limit <n> Maximum number of results (default: 10).
216
+ --detail <n> Show full detail of the Nth result (1-based).
217
+ --json Output as JSON.
218
+
219
+ Examples:
220
+ abapgit-agent dump
221
+ abapgit-agent dump --date TODAY
222
+ abapgit-agent dump --user DEVELOPER
223
+ abapgit-agent dump --date TODAY --limit 20
224
+ abapgit-agent dump --user DEVELOPER --detail 1
225
+ `);
226
+ return;
227
+ }
228
+
199
229
  const idx = (flag) => args.indexOf(flag);
200
230
  const val = (flag) => {
201
231
  const i = idx(flag);
@@ -11,6 +11,18 @@ module.exports = {
11
11
  async execute(args, context) {
12
12
  const { config, AbapHttp } = context;
13
13
 
14
+ if (args.includes('--help') || args.includes('-h')) {
15
+ console.log(`
16
+ Usage:
17
+ abapgit-agent health
18
+
19
+ Description:
20
+ Check if the ABAP REST API (z_abapgit_agent) is reachable and responding.
21
+ Useful for verifying connectivity and configuration.
22
+ `);
23
+ return;
24
+ }
25
+
14
26
  try {
15
27
  const http = new AbapHttp(config);
16
28
  const result = await http.get('/sap/bc/z_abapgit_agent/health');