argsbarg 3.4.2 → 3.6.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 +31 -1
- package/README.md +24 -8
- package/biome.json +29 -6
- package/bun.lock +22 -0
- package/docs/cli-program.md +75 -1
- package/docs/install.md +1 -1
- package/docs/mcp.md +45 -1
- package/docs/templates/cursor/rules/cli-program.mdc +15 -21
- package/index.d.ts +95 -50
- package/justfile +24 -6
- package/package.json +4 -2
- package/scripts/release.ts +26 -9
- package/src/builtins/builtins.test.ts +9 -4
- package/src/builtins/completion-bash.ts +74 -50
- package/src/builtins/completion-fish.ts +3 -8
- package/src/builtins/completion-group.ts +1 -1
- package/src/builtins/completion-zsh.ts +80 -42
- package/src/builtins/dispatch.ts +20 -16
- package/src/builtins/export.ts +19 -10
- package/src/builtins/index.ts +9 -4
- package/src/builtins/install.ts +10 -10
- package/src/builtins/mcp.ts +1 -1
- package/src/builtins/presentation.ts +8 -8
- package/src/builtins/scopes.ts +1 -1
- package/src/builtins/version.ts +1 -1
- package/src/completion.ts +4 -4
- package/src/context.ts +91 -15
- package/src/docs/api-guide.test.ts +2 -2
- package/src/docs/api-guide.ts +19 -5
- package/src/docs/builtin.ts +27 -8
- package/src/docs/docs.test.ts +18 -11
- package/src/docs/mcp-guide.ts +108 -25
- package/src/docs/resolve.ts +10 -3
- package/src/docs/save.ts +11 -3
- package/src/formats.test.ts +35 -0
- package/src/formats.ts +135 -0
- package/src/headless.test.ts +8 -16
- package/src/help.ts +73 -43
- package/src/hidden-mcpb.test.ts +7 -6
- package/src/hidden.ts +2 -2
- package/src/index.test.ts +120 -96
- package/src/index.ts +36 -24
- package/src/install/binary.ts +12 -5
- package/src/install/completions.ts +7 -3
- package/src/install/detect-installed.ts +29 -4
- package/src/install/gh-release-update.ts +31 -23
- package/src/install/index.ts +69 -19
- package/src/install/install.test.ts +31 -8
- package/src/install/mcp-codex.test.ts +57 -0
- package/src/install/mcp-codex.ts +125 -0
- package/src/install/mcp-config.ts +12 -5
- package/src/install/mcp-opencode.test.ts +98 -0
- package/src/install/mcp-opencode.ts +149 -0
- package/src/install/paths.ts +29 -3
- package/src/install/plan.ts +73 -6
- package/src/install/shell.ts +1 -4
- package/src/install/status.ts +12 -6
- package/src/install/uninstall.ts +38 -4
- package/src/install/update.test.ts +2 -2
- package/src/install/update.ts +3 -1
- package/src/invoke.ts +12 -9
- package/src/mcp/bundle.ts +36 -8
- package/src/mcp/env.ts +7 -13
- package/src/mcp/server.ts +12 -6
- package/src/mcp/tools.ts +83 -18
- package/src/mcp.ts +3 -3
- package/src/parse.ts +129 -27
- package/src/runtime.ts +22 -12
- package/src/schema.ts +11 -5
- package/src/skill/generate.ts +4 -4
- package/src/skill/install.ts +6 -2
- package/src/types.ts +24 -0
- package/src/validate.ts +75 -16
package/justfile
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
# https://github.com/casey/just — run `just` to list recipes.
|
|
2
2
|
|
|
3
|
+
set shell := ["bash", "-eu", "-o", "pipefail", "-c"]
|
|
4
|
+
|
|
3
5
|
_:
|
|
4
6
|
@just --list
|
|
5
7
|
|
|
6
|
-
#
|
|
7
|
-
check
|
|
8
|
-
|
|
8
|
+
# check the codebase
|
|
9
|
+
check: typecheck format
|
|
10
|
+
|
|
11
|
+
# Update local consumer apps: npm i argsbarg@latest, build, docgen
|
|
12
|
+
consumers-sync *apps:
|
|
13
|
+
#!/usr/bin/env bash
|
|
14
|
+
root="$(cd "{{justfile_directory()}}" && pwd)"
|
|
15
|
+
ss="$root/../../ss"
|
|
16
|
+
apps=(idp-trees sqsp-qa-tools sqsp-i18n-tools)
|
|
17
|
+
for app in "${apps[@]}"; do
|
|
18
|
+
dir="$(cd "$ss/$app" && pwd)"
|
|
19
|
+
echo "==> $app ($dir)"
|
|
20
|
+
(cd "$dir" && bun i argsbarg@latest && just build && just docgen && just install)
|
|
21
|
+
done
|
|
9
22
|
|
|
10
23
|
# run the minimal example
|
|
11
24
|
example *ARGS:
|
|
@@ -17,16 +30,20 @@ example-watch *ARGS:
|
|
|
17
30
|
|
|
18
31
|
# format the codebase
|
|
19
32
|
format:
|
|
20
|
-
bun
|
|
33
|
+
bun run biome check ./src ./scripts --write
|
|
21
34
|
|
|
22
35
|
# lint the codebase
|
|
23
36
|
lint:
|
|
24
|
-
bun
|
|
37
|
+
bun run biome check ./src ./scripts
|
|
25
38
|
|
|
26
39
|
# Typecheck, lint, then run the test suite.
|
|
27
|
-
test: check
|
|
40
|
+
test: check
|
|
28
41
|
bun test
|
|
29
42
|
|
|
43
|
+
# typecheck the codebase
|
|
44
|
+
typecheck:
|
|
45
|
+
bun run tsc --noEmit
|
|
46
|
+
|
|
30
47
|
# generate type declarations for the package
|
|
31
48
|
typegen:
|
|
32
49
|
bunx dts-bundle-generator --out-file index.d.ts src/index.ts
|
|
@@ -34,3 +51,4 @@ typegen:
|
|
|
34
51
|
# publish to github and npm
|
|
35
52
|
release bump: test typegen
|
|
36
53
|
bun scripts/release.ts {{bump}}
|
|
54
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "argsbarg",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"bun": ">=1.3"
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@
|
|
24
|
+
"@biomejs/biome": "^2.5.0",
|
|
25
|
+
"@types/bun": "^1.3.12",
|
|
26
|
+
"typescript": "^5.9.3"
|
|
25
27
|
}
|
|
26
28
|
}
|
package/scripts/release.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* repo changes (`git add -A`), not only the version and CHANGELOG edits from this script.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { unlink } from "fs/promises";
|
|
15
|
+
import { unlink } from "node:fs/promises";
|
|
16
16
|
|
|
17
17
|
/** Returns the parent directory of an absolute file path. */
|
|
18
18
|
function parentDir(absolute: string): string {
|
|
@@ -96,9 +96,13 @@ function githubRepoBaseFromOrigin(origin: string): string | null {
|
|
|
96
96
|
function releasedSemverVersionsFromChangelog(md: string): string[] {
|
|
97
97
|
const re = /^## \[(\d+\.\d+\.\d+)\] /gm;
|
|
98
98
|
const out: string[] = [];
|
|
99
|
-
let m
|
|
100
|
-
while (
|
|
101
|
-
|
|
99
|
+
let m = re.exec(md);
|
|
100
|
+
while (m !== null) {
|
|
101
|
+
const version = m[1];
|
|
102
|
+
if (version !== undefined) {
|
|
103
|
+
out.push(version);
|
|
104
|
+
}
|
|
105
|
+
m = re.exec(md);
|
|
102
106
|
}
|
|
103
107
|
return out;
|
|
104
108
|
}
|
|
@@ -108,14 +112,18 @@ function releasedSemverVersionsFromChangelog(md: string): string[] {
|
|
|
108
112
|
* (`[...]: http...`) at the end of the file.
|
|
109
113
|
*/
|
|
110
114
|
function stripChangelogLinkDefinitions(md: string): string {
|
|
111
|
-
|
|
115
|
+
const s = md.replace(/\r?\n## Links\r?\n/, "\n");
|
|
112
116
|
const lines = s.split(/\r?\n/);
|
|
113
117
|
let i = lines.length;
|
|
114
118
|
while (i > 0 && lines[i - 1] === "") {
|
|
115
119
|
i -= 1;
|
|
116
120
|
}
|
|
117
121
|
const refLine = /^\[[^\]]+\]: .+$/;
|
|
118
|
-
while (i > 0
|
|
122
|
+
while (i > 0) {
|
|
123
|
+
const line = lines[i - 1];
|
|
124
|
+
if (line === undefined || !refLine.test(line)) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
119
127
|
i -= 1;
|
|
120
128
|
}
|
|
121
129
|
while (i > 0 && lines[i - 1] === "") {
|
|
@@ -143,7 +151,10 @@ function appendChangelogLinkDefinitions(md: string, repoBase: string): string {
|
|
|
143
151
|
if (versions.length === 0) {
|
|
144
152
|
return `${body}\n`;
|
|
145
153
|
}
|
|
146
|
-
const newest = versions[0]
|
|
154
|
+
const newest = versions[0];
|
|
155
|
+
if (newest === undefined) {
|
|
156
|
+
return `${body}\n`;
|
|
157
|
+
}
|
|
147
158
|
const lines = [
|
|
148
159
|
"",
|
|
149
160
|
"",
|
|
@@ -178,7 +189,9 @@ function promoteChangelog(content: string, version: string, date: string): strin
|
|
|
178
189
|
const bodyStart = lineEnd + 1;
|
|
179
190
|
const nextIdx = content.indexOf("\n## [", bodyStart);
|
|
180
191
|
const body =
|
|
181
|
-
nextIdx === -1
|
|
192
|
+
nextIdx === -1
|
|
193
|
+
? content.slice(bodyStart).trimEnd()
|
|
194
|
+
: content.slice(bodyStart, nextIdx).trimEnd();
|
|
182
195
|
const tail = nextIdx === -1 ? "" : content.slice(nextIdx + 1);
|
|
183
196
|
const before = content.slice(0, idx);
|
|
184
197
|
const newBlock = `${header}\n\n## [${version}] - ${date}\n${body}\n\n`;
|
|
@@ -240,7 +253,11 @@ try {
|
|
|
240
253
|
run("git tag", ["git", "tag", "-a", tag, "-m", msg], repoRoot);
|
|
241
254
|
run("git push", ["git", "push"], repoRoot);
|
|
242
255
|
run("git push tags", ["git", "push", "--tags"], repoRoot);
|
|
243
|
-
run(
|
|
256
|
+
run(
|
|
257
|
+
"gh release",
|
|
258
|
+
["gh", "release", "create", tag, "--title", tag, "--notes-file", notesPath],
|
|
259
|
+
repoRoot,
|
|
260
|
+
);
|
|
244
261
|
run("npm publish", ["npm", "publish"], repoRoot);
|
|
245
262
|
} finally {
|
|
246
263
|
await unlink(notesPath).catch(() => {});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { CliProgram } from "../types.ts";
|
|
3
|
+
import { exportPresentationBuiltins } from "./export.ts";
|
|
4
|
+
import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
|
|
2
5
|
import { cliBuiltinInstallCommand, installBuiltinOptions } from "./install.ts";
|
|
3
6
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
4
7
|
import { cliPresentationRoot } from "./presentation.ts";
|
|
5
|
-
import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
|
|
6
|
-
import { exportPresentationBuiltins } from "./export.ts";
|
|
7
|
-
import { CliProgram } from "../types.ts";
|
|
8
8
|
|
|
9
9
|
const fixture: CliProgram = {
|
|
10
10
|
key: "myapp",
|
|
@@ -114,7 +114,12 @@ describe("completion emitters", () => {
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
test("zsh script registers compdef", () => {
|
|
117
|
-
const schema = cliPresentationRoot({
|
|
117
|
+
const schema = cliPresentationRoot({
|
|
118
|
+
key: "zapp",
|
|
119
|
+
version: "0.0.0",
|
|
120
|
+
description: "z",
|
|
121
|
+
handler: () => {},
|
|
122
|
+
});
|
|
118
123
|
const zsh = completionZshScript(schema);
|
|
119
124
|
expect(zsh).toContain("#compdef zapp");
|
|
120
125
|
expect(zsh).toContain("compdef _zapp zapp");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CliOptionKind, type CliRouter } from "../types.ts";
|
|
2
2
|
import { collectScopes, type ScopeRec } from "./scopes.ts";
|
|
3
3
|
import {
|
|
4
4
|
escShellSingleQuoted,
|
|
@@ -10,19 +10,24 @@ import {
|
|
|
10
10
|
|
|
11
11
|
function emitConsumeLong(ident: string, scopes: ScopeRec[]): string {
|
|
12
12
|
let o = "_${ident}_nac_consume_long() {\n".replace("${ident}", ident);
|
|
13
|
-
o +=
|
|
13
|
+
o += ' local sid="$1" w="$2" nw="$3"\n';
|
|
14
14
|
o += " case $sid in\n";
|
|
15
15
|
for (const [i, sc] of scopes.entries()) {
|
|
16
|
-
o +=
|
|
16
|
+
o += ` ${i})\n`;
|
|
17
17
|
o += " case $w in\n";
|
|
18
|
-
o +=
|
|
18
|
+
o +=
|
|
19
|
+
" " +
|
|
20
|
+
kHelpLong +
|
|
21
|
+
"|${kHelpLong}=*|${kHelpShort}) echo 1 ;;\n"
|
|
22
|
+
.replace(/\$\{kHelpLong\}/g, kHelpLong)
|
|
23
|
+
.replace(/\$\{kHelpShort\}/g, kHelpShort);
|
|
19
24
|
for (const op of sc.opts) {
|
|
20
|
-
const base =
|
|
25
|
+
const base = `--${op.name}`;
|
|
21
26
|
if (op.kind === "presence") {
|
|
22
|
-
o +=
|
|
27
|
+
o += ` ${base}${"|${base}=*) echo 1 ;;\n".replace(/\$\{base\}/g, base)}`;
|
|
23
28
|
} else {
|
|
24
|
-
o +=
|
|
25
|
-
o +=
|
|
29
|
+
o += ` ${base}=*) echo 1 ;;\n`;
|
|
30
|
+
o += ` ${base}) echo 2 ;;\n`;
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
33
|
o += " *) echo 0 ;;\n";
|
|
@@ -37,10 +42,10 @@ function emitConsumeLong(ident: string, scopes: ScopeRec[]): string {
|
|
|
37
42
|
|
|
38
43
|
function emitConsumeShort(ident: string, scopes: ScopeRec[]): string {
|
|
39
44
|
let o = "_${ident}_nac_consume_short() {\n".replace("${ident}", ident);
|
|
40
|
-
o +=
|
|
45
|
+
o += ' local sid="$1" w="$2"\n';
|
|
41
46
|
o += " case $sid in\n";
|
|
42
47
|
for (const [i, sc] of scopes.entries()) {
|
|
43
|
-
o +=
|
|
48
|
+
o += ` ${i})\n`;
|
|
44
49
|
o += " local rest=${w#-}\n";
|
|
45
50
|
o += " local ch\n";
|
|
46
51
|
o += " local saw=0\n";
|
|
@@ -52,16 +57,16 @@ function emitConsumeShort(ident: string, scopes: ScopeRec[]): string {
|
|
|
52
57
|
for (const op of sc.opts) {
|
|
53
58
|
if (!op.shortName) continue;
|
|
54
59
|
if (op.kind === "presence") {
|
|
55
|
-
boolChars += op.shortName
|
|
60
|
+
boolChars += `${op.shortName}|`;
|
|
56
61
|
} else {
|
|
57
|
-
o +=
|
|
62
|
+
o += ` ${op.shortName})\n`;
|
|
58
63
|
o += " if [[ $saw -ne 0 || -n $rest ]]; then echo 0; return; fi\n";
|
|
59
64
|
o += " echo 2; return ;;\n";
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
if (boolChars.length > 0) {
|
|
63
68
|
boolChars = boolChars.slice(0, -1);
|
|
64
|
-
o +=
|
|
69
|
+
o += ` ${boolChars}) ;;\n`;
|
|
65
70
|
}
|
|
66
71
|
o += " *) echo 0; return ;;\n";
|
|
67
72
|
o += " esac\n";
|
|
@@ -76,18 +81,22 @@ function emitConsumeShort(ident: string, scopes: ScopeRec[]): string {
|
|
|
76
81
|
return o;
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
function emitMatchChild(
|
|
84
|
+
function emitMatchChild(
|
|
85
|
+
ident: string,
|
|
86
|
+
scopes: ScopeRec[],
|
|
87
|
+
pathIndex: Record<string, number>,
|
|
88
|
+
): string {
|
|
80
89
|
let o = "_${ident}_nac_match_child() {\n".replace("${ident}", ident);
|
|
81
|
-
o +=
|
|
90
|
+
o += ' local sid="$1" w="$2"\n';
|
|
82
91
|
o += " case $sid in\n";
|
|
83
92
|
for (const [sid, sc] of scopes.entries()) {
|
|
84
93
|
if (sc.kids.length === 0) continue;
|
|
85
|
-
o +=
|
|
94
|
+
o += ` ${sid})\n`;
|
|
86
95
|
o += " case $w in\n";
|
|
87
96
|
for (const ch of sc.kids) {
|
|
88
|
-
const childPath = sc.path === "" ? ch.key : sc.path
|
|
97
|
+
const childPath = sc.path === "" ? ch.key : `${sc.path}/${ch.key}`;
|
|
89
98
|
const cid = pathIndex[childPath] ?? 0;
|
|
90
|
-
o +=
|
|
99
|
+
o += ` ${ch.key}) echo ${cid}; return 0 ;;\n`;
|
|
91
100
|
}
|
|
92
101
|
o += " esac\n";
|
|
93
102
|
o += " ;;\n";
|
|
@@ -102,12 +111,15 @@ function emitSimulate(ident: string): string {
|
|
|
102
111
|
let o = "_${ident}_nac_simulate() {\n".replace("${ident}", ident);
|
|
103
112
|
o += " local i=1 sid=0 w steps next\n";
|
|
104
113
|
o += " while (( i < COMP_CWORD )); do\n";
|
|
105
|
-
o +=
|
|
106
|
-
o +=
|
|
114
|
+
o += ' w="${COMP_WORDS[i]}"\n';
|
|
115
|
+
o += ` if [[ $w == ${kHelpShort} || $w == ${kHelpLong} ]]; then\n`;
|
|
107
116
|
o += " ((i++)); continue\n";
|
|
108
117
|
o += " fi\n";
|
|
109
118
|
o += " if [[ $w == --* ]]; then\n";
|
|
110
|
-
o +=
|
|
119
|
+
o += ' steps=$(_${ident}_nac_consume_long "$sid" "$w" "${COMP_WORDS[i+1]}")\n'.replace(
|
|
120
|
+
"${ident}",
|
|
121
|
+
ident,
|
|
122
|
+
);
|
|
111
123
|
o += " case $steps in\n";
|
|
112
124
|
o += " 0) break ;;\n";
|
|
113
125
|
o += " 1) ((i++)) ;;\n";
|
|
@@ -117,7 +129,7 @@ function emitSimulate(ident: string): string {
|
|
|
117
129
|
o += " continue\n";
|
|
118
130
|
o += " fi\n";
|
|
119
131
|
o += " if [[ $w == -* ]]; then\n";
|
|
120
|
-
o +=
|
|
132
|
+
o += ' steps=$(_${ident}_nac_consume_short "$sid" "$w")\n'.replace("${ident}", ident);
|
|
121
133
|
o += " case $steps in\n";
|
|
122
134
|
o += " 0) break ;;\n";
|
|
123
135
|
o += " 1) ((i++)) ;;\n";
|
|
@@ -126,7 +138,7 @@ function emitSimulate(ident: string): string {
|
|
|
126
138
|
o += " esac\n";
|
|
127
139
|
o += " continue\n";
|
|
128
140
|
o += " fi\n";
|
|
129
|
-
o +=
|
|
141
|
+
o += ' next=$(_${ident}_nac_match_child "$sid" "$w") || break\n'.replace("${ident}", ident);
|
|
130
142
|
o += " sid=$next\n";
|
|
131
143
|
o += " ((i++))\n";
|
|
132
144
|
o += " done\n";
|
|
@@ -137,16 +149,23 @@ function emitSimulate(ident: string): string {
|
|
|
137
149
|
|
|
138
150
|
function emitEnumReplyBash(ident: string, scopes: ScopeRec[]): string {
|
|
139
151
|
let o = "_${ident}_nac_enum_reply() {\n".replace("${ident}", ident);
|
|
140
|
-
o +=
|
|
152
|
+
o += ' local sid="$1" prev="$2" cur="$3"\n';
|
|
141
153
|
o += " case $sid in\n";
|
|
142
154
|
for (const [i, sc] of scopes.entries()) {
|
|
143
|
-
const enumOpts = sc.opts.filter(
|
|
155
|
+
const enumOpts = sc.opts.filter(
|
|
156
|
+
(op) => op.kind === CliOptionKind.Enum && (op.choices?.length ?? 0) > 0,
|
|
157
|
+
);
|
|
144
158
|
if (enumOpts.length === 0) continue;
|
|
145
|
-
o +=
|
|
159
|
+
o += ` ${i})\n`;
|
|
146
160
|
o += " case $prev in\n";
|
|
147
161
|
for (const op of enumOpts) {
|
|
148
162
|
const words = (op.choices ?? []).map((c) => escShellSingleQuoted(c)).join(" ");
|
|
149
|
-
o +=
|
|
163
|
+
o +=
|
|
164
|
+
" --" +
|
|
165
|
+
op.name +
|
|
166
|
+
") COMPREPLY=( $(compgen -W '" +
|
|
167
|
+
words +
|
|
168
|
+
'\' -- "$cur") ); return 0 ;;\n';
|
|
150
169
|
}
|
|
151
170
|
o += " esac\n";
|
|
152
171
|
o += " ;;\n";
|
|
@@ -160,34 +179,39 @@ function emitEnumReplyBash(ident: string, scopes: ScopeRec[]): string {
|
|
|
160
179
|
function emitMainBodyBash(schema: CliRouter, ident: string): string {
|
|
161
180
|
const main = mainName(schema.key);
|
|
162
181
|
let o = "_${main}() {\n".replace("${main}", main);
|
|
163
|
-
o +=
|
|
164
|
-
o +=
|
|
182
|
+
o += ' local cur="${COMP_WORDS[COMP_CWORD]}"\n';
|
|
183
|
+
o += ' local prev="${COMP_WORDS[COMP_CWORD-1]:-}"\n';
|
|
165
184
|
o += " _${ident}_nac_simulate\n".replace("${ident}", ident);
|
|
166
185
|
o += " local sid=$REPLY_SID\n";
|
|
167
|
-
o +=
|
|
186
|
+
o += ' if _${ident}_nac_enum_reply "$sid" "$prev" "$cur"; then return; fi\n'.replace(
|
|
187
|
+
"${ident}",
|
|
188
|
+
ident,
|
|
189
|
+
);
|
|
168
190
|
o += " if [[ $cur == -* ]]; then\n";
|
|
169
|
-
o +=
|
|
191
|
+
o += ' local oname="A_${ident}_${sid}_opts"\n'.replace("${ident}", ident);
|
|
170
192
|
o += " local -a optsarr\n";
|
|
171
|
-
o +=
|
|
172
|
-
o +=
|
|
193
|
+
o += ' local -n optsref="$oname"\n';
|
|
194
|
+
o += ' COMPREPLY=( $(compgen -W "${optsref[*]}" -- "$cur") )\n';
|
|
173
195
|
o += " else\n";
|
|
174
|
-
o +=
|
|
175
|
-
o +=
|
|
196
|
+
o += ' local lname="A_${ident}_${sid}_leaf"\n'.replace("${ident}", ident);
|
|
197
|
+
o += ' local -n leafref="$lname"\n';
|
|
176
198
|
o += " if [[ $leafref -eq 0 ]]; then\n";
|
|
177
|
-
o +=
|
|
199
|
+
o += ' local cname="A_${ident}_${sid}_cmds"\n'.replace("${ident}", ident);
|
|
178
200
|
o += " local -a cmdsarr\n";
|
|
179
|
-
o +=
|
|
180
|
-
o +=
|
|
201
|
+
o += ' local -n cmdsref="$cname"\n';
|
|
202
|
+
o += ' COMPREPLY=( $(compgen -W "${cmdsref[*]}" -- "$cur") )\n';
|
|
181
203
|
o += " else\n";
|
|
182
|
-
o +=
|
|
183
|
-
o +=
|
|
204
|
+
o += ' local pname="A_${ident}_${sid}_pos"\n'.replace("${ident}", ident);
|
|
205
|
+
o += ' local -n posref="$pname"\n';
|
|
184
206
|
o += " if [[ $posref -eq 1 ]]; then\n";
|
|
185
207
|
o += " compopt -o filenames\n";
|
|
186
208
|
o += " fi\n";
|
|
187
209
|
o += " fi\n";
|
|
188
210
|
o += " fi\n";
|
|
189
211
|
o += "}\n\n";
|
|
190
|
-
o += "complete -F _${main} ${schema.key}\n"
|
|
212
|
+
o += "complete -F _${main} ${schema.key}\n"
|
|
213
|
+
.replace("${main}", main)
|
|
214
|
+
.replace("${schema.key}", schema.key);
|
|
191
215
|
return o;
|
|
192
216
|
}
|
|
193
217
|
|
|
@@ -200,23 +224,23 @@ export function completionBashScript(schema: CliRouter): string {
|
|
|
200
224
|
pathIndex[s.path] = i;
|
|
201
225
|
}
|
|
202
226
|
|
|
203
|
-
let out =
|
|
227
|
+
let out = `# Generated bash completion for ${schema.key}.\n\n`;
|
|
204
228
|
|
|
205
229
|
for (const [i, sc] of scopes.entries()) {
|
|
206
|
-
out +=
|
|
207
|
-
out +=
|
|
230
|
+
out += `A_${ident}_${i}_opts=()\n`;
|
|
231
|
+
out += `A_${ident}_${i}_opts+=('${kHelpLong}' '${kHelpShort}')\n`;
|
|
208
232
|
for (const o of sc.opts) {
|
|
209
|
-
out +=
|
|
233
|
+
out += `A_${ident}_${i}_opts+=('--${o.name}')\n`;
|
|
210
234
|
if (o.shortName) {
|
|
211
|
-
out +=
|
|
235
|
+
out += `A_${ident}_${i}_opts+=('-${o.shortName}')\n`;
|
|
212
236
|
}
|
|
213
237
|
}
|
|
214
|
-
out +=
|
|
215
|
-
out +=
|
|
238
|
+
out += `A_${ident}_${i}_leaf=${sc.kids.length === 0 ? "1" : "0"}\n`;
|
|
239
|
+
out += `A_${ident}_${i}_pos=${sc.wantsFiles ? "1" : "0"}\n`;
|
|
216
240
|
if (sc.kids.length > 0) {
|
|
217
|
-
out +=
|
|
241
|
+
out += `A_${ident}_${i}_cmds=(`;
|
|
218
242
|
for (const ch of sc.kids) {
|
|
219
|
-
out +=
|
|
243
|
+
out += ` '${ch.key}'`;
|
|
220
244
|
}
|
|
221
245
|
out += ")\n";
|
|
222
246
|
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CliOptionKind, type CliRouter } from "../types.ts";
|
|
2
2
|
import { collectScopes } from "./scopes.ts";
|
|
3
|
-
import {
|
|
4
|
-
escFishSingleQuoted,
|
|
5
|
-
identToken,
|
|
6
|
-
kHelpLong,
|
|
7
|
-
kHelpShort,
|
|
8
|
-
} from "./shell-helpers.ts";
|
|
3
|
+
import { escFishSingleQuoted, identToken } from "./shell-helpers.ts";
|
|
9
4
|
|
|
10
5
|
function scopeCondition(ident: string, scopeIndex: number, path: string): string {
|
|
11
6
|
const fn = `__${ident}_scope_${scopeIndex}`;
|
|
@@ -30,7 +25,7 @@ export function completionFishScript(schema: CliRouter): string {
|
|
|
30
25
|
const app = schema.key;
|
|
31
26
|
const scopes = collectScopes(schema);
|
|
32
27
|
|
|
33
|
-
let out =
|
|
28
|
+
let out = `# Fish completion for ${app}\n\n`;
|
|
34
29
|
|
|
35
30
|
for (const [i, sc] of scopes.entries()) {
|
|
36
31
|
out += scopeCondition(ident, i, sc.path);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
-
import
|
|
2
|
+
import type { CliProgram, CliRouter } from "../types.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Builds the static `completion` / `bash` / `zsh` / `fish` command subtree (merged into the program root at runtime).
|