zinkee 0.1.10 → 0.1.12
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/.claude/settings.local.json +29 -1
- package/AGENTS.md +1 -0
- package/README.md +12 -0
- package/dist/index.js +748 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli-examples.ts +18 -36
- package/src/cli-runner.ts +28 -2
- package/src/completion/engine.test.ts +138 -0
- package/src/completion/engine.ts +168 -0
- package/src/completion/install.test.ts +179 -0
- package/src/completion/install.ts +260 -0
- package/src/completion/runtime.ts +150 -0
- package/src/completion/scripts.ts +91 -0
- package/src/index.test.ts +52 -0
- package/src/program.ts +3 -1
- package/src/utils/argv-rewrite.test.ts +149 -0
- package/src/utils/argv-rewrite.ts +269 -0
package/dist/index.js
CHANGED
|
@@ -732,6 +732,197 @@ var commandRegistry = {
|
|
|
732
732
|
}
|
|
733
733
|
};
|
|
734
734
|
|
|
735
|
+
// src/utils/argv-rewrite.ts
|
|
736
|
+
var HOISTABLE_GLOBAL_FLAGS = {
|
|
737
|
+
"--profile": "value",
|
|
738
|
+
"--base-url": "value",
|
|
739
|
+
"--api-key": "value",
|
|
740
|
+
"--read-only": "boolean",
|
|
741
|
+
"--json": "boolean"
|
|
742
|
+
};
|
|
743
|
+
function isHoistableFlag(flagName) {
|
|
744
|
+
return Object.hasOwn(HOISTABLE_GLOBAL_FLAGS, flagName);
|
|
745
|
+
}
|
|
746
|
+
function getCommandPath(command) {
|
|
747
|
+
const parts = [];
|
|
748
|
+
let cursor = command;
|
|
749
|
+
while (cursor && cursor.parent) {
|
|
750
|
+
parts.unshift(cursor.name());
|
|
751
|
+
cursor = cursor.parent;
|
|
752
|
+
}
|
|
753
|
+
return parts.join(" ");
|
|
754
|
+
}
|
|
755
|
+
function buildOptionMetadata(program) {
|
|
756
|
+
const valueFlags = /* @__PURE__ */ new Set();
|
|
757
|
+
const booleanFlags = /* @__PURE__ */ new Set();
|
|
758
|
+
const localOptionsByCommand = /* @__PURE__ */ new Map();
|
|
759
|
+
function visit(command, isRoot) {
|
|
760
|
+
const localFlags = /* @__PURE__ */ new Set();
|
|
761
|
+
for (const option of command.options) {
|
|
762
|
+
if (!option.long) continue;
|
|
763
|
+
if (option.required || option.optional) {
|
|
764
|
+
valueFlags.add(option.long);
|
|
765
|
+
} else {
|
|
766
|
+
booleanFlags.add(option.long);
|
|
767
|
+
}
|
|
768
|
+
if (!isRoot) {
|
|
769
|
+
localFlags.add(option.long);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (!isRoot) {
|
|
773
|
+
localOptionsByCommand.set(getCommandPath(command), localFlags);
|
|
774
|
+
}
|
|
775
|
+
for (const sub of command.commands) {
|
|
776
|
+
visit(sub, false);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
visit(program, true);
|
|
780
|
+
return { valueFlags, booleanFlags, localOptionsByCommand };
|
|
781
|
+
}
|
|
782
|
+
function createDefaultOptionMetadata() {
|
|
783
|
+
return {
|
|
784
|
+
valueFlags: /* @__PURE__ */ new Set([
|
|
785
|
+
"--profile",
|
|
786
|
+
"--base-url",
|
|
787
|
+
"--api-key",
|
|
788
|
+
"--install-completion",
|
|
789
|
+
"--show-completion"
|
|
790
|
+
]),
|
|
791
|
+
booleanFlags: /* @__PURE__ */ new Set([
|
|
792
|
+
"--read-only",
|
|
793
|
+
"--json",
|
|
794
|
+
"--example",
|
|
795
|
+
"--help"
|
|
796
|
+
]),
|
|
797
|
+
localOptionsByCommand: /* @__PURE__ */ new Map()
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
function isFlagToken(token) {
|
|
801
|
+
return token.length > 1 && token.startsWith("-");
|
|
802
|
+
}
|
|
803
|
+
function splitFlagToken(token) {
|
|
804
|
+
if (token.startsWith("--") && token.includes("=")) {
|
|
805
|
+
const eqIdx = token.indexOf("=");
|
|
806
|
+
return {
|
|
807
|
+
flagName: token.slice(0, eqIdx),
|
|
808
|
+
inlineValue: token.slice(eqIdx + 1)
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
return { flagName: token, inlineValue: void 0 };
|
|
812
|
+
}
|
|
813
|
+
function flagConsumesValue(flagName, optionMeta) {
|
|
814
|
+
if (optionMeta.valueFlags.has(flagName)) {
|
|
815
|
+
return true;
|
|
816
|
+
}
|
|
817
|
+
if (optionMeta.booleanFlags.has(flagName)) {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
return true;
|
|
821
|
+
}
|
|
822
|
+
function collectOperandTokens(userArgs, optionMeta, options = {}) {
|
|
823
|
+
const operands = [];
|
|
824
|
+
let i = 0;
|
|
825
|
+
while (i < userArgs.length) {
|
|
826
|
+
const token = userArgs[i];
|
|
827
|
+
if (token === "--") {
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
if (isFlagToken(token)) {
|
|
831
|
+
const { flagName, inlineValue } = splitFlagToken(token);
|
|
832
|
+
if (options.skipFlag !== void 0 && flagName === options.skipFlag) {
|
|
833
|
+
i += 1;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (inlineValue !== void 0) {
|
|
837
|
+
i += 1;
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
const consumes = flagConsumesValue(flagName, optionMeta);
|
|
841
|
+
const next = userArgs[i + 1];
|
|
842
|
+
if (consumes && next !== void 0 && !isFlagToken(next)) {
|
|
843
|
+
i += 2;
|
|
844
|
+
} else {
|
|
845
|
+
i += 1;
|
|
846
|
+
}
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
operands.push({ index: i, token });
|
|
850
|
+
i += 1;
|
|
851
|
+
}
|
|
852
|
+
return operands;
|
|
853
|
+
}
|
|
854
|
+
function findCommandPath(operands, registry) {
|
|
855
|
+
if (operands.length === 0) return null;
|
|
856
|
+
const commandsByLength = Object.keys(registry).sort(
|
|
857
|
+
(left, right) => right.split(" ").length - left.split(" ").length
|
|
858
|
+
);
|
|
859
|
+
for (let start = 0; start < operands.length; start += 1) {
|
|
860
|
+
for (const commandName of commandsByLength) {
|
|
861
|
+
const parts = commandName.split(" ");
|
|
862
|
+
if (start + parts.length > operands.length) continue;
|
|
863
|
+
let matches = true;
|
|
864
|
+
for (let j = 0; j < parts.length; j += 1) {
|
|
865
|
+
if (operands[start + j].token !== parts[j]) {
|
|
866
|
+
matches = false;
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (matches) {
|
|
871
|
+
return {
|
|
872
|
+
commandName,
|
|
873
|
+
firstArgvIndex: operands[start].index,
|
|
874
|
+
lastArgvIndex: operands[start + parts.length - 1].index
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
function hoistGlobalOptions(argv, optionMeta, registry) {
|
|
882
|
+
if (argv.length < 3) return [...argv];
|
|
883
|
+
const userArgs = argv.slice(2);
|
|
884
|
+
const operands = collectOperandTokens(userArgs, optionMeta);
|
|
885
|
+
const cmdPath = findCommandPath(operands, registry);
|
|
886
|
+
if (!cmdPath) return [...argv];
|
|
887
|
+
const localOptions = optionMeta.localOptionsByCommand.get(cmdPath.commandName) ?? /* @__PURE__ */ new Set();
|
|
888
|
+
const hoisted = [];
|
|
889
|
+
const remaining = [];
|
|
890
|
+
let i = 0;
|
|
891
|
+
while (i < userArgs.length) {
|
|
892
|
+
const token = userArgs[i];
|
|
893
|
+
if (token === "--") {
|
|
894
|
+
remaining.push(...userArgs.slice(i));
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
if (!isFlagToken(token)) {
|
|
898
|
+
remaining.push(token);
|
|
899
|
+
i += 1;
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
const { flagName, inlineValue } = splitFlagToken(token);
|
|
903
|
+
if (!isHoistableFlag(flagName)) {
|
|
904
|
+
remaining.push(token);
|
|
905
|
+
i += 1;
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
const isAfterCommandPath = i > cmdPath.lastArgvIndex;
|
|
909
|
+
const collidesWithLocal = localOptions.has(flagName);
|
|
910
|
+
const target = isAfterCommandPath && collidesWithLocal ? remaining : hoisted;
|
|
911
|
+
target.push(token);
|
|
912
|
+
const spec = HOISTABLE_GLOBAL_FLAGS[flagName];
|
|
913
|
+
if (spec === "value" && inlineValue === void 0) {
|
|
914
|
+
const next = userArgs[i + 1];
|
|
915
|
+
if (next !== void 0 && !isFlagToken(next)) {
|
|
916
|
+
target.push(next);
|
|
917
|
+
i += 2;
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
i += 1;
|
|
922
|
+
}
|
|
923
|
+
return [argv[0], argv[1], ...hoisted, ...remaining];
|
|
924
|
+
}
|
|
925
|
+
|
|
735
926
|
// src/client.ts
|
|
736
927
|
var ApiClientError = class extends Error {
|
|
737
928
|
name = "ApiClientError";
|
|
@@ -6392,26 +6583,6 @@ async function getResolvedCommandExamples(commandName, filters = {}, options = {
|
|
|
6392
6583
|
function getStdout(stdout) {
|
|
6393
6584
|
return stdout ?? process.stdout;
|
|
6394
6585
|
}
|
|
6395
|
-
function resolveExampleCommand(tokens) {
|
|
6396
|
-
const commands = Object.keys(commandRegistry).sort(
|
|
6397
|
-
(left, right) => right.split(" ").length - left.split(" ").length
|
|
6398
|
-
);
|
|
6399
|
-
for (const commandName of commands) {
|
|
6400
|
-
const commandTokens2 = commandName.split(" ");
|
|
6401
|
-
const matches = commandTokens2.every((token, index) => tokens[index] === token);
|
|
6402
|
-
if (matches) {
|
|
6403
|
-
return {
|
|
6404
|
-
commandName,
|
|
6405
|
-
remainingTokens: tokens.slice(commandTokens2.length)
|
|
6406
|
-
};
|
|
6407
|
-
}
|
|
6408
|
-
}
|
|
6409
|
-
const commandTokens = tokens.filter((token) => !token.startsWith("-"));
|
|
6410
|
-
return {
|
|
6411
|
-
commandName: commandTokens.join(" "),
|
|
6412
|
-
remainingTokens: []
|
|
6413
|
-
};
|
|
6414
|
-
}
|
|
6415
6586
|
function extractExampleFilters(tokens) {
|
|
6416
6587
|
const filters = {};
|
|
6417
6588
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
@@ -6451,18 +6622,19 @@ function extractExampleOverrides(tokens) {
|
|
|
6451
6622
|
}
|
|
6452
6623
|
return overrides;
|
|
6453
6624
|
}
|
|
6454
|
-
function extractExampleRequest(argv) {
|
|
6455
|
-
|
|
6456
|
-
if (exampleIndex === -1) {
|
|
6625
|
+
function extractExampleRequest(argv, optionMeta = createDefaultOptionMetadata()) {
|
|
6626
|
+
if (!argv.includes("--example")) {
|
|
6457
6627
|
return null;
|
|
6458
6628
|
}
|
|
6459
|
-
const
|
|
6460
|
-
const
|
|
6461
|
-
|
|
6629
|
+
const userArgs = argv.slice(2);
|
|
6630
|
+
const operands = collectOperandTokens(userArgs, optionMeta, { skipFlag: "--example" });
|
|
6631
|
+
const cmdPath = findCommandPath(operands, commandRegistry);
|
|
6632
|
+
if (!cmdPath) {
|
|
6462
6633
|
throw new CliError("invalid_cli_usage", 'Provide a command after "--example".');
|
|
6463
6634
|
}
|
|
6635
|
+
const remainingTokens = userArgs.slice(cmdPath.lastArgvIndex + 1);
|
|
6464
6636
|
return {
|
|
6465
|
-
commandName,
|
|
6637
|
+
commandName: cmdPath.commandName,
|
|
6466
6638
|
filters: extractExampleFilters(remainingTokens),
|
|
6467
6639
|
overrides: extractExampleOverrides(argv),
|
|
6468
6640
|
json: argv.includes("--json")
|
|
@@ -10961,7 +11133,7 @@ var require2 = createRequire(import.meta.url);
|
|
|
10961
11133
|
var { version } = require2("../package.json");
|
|
10962
11134
|
function buildProgram(options = {}) {
|
|
10963
11135
|
const program = new Command10();
|
|
10964
|
-
program.name("zinkee").description("CLI for Zinkee API v2").version(version).enablePositionalOptions().showHelpAfterError().option("--profile <name>", "select a profile from config").option("--base-url <url>", "override the configured base URL").option("--api-key <token>", "override the configured API key").option("--read-only", "run in read-only mode").option("--example", "show examples for a command").option("--json", "emit JSON output");
|
|
11136
|
+
program.name("zinkee").description("CLI for Zinkee API v2").version(version).enablePositionalOptions().showHelpAfterError().option("--profile <name>", "select a profile from config").option("--base-url <url>", "override the configured base URL").option("--api-key <token>", "override the configured API key").option("--read-only", "run in read-only mode").option("--example", "show examples for a command").option("--json", "emit JSON output").option("--install-completion [shell]", "install shell completion (bash, zsh, fish, powershell)").option("--show-completion [shell]", "print shell completion script for the current shell");
|
|
10965
11137
|
registerProfilesCommands(program, options);
|
|
10966
11138
|
registerAutomationsCommands(program, options);
|
|
10967
11139
|
registerConfigCommands(program, options);
|
|
@@ -10976,6 +11148,539 @@ function buildProgram(options = {}) {
|
|
|
10976
11148
|
return program;
|
|
10977
11149
|
}
|
|
10978
11150
|
|
|
11151
|
+
// src/completion/engine.ts
|
|
11152
|
+
function readProfileNamesFromConfig(configPath) {
|
|
11153
|
+
try {
|
|
11154
|
+
const config = readConfigIfExists(configPath);
|
|
11155
|
+
if (!config) {
|
|
11156
|
+
return [];
|
|
11157
|
+
}
|
|
11158
|
+
return Object.keys(config.profiles).sort();
|
|
11159
|
+
} catch {
|
|
11160
|
+
return [];
|
|
11161
|
+
}
|
|
11162
|
+
}
|
|
11163
|
+
function findSubcommand(command, name) {
|
|
11164
|
+
return command.commands.find((sub) => {
|
|
11165
|
+
if (sub.name() === name) {
|
|
11166
|
+
return true;
|
|
11167
|
+
}
|
|
11168
|
+
const aliases = sub.aliases();
|
|
11169
|
+
return aliases.includes(name);
|
|
11170
|
+
});
|
|
11171
|
+
}
|
|
11172
|
+
function collectFlagsThatTakeValues(root) {
|
|
11173
|
+
const flags = /* @__PURE__ */ new Set();
|
|
11174
|
+
const walk = (command) => {
|
|
11175
|
+
for (const option of command.options) {
|
|
11176
|
+
if (!option.required && !option.optional) {
|
|
11177
|
+
continue;
|
|
11178
|
+
}
|
|
11179
|
+
if (option.long) {
|
|
11180
|
+
flags.add(option.long);
|
|
11181
|
+
}
|
|
11182
|
+
if (option.short) {
|
|
11183
|
+
flags.add(option.short);
|
|
11184
|
+
}
|
|
11185
|
+
}
|
|
11186
|
+
for (const sub of command.commands) {
|
|
11187
|
+
walk(sub);
|
|
11188
|
+
}
|
|
11189
|
+
};
|
|
11190
|
+
walk(root);
|
|
11191
|
+
return flags;
|
|
11192
|
+
}
|
|
11193
|
+
function descend(root, words) {
|
|
11194
|
+
const flagsWithValues = collectFlagsThatTakeValues(root);
|
|
11195
|
+
let current = root;
|
|
11196
|
+
let index = 0;
|
|
11197
|
+
while (index < words.length) {
|
|
11198
|
+
const word = words[index];
|
|
11199
|
+
if (word.startsWith("-")) {
|
|
11200
|
+
const [flagPart] = word.split("=", 1);
|
|
11201
|
+
if (flagPart && flagsWithValues.has(flagPart) && !word.includes("=")) {
|
|
11202
|
+
index += 2;
|
|
11203
|
+
} else {
|
|
11204
|
+
index += 1;
|
|
11205
|
+
}
|
|
11206
|
+
continue;
|
|
11207
|
+
}
|
|
11208
|
+
const next = findSubcommand(current, word);
|
|
11209
|
+
if (!next) {
|
|
11210
|
+
break;
|
|
11211
|
+
}
|
|
11212
|
+
current = next;
|
|
11213
|
+
index += 1;
|
|
11214
|
+
}
|
|
11215
|
+
return current;
|
|
11216
|
+
}
|
|
11217
|
+
function collectVisibleSubcommands(command) {
|
|
11218
|
+
const names = [];
|
|
11219
|
+
for (const sub of command.commands) {
|
|
11220
|
+
if (sub.name().startsWith("__")) {
|
|
11221
|
+
continue;
|
|
11222
|
+
}
|
|
11223
|
+
names.push(sub.name());
|
|
11224
|
+
}
|
|
11225
|
+
return names;
|
|
11226
|
+
}
|
|
11227
|
+
function collectVisibleLongFlags(command) {
|
|
11228
|
+
const flags = [];
|
|
11229
|
+
let current = command;
|
|
11230
|
+
while (current) {
|
|
11231
|
+
for (const option of current.options) {
|
|
11232
|
+
if (option.hidden || !option.long) {
|
|
11233
|
+
continue;
|
|
11234
|
+
}
|
|
11235
|
+
if (!flags.includes(option.long)) {
|
|
11236
|
+
flags.push(option.long);
|
|
11237
|
+
}
|
|
11238
|
+
}
|
|
11239
|
+
current = current.parent;
|
|
11240
|
+
}
|
|
11241
|
+
if (!flags.includes("--help")) {
|
|
11242
|
+
flags.push("--help");
|
|
11243
|
+
}
|
|
11244
|
+
return flags;
|
|
11245
|
+
}
|
|
11246
|
+
function getCompletions(program, request, deps = {}) {
|
|
11247
|
+
const { words, cwordIndex } = request;
|
|
11248
|
+
const safeIndex = Math.max(0, Math.min(cwordIndex, words.length));
|
|
11249
|
+
const current = words[safeIndex] ?? "";
|
|
11250
|
+
const prevToken = safeIndex > 0 ? words[safeIndex - 1] : void 0;
|
|
11251
|
+
const readProfiles = deps.readProfileNames ?? readProfileNamesFromConfig;
|
|
11252
|
+
if (prevToken === "--profile" || prevToken === "-p") {
|
|
11253
|
+
try {
|
|
11254
|
+
return readProfiles(deps.configPath).filter((name) => name.startsWith(current)).slice().sort();
|
|
11255
|
+
} catch {
|
|
11256
|
+
return [];
|
|
11257
|
+
}
|
|
11258
|
+
}
|
|
11259
|
+
const tokensBeforeCurrent = words.slice(1, safeIndex);
|
|
11260
|
+
const command = descend(program, tokensBeforeCurrent);
|
|
11261
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
11262
|
+
if (current.startsWith("-")) {
|
|
11263
|
+
for (const flag of collectVisibleLongFlags(command)) {
|
|
11264
|
+
if (flag.startsWith(current)) {
|
|
11265
|
+
candidates.add(flag);
|
|
11266
|
+
}
|
|
11267
|
+
}
|
|
11268
|
+
return [...candidates].sort();
|
|
11269
|
+
}
|
|
11270
|
+
for (const subcommand of collectVisibleSubcommands(command)) {
|
|
11271
|
+
if (subcommand.startsWith(current)) {
|
|
11272
|
+
candidates.add(subcommand);
|
|
11273
|
+
}
|
|
11274
|
+
}
|
|
11275
|
+
for (const flag of collectVisibleLongFlags(command)) {
|
|
11276
|
+
if (flag.startsWith(current)) {
|
|
11277
|
+
candidates.add(flag);
|
|
11278
|
+
}
|
|
11279
|
+
}
|
|
11280
|
+
return [...candidates].sort();
|
|
11281
|
+
}
|
|
11282
|
+
|
|
11283
|
+
// src/completion/install.ts
|
|
11284
|
+
import { execFileSync } from "child_process";
|
|
11285
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
|
|
11286
|
+
import { homedir as homedir2, platform } from "os";
|
|
11287
|
+
import { basename as basename2, dirname as dirname2, join as join2 } from "path";
|
|
11288
|
+
|
|
11289
|
+
// src/completion/scripts.ts
|
|
11290
|
+
var PROGRAM = "zinkee";
|
|
11291
|
+
var ENV_VAR = "_ZINKEE_COMPLETE";
|
|
11292
|
+
function renderBashScript() {
|
|
11293
|
+
return `_zinkee_completion() {
|
|
11294
|
+
local IFS=$'\\n'
|
|
11295
|
+
local response
|
|
11296
|
+
|
|
11297
|
+
response=$(env COMP_WORDS="\${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD ${ENV_VAR}=bash_complete ${PROGRAM} 2>/dev/null)
|
|
11298
|
+
|
|
11299
|
+
COMPREPLY=( \${response} )
|
|
11300
|
+
return 0
|
|
11301
|
+
}
|
|
11302
|
+
|
|
11303
|
+
complete -o default -F _zinkee_completion ${PROGRAM}
|
|
11304
|
+
`;
|
|
11305
|
+
}
|
|
11306
|
+
function renderZshScript() {
|
|
11307
|
+
return `#compdef ${PROGRAM}
|
|
11308
|
+
|
|
11309
|
+
_zinkee_completion() {
|
|
11310
|
+
local -a response
|
|
11311
|
+
(( ! $+commands[${PROGRAM}] )) && return 1
|
|
11312
|
+
|
|
11313
|
+
response=("\${(@f)$(env COMP_WORDS="\${words[*]}" COMP_CWORD=$((CURRENT - 1)) ${ENV_VAR}=zsh_complete ${PROGRAM} 2>/dev/null)}")
|
|
11314
|
+
|
|
11315
|
+
if [ -n "$response" ]; then
|
|
11316
|
+
compadd -U -V unsorted -a response
|
|
11317
|
+
fi
|
|
11318
|
+
}
|
|
11319
|
+
|
|
11320
|
+
if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
|
|
11321
|
+
_zinkee_completion "$@"
|
|
11322
|
+
else
|
|
11323
|
+
compdef _zinkee_completion ${PROGRAM}
|
|
11324
|
+
fi
|
|
11325
|
+
`;
|
|
11326
|
+
}
|
|
11327
|
+
function renderFishScript() {
|
|
11328
|
+
return `function __zinkee_completion
|
|
11329
|
+
set -l tokens (commandline -opc) (commandline -ct)
|
|
11330
|
+
set -l cword (math (count $tokens) - 1)
|
|
11331
|
+
set -l joined (string join " " -- $tokens)
|
|
11332
|
+
|
|
11333
|
+
env COMP_WORDS="$joined" COMP_CWORD=$cword ${ENV_VAR}=fish_complete ${PROGRAM} 2>/dev/null
|
|
11334
|
+
end
|
|
11335
|
+
|
|
11336
|
+
complete --no-files --command ${PROGRAM} --arguments '(__zinkee_completion)'
|
|
11337
|
+
`;
|
|
11338
|
+
}
|
|
11339
|
+
function renderPowerShellScript() {
|
|
11340
|
+
return `Register-ArgumentCompleter -Native -CommandName ${PROGRAM} -ScriptBlock {
|
|
11341
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
11342
|
+
|
|
11343
|
+
$tokens = @($commandAst.CommandElements | ForEach-Object { $_.ToString() })
|
|
11344
|
+
$cword = [Math]::Max(0, $tokens.Count - 1)
|
|
11345
|
+
|
|
11346
|
+
$env:COMP_WORDS = ($tokens -join ' ')
|
|
11347
|
+
$env:COMP_CWORD = $cword
|
|
11348
|
+
$env:${ENV_VAR} = 'powershell_complete'
|
|
11349
|
+
|
|
11350
|
+
try {
|
|
11351
|
+
& ${PROGRAM} 2>$null | ForEach-Object {
|
|
11352
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
11353
|
+
}
|
|
11354
|
+
} finally {
|
|
11355
|
+
Remove-Item Env:COMP_WORDS -ErrorAction SilentlyContinue
|
|
11356
|
+
Remove-Item Env:COMP_CWORD -ErrorAction SilentlyContinue
|
|
11357
|
+
Remove-Item Env:${ENV_VAR} -ErrorAction SilentlyContinue
|
|
11358
|
+
}
|
|
11359
|
+
}
|
|
11360
|
+
`;
|
|
11361
|
+
}
|
|
11362
|
+
function renderScript(shell) {
|
|
11363
|
+
switch (shell) {
|
|
11364
|
+
case "bash":
|
|
11365
|
+
return renderBashScript();
|
|
11366
|
+
case "zsh":
|
|
11367
|
+
return renderZshScript();
|
|
11368
|
+
case "fish":
|
|
11369
|
+
return renderFishScript();
|
|
11370
|
+
case "powershell":
|
|
11371
|
+
return renderPowerShellScript();
|
|
11372
|
+
}
|
|
11373
|
+
}
|
|
11374
|
+
|
|
11375
|
+
// src/completion/install.ts
|
|
11376
|
+
var SUPPORTED_SHELLS = [
|
|
11377
|
+
"bash",
|
|
11378
|
+
"zsh",
|
|
11379
|
+
"fish",
|
|
11380
|
+
"powershell"
|
|
11381
|
+
];
|
|
11382
|
+
var BLOCK_BEGIN = "# >>> zinkee completion >>>";
|
|
11383
|
+
var BLOCK_END = "# <<< zinkee completion <<<";
|
|
11384
|
+
var CompletionShellError = class extends Error {
|
|
11385
|
+
name = "CompletionShellError";
|
|
11386
|
+
};
|
|
11387
|
+
function defaultPowerShellProfile(env) {
|
|
11388
|
+
for (const exe of ["pwsh", "powershell.exe"]) {
|
|
11389
|
+
try {
|
|
11390
|
+
const out = execFileSync(exe, ["-NoProfile", "-Command", "$PROFILE"], {
|
|
11391
|
+
encoding: "utf8",
|
|
11392
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
11393
|
+
});
|
|
11394
|
+
const path = out.trim();
|
|
11395
|
+
if (path) {
|
|
11396
|
+
return path;
|
|
11397
|
+
}
|
|
11398
|
+
} catch {
|
|
11399
|
+
}
|
|
11400
|
+
}
|
|
11401
|
+
if (env.platform === "win32") {
|
|
11402
|
+
return join2(env.home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
11403
|
+
}
|
|
11404
|
+
return join2(env.home, ".config", "powershell", "Microsoft.PowerShell_profile.ps1");
|
|
11405
|
+
}
|
|
11406
|
+
function detectShell(env) {
|
|
11407
|
+
const fromEnv = env.shellEnv;
|
|
11408
|
+
if (fromEnv) {
|
|
11409
|
+
const name = basename2(fromEnv).toLowerCase().replace(/\.exe$/, "");
|
|
11410
|
+
if (name === "bash") {
|
|
11411
|
+
return "bash";
|
|
11412
|
+
}
|
|
11413
|
+
if (name === "zsh") {
|
|
11414
|
+
return "zsh";
|
|
11415
|
+
}
|
|
11416
|
+
if (name === "fish") {
|
|
11417
|
+
return "fish";
|
|
11418
|
+
}
|
|
11419
|
+
if (name === "pwsh" || name === "powershell") {
|
|
11420
|
+
return "powershell";
|
|
11421
|
+
}
|
|
11422
|
+
}
|
|
11423
|
+
if (env.platform === "win32") {
|
|
11424
|
+
return "powershell";
|
|
11425
|
+
}
|
|
11426
|
+
throw new CompletionShellError(
|
|
11427
|
+
`Could not detect a supported shell from $SHELL=${fromEnv ?? ""}. Pass one of: ${SUPPORTED_SHELLS.join(", ")}.`
|
|
11428
|
+
);
|
|
11429
|
+
}
|
|
11430
|
+
function parseShell(value) {
|
|
11431
|
+
const normalized = value.toLowerCase();
|
|
11432
|
+
if (normalized === "bash" || normalized === "zsh" || normalized === "fish" || normalized === "powershell") {
|
|
11433
|
+
return normalized;
|
|
11434
|
+
}
|
|
11435
|
+
if (normalized === "pwsh") {
|
|
11436
|
+
return "powershell";
|
|
11437
|
+
}
|
|
11438
|
+
throw new CompletionShellError(
|
|
11439
|
+
`Unsupported shell "${value}". Supported: ${SUPPORTED_SHELLS.join(", ")}.`
|
|
11440
|
+
);
|
|
11441
|
+
}
|
|
11442
|
+
function resolveTarget(shell, env) {
|
|
11443
|
+
switch (shell) {
|
|
11444
|
+
case "bash":
|
|
11445
|
+
return {
|
|
11446
|
+
shell,
|
|
11447
|
+
scriptPath: join2(env.home, ".bash_completions", "zinkee.sh"),
|
|
11448
|
+
rcPath: join2(env.home, ".bashrc")
|
|
11449
|
+
};
|
|
11450
|
+
case "zsh":
|
|
11451
|
+
return {
|
|
11452
|
+
shell,
|
|
11453
|
+
scriptPath: join2(env.home, ".zfunc", "_zinkee"),
|
|
11454
|
+
rcPath: join2(env.home, ".zshrc")
|
|
11455
|
+
};
|
|
11456
|
+
case "fish":
|
|
11457
|
+
return {
|
|
11458
|
+
shell,
|
|
11459
|
+
scriptPath: join2(env.home, ".config", "fish", "completions", "zinkee.fish")
|
|
11460
|
+
};
|
|
11461
|
+
case "powershell": {
|
|
11462
|
+
const resolver = env.resolvePowerShellProfile ?? (() => defaultPowerShellProfile(env));
|
|
11463
|
+
const profilePath = resolver();
|
|
11464
|
+
if (!profilePath) {
|
|
11465
|
+
throw new CompletionShellError(
|
|
11466
|
+
"Could not resolve PowerShell $PROFILE path. Install PowerShell or use --show-completion."
|
|
11467
|
+
);
|
|
11468
|
+
}
|
|
11469
|
+
return {
|
|
11470
|
+
shell,
|
|
11471
|
+
scriptPath: profilePath,
|
|
11472
|
+
rcPath: profilePath
|
|
11473
|
+
};
|
|
11474
|
+
}
|
|
11475
|
+
}
|
|
11476
|
+
}
|
|
11477
|
+
function buildRcSnippet(shell, scriptPath) {
|
|
11478
|
+
switch (shell) {
|
|
11479
|
+
case "bash":
|
|
11480
|
+
return `source "${scriptPath}"`;
|
|
11481
|
+
case "zsh":
|
|
11482
|
+
return [
|
|
11483
|
+
`fpath=("${dirname2(scriptPath)}" $fpath)`,
|
|
11484
|
+
"autoload -Uz compinit && compinit"
|
|
11485
|
+
].join("\n");
|
|
11486
|
+
default:
|
|
11487
|
+
return "";
|
|
11488
|
+
}
|
|
11489
|
+
}
|
|
11490
|
+
function applyBlock(existing, block) {
|
|
11491
|
+
const wrapped = `${BLOCK_BEGIN}
|
|
11492
|
+
${block.trimEnd()}
|
|
11493
|
+
${BLOCK_END}
|
|
11494
|
+
`;
|
|
11495
|
+
const blockPattern = new RegExp(
|
|
11496
|
+
`${BLOCK_BEGIN.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${BLOCK_END.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`,
|
|
11497
|
+
"m"
|
|
11498
|
+
);
|
|
11499
|
+
if (blockPattern.test(existing)) {
|
|
11500
|
+
return existing.replace(blockPattern, wrapped);
|
|
11501
|
+
}
|
|
11502
|
+
const separator = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
11503
|
+
return `${existing}${separator}${wrapped}`;
|
|
11504
|
+
}
|
|
11505
|
+
function readFileOrEmpty(path) {
|
|
11506
|
+
try {
|
|
11507
|
+
return readFileSync10(path, "utf8");
|
|
11508
|
+
} catch (error) {
|
|
11509
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
11510
|
+
return "";
|
|
11511
|
+
}
|
|
11512
|
+
throw error;
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
function writeFileEnsuringDir(path, content) {
|
|
11516
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
11517
|
+
writeFileSync3(path, content, "utf8");
|
|
11518
|
+
}
|
|
11519
|
+
function installCompletion(shell, env) {
|
|
11520
|
+
const target = resolveTarget(shell, env);
|
|
11521
|
+
const script = renderScript(shell);
|
|
11522
|
+
if (shell === "powershell") {
|
|
11523
|
+
const existing = readFileOrEmpty(target.scriptPath);
|
|
11524
|
+
const updated = applyBlock(existing, script);
|
|
11525
|
+
writeFileEnsuringDir(target.scriptPath, updated);
|
|
11526
|
+
return {
|
|
11527
|
+
shell,
|
|
11528
|
+
scriptPath: target.scriptPath,
|
|
11529
|
+
rcPath: target.scriptPath,
|
|
11530
|
+
rcUpdated: true
|
|
11531
|
+
};
|
|
11532
|
+
}
|
|
11533
|
+
writeFileEnsuringDir(target.scriptPath, script);
|
|
11534
|
+
if (!target.rcPath) {
|
|
11535
|
+
return {
|
|
11536
|
+
shell,
|
|
11537
|
+
scriptPath: target.scriptPath,
|
|
11538
|
+
rcUpdated: false
|
|
11539
|
+
};
|
|
11540
|
+
}
|
|
11541
|
+
const snippet = buildRcSnippet(shell, target.scriptPath);
|
|
11542
|
+
if (!snippet) {
|
|
11543
|
+
return {
|
|
11544
|
+
shell,
|
|
11545
|
+
scriptPath: target.scriptPath,
|
|
11546
|
+
rcPath: target.rcPath,
|
|
11547
|
+
rcUpdated: false
|
|
11548
|
+
};
|
|
11549
|
+
}
|
|
11550
|
+
const existingRc = readFileOrEmpty(target.rcPath);
|
|
11551
|
+
const updatedRc = applyBlock(existingRc, snippet);
|
|
11552
|
+
if (updatedRc !== existingRc) {
|
|
11553
|
+
writeFileEnsuringDir(target.rcPath, updatedRc);
|
|
11554
|
+
return {
|
|
11555
|
+
shell,
|
|
11556
|
+
scriptPath: target.scriptPath,
|
|
11557
|
+
rcPath: target.rcPath,
|
|
11558
|
+
rcUpdated: true
|
|
11559
|
+
};
|
|
11560
|
+
}
|
|
11561
|
+
return {
|
|
11562
|
+
shell,
|
|
11563
|
+
scriptPath: target.scriptPath,
|
|
11564
|
+
rcPath: target.rcPath,
|
|
11565
|
+
rcUpdated: false
|
|
11566
|
+
};
|
|
11567
|
+
}
|
|
11568
|
+
function currentEnvironment() {
|
|
11569
|
+
return {
|
|
11570
|
+
home: homedir2(),
|
|
11571
|
+
platform: platform(),
|
|
11572
|
+
shellEnv: process.env.SHELL
|
|
11573
|
+
};
|
|
11574
|
+
}
|
|
11575
|
+
|
|
11576
|
+
// src/completion/runtime.ts
|
|
11577
|
+
var COMPLETION_ENV_VAR = "_ZINKEE_COMPLETE";
|
|
11578
|
+
function getStream(stream, fallback) {
|
|
11579
|
+
return stream ?? fallback;
|
|
11580
|
+
}
|
|
11581
|
+
function parseEnvWords(env) {
|
|
11582
|
+
const raw = env.COMP_WORDS ?? "";
|
|
11583
|
+
if (raw.trim().length === 0) {
|
|
11584
|
+
return [];
|
|
11585
|
+
}
|
|
11586
|
+
return raw.split(/\s+/).filter((token) => token.length > 0);
|
|
11587
|
+
}
|
|
11588
|
+
function parseEnvCwordIndex(env) {
|
|
11589
|
+
const raw = env.COMP_CWORD ?? "0";
|
|
11590
|
+
const parsed = Number.parseInt(raw, 10);
|
|
11591
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
11592
|
+
return 0;
|
|
11593
|
+
}
|
|
11594
|
+
return parsed;
|
|
11595
|
+
}
|
|
11596
|
+
function isCompletionRequest(env = process.env) {
|
|
11597
|
+
return typeof env[COMPLETION_ENV_VAR] === "string" && env[COMPLETION_ENV_VAR].length > 0;
|
|
11598
|
+
}
|
|
11599
|
+
function runCompletionRequest(env = process.env, stdout) {
|
|
11600
|
+
const out = getStream(stdout, process.stdout);
|
|
11601
|
+
try {
|
|
11602
|
+
const program = buildProgram();
|
|
11603
|
+
const candidates = getCompletions(program, {
|
|
11604
|
+
words: parseEnvWords(env),
|
|
11605
|
+
cwordIndex: parseEnvCwordIndex(env)
|
|
11606
|
+
});
|
|
11607
|
+
if (candidates.length > 0) {
|
|
11608
|
+
out.write(`${candidates.join("\n")}
|
|
11609
|
+
`);
|
|
11610
|
+
}
|
|
11611
|
+
} catch {
|
|
11612
|
+
}
|
|
11613
|
+
}
|
|
11614
|
+
function extractCompletionFlag(argv) {
|
|
11615
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
11616
|
+
const token = argv[index];
|
|
11617
|
+
if (!token) {
|
|
11618
|
+
continue;
|
|
11619
|
+
}
|
|
11620
|
+
if (token === "--install-completion" || token === "--show-completion") {
|
|
11621
|
+
const mode = token === "--install-completion" ? "install" : "show";
|
|
11622
|
+
const next = argv[index + 1];
|
|
11623
|
+
const shellOverride = next && !next.startsWith("-") ? next : void 0;
|
|
11624
|
+
return { mode, shellOverride };
|
|
11625
|
+
}
|
|
11626
|
+
if (token.startsWith("--install-completion=")) {
|
|
11627
|
+
const value = token.slice("--install-completion=".length);
|
|
11628
|
+
return { mode: "install", shellOverride: value || void 0 };
|
|
11629
|
+
}
|
|
11630
|
+
if (token.startsWith("--show-completion=")) {
|
|
11631
|
+
const value = token.slice("--show-completion=".length);
|
|
11632
|
+
return { mode: "show", shellOverride: value || void 0 };
|
|
11633
|
+
}
|
|
11634
|
+
}
|
|
11635
|
+
return null;
|
|
11636
|
+
}
|
|
11637
|
+
function resolveShell(request, env) {
|
|
11638
|
+
if (request.shellOverride) {
|
|
11639
|
+
return parseShell(request.shellOverride);
|
|
11640
|
+
}
|
|
11641
|
+
return detectShell(env);
|
|
11642
|
+
}
|
|
11643
|
+
function handleCompletionFlag(request, deps = {}) {
|
|
11644
|
+
const env = deps.environment ?? currentEnvironment();
|
|
11645
|
+
const out = getStream(deps.stdout, process.stdout);
|
|
11646
|
+
const err = getStream(deps.stderr, process.stderr);
|
|
11647
|
+
let shell;
|
|
11648
|
+
try {
|
|
11649
|
+
shell = resolveShell(request, env);
|
|
11650
|
+
} catch (error) {
|
|
11651
|
+
const message = error instanceof CompletionShellError ? error.message : String(error);
|
|
11652
|
+
err.write(`${message}
|
|
11653
|
+
`);
|
|
11654
|
+
return 1;
|
|
11655
|
+
}
|
|
11656
|
+
if (request.mode === "show") {
|
|
11657
|
+
out.write(renderScript(shell));
|
|
11658
|
+
return 0;
|
|
11659
|
+
}
|
|
11660
|
+
try {
|
|
11661
|
+
const result = installCompletion(shell, env);
|
|
11662
|
+
err.write(`zinkee completion installed for ${shell}.
|
|
11663
|
+
`);
|
|
11664
|
+
err.write(` script: ${result.scriptPath}
|
|
11665
|
+
`);
|
|
11666
|
+
if (result.rcPath && result.rcPath !== result.scriptPath) {
|
|
11667
|
+
err.write(
|
|
11668
|
+
result.rcUpdated ? ` updated: ${result.rcPath}
|
|
11669
|
+
` : ` unchanged: ${result.rcPath}
|
|
11670
|
+
`
|
|
11671
|
+
);
|
|
11672
|
+
}
|
|
11673
|
+
err.write(`Restart your shell or open a new session to enable completion.
|
|
11674
|
+
`);
|
|
11675
|
+
return 0;
|
|
11676
|
+
} catch (error) {
|
|
11677
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11678
|
+
err.write(`Failed to install zinkee completion: ${message}
|
|
11679
|
+
`);
|
|
11680
|
+
return 1;
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11683
|
+
|
|
10979
11684
|
// src/cli-runner.ts
|
|
10980
11685
|
var ExampleDisplayed = class extends Error {
|
|
10981
11686
|
name = "ExampleDisplayed";
|
|
@@ -10998,7 +11703,19 @@ function isExecutedAsEntry(argvEntry, moduleUrl) {
|
|
|
10998
11703
|
}
|
|
10999
11704
|
async function runCli(argv) {
|
|
11000
11705
|
try {
|
|
11001
|
-
|
|
11706
|
+
if (isCompletionRequest()) {
|
|
11707
|
+
runCompletionRequest();
|
|
11708
|
+
return;
|
|
11709
|
+
}
|
|
11710
|
+
const completionFlag = extractCompletionFlag(argv);
|
|
11711
|
+
if (completionFlag) {
|
|
11712
|
+
const exitCode = handleCompletionFlag(completionFlag);
|
|
11713
|
+
process.exitCode = exitCode;
|
|
11714
|
+
return;
|
|
11715
|
+
}
|
|
11716
|
+
const program = buildProgram();
|
|
11717
|
+
const optionMeta = buildOptionMetadata(program);
|
|
11718
|
+
const exampleRequest = extractExampleRequest(argv, optionMeta);
|
|
11002
11719
|
if (exampleRequest) {
|
|
11003
11720
|
await showExamples(
|
|
11004
11721
|
exampleRequest.commandName,
|
|
@@ -11012,7 +11729,8 @@ async function runCli(argv) {
|
|
|
11012
11729
|
);
|
|
11013
11730
|
throw new ExampleDisplayed();
|
|
11014
11731
|
}
|
|
11015
|
-
|
|
11732
|
+
const rewrittenArgv = hoistGlobalOptions(argv, optionMeta, commandRegistry);
|
|
11733
|
+
await program.parseAsync(rewrittenArgv);
|
|
11016
11734
|
} catch (error) {
|
|
11017
11735
|
if (error instanceof ExampleDisplayed) {
|
|
11018
11736
|
process.exitCode = 0;
|