@cspeach/cli 0.6.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.
Files changed (210) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +108 -0
  3. package/dist/agent/anthropic-provider.js +59 -0
  4. package/dist/agent/llm-provider.js +1 -0
  5. package/dist/agent/loop.js +709 -0
  6. package/dist/agent/maybe-build-project-context.js +126 -0
  7. package/dist/agent/providers/ai-hub-provider.js +58 -0
  8. package/dist/agent/providers/byok-provider.js +53 -0
  9. package/dist/agent/providers/factory.js +13 -0
  10. package/dist/agent/providers/local-provider.js +125 -0
  11. package/dist/agent/repair-partial.js +31 -0
  12. package/dist/agent/retry-key.js +58 -0
  13. package/dist/agent/retry.js +17 -0
  14. package/dist/agent/sap-connection-adapter.js +82 -0
  15. package/dist/agent/skill-checkpoint.js +119 -0
  16. package/dist/agent/tool-dispatch.js +47 -0
  17. package/dist/agent/turn-assistant-text.js +49 -0
  18. package/dist/agent/turn-error-ux.js +126 -0
  19. package/dist/agent/turn-stream.js +79 -0
  20. package/dist/agent/turn-watchdog.js +71 -0
  21. package/dist/approvals/advisory-prompt.js +40 -0
  22. package/dist/approvals/advisory-render.js +38 -0
  23. package/dist/approvals/approval-prompt.js +100 -0
  24. package/dist/approvals/jwt.js +33 -0
  25. package/dist/approvals/render.js +211 -0
  26. package/dist/approvals/risk-floor.js +26 -0
  27. package/dist/auth/api-key.js +40 -0
  28. package/dist/auth/auth-file.js +59 -0
  29. package/dist/auth/device.js +8 -0
  30. package/dist/auth/me.js +19 -0
  31. package/dist/classifier/client.js +58 -0
  32. package/dist/cli-args.js +38 -0
  33. package/dist/cli.js +148 -0
  34. package/dist/commands/config-set.js +245 -0
  35. package/dist/commands/config-show.js +159 -0
  36. package/dist/commands/help.js +93 -0
  37. package/dist/commands/login.js +122 -0
  38. package/dist/commands/logout.js +17 -0
  39. package/dist/commands/project-context-impact.js +215 -0
  40. package/dist/commands/reroute.js +60 -0
  41. package/dist/commands/spec-gap-status.js +52 -0
  42. package/dist/commands/whoami.js +35 -0
  43. package/dist/config/loader.js +67 -0
  44. package/dist/config/paths.js +20 -0
  45. package/dist/doctor/checks/_http-probe.js +56 -0
  46. package/dist/doctor/checks/auth.js +15 -0
  47. package/dist/doctor/checks/cert.js +24 -0
  48. package/dist/doctor/checks/forge-rules.js +102 -0
  49. package/dist/doctor/checks/keychain-fallback.js +14 -0
  50. package/dist/doctor/checks/keychain.js +23 -0
  51. package/dist/doctor/checks/llm-mode.js +27 -0
  52. package/dist/doctor/checks/proxy.js +13 -0
  53. package/dist/doctor/checks/sap.js +33 -0
  54. package/dist/doctor/checks/skill.js +24 -0
  55. package/dist/doctor/checks/write-mode.js +34 -0
  56. package/dist/doctor/checks/zcspeach.js +76 -0
  57. package/dist/doctor/run.js +46 -0
  58. package/dist/errors/codes.js +12 -0
  59. package/dist/index.js +6 -0
  60. package/dist/lock-contention.js +22 -0
  61. package/dist/one-shot.js +104 -0
  62. package/dist/project-context/conventions.js +309 -0
  63. package/dist/project-context/detect.js +250 -0
  64. package/dist/project-context/domain/abap-cloud.js +26 -0
  65. package/dist/project-context/domain/abapgit.js +177 -0
  66. package/dist/project-context/domain/cap.js +164 -0
  67. package/dist/project-context/domain/fiori.js +326 -0
  68. package/dist/project-context/git.js +115 -0
  69. package/dist/project-context/index-files.js +235 -0
  70. package/dist/project-context/index.js +117 -0
  71. package/dist/project-context/render.js +308 -0
  72. package/dist/project-context/types.js +14 -0
  73. package/dist/projects/build.js +20 -0
  74. package/dist/projects/canonicalize.js +39 -0
  75. package/dist/projects/email-template.js +54 -0
  76. package/dist/projects/extract-cca.js +139 -0
  77. package/dist/projects/extract-design.js +107 -0
  78. package/dist/projects/extract-estimate.js +93 -0
  79. package/dist/projects/extract-modernize.js +130 -0
  80. package/dist/projects/extract-spec-gap.js +101 -0
  81. package/dist/projects/extract-test-coverage.js +137 -0
  82. package/dist/projects/extract-upgrade.js +230 -0
  83. package/dist/projects/filename.js +18 -0
  84. package/dist/projects/index.js +8 -0
  85. package/dist/projects/migration.js +111 -0
  86. package/dist/projects/promote-command.js +96 -0
  87. package/dist/projects/promote.js +107 -0
  88. package/dist/projects/save-command.js +124 -0
  89. package/dist/projects/save.js +21 -0
  90. package/dist/projects/status.js +170 -0
  91. package/dist/projects/types.js +1 -0
  92. package/dist/projects/validate.js +146 -0
  93. package/dist/projects/workspace.js +478 -0
  94. package/dist/renderer/abap-inline.js +121 -0
  95. package/dist/renderer/banners.js +39 -0
  96. package/dist/renderer/highlighters/abap.js +126 -0
  97. package/dist/renderer/highlighters/bdef.js +81 -0
  98. package/dist/renderer/highlighters/cds.js +91 -0
  99. package/dist/renderer/markdown.js +291 -0
  100. package/dist/renderer/pipeline.js +201 -0
  101. package/dist/renderer/progress-chatter.js +237 -0
  102. package/dist/renderer/question-normalizer.js +306 -0
  103. package/dist/renderer/severity.js +61 -0
  104. package/dist/renderer/status-footer.js +50 -0
  105. package/dist/renderer/syntax.js +58 -0
  106. package/dist/renderer/tables.js +55 -0
  107. package/dist/renderer/thinking-heartbeat.js +70 -0
  108. package/dist/renderer/tool-widget.js +199 -0
  109. package/dist/renderer/tty.js +66 -0
  110. package/dist/renderer/widget-extractor.js +87 -0
  111. package/dist/renderer/widget-fallback.js +78 -0
  112. package/dist/renderer/widget-schemas.js +43 -0
  113. package/dist/repl/at-completer.js +64 -0
  114. package/dist/repl/at-picker.js +122 -0
  115. package/dist/repl/bracketed-paste.js +284 -0
  116. package/dist/repl/current-transport.js +46 -0
  117. package/dist/repl/diff-display.js +41 -0
  118. package/dist/repl/file-picker.js +219 -0
  119. package/dist/repl/inquirer-guard.js +130 -0
  120. package/dist/repl/inquirer-theme.js +41 -0
  121. package/dist/repl/rule8-detector.js +99 -0
  122. package/dist/repl/safety-confirm.js +106 -0
  123. package/dist/repl/safety-mode-state.js +36 -0
  124. package/dist/repl/slash-completer.js +59 -0
  125. package/dist/repl/slash-picker.js +124 -0
  126. package/dist/repl/update-method-preview-hook.js +45 -0
  127. package/dist/repl.js +1383 -0
  128. package/dist/router/classifier.js +38 -0
  129. package/dist/router/intent-extractor.js +140 -0
  130. package/dist/router/routing-decision.js +19 -0
  131. package/dist/sap/connection-manager.js +52 -0
  132. package/dist/sap/onboarding.js +178 -0
  133. package/dist/sap/system-info.js +515 -0
  134. package/dist/session/awaiting-answer.js +73 -0
  135. package/dist/session/gc.js +28 -0
  136. package/dist/session/pending.js +37 -0
  137. package/dist/session/resume.js +77 -0
  138. package/dist/session/schema.js +20 -0
  139. package/dist/session/store.js +147 -0
  140. package/dist/session/time-ago.js +41 -0
  141. package/dist/skill-catalog.js +222 -0
  142. package/dist/skills/bundled-skills.js +1 -0
  143. package/dist/skills/canonical.js +12 -0
  144. package/dist/skills/manifest-client.js +93 -0
  145. package/dist/skills/promotion-dispatch.js +24 -0
  146. package/dist/skills/signing-public-key.js +4 -0
  147. package/dist/skills/source-bundled.js +20 -0
  148. package/dist/skills/source-managed.js +26 -0
  149. package/dist/skills/source-manifest.js +26 -0
  150. package/dist/tools/_command-shared.js +110 -0
  151. package/dist/tools/_filesystem-shared.js +81 -0
  152. package/dist/tools/_flag.js +39 -0
  153. package/dist/tools/approval.js +228 -0
  154. package/dist/tools/ask-question.js +205 -0
  155. package/dist/tools/dispatch-skill.js +81 -0
  156. package/dist/tools/filesystem/file-edit.js +140 -0
  157. package/dist/tools/filesystem/file-read.js +89 -0
  158. package/dist/tools/filesystem/file-write.js +128 -0
  159. package/dist/tools/filesystem/glob.js +177 -0
  160. package/dist/tools/filesystem/grep.js +163 -0
  161. package/dist/tools/index.js +32 -0
  162. package/dist/tools/project/convention_get.js +91 -0
  163. package/dist/tools/project/playbook_get.js +132 -0
  164. package/dist/tools/project/project_context_get.js +101 -0
  165. package/dist/tools/sap-read.js +454 -0
  166. package/dist/tools/sap-write.js +746 -0
  167. package/dist/tools/shell/shell_exec.js +209 -0
  168. package/dist/tools/snapshot.js +107 -0
  169. package/dist/tools/subagent/_background-shared.js +133 -0
  170. package/dist/tools/subagent/agent_run.js +186 -0
  171. package/dist/tools/subagent/background_run.js +143 -0
  172. package/dist/tools/subagent/monitor_emit.js +65 -0
  173. package/dist/tools/subagent/schedule_create.js +131 -0
  174. package/dist/tools/transport.js +233 -0
  175. package/dist/tools/update-method-intercept.js +119 -0
  176. package/dist/tools/verify.js +39 -0
  177. package/dist/tools/web/_web-shared.js +251 -0
  178. package/dist/tools/web/web_fetch.js +257 -0
  179. package/dist/tools/web/web_search.js +195 -0
  180. package/dist/tools/write-mode.js +22 -0
  181. package/dist/ui/app.js +95 -0
  182. package/dist/ui/approval-emitter.js +10 -0
  183. package/dist/ui/approval-modal.js +53 -0
  184. package/dist/ui/ascii-chars.js +6 -0
  185. package/dist/ui/body.js +102 -0
  186. package/dist/ui/coaching-picker-classic.js +36 -0
  187. package/dist/ui/coaching-picker-emitter.js +27 -0
  188. package/dist/ui/command-palette.js +34 -0
  189. package/dist/ui/error-emitter.js +21 -0
  190. package/dist/ui/footer.js +103 -0
  191. package/dist/ui/header.js +17 -0
  192. package/dist/ui/ink-classifier-route.js +19 -0
  193. package/dist/ui/login-banner.js +72 -0
  194. package/dist/ui/rich-error-box.js +9 -0
  195. package/dist/ui/sap-state-store.js +65 -0
  196. package/dist/ui/session-timeline.js +31 -0
  197. package/dist/ui/sidebar.js +10 -0
  198. package/dist/ui/skill-picker.js +50 -0
  199. package/dist/ui/status-row.js +12 -0
  200. package/dist/ui/widget-control.js +4 -0
  201. package/dist/ui/widgets/bar-chart.js +15 -0
  202. package/dist/ui/widgets/coaching-picker.js +41 -0
  203. package/dist/ui/widgets/component-registry.js +12 -0
  204. package/dist/ui/widgets/dep-graph.js +9 -0
  205. package/dist/ui/widgets/diff-viewer.js +11 -0
  206. package/dist/ui/widgets/question-card.js +11 -0
  207. package/dist/ui/widgets/stack-frames.js +5 -0
  208. package/dist/upgrade-check.js +28 -0
  209. package/dist/upgrade.js +13 -0
  210. package/package.json +83 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * project_context_get — Phase 3 / Chunk 3D.
