abapgit-agent 1.1.5 → 1.2.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/.github/workflows/release.yml +9 -22
- package/CLAUDE.md +248 -0
- package/README.md +16 -2
- package/RELEASE_NOTES.md +80 -8
- package/abap/CLAUDE.md +72 -6
- package/abap/copilot-instructions.md +51 -0
- package/abap/zcl_abgagt_cmd_factory.clas.abap +2 -0
- package/abap/zcl_abgagt_command_tree.clas.abap +237 -0
- package/abap/zcl_abgagt_command_tree.clas.xml +15 -0
- package/abap/zcl_abgagt_command_view.clas.abap +238 -0
- package/abap/zcl_abgagt_command_view.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_tree.clas.abap +70 -0
- package/abap/zcl_abgagt_resource_tree.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_view.clas.abap +68 -0
- package/abap/zcl_abgagt_resource_view.clas.xml +15 -0
- package/abap/zcl_abgagt_rest_handler.clas.abap +2 -0
- package/abap/zcl_abgagt_viewer_clas.clas.abap +58 -0
- package/abap/zcl_abgagt_viewer_clas.clas.xml +15 -0
- package/abap/zcl_abgagt_viewer_dtel.clas.abap +98 -0
- package/abap/zcl_abgagt_viewer_dtel.clas.xml +15 -0
- package/abap/zcl_abgagt_viewer_factory.clas.abap +41 -0
- package/abap/zcl_abgagt_viewer_factory.clas.xml +15 -0
- package/abap/zcl_abgagt_viewer_intf.clas.abap +58 -0
- package/abap/zcl_abgagt_viewer_intf.clas.xml +15 -0
- package/abap/zcl_abgagt_viewer_stru.clas.abap +59 -0
- package/abap/zcl_abgagt_viewer_stru.clas.xml +15 -0
- package/abap/zcl_abgagt_viewer_tabl.clas.abap +59 -0
- package/abap/zcl_abgagt_viewer_tabl.clas.xml +15 -0
- package/abap/zif_abgagt_command.intf.abap +3 -1
- package/abap/zif_abgagt_viewer.intf.abap +11 -0
- package/abap/zif_abgagt_viewer.intf.xml +15 -0
- package/bin/abapgit-agent +397 -0
- package/docs/commands.md +27 -8
- package/docs/tree-command.md +303 -0
- package/docs/view-command.md +409 -0
- package/package.json +1 -1
- package/src/abap-client.js +22 -0
- package/src/agent.js +27 -0
package/bin/abapgit-agent
CHANGED
|
@@ -495,6 +495,136 @@ async function runUnitTestForFile(sourceFile, csrfToken, config) {
|
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
+
/**
|
|
499
|
+
* Run tree command and return raw result
|
|
500
|
+
*/
|
|
501
|
+
async function runTreeCommand(packageName, depth, includeObjects, csrfToken, config) {
|
|
502
|
+
const data = {
|
|
503
|
+
package: packageName,
|
|
504
|
+
depth: depth,
|
|
505
|
+
include_objects: includeObjects
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
return await request('POST', '/sap/bc/z_abapgit_agent/tree', data, { csrfToken });
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Display tree output in human-readable format
|
|
513
|
+
*/
|
|
514
|
+
async function displayTreeOutput(packageName, depth, includeObjects) {
|
|
515
|
+
const config = loadConfig();
|
|
516
|
+
const csrfToken = await fetchCsrfToken(config);
|
|
517
|
+
|
|
518
|
+
console.log(`\n Getting package tree for: ${packageName}`);
|
|
519
|
+
|
|
520
|
+
const result = await runTreeCommand(packageName, depth, includeObjects, csrfToken, config);
|
|
521
|
+
|
|
522
|
+
// Handle uppercase keys from ABAP
|
|
523
|
+
const success = result.SUCCESS || result.success;
|
|
524
|
+
const error = result.ERROR || result.error;
|
|
525
|
+
|
|
526
|
+
if (!success || error) {
|
|
527
|
+
console.error(`\n ❌ Error: ${error || 'Failed to get tree'}`);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Parse hierarchy structure (ABAP returns flat nodes with parent refs)
|
|
532
|
+
const nodes = result.NODES || result.nodes || [];
|
|
533
|
+
const rootPackage = result.PACKAGE || result.package || packageName;
|
|
534
|
+
const parentPackage = result.PARENT_PACKAGE || result.parent_package;
|
|
535
|
+
const totalPackages = result.TOTAL_PACKAGES || result.total_packages || 0;
|
|
536
|
+
const totalObjects = result.TOTAL_OBJECTS || result.total_objects || 0;
|
|
537
|
+
const objectTypes = result.OBJECTS || result.objects || [];
|
|
538
|
+
|
|
539
|
+
console.log(`\n Package Tree: ${rootPackage}`);
|
|
540
|
+
|
|
541
|
+
// Display parent info if available
|
|
542
|
+
if (parentPackage && parentPackage !== rootPackage) {
|
|
543
|
+
console.log(` ⬆️ Parent: ${parentPackage}`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
console.log('');
|
|
547
|
+
|
|
548
|
+
// Build and display tree from flat nodes list
|
|
549
|
+
const lines = buildTreeLinesFromNodes(nodes, '', true);
|
|
550
|
+
for (const line of lines) {
|
|
551
|
+
console.log(` ${line}`);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
console.log('');
|
|
555
|
+
console.log(' Summary');
|
|
556
|
+
console.log(` PACKAGES: ${totalPackages}`);
|
|
557
|
+
console.log(` OBJECTS: ${totalObjects}`);
|
|
558
|
+
|
|
559
|
+
// Display object types if available
|
|
560
|
+
if (includeObjects && objectTypes.length > 0) {
|
|
561
|
+
const typeStr = objectTypes.map(t => `${t.OBJECT || t.object}=${t.COUNT || t.count}`).join(' ');
|
|
562
|
+
console.log(` TYPES: ${typeStr}`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Build tree display lines from flat nodes list
|
|
568
|
+
*/
|
|
569
|
+
function buildTreeLinesFromNodes(nodes, prefix, isLast) {
|
|
570
|
+
const lines = [];
|
|
571
|
+
|
|
572
|
+
if (nodes.length === 0) {
|
|
573
|
+
return lines;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// First node is the root
|
|
577
|
+
const root = nodes[0];
|
|
578
|
+
const icon = '📦';
|
|
579
|
+
lines.push(`${prefix}${isLast ? '└─ ' : '├─ '} ${icon} ${root.PACKAGE || root.package}`);
|
|
580
|
+
|
|
581
|
+
// Get children (nodes with depth > 0, grouped by depth)
|
|
582
|
+
const children = nodes.filter(n => (n.DEPTH || n.depth) > 0);
|
|
583
|
+
|
|
584
|
+
// Group children by depth
|
|
585
|
+
const byDepth = {};
|
|
586
|
+
children.forEach(n => {
|
|
587
|
+
const d = n.DEPTH || n.depth;
|
|
588
|
+
if (!byDepth[d]) byDepth[d] = [];
|
|
589
|
+
byDepth[d].push(n);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// Process depth 1 children
|
|
593
|
+
const depth1 = byDepth[1] || [];
|
|
594
|
+
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
595
|
+
|
|
596
|
+
for (let i = 0; i < depth1.length; i++) {
|
|
597
|
+
const child = depth1[i];
|
|
598
|
+
const isSubLast = i === depth1.length - 1;
|
|
599
|
+
const childLines = buildChildLines(child, newPrefix, isSubLast, byDepth);
|
|
600
|
+
lines.push(...childLines);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return lines;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function buildChildLines(node, prefix, isLast, byDepth) {
|
|
607
|
+
const lines = [];
|
|
608
|
+
const icon = '📦';
|
|
609
|
+
lines.push(`${prefix}${isLast ? '└─ ' : '├─ '} ${icon} ${node.PACKAGE || node.package}`);
|
|
610
|
+
|
|
611
|
+
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
612
|
+
const nodeDepth = node.DEPTH || node.depth;
|
|
613
|
+
const children = byDepth[nodeDepth + 1] || [];
|
|
614
|
+
|
|
615
|
+
// Find children of this node
|
|
616
|
+
const myChildren = children.filter(n => (n.PARENT || n.parent) === (node.PACKAGE || node.package));
|
|
617
|
+
|
|
618
|
+
for (let i = 0; i < myChildren.length; i++) {
|
|
619
|
+
const child = myChildren[i];
|
|
620
|
+
const isSubLast = i === myChildren.length - 1;
|
|
621
|
+
const childLines = buildChildLines(child, newPrefix, isSubLast, byDepth);
|
|
622
|
+
lines.push(...childLines);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return lines;
|
|
626
|
+
}
|
|
627
|
+
|
|
498
628
|
/**
|
|
499
629
|
* Pull and activate repository
|
|
500
630
|
*/
|
|
@@ -818,6 +948,25 @@ async function runInit(args) {
|
|
|
818
948
|
console.error(`Error copying CLAUDE.md: ${error.message}`);
|
|
819
949
|
}
|
|
820
950
|
|
|
951
|
+
// Copy copilot-instructions.md for GitHub Copilot
|
|
952
|
+
const copilotMdPath = pathModule.join(__dirname, '..', 'abap', 'copilot-instructions.md');
|
|
953
|
+
const githubDir = pathModule.join(process.cwd(), '.github');
|
|
954
|
+
const localCopilotMdPath = pathModule.join(githubDir, 'copilot-instructions.md');
|
|
955
|
+
try {
|
|
956
|
+
if (fs.existsSync(copilotMdPath)) {
|
|
957
|
+
// Ensure .github directory exists
|
|
958
|
+
if (!fs.existsSync(githubDir)) {
|
|
959
|
+
fs.mkdirSync(githubDir, { recursive: true });
|
|
960
|
+
}
|
|
961
|
+
fs.copyFileSync(copilotMdPath, localCopilotMdPath);
|
|
962
|
+
console.log(`✅ Created .github/copilot-instructions.md`);
|
|
963
|
+
} else {
|
|
964
|
+
console.log(`⚠️ copilot-instructions.md not found in abap/ directory`);
|
|
965
|
+
}
|
|
966
|
+
} catch (error) {
|
|
967
|
+
console.error(`Error copying copilot-instructions.md: ${error.message}`);
|
|
968
|
+
}
|
|
969
|
+
|
|
821
970
|
// Create folder
|
|
822
971
|
const folderPath = pathModule.join(process.cwd(), folder);
|
|
823
972
|
try {
|
|
@@ -1173,6 +1322,240 @@ Examples:
|
|
|
1173
1322
|
break;
|
|
1174
1323
|
}
|
|
1175
1324
|
|
|
1325
|
+
case 'tree': {
|
|
1326
|
+
const packageArgIndex = args.indexOf('--package');
|
|
1327
|
+
if (packageArgIndex === -1 || packageArgIndex + 1 >= args.length) {
|
|
1328
|
+
console.error('Error: --package parameter required');
|
|
1329
|
+
console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
|
|
1330
|
+
console.error('Example: abapgit-agent tree --package $ZMY_PACKAGE');
|
|
1331
|
+
process.exit(1);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
const packageName = args[packageArgIndex + 1];
|
|
1335
|
+
|
|
1336
|
+
// Optional depth parameter
|
|
1337
|
+
const depthArgIndex = args.indexOf('--depth');
|
|
1338
|
+
let depth = 3;
|
|
1339
|
+
if (depthArgIndex !== -1 && depthArgIndex + 1 < args.length) {
|
|
1340
|
+
depth = parseInt(args[depthArgIndex + 1], 10);
|
|
1341
|
+
if (isNaN(depth) || depth < 1) {
|
|
1342
|
+
console.error('Error: --depth must be a positive number');
|
|
1343
|
+
process.exit(1);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Optional include-objects parameter
|
|
1348
|
+
const includeObjects = args.includes('--include-objects');
|
|
1349
|
+
|
|
1350
|
+
// Optional json parameter
|
|
1351
|
+
const jsonOutput = args.includes('--json');
|
|
1352
|
+
|
|
1353
|
+
if (jsonOutput) {
|
|
1354
|
+
const config = loadConfig();
|
|
1355
|
+
const csrfToken = await fetchCsrfToken(config);
|
|
1356
|
+
const result = await runTreeCommand(packageName, depth, includeObjects, csrfToken, config);
|
|
1357
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1358
|
+
} else {
|
|
1359
|
+
await displayTreeOutput(packageName, depth, includeObjects);
|
|
1360
|
+
}
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
case 'view': {
|
|
1365
|
+
const objectsArgIndex = args.indexOf('--objects');
|
|
1366
|
+
if (objectsArgIndex === -1 || objectsArgIndex + 1 >= args.length) {
|
|
1367
|
+
console.error('Error: --objects parameter required');
|
|
1368
|
+
console.error('Usage: abapgit-agent view --objects <obj1>,<obj2>,... [--type <type>] [--json]');
|
|
1369
|
+
console.error('Example: abapgit-agent view --objects ZCL_MY_CLASS');
|
|
1370
|
+
console.error('Example: abapgit-agent view --objects ZCL_CLASS1,ZCL_CLASS2 --type CLAS');
|
|
1371
|
+
process.exit(1);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
const objects = args[objectsArgIndex + 1].split(',').map(o => o.trim().toUpperCase());
|
|
1375
|
+
const typeArg = args.indexOf('--type');
|
|
1376
|
+
const type = typeArg !== -1 ? args[typeArg + 1].toUpperCase() : null;
|
|
1377
|
+
const jsonOutput = args.includes('--json');
|
|
1378
|
+
|
|
1379
|
+
console.log(`\n Viewing ${objects.length} object(s)`);
|
|
1380
|
+
|
|
1381
|
+
const config = loadConfig();
|
|
1382
|
+
const csrfToken = await fetchCsrfToken(config);
|
|
1383
|
+
|
|
1384
|
+
const data = {
|
|
1385
|
+
objects: objects
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
if (type) {
|
|
1389
|
+
data.type = type;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
const result = await request('POST', '/sap/bc/z_abapgit_agent/view', data, { csrfToken });
|
|
1393
|
+
|
|
1394
|
+
// Handle uppercase keys from ABAP
|
|
1395
|
+
const success = result.SUCCESS || result.success;
|
|
1396
|
+
const viewObjects = result.OBJECTS || result.objects || [];
|
|
1397
|
+
const message = result.MESSAGE || result.message || '';
|
|
1398
|
+
const error = result.ERROR || result.error;
|
|
1399
|
+
|
|
1400
|
+
if (!success || error) {
|
|
1401
|
+
console.error(`\n Error: ${error || 'Failed to view objects'}`);
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
if (jsonOutput) {
|
|
1406
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1407
|
+
} else {
|
|
1408
|
+
console.log(`\n ${message}`);
|
|
1409
|
+
console.log('');
|
|
1410
|
+
|
|
1411
|
+
for (let i = 0; i < viewObjects.length; i++) {
|
|
1412
|
+
const obj = viewObjects[i];
|
|
1413
|
+
const objName = obj.NAME || obj.name || `Object ${i + 1}`;
|
|
1414
|
+
const objType = obj.TYPE || obj.type || '';
|
|
1415
|
+
const objTypeText = obj.TYPE_TEXT || obj.type_text || '';
|
|
1416
|
+
const description = obj.DESCRIPTION || obj.description || '';
|
|
1417
|
+
const methods = obj.METHODS || obj.methods || [];
|
|
1418
|
+
const components = obj.COMPONENTS || obj.components || [];
|
|
1419
|
+
const notFound = obj.NOT_FOUND || obj.not_found || false;
|
|
1420
|
+
|
|
1421
|
+
// Check if object was not found
|
|
1422
|
+
if (notFound) {
|
|
1423
|
+
console.log(` ❌ ${objName} (${objTypeText})`);
|
|
1424
|
+
console.log(` Object not found: ${objName}`);
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
console.log(` 📖 ${objName} (${objTypeText})`);
|
|
1429
|
+
if (description) {
|
|
1430
|
+
console.log(` ${description}`);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// Display source code for classes and interfaces
|
|
1434
|
+
const source = obj.SOURCE || obj.source || '';
|
|
1435
|
+
if (source && (objType === 'INTF' || objType === 'Interface' || objType === 'CLAS' || objType === 'Class')) {
|
|
1436
|
+
console.log('');
|
|
1437
|
+
// Replace escaped newlines with actual newlines and display
|
|
1438
|
+
const displaySource = source.replace(/\\n/g, '\n');
|
|
1439
|
+
const lines = displaySource.split('\n');
|
|
1440
|
+
for (const line of lines) {
|
|
1441
|
+
console.log(` ${line}`);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
if (methods.length > 0) {
|
|
1446
|
+
console.log(` Methods: ${methods.length}`);
|
|
1447
|
+
for (const method of methods.slice(0, 5)) {
|
|
1448
|
+
const name = method.NAME || method.name || '';
|
|
1449
|
+
const visibility = method.VISIBILITY || method.visibility || '';
|
|
1450
|
+
console.log(` - ${visibility} ${name}`);
|
|
1451
|
+
}
|
|
1452
|
+
if (methods.length > 5) {
|
|
1453
|
+
console.log(` ... and ${methods.length - 5} more`);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
if (components.length > 0) {
|
|
1458
|
+
// Check if this is a data element (DTEL) - show domain info in property format
|
|
1459
|
+
if (objType === 'DTEL' || objType === 'Data Element') {
|
|
1460
|
+
const propWidth = 18;
|
|
1461
|
+
const valueWidth = 40;
|
|
1462
|
+
|
|
1463
|
+
// Build separator with corners
|
|
1464
|
+
const sep = '┌' + '─'.repeat(propWidth + 2) + '┬' + '─'.repeat(valueWidth + 2) + '┐';
|
|
1465
|
+
const mid = '├' + '─'.repeat(propWidth + 2) + '┼' + '─'.repeat(valueWidth + 2) + '┤';
|
|
1466
|
+
const end = '└' + '─'.repeat(propWidth + 2) + '┴' + '─'.repeat(valueWidth + 2) + '┘';
|
|
1467
|
+
|
|
1468
|
+
// Helper to build row
|
|
1469
|
+
const buildPropRow = (property, value) => {
|
|
1470
|
+
return '│ ' + String(property || '').padEnd(propWidth) + ' │ ' +
|
|
1471
|
+
String(value || '').substring(0, valueWidth).padEnd(valueWidth) + ' │';
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1474
|
+
console.log(` DATA ELEMENT ${objName}:`);
|
|
1475
|
+
console.log(sep);
|
|
1476
|
+
console.log(buildPropRow('Property', 'Value'));
|
|
1477
|
+
console.log(mid);
|
|
1478
|
+
|
|
1479
|
+
// Collect properties from top-level fields and components
|
|
1480
|
+
const domain = obj.DOMAIN || obj.domain || '';
|
|
1481
|
+
const domainType = obj.DOMAIN_TYPE || obj.domain_type || '';
|
|
1482
|
+
const domainLength = obj.DOMAIN_LENGTH || obj.domain_length || 0;
|
|
1483
|
+
const domainDecimals = obj.DOMAIN_DECIMALS || obj.domain_decimals || 0;
|
|
1484
|
+
const description = obj.DESCRIPTION || obj.description || '';
|
|
1485
|
+
|
|
1486
|
+
if (domainType) {
|
|
1487
|
+
console.log(buildPropRow('Data Type', domainType));
|
|
1488
|
+
}
|
|
1489
|
+
if (domainLength) {
|
|
1490
|
+
console.log(buildPropRow('Length', String(domainLength)));
|
|
1491
|
+
}
|
|
1492
|
+
if (domainDecimals) {
|
|
1493
|
+
console.log(buildPropRow('Decimals', String(domainDecimals)));
|
|
1494
|
+
}
|
|
1495
|
+
if (description) {
|
|
1496
|
+
console.log(buildPropRow('Description', description));
|
|
1497
|
+
}
|
|
1498
|
+
if (domain) {
|
|
1499
|
+
console.log(buildPropRow('Domain', domain));
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
console.log(end);
|
|
1503
|
+
} else {
|
|
1504
|
+
// Build table display for TABL/STRU with Data Element and Description
|
|
1505
|
+
const colWidths = {
|
|
1506
|
+
field: 16, // Max field name length
|
|
1507
|
+
key: 3,
|
|
1508
|
+
type: 8,
|
|
1509
|
+
length: 8,
|
|
1510
|
+
dataelement: 30, // Max data element name length
|
|
1511
|
+
description: 60, // Max field description length
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
// Helper to truncate with ellipsis if needed
|
|
1515
|
+
const truncate = (str, maxLen) => {
|
|
1516
|
+
const s = String(str || '');
|
|
1517
|
+
if (s.length <= maxLen) return s;
|
|
1518
|
+
return s.substring(0, maxLen - 1) + '…';
|
|
1519
|
+
};
|
|
1520
|
+
|
|
1521
|
+
// Helper to build row
|
|
1522
|
+
const buildRow = (field, key, type, length, dataelement, description) => {
|
|
1523
|
+
return ' | ' + truncate(field, colWidths.field).padEnd(colWidths.field) + ' | ' + String(key || '').padEnd(colWidths.key) + ' | ' + truncate(type, colWidths.type).padEnd(colWidths.type) + ' | ' + String(length || '').padStart(colWidths.length) + ' | ' + truncate(dataelement, colWidths.dataelement).padEnd(colWidths.dataelement) + ' | ' + truncate(description, colWidths.description).padEnd(colWidths.description) + ' |';
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1526
|
+
// Build separator line (matches row structure with | at ends and + between columns)
|
|
1527
|
+
const sep = ' |' + '-'.repeat(colWidths.field + 2) + '+' +
|
|
1528
|
+
'-'.repeat(colWidths.key + 2) + '+' +
|
|
1529
|
+
'-'.repeat(colWidths.type + 2) + '+' +
|
|
1530
|
+
'-'.repeat(colWidths.length + 2) + '+' +
|
|
1531
|
+
'-'.repeat(colWidths.dataelement + 2) + '+' +
|
|
1532
|
+
'-'.repeat(colWidths.description + 2) + '|';
|
|
1533
|
+
|
|
1534
|
+
// Header
|
|
1535
|
+
console.log(` TABLE ${objName}:`);
|
|
1536
|
+
console.log(sep);
|
|
1537
|
+
console.log(buildRow('Field', 'Key', 'Type', 'Length', 'Data Elem', 'Description'));
|
|
1538
|
+
console.log(sep);
|
|
1539
|
+
|
|
1540
|
+
// Rows
|
|
1541
|
+
for (const comp of components) {
|
|
1542
|
+
const key = comp.KEY || comp.key || false ? 'X' : '';
|
|
1543
|
+
const dataelement = comp.DATAELEMENT || comp.dataelement || '';
|
|
1544
|
+
const description = comp.DESCRIPTION || comp.description || '';
|
|
1545
|
+
console.log(buildRow(comp.FIELD || comp.field, key, comp.TYPE || comp.type, comp.LENGTH || comp.length, dataelement, description));
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
console.log(sep);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
console.log('');
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
}
|
|
1556
|
+
break;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1176
1559
|
case 'help':
|
|
1177
1560
|
case '--help':
|
|
1178
1561
|
case '-h':
|
|
@@ -1206,6 +1589,12 @@ Commands:
|
|
|
1206
1589
|
unit --files <file1>,<file2>,...
|
|
1207
1590
|
Run AUnit tests for ABAP test class files (.testclasses.abap)
|
|
1208
1591
|
|
|
1592
|
+
tree --package <package> [--depth <n>] [--include-objects] [--json]
|
|
1593
|
+
Display package hierarchy tree from ABAP system
|
|
1594
|
+
|
|
1595
|
+
view --objects <obj1>,<obj2>,... [--type <type>] [--json]
|
|
1596
|
+
View ABAP object definitions from the ABAP system
|
|
1597
|
+
|
|
1209
1598
|
health
|
|
1210
1599
|
Check if ABAP REST API is healthy
|
|
1211
1600
|
|
|
@@ -1223,6 +1612,14 @@ Examples:
|
|
|
1223
1612
|
abapgit-agent pull --transport DEVK900001 # With transport
|
|
1224
1613
|
abapgit-agent inspect --files zcl_my_class.clas.abap # Syntax check
|
|
1225
1614
|
abapgit-agent unit --files zcl_my_test.clas.testclasses.abap # Run tests
|
|
1615
|
+
abapgit-agent tree --package $ZMY_PACKAGE # Show package tree
|
|
1616
|
+
abapgit-agent tree --package $ZMY_PACKAGE --depth 2 # Shallow tree
|
|
1617
|
+
abapgit-agent tree --package $ZMY_PACKAGE --include-objects # With object counts
|
|
1618
|
+
abapgit-agent tree --package $ZMY_PACKAGE --json # JSON output
|
|
1619
|
+
abapgit-agent view --objects ZCL_MY_CLASS # View class definition
|
|
1620
|
+
abapgit-agent view --objects ZIF_MY_INTERFACE --type INTF # View interface
|
|
1621
|
+
abapgit-agent view --objects ZMY_TABLE --type TABL # View table structure
|
|
1622
|
+
abapgit-agent view --objects ZCL_CLASS1,ZCL_CLASS2 --json # Multiple objects
|
|
1226
1623
|
abapgit-agent health
|
|
1227
1624
|
abapgit-agent status
|
|
1228
1625
|
`);
|
package/docs/commands.md
CHANGED
|
@@ -11,7 +11,9 @@ All available CLI commands for abapGit Agent.
|
|
|
11
11
|
| [import](import-command.md) | ✅ | Import objects from ABAP package to git |
|
|
12
12
|
| [pull](pull-command.md) | ✅ | Pull and activate objects in ABAP |
|
|
13
13
|
| [inspect](inspect-command.md) | ✅ | Syntax check ABAP source files |
|
|
14
|
+
| [tree](tree-command.md) | ✅ | Display package hierarchy tree |
|
|
14
15
|
| [unit](unit-command.md) | ✅ | Run AUnit tests |
|
|
16
|
+
| [view](view-command.md) | ✅ | View ABAP object source code from system |
|
|
15
17
|
| [health](health-command.md) | ✅ | Health check |
|
|
16
18
|
| [status](status-command.md) | ✅ | Status check |
|
|
17
19
|
|
|
@@ -23,21 +25,24 @@ All available CLI commands for abapGit Agent.
|
|
|
23
25
|
|
|
24
26
|
```bash
|
|
25
27
|
# 1. Initialize local configuration
|
|
26
|
-
abapgit-agent init --folder /src --package ZMY_PACKAGE
|
|
28
|
+
abapgit-agent init --folder /src/ --package ZMY_PACKAGE
|
|
27
29
|
|
|
28
30
|
# 2. Edit .abapGitAgent with credentials
|
|
29
31
|
vim .abapGitAgent
|
|
30
32
|
|
|
31
|
-
# 3.
|
|
33
|
+
# 3. Commit and push new files to git
|
|
34
|
+
git add . && git commit -m "Initial commit" && git push origin main
|
|
35
|
+
|
|
36
|
+
# 4. Create online repository in ABAP
|
|
32
37
|
abapgit-agent create
|
|
33
38
|
|
|
34
|
-
#
|
|
39
|
+
# 5. Import objects from ABAP package to git
|
|
35
40
|
abapgit-agent import
|
|
36
41
|
|
|
37
|
-
#
|
|
38
|
-
git
|
|
42
|
+
# 6. Pull new objects imported from ABAP package
|
|
43
|
+
git pull origin main
|
|
39
44
|
|
|
40
|
-
#
|
|
45
|
+
# 7. Activate in ABAP
|
|
41
46
|
abapgit-agent pull
|
|
42
47
|
```
|
|
43
48
|
|
|
@@ -58,6 +63,13 @@ abapgit-agent inspect --files abap/zcl_my_class.clas.abap
|
|
|
58
63
|
# Run unit tests
|
|
59
64
|
abapgit-agent unit --files abap/zcl_my_test.clas.testclasses.abap
|
|
60
65
|
|
|
66
|
+
# Display package hierarchy
|
|
67
|
+
abapgit-agent tree --package $MY_PACKAGE
|
|
68
|
+
|
|
69
|
+
# View object definitions (classes, interfaces, tables, data elements)
|
|
70
|
+
abapgit-agent view --objects ZCL_MY_CLASS
|
|
71
|
+
abapgit-agent view --objects SFLIGHT --type TABL
|
|
72
|
+
|
|
61
73
|
# Check configuration
|
|
62
74
|
abapgit-agent status
|
|
63
75
|
|
|
@@ -77,6 +89,12 @@ abapgit-agent health
|
|
|
77
89
|
│
|
|
78
90
|
▼
|
|
79
91
|
┌─────────────────────────────────────────────────┐
|
|
92
|
+
│ git add && git commit && git push │
|
|
93
|
+
│ └── Push initial files to git │
|
|
94
|
+
└─────────────────────────────────────────────────┘
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
┌─────────────────────────────────────────────────┐
|
|
80
98
|
│ create │
|
|
81
99
|
│ └── Creates online repo in ABAP │
|
|
82
100
|
└─────────────────────────────────────────────────┘
|
|
@@ -84,12 +102,13 @@ abapgit-agent health
|
|
|
84
102
|
▼
|
|
85
103
|
┌─────────────────────────────────────────────────┐
|
|
86
104
|
│ import │
|
|
87
|
-
│ └── Stages, commits, pushes objects to git
|
|
105
|
+
│ └── Stages, commits, pushes objects to git │
|
|
88
106
|
└─────────────────────────────────────────────────┘
|
|
89
107
|
│
|
|
90
108
|
▼
|
|
91
109
|
┌─────────────────────────────────────────────────┐
|
|
92
|
-
│ git
|
|
110
|
+
│ git pull │
|
|
111
|
+
│ └── Pull imported objects from ABAP package │
|
|
93
112
|
└─────────────────────────────────────────────────┘
|
|
94
113
|
│
|
|
95
114
|
▼
|