akm-cli 0.1.0 → 0.1.1
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/dist/cli.js +36 -0
- package/dist/completions.js +137 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { defineCommand, runMain } from "citty";
|
|
5
5
|
import { resolveStashDir } from "./common";
|
|
6
|
+
import { generateBashCompletions, installBashCompletions } from "./completions";
|
|
6
7
|
import { DEFAULT_CONFIG, getConfigPath, loadConfig, saveConfig } from "./config";
|
|
7
8
|
import { getConfigValue, listConfig, setConfigValue, unsetConfigValue } from "./config-cli";
|
|
8
9
|
import { ConfigError, NotFoundError, UsageError } from "./errors";
|
|
@@ -921,6 +922,38 @@ const hintsCommand = defineCommand({
|
|
|
921
922
|
process.stdout.write(loadHints(detail));
|
|
922
923
|
},
|
|
923
924
|
});
|
|
925
|
+
const completionsCommand = defineCommand({
|
|
926
|
+
meta: {
|
|
927
|
+
name: "completions",
|
|
928
|
+
description: "Generate or install shell completion script",
|
|
929
|
+
},
|
|
930
|
+
args: {
|
|
931
|
+
install: {
|
|
932
|
+
type: "boolean",
|
|
933
|
+
description: "Install completions to the appropriate directory",
|
|
934
|
+
default: false,
|
|
935
|
+
},
|
|
936
|
+
shell: {
|
|
937
|
+
type: "string",
|
|
938
|
+
description: "Shell type (bash)",
|
|
939
|
+
default: "bash",
|
|
940
|
+
},
|
|
941
|
+
},
|
|
942
|
+
run({ args }) {
|
|
943
|
+
if (args.shell !== "bash") {
|
|
944
|
+
throw new UsageError(`Unsupported shell: ${args.shell}. Only bash is supported.`);
|
|
945
|
+
}
|
|
946
|
+
const script = generateBashCompletions(main);
|
|
947
|
+
if (args.install) {
|
|
948
|
+
const dest = installBashCompletions(script);
|
|
949
|
+
console.error(`Completions installed to ${dest}`);
|
|
950
|
+
console.error(`Restart your shell or run: source ${dest}`);
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
process.stdout.write(script);
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
});
|
|
924
957
|
const main = defineCommand({
|
|
925
958
|
meta: {
|
|
926
959
|
name: "akm",
|
|
@@ -948,6 +981,7 @@ const main = defineCommand({
|
|
|
948
981
|
registry: registryCommand,
|
|
949
982
|
config: configCommand,
|
|
950
983
|
hints: hintsCommand,
|
|
984
|
+
completions: completionsCommand,
|
|
951
985
|
},
|
|
952
986
|
});
|
|
953
987
|
const CONFIG_SUBCOMMAND_SET = new Set(["path", "list", "get", "set", "unset"]);
|
|
@@ -1238,6 +1272,8 @@ akm kit # Kit management (add, list, remov
|
|
|
1238
1272
|
akm upgrade # Upgrade akm binary
|
|
1239
1273
|
akm upgrade --check # Check for updates
|
|
1240
1274
|
akm hints # Print this reference
|
|
1275
|
+
akm completions # Print bash completion script
|
|
1276
|
+
akm completions --install # Install completions
|
|
1241
1277
|
\`\`\`
|
|
1242
1278
|
|
|
1243
1279
|
## Output Control
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
// ── Known flag values ────────────────────────────────────────────────────────
|
|
5
|
+
const FLAG_VALUES = {
|
|
6
|
+
"--format": ["json", "text", "yaml"],
|
|
7
|
+
"--detail": ["brief", "normal", "full"],
|
|
8
|
+
"--type": ["skill", "command", "agent", "knowledge", "script", "memory", "any"],
|
|
9
|
+
"--source": ["stash", "registry", "both"],
|
|
10
|
+
"--shell": ["bash"],
|
|
11
|
+
};
|
|
12
|
+
function walkCommandTree(cmd, parentPath = "") {
|
|
13
|
+
const name = cmd.meta?.name ?? "";
|
|
14
|
+
const currentPath = parentPath ? `${parentPath} ${name}` : name;
|
|
15
|
+
const result = [];
|
|
16
|
+
const subcommands = Object.keys(cmd.subCommands ?? {});
|
|
17
|
+
const flags = [];
|
|
18
|
+
if (cmd.args) {
|
|
19
|
+
for (const [flagName, arg] of Object.entries(cmd.args)) {
|
|
20
|
+
if (arg.type === "positional")
|
|
21
|
+
continue;
|
|
22
|
+
flags.push(`--${flagName}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
result.push({ path: currentPath, subcommands, flags });
|
|
26
|
+
if (cmd.subCommands) {
|
|
27
|
+
for (const sub of Object.values(cmd.subCommands)) {
|
|
28
|
+
result.push(...walkCommandTree(sub, currentPath));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
// ── Bash completion script generator ─────────────────────────────────────────
|
|
34
|
+
export function generateBashCompletions(cmd) {
|
|
35
|
+
const commands = walkCommandTree(cmd);
|
|
36
|
+
const rootName = cmd.meta?.name ?? "akm";
|
|
37
|
+
// Collect global flags from root command
|
|
38
|
+
const rootInfo = commands.find((c) => c.path === rootName);
|
|
39
|
+
const globalFlags = rootInfo?.flags ?? [];
|
|
40
|
+
// Build the case blocks for subcommand completion
|
|
41
|
+
const caseBlocks = [];
|
|
42
|
+
for (const info of commands) {
|
|
43
|
+
const allFlags = [...new Set([...info.flags, ...globalFlags])];
|
|
44
|
+
if (info.subcommands.length > 0 || allFlags.length > 0) {
|
|
45
|
+
const matchPath = info.path;
|
|
46
|
+
const subcmdStr = info.subcommands.join(" ");
|
|
47
|
+
const flagStr = allFlags.join(" ");
|
|
48
|
+
caseBlocks.push(` "${matchPath}")
|
|
49
|
+
if [[ "\${cur}" == -* ]]; then
|
|
50
|
+
COMPREPLY=( $(compgen -W "${flagStr}" -- "\${cur}") )
|
|
51
|
+
else
|
|
52
|
+
COMPREPLY=( $(compgen -W "${subcmdStr}" -- "\${cur}") )
|
|
53
|
+
fi
|
|
54
|
+
return 0
|
|
55
|
+
;;`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Build flag-value completion cases
|
|
59
|
+
const valueCases = [];
|
|
60
|
+
for (const [flag, values] of Object.entries(FLAG_VALUES)) {
|
|
61
|
+
valueCases.push(` ${flag})
|
|
62
|
+
COMPREPLY=( $(compgen -W "${values.join(" ")}" -- "\${cur}") )
|
|
63
|
+
return 0
|
|
64
|
+
;;`);
|
|
65
|
+
}
|
|
66
|
+
const script = `#!/bin/bash
|
|
67
|
+
# Bash completion for ${rootName}
|
|
68
|
+
# Generated by ${rootName} completions
|
|
69
|
+
|
|
70
|
+
_${rootName}() {
|
|
71
|
+
local cur prev words cword
|
|
72
|
+
if type _init_completion &>/dev/null; then
|
|
73
|
+
_init_completion || return
|
|
74
|
+
else
|
|
75
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
76
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
77
|
+
words=("\${COMP_WORDS[@]}")
|
|
78
|
+
cword=\${COMP_CWORD}
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Complete flag values
|
|
82
|
+
case "\${prev}" in
|
|
83
|
+
${valueCases.join("\n")}
|
|
84
|
+
esac
|
|
85
|
+
|
|
86
|
+
# Build the command path from COMP_WORDS
|
|
87
|
+
local cmd_path="${rootName}"
|
|
88
|
+
for (( i=1; i < cword; i++ )); do
|
|
89
|
+
case "\${words[i]}" in
|
|
90
|
+
-*) continue ;;
|
|
91
|
+
*) cmd_path="\${cmd_path} \${words[i]}" ;;
|
|
92
|
+
esac
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
# Complete based on current command path
|
|
96
|
+
case "\${cmd_path}" in
|
|
97
|
+
${caseBlocks.join("\n")}
|
|
98
|
+
esac
|
|
99
|
+
|
|
100
|
+
return 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
complete -F _${rootName} ${rootName}
|
|
104
|
+
`;
|
|
105
|
+
return script;
|
|
106
|
+
}
|
|
107
|
+
// ── Install ──────────────────────────────────────────────────────────────────
|
|
108
|
+
export function installBashCompletions(script) {
|
|
109
|
+
const dest = resolveInstallPath();
|
|
110
|
+
const dir = path.dirname(dest);
|
|
111
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
112
|
+
fs.writeFileSync(dest, script, "utf8");
|
|
113
|
+
return dest;
|
|
114
|
+
}
|
|
115
|
+
function resolveInstallPath() {
|
|
116
|
+
const xdgData = process.env.XDG_DATA_HOME?.trim();
|
|
117
|
+
if (xdgData) {
|
|
118
|
+
return path.join(xdgData, "bash-completion", "completions", "akm");
|
|
119
|
+
}
|
|
120
|
+
const home = os.homedir();
|
|
121
|
+
// Default XDG location
|
|
122
|
+
const defaultXdg = path.join(home, ".local", "share", "bash-completion", "completions", "akm");
|
|
123
|
+
const defaultXdgDir = path.dirname(defaultXdg);
|
|
124
|
+
if (isDir(defaultXdgDir) || isDir(path.dirname(defaultXdgDir))) {
|
|
125
|
+
return defaultXdg;
|
|
126
|
+
}
|
|
127
|
+
// Fallback
|
|
128
|
+
return path.join(home, ".bash_completion.d", "akm");
|
|
129
|
+
}
|
|
130
|
+
function isDir(p) {
|
|
131
|
+
try {
|
|
132
|
+
return fs.statSync(p).isDirectory();
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|