argsbarg 3.4.1 → 3.5.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 +26 -1
- package/biome.json +29 -6
- package/bun.lock +22 -0
- package/docs/install.md +1 -1
- package/docs/mcp.md +63 -3
- package/index.d.ts +51 -51
- package/justfile +27 -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 +3 -3
- 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/docs/api-guide.test.ts +2 -2
- package/src/docs/api-guide.ts +2 -2
- package/src/docs/builtin.ts +27 -8
- package/src/docs/docs.test.ts +23 -12
- package/src/docs/mcp-guide.ts +112 -11
- package/src/docs/resolve.ts +10 -3
- package/src/docs/save.ts +11 -3
- package/src/headless.test.ts +8 -16
- package/src/help.ts +73 -43
- package/src/hidden-mcpb.test.ts +8 -10
- package/src/hidden.ts +2 -2
- package/src/index.test.ts +113 -89
- package/src/index.ts +24 -24
- package/src/install/binary.ts +12 -5
- package/src/install/completions.ts +7 -3
- package/src/install/detect-installed.ts +35 -4
- package/src/install/gh-release-update.ts +31 -23
- package/src/install/index.ts +69 -19
- package/src/install/install.test.ts +57 -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 +51 -3
- package/src/install/plan.ts +96 -7
- package/src/install/shell.ts +1 -4
- package/src/install/status.ts +15 -7
- package/src/install/uninstall.ts +49 -5
- 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 +38 -14
- package/src/mcp/env.ts +7 -13
- package/src/mcp/server.ts +12 -6
- package/src/mcp/tools.ts +20 -4
- package/src/mcp.ts +3 -3
- package/src/parse.ts +96 -24
- 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/validate.ts +21 -16
|
@@ -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).
|
|
@@ -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,
|
|
@@ -11,22 +11,41 @@ import {
|
|
|
11
11
|
function emitScopeArraysZsh(ident: string, scopes: ScopeRec[]): string {
|
|
12
12
|
let out = "";
|
|
13
13
|
for (const [i, sc] of scopes.entries()) {
|
|
14
|
-
out +=
|
|
15
|
-
out +=
|
|
16
|
-
out +=
|
|
14
|
+
out += `typeset -g A_${ident}_${i}_opts\n`;
|
|
15
|
+
out += `A_${ident}_${i}_opts=(`;
|
|
16
|
+
out +=
|
|
17
|
+
"'" +
|
|
18
|
+
escShellSingleQuoted(kHelpLong) +
|
|
19
|
+
":" +
|
|
20
|
+
escShellSingleQuoted("Show help for this command.") +
|
|
21
|
+
"' '" +
|
|
22
|
+
escShellSingleQuoted(kHelpShort) +
|
|
23
|
+
":" +
|
|
24
|
+
escShellSingleQuoted("Show help for this command.") +
|
|
25
|
+
"'";
|
|
17
26
|
for (const o of sc.opts) {
|
|
18
|
-
out +=
|
|
27
|
+
out +=
|
|
28
|
+
" '" +
|
|
29
|
+
escShellSingleQuoted(`--${o.name}`) +
|
|
30
|
+
":" +
|
|
31
|
+
escShellSingleQuoted(o.description) +
|
|
32
|
+
"'";
|
|
19
33
|
if (o.shortName) {
|
|
20
|
-
out +=
|
|
34
|
+
out +=
|
|
35
|
+
" '" +
|
|
36
|
+
escShellSingleQuoted(`-${o.shortName}`) +
|
|
37
|
+
":" +
|
|
38
|
+
escShellSingleQuoted(o.description) +
|
|
39
|
+
"'";
|
|
21
40
|
}
|
|
22
41
|
}
|
|
23
42
|
out += ")\n";
|
|
24
|
-
out +=
|
|
25
|
-
out +=
|
|
43
|
+
out += `typeset -g A_${ident}_${i}_leaf=${sc.kids.length === 0 ? "1" : "0"}\n`;
|
|
44
|
+
out += `typeset -g A_${ident}_${i}_pos=${sc.wantsFiles ? "1" : "0"}\n`;
|
|
26
45
|
if (sc.kids.length > 0) {
|
|
27
|
-
out +=
|
|
46
|
+
out += `typeset -g A_${ident}_${i}_cmds=(`;
|
|
28
47
|
for (const ch of sc.kids) {
|
|
29
|
-
out +=
|
|
48
|
+
out += ` '${escShellSingleQuoted(ch.key)}:${escShellSingleQuoted(ch.description)}'`;
|
|
30
49
|
}
|
|
31
50
|
out += ")\n";
|
|
32
51
|
}
|
|
@@ -36,19 +55,24 @@ function emitScopeArraysZsh(ident: string, scopes: ScopeRec[]): string {
|
|
|
36
55
|
|
|
37
56
|
function emitConsumeLongZsh(ident: string, scopes: ScopeRec[]): string {
|
|
38
57
|
let o = "_${ident}_nac_consume_long() {\n".replace("${ident}", ident);
|
|
39
|
-
o +=
|
|
58
|
+
o += ' local sid="$1" w="$2" nw="$3"\n';
|
|
40
59
|
o += " case $sid in\n";
|
|
41
60
|
for (const [i, sc] of scopes.entries()) {
|
|
42
|
-
o +=
|
|
61
|
+
o += ` ${i})\n`;
|
|
43
62
|
o += " case $w in\n";
|
|
44
|
-
o +=
|
|
63
|
+
o +=
|
|
64
|
+
" " +
|
|
65
|
+
kHelpLong +
|
|
66
|
+
"|${kHelpLong}=*|${kHelpShort}) echo 1 ;;\n"
|
|
67
|
+
.replace(/\$\{kHelpLong\}/g, kHelpLong)
|
|
68
|
+
.replace(/\$\{kHelpShort\}/g, kHelpShort);
|
|
45
69
|
for (const op of sc.opts) {
|
|
46
|
-
const base =
|
|
70
|
+
const base = `--${op.name}`;
|
|
47
71
|
if (op.kind === "presence") {
|
|
48
|
-
o +=
|
|
72
|
+
o += ` ${base}${"|${base}=*) echo 1 ;;\n".replace(/\$\{base\}/g, base)}`;
|
|
49
73
|
} else {
|
|
50
|
-
o +=
|
|
51
|
-
o +=
|
|
74
|
+
o += ` ${base}=*) echo 1 ;;\n`;
|
|
75
|
+
o += ` ${base}) echo 2 ;;\n`;
|
|
52
76
|
}
|
|
53
77
|
}
|
|
54
78
|
o += " *) echo 0 ;;\n";
|
|
@@ -63,10 +87,10 @@ function emitConsumeLongZsh(ident: string, scopes: ScopeRec[]): string {
|
|
|
63
87
|
|
|
64
88
|
function emitConsumeShortZsh(ident: string, scopes: ScopeRec[]): string {
|
|
65
89
|
let o = "_${ident}_nac_consume_short() {\n".replace("${ident}", ident);
|
|
66
|
-
o +=
|
|
90
|
+
o += ' local sid="$1" w="$2"\n';
|
|
67
91
|
o += " case $sid in\n";
|
|
68
92
|
for (const [i, sc] of scopes.entries()) {
|
|
69
|
-
o +=
|
|
93
|
+
o += ` ${i})\n`;
|
|
70
94
|
o += " local rest=${w#-}\n";
|
|
71
95
|
o += " local ch\n";
|
|
72
96
|
o += " local saw=0\n";
|
|
@@ -78,16 +102,16 @@ function emitConsumeShortZsh(ident: string, scopes: ScopeRec[]): string {
|
|
|
78
102
|
for (const op of sc.opts) {
|
|
79
103
|
if (!op.shortName) continue;
|
|
80
104
|
if (op.kind === "presence") {
|
|
81
|
-
boolChars += op.shortName
|
|
105
|
+
boolChars += `${op.shortName}|`;
|
|
82
106
|
} else {
|
|
83
|
-
o +=
|
|
107
|
+
o += ` ${op.shortName})\n`;
|
|
84
108
|
o += " if [[ $saw -ne 0 || -n $rest ]]; then echo 0; return; fi\n";
|
|
85
109
|
o += " echo 2; return ;;\n";
|
|
86
110
|
}
|
|
87
111
|
}
|
|
88
112
|
if (boolChars.length > 0) {
|
|
89
113
|
boolChars = boolChars.slice(0, -1);
|
|
90
|
-
o +=
|
|
114
|
+
o += ` ${boolChars}) ;;\n`;
|
|
91
115
|
}
|
|
92
116
|
o += " *) echo 0; return ;;\n";
|
|
93
117
|
o += " esac\n";
|
|
@@ -102,18 +126,22 @@ function emitConsumeShortZsh(ident: string, scopes: ScopeRec[]): string {
|
|
|
102
126
|
return o;
|
|
103
127
|
}
|
|
104
128
|
|
|
105
|
-
function emitMatchChildZsh(
|
|
129
|
+
function emitMatchChildZsh(
|
|
130
|
+
ident: string,
|
|
131
|
+
scopes: ScopeRec[],
|
|
132
|
+
pathIndex: Record<string, number>,
|
|
133
|
+
): string {
|
|
106
134
|
let o = "_${ident}_nac_match_child() {\n".replace("${ident}", ident);
|
|
107
|
-
o +=
|
|
135
|
+
o += ' local sid="$1" w="$2"\n';
|
|
108
136
|
o += " case $sid in\n";
|
|
109
137
|
for (const [sid, sc] of scopes.entries()) {
|
|
110
138
|
if (sc.kids.length === 0) continue;
|
|
111
|
-
o +=
|
|
139
|
+
o += ` ${sid})\n`;
|
|
112
140
|
o += " case $w in\n";
|
|
113
141
|
for (const ch of sc.kids) {
|
|
114
|
-
const childPath = sc.path === "" ? ch.key : sc.path
|
|
142
|
+
const childPath = sc.path === "" ? ch.key : `${sc.path}/${ch.key}`;
|
|
115
143
|
const cid = pathIndex[childPath] ?? 0;
|
|
116
|
-
o +=
|
|
144
|
+
o += ` ${ch.key}) echo ${cid}; return 0 ;;\n`;
|
|
117
145
|
}
|
|
118
146
|
o += " esac\n";
|
|
119
147
|
o += " ;;\n";
|
|
@@ -129,11 +157,14 @@ function emitSimulateZsh(ident: string): string {
|
|
|
129
157
|
o += " local i=2 sid=0 w steps next\n";
|
|
130
158
|
o += " while (( i < CURRENT )); do\n";
|
|
131
159
|
o += " w=$words[i]\n";
|
|
132
|
-
o +=
|
|
160
|
+
o += ` if [[ $w == ${kHelpShort} || $w == ${kHelpLong} ]]; then\n`;
|
|
133
161
|
o += " ((i++)); continue\n";
|
|
134
162
|
o += " fi\n";
|
|
135
163
|
o += " if [[ $w == --* ]]; then\n";
|
|
136
|
-
o +=
|
|
164
|
+
o += ' steps=$(_${ident}_nac_consume_long "$sid" "$w" "${words[i+1]}")\n'.replace(
|
|
165
|
+
"${ident}",
|
|
166
|
+
ident,
|
|
167
|
+
);
|
|
137
168
|
o += " case $steps in\n";
|
|
138
169
|
o += " 0) break ;;\n";
|
|
139
170
|
o += " 1) ((i++)) ;;\n";
|
|
@@ -143,7 +174,7 @@ function emitSimulateZsh(ident: string): string {
|
|
|
143
174
|
o += " continue\n";
|
|
144
175
|
o += " fi\n";
|
|
145
176
|
o += " if [[ $w == -* ]]; then\n";
|
|
146
|
-
o +=
|
|
177
|
+
o += ' steps=$(_${ident}_nac_consume_short "$sid" "$w")\n'.replace("${ident}", ident);
|
|
147
178
|
o += " case $steps in\n";
|
|
148
179
|
o += " 0) break ;;\n";
|
|
149
180
|
o += " 1) ((i++)) ;;\n";
|
|
@@ -152,7 +183,7 @@ function emitSimulateZsh(ident: string): string {
|
|
|
152
183
|
o += " esac\n";
|
|
153
184
|
o += " continue\n";
|
|
154
185
|
o += " fi\n";
|
|
155
|
-
o +=
|
|
186
|
+
o += ' next=$(_${ident}_nac_match_child "$sid" "$w") || break\n'.replace("${ident}", ident);
|
|
156
187
|
o += " sid=$next\n";
|
|
157
188
|
o += " ((i++))\n";
|
|
158
189
|
o += " done\n";
|
|
@@ -166,13 +197,15 @@ function emitEnumReplyZsh(ident: string, scopes: ScopeRec[]): string {
|
|
|
166
197
|
o += " local sid=$1 prev=$2\n";
|
|
167
198
|
o += " case $sid in\n";
|
|
168
199
|
for (const [i, sc] of scopes.entries()) {
|
|
169
|
-
const enumOpts = sc.opts.filter(
|
|
200
|
+
const enumOpts = sc.opts.filter(
|
|
201
|
+
(op) => op.kind === CliOptionKind.Enum && (op.choices?.length ?? 0) > 0,
|
|
202
|
+
);
|
|
170
203
|
if (enumOpts.length === 0) continue;
|
|
171
|
-
o +=
|
|
204
|
+
o += ` ${i})\n`;
|
|
172
205
|
o += " case $prev in\n";
|
|
173
206
|
for (const op of enumOpts) {
|
|
174
207
|
const vals = (op.choices ?? []).map((c) => escShellSingleQuoted(c)).join(" ");
|
|
175
|
-
o +=
|
|
208
|
+
o += ` --${op.name}) _values ${vals}; return 0 ;;\n`;
|
|
176
209
|
}
|
|
177
210
|
o += " esac\n";
|
|
178
211
|
o += " ;;\n";
|
|
@@ -186,24 +219,27 @@ function emitEnumReplyZsh(ident: string, scopes: ScopeRec[]): string {
|
|
|
186
219
|
function emitMainBodyZsh(schema: CliRouter, ident: string): string {
|
|
187
220
|
const main = mainName(schema.key);
|
|
188
221
|
let o = "_${main}() {\n".replace("${main}", main);
|
|
189
|
-
o +=
|
|
222
|
+
o += ' local curcontext="$curcontext" ret=1\n';
|
|
190
223
|
o += " _${ident}_nac_simulate\n".replace("${ident}", ident);
|
|
191
224
|
o += " local sid=$REPLY_SID\n";
|
|
192
|
-
o +=
|
|
225
|
+
o += ' if _${ident}_nac_enum_reply "$sid" "$words[CURRENT-1]"; then return 0; fi\n'.replace(
|
|
226
|
+
"${ident}",
|
|
227
|
+
ident,
|
|
228
|
+
);
|
|
193
229
|
o += " if [[ $PREFIX == -* ]]; then\n";
|
|
194
230
|
o += " local -a optsarr\n";
|
|
195
|
-
o +=
|
|
231
|
+
o += ' local oname="A_${ident}_${sid}_opts"\n'.replace("${ident}", ident);
|
|
196
232
|
o += " optsarr=(${(@P)oname})\n";
|
|
197
233
|
o += " _describe -t options 'option' optsarr && ret=0\n";
|
|
198
234
|
o += " else\n";
|
|
199
|
-
o +=
|
|
235
|
+
o += ' local lname="A_${ident}_${sid}_leaf"\n'.replace("${ident}", ident);
|
|
200
236
|
o += " if [[ ${(P)lname} -eq 0 ]]; then\n";
|
|
201
237
|
o += " local -a cmdsarr\n";
|
|
202
|
-
o +=
|
|
238
|
+
o += ' local cname="A_${ident}_${sid}_cmds"\n'.replace("${ident}", ident);
|
|
203
239
|
o += " cmdsarr=(${(@P)cname})\n";
|
|
204
240
|
o += " _describe -t commands 'command' cmdsarr && ret=0\n";
|
|
205
241
|
o += " else\n";
|
|
206
|
-
o +=
|
|
242
|
+
o += ' local pname="A_${ident}_${sid}_pos"\n'.replace("${ident}", ident);
|
|
207
243
|
o += " if [[ ${(P)pname} -eq 1 ]]; then\n";
|
|
208
244
|
o += " _files && ret=0\n";
|
|
209
245
|
o += " fi\n";
|
|
@@ -211,7 +247,9 @@ function emitMainBodyZsh(schema: CliRouter, ident: string): string {
|
|
|
211
247
|
o += " fi\n";
|
|
212
248
|
o += " return ret\n";
|
|
213
249
|
o += "}\n\n";
|
|
214
|
-
o += "compdef _${main} ${schema.key}\n"
|
|
250
|
+
o += "compdef _${main} ${schema.key}\n"
|
|
251
|
+
.replace("${main}", main)
|
|
252
|
+
.replace("${schema.key}", schema.key);
|
|
215
253
|
return o;
|
|
216
254
|
}
|
|
217
255
|
|
|
@@ -224,7 +262,7 @@ export function completionZshScript(schema: CliRouter): string {
|
|
|
224
262
|
pathIndex[s.path] = i;
|
|
225
263
|
}
|
|
226
264
|
|
|
227
|
-
let out =
|
|
265
|
+
let out = `#compdef ${schema.key}\n\n`;
|
|
228
266
|
out += emitScopeArraysZsh(ident, scopes);
|
|
229
267
|
out += emitConsumeLongZsh(ident, scopes);
|
|
230
268
|
out += emitConsumeShortZsh(ident, scopes);
|
package/src/builtins/dispatch.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
3
|
+
import { cliInstall } from "../install/index.ts";
|
|
4
|
+
import { runMcpBundle } from "../mcp/bundle.ts";
|
|
5
|
+
import { cliMcpServeStdio } from "../mcp.ts";
|
|
6
|
+
import type { ParseResult } from "../parse.ts";
|
|
7
|
+
import { ParseKind } from "../parse.ts";
|
|
2
8
|
import type { CliNode, CliProgram, CliRouter } from "../types.ts";
|
|
3
|
-
import { isCliLeaf
|
|
9
|
+
import { isCliLeaf } from "../types.ts";
|
|
4
10
|
import { completionBashScript } from "./completion-bash.ts";
|
|
5
11
|
import { completionFishScript } from "./completion-fish.ts";
|
|
12
|
+
import { cliBuiltinCompletionGroup as completionGroup } from "./completion-group.ts";
|
|
6
13
|
import { completionZshScript } from "./completion-zsh.ts";
|
|
7
14
|
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
8
15
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
9
|
-
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
10
|
-
import { cliBuiltinCompletionGroup as completionGroup } from "./completion-group.ts";
|
|
11
16
|
import { cliPresentationRoot } from "./presentation.ts";
|
|
12
|
-
import {
|
|
13
|
-
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
14
|
-
import { cliMcpServeStdio } from "../mcp.ts";
|
|
15
|
-
import { cliInstall } from "../install/index.ts";
|
|
16
|
-
import type { ParseResult } from "../parse.ts";
|
|
17
|
-
import { ParseKind } from "../parse.ts";
|
|
17
|
+
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
18
18
|
|
|
19
19
|
export interface DispatchBuiltinOpts {
|
|
20
20
|
isLeafCompletionIntercept: boolean;
|
|
@@ -61,16 +61,18 @@ export async function dispatchBuiltin(
|
|
|
61
61
|
|
|
62
62
|
if (pr.path[0] === "version") {
|
|
63
63
|
if (pr.path.length !== 1) {
|
|
64
|
-
process.stderr.write(
|
|
64
|
+
process.stderr.write(`Unknown subcommand: version ${pr.path.slice(1).join(" ")}\n`);
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
|
67
|
-
process.stdout.write(program.version
|
|
67
|
+
process.stdout.write(`${program.version}\n`);
|
|
68
68
|
process.exit(0);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
if (pr.path[0] === "mcp") {
|
|
72
72
|
if (!caps.mcp) {
|
|
73
|
-
process.stderr.write(
|
|
73
|
+
process.stderr.write(
|
|
74
|
+
"MCP is not enabled. Set mcpServer: { enabled: true } on the program root.\n",
|
|
75
|
+
);
|
|
74
76
|
process.exit(1);
|
|
75
77
|
}
|
|
76
78
|
const sub = pr.path[1];
|
|
@@ -82,22 +84,24 @@ export async function dispatchBuiltin(
|
|
|
82
84
|
try {
|
|
83
85
|
runMcpBundle(program);
|
|
84
86
|
} catch (err) {
|
|
85
|
-
process.stderr.write(err instanceof Error ? err.message
|
|
87
|
+
process.stderr.write(err instanceof Error ? `${err.message}\n` : "mcp bundle failed.\n");
|
|
86
88
|
process.exit(1);
|
|
87
89
|
}
|
|
88
90
|
process.exit(0);
|
|
89
91
|
}
|
|
90
|
-
process.stderr.write(
|
|
92
|
+
process.stderr.write(`Unknown subcommand: mcp ${pr.path.slice(1).join(" ")}\n`);
|
|
91
93
|
process.exit(1);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
if (pr.path[0] === "install") {
|
|
95
97
|
if (!caps.install) {
|
|
96
|
-
process.stderr.write(
|
|
98
|
+
process.stderr.write(
|
|
99
|
+
"install is disabled. Remove install.enabled: false from the program root.\n",
|
|
100
|
+
);
|
|
97
101
|
process.exit(1);
|
|
98
102
|
}
|
|
99
103
|
if (pr.path.length !== 1) {
|
|
100
|
-
process.stderr.write(
|
|
104
|
+
process.stderr.write(`Unknown subcommand: install ${pr.path.slice(1).join(" ")}\n`);
|
|
101
105
|
process.exit(1);
|
|
102
106
|
}
|
|
103
107
|
await cliInstall(program, pr.opts);
|