3
+ *
4
+ * On-demand refresh of the rendered <project_context> block. Wraps
5
+ * Phase 2's getProjectContext + Phase 2.5's resetProjectContextCache.
6
+ *
7
+ * Returns the rendered XML-flavored block (the same shape Phase 2
8
+ * splices into the first user message of a flag-on session). When
9
+ * force_refresh: true, the per-process cache is cleared first; the
10
+ * next render reflects whatever the project looks like NOW (post-file_write,
11
+ * post-checkout, post-SAP-state-change).
12
+ *
13
+ * Flag-gated: invisible to listTools() unless
14
+ * CSPEACH_TOOL_PROJECT_CONTEXT_GET=on. isMutating: false — reads only.
15
+ *
16
+ * Note on caching: this tool does NOT consult the
17
+ * CSPEACH_PROJECT_CONTEXT env flag — it always renders, regardless of
18
+ * whether the flag-gated Phase 2 enrichment is on. The flag gates
19
+ * AUTOMATIC injection; this tool is for EXPLICIT model-driven reads.
20
+ */
21
+ import { registerTool } from '../index.js';
22
+ import { getProjectContext, renderProjectContext, } from '../../project-context/index.js';
23
+ import { resetProjectContextCache } from '../../agent/maybe-build-project-context.js';
24
+ // Tool-local one-shot cache so default (force_refresh: false) calls
25
+ // return the same render as the previous call in the same process.
26
+ // Cleared on force_refresh: true OR via __resetToolCacheForTests().
27
+ // Independent from the Phase 2 cachedBlock — that one is gated on
28
+ // the env flag; this one is always live for explicit tool calls.
29
+ let cachedRender = null;
30
+ /** Test-only: reset the tool-local cache between cases. */
31
+ export function __resetToolCacheForTests() {
32
+ cachedRender = null;
33
+ }
34
+ export async function projectContextGetHandler(args, ctx) {
35
+ if (args.force_refresh !== undefined && typeof args.force_refresh !== 'boolean') {
36
+ return {
37
+ content: `error: force_refresh must be a boolean (got ${typeof args.force_refresh}).`,
38
+ is_error: true,
39
+ };
40
+ }
41
+ if (!ctx.cwd || typeof ctx.cwd !== 'string') {
42
+ return {
43
+ content: 'error: project_context_get requires a working directory (ctx.cwd).',
44
+ is_error: true,
45
+ };
46
+ }
47
+ if (args.force_refresh === true) {
48
+ // Phase 2.5 public API. Also clears the one-shot warning latch.
49
+ resetProjectContextCache();
50
+ cachedRender = null;
51
+ }
52
+ else if (cachedRender !== null) {
53
+ return { content: cachedRender };
54
+ }
55
+ let pc;
56
+ try {
57
+ pc = await getProjectContext(ctx.cwd);
58
+ }
59
+ catch (err) {
60
+ return {
61
+ content: `error: project context computation failed — ${err instanceof Error ? err.message : String(err)}`,
62
+ is_error: true,
63
+ };
64
+ }
65
+ if (pc === null) {
66
+ const empty = '<project_context>(no project detected for this working directory)</project_context>';
67
+ cachedRender = empty;
68
+ return { content: empty };
69
+ }
70
+ let rendered;
71
+ try {
72
+ rendered = renderProjectContext(pc);
73
+ }
74
+ catch (err) {
75
+ return {
76
+ content: `error: project context render failed — ${err instanceof Error ? err.message : String(err)}`,
77
+ is_error: true,
78
+ };
79
+ }
80
+ cachedRender = rendered;
81
+ return { content: rendered };
82
+ }
83
+ registerTool({
84
+ name: 'project_context_get',
85
+ description: "Return the rendered <project_context> block for the current working directory. " +
86
+ "Useful when the model needs a fresh view of the project after a write, a checkout, or a SAP state change. " +
87
+ "Pass force_refresh: true to invalidate the per-process cache first; default false returns the cached render.",
88
+ isMutating: false,
89
+ category: 'project',
90
+ flagGated: true,
91
+ input_schema: {
92
+ type: 'object',
93
+ properties: {
94
+ force_refresh: {
95
+ type: 'boolean',
96
+ description: "If true, clear the per-process project-context cache before rendering. Default false.",
97
+ },
98
+ },
99
+ },
100
+ handler: (args, ctx) => projectContextGetHandler(args, ctx),
101
+ });
@@ -0,0 +1,454 @@
1
+ /**
2
+ * 18 read-only SAP tools wired to @cspeach/sap-client AdtClient.
3
+ *
4
+ * All method names verified against sap-client/src/adt-client.ts:
5
+ * getSource(type, name)
6
+ * objectStructure(type, name)
7
+ * searchObject(query, objType?, maxResults?)
8
+ * changeLog(type, name)
9
+ * sqlQuery(query, maxRows?, extraBlocked?)
10
+ * shortDump(dumpId?, user?)
11
+ * atcRun(type, name, variant?)
12
+ * usageReferences(objectType, objectName)
13
+ * apiState(objects)
14
+ * abapDocu(keyword)
15
+ * classIncludes(className, includeType?)
16
+ * inactiveObjects()
17
+ * syntaxCheck(type, name)
18
+ * diff(type, name, version1?, version2?)
19
+ * previewChange(type, name, newSource)
20
+ * runUnitTest(type, name)
21
+ * odataTest(servicePath, entity?, method?)
22
+ * badiList(query, enhancementSpot?, maxResults?)
23
+ */
24
+ import { registerTool } from './index.js';
25
+ // -----------------------------------------------------------------------
26
+ // 1. sap_get_source
27
+ // -----------------------------------------------------------------------
28
+ registerTool({
29
+ name: 'sap_get_source',
30
+ description: 'Read the source code of an ABAP object by name and type.',
31
+ isMutating: false,
32
+ input_schema: {
33
+ type: 'object',
34
+ properties: {
35
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
36
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
37
+ },
38
+ required: ['name', 'type'],
39
+ },
40
+ handler: async (args, ctx) => {
41
+ // AdtClient.getSource takes (type, name) — type first
42
+ const source = await ctx.adt.getSource(args.type, args.name);
43
+ return { content: source };
44
+ },
45
+ });
46
+ // -----------------------------------------------------------------------
47
+ // 2. sap_object_structure
48
+ // -----------------------------------------------------------------------
49
+ registerTool({
50
+ name: 'sap_object_structure',
51
+ description: 'Retrieve metadata and structural information for an ABAP repository object (package, description, lock state, etc.).',
52
+ isMutating: false,
53
+ input_schema: {
54
+ type: 'object',
55
+ properties: {
56
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
57
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
58
+ },
59
+ required: ['name', 'type'],
60
+ },
61
+ handler: async (args, ctx) => {
62
+ const result = await ctx.adt.objectStructure(args.type, args.name);
63
+ return { content: JSON.stringify(result, null, 2) };
64
+ },
65
+ });
66
+ // -----------------------------------------------------------------------
67
+ // 3. sap_search_object
68
+ // -----------------------------------------------------------------------
69
+ registerTool({
70
+ name: 'sap_search_object',
71
+ description: 'Search the SAP repository by object name pattern. Supports wildcard * (e.g. ZCL_ORDER*).',
72
+ isMutating: false,
73
+ input_schema: {
74
+ type: 'object',
75
+ properties: {
76
+ query: { type: 'string', description: 'Name pattern, e.g. ZCL_ORDER* or ZHELLO*' },
77
+ type: {
78
+ type: 'string',
79
+ description: 'Optional object type filter, e.g. PROG, CLAS, INTF, DDLS',
80
+ },
81
+ maxResults: {
82
+ type: 'number',
83
+ description: 'Maximum number of results (default 100)',
84
+ },
85
+ },
86
+ required: ['query'],
87
+ },
88
+ handler: async (args, ctx) => {
89
+ const results = await ctx.adt.searchObject(args.query, args.type, args.maxResults ?? 100);
90
+ return { content: JSON.stringify(results, null, 2) };
91
+ },
92
+ });
93
+ // -----------------------------------------------------------------------
94
+ // 4. sap_change_log
95
+ // -----------------------------------------------------------------------
96
+ registerTool({
97
+ name: 'sap_change_log',
98
+ description: 'Retrieve the version history (change log) of an ABAP object — date, author, description per revision.',
99
+ isMutating: false,
100
+ input_schema: {
101
+ type: 'object',
102
+ properties: {
103
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
104
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
105
+ },
106
+ required: ['name', 'type'],
107
+ },
108
+ handler: async (args, ctx) => {
109
+ const entries = await ctx.adt.changeLog(args.type, args.name);
110
+ return { content: JSON.stringify(entries, null, 2) };
111
+ },
112
+ });
113
+ // -----------------------------------------------------------------------
114
+ // 5. sap_sql_query
115
+ // -----------------------------------------------------------------------
116
+ registerTool({
117
+ name: 'sap_sql_query',
118
+ description: 'Execute a read-only SQL SELECT against the SAP database via ADT Data Preview. Security-sensitive tables are blocked automatically.',
119
+ isMutating: false,
120
+ input_schema: {
121
+ type: 'object',
122
+ properties: {
123
+ query: { type: 'string', description: 'SQL SELECT statement, e.g. SELECT * FROM mara WHERE matnr = \'MAT001\'' },
124
+ maxRows: { type: 'number', description: 'Maximum rows to return (default 100)' },
125
+ },
126
+ required: ['query'],
127
+ },
128
+ handler: async (args, ctx) => {
129
+ const result = await ctx.adt.sqlQuery(args.query, args.maxRows ?? 100);
130
+ return { content: JSON.stringify(result, null, 2) };
131
+ },
132
+ });
133
+ // -----------------------------------------------------------------------
134
+ // 6. sap_short_dump
135
+ // -----------------------------------------------------------------------
136
+ registerTool({
137
+ name: 'sap_short_dump',
138
+ description: 'Read ABAP runtime errors (short dumps / ST22). Without dumpId returns a list of recent dumps; with dumpId returns the full diagnostic detail.',
139
+ isMutating: false,
140
+ input_schema: {
141
+ type: 'object',
142
+ properties: {
143
+ dumpId: {
144
+ type: 'string',
145
+ description: 'Specific dump ID for detailed view (from a previous list call)',
146
+ },
147
+ user: {
148
+ type: 'string',
149
+ description: 'Filter dumps by SAP username (list mode only)',
150
+ },
151
+ },
152
+ required: [],
153
+ },
154
+ handler: async (args, ctx) => {
155
+ const result = await ctx.adt.shortDump(args.dumpId, args.user);
156
+ return { content: JSON.stringify(result, null, 2) };
157
+ },
158
+ });
159
+ // -----------------------------------------------------------------------
160
+ // 7. sap_atc_run
161
+ // -----------------------------------------------------------------------
162
+ registerTool({
163
+ name: 'sap_atc_run',
164
+ description: 'Run ABAP Test Cockpit (ATC) checks on a specific object and return all findings.',
165
+ isMutating: false,
166
+ input_schema: {
167
+ type: 'object',
168
+ properties: {
169
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
170
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF' },
171
+ variant: {
172
+ type: 'string',
173
+ description: 'ATC check variant (default: DEFAULT)',
174
+ },
175
+ },
176
+ required: ['name', 'type'],
177
+ },
178
+ handler: async (args, ctx) => {
179
+ // atcRun(type, name, variant?) — type first
180
+ const result = await ctx.adt.atcRun(args.type, args.name, args.variant ?? 'DEFAULT');
181
+ return { content: JSON.stringify(result, null, 2) };
182
+ },
183
+ });
184
+ // -----------------------------------------------------------------------
185
+ // 8. sap_usage_references
186
+ // -----------------------------------------------------------------------
187
+ registerTool({
188
+ name: 'sap_usage_references',
189
+ description: 'Find all places where a given ABAP object is referenced (where-used list).',
190
+ isMutating: false,
191
+ input_schema: {
192
+ type: 'object',
193
+ properties: {
194
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
195
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
196
+ },
197
+ required: ['name', 'type'],
198
+ },
199
+ handler: async (args, ctx) => {
200
+ // usageReferences(objectType, objectName) — type first
201
+ const refs = await ctx.adt.usageReferences(args.type, args.name);
202
+ return { content: JSON.stringify(refs, null, 2) };
203
+ },
204
+ });
205
+ // -----------------------------------------------------------------------
206
+ // 9. sap_api_state
207
+ // -----------------------------------------------------------------------
208
+ registerTool({
209
+ name: 'sap_api_state',
210
+ description: 'Check the ABAP Cloud / clean-core release state of SAP objects. Pass comma-separated TYPE:NAME pairs, e.g. "CLAS:CL_GUI_ALV_GRID,FUNC:BAPI_SALESORDER_GETLIST".',
211
+ isMutating: false,
212
+ input_schema: {
213
+ type: 'object',
214
+ properties: {
215
+ objects: {
216
+ type: 'string',
217
+ description: 'Comma-separated TYPE:NAME pairs, e.g. "CLAS:CL_GUI_ALV_GRID,FUNC:BAPI_SALESORDER_GETLIST"',
218
+ },
219
+ },
220
+ required: ['objects'],
221
+ },
222
+ handler: async (args, ctx) => {
223
+ // apiState(objects) — single comma-separated string
224
+ const result = await ctx.adt.apiState(args.objects);
225
+ return { content: JSON.stringify(result, null, 2) };
226
+ },
227
+ });
228
+ // -----------------------------------------------------------------------
229
+ // 10. sap_abap_docu
230
+ // -----------------------------------------------------------------------
231
+ registerTool({
232
+ name: 'sap_abap_docu',
233
+ description: 'Retrieve official ABAP language documentation from the SAP system for a keyword, class, or function module.',
234
+ isMutating: false,
235
+ input_schema: {
236
+ type: 'object',
237
+ properties: {
238
+ keyword: {
239
+ type: 'string',
240
+ description: 'ABAP keyword, class name, or function module, e.g. SELECT, CL_ABAP_UNIT_ASSERT',
241
+ },
242
+ },
243
+ required: ['keyword'],
244
+ },
245
+ handler: async (args, ctx) => {
246
+ const docu = await ctx.adt.abapDocu(args.keyword);
247
+ return { content: docu };
248
+ },
249
+ });
250
+ // -----------------------------------------------------------------------
251
+ // 11. sap_class_includes
252
+ // -----------------------------------------------------------------------
253
+ registerTool({
254
+ name: 'sap_class_includes',
255
+ description: 'List the includes of an ABAP class, or read one specific include source (testclasses, locals_def, locals_imp, macros).',
256
+ isMutating: false,
257
+ input_schema: {
258
+ type: 'object',
259
+ properties: {
260
+ className: {
261
+ type: 'string',
262
+ description: 'Class name, e.g. ZCL_ORDER_HANDLER',
263
+ },
264
+ includeType: {
265
+ type: 'string',
266
+ enum: ['testclasses', 'locals_def', 'locals_imp', 'macros'],
267
+ description: 'Specific include to read; omit to list all includes',
268
+ },
269
+ },
270
+ required: ['className'],
271
+ },
272
+ handler: async (args, ctx) => {
273
+ const result = await ctx.adt.classIncludes(args.className, args.includeType);
274
+ // classIncludes returns string (source) when includeType is given, array otherwise
275
+ if (typeof result === 'string') {
276
+ return { content: result };
277
+ }
278
+ return { content: JSON.stringify(result, null, 2) };
279
+ },
280
+ });
281
+ // -----------------------------------------------------------------------
282
+ // 12. sap_inactive_objects
283
+ // -----------------------------------------------------------------------
284
+ registerTool({
285
+ name: 'sap_inactive_objects',
286
+ description: 'List all ABAP objects that are currently inactive (not yet activated) in the system.',
287
+ isMutating: false,
288
+ input_schema: {
289
+ type: 'object',
290
+ properties: {},
291
+ required: [],
292
+ },
293
+ handler: async (_args, ctx) => {
294
+ const result = await ctx.adt.inactiveObjects();
295
+ return { content: JSON.stringify(result, null, 2) };
296
+ },
297
+ });
298
+ // -----------------------------------------------------------------------
299
+ // 13. sap_syntax_check
300
+ // -----------------------------------------------------------------------
301
+ registerTool({
302
+ name: 'sap_syntax_check',
303
+ description: 'Run a syntax check on an ABAP object and return any errors or warnings without activating it.',
304
+ isMutating: false,
305
+ input_schema: {
306
+ type: 'object',
307
+ properties: {
308
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
309
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
310
+ },
311
+ required: ['name', 'type'],
312
+ },
313
+ handler: async (args, ctx) => {
314
+ // syntaxCheck(type, name) — type first
315
+ const result = await ctx.adt.syntaxCheck(args.type, args.name);
316
+ return { content: JSON.stringify(result, null, 2) };
317
+ },
318
+ });
319
+ // -----------------------------------------------------------------------
320
+ // 14. sap_diff
321
+ // -----------------------------------------------------------------------
322
+ registerTool({
323
+ name: 'sap_diff',
324
+ description: 'Compute a unified diff for an ABAP object between two optional source strings; if omitted, diffs the current version against the previous from the change log.',
325
+ isMutating: false,
326
+ input_schema: {
327
+ type: 'object',
328
+ properties: {
329
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
330
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
331
+ version1: {
332
+ type: 'string',
333
+ description: 'Optional "before" source text; omit to use the previous saved version',
334
+ },
335
+ version2: {
336
+ type: 'string',
337
+ description: 'Optional "after" source text; omit to use the current saved version',
338
+ },
339
+ },
340
+ required: ['name', 'type'],
341
+ },
342
+ handler: async (args, ctx) => {
343
+ // diff(type, name, version1?, version2?) — type first
344
+ const result = await ctx.adt.diff(args.type, args.name, args.version1, args.version2);
345
+ return { content: JSON.stringify(result, null, 2) };
346
+ },
347
+ });
348
+ // -----------------------------------------------------------------------
349
+ // 15. sap_preview_change
350
+ // -----------------------------------------------------------------------
351
+ registerTool({
352
+ name: 'sap_preview_change',
353
+ description: 'Preview a proposed source change for an ABAP object as a unified diff without writing anything to the system.',
354
+ isMutating: false,
355
+ input_schema: {
356
+ type: 'object',
357
+ properties: {
358
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
359
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG, INTF, DDLS' },
360
+ newSource: {
361
+ type: 'string',
362
+ description: 'The full proposed source code to compare against the current version',
363
+ },
364
+ },
365
+ required: ['name', 'type', 'newSource'],
366
+ },
367
+ handler: async (args, ctx) => {
368
+ // previewChange(type, name, newSource) — type first
369
+ const result = await ctx.adt.previewChange(args.type, args.name, args.newSource);
370
+ return { content: JSON.stringify(result, null, 2) };
371
+ },
372
+ });
373
+ // -----------------------------------------------------------------------
374
+ // 16. sap_run_unit_test
375
+ // -----------------------------------------------------------------------
376
+ registerTool({
377
+ name: 'sap_run_unit_test',
378
+ description: 'Execute ABAP Unit tests for an object and return a structured pass/fail report with method-level results.',
379
+ isMutating: false,
380
+ input_schema: {
381
+ type: 'object',
382
+ properties: {
383
+ name: { type: 'string', description: 'Object name, e.g. ZCL_ORDER_HANDLER' },
384
+ type: { type: 'string', description: 'Object type token, e.g. CLAS, PROG' },
385
+ },
386
+ required: ['name', 'type'],
387
+ },
388
+ handler: async (args, ctx) => {
389
+ // runUnitTest(type, name) — type first
390
+ const result = await ctx.adt.runUnitTest(args.type, args.name);
391
+ return { content: JSON.stringify(result, null, 2) };
392
+ },
393
+ });
394
+ // -----------------------------------------------------------------------
395
+ // 17. sap_odata_test
396
+ // -----------------------------------------------------------------------
397
+ registerTool({
398
+ name: 'sap_odata_test',
399
+ description: 'Test an OData service endpoint by issuing a GET or HEAD request and returning the HTTP status and response body.',
400
+ isMutating: false,
401
+ input_schema: {
402
+ type: 'object',
403
+ properties: {
404
+ servicePath: {
405
+ type: 'string',
406
+ description: 'Full service path, e.g. /sap/opu/odata/sap/ZSALESORDER_SD_SRV',
407
+ },
408
+ entity: {
409
+ type: 'string',
410
+ description: 'Optional entity set name to append, e.g. SalesOrder',
411
+ },
412
+ method: {
413
+ type: 'string',
414
+ enum: ['GET', 'HEAD'],
415
+ description: 'HTTP method to use (default: GET)',
416
+ },
417
+ },
418
+ required: ['servicePath'],
419
+ },
420
+ handler: async (args, ctx) => {
421
+ const result = await ctx.adt.odataTest(args.servicePath, args.entity, args.method ?? 'GET');
422
+ return { content: JSON.stringify(result, null, 2) };
423
+ },
424
+ });
425
+ // -----------------------------------------------------------------------
426
+ // 18. sap_badi_list
427
+ // -----------------------------------------------------------------------
428
+ registerTool({
429
+ name: 'sap_badi_list',
430
+ description: 'Search for BAdI definitions and enhancement spots by name pattern (supports * wildcard), with optional enhancement-spot drill-down.',
431
+ isMutating: false,
432
+ input_schema: {
433
+ type: 'object',
434
+ properties: {
435
+ query: {
436
+ type: 'string',
437
+ description: 'BAdI name pattern with optional * wildcard, e.g. MD_PURCH* or BADI_SD*',
438
+ },
439
+ enhancementSpot: {
440
+ type: 'string',
441
+ description: 'Optional enhancement spot name to drill into for its BAdI definitions',
442
+ },
443
+ maxResults: {
444
+ type: 'number',
445
+ description: 'Maximum search results (default 50)',
446
+ },
447
+ },
448
+ required: ['query'],
449
+ },
450
+ handler: async (args, ctx) => {
451
+ const result = await ctx.adt.badiList(args.query, args.enhancementSpot, args.maxResults ?? 50);
452
+ return { content: JSON.stringify(result, null, 2) };
453
+ },
454
+ });