magus-cli 7.11.8 → 7.12.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/package.json +4 -4
- package/src/__tests__/alias-agent-teams.test.ts +281 -0
- package/src/__tests__/alias-autocompact.test.ts +169 -0
- package/src/__tests__/alias-env-adoption.test.ts +475 -0
- package/src/__tests__/alias-option-compatibility.test.ts +92 -0
- package/src/data/alias-flags.ts +114 -1
- package/src/services/alias-shell-writer.ts +129 -21
- package/src/ui/components/modals/InputModal.tsx +5 -0
- package/src/ui/screens/AliasScreen.tsx +31 -6
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
argsToFlagValuesWithLeftovers,
|
|
4
|
+
findAdoptableAlias,
|
|
5
|
+
parseAliasFromRc,
|
|
6
|
+
parseAliasLine,
|
|
7
|
+
parseManagedBlock,
|
|
8
|
+
renderAlias,
|
|
9
|
+
renderArgsAsTokens,
|
|
10
|
+
} from "../services/alias-shell-writer";
|
|
11
|
+
import type { AliasConfig, FlagValue } from "../services/alias-store";
|
|
12
|
+
|
|
13
|
+
const assignment = "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1";
|
|
14
|
+
const modes = ["tmux", "iterm2", "in-process", "auto"];
|
|
15
|
+
|
|
16
|
+
function combinedFlags(): Record<string, FlagValue> {
|
|
17
|
+
return {
|
|
18
|
+
"agent-teams": { kind: "select", enabled: true, value: "tmux" },
|
|
19
|
+
autocompact: { kind: "text", enabled: true, value: "200k" },
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const forms: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
envLauncher: boolean;
|
|
26
|
+
line: (body: string) => string;
|
|
27
|
+
}> = [
|
|
28
|
+
{ name: "POSIX", envLauncher: false, line: (body) => `alias crew='${body}'` },
|
|
29
|
+
// The env launcher is a body form; the parser contract uses POSIX alias lines.
|
|
30
|
+
{
|
|
31
|
+
name: "POSIX with env launcher",
|
|
32
|
+
envLauncher: true,
|
|
33
|
+
line: (body) => `alias crew='${body}'`,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function commandBody(
|
|
38
|
+
envLauncher: boolean,
|
|
39
|
+
env: string[],
|
|
40
|
+
argv: string[],
|
|
41
|
+
): string {
|
|
42
|
+
return [
|
|
43
|
+
...(envLauncher && env.length > 0 ? ["env"] : []),
|
|
44
|
+
...env,
|
|
45
|
+
"claude",
|
|
46
|
+
...argv,
|
|
47
|
+
].join(" ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("single agent-team parse rows", () => {
|
|
51
|
+
type ParseRow = {
|
|
52
|
+
name: string;
|
|
53
|
+
env: string[];
|
|
54
|
+
argv: string[];
|
|
55
|
+
flags: () => Record<string, FlagValue>;
|
|
56
|
+
leftovers: string[];
|
|
57
|
+
};
|
|
58
|
+
const rows: ParseRow[] = [
|
|
59
|
+
...modes.map(
|
|
60
|
+
(mode): ParseRow => ({
|
|
61
|
+
name: `assignment and ${mode} argv become one select`,
|
|
62
|
+
env: [assignment],
|
|
63
|
+
argv: ["--teammate-mode", mode],
|
|
64
|
+
flags: () => ({
|
|
65
|
+
"agent-teams": { kind: "select", enabled: true, value: mode },
|
|
66
|
+
}),
|
|
67
|
+
leftovers: [],
|
|
68
|
+
}),
|
|
69
|
+
),
|
|
70
|
+
{
|
|
71
|
+
name: "assignment alone defaults to auto",
|
|
72
|
+
env: [assignment],
|
|
73
|
+
argv: [],
|
|
74
|
+
flags: () => ({
|
|
75
|
+
"agent-teams": { kind: "select", enabled: true, value: "auto" },
|
|
76
|
+
}),
|
|
77
|
+
leftovers: [],
|
|
78
|
+
},
|
|
79
|
+
...modes.map(
|
|
80
|
+
(mode): ParseRow => ({
|
|
81
|
+
name: `${mode} argv alone remains leftovers`,
|
|
82
|
+
env: [],
|
|
83
|
+
argv: ["--teammate-mode", mode],
|
|
84
|
+
flags: () => ({}),
|
|
85
|
+
leftovers: ["--teammate-mode", mode],
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
it(`TEST-24 REQ-12 REQ-14 maps tokens so ${row.name}`, () => {
|
|
92
|
+
const args = [...row.env, ...row.argv];
|
|
93
|
+
|
|
94
|
+
expect(argsToFlagValuesWithLeftovers(args)).toEqual({
|
|
95
|
+
flags: row.flags(),
|
|
96
|
+
leftovers: [...row.leftovers],
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
for (const form of forms) {
|
|
101
|
+
it(`TEST-25 REQ-11 REQ-12 REQ-14 parses ${form.name} so ${row.name}`, () => {
|
|
102
|
+
const line = form.line(
|
|
103
|
+
commandBody(form.envLauncher, row.env, row.argv),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const parsed = parseAliasLine(line);
|
|
107
|
+
|
|
108
|
+
expect(parsed).toEqual({
|
|
109
|
+
aliasName: "crew",
|
|
110
|
+
args: [...row.env, ...row.argv],
|
|
111
|
+
});
|
|
112
|
+
if (parsed === null) {
|
|
113
|
+
throw new Error("Expected a parsed claude alias");
|
|
114
|
+
}
|
|
115
|
+
expect(argsToFlagValuesWithLeftovers(parsed.args)).toEqual({
|
|
116
|
+
flags: row.flags(),
|
|
117
|
+
leftovers: [...row.leftovers],
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("managed assignment-prefixed alias parsing", () => {
|
|
125
|
+
const examples = [
|
|
126
|
+
{
|
|
127
|
+
name: "POSIX known assignment",
|
|
128
|
+
line: `alias crew='${assignment} claude --teammate-mode tmux --autocompact 200k'`,
|
|
129
|
+
args: [assignment, "--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "POSIX alias with env launcher",
|
|
133
|
+
line: `alias crew='env ${assignment} claude --teammate-mode tmux --autocompact 200k'`,
|
|
134
|
+
args: [assignment, "--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "POSIX unknown before known assignment",
|
|
138
|
+
line: `alias crew='EXTRA=keep ${assignment} claude --autocompact 200k'`,
|
|
139
|
+
args: ["EXTRA=keep", assignment, "--autocompact", "200k"],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "POSIX env launcher with unknown after known assignment",
|
|
143
|
+
line: `alias crew='env ${assignment} EXTRA=a=b claude --teammate-mode tmux'`,
|
|
144
|
+
args: [assignment, "EXTRA=a=b", "--teammate-mode", "tmux"],
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
for (const example of examples) {
|
|
149
|
+
it(`TEST-15 REQ-11 preserves env-first tokens for ${example.name}`, () => {
|
|
150
|
+
const block = [
|
|
151
|
+
"# >>> magus managed alias >>>",
|
|
152
|
+
example.line,
|
|
153
|
+
"# <<< magus managed alias <<<",
|
|
154
|
+
"",
|
|
155
|
+
].join("\n");
|
|
156
|
+
const expected = { aliasName: "crew", args: [...example.args] };
|
|
157
|
+
|
|
158
|
+
expect(parseAliasLine(example.line)).toEqual(expected);
|
|
159
|
+
expect(parseManagedBlock(block)).toEqual(expected);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("assignment mapping and leftovers", () => {
|
|
165
|
+
const mappingCases: Array<{
|
|
166
|
+
name: string;
|
|
167
|
+
args: string[];
|
|
168
|
+
flags: () => Record<string, FlagValue>;
|
|
169
|
+
leftovers: string[];
|
|
170
|
+
}> = [
|
|
171
|
+
{
|
|
172
|
+
name: "known assignment and all new argv options",
|
|
173
|
+
args: [assignment, "--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
174
|
+
flags: combinedFlags,
|
|
175
|
+
leftovers: [],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "unknown assignment before the known one",
|
|
179
|
+
args: [
|
|
180
|
+
"EXTRA=keep",
|
|
181
|
+
assignment,
|
|
182
|
+
"--teammate-mode",
|
|
183
|
+
"tmux",
|
|
184
|
+
"--autocompact",
|
|
185
|
+
"200k",
|
|
186
|
+
],
|
|
187
|
+
flags: combinedFlags,
|
|
188
|
+
leftovers: ["EXTRA=keep"],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "unknown assignments after the known one",
|
|
192
|
+
args: [
|
|
193
|
+
assignment,
|
|
194
|
+
"EMPTY=",
|
|
195
|
+
"EXTRA=a=b",
|
|
196
|
+
"--teammate-mode",
|
|
197
|
+
"tmux",
|
|
198
|
+
"--autocompact",
|
|
199
|
+
"200k",
|
|
200
|
+
],
|
|
201
|
+
flags: combinedFlags,
|
|
202
|
+
leftovers: ["EMPTY=", "EXTRA=a=b"],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "unknown assignments on both sides of the known one",
|
|
206
|
+
args: [
|
|
207
|
+
"FIRST=one",
|
|
208
|
+
assignment,
|
|
209
|
+
"EMPTY=",
|
|
210
|
+
"EXTRA=a=b",
|
|
211
|
+
"--teammate-mode",
|
|
212
|
+
"tmux",
|
|
213
|
+
"--autocompact",
|
|
214
|
+
"200k",
|
|
215
|
+
],
|
|
216
|
+
flags: combinedFlags,
|
|
217
|
+
leftovers: ["FIRST=one", "EMPTY=", "EXTRA=a=b"],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "known env name with a different value",
|
|
221
|
+
args: ["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=0", "--ide"],
|
|
222
|
+
flags: () => ({ ide: { kind: "boolean", enabled: true } }),
|
|
223
|
+
leftovers: ["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=0"],
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
for (const example of mappingCases) {
|
|
228
|
+
it(`TEST-16 REQ-12 retains every unmatched token for ${example.name}`, () => {
|
|
229
|
+
const args = [...example.args];
|
|
230
|
+
|
|
231
|
+
expect(argsToFlagValuesWithLeftovers(args)).toEqual({
|
|
232
|
+
flags: example.flags(),
|
|
233
|
+
leftovers: [...example.leftovers],
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
it("TEST-17 REQ-12 keeps an assignment after an argv flag as a leftover rather than enabling teams", () => {
|
|
239
|
+
const args = ["--ide", assignment];
|
|
240
|
+
|
|
241
|
+
expect(argsToFlagValuesWithLeftovers(args)).toEqual({
|
|
242
|
+
flags: { ide: { kind: "boolean", enabled: true } },
|
|
243
|
+
leftovers: [assignment],
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("TEST-17 REQ-12 retains an argv assignment without losing an earlier recognized env assignment", () => {
|
|
248
|
+
const args = [assignment, "--ide", "EXTRA=keep"];
|
|
249
|
+
|
|
250
|
+
expect(argsToFlagValuesWithLeftovers(args)).toEqual({
|
|
251
|
+
flags: {
|
|
252
|
+
"agent-teams": { kind: "select", enabled: true, value: "auto" },
|
|
253
|
+
ide: { kind: "boolean", enabled: true },
|
|
254
|
+
},
|
|
255
|
+
leftovers: ["EXTRA=keep"],
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("TEST-17 REQ-12 leaves an assignment used as a text value inside that text flag", () => {
|
|
260
|
+
const args = ["--append-system-prompt", assignment];
|
|
261
|
+
|
|
262
|
+
expect(argsToFlagValuesWithLeftovers(args)).toEqual({
|
|
263
|
+
flags: {
|
|
264
|
+
"append-system-prompt": {
|
|
265
|
+
kind: "text",
|
|
266
|
+
enabled: true,
|
|
267
|
+
value: assignment,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
leftovers: [],
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("safe adoption of assignment-prefixed aliases", () => {
|
|
276
|
+
type LosslessCase = {
|
|
277
|
+
name: string;
|
|
278
|
+
env: string[];
|
|
279
|
+
argv: string[];
|
|
280
|
+
flags: () => Record<string, FlagValue>;
|
|
281
|
+
renderedArgs: string[];
|
|
282
|
+
};
|
|
283
|
+
const losslessCases: LosslessCase[] = [
|
|
284
|
+
{
|
|
285
|
+
name: "context size only",
|
|
286
|
+
env: [],
|
|
287
|
+
argv: ["--autocompact", "200k"],
|
|
288
|
+
flags: () => ({
|
|
289
|
+
autocompact: { kind: "text", enabled: true, value: "200k" },
|
|
290
|
+
}),
|
|
291
|
+
renderedArgs: ["--autocompact", "200k"],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: "agent teams assignment alone as auto",
|
|
295
|
+
env: [assignment],
|
|
296
|
+
argv: [],
|
|
297
|
+
flags: () => ({
|
|
298
|
+
"agent-teams": { kind: "select", enabled: true, value: "auto" },
|
|
299
|
+
}),
|
|
300
|
+
renderedArgs: [assignment, "--teammate-mode", "auto"],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: "all new options",
|
|
304
|
+
env: [assignment],
|
|
305
|
+
argv: ["--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
306
|
+
flags: combinedFlags,
|
|
307
|
+
renderedArgs: [
|
|
308
|
+
assignment,
|
|
309
|
+
"--autocompact",
|
|
310
|
+
"200k",
|
|
311
|
+
"--teammate-mode",
|
|
312
|
+
"tmux",
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
...modes.map(
|
|
316
|
+
(mode): LosslessCase => ({
|
|
317
|
+
name: `agent teams with explicit ${mode} mode`,
|
|
318
|
+
env: [assignment],
|
|
319
|
+
argv: ["--teammate-mode", mode],
|
|
320
|
+
flags: () => ({
|
|
321
|
+
"agent-teams": { kind: "select", enabled: true, value: mode },
|
|
322
|
+
}),
|
|
323
|
+
renderedArgs: [assignment, "--teammate-mode", mode],
|
|
324
|
+
}),
|
|
325
|
+
),
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
const unknownCases: Array<{
|
|
329
|
+
name: string;
|
|
330
|
+
env: string[];
|
|
331
|
+
argv: string[];
|
|
332
|
+
flags: () => Record<string, FlagValue>;
|
|
333
|
+
leftovers: string[];
|
|
334
|
+
}> = [
|
|
335
|
+
{
|
|
336
|
+
name: "an unknown assignment alone",
|
|
337
|
+
env: ["EXTRA=keep"],
|
|
338
|
+
argv: ["--ide"],
|
|
339
|
+
flags: () => ({ ide: { kind: "boolean", enabled: true } }),
|
|
340
|
+
leftovers: ["EXTRA=keep"],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "an empty-valued unknown assignment before known teams",
|
|
344
|
+
env: ["EMPTY=", assignment],
|
|
345
|
+
argv: ["--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
346
|
+
flags: combinedFlags,
|
|
347
|
+
leftovers: ["EMPTY="],
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: "an unknown assignment containing another equals sign after known teams",
|
|
351
|
+
env: [assignment, "EXTRA=a=b"],
|
|
352
|
+
argv: ["--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
353
|
+
flags: combinedFlags,
|
|
354
|
+
leftovers: ["EXTRA=a=b"],
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "unknown assignments on both sides of known teams in their original order",
|
|
358
|
+
env: ["FIRST=one", assignment, "EMPTY=", "EXTRA=a=b"],
|
|
359
|
+
argv: ["--teammate-mode", "tmux", "--autocompact", "200k"],
|
|
360
|
+
flags: combinedFlags,
|
|
361
|
+
leftovers: ["FIRST=one", "EMPTY=", "EXTRA=a=b"],
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "a known assignment name with an unsupported value",
|
|
365
|
+
env: ["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=0"],
|
|
366
|
+
argv: ["--ide"],
|
|
367
|
+
flags: () => ({ ide: { kind: "boolean", enabled: true } }),
|
|
368
|
+
leftovers: ["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=0"],
|
|
369
|
+
},
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
for (const form of forms) {
|
|
373
|
+
for (const example of losslessCases) {
|
|
374
|
+
it(`TEST-18 REQ-11 REQ-13 adopts ${example.name} losslessly in ${form.name}`, () => {
|
|
375
|
+
const line = form.line(
|
|
376
|
+
commandBody(form.envLauncher, example.env, example.argv),
|
|
377
|
+
);
|
|
378
|
+
const rc = `# personal aliases\n${line}\n# end aliases\n`;
|
|
379
|
+
const expectedFlags = example.flags();
|
|
380
|
+
|
|
381
|
+
const adopted = findAdoptableAlias(rc);
|
|
382
|
+
|
|
383
|
+
expect(adopted).toEqual({
|
|
384
|
+
name: "crew",
|
|
385
|
+
flags: expectedFlags,
|
|
386
|
+
lineRange: [1, 1],
|
|
387
|
+
leftovers: [],
|
|
388
|
+
lossless: true,
|
|
389
|
+
rawLine: line,
|
|
390
|
+
others: [],
|
|
391
|
+
});
|
|
392
|
+
if (adopted === null) {
|
|
393
|
+
throw new Error("Expected a lossless adoptable alias");
|
|
394
|
+
}
|
|
395
|
+
const config: AliasConfig = {
|
|
396
|
+
aliasName: adopted.name,
|
|
397
|
+
flags: adopted.flags,
|
|
398
|
+
};
|
|
399
|
+
const renderedArgs = renderArgsAsTokens(config);
|
|
400
|
+
expect(renderedArgs).toEqual(example.renderedArgs);
|
|
401
|
+
expect(argsToFlagValuesWithLeftovers(renderedArgs)).toEqual({
|
|
402
|
+
flags: expectedFlags,
|
|
403
|
+
leftovers: [],
|
|
404
|
+
});
|
|
405
|
+
expect(parseAliasFromRc(renderAlias(config, "bash").block)).toEqual({
|
|
406
|
+
aliasName: "crew",
|
|
407
|
+
flags: expectedFlags,
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
for (const example of unknownCases) {
|
|
413
|
+
it(`TEST-19 REQ-11 REQ-12 REQ-13 preserves ${example.name} as a leftover in ${form.name}`, () => {
|
|
414
|
+
const line = form.line(
|
|
415
|
+
commandBody(form.envLauncher, example.env, example.argv),
|
|
416
|
+
);
|
|
417
|
+
const rc = `# personal aliases\n${line}\n# end aliases\n`;
|
|
418
|
+
|
|
419
|
+
const adopted = findAdoptableAlias(rc);
|
|
420
|
+
|
|
421
|
+
expect(adopted).toEqual({
|
|
422
|
+
name: "crew",
|
|
423
|
+
flags: example.flags(),
|
|
424
|
+
lineRange: [1, 1],
|
|
425
|
+
leftovers: [...example.leftovers],
|
|
426
|
+
lossless: false,
|
|
427
|
+
rawLine: line,
|
|
428
|
+
others: [],
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
it(`TEST-20 REQ-13 REQ-14 refuses lossless adoption of orphan teammate-mode in ${form.name}`, () => {
|
|
434
|
+
const line = form.line("claude --teammate-mode tmux");
|
|
435
|
+
|
|
436
|
+
const adopted = findAdoptableAlias(`${line}\n`);
|
|
437
|
+
|
|
438
|
+
expect(adopted).toEqual({
|
|
439
|
+
name: "crew",
|
|
440
|
+
flags: {},
|
|
441
|
+
lineRange: [0, 0],
|
|
442
|
+
leftovers: ["--teammate-mode", "tmux"],
|
|
443
|
+
lossless: false,
|
|
444
|
+
rawLine: line,
|
|
445
|
+
others: [],
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it(`TEST-26 REQ-12 REQ-13 REQ-14 keeps context size but refuses lossless argv-only teams in ${form.name}`, () => {
|
|
450
|
+
const line = form.line("claude --teammate-mode tmux --autocompact 200k");
|
|
451
|
+
|
|
452
|
+
const adopted = findAdoptableAlias(`${line}\n`);
|
|
453
|
+
|
|
454
|
+
expect(adopted).toEqual({
|
|
455
|
+
name: "crew",
|
|
456
|
+
flags: {
|
|
457
|
+
autocompact: { kind: "text", enabled: true, value: "200k" },
|
|
458
|
+
},
|
|
459
|
+
lineRange: [0, 0],
|
|
460
|
+
leftovers: ["--teammate-mode", "tmux"],
|
|
461
|
+
lossless: false,
|
|
462
|
+
rawLine: line,
|
|
463
|
+
others: [],
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it(`TEST-21 REQ-11 rejects an assignment-prefixed non-claude alias in ${form.name}`, () => {
|
|
468
|
+
const prefix = form.envLauncher ? "env " : "";
|
|
469
|
+
const line = form.line(`${prefix + assignment} other-command --ide`);
|
|
470
|
+
|
|
471
|
+
expect(parseAliasLine(line)).toBeNull();
|
|
472
|
+
expect(findAdoptableAlias(`${line}\n`)).toBeNull();
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
type ShellKind,
|
|
4
|
+
renderAlias,
|
|
5
|
+
renderArgsAsTokens,
|
|
6
|
+
renderEnv,
|
|
7
|
+
} from "../services/alias-shell-writer";
|
|
8
|
+
import {
|
|
9
|
+
type AliasConfig,
|
|
10
|
+
type FlagValue,
|
|
11
|
+
defaultAliasConfig,
|
|
12
|
+
} from "../services/alias-store";
|
|
13
|
+
|
|
14
|
+
const shells: ShellKind[] = ["bash", "zsh", "fish"];
|
|
15
|
+
|
|
16
|
+
describe("alias output when the new options are not enabled", () => {
|
|
17
|
+
const establishedCases: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
flags: () => Record<string, FlagValue>;
|
|
20
|
+
tokens: string[];
|
|
21
|
+
posixLine: string;
|
|
22
|
+
fishLine: string;
|
|
23
|
+
}> = [
|
|
24
|
+
{
|
|
25
|
+
name: "plain claude alias",
|
|
26
|
+
flags: () => ({}),
|
|
27
|
+
tokens: [],
|
|
28
|
+
posixLine: "alias c='claude'",
|
|
29
|
+
fishLine: "alias c 'claude'",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "ide-only alias",
|
|
33
|
+
flags: () => ({ ide: { kind: "boolean", enabled: true } }),
|
|
34
|
+
tokens: ["--ide"],
|
|
35
|
+
posixLine: "alias c='claude --ide'",
|
|
36
|
+
fishLine: "alias c 'claude '--ide''",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
for (const shell of shells) {
|
|
41
|
+
for (const example of establishedCases) {
|
|
42
|
+
it(`TEST-22 REQ-10 preserves established ${example.name} bytes on ${shell} with absent or disabled new options`, () => {
|
|
43
|
+
const absent: AliasConfig = {
|
|
44
|
+
aliasName: "c",
|
|
45
|
+
flags: example.flags(),
|
|
46
|
+
};
|
|
47
|
+
const disabled: AliasConfig = {
|
|
48
|
+
aliasName: "c",
|
|
49
|
+
flags: {
|
|
50
|
+
...example.flags(),
|
|
51
|
+
autocompact: { kind: "text", enabled: false, value: "200k" },
|
|
52
|
+
"agent-teams": { kind: "boolean", enabled: false },
|
|
53
|
+
"teammate-mode": { kind: "select", enabled: false, value: "tmux" },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const absentOutput = renderAlias(absent, shell);
|
|
58
|
+
const disabledOutput = renderAlias(disabled, shell);
|
|
59
|
+
|
|
60
|
+
expect(absentOutput.shell).toBe(shell);
|
|
61
|
+
expect(disabledOutput).toEqual(absentOutput);
|
|
62
|
+
expect(
|
|
63
|
+
absentOutput.block
|
|
64
|
+
.split("\n")
|
|
65
|
+
.filter((line) => line.startsWith("alias ")),
|
|
66
|
+
).toEqual([shell === "fish" ? example.fishLine : example.posixLine]);
|
|
67
|
+
expect(
|
|
68
|
+
absentOutput.block.startsWith("# >>> magus managed alias >>>\n"),
|
|
69
|
+
).toBe(true);
|
|
70
|
+
expect(
|
|
71
|
+
absentOutput.block.endsWith("# <<< magus managed alias <<<\n"),
|
|
72
|
+
).toBe(true);
|
|
73
|
+
expect(renderEnv(absent)).toEqual([]);
|
|
74
|
+
expect(renderEnv(disabled)).toEqual([]);
|
|
75
|
+
expect(renderArgsAsTokens(absent)).toEqual([...example.tokens]);
|
|
76
|
+
expect(renderArgsAsTokens(disabled)).toEqual([...example.tokens]);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
it(`TEST-23 REQ-10 leaves all new options off in the default ${shell} alias`, () => {
|
|
81
|
+
const config = defaultAliasConfig();
|
|
82
|
+
const emptyConfig: AliasConfig = { aliasName: "c", flags: {} };
|
|
83
|
+
|
|
84
|
+
expect(config.aliasName).toBe("c");
|
|
85
|
+
expect(renderEnv(config)).toEqual([]);
|
|
86
|
+
expect(renderArgsAsTokens(config)).toEqual([]);
|
|
87
|
+
expect(renderAlias(config, shell)).toEqual(
|
|
88
|
+
renderAlias(emptyConfig, shell),
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|