@catladder/cli 0.0.0-new-cli-51a5f8b1 → 0.0.0-new-cli-3ec1f6ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -53,7 +53,7 @@
53
53
  }
54
54
  ],
55
55
  "license": "MIT",
56
- "version": "0.0.0-new-cli-51a5f8b1",
56
+ "version": "0.0.0-new-cli-3ec1f6ea",
57
57
  "scripts": {
58
58
  "lint": "eslint \"src/**/*.ts\"",
59
59
  "lint:fix": "eslint \"src/**/*.ts\" --fix",
package/src/cli.ts CHANGED
@@ -188,30 +188,22 @@ const completionCmd = program
188
188
  completionCmd
189
189
  .command("zsh")
190
190
  .description("output zsh completion script")
191
- .option(
192
- "--command <cmd>",
193
- "command used to invoke the CLI (e.g. 'yarn catladder')",
194
- )
195
- .action((opts: { command?: string }) => {
196
- const binaryName =
197
- opts.command ?? process.argv[1].split("/").pop() ?? "catladder";
198
- console.log(generateZshCompletionScript(binaryName));
191
+ .action(() => {
192
+ console.log(generateZshCompletionScript());
199
193
  });
200
194
 
201
195
  completionCmd
202
196
  .command("install")
203
197
  .description("install shell completions into your .zshrc")
204
198
  .action(async () => {
205
- const binaryName = process.argv[1].split("/").pop() ?? "catladder";
206
- await installCompletions(binaryName);
199
+ await installCompletions();
207
200
  });
208
201
 
209
202
  completionCmd
210
203
  .command("uninstall")
211
204
  .description("remove shell completions from your .zshrc")
212
205
  .action(async () => {
213
- const binaryName = process.argv[1].split("/").pop() ?? "catladder";
214
- await uninstallCompletions(binaryName);
206
+ await uninstallCompletions();
215
207
  });
216
208
 
217
209
  // Cleanup on exit
package/src/completion.ts CHANGED
@@ -142,106 +142,110 @@ async function resolveChoicesForCompletion(
142
142
 
143
143
  /**
144
144
  * Generate the zsh completion script.
145
- * Supports both direct invocation (`catladder`) and prefixed (`yarn catladder`).
145
+ * The script discovers the local catladder binary at completion time,
146
+ * so it works without a global install.
146
147
  */
147
- export function generateZshCompletionScript(command: string): string {
148
- const parts = command.split(" ");
149
- const funcName = command.replace(/[- ]/g, "_");
150
-
151
- if (parts.length === 1) {
152
- // Direct binary: catladder
153
- return `
154
- ###-begin-${funcName}-completions-###
155
- _${funcName}_completions() {
156
- local -a completions
157
- local current_word="\${words[$CURRENT]}"
158
-
159
- # Words after the binary name, excluding the current word being completed
160
- local -a cmd_words=(\${words:1:$((CURRENT-2))})
161
-
162
- # Call the CLI's __complete command
163
- completions=(\${(f)"$(${command} __complete -- "\${cmd_words[*]}" "$current_word" 2>/dev/null)"})
164
-
165
- if [[ \${#completions[@]} -gt 0 ]]; then
166
- compadd -a completions
148
+ export function generateZshCompletionScript(): string {
149
+ return `
150
+ ###-begin-catladder-completions-###
151
+ # Find catladder binary by walking up from cwd
152
+ _catladder_find_binary() {
153
+ local dir="$PWD"
154
+ while [[ "$dir" != "/" ]]; do
155
+ if [[ -x "$dir/node_modules/.bin/catladder" ]]; then
156
+ echo "$dir/node_modules/.bin/catladder"
157
+ return
158
+ fi
159
+ # Also check for catladder-dev (monorepo development)
160
+ if [[ -x "$dir/cli/bin/catladder-dev" ]]; then
161
+ echo "$dir/cli/bin/catladder-dev"
162
+ return
163
+ fi
164
+ dir="\${dir:h}"
165
+ done
166
+ # Fall back to global
167
+ if command -v catladder &>/dev/null; then
168
+ echo "catladder"
169
+ elif command -v catladder-dev &>/dev/null; then
170
+ echo "catladder-dev"
167
171
  fi
168
172
  }
169
173
 
170
- compdef _${funcName}_completions ${command}
171
- ###-end-${funcName}-completions-###
172
- `.trim();
173
- }
174
-
175
- // Multi-word command (e.g., "yarn catladder")
176
- // Register on the first word (e.g., "yarn") but only activate
177
- // when the subcommand matches (e.g., "catladder")
178
- const prefix = parts[0];
179
- const subcommand = parts.slice(1).join(" ");
180
- const skipWords = parts.length; // how many words to skip before CLI args
181
-
182
- return `
183
- ###-begin-${funcName}-completions-###
184
- _${funcName}_completions() {
185
- # Only activate when subcommand is "${subcommand}"
186
- if [[ $CURRENT -le ${skipWords} ]] || [[ "\${words[2,${skipWords}]}" != "${subcommand}" ]]; then
187
- return
188
- fi
174
+ # Run completion with given offset (number of words to skip before CLI args)
175
+ _catladder_do_complete() {
176
+ local skip=$1
177
+ local catladder_bin=$(_catladder_find_binary)
178
+ [[ -z "$catladder_bin" ]] && return
189
179
 
190
180
  local -a completions
191
181
  local current_word="\${words[$CURRENT]}"
182
+ local -a cmd_words=(\${words:$skip:$((CURRENT-skip-1))})
192
183
 
193
- # Words after "${command}", excluding the current word being completed
194
- local -a cmd_words=(\${words:${skipWords}:$((CURRENT-${skipWords}-1))})
195
-
196
- completions=(\${(f)"$(${command} __complete -- "\${cmd_words[*]}" "$current_word" 2>/dev/null)"})
184
+ completions=(\${(f)"$($catladder_bin __complete -- "\${cmd_words[*]}" "$current_word" 2>/dev/null)"})
197
185
 
198
186
  if [[ \${#completions[@]} -gt 0 ]]; then
199
187
  compadd -a completions
200
188
  fi
201
189
  }
202
190
 
203
- compdef _${funcName}_completions ${prefix}
204
- ###-end-${funcName}-completions-###
191
+ # catladder <TAB>
192
+ _catladder_completions() { _catladder_do_complete 1; }
193
+
194
+ # yarn catladder <TAB>
195
+ _yarn_catladder_completions() {
196
+ [[ $CURRENT -le 2 || "\${words[2]}" != "catladder" ]] && return
197
+ _catladder_do_complete 2
198
+ }
199
+
200
+ compdef _catladder_completions catladder
201
+ compdef _catladder_completions catladder-dev
202
+ compdef _yarn_catladder_completions yarn
203
+ ###-end-catladder-completions-###
205
204
  `.trim();
206
205
  }
207
206
 
208
- const BEGIN_MARKER = (name: string) =>
209
- `###-begin-${name.replace(/-/g, "_")}-completions-###`;
207
+ const BEGIN_MARKER = "###-begin-catladder-completions-###";
208
+ const END_MARKER = "###-end-catladder-completions-###";
210
209
 
211
210
  function getZshrcPath(): string {
212
211
  return join(homedir(), ".zshrc");
213
212
  }
214
213
 
215
214
  /**
216
- * Install completions by adding an eval line to .zshrc.
215
+ * Install completions by writing the script directly into .zshrc.
217
216
  */
218
- export async function installCompletions(binaryName: string): Promise<void> {
217
+ export async function installCompletions(): Promise<void> {
219
218
  const zshrcPath = getZshrcPath();
220
- const evalLine = `eval "$(${binaryName} completion zsh)"`;
221
- const marker = BEGIN_MARKER(binaryName);
222
219
 
223
220
  let content = "";
224
221
  if (existsSync(zshrcPath)) {
225
222
  content = readFileSync(zshrcPath, "utf-8");
226
223
  }
227
224
 
228
- if (content.includes(marker)) {
229
- console.log(
230
- `Completions for ${binaryName} are already installed in ${zshrcPath}`,
225
+ if (content.includes(BEGIN_MARKER)) {
226
+ // Replace existing
227
+ const startIdx = content.indexOf(BEGIN_MARKER);
228
+ const endIdx = content.indexOf(END_MARKER) + END_MARKER.length;
229
+ content =
230
+ content.slice(0, startIdx) +
231
+ generateZshCompletionScript() +
232
+ content.slice(endIdx);
233
+ writeFileSync(zshrcPath, content);
234
+ console.log(`Updated catladder completions in ${zshrcPath}`);
235
+ } else {
236
+ writeFileSync(
237
+ zshrcPath,
238
+ content + "\n" + generateZshCompletionScript() + "\n",
231
239
  );
232
- return;
240
+ console.log(`Installed catladder completions in ${zshrcPath}`);
233
241
  }
234
-
235
- const addition = `\n# ${binaryName} shell completions\n${evalLine}\n`;
236
- writeFileSync(zshrcPath, content + addition);
237
- console.log(`Installed completions for ${binaryName} in ${zshrcPath}`);
238
242
  console.log(`Restart your shell or run: source ${zshrcPath}`);
239
243
  }
240
244
 
241
245
  /**
242
246
  * Remove completions from .zshrc.
243
247
  */
244
- export async function uninstallCompletions(binaryName: string): Promise<void> {
248
+ export async function uninstallCompletions(): Promise<void> {
245
249
  const zshrcPath = getZshrcPath();
246
250
  if (!existsSync(zshrcPath)) {
247
251
  console.log("No .zshrc found");
@@ -249,31 +253,20 @@ export async function uninstallCompletions(binaryName: string): Promise<void> {
249
253
  }
250
254
 
251
255
  const content = readFileSync(zshrcPath, "utf-8");
252
- const evalLine = `eval "$(${binaryName} completion zsh)"`;
253
- const commentLine = `# ${binaryName} shell completions`;
254
256
 
255
- if (!content.includes(evalLine)) {
256
- console.log(`No completions for ${binaryName} found in ${zshrcPath}`);
257
+ if (!content.includes(BEGIN_MARKER)) {
258
+ console.log("No catladder completions found in " + zshrcPath);
257
259
  return;
258
260
  }
259
261
 
260
- const cleaned = content
261
- .replace(
262
- new RegExp(
263
- `\\n?${commentLine.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`,
264
- "g",
265
- ),
266
- "\n",
267
- )
268
- .replace(
269
- new RegExp(
270
- `\\n?${evalLine.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`,
271
- "g",
272
- ),
273
- "\n",
274
- );
262
+ const startIdx = content.indexOf(BEGIN_MARKER);
263
+ const endIdx = content.indexOf(END_MARKER) + END_MARKER.length;
264
+ const cleaned = (content.slice(0, startIdx) + content.slice(endIdx)).replace(
265
+ /\n{3,}/g,
266
+ "\n\n",
267
+ );
275
268
 
276
269
  writeFileSync(zshrcPath, cleaned);
277
- console.log(`Removed completions for ${binaryName} from ${zshrcPath}`);
270
+ console.log(`Removed catladder completions from ${zshrcPath}`);
278
271
  console.log(`Restart your shell or run: source ${zshrcPath}`);
279
272
  }