abapgit-agent 1.7.2 → 1.8.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 (39) hide show
  1. package/README.md +7 -7
  2. package/abap/CLAUDE.md +145 -26
  3. package/abap/guidelines/00_index.md +8 -0
  4. package/abap/guidelines/01_sql.md +8 -0
  5. package/abap/guidelines/02_exceptions.md +8 -0
  6. package/abap/guidelines/03_testing.md +8 -0
  7. package/abap/guidelines/04_cds.md +8 -0
  8. package/abap/guidelines/05_classes.md +8 -0
  9. package/abap/guidelines/06_objects.md +8 -0
  10. package/abap/guidelines/07_json.md +8 -0
  11. package/abap/guidelines/08_abapgit.md +8 -0
  12. package/abap/guidelines/09_unit_testable_code.md +8 -0
  13. package/bin/abapgit-agent +61 -2852
  14. package/package.json +21 -5
  15. package/src/agent.js +205 -16
  16. package/src/commands/create.js +102 -0
  17. package/src/commands/delete.js +72 -0
  18. package/src/commands/health.js +24 -0
  19. package/src/commands/help.js +111 -0
  20. package/src/commands/import.js +99 -0
  21. package/src/commands/init.js +321 -0
  22. package/src/commands/inspect.js +184 -0
  23. package/src/commands/list.js +143 -0
  24. package/src/commands/preview.js +277 -0
  25. package/src/commands/pull.js +278 -0
  26. package/src/commands/ref.js +96 -0
  27. package/src/commands/status.js +52 -0
  28. package/src/commands/syntax.js +290 -0
  29. package/src/commands/tree.js +209 -0
  30. package/src/commands/unit.js +133 -0
  31. package/src/commands/view.js +215 -0
  32. package/src/commands/where.js +138 -0
  33. package/src/config.js +11 -1
  34. package/src/utils/abap-http.js +347 -0
  35. package/src/utils/git-utils.js +58 -0
  36. package/src/utils/validators.js +72 -0
  37. package/src/utils/version-check.js +80 -0
  38. package/src/abap-client.js +0 -526
  39. /package/src/{ref-search.js → utils/abap-reference.js} +0 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Import command - Import existing objects from package to git repository
