Remove ESBuild experiment. Add --install option for bash completions. Move shell scripts to separate files for auditability. Fix template inspect command autocomplete and output formatting
This commit is contained in:
487
package-lock.json
generated
487
package-lock.json
generated
@@ -26,7 +26,7 @@
|
|||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"xo-cli": "dist/cli/index.js",
|
"xo-cli": "dist/cli/index.js",
|
||||||
"xo-complete": "dist/cli/complete.js",
|
"xo-complete": "dist/cli/autocomplete/complete.js",
|
||||||
"xo-tui": "dist/index.js"
|
"xo-tui": "dist/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -35,7 +35,6 @@
|
|||||||
"@types/qrcode": "^1.5.6",
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@vitest/coverage-v8": "^4.1.2",
|
"@vitest/coverage-v8": "^4.1.2",
|
||||||
"esbuild": "^0.28.0",
|
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vitest": "^4.1.2"
|
"vitest": "^4.1.2"
|
||||||
@@ -331,448 +330,6 @@
|
|||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"aix"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
|
||||||
"cpu": [
|
|
||||||
"mips64el"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openharmony-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openharmony"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"sunos"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
@@ -1963,48 +1520,6 @@
|
|||||||
"benchmarks"
|
"benchmarks"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"esbuild": "bin/esbuild"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@esbuild/aix-ppc64": "0.28.0",
|
|
||||||
"@esbuild/android-arm": "0.28.0",
|
|
||||||
"@esbuild/android-arm64": "0.28.0",
|
|
||||||
"@esbuild/android-x64": "0.28.0",
|
|
||||||
"@esbuild/darwin-arm64": "0.28.0",
|
|
||||||
"@esbuild/darwin-x64": "0.28.0",
|
|
||||||
"@esbuild/freebsd-arm64": "0.28.0",
|
|
||||||
"@esbuild/freebsd-x64": "0.28.0",
|
|
||||||
"@esbuild/linux-arm": "0.28.0",
|
|
||||||
"@esbuild/linux-arm64": "0.28.0",
|
|
||||||
"@esbuild/linux-ia32": "0.28.0",
|
|
||||||
"@esbuild/linux-loong64": "0.28.0",
|
|
||||||
"@esbuild/linux-mips64el": "0.28.0",
|
|
||||||
"@esbuild/linux-ppc64": "0.28.0",
|
|
||||||
"@esbuild/linux-riscv64": "0.28.0",
|
|
||||||
"@esbuild/linux-s390x": "0.28.0",
|
|
||||||
"@esbuild/linux-x64": "0.28.0",
|
|
||||||
"@esbuild/netbsd-arm64": "0.28.0",
|
|
||||||
"@esbuild/netbsd-x64": "0.28.0",
|
|
||||||
"@esbuild/openbsd-arm64": "0.28.0",
|
|
||||||
"@esbuild/openbsd-x64": "0.28.0",
|
|
||||||
"@esbuild/openharmony-arm64": "0.28.0",
|
|
||||||
"@esbuild/sunos-x64": "0.28.0",
|
|
||||||
"@esbuild/win32-arm64": "0.28.0",
|
|
||||||
"@esbuild/win32-ia32": "0.28.0",
|
|
||||||
"@esbuild/win32-x64": "0.28.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
"bin": {
|
"bin": {
|
||||||
"xo-cli": "./dist/cli/index.js",
|
"xo-cli": "./dist/cli/index.js",
|
||||||
"xo-tui": "./dist/index.js",
|
"xo-tui": "./dist/index.js",
|
||||||
"xo-complete": "./dist/cli/autocomplete/complete.bundle.js"
|
"xo-complete": "./dist/cli/autocomplete/complete.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "SYNC_SERVER_URL=https://sync.xo.harvmaster.com tsx src/index.ts",
|
"dev": "SYNC_SERVER_URL=https://sync.xo.harvmaster.com tsx src/index.ts",
|
||||||
"build": "tsc && npm run build:autocomplete",
|
"build": "tsc && npm run build:copy-scripts",
|
||||||
"build:autocomplete": "node scripts/build-autocomplete.mjs",
|
"build:copy-scripts": "cp -r src/cli/autocomplete/scripts dist/cli/autocomplete/",
|
||||||
"start": "SYNC_SERVER_URL=https://sync.xo.harvmaster.com node dist/index.js",
|
"start": "SYNC_SERVER_URL=https://sync.xo.harvmaster.com node dist/index.js",
|
||||||
"test": "vitest --run --passWithNoTests",
|
"test": "vitest --run --passWithNoTests",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
@@ -52,7 +52,6 @@
|
|||||||
"@types/qrcode": "^1.5.6",
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@vitest/coverage-v8": "^4.1.2",
|
"@vitest/coverage-v8": "^4.1.2",
|
||||||
"esbuild": "^0.28.0",
|
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vitest": "^4.1.2"
|
"vitest": "^4.1.2"
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
/**
|
|
||||||
* Build script for xo-complete autocomplete helper.
|
|
||||||
*
|
|
||||||
* Bundles the autocomplete script with all dependencies into a single file
|
|
||||||
* for faster startup time. This avoids the ~600ms module import overhead
|
|
||||||
* that occurs with dynamic ESM imports.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as esbuild from "esbuild";
|
|
||||||
import { join, dirname } from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
||||||
const projectRoot = join(__dirname, "..");
|
|
||||||
|
|
||||||
async function build() {
|
|
||||||
try {
|
|
||||||
const result = await esbuild.build({
|
|
||||||
entryPoints: [join(projectRoot, "src/cli/autocomplete/complete.ts")],
|
|
||||||
bundle: true,
|
|
||||||
platform: "node",
|
|
||||||
target: "node20",
|
|
||||||
format: "esm",
|
|
||||||
outfile: join(projectRoot, "dist/cli/autocomplete/complete.bundle.js"),
|
|
||||||
|
|
||||||
// Mark modules as external that either have native components or bundling issues
|
|
||||||
// We keep heavy deps external, and also keep offline-engine external so it stays
|
|
||||||
// as a dynamic import (it pulls in the heavy engine dependencies)
|
|
||||||
external: [
|
|
||||||
"better-sqlite3",
|
|
||||||
"fsevents",
|
|
||||||
"@bitauth/libauth",
|
|
||||||
"@xo-cash/*",
|
|
||||||
"@electrum-cash/*",
|
|
||||||
"./offline-engine.js",
|
|
||||||
],
|
|
||||||
|
|
||||||
// Generate source maps for debugging
|
|
||||||
sourcemap: true,
|
|
||||||
|
|
||||||
// Minify for slightly smaller bundle
|
|
||||||
minify: false,
|
|
||||||
|
|
||||||
// Keep names for better error messages
|
|
||||||
keepNames: true,
|
|
||||||
|
|
||||||
// Log build info
|
|
||||||
logLevel: "info",
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Autocomplete bundle built successfully!");
|
|
||||||
if (result.warnings.length > 0) {
|
|
||||||
console.warn("Warnings:", result.warnings);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Build failed:", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build();
|
|
||||||
@@ -36,6 +36,7 @@ export function convertArgsToObject(args: string[]): { args: string[], options:
|
|||||||
'autoInputs',
|
'autoInputs',
|
||||||
'sign',
|
'sign',
|
||||||
'broadcast',
|
'broadcast',
|
||||||
|
'install',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const positionalArgs: string[] = [];
|
const positionalArgs: string[] = [];
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
* mnemonics - List mnemonic file names
|
* mnemonics - List mnemonic file names
|
||||||
* templates - List template names/IDs
|
* templates - List template names/IDs
|
||||||
* actions <template> - List actions for a template
|
* actions <template> - List actions for a template
|
||||||
|
* fields <category> <template> - List fields for a template category
|
||||||
* invitations - List invitation IDs
|
* invitations - List invitation IDs
|
||||||
* resources - List UTXO outpoints (txhash:vout)
|
* resources - List UTXO outpoints (txhash:vout)
|
||||||
* subcommands <command> - List subcommands for a top-level command
|
* subcommands <command> - List subcommands for a top-level command
|
||||||
@@ -150,6 +151,33 @@ async function listTemplates(prefix?: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a template by name or ID.
|
||||||
|
*/
|
||||||
|
async function resolveTemplate(
|
||||||
|
engine: Awaited<ReturnType<Awaited<ReturnType<typeof getOfflineEngineModule>>["tryCreateOfflineEngine"]>>,
|
||||||
|
templateQuery: string,
|
||||||
|
) {
|
||||||
|
if (!engine) return null;
|
||||||
|
|
||||||
|
const { generateTemplateIdentifier } = await getEngineModule();
|
||||||
|
const templates = await engine.listImportedTemplates();
|
||||||
|
|
||||||
|
// Try exact match on name or ID
|
||||||
|
let template = templates.find(
|
||||||
|
(t) => t.name === templateQuery || generateTemplateIdentifier(t) === templateQuery,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Try partial match on name
|
||||||
|
if (!template) {
|
||||||
|
template = templates.find((t) =>
|
||||||
|
t.name?.toLowerCase().includes(templateQuery.toLowerCase()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return template ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists actions for a specific template.
|
* Lists actions for a specific template.
|
||||||
*/
|
*/
|
||||||
@@ -158,7 +186,6 @@ async function listActions(templateQuery: string, prefix?: string): Promise<void
|
|||||||
if (!mnemonic) return;
|
if (!mnemonic) return;
|
||||||
|
|
||||||
const { tryCreateOfflineEngine } = await getOfflineEngineModule();
|
const { tryCreateOfflineEngine } = await getOfflineEngineModule();
|
||||||
const { generateTemplateIdentifier } = await getEngineModule();
|
|
||||||
|
|
||||||
const engine = await tryCreateOfflineEngine(mnemonic, {
|
const engine = await tryCreateOfflineEngine(mnemonic, {
|
||||||
databasePath: getDataDir(),
|
databasePath: getDataDir(),
|
||||||
@@ -168,18 +195,7 @@ async function listActions(templateQuery: string, prefix?: string): Promise<void
|
|||||||
if (!engine) return;
|
if (!engine) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to resolve the template by name or ID
|
const template = await resolveTemplate(engine, templateQuery);
|
||||||
const templates = await engine.listImportedTemplates();
|
|
||||||
let template = templates.find(
|
|
||||||
(t) => t.name === templateQuery || generateTemplateIdentifier(t) === templateQuery,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!template) {
|
|
||||||
// Try partial match on name
|
|
||||||
template = templates.find((t) =>
|
|
||||||
t.name?.toLowerCase().includes(templateQuery.toLowerCase()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (template && template.actions) {
|
if (template && template.actions) {
|
||||||
const actions = Object.keys(template.actions);
|
const actions = Object.keys(template.actions);
|
||||||
@@ -190,6 +206,53 @@ async function listActions(templateQuery: string, prefix?: string): Promise<void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists fields (actions, transactions, outputs, etc.) for a specific template category.
|
||||||
|
* Used for completing the 3rd argument of `template inspect <category> <template> <field>`.
|
||||||
|
*/
|
||||||
|
async function listFields(category: string, templateQuery: string, prefix?: string): Promise<void> {
|
||||||
|
const mnemonic = getCurrentMnemonic();
|
||||||
|
if (!mnemonic) return;
|
||||||
|
|
||||||
|
const { tryCreateOfflineEngine } = await getOfflineEngineModule();
|
||||||
|
|
||||||
|
const engine = await tryCreateOfflineEngine(mnemonic, {
|
||||||
|
databasePath: getDataDir(),
|
||||||
|
databaseFilename: "xo-wallet.db",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!engine) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const template = await resolveTemplate(engine, templateQuery);
|
||||||
|
if (!template) return;
|
||||||
|
|
||||||
|
let fields: string[] = [];
|
||||||
|
|
||||||
|
switch (category) {
|
||||||
|
case "action":
|
||||||
|
fields = Object.keys(template.actions || {});
|
||||||
|
break;
|
||||||
|
case "transaction":
|
||||||
|
fields = Object.keys(template.transactions || {});
|
||||||
|
break;
|
||||||
|
case "output":
|
||||||
|
fields = Object.keys(template.outputs || {});
|
||||||
|
break;
|
||||||
|
case "lockingscript":
|
||||||
|
fields = Object.keys(template.lockingScripts || {});
|
||||||
|
break;
|
||||||
|
case "variable":
|
||||||
|
fields = Object.keys(template.variables || {});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputCompletions(fields, prefix);
|
||||||
|
} finally {
|
||||||
|
await engine.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists invitation IDs from the invitation storage.
|
* Lists invitation IDs from the invitation storage.
|
||||||
*/
|
*/
|
||||||
@@ -278,6 +341,13 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "fields":
|
||||||
|
// fields <category> <template> [prefix]
|
||||||
|
if (arg1 && arg2) {
|
||||||
|
await listFields(arg1, arg2, process.argv[5]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "invitations":
|
case "invitations":
|
||||||
await listInvitations(arg1);
|
await listInvitations(arg1);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Shell completion script generation.
|
* Shell completion script generation.
|
||||||
*
|
*
|
||||||
* Defines the CLI command tree in one place and generates
|
* Loads shell-native template files and replaces placeholders with
|
||||||
* bash/zsh/fish completion scripts from it. Users source the output
|
* dynamic values. This approach keeps the shell scripts readable
|
||||||
* in their shell profile for tab-completion support.
|
* and auditable in their native format.
|
||||||
*
|
*
|
||||||
* The generated scripts use the `xo-complete` helper binary for dynamic
|
* The generated scripts use the `xo-complete` helper binary for dynamic
|
||||||
* completions (invitation IDs, template names, resources, etc.).
|
* completions (invitation IDs, template names, resources, etc.).
|
||||||
@@ -12,8 +12,18 @@
|
|||||||
* eval "$(xo-cli completions bash)"
|
* eval "$(xo-cli completions bash)"
|
||||||
* eval "$(xo-cli completions zsh)"
|
* eval "$(xo-cli completions zsh)"
|
||||||
* xo-cli completions fish | source
|
* xo-cli completions fish | source
|
||||||
|
*
|
||||||
|
* Install to shell config:
|
||||||
|
* xo-cli completions bash --install
|
||||||
|
* xo-cli completions zsh --install
|
||||||
|
* xo-cli completions fish --install
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { existsSync, readFileSync, appendFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { homedir } from "node:os";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single source of truth for the CLI command tree.
|
* Single source of truth for the CLI command tree.
|
||||||
* Each top-level key is a command, and its value is an array of sub-commands.
|
* Each top-level key is a command, and its value is an array of sub-commands.
|
||||||
@@ -50,490 +60,96 @@ export const COMMAND_TREE = {
|
|||||||
const GLOBAL_OPTIONS = ["-h", "--help", "-v", "--verbose", "-m", "--mnemonic-file", "-o", "--output"];
|
const GLOBAL_OPTIONS = ["-h", "--help", "-v", "--verbose", "-m", "--mnemonic-file", "-o", "--output"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a bash completion script with dynamic completion support.
|
* Gets the path to the scripts directory containing shell templates.
|
||||||
* @param binName - The name of the CLI binary (used in the `complete` registration).
|
|
||||||
*/
|
*/
|
||||||
export function generateBashCompletions(binName: string): string {
|
function getScriptsDir(): string {
|
||||||
const commands = Object.keys(COMMAND_TREE).join(" ");
|
const currentFile = fileURLToPath(import.meta.url);
|
||||||
const options = GLOBAL_OPTIONS.join(" ");
|
return join(dirname(currentFile), "scripts");
|
||||||
const funcName = binName.replace(/-/g, "_");
|
|
||||||
|
|
||||||
return `# bash completion for ${binName}
|
|
||||||
# Add to ~/.bashrc: eval "$(${binName} completions bash)"
|
|
||||||
|
|
||||||
# Find xo-complete in the same directory as xo-cli
|
|
||||||
__xo_complete_bin=""
|
|
||||||
if command -v xo-complete &>/dev/null; then
|
|
||||||
__xo_complete_bin="xo-complete"
|
|
||||||
elif command -v ${binName} &>/dev/null; then
|
|
||||||
__xo_complete_bin="$(dirname "$(command -v ${binName})")/xo-complete"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wrapper to call xo-complete helper
|
|
||||||
__xo_complete() {
|
|
||||||
[[ -n "\${__xo_complete_bin}" ]] && "\${__xo_complete_bin}" "$@" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
_${funcName}_completions() {
|
|
||||||
local cur prev words cword
|
|
||||||
_init_completion || return
|
|
||||||
|
|
||||||
# Handle -m/--mnemonic-file argument (previous word was -m)
|
|
||||||
if [[ "\${prev}" == "-m" || "\${prev}" == "--mnemonic-file" ]]; then
|
|
||||||
local mnemonics
|
|
||||||
mnemonics=$(__xo_complete mnemonics "\${cur}")
|
|
||||||
if [[ -n "\${mnemonics}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${mnemonics}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If the current word starts with "-", offer option flags
|
|
||||||
if [[ "\${cur}" == -* ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "${options}" -- "\${cur}"))
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find the command and subcommand positions
|
|
||||||
local cmd="" subcmd="" cmd_idx=0 subcmd_idx=0
|
|
||||||
for ((i=1; i < cword; i++)); do
|
|
||||||
if [[ "\${words[i]}" != -* ]]; then
|
|
||||||
if [[ -z "\${cmd}" ]]; then
|
|
||||||
cmd="\${words[i]}"
|
|
||||||
cmd_idx=\$i
|
|
||||||
else
|
|
||||||
subcmd="\${words[i]}"
|
|
||||||
subcmd_idx=\$i
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# No command yet — offer the top-level commands
|
|
||||||
if [[ -z "\${cmd}" ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "${commands}" -- "\${cur}"))
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle each command's completion
|
|
||||||
case "\${cmd}" in
|
|
||||||
mnemonic)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "${MNEMONIC_SUBS.join(" ")}" -- "\${cur}"))
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
template)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "${TEMPLATE_SUBS.join(" ")}" -- "\${cur}"))
|
|
||||||
elif [[ "\${subcmd}" == "list" || "\${subcmd}" == "inspect" ]]; then
|
|
||||||
# template list/inspect <category> <template> [field] - category first, then template
|
|
||||||
local pos=$((cword - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "action transaction output lockingscript variable" -- "\${cur}"))
|
|
||||||
elif [[ \$pos -eq 2 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=$(__xo_complete templates "\${cur}")
|
|
||||||
if [[ -n "\${templates}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${templates}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
elif [[ "\${subcmd}" == "set-default" ]]; then
|
|
||||||
# template set-default <template> <output> <role> - template first
|
|
||||||
local pos=$((cword - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=$(__xo_complete templates "\${cur}")
|
|
||||||
if [[ -n "\${templates}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${templates}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
invitation)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "${INVITATION_SUBS.join(" ")}" -- "\${cur}"))
|
|
||||||
else
|
|
||||||
case "\${subcmd}" in
|
|
||||||
create)
|
|
||||||
# invitation create <template> <action> - offer templates then actions
|
|
||||||
local pos=$((cword - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=$(__xo_complete templates "\${cur}")
|
|
||||||
if [[ -n "\${templates}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${templates}"
|
|
||||||
fi
|
|
||||||
elif [[ \$pos -eq 2 ]]; then
|
|
||||||
local template_arg="\${words[subcmd_idx + 1]}"
|
|
||||||
local actions
|
|
||||||
actions=$(__xo_complete actions "\${template_arg}" "\${cur}")
|
|
||||||
if [[ -n "\${actions}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${actions}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
append|sign|broadcast|requirements|inspect)
|
|
||||||
# These take an invitation ID
|
|
||||||
local pos=$((cword - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local invitations
|
|
||||||
invitations=$(__xo_complete invitations "\${cur}")
|
|
||||||
if [[ -n "\${invitations}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${invitations}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
import)
|
|
||||||
# import takes a file path - use default file completion
|
|
||||||
COMPREPLY=($(compgen -f -- "\${cur}"))
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
resource)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "${RESOURCE_SUBS.join(" ")}" -- "\${cur}"))
|
|
||||||
elif [[ "\${subcmd}" == "unreserve" ]]; then
|
|
||||||
# resource unreserve <txhash:vout> - offer resources
|
|
||||||
local pos=$((cword - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local resources
|
|
||||||
resources=$(__xo_complete resources "\${cur}")
|
|
||||||
if [[ -n "\${resources}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${resources}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
receive)
|
|
||||||
# receive <template> [output] - offer templates
|
|
||||||
local pos=$((cword - cmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=$(__xo_complete templates "\${cur}")
|
|
||||||
if [[ -n "\${templates}" ]]; then
|
|
||||||
while IFS= read -r line; do
|
|
||||||
COMPREPLY+=("\$line")
|
|
||||||
done <<< "\${templates}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
completions)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "bash zsh fish" -- "\${cur}"))
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _${funcName}_completions ${binName}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a zsh completion script with dynamic completion support.
|
* Loads a shell template file and replaces placeholders.
|
||||||
* @param binName - The name of the CLI binary.
|
* @param templateName - The template file name (e.g., "bash.sh")
|
||||||
|
* @param binName - The CLI binary name
|
||||||
*/
|
*/
|
||||||
export function generateZshCompletions(binName: string): string {
|
function loadAndProcessTemplate(templateName: string, binName: string): string {
|
||||||
|
const scriptsDir = getScriptsDir();
|
||||||
|
const templatePath = join(scriptsDir, templateName);
|
||||||
|
|
||||||
|
if (!existsSync(templatePath)) {
|
||||||
|
throw new Error(`Template file not found: ${templatePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = readFileSync(templatePath, "utf8");
|
||||||
|
const funcName = binName.replace(/-/g, "_");
|
||||||
const commands = Object.keys(COMMAND_TREE).join(" ");
|
const commands = Object.keys(COMMAND_TREE).join(" ");
|
||||||
const options = GLOBAL_OPTIONS.join(" ");
|
const options = GLOBAL_OPTIONS.join(" ");
|
||||||
const funcName = binName.replace(/-/g, "_");
|
|
||||||
|
|
||||||
return `# zsh completion for ${binName}
|
// Replace all placeholders
|
||||||
# Add to ~/.zshrc: eval "$(${binName} completions zsh)"
|
content = content.replace(/\{\{BIN_NAME\}\}/g, binName);
|
||||||
|
content = content.replace(/\{\{FUNC_NAME\}\}/g, funcName);
|
||||||
|
content = content.replace(/\{\{COMMANDS\}\}/g, commands);
|
||||||
|
content = content.replace(/\{\{OPTIONS\}\}/g, options);
|
||||||
|
content = content.replace(/\{\{MNEMONIC_SUBS\}\}/g, MNEMONIC_SUBS.join(" "));
|
||||||
|
content = content.replace(/\{\{TEMPLATE_SUBS\}\}/g, TEMPLATE_SUBS.join(" "));
|
||||||
|
content = content.replace(/\{\{INVITATION_SUBS\}\}/g, INVITATION_SUBS.join(" "));
|
||||||
|
content = content.replace(/\{\{RESOURCE_SUBS\}\}/g, RESOURCE_SUBS.join(" "));
|
||||||
|
|
||||||
# Find xo-complete in the same directory as xo-cli
|
// Fish-specific placeholders
|
||||||
__xo_complete_bin=""
|
if (templateName.endsWith(".fish")) {
|
||||||
if (( \$+commands[xo-complete] )); then
|
content = content.replace(/\{\{TOP_LEVEL_COMMANDS\}\}/g, generateFishTopLevelCommands(binName));
|
||||||
__xo_complete_bin="xo-complete"
|
content = content.replace(/\{\{STATIC_SUBCOMMANDS\}\}/g, generateFishStaticSubcommands(binName));
|
||||||
elif (( \$+commands[${binName}] )); then
|
|
||||||
__xo_complete_bin="\${commands[${binName}]:h}/xo-complete"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wrapper to call xo-complete helper
|
|
||||||
__xo_complete() {
|
|
||||||
[[ -n "\${__xo_complete_bin}" ]] && "\${__xo_complete_bin}" "$@" 2>/dev/null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_${funcName}_completions() {
|
return content;
|
||||||
local -a commands
|
|
||||||
commands=(${commands})
|
|
||||||
|
|
||||||
# Handle -m/--mnemonic-file argument (previous word was -m)
|
|
||||||
if [[ "\${words[CURRENT-1]}" == "-m" || "\${words[CURRENT-1]}" == "--mnemonic-file" ]]; then
|
|
||||||
local mnemonics
|
|
||||||
mnemonics=("\${(@f)$(__xo_complete mnemonics "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#mnemonics[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${mnemonics[@]}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If typing an option flag, complete options
|
|
||||||
if [[ "\${words[\${CURRENT}]}" == -* ]]; then
|
|
||||||
compadd -- ${options}
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find the command and subcommand
|
|
||||||
local cmd="" subcmd="" cmd_idx=0 subcmd_idx=0
|
|
||||||
for ((i=2; i < CURRENT; i++)); do
|
|
||||||
if [[ "\${words[i]}" != -* ]]; then
|
|
||||||
if [[ -z "\${cmd}" ]]; then
|
|
||||||
cmd="\${words[i]}"
|
|
||||||
cmd_idx=\$i
|
|
||||||
else
|
|
||||||
subcmd="\${words[i]}"
|
|
||||||
subcmd_idx=\$i
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# No command yet — offer top-level commands
|
|
||||||
if [[ -z "\${cmd}" ]]; then
|
|
||||||
compadd -- \${commands[@]}
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle each command's completion
|
|
||||||
case "\${cmd}" in
|
|
||||||
mnemonic)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
compadd -- ${MNEMONIC_SUBS.join(" ")}
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
template)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
compadd -- ${TEMPLATE_SUBS.join(" ")}
|
|
||||||
elif [[ "\${subcmd}" == "list" || "\${subcmd}" == "inspect" ]]; then
|
|
||||||
# template list/inspect <category> <template> - category first
|
|
||||||
local pos=$((CURRENT - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
compadd -- action transaction output lockingscript variable
|
|
||||||
elif [[ \$pos -eq 2 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=("\${(@f)$(__xo_complete templates "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#templates[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${templates[@]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
elif [[ "\${subcmd}" == "set-default" ]]; then
|
|
||||||
# template set-default <template> <output> <role> - template first
|
|
||||||
local pos=$((CURRENT - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=("\${(@f)$(__xo_complete templates "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#templates[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${templates[@]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
invitation)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
compadd -- ${INVITATION_SUBS.join(" ")}
|
|
||||||
else
|
|
||||||
case "\${subcmd}" in
|
|
||||||
create)
|
|
||||||
local pos=$((CURRENT - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=("\${(@f)$(__xo_complete templates "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#templates[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${templates[@]}"
|
|
||||||
fi
|
|
||||||
elif [[ \$pos -eq 2 ]]; then
|
|
||||||
local template_arg="\${words[subcmd_idx + 1]}"
|
|
||||||
local actions
|
|
||||||
actions=("\${(@f)$(__xo_complete actions "\${template_arg}" "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#actions[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${actions[@]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
append|sign|broadcast|requirements|inspect)
|
|
||||||
local pos=$((CURRENT - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local invitations
|
|
||||||
invitations=("\${(@f)$(__xo_complete invitations "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#invitations[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${invitations[@]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
import)
|
|
||||||
_files
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
resource)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
compadd -- ${RESOURCE_SUBS.join(" ")}
|
|
||||||
elif [[ "\${subcmd}" == "unreserve" ]]; then
|
|
||||||
local pos=$((CURRENT - subcmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local resources
|
|
||||||
resources=("\${(@f)$(__xo_complete resources "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#resources[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${resources[@]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
receive)
|
|
||||||
local pos=$((CURRENT - cmd_idx))
|
|
||||||
if [[ \$pos -eq 1 ]]; then
|
|
||||||
local templates
|
|
||||||
templates=("\${(@f)$(__xo_complete templates "\${words[CURRENT]}")}")
|
|
||||||
if [[ \${#templates[@]} -gt 0 ]]; then
|
|
||||||
compadd -- "\${templates[@]}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
completions)
|
|
||||||
if [[ -z "\${subcmd}" ]]; then
|
|
||||||
compadd -- bash zsh fish
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
compdef _${funcName}_completions ${binName}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a fish completion script with dynamic completion support.
|
* Generates fish top-level command completions.
|
||||||
* @param binName - The name of the CLI binary.
|
|
||||||
*/
|
*/
|
||||||
export function generateFishCompletions(binName: string): string {
|
function generateFishTopLevelCommands(binName: string): string {
|
||||||
const lines: string[] = [
|
const lines: string[] = [];
|
||||||
`# fish completion for ${binName}`,
|
for (const cmd of Object.keys(COMMAND_TREE)) {
|
||||||
`# Add to fish config: ${binName} completions fish | source`,
|
|
||||||
"",
|
|
||||||
`# Disable file completions by default`,
|
|
||||||
`complete -c ${binName} -f`,
|
|
||||||
"",
|
|
||||||
`# Helper function to get dynamic completions`,
|
|
||||||
`# Finds xo-complete in the same directory as ${binName}`,
|
|
||||||
`function __${binName.replace(/-/g, "_")}_complete_dynamic`,
|
|
||||||
` set -l xo_complete_bin ""`,
|
|
||||||
` if command -q xo-complete`,
|
|
||||||
` set xo_complete_bin xo-complete`,
|
|
||||||
` else if command -q ${binName}`,
|
|
||||||
` set xo_complete_bin (dirname (command -s ${binName}))/xo-complete`,
|
|
||||||
` end`,
|
|
||||||
` if test -n "$xo_complete_bin"`,
|
|
||||||
` $xo_complete_bin $argv 2>/dev/null`,
|
|
||||||
` end`,
|
|
||||||
`end`,
|
|
||||||
"",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Global options
|
|
||||||
for (const opt of GLOBAL_OPTIONS) {
|
|
||||||
const isShort = !opt.startsWith("--");
|
|
||||||
const flag = opt.replace(/^-+/, "");
|
|
||||||
if (isShort) {
|
|
||||||
lines.push(`complete -c ${binName} -s ${flag} -d "Option flag"`);
|
|
||||||
} else {
|
|
||||||
lines.push(`complete -c ${binName} -l ${flag} -d "Option flag"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mnemonic file completion for -m
|
|
||||||
lines.push("");
|
|
||||||
lines.push(`# Dynamic mnemonic file completion for -m`);
|
|
||||||
lines.push(`complete -c ${binName} -s m -l mnemonic-file -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic mnemonics)'`);
|
|
||||||
lines.push("");
|
|
||||||
|
|
||||||
// Top-level commands (only when no sub-command is given yet)
|
|
||||||
lines.push(`# Top-level commands`);
|
|
||||||
const commandNames = Object.keys(COMMAND_TREE);
|
|
||||||
for (const cmd of commandNames) {
|
|
||||||
lines.push(`complete -c ${binName} -n "__fish_use_subcommand" -a "${cmd}" -d "${cmd} command"`);
|
lines.push(`complete -c ${binName} -n "__fish_use_subcommand" -a "${cmd}" -d "${cmd} command"`);
|
||||||
}
|
}
|
||||||
lines.push("");
|
return lines.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Static sub-commands for each command
|
/**
|
||||||
lines.push(`# Static sub-commands`);
|
* Generates fish static subcommand completions.
|
||||||
|
*/
|
||||||
|
function generateFishStaticSubcommands(binName: string): string {
|
||||||
|
const lines: string[] = [];
|
||||||
for (const [cmd, subs] of Object.entries(COMMAND_TREE)) {
|
for (const [cmd, subs] of Object.entries(COMMAND_TREE)) {
|
||||||
for (const sub of subs) {
|
for (const sub of subs) {
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from ${cmd}; and not __fish_seen_subcommand_from ${subs.join(" ")}" -a "${sub}" -d "${cmd} ${sub}"`);
|
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from ${cmd}; and not __fish_seen_subcommand_from ${subs.join(" ")}" -a "${sub}" -d "${cmd} ${sub}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.push("");
|
return lines.join("\n");
|
||||||
|
|
||||||
// Dynamic completions
|
|
||||||
lines.push(`# Dynamic completions`);
|
|
||||||
lines.push("");
|
|
||||||
|
|
||||||
// invitation create <template> <action>
|
|
||||||
lines.push(`# invitation create: template names`);
|
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from create; and test (count (commandline -opc)) -eq 3" -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic templates)'`);
|
|
||||||
lines.push("");
|
|
||||||
|
|
||||||
// invitation append/sign/broadcast/requirements/inspect: invitation IDs
|
|
||||||
lines.push(`# invitation append/sign/broadcast/requirements/inspect: invitation IDs`);
|
|
||||||
for (const sub of ["append", "sign", "broadcast", "requirements", "inspect"]) {
|
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from ${sub}; and test (count (commandline -opc)) -eq 3" -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic invitations)'`);
|
|
||||||
}
|
}
|
||||||
lines.push("");
|
|
||||||
|
|
||||||
// invitation import: file completion
|
/**
|
||||||
lines.push(`# invitation import: file completion`);
|
* Generates a bash completion script.
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from import" -F`);
|
* @param binName - The name of the CLI binary.
|
||||||
lines.push("");
|
*/
|
||||||
|
export function generateBashCompletions(binName: string): string {
|
||||||
// template list/inspect: category first, then template
|
return loadAndProcessTemplate("bash.sh", binName);
|
||||||
lines.push(`# template list/inspect: category first (pos 3), then template (pos 4)`);
|
|
||||||
for (const sub of ["list", "inspect"]) {
|
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from ${sub}; and test (count (commandline -opc)) -eq 3" -xa 'action transaction output lockingscript variable'`);
|
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from ${sub}; and test (count (commandline -opc)) -eq 4" -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic templates)'`);
|
|
||||||
}
|
}
|
||||||
lines.push("");
|
|
||||||
|
|
||||||
// template set-default: template first
|
/**
|
||||||
lines.push(`# template set-default: template first`);
|
* Generates a zsh completion script.
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from set-default; and test (count (commandline -opc)) -eq 3" -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic templates)'`);
|
* @param binName - The name of the CLI binary.
|
||||||
lines.push("");
|
*/
|
||||||
|
export function generateZshCompletions(binName: string): string {
|
||||||
|
return loadAndProcessTemplate("zsh.zsh", binName);
|
||||||
|
}
|
||||||
|
|
||||||
// resource unreserve: outpoints
|
/**
|
||||||
lines.push(`# resource unreserve: UTXO outpoints`);
|
* Generates a fish completion script.
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from resource; and __fish_seen_subcommand_from unreserve; and test (count (commandline -opc)) -eq 3" -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic resources)'`);
|
* @param binName - The name of the CLI binary.
|
||||||
lines.push("");
|
*/
|
||||||
|
export function generateFishCompletions(binName: string): string {
|
||||||
// receive: template names
|
return loadAndProcessTemplate("fish.fish", binName);
|
||||||
lines.push(`# receive: template names`);
|
|
||||||
lines.push(`complete -c ${binName} -n "__fish_seen_subcommand_from receive; and test (count (commandline -opc)) -eq 2" -xa '(__${binName.replace(/-/g, "_")}_complete_dynamic templates)'`);
|
|
||||||
|
|
||||||
return lines.join("\n") + "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShellType = "bash" | "zsh" | "fish";
|
type ShellType = "bash" | "zsh" | "fish";
|
||||||
@@ -544,25 +160,96 @@ const generators: Record<ShellType, (binName: string) => string> = {
|
|||||||
fish: generateFishCompletions,
|
fish: generateFishCompletions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shell config file paths and eval commands for each shell type.
|
||||||
|
*/
|
||||||
|
const shellConfigs: Record<ShellType, { configFile: string; evalCommand: (binName: string) => string }> = {
|
||||||
|
bash: {
|
||||||
|
configFile: join(homedir(), ".bashrc"),
|
||||||
|
evalCommand: (binName) => `eval "$(${binName} completions bash)"`,
|
||||||
|
},
|
||||||
|
zsh: {
|
||||||
|
configFile: join(homedir(), ".zshrc"),
|
||||||
|
evalCommand: (binName) => `eval "$(${binName} completions zsh)"`,
|
||||||
|
},
|
||||||
|
fish: {
|
||||||
|
configFile: join(homedir(), ".config", "fish", "config.fish"),
|
||||||
|
evalCommand: (binName) => `${binName} completions fish | source`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs completions to the user's shell config file.
|
||||||
|
* Adds the eval command if not already present.
|
||||||
|
* @param shell - The shell type
|
||||||
|
* @param binName - The CLI binary name
|
||||||
|
* @returns true if installed, false if already present
|
||||||
|
*/
|
||||||
|
function installCompletions(shell: ShellType, binName: string): boolean {
|
||||||
|
const config = shellConfigs[shell];
|
||||||
|
const evalCommand = config.evalCommand(binName);
|
||||||
|
|
||||||
|
// Check if config file exists and already has the completion line
|
||||||
|
let existingContent = "";
|
||||||
|
if (existsSync(config.configFile)) {
|
||||||
|
existingContent = readFileSync(config.configFile, "utf8");
|
||||||
|
if (existingContent.includes(evalCommand)) {
|
||||||
|
return false; // Already installed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the completion line
|
||||||
|
const newLine = existingContent.endsWith("\n") || existingContent === "" ? "" : "\n";
|
||||||
|
const completionBlock = `${newLine}\n# ${binName} shell completions\n${evalCommand}\n`;
|
||||||
|
|
||||||
|
appendFileSync(config.configFile, completionBlock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the `completions` command.
|
* Handles the `completions` command.
|
||||||
* Prints the generated completion script for the given shell to stdout.
|
* Prints the generated completion script for the given shell to stdout,
|
||||||
|
* or installs it to the shell config file with --install.
|
||||||
* @param args - Positional args after "completions", e.g. ["bash"].
|
* @param args - Positional args after "completions", e.g. ["bash"].
|
||||||
|
* @param options - Parsed command options (may include "install").
|
||||||
* @param binName - The CLI binary name to use in the completion script.
|
* @param binName - The CLI binary name to use in the completion script.
|
||||||
*/
|
*/
|
||||||
export function handleCompletionsCommand(args: string[], binName: string = "xo-cli"): void {
|
export function handleCompletionsCommand(
|
||||||
|
args: string[],
|
||||||
|
options: Record<string, string> = {},
|
||||||
|
binName: string = "xo-cli",
|
||||||
|
): void {
|
||||||
const shell = args[0] as ShellType | undefined;
|
const shell = args[0] as ShellType | undefined;
|
||||||
|
const installFlag = options["install"] === "true";
|
||||||
|
|
||||||
if (!shell || !generators[shell]) {
|
if (!shell || !generators[shell]) {
|
||||||
const supported = Object.keys(generators).join(", ");
|
const supported = Object.keys(generators).join(", ");
|
||||||
console.error(`Usage: ${binName} completions <${supported}>`);
|
console.error(`Usage: ${binName} completions <${supported}> [--install]`);
|
||||||
console.error("");
|
console.error("");
|
||||||
console.error("Examples:");
|
console.error("Examples:");
|
||||||
console.error(` eval "$(${binName} completions bash)" # Add to ~/.bashrc`);
|
console.error(` eval "$(${binName} completions bash)" # Output to stdout (add to ~/.bashrc)`);
|
||||||
console.error(` eval "$(${binName} completions zsh)" # Add to ~/.zshrc`);
|
console.error(` eval "$(${binName} completions zsh)" # Output to stdout (add to ~/.zshrc)`);
|
||||||
console.error(` ${binName} completions fish | source # Add to fish config`);
|
console.error(` ${binName} completions fish | source # Output to stdout (add to fish config)`);
|
||||||
|
console.error("");
|
||||||
|
console.error("Install directly to shell config:");
|
||||||
|
console.error(` ${binName} completions bash --install # Appends to ~/.bashrc`);
|
||||||
|
console.error(` ${binName} completions zsh --install # Appends to ~/.zshrc`);
|
||||||
|
console.error(` ${binName} completions fish --install # Appends to ~/.config/fish/config.fish`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (installFlag) {
|
||||||
|
const config = shellConfigs[shell];
|
||||||
|
const installed = installCompletions(shell, binName);
|
||||||
|
|
||||||
|
if (installed) {
|
||||||
|
console.log(`Completions installed to ${config.configFile}`);
|
||||||
|
console.log(`Restart your shell or run: source ${config.configFile}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Completions already installed in ${config.configFile}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
process.stdout.write(generators[shell](binName));
|
process.stdout.write(generators[shell](binName));
|
||||||
}
|
}
|
||||||
|
|||||||
199
src/cli/autocomplete/scripts/bash.sh
Normal file
199
src/cli/autocomplete/scripts/bash.sh
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# bash completion for {{BIN_NAME}}
|
||||||
|
# Add to ~/.bashrc: eval "$({{BIN_NAME}} completions bash)"
|
||||||
|
|
||||||
|
# Find xo-complete in the same directory as xo-cli
|
||||||
|
__xo_complete_bin=""
|
||||||
|
if command -v xo-complete &>/dev/null; then
|
||||||
|
__xo_complete_bin="xo-complete"
|
||||||
|
elif command -v {{BIN_NAME}} &>/dev/null; then
|
||||||
|
__xo_complete_bin="$(dirname "$(command -v {{BIN_NAME}})")/xo-complete"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wrapper to call xo-complete helper
|
||||||
|
__xo_complete() {
|
||||||
|
[[ -n "${__xo_complete_bin}" ]] && "${__xo_complete_bin}" "$@" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
_{{FUNC_NAME}}_completions() {
|
||||||
|
local cur prev words cword
|
||||||
|
_init_completion || return
|
||||||
|
|
||||||
|
# Handle -m/--mnemonic-file argument (previous word was -m)
|
||||||
|
if [[ "${prev}" == "-m" || "${prev}" == "--mnemonic-file" ]]; then
|
||||||
|
local mnemonics
|
||||||
|
mnemonics=$(__xo_complete mnemonics "${cur}")
|
||||||
|
if [[ -n "${mnemonics}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${mnemonics}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the current word starts with "-", offer option flags
|
||||||
|
if [[ "${cur}" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "{{OPTIONS}}" -- "${cur}"))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the command and subcommand positions
|
||||||
|
local cmd="" subcmd="" cmd_idx=0 subcmd_idx=0
|
||||||
|
for ((i=1; i < cword; i++)); do
|
||||||
|
if [[ "${words[i]}" != -* ]]; then
|
||||||
|
if [[ -z "${cmd}" ]]; then
|
||||||
|
cmd="${words[i]}"
|
||||||
|
cmd_idx=$i
|
||||||
|
else
|
||||||
|
subcmd="${words[i]}"
|
||||||
|
subcmd_idx=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# No command yet — offer the top-level commands
|
||||||
|
if [[ -z "${cmd}" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "{{COMMANDS}}" -- "${cur}"))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle each command's completion
|
||||||
|
case "${cmd}" in
|
||||||
|
mnemonic)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "{{MNEMONIC_SUBS}}" -- "${cur}"))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
template)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "{{TEMPLATE_SUBS}}" -- "${cur}"))
|
||||||
|
elif [[ "${subcmd}" == "list" || "${subcmd}" == "inspect" ]]; then
|
||||||
|
# template list/inspect <category> <template> [field] - category first, then template, then field
|
||||||
|
local pos=$((cword - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "action transaction output lockingscript variable" -- "${cur}"))
|
||||||
|
elif [[ $pos -eq 2 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=$(__xo_complete templates "${cur}")
|
||||||
|
if [[ -n "${templates}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${templates}"
|
||||||
|
fi
|
||||||
|
elif [[ $pos -eq 3 && "${subcmd}" == "inspect" ]]; then
|
||||||
|
# Get the category and template from previous args
|
||||||
|
local category="${words[subcmd_idx + 1]}"
|
||||||
|
local template_arg="${words[subcmd_idx + 2]}"
|
||||||
|
local fields
|
||||||
|
fields=$(__xo_complete fields "${category}" "${template_arg}" "${cur}")
|
||||||
|
if [[ -n "${fields}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${fields}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ "${subcmd}" == "set-default" ]]; then
|
||||||
|
# template set-default <template> <output> <role> - template first
|
||||||
|
local pos=$((cword - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=$(__xo_complete templates "${cur}")
|
||||||
|
if [[ -n "${templates}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${templates}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
invitation)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "{{INVITATION_SUBS}}" -- "${cur}"))
|
||||||
|
else
|
||||||
|
case "${subcmd}" in
|
||||||
|
create)
|
||||||
|
# invitation create <template> <action> - offer templates then actions
|
||||||
|
local pos=$((cword - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=$(__xo_complete templates "${cur}")
|
||||||
|
if [[ -n "${templates}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${templates}"
|
||||||
|
fi
|
||||||
|
elif [[ $pos -eq 2 ]]; then
|
||||||
|
local template_arg="${words[subcmd_idx + 1]}"
|
||||||
|
local actions
|
||||||
|
actions=$(__xo_complete actions "${template_arg}" "${cur}")
|
||||||
|
if [[ -n "${actions}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${actions}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
append|sign|broadcast|requirements|inspect)
|
||||||
|
# These take an invitation ID
|
||||||
|
local pos=$((cword - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local invitations
|
||||||
|
invitations=$(__xo_complete invitations "${cur}")
|
||||||
|
if [[ -n "${invitations}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${invitations}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
import)
|
||||||
|
# import takes a file path - use default file completion
|
||||||
|
COMPREPLY=($(compgen -f -- "${cur}"))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
resource)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "{{RESOURCE_SUBS}}" -- "${cur}"))
|
||||||
|
elif [[ "${subcmd}" == "unreserve" ]]; then
|
||||||
|
# resource unreserve <txhash:vout> - offer resources
|
||||||
|
local pos=$((cword - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local resources
|
||||||
|
resources=$(__xo_complete resources "${cur}")
|
||||||
|
if [[ -n "${resources}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${resources}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
receive)
|
||||||
|
# receive <template> [output] - offer templates
|
||||||
|
local pos=$((cword - cmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=$(__xo_complete templates "${cur}")
|
||||||
|
if [[ -n "${templates}" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
COMPREPLY+=("$line")
|
||||||
|
done <<< "${templates}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
completions)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "bash zsh fish" -- "${cur}"))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _{{FUNC_NAME}}_completions {{BIN_NAME}}
|
||||||
70
src/cli/autocomplete/scripts/fish.fish
Normal file
70
src/cli/autocomplete/scripts/fish.fish
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# fish completion for {{BIN_NAME}}
|
||||||
|
# Add to fish config: {{BIN_NAME}} completions fish | source
|
||||||
|
|
||||||
|
# Disable file completions by default
|
||||||
|
complete -c {{BIN_NAME}} -f
|
||||||
|
|
||||||
|
# Helper function to get dynamic completions
|
||||||
|
# Finds xo-complete in the same directory as {{BIN_NAME}}
|
||||||
|
function __{{FUNC_NAME}}_complete_dynamic
|
||||||
|
set -l xo_complete_bin ""
|
||||||
|
if command -q xo-complete
|
||||||
|
set xo_complete_bin xo-complete
|
||||||
|
else if command -q {{BIN_NAME}}
|
||||||
|
set xo_complete_bin (dirname (command -s {{BIN_NAME}}))/xo-complete
|
||||||
|
end
|
||||||
|
if test -n "$xo_complete_bin"
|
||||||
|
$xo_complete_bin $argv 2>/dev/null
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Global options
|
||||||
|
complete -c {{BIN_NAME}} -s h -d "Show help"
|
||||||
|
complete -c {{BIN_NAME}} -l help -d "Show help"
|
||||||
|
complete -c {{BIN_NAME}} -s v -d "Verbose output"
|
||||||
|
complete -c {{BIN_NAME}} -l verbose -d "Verbose output"
|
||||||
|
complete -c {{BIN_NAME}} -s o -d "Output file"
|
||||||
|
complete -c {{BIN_NAME}} -l output -d "Output file"
|
||||||
|
|
||||||
|
# Dynamic mnemonic file completion for -m
|
||||||
|
complete -c {{BIN_NAME}} -s m -l mnemonic-file -xa '(__{{FUNC_NAME}}_complete_dynamic mnemonics)'
|
||||||
|
|
||||||
|
# Top-level commands
|
||||||
|
{{TOP_LEVEL_COMMANDS}}
|
||||||
|
|
||||||
|
# Static sub-commands
|
||||||
|
{{STATIC_SUBCOMMANDS}}
|
||||||
|
|
||||||
|
# Dynamic completions
|
||||||
|
|
||||||
|
# invitation create: template names
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from create; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic templates)'
|
||||||
|
|
||||||
|
# invitation create: action names (2nd arg)
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from create; and test (count (commandline -opc)) -eq 4" -xa '(__{{FUNC_NAME}}_complete_dynamic actions (commandline -opc)[4])'
|
||||||
|
|
||||||
|
# invitation append/sign/broadcast/requirements/inspect: invitation IDs
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from append; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic invitations)'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from sign; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic invitations)'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from broadcast; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic invitations)'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from requirements; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic invitations)'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from inspect; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic invitations)'
|
||||||
|
|
||||||
|
# invitation import: file completion
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from invitation; and __fish_seen_subcommand_from import" -F
|
||||||
|
|
||||||
|
# template list/inspect: category first (pos 3), then template (pos 4), then field (pos 5 for inspect)
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from list; and test (count (commandline -opc)) -eq 3" -xa 'action transaction output lockingscript variable'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from list; and test (count (commandline -opc)) -eq 4" -xa '(__{{FUNC_NAME}}_complete_dynamic templates)'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from inspect; and test (count (commandline -opc)) -eq 3" -xa 'action transaction output lockingscript variable'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from inspect; and test (count (commandline -opc)) -eq 4" -xa '(__{{FUNC_NAME}}_complete_dynamic templates)'
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from inspect; and test (count (commandline -opc)) -eq 5" -xa '(__{{FUNC_NAME}}_complete_dynamic fields (commandline -opc)[4] (commandline -opc)[5])'
|
||||||
|
|
||||||
|
# template set-default: template first
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from template; and __fish_seen_subcommand_from set-default; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic templates)'
|
||||||
|
|
||||||
|
# resource unreserve: UTXO outpoints
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from resource; and __fish_seen_subcommand_from unreserve; and test (count (commandline -opc)) -eq 3" -xa '(__{{FUNC_NAME}}_complete_dynamic resources)'
|
||||||
|
|
||||||
|
# receive: template names
|
||||||
|
complete -c {{BIN_NAME}} -n "__fish_seen_subcommand_from receive; and test (count (commandline -opc)) -eq 2" -xa '(__{{FUNC_NAME}}_complete_dynamic templates)'
|
||||||
176
src/cli/autocomplete/scripts/zsh.zsh
Normal file
176
src/cli/autocomplete/scripts/zsh.zsh
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
# zsh completion for {{BIN_NAME}}
|
||||||
|
# Add to ~/.zshrc: eval "$({{BIN_NAME}} completions zsh)"
|
||||||
|
|
||||||
|
# Find xo-complete in the same directory as xo-cli
|
||||||
|
__xo_complete_bin=""
|
||||||
|
if (( $+commands[xo-complete] )); then
|
||||||
|
__xo_complete_bin="xo-complete"
|
||||||
|
elif (( $+commands[{{BIN_NAME}}] )); then
|
||||||
|
__xo_complete_bin="${commands[{{BIN_NAME}}]:h}/xo-complete"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wrapper to call xo-complete helper
|
||||||
|
__xo_complete() {
|
||||||
|
[[ -n "${__xo_complete_bin}" ]] && "${__xo_complete_bin}" "$@" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
_{{FUNC_NAME}}_completions() {
|
||||||
|
local -a commands
|
||||||
|
commands=({{COMMANDS}})
|
||||||
|
|
||||||
|
# Handle -m/--mnemonic-file argument (previous word was -m)
|
||||||
|
if [[ "${words[CURRENT-1]}" == "-m" || "${words[CURRENT-1]}" == "--mnemonic-file" ]]; then
|
||||||
|
local mnemonics
|
||||||
|
mnemonics=("${(@f)$(__xo_complete mnemonics "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#mnemonics[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${mnemonics[@]}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If typing an option flag, complete options
|
||||||
|
if [[ "${words[${CURRENT}]}" == -* ]]; then
|
||||||
|
compadd -- {{OPTIONS}}
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the command and subcommand
|
||||||
|
local cmd="" subcmd="" cmd_idx=0 subcmd_idx=0
|
||||||
|
for ((i=2; i < CURRENT; i++)); do
|
||||||
|
if [[ "${words[i]}" != -* ]]; then
|
||||||
|
if [[ -z "${cmd}" ]]; then
|
||||||
|
cmd="${words[i]}"
|
||||||
|
cmd_idx=$i
|
||||||
|
else
|
||||||
|
subcmd="${words[i]}"
|
||||||
|
subcmd_idx=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# No command yet — offer top-level commands
|
||||||
|
if [[ -z "${cmd}" ]]; then
|
||||||
|
compadd -- ${commands[@]}
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle each command's completion
|
||||||
|
case "${cmd}" in
|
||||||
|
mnemonic)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
compadd -- {{MNEMONIC_SUBS}}
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
template)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
compadd -- {{TEMPLATE_SUBS}}
|
||||||
|
elif [[ "${subcmd}" == "list" || "${subcmd}" == "inspect" ]]; then
|
||||||
|
# template list/inspect <category> <template> [field] - category first, then template, then field
|
||||||
|
local pos=$((CURRENT - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
compadd -- action transaction output lockingscript variable
|
||||||
|
elif [[ $pos -eq 2 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=("${(@f)$(__xo_complete templates "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#templates[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${templates[@]}"
|
||||||
|
fi
|
||||||
|
elif [[ $pos -eq 3 && "${subcmd}" == "inspect" ]]; then
|
||||||
|
# Get the category and template from previous args
|
||||||
|
local category="${words[subcmd_idx + 1]}"
|
||||||
|
local template_arg="${words[subcmd_idx + 2]}"
|
||||||
|
local fields
|
||||||
|
fields=("${(@f)$(__xo_complete fields "${category}" "${template_arg}" "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#fields[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${fields[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ "${subcmd}" == "set-default" ]]; then
|
||||||
|
# template set-default <template> <output> <role> - template first
|
||||||
|
local pos=$((CURRENT - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=("${(@f)$(__xo_complete templates "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#templates[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${templates[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
invitation)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
compadd -- {{INVITATION_SUBS}}
|
||||||
|
else
|
||||||
|
case "${subcmd}" in
|
||||||
|
create)
|
||||||
|
local pos=$((CURRENT - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=("${(@f)$(__xo_complete templates "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#templates[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${templates[@]}"
|
||||||
|
fi
|
||||||
|
elif [[ $pos -eq 2 ]]; then
|
||||||
|
local template_arg="${words[subcmd_idx + 1]}"
|
||||||
|
local actions
|
||||||
|
actions=("${(@f)$(__xo_complete actions "${template_arg}" "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#actions[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${actions[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
append|sign|broadcast|requirements|inspect)
|
||||||
|
local pos=$((CURRENT - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local invitations
|
||||||
|
invitations=("${(@f)$(__xo_complete invitations "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#invitations[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${invitations[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
import)
|
||||||
|
_files
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
resource)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
compadd -- {{RESOURCE_SUBS}}
|
||||||
|
elif [[ "${subcmd}" == "unreserve" ]]; then
|
||||||
|
local pos=$((CURRENT - subcmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local resources
|
||||||
|
resources=("${(@f)$(__xo_complete resources "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#resources[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${resources[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
receive)
|
||||||
|
local pos=$((CURRENT - cmd_idx))
|
||||||
|
if [[ $pos -eq 1 ]]; then
|
||||||
|
local templates
|
||||||
|
templates=("${(@f)$(__xo_complete templates "${words[CURRENT]}")}")
|
||||||
|
if [[ ${#templates[@]} -gt 0 ]]; then
|
||||||
|
compadd -- "${templates[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
completions)
|
||||||
|
if [[ -z "${subcmd}" ]]; then
|
||||||
|
compadd -- bash zsh fish
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
compdef _{{FUNC_NAME}}_completions {{BIN_NAME}}
|
||||||
@@ -145,8 +145,11 @@ export const handleTemplateInspectCommand = async (
|
|||||||
throw new CommandError("template.inspect.arguments_missing", "No template category, identifier, or field provided");
|
throw new CommandError("template.inspect.arguments_missing", "No template category, identifier, or field provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = await resolveTemplate(deps, templateQuery);
|
const originalTemplate = await resolveTemplate(deps, templateQuery);
|
||||||
deps.io.verbose(`Template: ${formatObject(template)}`);
|
deps.io.verbose(`Original Template: ${formatObject(originalTemplate)}`);
|
||||||
|
|
||||||
|
const template = await resolveTemplateReferences(originalTemplate);
|
||||||
|
deps.io.verbose(`Extended Template: ${formatObject(template)}`);
|
||||||
|
|
||||||
switch (templateCategory) {
|
switch (templateCategory) {
|
||||||
case "action": {
|
case "action": {
|
||||||
|
|||||||
@@ -112,10 +112,9 @@ async function main(): Promise<void> {
|
|||||||
workingDir: process.cwd(),
|
workingDir: process.cwd(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Early handling if we are calling the mnemonic command
|
// Early handling for completions command
|
||||||
// TODO: This is ugly. I would like to find a nicer way of doing this.
|
|
||||||
if (command === "completions") {
|
if (command === "completions") {
|
||||||
handleCompletionsCommand(subArgs);
|
handleCompletionsCommand(subArgs, options);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user