Add remote script loader with intelligent completions
Features: - Remote script execution with version control (name@ref) - Background daemon for fast completions (< 15ms) - HTTP caching with ETag/Last-Modified support - Context-aware completions with async support - Comprehensive documentation and type definitions Changes: - Add loader.ts with daemon mode - Add completions.d.ts type definitions - Add COMPLETIONS.md guide for developers - Add README.md with usage examples - Update configure_ssh_key.ts with async completions - Use JSR imports for remote execution compatibility
This commit is contained in:
parent
616a2d9c96
commit
df5df0ea92
7 changed files with 1448 additions and 3 deletions
149
completions.d.ts
vendored
Normal file
149
completions.d.ts
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* Type definitions for script completion functions
|
||||
*
|
||||
* Scripts can export a `getCompletions` function to provide intelligent
|
||||
* shell completion suggestions for their arguments.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* export function getCompletions(ctx: CompletionContext): string[] {
|
||||
* const { current, args } = ctx;
|
||||
*
|
||||
* // Suggest values for --key flag
|
||||
* if (current.startsWith("--key=")) {
|
||||
* return ["--key=~/.ssh/id_rsa", "--key=~/.ssh/id_ed25519"];
|
||||
* }
|
||||
*
|
||||
* // Check what's already provided
|
||||
* const hasForce = args.includes("--force");
|
||||
*
|
||||
* // Return available flags
|
||||
* return ["--force", "--dry-run", "--help"].filter(
|
||||
* flag => flag !== "--force" || !hasForce
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* Context provided to the completion function
|
||||
*/
|
||||
export interface CompletionContext {
|
||||
/**
|
||||
* All arguments provided so far (excluding the script name)
|
||||
*
|
||||
* @example
|
||||
* If user typed: `scripts my-script --flag1 value1 --flag2`
|
||||
* Then args would be: ["--flag1", "value1", "--flag2"]
|
||||
*/
|
||||
args: string[];
|
||||
|
||||
/**
|
||||
* The current word being completed
|
||||
*
|
||||
* @example
|
||||
* - User typed `--k` and pressed TAB → current = "--k"
|
||||
* - User typed `--key=` and pressed TAB → current = "--key="
|
||||
* - User typed `--port=22` and pressed TAB → current = "--port=22"
|
||||
*/
|
||||
current: string;
|
||||
|
||||
/**
|
||||
* Position of the current word in the args array (0-indexed)
|
||||
*
|
||||
* @example
|
||||
* If user typed: `scripts my-script arg1 arg2 <TAB>`
|
||||
* Then position would be 2 (0=arg1, 1=arg2, 2=current)
|
||||
*/
|
||||
position: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completion function that scripts should export
|
||||
*
|
||||
* This function is called by the loader daemon when the user requests
|
||||
* shell completions. It should return an array of completion suggestions
|
||||
* based on the provided context.
|
||||
*
|
||||
* Can be synchronous or asynchronous (return Promise<string[]>).
|
||||
*
|
||||
* @param ctx - Completion context with current state
|
||||
* @returns Array of completion suggestions (can be empty), or Promise resolving to array
|
||||
*
|
||||
* @remarks
|
||||
* - Return empty array if no completions are available
|
||||
* - Suggestions are filtered by the shell, so return all matches
|
||||
* - Can include partial matches (e.g., "--key=" to prompt for value)
|
||||
* - Keep execution fast (< 50ms recommended) for responsive UX
|
||||
* - Use Deno APIs to discover filesystem, environment, etc.
|
||||
* - Async functions enable API calls, but keep them fast
|
||||
*
|
||||
* @example Simple flag completions
|
||||
* ```typescript
|
||||
* export function getCompletions(ctx: CompletionContext): string[] {
|
||||
* return ["--verbose", "--quiet", "--help"];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example Async completions with API call
|
||||
* ```typescript
|
||||
* export async function getCompletions(ctx: CompletionContext): Promise<string[]> {
|
||||
* const { current } = ctx;
|
||||
*
|
||||
* if (current.startsWith("--branch=")) {
|
||||
* try {
|
||||
* const response = await fetch("https://api.github.com/repos/user/repo/branches");
|
||||
* const branches = await response.json();
|
||||
* return branches.map(b => `--branch=${b.name}`);
|
||||
* } catch {
|
||||
* return ["--branch=main"];
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* return ["--branch=", "--help"];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example Context-aware completions
|
||||
* ```typescript
|
||||
* export function getCompletions(ctx: CompletionContext): string[] {
|
||||
* const { current, args } = ctx;
|
||||
*
|
||||
* // Complete values for flags
|
||||
* if (current.startsWith("--output=")) {
|
||||
* return ["--output=json", "--output=yaml", "--output=text"];
|
||||
* }
|
||||
*
|
||||
* // Avoid duplicate flags
|
||||
* const usedFlags = new Set(args.map(a => a.split("=")[0]));
|
||||
*
|
||||
* const allFlags = ["--output=", "--verbose", "--format="];
|
||||
* return allFlags.filter(f => !usedFlags.has(f.replace("=", "")));
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example Dynamic completions with filesystem
|
||||
* ```typescript
|
||||
* export function getCompletions(ctx: CompletionContext): string[] {
|
||||
* const { current } = ctx;
|
||||
*
|
||||
* if (current.startsWith("--config=")) {
|
||||
* try {
|
||||
* // Find config files in current directory
|
||||
* const files: string[] = [];
|
||||
* for (const entry of Deno.readDirSync(".")) {
|
||||
* if (entry.isFile && entry.name.endsWith(".json")) {
|
||||
* files.push(`--config=${entry.name}`);
|
||||
* }
|
||||
* }
|
||||
* return files;
|
||||
* } catch {
|
||||
* return ["--config=config.json"];
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* return ["--config=", "--help"];
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type CompletionFunction = (ctx: CompletionContext) => string[] | Promise<string[]>;
|
||||
Loading…
Add table
Add a link
Reference in a new issue