From 37567aa0c32dc33fa69a4cf4c182185b94e54d7d Mon Sep 17 00:00:00 2001 From: adityabhatkar23 Date: Sun, 31 May 2026 12:40:09 +0530 Subject: [PATCH] feat: add bash shell completions --- README.md | 13 ++++++ src/index.ts | 86 +++++++++++++++++++++++++++++++++++++++ test/completions.test.mjs | 20 +++++++++ 3 files changed, 119 insertions(+) create mode 100644 test/completions.test.mjs diff --git a/README.md b/README.md index c323c62..04aa935 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ Status of every feature shipped. ✅ = implemented, ⬜ = roadmap. Section ancho - ✅ JSON (default — pipeable to `jq`) - ✅ `-H` / `--human` table mode (human-readable) - ✅ `-q` / `--quiet` mode (exit code only) +- ✅ `completions bash` — generate Bash shell completions ### Quality (v0.4) - ✅ 60+ tests `node:test` suite ([`test/`](./test/)) running against [`test/draft_content.json`](./test/draft_content.json) @@ -566,6 +567,18 @@ $ capcut set-text ./project a1b2c3 "Hey everyone" - `1:30` -- 1 minute 30 seconds - `0:05.5` -- 5.5 seconds +### Shell completions + +Generate a Bash completion script: + +Install permanently: + +```bash +capcut completions bash >> ~/.bashrc +``` + +Completes command names and global flags (`--jianying`, `-H`/`--human`, `-q`/`--quiet`, `-v`/`--version`). + ## How it works CapCut stores projects as JSON (`draft_content.json` on Windows, `draft_info.json` on macOS). This CLI reads and modifies that JSON directly. It preserves the original file's indentation style on save. diff --git a/src/index.ts b/src/index.ts index 1f987c1..b471d57 100644 --- a/src/index.ts +++ b/src/index.ts @@ -79,6 +79,63 @@ import { formatDuration, formatTime, parseTimeInput, srtTime } from "./time.js"; import { translateDraft } from "./translate.js"; import { detectVersion } from "./version.js"; +export const COMMANDS = [ + "info", + "version", + "lint", + "tracks", + "segments", + "texts", + "set-text", + "shift", + "shift-all", + "speed", + "volume", + "trim", + "opacity", + "export-srt", + "materials", + "segment", + "material", + "add-audio", + "add-video", + "add-text", + "cut", + "keyframe", + "transition", + "mask", + "bg-blur", + "text-style", + "text-anim", + "image-anim", + "add-sticker", + "mix-mode", + "audio-fade", + "add-cover", + "add-filter", + "bubble-text", + "add-effect", + "save-template", + "apply-template", + "batch", + "import-srt", + "import-ass", + "text-ranges", + "caption", + "translate", + "migrate", + "add-sfx", + "chroma", + "enums", + "doctor", + "serve", + "decrypt", + "export", + "init", +] as const; + +const GLOBAL_FLAGS = ["--jianying", "-H", "--human", "-q", "--quiet", "-v", "--version"] as const; + const HELP = `capcut-cli -- fast edits to CapCut projects Usage: capcut [options] @@ -478,6 +535,23 @@ const ENUM_FLAG_MAP: Array<{ flag: string; category: Category }> = [ { flag: "--filters", category: "filters" }, ]; +function bashCompletion(): string { + const words = [...COMMANDS, ...GLOBAL_FLAGS].join(" "); + + return `# bash completion for capcut + +_capcut() +{ + local cur + cur="\${COMP_WORDS[COMP_CWORD]}" + + COMPREPLY=( $(compgen -W "${words}" -- "$cur") ) +} + +complete -F _capcut capcut +`; +} + function parseFlags(args: string[]): { positional: string[]; flags: Flags } { const positional: string[] = []; const flags: Flags = { human: false, quiet: false, batch: false }; @@ -1996,6 +2070,18 @@ async function main(): Promise { process.exit(0); } const cmd = positional[0]; + + if (cmd === "completions") { + const shell = positional[1]; + + if (shell !== "bash") { + die("Usage: capcut completions bash"); + } + + process.stdout.write(bashCompletion()); + process.exit(0); + } + const projectPath = positional[1]; // `enums` is a pure lookup — no project needed. diff --git a/test/completions.test.mjs b/test/completions.test.mjs new file mode 100644 index 0000000..aa04c4a --- /dev/null +++ b/test/completions.test.mjs @@ -0,0 +1,20 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; +import { spawnCli } from "./helpers/spawn-cli.mjs"; + +describe("capcut completions bash", () => { + it("prints bash completion script", () => { + const r = spawnCli(["completions", "bash"]); + + assert.equal(r.status, 0); + assert.match(r.stdout, /complete -F _capcut capcut/); + assert.match(r.stdout, /info/); + assert.match(r.stdout, /tracks/); + assert.match(r.stdout, /--jianying/); + assert.match(r.stdout, /--quiet/); + assert.match(r.stdout, /--version/); + assert.match(r.stdout, /-H/); + assert.match(r.stdout, /-q/); + assert.match(r.stdout, /-v/); + }); +});