@speedkit/cli 4.16.0 → 4.17.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [4.17.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.16.0...v4.17.0) (2026-07-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * extend autocomplete for fish ([b9d1fcb](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/b9d1fcb2fb921ad91895b69aad8f02206ca09cbf))
7
+
1
8
  # [4.16.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.15.1...v4.16.0) (2026-07-13)
2
9
 
3
10
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @speedkit/cli
21
21
  $ sk COMMAND
22
22
  running command...
23
23
  $ sk (--version)
24
- @speedkit/cli/4.16.0 linux-x64 node-v22.23.1
24
+ @speedkit/cli/4.17.0 linux-x64 node-v22.23.1
25
25
  $ sk --help [COMMAND]
26
26
  USAGE
27
27
  $ sk COMMAND
@@ -67,6 +67,7 @@ By either...
67
67
  <!-- commands -->
68
68
  * [`sk auto-prewarm CUSTOMERPATH`](#sk-auto-prewarm-customerpath)
69
69
  * [`sk autocomplete [SHELL]`](#sk-autocomplete-shell)
70
+ * [`sk autocomplete fish`](#sk-autocomplete-fish)
70
71
  * [`sk build-parameter-query CUSTOMERPATH`](#sk-build-parameter-query-customerpath)
71
72
  * [`sk build-prewarm-query CUSTOMERPATH`](#sk-build-prewarm-query-customerpath)
72
73
  * [`sk config edit`](#sk-config-edit)
@@ -146,6 +147,26 @@ EXAMPLES
146
147
 
147
148
  _See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.2.49/src/commands/autocomplete/index.ts)_
148
149
 
150
+ ## `sk autocomplete fish`
151
+
152
+ Generate fish shell completions and install them to ~/.config/fish/completions.
153
+
154
+ ```
155
+ USAGE
156
+ $ sk autocomplete fish [-p]
157
+
158
+ FLAGS
159
+ -p, --print Print the completion script to stdout instead of installing it
160
+
161
+ DESCRIPTION
162
+ Generate fish shell completions and install them to ~/.config/fish/completions.
163
+
164
+ EXAMPLES
165
+ $ sk autocomplete fish
166
+
167
+ $ sk autocomplete fish --print > sk.fish
168
+ ```
169
+
149
170
  ## `sk build-parameter-query CUSTOMERPATH`
150
171
 
151
172
  Build a parameter query that can be executed in Athena
@@ -0,0 +1,19 @@
1
+ import { Command } from "@oclif/core";
2
+ /**
3
+ * Fish is not supported by @oclif/plugin-autocomplete (only bash, zsh and
4
+ * powershell), so this command generates a fish completion script from the
5
+ * oclif command manifest and installs it to ~/.config/fish/completions,
6
+ * where fish picks it up automatically.
7
+ */
8
+ export default class AutocompleteFish extends Command {
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ print: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ };
14
+ run(): Promise<void>;
15
+ private generateCompletionScript;
16
+ private topicDescription;
17
+ private summarize;
18
+ private escape;
19
+ }
@@ -0,0 +1,140 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ /**
6
+ * Fish is not supported by @oclif/plugin-autocomplete (only bash, zsh and
7
+ * powershell), so this command generates a fish completion script from the
8
+ * oclif command manifest and installs it to ~/.config/fish/completions,
9
+ * where fish picks it up automatically.
10
+ */
11
+ export default class AutocompleteFish extends Command {
12
+ static description = "Generate fish shell completions and install them to ~/.config/fish/completions.";
13
+ static examples = [
14
+ "$ sk autocomplete fish",
15
+ "$ sk autocomplete fish --print > sk.fish",
16
+ ];
17
+ static flags = {
18
+ print: Flags.boolean({
19
+ char: "p",
20
+ description: "Print the completion script to stdout instead of installing it",
21
+ }),
22
+ };
23
+ async run() {
24
+ const { flags } = await this.parse(AutocompleteFish);
25
+ const script = this.generateCompletionScript();
26
+ if (flags.print) {
27
+ this.log(script);
28
+ return;
29
+ }
30
+ const completionsDir = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "fish", "completions");
31
+ const completionFile = path.join(completionsDir, `${this.config.bin}.fish`);
32
+ await mkdir(completionsDir, { recursive: true });
33
+ await writeFile(completionFile, script);
34
+ this.log(`Fish completions written to ${completionFile}`);
35
+ this.log("New fish sessions pick them up automatically. Rerun this command after updating the CLI.");
36
+ }
37
+ generateCompletionScript() {
38
+ const { bin } = this.config;
39
+ const fn = bin.replaceAll("-", "_");
40
+ const commands = this.config.commands
41
+ .filter((command) => !command.hidden)
42
+ .sort((a, b) => a.id.localeCompare(b.id));
43
+ const lines = [
44
+ `# Fish completions for ${bin} — generated by \`${bin} autocomplete fish\`. Do not edit by hand.`,
45
+ "",
46
+ `function __${fn}_at_level`,
47
+ " test (count (commandline -opc)) -eq $argv[1]",
48
+ "end",
49
+ "",
50
+ `function __${fn}_using_command`,
51
+ " set -l cmd (commandline -opc)",
52
+ " set -e cmd[1]",
53
+ " set -l expected (string split ' ' -- $argv[1])",
54
+ " test (count $cmd) -ge (count $expected); or return 1",
55
+ " for i in (seq (count $expected))",
56
+ ' test "$cmd[$i]" = "$expected[$i]"; or return 1',
57
+ " end",
58
+ "end",
59
+ "",
60
+ ];
61
+ // Command and topic name completions, e.g. `sk dep<TAB>` or `sk config <TAB>`.
62
+ const seen = new Set();
63
+ for (const command of commands) {
64
+ const tokens = command.id.split(":");
65
+ for (let depth = 1; depth <= tokens.length; depth++) {
66
+ const token = tokens[depth - 1];
67
+ const prefix = tokens.slice(0, depth - 1);
68
+ const key = `${prefix.join(" ")} ${token}`;
69
+ if (seen.has(key))
70
+ continue;
71
+ seen.add(key);
72
+ const description = depth === tokens.length
73
+ ? this.summarize(command.summary ?? command.description)
74
+ : this.topicDescription([...prefix, token].join(":"));
75
+ const condition = depth === 1
76
+ ? `__${fn}_at_level 1`
77
+ : `__${fn}_at_level ${depth}; and __${fn}_using_command '${prefix.join(" ")}'`;
78
+ let entry = `complete -f -c ${bin} -n "${condition}" -a ${token}`;
79
+ if (description)
80
+ entry += ` -d '${this.escape(description)}'`;
81
+ lines.push(entry);
82
+ }
83
+ }
84
+ // Flag and positional-argument completions per command.
85
+ for (const command of commands) {
86
+ const tokens = command.id.split(":");
87
+ const commandPath = tokens.join(" ");
88
+ // Don't offer a parent command's flags while a subcommand is being used,
89
+ // e.g. `sk autocomplete fish` must not suggest `--refresh-cache`.
90
+ const childPaths = new Set(commands
91
+ .filter((other) => other.id.startsWith(`${command.id}:`))
92
+ .map((other) => other.id
93
+ .split(":")
94
+ .slice(0, tokens.length + 1)
95
+ .join(" ")));
96
+ const condition = [
97
+ `__${fn}_using_command '${commandPath}'`,
98
+ ...[...childPaths].map((child) => `and not __${fn}_using_command '${child}'`),
99
+ ].join("; ");
100
+ for (const [name, flag] of Object.entries(command.flags ?? {})) {
101
+ if (flag.hidden)
102
+ continue;
103
+ let entry = `complete -c ${bin} -n "${condition}" -l ${name}`;
104
+ if (flag.char)
105
+ entry += ` -s ${flag.char}`;
106
+ if (flag.type === "option") {
107
+ entry += flag.options?.length
108
+ ? ` -rfa '${flag.options.join(" ")}'`
109
+ : " -r";
110
+ }
111
+ const description = this.summarize(flag.summary ?? flag.description);
112
+ if (description)
113
+ entry += ` -d '${this.escape(description)}'`;
114
+ lines.push(entry);
115
+ }
116
+ // Args with a fixed option set, e.g. the SHELL arg of `sk autocomplete`.
117
+ Object.values(command.args ?? {}).forEach((arg, index) => {
118
+ if (arg.hidden || !arg.options?.length)
119
+ return;
120
+ const level = tokens.length + 1 + index;
121
+ lines.push(`complete -f -c ${bin} -n "__${fn}_at_level ${level}; and ${condition}" -a '${arg.options.join(" ")}'`);
122
+ });
123
+ }
124
+ return lines.join("\n") + "\n";
125
+ }
126
+ topicDescription(topicName) {
127
+ const topic = this.config.topics.find(({ name }) => name === topicName);
128
+ return this.summarize(topic?.description);
129
+ }
130
+ summarize(text) {
131
+ // Plugin descriptions may contain ejs templates, e.g. "Display help for <%= config.bin %>."
132
+ return (text ?? "")
133
+ .split("\n")[0]
134
+ .replaceAll("<%= config.bin %>", this.config.bin)
135
+ .trim();
136
+ }
137
+ escape(text) {
138
+ return text.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
139
+ }
140
+ }
@@ -822,6 +822,39 @@
822
822
  "wpt-launcher.js"
823
823
  ]
824
824
  },
825
+ "autocomplete:fish": {
826
+ "aliases": [],
827
+ "args": {},
828
+ "description": "Generate fish shell completions and install them to ~/.config/fish/completions.",
829
+ "examples": [
830
+ "$ sk autocomplete fish",
831
+ "$ sk autocomplete fish --print > sk.fish"
832
+ ],
833
+ "flags": {
834
+ "print": {
835
+ "char": "p",
836
+ "description": "Print the completion script to stdout instead of installing it",
837
+ "name": "print",
838
+ "allowNo": false,
839
+ "type": "boolean"
840
+ }
841
+ },
842
+ "hasDynamicHelp": false,
843
+ "hiddenAliases": [],
844
+ "id": "autocomplete:fish",
845
+ "pluginAlias": "@speedkit/cli",
846
+ "pluginName": "@speedkit/cli",
847
+ "pluginType": "core",
848
+ "strict": true,
849
+ "enableJsonFlag": false,
850
+ "isESM": true,
851
+ "relativePath": [
852
+ "dist",
853
+ "commands",
854
+ "autocomplete",
855
+ "fish.js"
856
+ ]
857
+ },
825
858
  "config:edit": {
826
859
  "aliases": [],
827
860
  "args": {},
@@ -847,5 +880,5 @@
847
880
  ]
848
881
  }
849
882
  },
850
- "version": "4.16.0"
883
+ "version": "4.17.0"
851
884
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@speedkit/cli",
3
3
  "description": "Speed Kit CLI",
4
- "version": "4.16.0",
4
+ "version": "4.17.0",
5
5
  "author": {
6
6
  "name": "Baqend.com",
7
7
  "email": "info@baqend.com"