abapgit-agent 1.3.0 → 1.5.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 +4 -1
- package/API.md +179 -1
- package/CLAUDE.md +229 -0
- package/INSTALL.md +6 -11
- package/README.md +11 -0
- package/RELEASE_NOTES.md +44 -0
- package/abap/CLAUDE.md +242 -0
- package/abap/zcl_abgagt_cmd_factory.clas.abap +1 -0
- package/abap/zcl_abgagt_command_inspect.clas.abap +57 -12
- package/abap/zcl_abgagt_command_preview.clas.abap +386 -0
- package/abap/zcl_abgagt_command_preview.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_health.clas.abap +1 -1
- package/abap/zcl_abgagt_resource_inspect.clas.abap +1 -0
- package/abap/zcl_abgagt_resource_preview.clas.abap +67 -0
- package/abap/zcl_abgagt_resource_preview.clas.xml +15 -0
- package/abap/zcl_abgagt_rest_handler.clas.abap +1 -0
- package/abap/zif_abgagt_command.intf.abap +2 -1
- package/bin/abapgit-agent +370 -17
- package/docs/commands.md +5 -0
- package/docs/inspect-command.md +12 -1
- package/docs/list-command.md +289 -0
- package/docs/preview-command.md +528 -0
- package/docs/pull-command.md +15 -1
- package/package.json +4 -2
- package/scripts/release.js +298 -0
- package/scripts/unrelease.js +277 -0
- package/src/abap-client.js +18 -0
- package/src/agent.js +20 -1
- package/src/config.js +9 -2
package/bin/abapgit-agent
CHANGED
|
@@ -23,6 +23,74 @@ const TERM_WIDTH = getTermWidth();
|
|
|
23
23
|
|
|
24
24
|
const COOKIE_FILE = '.abapgit_agent_cookies.txt';
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Get CLI version from package.json
|
|
28
|
+
*/
|
|
29
|
+
function getCliVersion() {
|
|
30
|
+
const packageJsonPath = pathModule.join(__dirname, '..', 'package.json');
|
|
31
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
32
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
33
|
+
return pkg.version || '1.0.0';
|
|
34
|
+
}
|
|
35
|
+
return '1.0.0';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check version compatibility between CLI and ABAP API
|
|
40
|
+
*/
|
|
41
|
+
async function checkVersionCompatibility() {
|
|
42
|
+
const cliVersion = getCliVersion();
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const config = loadConfig();
|
|
46
|
+
const https = require('https');
|
|
47
|
+
const url = new URL(`/sap/bc/z_abapgit_agent/health`, `https://${config.host}:${config.sapport}`);
|
|
48
|
+
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const options = {
|
|
51
|
+
hostname: url.hostname,
|
|
52
|
+
port: url.port,
|
|
53
|
+
path: url.pathname,
|
|
54
|
+
method: 'GET',
|
|
55
|
+
headers: {
|
|
56
|
+
'Authorization': `Basic ${Buffer.from(`${config.user}:${config.password}`).toString('base64')}`,
|
|
57
|
+
'sap-client': config.client,
|
|
58
|
+
'sap-language': config.language || 'EN',
|
|
59
|
+
'Content-Type': 'application/json'
|
|
60
|
+
},
|
|
61
|
+
agent: new https.Agent({ rejectUnauthorized: false })
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const req = https.request(options, (res) => {
|
|
65
|
+
let body = '';
|
|
66
|
+
res.on('data', chunk => body += chunk);
|
|
67
|
+
res.on('end', () => {
|
|
68
|
+
try {
|
|
69
|
+
const result = JSON.parse(body);
|
|
70
|
+
const apiVersion = result.version || '1.0.0';
|
|
71
|
+
|
|
72
|
+
if (cliVersion !== apiVersion) {
|
|
73
|
+
console.log(`\n⚠️ Version mismatch: CLI ${cliVersion}, ABAP API ${apiVersion}`);
|
|
74
|
+
console.log(' Some commands may not work correctly.');
|
|
75
|
+
console.log(' Update ABAP code: abapgit-agent pull\n');
|
|
76
|
+
}
|
|
77
|
+
resolve({ cliVersion, apiVersion, compatible: cliVersion === apiVersion });
|
|
78
|
+
} catch (e) {
|
|
79
|
+
resolve({ cliVersion, apiVersion: null, compatible: false, error: e.message });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
req.on('error', (e) => {
|
|
85
|
+
resolve({ cliVersion, apiVersion: null, compatible: false, error: e.message });
|
|
86
|
+
});
|
|
87
|
+
req.end();
|
|
88
|
+
});
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return { cliVersion, apiVersion: null, compatible: false, error: error.message };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
26
94
|
/**
|
|
27
95
|
* Load configuration from .abapGitAgent in current working directory
|
|
28
96
|
*/
|
|
@@ -42,10 +110,19 @@ function loadConfig() {
|
|
|
42
110
|
password: process.env.ABAP_PASSWORD,
|
|
43
111
|
language: process.env.ABAP_LANGUAGE || 'EN',
|
|
44
112
|
gitUsername: process.env.GIT_USERNAME,
|
|
45
|
-
gitPassword: process.env.GIT_PASSWORD
|
|
113
|
+
gitPassword: process.env.GIT_PASSWORD,
|
|
114
|
+
transport: process.env.ABAP_TRANSPORT
|
|
46
115
|
};
|
|
47
116
|
}
|
|
48
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Get transport request from config or environment
|
|
120
|
+
*/
|
|
121
|
+
function getTransport() {
|
|
122
|
+
const config = loadConfig();
|
|
123
|
+
return config.transport;
|
|
124
|
+
}
|
|
125
|
+
|
|
49
126
|
/**
|
|
50
127
|
* Check if ABAP integration is configured for this repo
|
|
51
128
|
*/
|
|
@@ -385,7 +462,7 @@ async function syntaxCheckSource(sourceFile, csrfToken, config) {
|
|
|
385
462
|
/**
|
|
386
463
|
* Inspect all files in one request
|
|
387
464
|
*/
|
|
388
|
-
async function inspectAllFiles(files, csrfToken, config) {
|
|
465
|
+
async function inspectAllFiles(files, csrfToken, config, variant = null) {
|
|
389
466
|
// Convert files to uppercase names (same as syntaxCheckSource does)
|
|
390
467
|
const fileNames = files.map(f => {
|
|
391
468
|
const baseName = pathModule.basename(f).toUpperCase();
|
|
@@ -398,6 +475,11 @@ async function inspectAllFiles(files, csrfToken, config) {
|
|
|
398
475
|
files: fileNames
|
|
399
476
|
};
|
|
400
477
|
|
|
478
|
+
// Add variant if specified
|
|
479
|
+
if (variant) {
|
|
480
|
+
data.variant = variant;
|
|
481
|
+
}
|
|
482
|
+
|
|
401
483
|
const result = await request('POST', '/sap/bc/z_abapgit_agent/inspect', data, { csrfToken: csrfToken });
|
|
402
484
|
|
|
403
485
|
// Handle both table result and old single result
|
|
@@ -1195,6 +1277,12 @@ To enable integration:
|
|
|
1195
1277
|
}
|
|
1196
1278
|
}
|
|
1197
1279
|
|
|
1280
|
+
// Version compatibility check for commands that interact with ABAP
|
|
1281
|
+
const abapCommands = ['create', 'import', 'pull', 'inspect', 'unit', 'tree', 'view', 'preview'];
|
|
1282
|
+
if (command && abapCommands.includes(command)) {
|
|
1283
|
+
await checkVersionCompatibility();
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1198
1286
|
try {
|
|
1199
1287
|
switch (command) {
|
|
1200
1288
|
case 'init':
|
|
@@ -1390,16 +1478,21 @@ Examples:
|
|
|
1390
1478
|
let gitUrl = urlArgIndex !== -1 ? args[urlArgIndex + 1] : null;
|
|
1391
1479
|
let branch = branchArgIndex !== -1 ? args[branchArgIndex + 1] : getGitBranch();
|
|
1392
1480
|
let files = null;
|
|
1481
|
+
|
|
1482
|
+
// Transport: CLI arg takes priority, then config/environment, then null
|
|
1393
1483
|
let transportRequest = null;
|
|
1484
|
+
if (transportArgIndex !== -1 && transportArgIndex + 1 < args.length) {
|
|
1485
|
+
// Explicit --transport argument
|
|
1486
|
+
transportRequest = args[transportArgIndex + 1];
|
|
1487
|
+
} else {
|
|
1488
|
+
// Fall back to config or environment variable
|
|
1489
|
+
transportRequest = getTransport();
|
|
1490
|
+
}
|
|
1394
1491
|
|
|
1395
1492
|
if (filesArgIndex !== -1 && filesArgIndex + 1 < args.length) {
|
|
1396
1493
|
files = args[filesArgIndex + 1].split(',').map(f => f.trim());
|
|
1397
1494
|
}
|
|
1398
1495
|
|
|
1399
|
-
if (transportArgIndex !== -1 && transportArgIndex + 1 < args.length) {
|
|
1400
|
-
transportRequest = args[transportArgIndex + 1];
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
1496
|
if (!gitUrl) {
|
|
1404
1497
|
gitUrl = getGitRemoteUrl();
|
|
1405
1498
|
if (!gitUrl) {
|
|
@@ -1427,31 +1520,32 @@ Examples:
|
|
|
1427
1520
|
break;
|
|
1428
1521
|
|
|
1429
1522
|
case 'inspect': {
|
|
1430
|
-
// TODO: Implement full inspect feature with:
|
|
1431
|
-
// - Syntax check (currently implemented via /inspect)
|
|
1432
|
-
// - Code Inspector checks (SE51, SCI)
|
|
1433
|
-
// - ATC checks (SATC)
|
|
1434
|
-
// - Custom rule checks
|
|
1435
|
-
// Add --check-type parameter to specify which check to run
|
|
1436
|
-
|
|
1437
1523
|
const filesArgIndex = args.indexOf('--files');
|
|
1438
1524
|
if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
|
|
1439
1525
|
console.error('Error: --files parameter required');
|
|
1440
|
-
console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,...');
|
|
1526
|
+
console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,... [--variant <check-variant>]');
|
|
1441
1527
|
console.error('Example: abapgit-agent inspect --files zcl_my_class.clas.abap');
|
|
1528
|
+
console.error('Example: abapgit-agent inspect --files zcl_my_class.clas.abap --variant ALL_CHECKS');
|
|
1442
1529
|
process.exit(1);
|
|
1443
1530
|
}
|
|
1444
1531
|
|
|
1445
1532
|
const filesSyntaxCheck = args[filesArgIndex + 1].split(',').map(f => f.trim());
|
|
1446
1533
|
|
|
1534
|
+
// Parse optional --variant parameter
|
|
1535
|
+
const variantArgIndex = args.indexOf('--variant');
|
|
1536
|
+
const variant = variantArgIndex !== -1 ? args[variantArgIndex + 1] : null;
|
|
1537
|
+
|
|
1447
1538
|
console.log(`\n Inspect for ${filesSyntaxCheck.length} file(s)`);
|
|
1539
|
+
if (variant) {
|
|
1540
|
+
console.log(` Using variant: ${variant}`);
|
|
1541
|
+
}
|
|
1448
1542
|
console.log('');
|
|
1449
1543
|
|
|
1450
1544
|
const config = loadConfig();
|
|
1451
1545
|
const csrfToken = await fetchCsrfToken(config);
|
|
1452
1546
|
|
|
1453
1547
|
// Send all files in one request
|
|
1454
|
-
const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config);
|
|
1548
|
+
const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config, variant);
|
|
1455
1549
|
|
|
1456
1550
|
// Process results
|
|
1457
1551
|
for (const result of results) {
|
|
@@ -1485,15 +1579,37 @@ Examples:
|
|
|
1485
1579
|
|
|
1486
1580
|
case 'tree': {
|
|
1487
1581
|
const packageArgIndex = args.indexOf('--package');
|
|
1488
|
-
if (packageArgIndex === -1
|
|
1582
|
+
if (packageArgIndex === -1) {
|
|
1489
1583
|
console.error('Error: --package parameter required');
|
|
1490
1584
|
console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
|
|
1491
|
-
console.error('Example: abapgit-agent tree --package
|
|
1585
|
+
console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
|
|
1586
|
+
process.exit(1);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// Check if package value is missing (happens when shell variable expands to empty)
|
|
1590
|
+
if (packageArgIndex + 1 >= args.length) {
|
|
1591
|
+
console.error('Error: --package parameter value is missing');
|
|
1592
|
+
console.error('');
|
|
1593
|
+
console.error('Tip: If you are using a shell variable, make sure to quote it:');
|
|
1594
|
+
console.error(' abapgit-agent tree --package "$ZMY_PACKAGE"');
|
|
1595
|
+
console.error(' or escape the $ character:');
|
|
1596
|
+
console.error(' abapgit-agent tree --package \\$ZMY_PACKAGE');
|
|
1597
|
+
console.error('');
|
|
1598
|
+
console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
|
|
1599
|
+
console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
|
|
1492
1600
|
process.exit(1);
|
|
1493
1601
|
}
|
|
1494
1602
|
|
|
1495
1603
|
const packageName = args[packageArgIndex + 1];
|
|
1496
1604
|
|
|
1605
|
+
// Check for empty/whitespace-only package name
|
|
1606
|
+
if (!packageName || packageName.trim() === '') {
|
|
1607
|
+
console.error('Error: --package parameter cannot be empty');
|
|
1608
|
+
console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
|
|
1609
|
+
console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
|
|
1610
|
+
process.exit(1);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1497
1613
|
// Optional depth parameter
|
|
1498
1614
|
const depthArgIndex = args.indexOf('--depth');
|
|
1499
1615
|
let depth = 3;
|
|
@@ -1726,6 +1842,243 @@ Examples:
|
|
|
1726
1842
|
break;
|
|
1727
1843
|
}
|
|
1728
1844
|
|
|
1845
|
+
case 'preview': {
|
|
1846
|
+
const objectsArgIndex = args.indexOf('--objects');
|
|
1847
|
+
if (objectsArgIndex === -1 || objectsArgIndex + 1 >= args.length) {
|
|
1848
|
+
console.error('Error: --objects parameter required');
|
|
1849
|
+
console.error('Usage: abapgit-agent preview --objects <table1>,<view1>,... [--type <type>] [--limit <n>] [--where <condition>] [--columns <cols>] [--vertical] [--compact] [--json]');
|
|
1850
|
+
console.error('Example: abapgit-agent preview --objects SFLIGHT');
|
|
1851
|
+
console.error('Example: abapgit-agent preview --objects ZC_MY_CDS_VIEW --type DDLS');
|
|
1852
|
+
console.error('Example: abapgit-agent preview --objects SFLIGHT --where "CARRID = \'AA\'"');
|
|
1853
|
+
process.exit(1);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
const objects = args[objectsArgIndex + 1].split(',').map(o => o.trim().toUpperCase());
|
|
1857
|
+
const typeArg = args.indexOf('--type');
|
|
1858
|
+
const type = typeArg !== -1 ? args[typeArg + 1].toUpperCase() : null;
|
|
1859
|
+
const limitArg = args.indexOf('--limit');
|
|
1860
|
+
const limit = limitArg !== -1 ? parseInt(args[limitArg + 1], 10) : 10;
|
|
1861
|
+
const whereArg = args.indexOf('--where');
|
|
1862
|
+
const where = whereArg !== -1 ? args[whereArg + 1] : null;
|
|
1863
|
+
const columnsArg = args.indexOf('--columns');
|
|
1864
|
+
const columns = columnsArg !== -1 ? args[columnsArg + 1].split(',').map(c => c.trim().toUpperCase()) : null;
|
|
1865
|
+
const verticalOutput = args.includes('--vertical');
|
|
1866
|
+
const compactOutput = args.includes('--compact');
|
|
1867
|
+
const jsonOutput = args.includes('--json');
|
|
1868
|
+
|
|
1869
|
+
console.log(`\n Previewing ${objects.length} object(s)`);
|
|
1870
|
+
|
|
1871
|
+
const config = loadConfig();
|
|
1872
|
+
const csrfToken = await fetchCsrfToken(config);
|
|
1873
|
+
|
|
1874
|
+
const data = {
|
|
1875
|
+
objects: objects,
|
|
1876
|
+
limit: Math.min(Math.max(1, limit), 100)
|
|
1877
|
+
};
|
|
1878
|
+
|
|
1879
|
+
if (type) {
|
|
1880
|
+
data.type = type;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
if (where) {
|
|
1884
|
+
data.where = where;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
if (columns) {
|
|
1888
|
+
data.columns = columns;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
const result = await request('POST', '/sap/bc/z_abapgit_agent/preview', data, { csrfToken });
|
|
1892
|
+
|
|
1893
|
+
// Handle uppercase keys from ABAP
|
|
1894
|
+
const success = result.SUCCESS || result.success;
|
|
1895
|
+
const previewObjects = result.OBJECTS || result.objects || [];
|
|
1896
|
+
const message = result.MESSAGE || result.message || '';
|
|
1897
|
+
const error = result.ERROR || result.error;
|
|
1898
|
+
|
|
1899
|
+
if (!success || error) {
|
|
1900
|
+
console.error(`\n Error: ${error || 'Failed to preview objects'}`);
|
|
1901
|
+
break;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
if (jsonOutput) {
|
|
1905
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1906
|
+
} else {
|
|
1907
|
+
console.log(`\n ${message}`);
|
|
1908
|
+
console.log('');
|
|
1909
|
+
|
|
1910
|
+
// Track if columns were explicitly specified
|
|
1911
|
+
const columnsExplicit = columns !== null;
|
|
1912
|
+
|
|
1913
|
+
for (let i = 0; i < previewObjects.length; i++) {
|
|
1914
|
+
const obj = previewObjects[i];
|
|
1915
|
+
const objName = obj.NAME || obj.name || `Object ${i + 1}`;
|
|
1916
|
+
const objType = obj.TYPE || obj.type || '';
|
|
1917
|
+
const objTypeText = obj.TYPE_TEXT || obj.type_text || '';
|
|
1918
|
+
// Parse rows - could be a JSON string or array
|
|
1919
|
+
let rows = obj.ROWS || obj.rows || [];
|
|
1920
|
+
if (typeof rows === 'string') {
|
|
1921
|
+
try {
|
|
1922
|
+
rows = JSON.parse(rows);
|
|
1923
|
+
} catch (e) {
|
|
1924
|
+
rows = [];
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
const fields = obj.FIELDS || obj.fields || [];
|
|
1928
|
+
const rowCount = obj.ROW_COUNT || obj.row_count || 0;
|
|
1929
|
+
const totalRows = obj.TOTAL_ROWS || obj.total_rows || 0;
|
|
1930
|
+
const notFound = obj.NOT_FOUND || obj.not_found || false;
|
|
1931
|
+
const accessDenied = obj.ACCESS_DENIED || obj.access_denied || false;
|
|
1932
|
+
|
|
1933
|
+
// Check if object was not found
|
|
1934
|
+
if (notFound) {
|
|
1935
|
+
console.log(` ❌ ${objName} (${objTypeText})`);
|
|
1936
|
+
console.log(` Object not found: ${objName}`);
|
|
1937
|
+
continue;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
// Check if access denied
|
|
1941
|
+
if (accessDenied) {
|
|
1942
|
+
console.log(` ❌ ${objName} (${objTypeText})`);
|
|
1943
|
+
console.log(` Access denied to: ${objName}`);
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
console.log(` 📊 Preview: ${objName} (${objTypeText})`);
|
|
1948
|
+
|
|
1949
|
+
// Check for errors first
|
|
1950
|
+
const objError = obj.ERROR || obj.error;
|
|
1951
|
+
if (objError) {
|
|
1952
|
+
console.log(` ❌ Error: ${objError}`);
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
if (rows.length === 0) {
|
|
1957
|
+
console.log(' No data found');
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// Get all unique field names from all rows
|
|
1962
|
+
const allFields = new Set();
|
|
1963
|
+
rows.forEach(row => {
|
|
1964
|
+
Object.keys(row).forEach(key => allFields.add(key));
|
|
1965
|
+
});
|
|
1966
|
+
const allFieldNames = Array.from(allFields);
|
|
1967
|
+
|
|
1968
|
+
// Display as table - use fields metadata only if --columns was explicitly specified
|
|
1969
|
+
let fieldNames;
|
|
1970
|
+
let columnsAutoSelected = false;
|
|
1971
|
+
if (columnsExplicit && fields && fields.length > 0) {
|
|
1972
|
+
// Use fields from metadata (filtered by explicit --columns)
|
|
1973
|
+
fieldNames = fields.map(f => f.FIELD || f.field);
|
|
1974
|
+
} else {
|
|
1975
|
+
// Use all fields - let terminal handle wrapping if needed
|
|
1976
|
+
// Terminal width detection is unreliable without a proper TTY
|
|
1977
|
+
fieldNames = allFieldNames;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// Calculate column widths - use reasonable defaults
|
|
1981
|
+
const colWidths = {};
|
|
1982
|
+
const maxColWidth = compactOutput ? 10 : 20;
|
|
1983
|
+
fieldNames.forEach(field => {
|
|
1984
|
+
let maxWidth = field.length;
|
|
1985
|
+
rows.forEach(row => {
|
|
1986
|
+
const value = String(row[field] || '');
|
|
1987
|
+
maxWidth = Math.max(maxWidth, value.length);
|
|
1988
|
+
});
|
|
1989
|
+
// Cap at maxColWidth (truncates both headers and data in compact mode)
|
|
1990
|
+
colWidths[field] = Math.min(maxWidth, maxColWidth);
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
// Render output - either vertical or table
|
|
1994
|
+
if (verticalOutput) {
|
|
1995
|
+
// Vertical format: each field on its own line
|
|
1996
|
+
rows.forEach((row, rowIndex) => {
|
|
1997
|
+
if (rows.length > 1) {
|
|
1998
|
+
console.log(`\n Row ${rowIndex + 1}:`);
|
|
1999
|
+
console.log(' ' + '─'.repeat(30));
|
|
2000
|
+
}
|
|
2001
|
+
fieldNames.forEach(field => {
|
|
2002
|
+
const value = String(row[field] || '');
|
|
2003
|
+
console.log(` ${field}: ${value}`);
|
|
2004
|
+
});
|
|
2005
|
+
});
|
|
2006
|
+
console.log('');
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// Build table header
|
|
2011
|
+
let headerLine = ' ┌';
|
|
2012
|
+
let separatorLine = ' ├';
|
|
2013
|
+
fieldNames.forEach(field => {
|
|
2014
|
+
const width = colWidths[field];
|
|
2015
|
+
headerLine += '─'.repeat(width + 2) + '┬';
|
|
2016
|
+
separatorLine += '─'.repeat(width + 2) + '┼';
|
|
2017
|
+
});
|
|
2018
|
+
headerLine = headerLine.slice(0, -1) + '┐';
|
|
2019
|
+
separatorLine = separatorLine.slice(0, -1) + '┤';
|
|
2020
|
+
|
|
2021
|
+
// Build header row
|
|
2022
|
+
let headerRow = ' │';
|
|
2023
|
+
fieldNames.forEach(field => {
|
|
2024
|
+
const width = colWidths[field];
|
|
2025
|
+
let displayField = String(field);
|
|
2026
|
+
if (displayField.length > width) {
|
|
2027
|
+
displayField = displayField.slice(0, width - 3) + '...';
|
|
2028
|
+
}
|
|
2029
|
+
headerRow += ' ' + displayField.padEnd(width) + ' │';
|
|
2030
|
+
});
|
|
2031
|
+
|
|
2032
|
+
console.log(headerLine);
|
|
2033
|
+
console.log(headerRow);
|
|
2034
|
+
console.log(separatorLine);
|
|
2035
|
+
|
|
2036
|
+
// Build data rows
|
|
2037
|
+
rows.forEach(row => {
|
|
2038
|
+
let dataRow = ' │';
|
|
2039
|
+
fieldNames.forEach(field => {
|
|
2040
|
+
const width = colWidths[field];
|
|
2041
|
+
const value = String(row[field] || '');
|
|
2042
|
+
const displayValue = value.length > width ? value.slice(0, width - 3) + '...' : value;
|
|
2043
|
+
dataRow += ' ' + displayValue.padEnd(width) + ' │';
|
|
2044
|
+
});
|
|
2045
|
+
console.log(dataRow);
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
// Build bottom border
|
|
2049
|
+
let bottomLine = ' └';
|
|
2050
|
+
fieldNames.forEach(field => {
|
|
2051
|
+
const width = colWidths[field];
|
|
2052
|
+
bottomLine += '─'.repeat(width + 2) + '┴';
|
|
2053
|
+
});
|
|
2054
|
+
bottomLine = bottomLine.slice(0, -1) + '┘';
|
|
2055
|
+
console.log(bottomLine);
|
|
2056
|
+
|
|
2057
|
+
// Show row count
|
|
2058
|
+
if (totalRows > rowCount) {
|
|
2059
|
+
console.log(`\n Showing ${rowCount} of ${totalRows} rows`);
|
|
2060
|
+
} else {
|
|
2061
|
+
console.log(`\n ${rowCount} row(s)`);
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
// Note about hidden columns only when --columns was explicitly specified
|
|
2065
|
+
const columnsDisplayed = fieldNames.length;
|
|
2066
|
+
let columnsHidden = [];
|
|
2067
|
+
|
|
2068
|
+
if (columnsExplicit) {
|
|
2069
|
+
columnsHidden = obj.COLUMNS_HIDDEN || obj.columns_hidden || [];
|
|
2070
|
+
if (columnsHidden.length > 0) {
|
|
2071
|
+
console.log(`\n ⚠️ ${columnsHidden.length} more columns hidden (${columnsHidden.join(', ')})`);
|
|
2072
|
+
console.log(' Use --json for full data');
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
console.log('');
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
break;
|
|
2080
|
+
}
|
|
2081
|
+
|
|
1729
2082
|
case 'help':
|
|
1730
2083
|
case '--help':
|
|
1731
2084
|
case '-h':
|
package/docs/commands.md
CHANGED
|
@@ -14,6 +14,7 @@ All available CLI commands for abapGit Agent.
|
|
|
14
14
|
| [tree](tree-command.md) | ✅ | Display package hierarchy tree |
|
|
15
15
|
| [unit](unit-command.md) | ✅ | Run AUnit tests |
|
|
16
16
|
| [view](view-command.md) | ✅ | View ABAP object source code from system |
|
|
17
|
+
| [preview](preview-command.md) | 🔄 | Preview table/CDS view data |
|
|
17
18
|
| [health](health-command.md) | ✅ | Health check |
|
|
18
19
|
| [status](status-command.md) | ✅ | Status check |
|
|
19
20
|
|
|
@@ -70,6 +71,10 @@ abapgit-agent tree --package $MY_PACKAGE
|
|
|
70
71
|
abapgit-agent view --objects ZCL_MY_CLASS
|
|
71
72
|
abapgit-agent view --objects SFLIGHT --type TABL
|
|
72
73
|
|
|
74
|
+
# Preview table/CDS view data
|
|
75
|
+
abapgit-agent preview --objects SFLIGHT
|
|
76
|
+
abapgit-agent preview --objects ZC_MY_CDS_VIEW --type DDLS
|
|
77
|
+
|
|
73
78
|
# Check configuration
|
|
74
79
|
abapgit-agent status
|
|
75
80
|
|
package/docs/inspect-command.md
CHANGED
|
@@ -15,6 +15,12 @@ abapgit-agent inspect --files zcl_my_class.clas.abap,zcl_other.clas.abap
|
|
|
15
15
|
|
|
16
16
|
# With path
|
|
17
17
|
abapgit-agent inspect --files abap/zcl_my_class.clas.abap
|
|
18
|
+
|
|
19
|
+
# With Code Inspector variant
|
|
20
|
+
abapgit-agent inspect --files abap/zcl_my_class.clas.abap --variant ALL_CHECKS
|
|
21
|
+
|
|
22
|
+
# With no variant (uses default SAP standard checks)
|
|
23
|
+
abapgit-agent inspect --files abap/zcl_my_class.clas.abap --variant EMPTY
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
## Prerequisite
|
|
@@ -27,6 +33,7 @@ abapgit-agent inspect --files abap/zcl_my_class.clas.abap
|
|
|
27
33
|
| Parameter | Required | Description |
|
|
28
34
|
|-----------|----------|-------------|
|
|
29
35
|
| `--files` | Yes | Comma-separated list of files to inspect |
|
|
36
|
+
| `--variant` | No | Code Inspector variant name (e.g., `ALL_CHECKS`, `EMPTY`) |
|
|
30
37
|
|
|
31
38
|
---
|
|
32
39
|
|
|
@@ -54,7 +61,8 @@ GET /health (with X-CSRF-Token: fetch)
|
|
|
54
61
|
**Request Body:**
|
|
55
62
|
```json
|
|
56
63
|
{
|
|
57
|
-
"files": ["ZCL_MY_CLASS.CLASS.ABAP"]
|
|
64
|
+
"files": ["ZCL_MY_CLASS.CLASS.ABAP"],
|
|
65
|
+
"variant": "ALL_CHECKS"
|
|
58
66
|
}
|
|
59
67
|
```
|
|
60
68
|
|
|
@@ -140,6 +148,9 @@ abapgit-agent inspect --files zcl_my_class.clas.abap
|
|
|
140
148
|
|
|
141
149
|
# Multiple files
|
|
142
150
|
abapgit-agent inspect --files abap/zcl_my_class.clas.abap,abap/zcl_other.clas.abap
|
|
151
|
+
|
|
152
|
+
# With Code Inspector variant
|
|
153
|
+
abapgit-agent inspect --files abap/zcl_my_class.clas.abap --variant ALL_CHECKS
|
|
143
154
|
```
|
|
144
155
|
|
|
145
156
|
## Use Case
|