@patimweb/pi-abap-fs 1.0.0 → 1.1.0

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/README.md CHANGED
@@ -70,6 +70,14 @@ pi -e ./index.ts
70
70
  | `abap_object_types` | List searchable object types |
71
71
  | `abap_node_contents` | Browse a package's object tree |
72
72
  | `abap_transports` | List transport requests |
73
+ | `abap_atc` | Run ATC analysis on an object |
74
+ | `abap_dumps` | List runtime dumps |
75
+ | `abap_traces` | List performance traces |
76
+ | `abap_trace_hitlist` | Analyze a performance trace |
77
+ | `abap_transport_details` | Details of one transport request |
78
+ | `abap_text_elements` | Read text elements (symbols/selections/headings) |
79
+ | `abap_set_text_elements` | Write text elements |
80
+ | `abap_create_object` | Create a new ABAP object |
73
81
 
74
82
  ## Example prompts
75
83
 
package/index.ts CHANGED
@@ -23,6 +23,14 @@
23
23
  * - abap_object_types: List searchable object types
24
24
  * - abap_node_contents: Browse package object tree
25
25
  * - abap_transports: List user transports
26
+ * - abap_atc: Run ATC analysis
27
+ * - abap_dumps: List runtime dumps
28
+ * - abap_traces: List performance traces
29
+ * - abap_trace_hitlist: Analyze a performance trace
30
+ * - abap_transport_details: Details of one transport request
31
+ * - abap_text_elements: Read text elements (symbols/selections/headings)
32
+ * - abap_set_text_elements: Write text elements
33
+ * - abap_create_object: Create a new ABAP object
26
34
  *
27
35
  * Data-oriented design:
28
36
  * - All domain data is represented as plain immutable interfaces (types.ts)
@@ -54,6 +62,14 @@ import { AbapWhereUsedTool } from "./src/tools/abap-where-used.ts";
54
62
  import { AbapObjectTypesTool } from "./src/tools/abap-object-types.ts";
55
63
  import { AbapNodeContentsTool } from "./src/tools/abap-node-contents.ts";
56
64
  import { AbapTransportsTool } from "./src/tools/abap-transports.ts";
65
+ import { AbapAtcTool } from "./src/tools/abap-atc.ts";
66
+ import { AbapDumpsTool } from "./src/tools/abap-dumps.ts";
67
+ import { AbapTracesTool } from "./src/tools/abap-traces.ts";
68
+ import { AbapTraceHitListTool } from "./src/tools/abap-trace-hitlist.ts";
69
+ import { AbapTransportDetailsTool } from "./src/tools/abap-transport-details.ts";
70
+ import { AbapTextElementsTool } from "./src/tools/abap-text-elements.ts";
71
+ import { AbapSetTextElementsTool } from "./src/tools/abap-set-text-elements.ts";
72
+ import { AbapCreateObjectTool } from "./src/tools/abap-create-object.ts";
57
73
 
