Skip to content

Commit b6f8b82

Browse files
AayushSaini101Amzaniasyncapi-botShurtu-galAdi-204
authored
feat: add autocomplete feature in cli (#1724)
Co-authored-by: AayushSaini101 <[email protected]> Co-authored-by: samz <[email protected]> Co-authored-by: Chan <[email protected]> Co-authored-by: Ashish Padhy <[email protected]> Co-authored-by: Adi Boghawala <[email protected]>
1 parent 59be554 commit b6f8b82

File tree

6 files changed

+274
-6
lines changed

6 files changed

+274
-6
lines changed

Diff for: .changeset/breezy-pandas-arrive.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@asyncapi/cli": major
3+
---
4+
5+
feat: add autocomplete feature in cli

Diff for: docs/autocompleteEnabled.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: 'Auto-complete setup'
3+
weight: 30
4+
---
5+
6+
# AsyncAPI CLI Autocomplete Setup
7+
8+
This guide provides steps to enable autocomplete for the AsyncAPI CLI. The setup supports `zsh`, `bash`, and `PowerShell` (manual setup only).
9+
10+
## Automatic Setup (Post-Install Script)
11+
The AsyncAPI CLI includes a post-install script that automatically configures autocomplete for supported shells (`zsh` and `bash`). No additional steps are required.
12+
13+
### Steps:
14+
1. Ensure that AsyncAPI CLI is installed. You can verify by running:
15+
```sh
16+
asyncapi --version
17+
```
18+
If the command fails, install it using:
19+
```sh
20+
npm install -g @asyncapi/cli
21+
```
22+
23+
24+
2. Apply the changes by running:
25+
```sh
26+
source ~/.bashrc # For bash
27+
source ~/.zshrc # For zsh
28+
```
29+
30+
3. Verify autocomplete by typing:
31+
```sh
32+
asyncapi <TAB>
33+
```
34+
You should see command suggestions.
35+
36+
## Manual Setup (For PowerShell and Troubleshooting)
37+
If the automatic setup does not work or if you need to enable autocomplete manually (especially for PowerShell), follow these steps.
38+
39+
### Steps:
40+
1. **Build the AsyncAPI CLI manually:**
41+
If you are working with the CLI project locally, you need to build it first:
42+
```sh
43+
npm install
44+
npm run build
45+
```
46+
47+
2. **Run the autocomplete command manually:**
48+
```sh
49+
./bin/run autocomplete # Run this from the project root folder
50+
```
51+
52+
3. **Locate the AsyncAPI CLI executable:**
53+
Run the following command to find the executable path:
54+
```sh
55+
which asyncapi # For bash/zsh
56+
Get-Command asyncapi | Select-Object -ExpandProperty Definition # For PowerShell
57+
```
58+
If the command does not return a path, ensure AsyncAPI CLI is installed.
59+
60+
4. **Generate and apply the autocomplete script:**
61+
Run the following command based on your shell:
62+
```sh
63+
printf "$(./bin/run autocomplete script bash)" >> ~/.bashrc; source ~/.bashrc # For bash
64+
printf "$(./bin/run autocomplete script zsh)" >> ~/.zshrc; source ~/.zshrc # For zsh
65+
printf "$(./bin/run autocomplete script powershell)" >> $PROFILE; . $PROFILE # For PowerShell
66+
```
67+
68+
5. **Test autocomplete:**
69+
```sh
70+
asyncapi <TAB>
71+
```
72+
If it works, autocomplete is successfully enabled!
73+
74+
---
75+
76+
If you encounter any issues, ensure that your shell configuration file is correctly updated and sourced. Restart your terminal if necessary.
77+

Diff for: docs/installation.md

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: 'Installation guide'
3-
weight: 30
3+
weight: 20
44
---
55

66
## Node and npm
@@ -21,10 +21,30 @@ npm --version
2121

2222
If you don’t have Node.js or NPM installed, you can install both with this [Node.js package manager](https://nodejs.org/en/download/package-manager/).
2323

24-
After installing Node.js and NPM, run the following command to install the AsyncAPI CLI globally:
24+
After installing Node.js and NPM, run the following command to install the AsyncAPI ClI globally:
2525
```sh
2626
npm install -g @asyncapi/cli
2727
```
28+
To enable the autocomplete feature in the CLI for the shells **bash and zshrc**, there is a script that will run automatically and autocomplete is only support for **bash and zshrc** for the **powershell** refer to manually enabling [autocomplete](https://www.asyncapi.com/docs/tools/cli/autocompleteEnabled) guide in ClI:
29+
30+
After the ClI installation :
31+
32+
if the configuration is not present logs will be:
33+
```sh
34+
✅ Autocomplete configuration added to .zshrc.
35+
```
36+
If the configuration is present for autocomplete logs:
37+
```sh
38+
✅ Autocomplete is already configured. Skipping addition.
39+
```
40+
41+
To refresh the variables:
42+
43+
```sh
44+
source ~/.bashrc # For bash
45+
source ~/.zshrc # For zsh
46+
```
47+
2848
## Docker
2949

3050
Install [Docker](https://docs.docker.com/get-docker/) first, then use docker to build the image using the following command :

Diff for: package-lock.json

+6-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@asyncapi/studio": "^0.23.1",
2323
"@clack/prompts": "^0.7.0",
2424
"@oclif/core": "^4.2.9",
25+
"@oclif/plugin-autocomplete": "^3.2.26",
2526
"@smoya/asyncapi-adoption-metrics": "^2.4.9",
2627
"@stoplight/spectral-cli": "6.9.0",
2728
"chalk": "^4.1.0",
@@ -126,7 +127,8 @@
126127
}
127128
},
128129
"plugins": [
129-
"@oclif/plugin-warn-if-update-available"
130+
"@oclif/plugin-warn-if-update-available",
131+
"@oclif/plugin-autocomplete"
130132
],
131133
"warn-if-update-available": {
132134
"frequency": 24,
@@ -163,6 +165,7 @@
163165
"pretest": "npm run build",
164166
"pretest:coverage": "npm run build",
165167
"posttest": "rimraf ./test.asyncapi-cli",
168+
"postinstall": "node ./scripts/enableAutoComplete.js",
166169
"test": "npm run test:unit && npm run action:test",
167170
"test:unit": "cross-env NODE_ENV=development TEST=1 CUSTOM_CONTEXT_FILENAME=\"test.asyncapi-cli\" CUSTOM_CONTEXT_FILE_LOCATION=\"\" nyc --extension .ts mocha --require ts-node/register --require test/helpers/init.js --reporter spec --timeout 100000 \"test/**/*.test.ts\"",
168171
"test:one": "cross-env NODE_ENV=development TEST=1 CUSTOM_CONTEXT_FILENAME=\"test.asyncapi-cli\" CUSTOM_CONTEXT_FILE_LOCATION=\"\" nyc --extension .ts mocha --require ts-node/register --require test/helpers/init.js --reporter spec --timeout 100000",

Diff for: scripts/enableAutoComplete.js

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/* eslint-disable @typescript-eslint/no-var-requires */
2+
const { spawnSync } = require('child_process');
3+
const os = require('os');
4+
const fs = require('fs');
5+
const path = require('path');
6+
7+
const allowedShells = ['zsh', 'bash'];
8+
9+
// Helper function to find the first existing file among a list of paths
10+
function findExistingFile(possibleFiles) {
11+
for (const file of possibleFiles) {
12+
const fullPath = path.join(os.homedir(), file);
13+
if (fs.existsSync(fullPath)) {
14+
return fullPath;
15+
}
16+
}
17+
return null;
18+
}
19+
20+
const shellConfigs = {
21+
zsh: {
22+
rcFile: path.join(os.homedir(), '.zshrc'),
23+
detectFile: path.join(os.homedir(), '.zshrc'),
24+
postMessage: 'Run: source ~/.zshrc',
25+
action: (output, rcFile) => {
26+
const configContent = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf-8') : '';
27+
28+
if (configContent.includes(output.trim())) {
29+
console.log(`✅ Autocomplete is already configured in ${rcFile}. Skipping addition.`);
30+
} else {
31+
fs.appendFileSync(rcFile, `\n# AsyncAPI CLI Autocomplete\n${output}\n`);
32+
console.log(`✅ Autocomplete configuration added to ${rcFile}.`);
33+
}
34+
},
35+
},
36+
bash: {
37+
rcFile: findExistingFile(['.bashrc', '.bash_profile', '.profile']) || path.join(os.homedir(), '.bashrc'),
38+
detectFile: findExistingFile(['.bashrc', '.bash_profile', '.profile']),
39+
postMessage: '', // This will be set dynamically later
40+
action: (output, rcFile) => {
41+
const configContent = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf-8') : '';
42+
43+
if (configContent.includes(output.trim())) {
44+
console.log(`✅ Autocomplete is already configured in ${rcFile}. Skipping addition.`);
45+
} else {
46+
fs.appendFileSync(rcFile, `\n# AsyncAPI CLI Autocomplete\n${output}\n`);
47+
console.log(`✅ Autocomplete configuration added to ${rcFile}.`);
48+
}
49+
},
50+
},
51+
};
52+
53+
// Set correct postMessage dynamically
54+
if (shellConfigs.bash.detectFile) {
55+
shellConfigs.bash.postMessage = `Run: source ${shellConfigs.bash.detectFile}`;
56+
} else {
57+
shellConfigs.bash.postMessage = 'Run: source ~/.bashrc';
58+
}
59+
60+
function getShellConfig(shell) {
61+
if (!allowedShells.includes(shell)) {
62+
throw new Error(`Unsupported shell: ${shell}. Autocomplete only supports zsh and bash.`);
63+
}
64+
return shellConfigs[shell];
65+
}
66+
67+
function detectShell() {
68+
const detectedShells = [];
69+
for (const [shell, config] of Object.entries(shellConfigs)) {
70+
if (config.detectFile && fs.existsSync(config.detectFile)) {
71+
detectedShells.push(shell);
72+
}
73+
}
74+
return detectedShells;
75+
}
76+
77+
function checkPotentialPath(potentialPath) {
78+
if (potentialPath.includes(path.sep)) {
79+
if (fs.existsSync(potentialPath)) {
80+
return potentialPath;
81+
}
82+
} else {
83+
const result = spawnSync('/bin/sh', ['-c', `command -v ${potentialPath}`], {
84+
encoding: 'utf-8',
85+
stdio: 'pipe',
86+
});
87+
if (result.status === 0 && result.stdout) {
88+
return result.stdout.trim().split('\n')[0];
89+
}
90+
}
91+
return null;
92+
}
93+
94+
function findCliExecutable() {
95+
const possiblePaths = [
96+
path.resolve('./bin/run'),
97+
path.resolve('../bin/run'),
98+
path.resolve('./node_modules/.bin/asyncapi'),
99+
'asyncapi',
100+
];
101+
102+
for (const potentialPath of possiblePaths) {
103+
try {
104+
const foundPath = checkPotentialPath(potentialPath);
105+
if (foundPath) {
106+
console.log(`Found CLI executable at: ${foundPath}`);
107+
return foundPath;
108+
}
109+
} catch (error) {
110+
console.warn(`⚠️ Ignored error while checking path ${potentialPath}: ${error.message}`);
111+
}
112+
}
113+
114+
throw new Error('CLI executable not found. Ensure AsyncAPI CLI is installed.');
115+
}
116+
117+
function generateAutocompleteScript(shell) {
118+
const executablePath = findCliExecutable();
119+
const result = spawnSync(executablePath, ['autocomplete', 'script', shell], {
120+
encoding: 'utf-8',
121+
stdio: 'pipe',
122+
});
123+
if (result.status !== 0 || result.error) {
124+
throw new Error(
125+
`Autocomplete setup for ${shell} failed: ${result.stderr || result.error?.message || 'Unknown error'}`
126+
);
127+
}
128+
const output = result.stdout;
129+
if (!output || output.trim() === '') {
130+
throw new Error(`No autocomplete script generated for ${shell}.`);
131+
}
132+
return output;
133+
}
134+
135+
function setupAutocomplete(shell) {
136+
if (!allowedShells.includes(shell)) {
137+
console.error(`❌ Autocomplete only supports zsh and bash. Skipping setup for ${shell}.`);
138+
return;
139+
}
140+
141+
try {
142+
const config = getShellConfig(shell);
143+
console.log(`🔧 Generating autocomplete script for ${shell}...`);
144+
const output = generateAutocompleteScript(shell);
145+
config.action(output, config.rcFile);
146+
console.log(`✅ Autocomplete configured for ${shell}. ${config.postMessage}`);
147+
} catch (error) {
148+
console.error(`❌ Failed to setup autocomplete for ${shell}: ${error.message}`);
149+
}
150+
}
151+
152+
// Start
153+
const shells = detectShell();
154+
if (shells.length) {
155+
for (const shell of shells) {
156+
setupAutocomplete(shell);
157+
}
158+
} else {
159+
console.log('⚠️ Shell not detected or unsupported. Autocomplete setup skipped.');
160+
}

0 commit comments

Comments
 (0)