3
+ */
4
+
5
+ module.exports = {
6
+ name: 'import',
7
+ description: 'Import existing objects from package to git repository',
8
+ requiresAbapConfig: true,
9
+ requiresVersionCheck: true,
10
+
11
+ async execute(args, context) {
12
+ const { loadConfig, gitUtils, AbapHttp } = context;
13
+
14
+ // Show help if requested
15
+ const helpIndex = args.findIndex(a => a === '--help' || a === '-h');
16
+ if (helpIndex !== -1) {
17
+ console.log(`
18
+ Usage:
19
+ abapgit-agent import [--message <message>]
20
+
21
+ Description:
22
+ Import existing objects from package to git repository.
23
+ Uses the git remote URL to find the abapGit online repository.
24
+
25
+ Prerequisites:
26
+ - Run "abapgit-agent create" first or create repository in abapGit UI
27
+ - Package must have objects to import
28
+
29
+ Options:
30
+ --message Commit message (default: "feat: initial import from ABAP package <package>")
31
+
32
+ Examples:
33
+ abapgit-agent import
34
+ abapgit-agent import --message "Initial import from SAP"
35
+ `);
36
+ return;
37
+ }
38
+
39
+ // Get parameters from config
40
+ const config = loadConfig();
41
+ const repoUrl = gitUtils.getRemoteUrl();
42
+
43
+ if (!repoUrl) {
44
+ console.error('Error: No git remote configured. Please configure a remote origin.');
45
+ process.exit(1);
46
+ }
47
+
48
+ const messageArgIndex = args.indexOf('--message');
49
+ let commitMessage = null;
50
+ if (messageArgIndex !== -1 && messageArgIndex + 1 < args.length) {
51
+ commitMessage = args[messageArgIndex + 1];
52
+ }
53
+
54
+ console.log(`\nšŸ“¦ Importing objects to git repository`);
55
+ console.log(` URL: ${repoUrl}`);
56
+ if (commitMessage) {
57
+ console.log(` Message: ${commitMessage}`);
58
+ }
59
+
60
+ const http = new AbapHttp(config);
61
+ const csrfToken = await http.fetchCsrfToken();
62
+
63
+ const data = {
64
+ url: repoUrl
65
+ };
66
+
67
+ if (commitMessage) {
68
+ data.message = commitMessage;
69
+ }
70
+
71
+ if (config.gitUsername) {
72
+ data.username = config.gitUsername;
73
+ }
74
+
75
+ if (config.gitPassword) {
76
+ data.password = config.gitPassword;
77
+ }
78
+
79
+ const result = await http.post('/sap/bc/z_abapgit_agent/import', data, { csrfToken });
80
+
81
+ console.log('\n');
82
+
83
+ // Handle uppercase keys from ABAP
84
+ const success = result.SUCCESS || result.success;
85
+ const filesStaged = result.FILES_STAGED || result.files_staged;
86
+ const abapCommitMessage = result.COMMIT_MESSAGE || result.commit_message;
87
+ const error = result.ERROR || result.error;
88
+
89
+ if (success === 'X' || success === true) {
90
+ console.log(`āœ… Objects imported successfully!`);
91
+ console.log(` Files staged: ${filesStaged}`);
92
+ console.log(` Commit: ${commitMessage || abapCommitMessage}`);
93
+ } else {
94
+ console.log(`āŒ Import failed`);
95
+ console.log(` Error: ${error || 'Unknown error'}`);
96
+ process.exit(1);
97
+ }
98
+ }
99
+ };
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Init command - Initialize local repository configuration
3
+ */
4
+
5
+ const pathModule = require('path');
6
+ const fs = require('fs');
7
+
8
+ /**
9
+ * Copy a file if source exists (helper for init --update)
10
+ */
11
+ async function copyFileIfExists(srcPath, destPath, label, createParentDir = false) {
12
+ try {
13
+ if (fs.existsSync(srcPath)) {
14
+ if (createParentDir) {
15
+ const parentDir = pathModule.dirname(destPath);
16
+ if (!fs.existsSync(parentDir)) {
17
+ fs.mkdirSync(parentDir, { recursive: true });
18
+ }
19
+ }
20
+ fs.copyFileSync(srcPath, destPath);
21
+ console.log(`āœ… Updated ${label}`);
22
+ } else {
23
+ console.log(`āš ļø ${label} not found in abapgit-agent`);
24
+ }
25
+ } catch (error) {
26
+ console.error(`Error copying ${label}: ${error.message}`);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Copy guidelines folder (helper for init --update)
32
+ */
33
+ async function copyGuidelinesFolder(srcPath, destPath, overwrite = false) {
34
+ try {
35
+ if (fs.existsSync(srcPath)) {
36
+ // Create destination directory if needed
37
+ if (!fs.existsSync(destPath)) {
38
+ fs.mkdirSync(destPath, { recursive: true });
39
+ }
40
+
41
+ const files = fs.readdirSync(srcPath);
42
+ let copied = 0;
43
+
44
+ for (const file of files) {
45
+ if (file.endsWith('.md')) {
46
+ const srcFile = pathModule.join(srcPath, file);
47
+ const destFile = pathModule.join(destPath, file);
48
+
49
+ // Skip if file exists and not overwriting
50
+ if (fs.existsSync(destFile) && !overwrite) {
51
+ continue;
52
+ }
53
+
54
+ fs.copyFileSync(srcFile, destFile);
55
+ copied++;
56
+ }
57
+ }
58
+
59
+ if (copied > 0) {
60
+ console.log(`āœ… Updated guidelines/ (${copied} files)`);
61
+ } else {
62
+ console.log(`āš ļø No guideline files found`);
63
+ }
64
+ } else {
65
+ console.log(`āš ļø guidelines folder not found in abapgit-agent`);
66
+ }
67
+ } catch (error) {
68
+ console.error(`Error copying guidelines: ${error.message}`);
69
+ }
70
+ }
71
+
72
+ module.exports = {
73
+ name: 'init',
74
+ description: 'Initialize local repository configuration',
75
+ requiresAbapConfig: false,
76
+ requiresVersionCheck: false,
77
+
78
+ async execute(args, context) {
79
+ const { gitUtils } = context;
80
+
81
+ const folderArgIndex = args.indexOf('--folder');
82
+ const packageArgIndex = args.indexOf('--package');
83
+ const updateMode = args.includes('--update');
84
+
85
+ // Get parameters
86
+ let folder = folderArgIndex !== -1 && folderArgIndex + 1 < args.length
87
+ ? args[folderArgIndex + 1]
88
+ : '/src/';
89
+
90
+ // Normalize folder path: ensure it starts with / and ends with /
91
+ folder = folder.trim();
92
+ if (!folder.startsWith('/')) {
93
+ folder = '/' + folder;
94
+ }
95
+ if (!folder.endsWith('/')) {
96
+ folder = folder + '/';
97
+ }
98
+
99
+ const packageName = packageArgIndex !== -1 && packageArgIndex + 1 < args.length
100
+ ? args[packageArgIndex + 1]
101
+ : null;
102
+
103
+ // Validate package is required for non-update mode
104
+ if (!updateMode && !packageName) {
105
+ console.error('Error: --package is required');
106
+ console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
107
+ console.error(' abapgit-agent init --update # Update files to latest version');
108
+ process.exit(1);
109
+ }
110
+
111
+ // Check if current folder is git repo root
112
+ const gitDir = pathModule.join(process.cwd(), '.git');
113
+ if (!fs.existsSync(gitDir)) {
114
+ console.error('Error: Current folder is not a git repository.');
115
+ console.error('Run this command from the root folder of your git repository.');
116
+ process.exit(1);
117
+ }
118
+
119
+ // In update mode, just copy the files and return
120
+ if (updateMode) {
121
+ console.log(`\nšŸ”„ Updating abapGit Agent files`);
122
+ console.log('');
123
+
124
+ // Copy CLAUDE.md
125
+ await copyFileIfExists(
126
+ pathModule.join(__dirname, '..', '..', 'abap', 'CLAUDE.md'),
127
+ pathModule.join(process.cwd(), 'CLAUDE.md'),
128
+ 'CLAUDE.md'
129
+ );
130
+
131
+ // Copy copilot-instructions.md
132
+ await copyFileIfExists(
133
+ pathModule.join(__dirname, '..', '..', 'abap', '.github', 'copilot-instructions.md'),
134
+ pathModule.join(process.cwd(), '.github', 'copilot-instructions.md'),
135
+ '.github/copilot-instructions.md',
136
+ true // create parent dir
137
+ );
138
+
139
+ // Copy guidelines folder to project root
140
+ await copyGuidelinesFolder(
141
+ pathModule.join(__dirname, '..', '..', 'abap', 'guidelines'),
142
+ pathModule.join(process.cwd(), 'guidelines'),
143
+ true // overwrite
144
+ );
145
+
146
+ console.log(`
147
+ šŸ“‹ Update complete!
148
+ Run 'abapgit-agent ref --list-topics' to see available topics.
149
+ `);
150
+ return;
151
+ }
152
+
153
+ // Validate package is required
154
+ if (!packageName) {
155
+ console.error('Error: --package is required');
156
+ console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
157
+ console.error(' abapgit-agent init --update # Update files to latest version');
158
+ process.exit(1);
159
+ }
160
+
161
+ console.log(`\nšŸš€ Initializing abapGit Agent for local repository`);
162
+ console.log(` Folder: ${folder}`);
163
+ console.log(` Package: ${packageName}`);
164
+ console.log('');
165
+
166
+ // Detect git remote URL
167
+ const gitUrl = gitUtils.getRemoteUrl();
168
+ if (!gitUrl) {
169
+ console.error('Error: No git remote configured.');
170
+ console.error('Configure a remote with: git remote add origin <url>');
171
+ process.exit(1);
172
+ }
173
+ console.log(`šŸ“Œ Git remote: ${gitUrl}`);
174
+
175
+ // Check if .abapGitAgent already exists
176
+ const configPath = pathModule.join(process.cwd(), '.abapGitAgent');
177
+ if (fs.existsSync(configPath)) {
178
+ console.error('Error: .abapGitAgent already exists.');
179
+ console.error('To reinitialize, delete the existing file first.');
180
+ process.exit(1);
181
+ }
182
+
183
+ // Copy .abapGitAgent.example to .abapGitAgent
184
+ const samplePath = pathModule.join(__dirname, '..', '..', '.abapGitAgent.example');
185
+ if (!fs.existsSync(samplePath)) {
186
+ console.error('Error: .abapGitAgent.example not found.');
187
+ process.exit(1);
188
+ }
189
+
190
+ try {
191
+ // Read sample and update with package/folder
192
+ const sampleContent = fs.readFileSync(samplePath, 'utf8');
193
+ const config = JSON.parse(sampleContent);
194
+ config.package = packageName;
195
+ config.folder = folder;
196
+
197
+ // Write updated config
198
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
199
+ console.log(`āœ… Created .abapGitAgent`);
200
+ } catch (error) {
201
+ console.error(`Error creating .abapGitAgent: ${error.message}`);
202
+ process.exit(1);
203
+ }
204
+
205
+ // Update .gitignore
206
+ const gitignorePath = pathModule.join(process.cwd(), '.gitignore');
207
+ const ignoreEntries = ['.abapGitAgent', '.abapgit_agent_cookies.txt'];
208
+ let existingIgnore = '';
209
+
210
+ if (fs.existsSync(gitignorePath)) {
211
+ existingIgnore = fs.readFileSync(gitignorePath, 'utf8');
212
+ }
213
+
214
+ let updated = false;
215
+ let newIgnoreContent = existingIgnore;
216
+
217
+ for (const entry of ignoreEntries) {
218
+ if (!newIgnoreContent.includes(entry)) {
219
+ if (newIgnoreContent && !newIgnoreContent.endsWith('\n')) {
220
+ newIgnoreContent += '\n';
221
+ }
222
+ newIgnoreContent += entry + '\n';
223
+ updated = true;
224
+ }
225
+ }
226
+
227
+ if (updated) {
228
+ fs.writeFileSync(gitignorePath, newIgnoreContent);
229
+ console.log(`āœ… Updated .gitignore`);
230
+ } else {
231
+ fs.writeFileSync(gitignorePath, newIgnoreContent);
232
+ console.log(`āœ… .gitignore already up to date`);
233
+ }
234
+
235
+ // Copy CLAUDE.md
236
+ const claudeMdPath = pathModule.join(__dirname, '..', '..', 'abap', 'CLAUDE.md');
237
+ const localClaudeMdPath = pathModule.join(process.cwd(), 'CLAUDE.md');
238
+ try {
239
+ if (fs.existsSync(claudeMdPath)) {
240
+ fs.copyFileSync(claudeMdPath, localClaudeMdPath);
241
+ console.log(`āœ… Created CLAUDE.md`);
242
+ } else {
243
+ console.log(`āš ļø CLAUDE.md not found in abap/ directory`);
244
+ }
245
+ } catch (error) {
246
+ console.error(`Error copying CLAUDE.md: ${error.message}`);
247
+ }
248
+
249
+ // Copy copilot-instructions.md for GitHub Copilot
250
+ const copilotMdPath = pathModule.join(__dirname, '..', '..', 'abap', '.github', 'copilot-instructions.md');
251
+ const githubDir = pathModule.join(process.cwd(), '.github');
252
+ const localCopilotMdPath = pathModule.join(githubDir, 'copilot-instructions.md');
253
+ try {
254
+ if (fs.existsSync(copilotMdPath)) {
255
+ // Ensure .github directory exists
256
+ if (!fs.existsSync(githubDir)) {
257
+ fs.mkdirSync(githubDir, { recursive: true });
258
+ }
259
+ fs.copyFileSync(copilotMdPath, localCopilotMdPath);
260
+ console.log(`āœ… Created .github/copilot-instructions.md`);
261
+ } else {
262
+ console.log(`āš ļø copilot-instructions.md not found in abap/ directory`);
263
+ }
264
+ } catch (error) {
265
+ console.error(`Error copying copilot-instructions.md: ${error.message}`);
266
+ }
267
+
268
+ // Copy guidelines folder to project root
269
+ const guidelinesSrcPath = pathModule.join(__dirname, '..', '..', 'abap', 'guidelines');
270
+ const guidelinesDestPath = pathModule.join(process.cwd(), 'guidelines');
271
+ try {
272
+ if (fs.existsSync(guidelinesSrcPath)) {
273
+ if (!fs.existsSync(guidelinesDestPath)) {
274
+ // Create guidelines directory
275
+ fs.mkdirSync(guidelinesDestPath, { recursive: true });
276
+ // Copy all files from guidelines folder
277
+ const files = fs.readdirSync(guidelinesSrcPath);
278
+ for (const file of files) {
279
+ if (file.endsWith('.md')) {
280
+ fs.copyFileSync(
281
+ pathModule.join(guidelinesSrcPath, file),
282
+ pathModule.join(guidelinesDestPath, file)
283
+ );
284
+ }
285
+ }
286
+ console.log(`āœ… Created guidelines/ (${files.filter(f => f.endsWith('.md')).length} files)`);
287
+ } else {
288
+ console.log(`āš ļø guidelines/ already exists, skipped`);
289
+ }
290
+ } else {
291
+ console.log(`āš ļø guidelines folder not found in abap/ directory`);
292
+ }
293
+ } catch (error) {
294
+ console.error(`Error copying guidelines: ${error.message}`);
295
+ }
296
+
297
+ // Create folder
298
+ const folderPath = pathModule.join(process.cwd(), folder);
299
+ try {
300
+ if (!fs.existsSync(folderPath)) {
301
+ fs.mkdirSync(folderPath, { recursive: true });
302
+ console.log(`āœ… Created folder: ${folder}`);
303
+
304
+ // Create .gitkeep
305
+ const gitkeepPath = pathModule.join(folderPath, '.gitkeep');
306
+ fs.writeFileSync(gitkeepPath, '');
307
+ } else {
308
+ console.log(`āœ… Folder already exists: ${folder}`);
309
+ }
310
+ } catch (error) {
311
+ console.error(`Error creating folder: ${error.message}`);
312
+ }
313
+
314
+ console.log(`
315
+ šŸ“‹ Next steps:
316
+ 1. Edit .abapGitAgent with your ABAP credentials (host, user, password)
317
+ 2. Run 'abapgit-agent create --import' to create online repository
318
+ 3. Run 'abapgit-agent pull' to activate objects
319
+ `);
320
+ }
321
+ };
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Inspect command - Syntax check for ABAP files
3
+ */
4
+
5
+ const pathModule = require('path');
6
+
7
+ /**
8
+ * Inspect all files in one request
9
+ */
10
+ async function inspectAllFiles(files, csrfToken, config, variant, http) {
11
+ // Convert files to uppercase names
12
+ const fileNames = files.map(f => {
13
+ const baseName = pathModule.basename(f).toUpperCase();
14
+ return baseName;
15
+ });
16
+
17
+ try {
18
+ // Send all files in one request
19
+ const data = {
20
+ files: fileNames
21
+ };
22
+
23
+ // Add variant if specified
24
+ if (variant) {
25
+ data.variant = variant;
26
+ }
27
+
28
+ const result = await http.post('/sap/bc/z_abapgit_agent/inspect', data, { csrfToken });
29
+
30
+ // Handle both table result and old single result
31
+ let results = [];
32
+ if (Array.isArray(result)) {
33
+ results = result;
34
+ } else {
35
+ // Convert single result to array format
36
+ results = [{
37
+ OBJECT_TYPE: 'UNKNOWN',
38
+ OBJECT_NAME: files.join(', '),
39
+ SUCCESS: result.SUCCESS !== undefined ? result.SUCCESS === 'X' || result.SUCCESS === true : result.success,
40
+ ERROR_COUNT: result.ERROR_COUNT || result.error_count || 0,
41
+ ERRORS: result.ERRORS || result.errors || [],
42
+ WARNINGS: result.warnings || []
43
+ }];
44
+ }
45
+
46
+ return results;
47
+ } catch (error) {
48
+ console.error(`\n Error: ${error.message}`);
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Process a single inspect result
55
+ */
56
+ async function processInspectResult(res) {
57
+ // Handle both uppercase and lowercase keys
58
+ const success = res.SUCCESS !== undefined ? res.SUCCESS : res.success;
59
+ const objectType = res.OBJECT_TYPE !== undefined ? res.OBJECT_TYPE : res.object_type;
60
+ const objectName = res.OBJECT_NAME !== undefined ? res.OBJECT_NAME : res.object_name;
61
+ const errorCount = res.ERROR_COUNT !== undefined ? res.ERROR_COUNT : (res.error_count || 0);
62
+ const errors = res.ERRORS !== undefined ? res.ERRORS : (res.errors || []);
63
+ const warnings = res.WARNINGS !== undefined ? res.WARNINGS : (res.warnings || []);
64
+ const infos = res.INFOS !== undefined ? res.INFOS : (res.infos || []);
65
+
66
+ if (errorCount > 0 || warnings.length > 0 || infos.length > 0) {
67
+ if (errorCount > 0) {
68
+ console.log(`āŒ ${objectType} ${objectName} - Syntax check failed (${errorCount} error(s)):`);
69
+ } else {
70
+ const total = warnings.length + infos.length;
71
+ console.log(`āš ļø ${objectType} ${objectName} - Syntax check passed with warnings (${total}):`);
72
+ }
73
+ console.log('\nErrors:');
74
+ console.log('─'.repeat(60));
75
+
76
+ for (const err of errors) {
77
+ const line = err.LINE || err.line || '?';
78
+ const column = err.COLUMN || err.column || '?';
79
+ const text = err.TEXT || err.text || 'Unknown error';
80
+ const methodName = err.METHOD_NAME || err.method_name;
81
+ const sobjname = err.SOBJNAME || err.sobjname;
82
+
83
+ if (methodName) {
84
+ console.log(` Method: ${methodName}`);
85
+ }
86
+ console.log(` Line ${line}, Column ${column}:`);
87
+ if (sobjname && sobjname.includes('====')) {
88
+ console.log(` Include: ${sobjname}`);
89
+ }
90
+ console.log(` ${text}`);
91
+ console.log('');
92
+ }
93
+
94
+ // Show warnings if any
95
+ if (warnings.length > 0) {
96
+ console.log('Warnings:');
97
+ console.log('─'.repeat(60));
98
+ for (const warn of warnings) {
99
+ const line = warn.LINE || warn.line || '?';
100
+ const text = warn.MESSAGE || warn.message || 'Unknown warning';
101
+ const methodName = warn.METHOD_NAME || warn.method_name;
102
+ const sobjname = warn.SOBJNAME || warn.sobjname;
103
+
104
+ if (methodName) {
105
+ console.log(` Method: ${methodName}`);
106
+ }
107
+ console.log(` Line ${line}:`);
108
+ if (sobjname && sobjname.includes('====')) {
109
+ console.log(` Include: ${sobjname}`);
110
+ }
111
+ console.log(` ${text}`);
112
+ }
113
+ }
114
+
115
+ // Show infos if any
116
+ if (infos.length > 0) {
117
+ console.log('Info:');
118
+ console.log('─'.repeat(60));
119
+ for (const info of infos) {
120
+ const line = info.LINE || info.line || '?';
121
+ const text = info.MESSAGE || info.message || 'Unknown info';
122
+ const methodName = info.METHOD_NAME || info.method_name;
123
+ const sobjname = info.SOBJNAME || info.sobjname;
124
+
125
+ if (methodName) {
126
+ console.log(` Method: ${methodName}`);
127
+ }
128
+ console.log(` Line ${line}:`);
129
+ if (sobjname && sobjname.includes('====')) {
130
+ console.log(` Include: ${sobjname}`);
131
+ }
132
+ console.log(` ${text}`);
133
+ }
134
+ }
135
+ } else if (success === true || success === 'X') {
136
+ console.log(`āœ… ${objectType} ${objectName} - Syntax check passed`);
137
+ } else {
138
+ console.log(`āš ļø ${objectType} ${objectName} - Syntax check returned unexpected status`);
139
+ }
140
+ }
141
+
142
+ module.exports = {
143
+ name: 'inspect',
144
+ description: 'Inspect ABAP source files for syntax issues',
145
+ requiresAbapConfig: true,
146
+ requiresVersionCheck: true,
147
+
148
+ async execute(args, context) {
149
+ const { loadConfig, AbapHttp } = context;
150
+
151
+ const filesArgIndex = args.indexOf('--files');
152
+ if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
153
+ console.error('Error: --files parameter required');
154
+ console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,... [--variant <check-variant>]');
155
+ console.error('Example: abapgit-agent inspect --files src/zcl_my_class.clas.abap');
156
+ console.error('Example: abapgit-agent inspect --files src/zcl_my_class.clas.abap --variant ALL_CHECKS');
157
+ process.exit(1);
158
+ }
159
+
160
+ const filesSyntaxCheck = args[filesArgIndex + 1].split(',').map(f => f.trim());
161
+
162
+ // Parse optional --variant parameter
163
+ const variantArgIndex = args.indexOf('--variant');
164
+ const variant = variantArgIndex !== -1 ? args[variantArgIndex + 1] : null;
165
+
166
+ console.log(`\n Inspect for ${filesSyntaxCheck.length} file(s)`);
167
+ if (variant) {
168
+ console.log(` Using variant: ${variant}`);
169
+ }
170
+ console.log('');
171
+
172
+ const config = loadConfig();
173
+ const http = new AbapHttp(config);
174
+ const csrfToken = await http.fetchCsrfToken();
175
+
176
+ // Send all files in one request
177
+ const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config, variant, http);
178
+
179
+ // Process results
180
+ for (const result of results) {
181
+ await processInspectResult(result);
182
+ }
183
+ }
184
+ };