58
74
  export default function (pi: ExtensionAPI) {
59
75
  // Load saved config on startup.
@@ -79,4 +95,12 @@ export default function (pi: ExtensionAPI) {
79
95
  pi.registerTool(AbapObjectTypesTool);
80
96
  pi.registerTool(AbapNodeContentsTool);
81
97
  pi.registerTool(AbapTransportsTool);
98
+ pi.registerTool(AbapAtcTool);
99
+ pi.registerTool(AbapDumpsTool);
100
+ pi.registerTool(AbapTracesTool);
101
+ pi.registerTool(AbapTraceHitListTool);
102
+ pi.registerTool(AbapTransportDetailsTool);
103
+ pi.registerTool(AbapTextElementsTool);
104
+ pi.registerTool(AbapSetTextElementsTool);
105
+ pi.registerTool(AbapCreateObjectTool);
82
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patimweb/pi-abap-fs",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "ABAP remote filesystem extension for pi coding agent. Read, search, write and activate ABAP objects on SAP systems via the ADT protocol.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -16,6 +16,13 @@ import type {
16
16
  UsageReference,
17
17
  AdtLock,
18
18
  SyntaxCheckResult,
19
+ AtcWorkList,
20
+ DumpsFeed,
21
+ TraceResults,
22
+ TraceHitList,
23
+ TraceStatementResponse,
24
+ TransportRequest,
25
+ TextElementsResult,
19
26
  } from "abap-adt-api";
20
27
 
21
28
  function isClassStructure(
@@ -252,3 +259,124 @@ export function formatSyntaxCheck(results: ReadonlyArray<SyntaxCheckResult>): st
252
259
  );
253
260
  return `${results.length} message(s):\n${lines.join("\n")}`;
254
261
  }
262
+
263
+ // ATC
264
+
265
+ export function formatAtcResult(result: AtcWorkList): string {
266
+ const findings = result.objects.flatMap((o) =>
267
+ o.findings.map((f) => ({ object: o.name, objectType: o.type, ...f })),
268
+ );
269
+ if (findings.length === 0) {
270
+ return "ATC analysis completed with no findings.";
271
+ }
272
+ const lines: string[] = [];
273
+ for (const { object, objectType, checkTitle, messageTitle, priority, location } of findings) {
274
+ const pos = location ? `line ${location.range?.start?.line ?? "?"}` : "";
275
+ lines.push(
276
+ `- [P${priority}] ${object} (${objectType}): ${checkTitle} — ${messageTitle} ${pos}`,
277
+ );
278
+ }
279
+ lines.unshift(`${findings.length} ATC finding(s):`);
280
+ return lines.join("\n");
281
+ }
282
+
283
+ // Dumps
284
+
285
+ export function formatDumps(feed: DumpsFeed): string {
286
+ if (feed.dumps.length === 0) return "No runtime dumps found.";
287
+ const lines = feed.dumps.map((d) => {
288
+ const when = d.id ? ` [${d.id}]` : "";
289
+ return `- ${d.type}${when}: ${d.text}`;
290
+ });
291
+ return `${feed.dumps.length} runtime dump(s):\n${lines.join("\n")}`;
292
+ }
293
+
294
+ // Traces
295
+
296
+ export function formatTraces(result: TraceResults): string {
297
+ if (result.runs.length === 0) return "No traces found for this user.";
298
+ const lines = result.runs.map((r) => {
299
+ const obj = r.extendedData?.objectName
300
+ ? ` (${r.extendedData.objectName})`
301
+ : "";
302
+ const state = r.extendedData?.state?.text ?? "";
303
+ const runtime = r.extendedData?.runtime
304
+ ? `${Math.round(r.extendedData.runtime)}s`
305
+ : "";
306
+ return `- ${r.title}${obj} [${state}] ${runtime} — id=${r.id}`;
307
+ });
308
+ return `${result.runs.length} trace(s):\n${lines.join("\n")}`;
309
+ }
310
+
311
+ export function formatTraceHitList(
312
+ hitList: TraceHitList,
313
+ statements: TraceStatementResponse,
314
+ ): string {
315
+ const lines: string[] = [];
316
+ if (statements.statements.length > 0) {
317
+ lines.push("Statements (aggregated call tree):");
318
+ for (const s of statements.statements.slice(0, 50)) {
319
+ lines.push(
320
+ ` - ${s.description} — hits=${s.hitCount}, gross=${s.grossTime.time}ms`,
321
+ );
322
+ }
323
+ }
324
+ if (hitList.entries.length > 0) {
325
+ if (lines.length > 0) lines.push("");
326
+ lines.push("Hit list (hot spots):");
327
+ const sorted = [...hitList.entries].sort(
328
+ (a, b) => b.grossTime.time - a.grossTime.time,
329
+ );
330
+ for (const e of sorted.slice(0, 25)) {
331
+ lines.push(
332
+ ` - ${e.description} — hits=${e.hitCount}, gross=${e.grossTime.time}ms (${e.grossTime.percentage}%)`,
333
+ );
334
+ }
335
+ }
336
+ if (lines.length === 0) return "Trace has no statements or hit list entries.";
337
+ return lines.join("\n");
338
+ }
339
+
340
+ // Transport details
341
+
342
+ export function formatTransportDetails(request: TransportRequest): string {
343
+ const lines: string[] = [
344
+ `Transport: ${request["tm:number"]}`,
345
+ `Description: ${request["tm:desc"]}`,
346
+ `Status: ${request["tm:status"]}`,
347
+ `Owner: ${request["tm:owner"]}`,
348
+ ];
349
+ const ownObjects = (request.objects ?? []).map((o) =>
350
+ ` - ${o["tm:name"]} (${o["tm:type"]})`,
351
+ );
352
+ if (ownObjects.length > 0) {
353
+ lines.push("Objects:");
354
+ lines.push(...ownObjects);
355
+ }
356
+ if (request.tasks.length > 0) {
357
+ lines.push("Tasks:");
358
+ for (const t of request.tasks) {
359
+ lines.push(` ${t["tm:number"]}: ${t["tm:desc"]}`);
360
+ for (const o of t.objects ?? []) {
361
+ lines.push(` - ${o["tm:name"]} (${o["tm:type"]})`);
362
+ }
363
+ }
364
+ }
365
+ return lines.join("\n");
366
+ }
367
+
368
+ // Text elements
369
+
370
+ export function formatTextElements(
371
+ result: TextElementsResult,
372
+ category: string,
373
+ ): string {
374
+ if (result.textElements.length === 0) {
375
+ return `No ${category} text elements found for ${result.programName}.`;
376
+ }
377
+ const lines = result.textElements.map((e) => {
378
+ const extra = e.maxLength ? ` (max ${e.maxLength})` : "";
379
+ return `- ${e.id} = ${e.text}${extra}`;
380
+ });
381
+ return `${result.textElements.length} ${category} text element(s) for ${result.programName}:\n${lines.join("\n")}`;
382
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * abap_atc tool — Run ABAP Test Cockpit (ATC) analysis on an object.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { resolveObject, withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatAtcResult } from "../formatting/formatters.ts";
9
+
10
+ export const AbapAtcTool = {
11
+ name: "abap_atc",
12
+ label: "Run ATC Analysis",
13
+ description:
14
+ "Run ABAP Test Cockpit (ATC) analysis on an object and return its findings. Provide the object (name or objectUrl) and optionally a check variant name (default 'DEFAULT'). Use abap_object_types or a known variant name if the default is unavailable.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ name: Type.Optional(
22
+ Type.String({ description: "Object name, e.g. 'ZCL_MY_CLASS'" }),
23
+ ),
24
+ objectUrl: Type.Optional(
25
+ Type.String({
26
+ description:
27
+ "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'. Takes precedence over name.",
28
+ }),
29
+ ),
30
+ type: Type.Optional(
31
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
32
+ ),
33
+ variant: Type.Optional(
34
+ Type.String({
35
+ description: "ATC check variant / worklist id (default 'DEFAULT').",
36
+ }),
37
+ ),
38
+ maxResults: Type.Optional(
39
+ Type.Number({ description: "Maximum findings to collect (default 100)." }),
40
+ ),
41
+ }),
42
+
43
+ async execute(
44
+ _toolCallId: string,
45
+ params: {
46
+ profile?: string;
47
+ name?: string;
48
+ objectUrl?: string;
49
+ type?: string;
50
+ variant?: string;
51
+ maxResults?: number;
52
+ },
53
+ _signal: AbortSignal,
54
+ ) {
55
+ const config = resolveConfig(params.profile);
56
+ const { resolved, worklist } = await withClient(config, async (client) => {
57
+ const resolved = await resolveObject(client, params);
58
+ const run = await client.createAtcRun(
59
+ params.variant ?? "DEFAULT",
60
+ resolved.mainInclude,
61
+ params.maxResults ?? 100,
62
+ );
63
+ const worklist = await client.atcWorklists(run.id);
64
+ return { resolved, worklist };
65
+ });
66
+
67
+ return {
68
+ content: [
69
+ { type: "text" as const, text: formatAtcResult(worklist) },
70
+ ],
71
+ details: {
72
+ name: resolved.name,
73
+ findingCount: worklist.objects.reduce(
74
+ (n, o) => n + o.findings.length,
75
+ 0,
76
+ ),
77
+ findings: worklist.objects.flatMap((o) =>
78
+ o.findings.map((f) => ({
79
+ object: o.name,
80
+ objectType: o.type,
81
+ checkId: f.checkId,
82
+ checkTitle: f.checkTitle,
83
+ messageId: f.messageId,
84
+ messageTitle: f.messageTitle,
85
+ priority: f.priority,
86
+ })),
87
+ ),
88
+ },
89
+ };
90
+ },
91
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * abap_create_object tool — Create a new ABAP object (class, program, …).
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+
9
+ const CREATABLE_TYPES = new Set([
10
+ "CLAS/OC",
11
+ "INTF/OI",
12
+ "PROG/P",
13
+ "PROG/I",
14
+ "FUGR/F",
15
+ "FUGR/FF",
16
+ "FUGR/I",
17
+ "TABL/DT",
18
+ "TABL/DS",
19
+ "DTEL/DE",
20
+ "DOMA/DD",
21
+ "DDLS/DF",
22
+ "DCLS/DL",
23
+ "DDLX/EX",
24
+ "DDLA/ADF",
25
+ "SRVD/SRV",
26
+ "MSAG/N",
27
+ "AUTH",
28
+ "SUSO/B",
29
+ "DEVC/K",
30
+ ]);
31
+
32
+ /** Resolve the container URI used for function-group includes/modules. */
33
+ function parentPathFor(objectType: string, parentName: string): string {
34
+ const encoded = encodeURIComponent(parentName.toLowerCase());
35
+ if (
36
+ objectType === "FUGR/F" ||
37
+ objectType === "FUGR/FF" ||
38
+ objectType === "FUGR/I"
39
+ ) {
40
+ return `/sap/bc/adt/functions/groups/${encoded}`;
41
+ }
42
+ return parentName ? `/sap/bc/adt/packages/${encoded}` : "";
43
+ }
44
+
45
+ export const AbapCreateObjectTool = {
46
+ name: "abap_create_object",
47
+ label: "Create ABAP Object",
48
+ description:
49
+ "Create a new ABAP object programmatically. Provide the object type (e.g. CLAS/OC, INTF/OI, PROG/P, PROG/I, FUGR/F, TABL/DT, DTEL/DE, DOMA/DD, DDLS/DF), the name, the parent package, and a description. The object is created in the given transport (or locally when omitted).",
50
+ parameters: Type.Object({
51
+ profile: Type.Optional(
52
+ Type.String({
53
+ description: "Profile name to use. Uses active profile if omitted.",
54
+ }),
55
+ ),
56
+ objectType: Type.String({
57
+ description:
58
+ "Object type, e.g. 'CLAS/OC' (class), 'INTF/OI' (interface), 'PROG/P' (program), 'PROG/I' (include), 'FUGR/F' (function group), 'FUGR/FF' (function module), 'TABL/DT' (table), 'DTEL/DE' (data element), 'DOMA/DD' (domain), 'DDLS/DF' (CDS view).",
59
+ }),
60
+ name: Type.String({
61
+ description: "Object name, e.g. 'ZCL_MY_CLASS'.",
62
+ }),
63
+ parentName: Type.String({
64
+ description:
65
+ "Parent package name (e.g. 'Z_MY_PACKAGE') — or the function group name for FUGR/FF and FUGR/I. Use '$TMP' for local objects.",
66
+ }),
67
+ description: Type.String({
68
+ description: "Object description.",
69
+ }),
70
+ transport: Type.Optional(
71
+ Type.String({
72
+ description: "Transport / correction request number.",
73
+ }),
74
+ ),
75
+ }),
76
+
77
+ async execute(
78
+ _toolCallId: string,
79
+ params: {
80
+ profile?: string;
81
+ objectType: string;
82
+ name: string;
83
+ parentName: string;
84
+ description: string;
85
+ transport?: string;
86
+ },
87
+ _signal: AbortSignal,
88
+ ) {
89
+ const config = resolveConfig(params.profile);
90
+ const objectType = params.objectType.toUpperCase();
91
+
92
+ if (!CREATABLE_TYPES.has(objectType)) {
93
+ throw new Error(
94
+ `Unsupported object type "${params.objectType}". Supported: ${[...CREATABLE_TYPES].join(", ")}`,
95
+ );
96
+ }
97
+
98
+ await withClient(config, (client) =>
99
+ client.createObject({
100
+ objtype: objectType as never,
101
+ name: params.name,
102
+ parentName: params.parentName,
103
+ description: params.description,
104
+ parentPath: parentPathFor(objectType, params.parentName),
105
+ transport: params.transport,
106
+ language: config.language ?? "EN",
107
+ }),
108
+ );
109
+
110
+ return {
111
+ content: [
112
+ {
113
+ type: "text" as const,
114
+ text:
115
+ `Created ${objectType} "${params.name}" in ${params.parentName}.` +
116
+ (params.transport ? ` Transport: ${params.transport}.` : "") +
117
+ " Use abap_read to verify and abap_write to add source code.",
118
+ },
119
+ ],
120
+ details: {
121
+ objectType,
122
+ name: params.name,
123
+ parentName: params.parentName,
124
+ transport: params.transport ?? null,
125
+ },
126
+ };
127
+ },
128
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * abap_dumps tool — List runtime dumps (ABAP short dumps) on the system.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatDumps } from "../formatting/formatters.ts";
9
+
10
+ export const AbapDumpsTool = {
11
+ name: "abap_dumps",
12
+ label: "List ABAP Runtime Dumps",
13
+ description:
14
+ "List ABAP runtime dumps (short dumps) on the connected system. Optionally filter with a query string (e.g. a program name).",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ query: Type.Optional(
22
+ Type.String({
23
+ description: "Optional filter, e.g. a program name or '*'. Defaults to all dumps.",
24
+ }),
25
+ ),
26
+ }),
27
+
28
+ async execute(
29
+ _toolCallId: string,
30
+ params: { profile?: string; query?: string },
31
+ _signal: AbortSignal,
32
+ ) {
33
+ const config = resolveConfig(params.profile);
34
+ const feed = await withClient(config, (client) =>
35
+ client.dumps(params.query ?? ""),
36
+ );
37
+
38
+ return {
39
+ content: [{ type: "text" as const, text: formatDumps(feed) }],
40
+ details: {
41
+ count: feed.dumps.length,
42
+ dumps: feed.dumps.map((d) => ({
43
+ id: d.id,
44
+ type: d.type,
45
+ text: d.text,
46
+ author: d.author,
47
+ })),
48
+ },
49
+ };
50
+ },
51
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * abap_set_text_elements tool — Write text elements for an ABAP object.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { ADTClient, type TextElement } from "abap-adt-api";
7
+ import { resolveObject, withClient } from "../client.ts";
8
+ import { resolveConfig } from "../config.ts";
9
+
10
+ export const AbapSetTextElementsTool = {
11
+ name: "abap_set_text_elements",
12
+ label: "Write ABAP Text Elements",
13
+ description:
14
+ "Write (save) text elements for an ABAP object. Provide the object (name or objectUrl), the category, and an array of { id, text, maxLength?, ddicReference? }. The object is locked, written and unlocked automatically. Activate afterwards with abap_activate if needed.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ name: Type.Optional(
22
+ Type.String({ description: "Object name, e.g. 'ZCL_MY_CLASS' or a program." }),
23
+ ),
24
+ objectUrl: Type.Optional(
25
+ Type.String({
26
+ description:
27
+ "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'. Takes precedence over name.",
28
+ }),
29
+ ),
30
+ type: Type.Optional(
31
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
32
+ ),
33
+ category: Type.String({
34
+ description: "Text element category: symbols, selections or headings.",
35
+ }),
36
+ elements: Type.Array(
37
+ Type.Object({
38
+ id: Type.String({ description: "Text element key, e.g. '001' or 'LISTHEADER'." }),
39
+ text: Type.String({ description: "Text content." }),
40
+ maxLength: Type.Optional(
41
+ Type.Number({ description: "Maximum length (symbols only)." }),
42
+ ),
43
+ ddicReference: Type.Optional(
44
+ Type.String({ description: "DDIC reference (selections only)." }),
45
+ ),
46
+ }),
47
+ ),
48
+ transport: Type.Optional(
49
+ Type.String({
50
+ description: "Transport / correction request number.",
51
+ }),
52
+ ),
53
+ }),
54
+
55
+ async execute(
56
+ _toolCallId: string,
57
+ params: {
58
+ profile?: string;
59
+ name?: string;
60
+ objectUrl?: string;
61
+ type?: string;
62
+ category: string;
63
+ elements: Array<{
64
+ id: string;
65
+ text: string;
66
+ maxLength?: number;
67
+ ddicReference?: string;
68
+ }>;
69
+ transport?: string;
70
+ },
71
+ _signal: AbortSignal,
72
+ ) {
73
+ const config = resolveConfig(params.profile);
74
+ const category = params.category as "symbols" | "selections" | "headings";
75
+ const elements: TextElement[] = params.elements.map((e) => ({
76
+ id: e.id,
77
+ text: e.text,
78
+ ...(e.maxLength !== undefined && { maxLength: e.maxLength }),
79
+ ...(e.ddicReference !== undefined && { ddicReference: e.ddicReference }),
80
+ }));
81
+
82
+ const resolved = await withClient(
83
+ config,
84
+ async (client) => {
85
+ const resolved = await resolveObject(client, params);
86
+ const url = ADTClient.textElementsUrl(resolved.type, resolved.name);
87
+ const lock = await client.lock(resolved.objectUrl, "MODIFY");
88
+ try {
89
+ await client.setTextElements(
90
+ url,
91
+ category,
92
+ elements,
93
+ lock.LOCK_HANDLE,
94
+ params.transport,
95
+ );
96
+ } finally {
97
+ await client.unLock(resolved.objectUrl, lock.LOCK_HANDLE);
98
+ }
99
+ return resolved;
100
+ },
101
+ { stateful: true },
102
+ );
103
+
104
+ return {
105
+ content: [
106
+ {
107
+ type: "text" as const,
108
+ text:
109
+ `Wrote ${elements.length} ${category} text element(s) to ${resolved.name}. ` +
110
+ "Activate the object afterwards if required.",
111
+ },
112
+ ],
113
+ details: {
114
+ name: resolved.name,
115
+ category,
116
+ count: elements.length,
117
+ },
118
+ };
119
+ },
120
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * abap_text_elements tool — Read text elements (symbols / selection texts / headings).
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { ADTClient } from "abap-adt-api";
7
+ import { resolveObject, withClient } from "../client.ts";
8
+ import { resolveConfig } from "../config.ts";
9
+ import { formatTextElements } from "../formatting/formatters.ts";
10
+
11
+ export const AbapTextElementsTool = {
12
+ name: "abap_text_elements",
13
+ label: "Read ABAP Text Elements",
14
+ description:
15
+ "Read the text elements of an ABAP object: symbols (text symbols), selections (selection texts) or headings. Provide the object (name or objectUrl).",
16
+ parameters: Type.Object({
17
+ profile: Type.Optional(
18
+ Type.String({
19
+ description: "Profile name to use. Uses active profile if omitted.",
20
+ }),
21
+ ),
22
+ name: Type.Optional(
23
+ Type.String({ description: "Object name, e.g. 'ZCL_MY_CLASS' or a program." }),
24
+ ),
25
+ objectUrl: Type.Optional(
26
+ Type.String({
27
+ description:
28
+ "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'. Takes precedence over name.",
29
+ }),
30
+ ),
31
+ type: Type.Optional(
32
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
33
+ ),
34
+ category: Type.Optional(
35
+ Type.String({
36
+ description: "Text element category: symbols, selections or headings. Default 'symbols'.",
37
+ }),
38
+ ),
39
+ }),
40
+
41
+ async execute(
42
+ _toolCallId: string,
43
+ params: {
44
+ profile?: string;
45
+ name?: string;
46
+ objectUrl?: string;
47
+ type?: string;
48
+ category?: string;
49
+ },
50
+ _signal: AbortSignal,
51
+ ) {
52
+ const config = resolveConfig(params.profile);
53
+ const category = (params.category ?? "symbols") as
54
+ | "symbols"
55
+ | "selections"
56
+ | "headings";
57
+
58
+ const { resolved, result } = await withClient(config, async (client) => {
59
+ const resolved = await resolveObject(client, params);
60
+ const url = ADTClient.textElementsUrl(resolved.type, resolved.name);
61
+ const result = await client.getTextElements(url, category);
62
+ return { resolved, result };
63
+ });
64
+
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text" as const,
69
+ text: formatTextElements(result, category),
70
+ },
71
+ ],
72
+ details: {
73
+ name: resolved.name,
74
+ category,
75
+ count: result.textElements.length,
76
+ textElements: result.textElements,
77
+ },
78
+ };
79
+ },
80
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * abap_trace_hitlist tool — Analyze a performance trace (hit list + statements).
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatTraceHitList } from "../formatting/formatters.ts";
9
+
10
+ export const AbapTraceHitListTool = {
11
+ name: "abap_trace_hitlist",
12
+ label: "Analyze ABAP Trace",
13
+ description:
14
+ "Analyze an ABAP performance trace: returns the hot-spot hit list and the aggregated statement call tree. Provide the trace id returned by abap_traces.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ traceId: Type.String({
22
+ description:
23
+ "Trace id from abap_traces, e.g. '/sap/bc/adt/runtime/traces/abaptraces/...' or the short id.",
24
+ }),
25
+ withSystemEvents: Type.Optional(
26
+ Type.Boolean({
27
+ description: "Include system events (default false).",
28
+ }),
29
+ ),
30
+ }),
31
+
32
+ async execute(
33
+ _toolCallId: string,
34
+ params: { profile?: string; traceId: string; withSystemEvents?: boolean },
35
+ _signal: AbortSignal,
36
+ ) {
37
+ const config = resolveConfig(params.profile);
38
+ const { hitList, statements } = await withClient(config, async (client) => {
39
+ const hitList = await client.tracesHitList(
40
+ params.traceId,
41
+ params.withSystemEvents ?? false,
42
+ );
43
+ const statements = await client.tracesStatements(params.traceId, {
44
+ withSystemEvents: params.withSystemEvents ?? false,
45
+ });
46
+ return { hitList, statements };
47
+ });
48
+
49
+ return {
50
+ content: [
51
+ {
52
+ type: "text" as const,
53
+ text: formatTraceHitList(hitList, statements),
54
+ },
55
+ ],
56
+ details: {
57
+ hitCount: hitList.entries.length,
58
+ statementCount: statements.count,
59
+ },
60
+ };
61
+ },
62
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * abap_traces tool — List ABAP performance traces for a user.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatTraces } from "../formatting/formatters.ts";
9
+
10
+ export const AbapTracesTool = {
11
+ name: "abap_traces",
12
+ label: "List ABAP Traces",
13
+ description:
14
+ "List ABAP performance traces recorded for a user. By default lists the traces of the profile's username. Use abap_trace_hitlist with a trace id to analyze a trace.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ user: Type.Optional(
22
+ Type.String({
23
+ description: "User to list traces for. Defaults to the connected user.",
24
+ }),
25
+ ),
26
+ }),
27
+
28
+ async execute(
29
+ _toolCallId: string,
30
+ params: { profile?: string; user?: string },
31
+ _signal: AbortSignal,
32
+ ) {
33
+ const config = resolveConfig(params.profile);
34
+ const result = await withClient(config, (client) =>
35
+ client.tracesList(params.user ?? config.username),
36
+ );
37
+
38
+ return {
39
+ content: [{ type: "text" as const, text: formatTraces(result) }],
40
+ details: {
41
+ count: result.runs.length,
42
+ runs: result.runs.map((r) => ({
43
+ id: r.id,
44
+ title: r.title,
45
+ objectName: r.extendedData?.objectName,
46
+ state: r.extendedData?.state?.text,
47
+ runtime: r.extendedData?.runtime,
48
+ })),
49
+ },
50
+ };
51
+ },
52
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * abap_transport_details tool — Show details of a single transport request.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatTransportDetails } from "../formatting/formatters.ts";
9
+
10
+ export const AbapTransportDetailsTool = {
11
+ name: "abap_transport_details",
12
+ label: "ABAP Transport Details",
13
+ description:
14
+ "Show details of a single transport request: description, status, owner, its tasks and the objects it contains. Provide the transport number (e.g. 'DEVK900123') from abap_transports.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ transportNumber: Type.String({
22
+ description: "Transport request number, e.g. 'DEVK900123'.",
23
+ }),
24
+ }),
25
+
26
+ async execute(
27
+ _toolCallId: string,
28
+ params: { profile?: string; transportNumber: string },
29
+ _signal: AbortSignal,
30
+ ) {
31
+ const config = resolveConfig(params.profile);
32
+ const request = await withClient(config, (client) =>
33
+ client.transportDetails(params.transportNumber.toUpperCase()),
34
+ );
35
+
36
+ return {
37
+ content: [
38
+ { type: "text" as const, text: formatTransportDetails(request) },
39
+ ],
40
+ details: {
41
+ transport: request["tm:number"],
42
+ status: request["tm:status"],
43
+ owner: request["tm:owner"],
44
+ taskCount: request.tasks.length,
45
+ objectCount:
46
+ (request.objects?.length ?? 0) +
47
+ request.tasks.reduce((n, t) => n + (t.objects?.length ?? 0), 0),
48
+ },
49
+ };
50
+ },
51
+ };