@catladder/cli 0.0.0-new-cli-51a5f8b1 → 0.0.0-new-cli-19442092

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-19442092",
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,113 @@ 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() {
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
+ dir="\${dir:h}"
160
+ done
161
+ # Fall back to global
162
+ if command -v catladder &>/dev/null; then
163
+ echo "catladder"
164
+ fi
165
+ }
166
+
167
+ # Completion for direct invocation: catladder <TAB>
168
+ _catladder_completions() {
169
+ local catladder_bin=$(_catladder_find_binary)
170
+ [[ -z "$catladder_bin" ]] && return
171
+
156
172
  local -a completions
157
173
  local current_word="\${words[$CURRENT]}"
158
-
159
- # Words after the binary name, excluding the current word being completed
160
174
  local -a cmd_words=(\${words:1:$((CURRENT-2))})
161
175
 
162
- # Call the CLI's __complete command
163
- completions=(\${(f)"$(${command} __complete -- "\${cmd_words[*]}" "$current_word" 2>/dev/null)"})
176
+ completions=(\${(f)"$($catladder_bin __complete -- "\${cmd_words[*]}" "$current_word" 2>/dev/null)"})
164
177
 
165
178
  if [[ \${#completions[@]} -gt 0 ]]; then
166
179
  compadd -a completions
167
180
  fi
168
181
  }
169
182
 
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
183
+ # Completion for yarn invocation: yarn catladder <TAB>
184
+ _yarn_catladder_completions() {
185
+ # Only activate when second word is "catladder"
186
+ if [[ $CURRENT -le 2 ]] || [[ "\${words[2]}" != "catladder" ]]; then
187
187
  return
188
188
  fi
189
189
 
190
+ local catladder_bin=$(_catladder_find_binary)
191
+ [[ -z "$catladder_bin" ]] && return
192
+
190
193
  local -a completions
191
194
  local current_word="\${words[$CURRENT]}"
195
+ local -a cmd_words=(\${words:2:$((CURRENT-3))})
192
196
 
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)"})
197
+ completions=(\${(f)"$($catladder_bin __complete -- "\${cmd_words[*]}" "$current_word" 2>/dev/null)"})
197
198
 
198
199
  if [[ \${#completions[@]} -gt 0 ]]; then
199
200
  compadd -a completions
200
201
  fi
201
202
  }
202
203
 
203
- compdef _${funcName}_completions ${prefix}
204
- ###-end-${funcName}-completions-###
204
+ compdef _catladder_completions catladder
205
+ compdef _yarn_catladder_completions yarn
206
+ ###-end-catladder-completions-###
205
207
  `.trim();
206
208
  }
207
209
 
208
- const BEGIN_MARKER = (name: string) =>
209
- `###-begin-${name.replace(/-/g, "_")}-completions-###`;
210
+ const BEGIN_MARKER = "###-begin-catladder-completions-###";
211
+ const END_MARKER = "###-end-catladder-completions-###";
210
212
 
211
213
  function getZshrcPath(): string {
212
214
  return join(homedir(), ".zshrc");
213
215
  }
214
216
 
215
217
  /**
216
- * Install completions by adding an eval line to .zshrc.
218
+ * Install completions by writing the script directly into .zshrc.
217
219
  */
218
- export async function installCompletions(binaryName: string): Promise<void> {
220
+ export async function installCompletions(): Promise<void> {
219
221
  const zshrcPath = getZshrcPath();
220
- const evalLine = `eval "$(${binaryName} completion zsh)"`;
221
- const marker = BEGIN_MARKER(binaryName);
222
222
 
223
223
  let content = "";
224
224
  if (existsSync(zshrcPath)) {
225
225
  content = readFileSync(zshrcPath, "utf-8");
226
226
  }
227
227
 
228
- if (content.includes(marker)) {
229
- console.log(
230
- `Completions for ${binaryName} are already installed in ${zshrcPath}`,
228
+ if (content.includes(BEGIN_MARKER)) {
229
+ // Replace existing
230
+ const startIdx = content.indexOf(BEGIN_MARKER);
231
+ const endIdx = content.indexOf(END_MARKER) + END_MARKER.length;
232
+ content =
233
+ content.slice(0, startIdx) +
234
+ generateZshCompletionScript() +
235
+ content.slice(endIdx);
236
+ writeFileSync(zshrcPath, content);
237
+ console.log(`Updated catladder completions in ${zshrcPath}`);
238
+ } else {
239
+ writeFileSync(
240
+ zshrcPath,
241
+ content + "\n" + generateZshCompletionScript() + "\n",
231
242
  );
232
- return;
243
+ console.log(`Installed catladder completions in ${zshrcPath}`);
233
244
  }
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
245
  console.log(`Restart your shell or run: source ${zshrcPath}`);
239
246
  }
240
247
 
241
248
  /**
242
249
  * Remove completions from .zshrc.
243
250
  */
244
- export async function uninstallCompletions(binaryName: string): Promise<void> {
251
+ export async function uninstallCompletions(): Promise<void> {
245
252
  const zshrcPath = getZshrcPath();
246
253
  if (!existsSync(zshrcPath)) {
247
254
  console.log("No .zshrc found");
@@ -249,31 +256,20 @@ export async function uninstallCompletions(binaryName: string): Promise<void> {
249
256
  }
250
257
 
251
258
  const content = readFileSync(zshrcPath, "utf-8");
252
- const evalLine = `eval "$(${binaryName} completion zsh)"`;
253
- const commentLine = `# ${binaryName} shell completions`;
254
259
 
255
- if (!content.includes(evalLine)) {
256
- console.log(`No completions for ${binaryName} found in ${zshrcPath}`);
260
+ if (!content.includes(BEGIN_MARKER)) {
261
+ console.log("No catladder completions found in " + zshrcPath);
257
262
  return;
258
263
  }
259
264
 
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
- );
265
+ const startIdx = content.indexOf(BEGIN_MARKER);
266
+ const endIdx = content.indexOf(END_MARKER) + END_MARKER.length;
267
+ const cleaned = (content.slice(0, startIdx) + content.slice(endIdx)).replace(
268
+ /\n{3,}/g,
269
+ "\n\n",
270
+ );
275
271
 
276
272
  writeFileSync(zshrcPath, cleaned);
277
- console.log(`Removed completions for ${binaryName} from ${zshrcPath}`);
273
+ console.log(`Removed catladder completions from ${zshrcPath}`);
278
274
  console.log(`Restart your shell or run: source ${zshrcPath}`);
279
275
  }