diff --git a/package-lock.json b/package-lock.json index b16bf87787..09ed60984f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5418,12 +5418,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, - "node_modules/arity-n": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", - "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==", - "license": "MIT" - }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -5474,27 +5468,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", - "license": "MIT", - "dependencies": { - "is-number": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-last/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array-timsort": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", @@ -5774,15 +5747,6 @@ } } }, - "node_modules/babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "license": "MIT", - "bin": { - "babylon": "bin/babylon.js" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -5899,47 +5863,6 @@ ], "license": "MIT" }, - "node_modules/bash-parser": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/bash-parser/-/bash-parser-0.5.0.tgz", - "integrity": "sha512-AQR43o4W4sj4Jf+oy4cFtGgyBps4B+MYnJg6Xds8VVC7yomFtQekhOORQNHfQ8D6YJ0XENykr3TpxMn3rUtgeg==", - "license": "MIT", - "dependencies": { - "array-last": "^1.1.1", - "babylon": "^6.9.1", - "compose-function": "^3.0.3", - "curry": "^1.2.0", - "deep-freeze": "0.0.1", - "filter-iterator": "0.0.1", - "filter-obj": "^1.1.0", - "has-own-property": "^0.1.0", - "identity-function": "^1.0.0", - "iterable-lookahead": "^1.0.0", - "iterable-transform-replace": "^1.1.1", - "magic-string": "^0.16.0", - "map-iterable": "^1.0.1", - "map-obj": "^2.0.0", - "object-pairs": "^0.1.0", - "object-values": "^1.0.0", - "reverse-arguments": "^1.0.0", - "shell-quote-word": "^1.0.1", - "to-pascal-case": "^1.0.0", - "transform-spread-iterable": "^1.1.0", - "unescape-js": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/bash-parser/node_modules/magic-string": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.16.0.tgz", - "integrity": "sha512-c4BEos3y6G2qO0B9X7K0FVLOPT9uGrjYwYRLFmDqyl5YMboUviyecnXWp94fJTSMwPw2/sf+CEYt5AGpmklkkQ==", - "license": "MIT", - "dependencies": { - "vlq": "^0.2.1" - } - }, "node_modules/basic-ftp": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", @@ -6743,15 +6666,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/compose-function": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", - "integrity": "sha512-xzhzTJ5eC+gmIzvZq+C3kCJHsp9os6tJkrigDRZclyGtOKINbZtE8n1Tzmeh32jW+BUDPbvZpibwvJHBLGMVwg==", - "license": "MIT", - "dependencies": { - "arity-n": "^1.0.4" - } - }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -6997,11 +6911,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/curry": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/curry/-/curry-1.2.0.tgz", - "integrity": "sha512-PAdmqPH2DUYTCc/aknv6RxRxmqdRHclvbz+wP8t1Xpg2Nu13qg+oLb6/5iFoDmf4dbmC9loYoy9PwwGbFt/AqA==" - }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -7127,12 +7036,6 @@ "node": ">=4.0.0" } }, - "node_modules/deep-freeze": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", - "integrity": "sha512-Z+z8HiAvsGwmjqlphnHW5oz6yWlOwu6EQfFTjmeTWlDeda3FS2yv3jhq35TX/ewmsnqB+RX2IdsIOyjJCQN5tg==", - "license": "public domain" - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8930,20 +8833,6 @@ "node": ">=8" } }, - "node_modules/filter-iterator": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/filter-iterator/-/filter-iterator-0.0.1.tgz", - "integrity": "sha512-v4lhL7Qa8XpbW3LN46CEnmhGk3eHZwxfNl5at20aEkreesht4YKb/Ba3BUIbnPhAC/r3dmu7ABaGk6MAvh2alA==" - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -9839,12 +9728,6 @@ "node": ">=8" } }, - "node_modules/has-own-property": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-own-property/-/has-own-property-0.1.0.tgz", - "integrity": "sha512-14qdBKoonU99XDhWcFKZTShK+QV47qU97u8zzoVo9cL5TZ3BmBHXogItSt9qJjR0KUMFRhcCW8uGIGl8nkl7Aw==", - "license": "MIT" - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -10134,12 +10017,6 @@ "node": ">=0.10.0" } }, - "node_modules/identity-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/identity-function/-/identity-function-1.0.0.tgz", - "integrity": "sha512-kNrgUK0qI+9qLTBidsH85HjDLpZfrrS0ElquKKe/fJFdB3D7VeKdXXEvOPDUHSHOzdZKCAAaQIWWyp0l2yq6pw==", - "license": "public domain" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -10659,15 +10536,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-iterable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-iterable/-/is-iterable-1.1.1.tgz", - "integrity": "sha512-EdOZCr0NsGE00Pot+x1ZFx9MJK3C6wy91geZpXwvwexDLJvA4nzYyZf7r+EIwSeVsOLDdBz7ATg9NqKTzuNYuQ==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -11073,24 +10941,6 @@ "url": "https://bevry.me/fund" } }, - "node_modules/iterable-lookahead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/iterable-lookahead/-/iterable-lookahead-1.0.0.tgz", - "integrity": "sha512-hJnEP2Xk4+44DDwJqUQGdXal5VbyeWLaPyDl2AQc242Zr7iqz4DgpQOrEzglWVMGHMDCkguLHEKxd1+rOsmgSQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/iterable-transform-replace": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/iterable-transform-replace/-/iterable-transform-replace-1.2.0.tgz", - "integrity": "sha512-AVCCj7CTUifWQ0ubraDgx5/e6tOWaL5qh/C8BDTjH0GuhNyFMCSsSmDtYpa4Y3ReAAQNSjUWfQ+ojhmjX10pdQ==", - "license": "MIT", - "dependencies": { - "curry": "^1.2.0" - } - }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", @@ -11953,28 +11803,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/map-iterable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-iterable/-/map-iterable-1.0.1.tgz", - "integrity": "sha512-siKFftph+ka2jWt8faiOWFzKP+eEuXrHuhYBitssJ5zJm209FCw5JBnaNLDiaCCb/CYZmxprdM6P7p16nA6YRA==", - "license": "MIT", - "dependencies": { - "curry": "^1.2.0", - "is-iterable": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/markdown-it": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", @@ -12891,21 +12719,6 @@ "node": ">= 0.4" } }, - "node_modules/object-pairs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-pairs/-/object-pairs-0.1.0.tgz", - "integrity": "sha512-3ECr6K831I4xX/Mduxr9UC+HPOz/d6WKKYj9p4cmC8Lg8p7g8gitzsxNX5IWlSIgFWN/a4JgrJaoAMKn20oKwA==", - "license": "MIT" - }, - "node_modules/object-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/object-values/-/object-values-1.0.0.tgz", - "integrity": "sha512-+8hwcz/JnQ9EpLIXzN0Rs7DLsBpJNT/xYehtB/jU93tHYr5BFEO8E+JGQNOSqE7opVzz5cGksKFHt7uUJVLSjQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object.assign": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", @@ -14511,12 +14324,6 @@ "node": ">=0.10.0" } }, - "node_modules/reverse-arguments": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/reverse-arguments/-/reverse-arguments-1.0.0.tgz", - "integrity": "sha512-/x8uIPdTafBqakK0TmPNJzgkLP+3H+yxpUJhCQHsLBg1rYEVNR2D8BRYNWQhVBjyOd7oo1dZRVzIkwMY2oqfYQ==", - "license": "MIT" - }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -15021,12 +14828,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shell-quote-word": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/shell-quote-word/-/shell-quote-word-1.0.1.tgz", - "integrity": "sha512-lT297f1WLAdq0A4O+AknIFRP6kkiI3s8C913eJ0XqBxJbZPGWUNkRQk2u8zk4bEAjUJ5i+fSLwB6z1HzeT+DEg==", - "license": "MIT" - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -15481,11 +15282,6 @@ "node": ">=8" } }, - "node_modules/string.fromcodepoint": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz", - "integrity": "sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg==" - }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -16286,21 +16082,6 @@ "node": ">=14.14" } }, - "node_modules/to-no-case": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==", - "license": "MIT" - }, - "node_modules/to-pascal-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-pascal-case/-/to-pascal-case-1.0.0.tgz", - "integrity": "sha512-QGMWHqM6xPrcQW57S23c5/3BbYb0Tbe9p+ur98ckRnGDwD4wbbtDiYI38CfmMKNB5Iv0REjs5SNDntTwvDxzZA==", - "license": "MIT", - "dependencies": { - "to-space-case": "^1.0.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -16314,15 +16095,6 @@ "node": ">=8.0" } }, - "node_modules/to-space-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", - "license": "MIT", - "dependencies": { - "to-no-case": "^1.0.0" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -16332,15 +16104,6 @@ "node": ">=0.6" } }, - "node_modules/transform-spread-iterable": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/transform-spread-iterable/-/transform-spread-iterable-1.4.1.tgz", - "integrity": "sha512-/GnF26X3zC8wfWyRzvuXX/Vb31TrU3Rwipmr4MC5hTi6X/yOXxXUSw4+pcHmKJ2+0KRrcS21YWZw77ukhVJBdQ==", - "license": "MIT", - "dependencies": { - "curry": "^1.2.0" - } - }, "node_modules/tree-dump": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz", @@ -16742,15 +16505,6 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, - "node_modules/unescape-js": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unescape-js/-/unescape-js-1.1.4.tgz", - "integrity": "sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g==", - "license": "MIT", - "dependencies": { - "string.fromcodepoint": "^0.2.1" - } - }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", @@ -17101,12 +16855,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "license": "MIT" - }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -18013,7 +17761,6 @@ "@xterm/headless": "5.5.0", "ajv": "^8.17.1", "ajv-formats": "^3.0.0", - "bash-parser": "^0.5.0", "chardet": "^2.1.0", "diff": "^8.0.3", "dotenv": "^17.2.4", diff --git a/packages/core/package.json b/packages/core/package.json index e3fb4b153b..122ca375c3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -54,7 +54,6 @@ "@xterm/headless": "5.5.0", "ajv": "^8.17.1", "ajv-formats": "^3.0.0", - "bash-parser": "^0.5.0", "chardet": "^2.1.0", "diff": "^8.0.3", "dotenv": "^17.2.4", diff --git a/packages/core/src/utils/shell-ast-parser.test.ts b/packages/core/src/utils/shell-ast-parser.test.ts index 7858f6b462..6960feefba 100644 --- a/packages/core/src/utils/shell-ast-parser.test.ts +++ b/packages/core/src/utils/shell-ast-parser.test.ts @@ -4,23 +4,28 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { extractCommandsFromAst } from './shell-ast-parser.js'; +import { initializeShellParsers } from './shell-utils.js'; describe('shell-ast-parser', () => { + beforeAll(async () => { + await initializeShellParsers(); + }); + it('extracts a simple command', () => { const cmds = extractCommandsFromAst('echo "hello"'); - expect(cmds).toEqual(['echo hello']); + expect(cmds).toEqual(['echo "hello"']); }); it('extracts commands from a pipeline', () => { const cmds = extractCommandsFromAst('echo "hello" | grep h'); - expect(cmds).toEqual(['echo hello', 'grep h']); + expect(cmds).toEqual(['echo "hello"', 'grep h']); }); it('extracts commands from lists', () => { const cmds = extractCommandsFromAst('mkdir foo && cd foo || echo "failed" ; ls'); - expect(cmds).toEqual(['mkdir foo', 'cd foo', 'echo failed', 'ls']); + expect(cmds).toEqual(['mkdir foo', 'cd foo', 'echo "failed"', 'ls']); }); it('extracts commands from subshells', () => { diff --git a/packages/core/src/utils/shell-ast-parser.ts b/packages/core/src/utils/shell-ast-parser.ts index 56b26c2671..0203dfcf14 100644 --- a/packages/core/src/utils/shell-ast-parser.ts +++ b/packages/core/src/utils/shell-ast-parser.ts @@ -4,15 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import parse from 'bash-parser'; - -interface BashNode { - type?: string; - name?: { text?: string }; - suffix?: Array<{ text?: string }>; -} +import { parseCommandDetails } from './shell-utils.js'; /** * Parses a raw shell string and extracts all individual executable commands. @@ -26,59 +18,11 @@ export function extractCommandsFromAst(shellString: string): string[] { return []; } - const commands: string[] = []; - - try { - // We use bash-parser to construct an AST synchronously. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call - const ast = parse(shellString, { insertResolutionScope: true }); - - // A simple recursive traversal to find all 'Command' nodes - const traverse = (node: unknown) => { - if (!node || typeof node !== 'object') return; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const bashNode = node as BashNode; - - if (bashNode.type === 'Command') { - const parts: string[] = []; - if (bashNode.name && bashNode.name.text) { - parts.push(bashNode.name.text); - } - - if (Array.isArray(bashNode.suffix)) { - bashNode.suffix.forEach((s: unknown) => { - if (s && typeof s === 'object' && 'text' in s && typeof (s as Record)['text'] === 'string') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - parts.push((s as {text: string}).text); - } - }); - } - - if (parts.length > 0) { - commands.push(parts.join(' ')); - } - } - - // Recursively traverse all object properties to find nested commands - // (like those in pipelines, lists, or subshells) - for (const key of Object.keys(node)) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const child = (node as Record)[key]; - if (typeof child === 'object' && child !== null) { - if (Array.isArray(child)) { - child.forEach(traverse); - } else { - traverse(child); - } - } - } - }; - - traverse(ast); - } catch (_error) { - // Graceful failure on syntax errors; return whatever we successfully parsed so far, or empty. + const parsed = parseCommandDetails(shellString); + + if (!parsed || parsed.hasError) { + return []; } - return commands; + return parsed.details.map((detail) => detail.text); } diff --git a/packages/core/src/utils/tool-utils.test.ts b/packages/core/src/utils/tool-utils.test.ts index cddbec66b0..ce4ada0a21 100644 --- a/packages/core/src/utils/tool-utils.test.ts +++ b/packages/core/src/utils/tool-utils.test.ts @@ -4,12 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { expect, describe, it } from 'vitest'; +import { expect, describe, it, beforeAll } from 'vitest'; import { doesToolInvocationMatch, getToolSuggestion, shouldHideToolCall, } from './tool-utils.js'; +import { initializeShellParsers } from './shell-utils.js'; import { ReadFileTool, ApprovalMode, @@ -137,6 +138,10 @@ describe('getToolSuggestion', () => { }); describe('doesToolInvocationMatch', () => { + beforeAll(async () => { + await initializeShellParsers(); + }); + it('should not match a partial command prefix', () => { const invocation = { params: { command: 'git commitsomething' }, diff --git a/packages/core/src/utils/tool-utils.ts b/packages/core/src/utils/tool-utils.ts index f6985de909..0b24017a0c 100644 --- a/packages/core/src/utils/tool-utils.ts +++ b/packages/core/src/utils/tool-utils.ts @@ -158,7 +158,17 @@ export function doesToolInvocationMatch( if (isShellTool) { toolNames = [...new Set([...toolNames, ...SHELL_TOOL_NAMES])]; + } + // Globally allowed tools check (non-shell and shell) + for (const pattern of patterns) { + const openParen = pattern.indexOf('('); + if (openParen === -1 && toolNames.includes(pattern)) { + return true; + } + } + + if (isShellTool) { let command: string | undefined; if (typeof invocation === 'string') { command = invocation; @@ -215,13 +225,5 @@ export function doesToolInvocationMatch( return true; // All sub-commands matched at least one pattern } - // Non-shell tool validation - for (const pattern of patterns) { - const openParen = pattern.indexOf('('); - if (openParen === -1 && toolNames.includes(pattern)) { - return true; - } - } - return false; }