Skip to content

Commit c473823

Browse files
authored
feat(kimi-code): use positional args and bare keywords for /connect (#50)
Replace the --flag style (/connect --url=..., /connect --refresh) with positional arguments and bare keywords: - /connect https://example.com/catalog.json - /connect refresh - /connect (no args, default behavior) This aligns the slash-command UX with peer CLI tools (claude-code, pi, opencode) where slash commands use bare words rather than CLI-style flags. Closes #30 follow-up
1 parent cf5270c commit c473823

3 files changed

Lines changed: 75 additions & 61 deletions

File tree

apps/kimi-code/src/tui/kimi-tui.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5224,7 +5224,7 @@ export class KimiTUI {
52245224
if (preferBuiltIn) {
52255225
const builtIn = loadBuiltInCatalog(BUILT_IN_CATALOG_JSON);
52265226
if (builtIn !== undefined) {
5227-
this.showStatus('Loaded built-in catalog. Run /connect --refresh for the latest.');
5227+
this.showStatus('Loaded built-in catalog. Run /connect refresh for the latest.');
52285228
catalog = builtIn;
52295229
}
52305230
}

apps/kimi-code/src/tui/utils/connect-catalog.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { DEFAULT_CATALOG_URL } from '@moonshot-ai/kimi-code-sdk';
22

3-
const CATALOG_URL_FLAG_RE = /--url(?:=|\s+)(https?:\/\/\S+)/;
4-
const URL_FLAG_PRESENT_RE = /(?:^|\s)--url(?=\s|=|$)/;
5-
const REFRESH_FLAG_RE = /(?:^|\s)--refresh(?=\s|$)/;
63
const BARE_HTTP_URL_RE = /^https?:\/\/\S+$/;
74

85
export interface ConnectCatalogRequest {
@@ -17,30 +14,63 @@ export type ConnectCatalogResolution =
1714

1815
export function resolveConnectCatalogRequest(args: string): ConnectCatalogResolution {
1916
const trimmed = args.trim();
20-
const urlMatch = CATALOG_URL_FLAG_RE.exec(trimmed);
21-
const bareUrl = BARE_HTTP_URL_RE.test(trimmed) ? trimmed : undefined;
22-
const explicitUrl = urlMatch?.[1] ?? bareUrl;
2317

24-
if (explicitUrl !== undefined) {
18+
if (trimmed === '') {
2519
return {
2620
kind: 'ok',
2721
request: {
28-
url: explicitUrl,
29-
preferBuiltIn: false,
30-
allowBuiltInFallback: false,
22+
url: DEFAULT_CATALOG_URL,
23+
preferBuiltIn: true,
24+
allowBuiltInFallback: true,
3125
},
3226
};
3327
}
3428

35-
if (URL_FLAG_PRESENT_RE.test(trimmed)) {
29+
const tokens = trimmed.split(/\s+/).filter(Boolean);
30+
let explicitUrl: string | undefined;
31+
let refreshRequested = false;
32+
33+
for (const token of tokens) {
34+
if (token.toLowerCase() === 'refresh') {
35+
refreshRequested = true;
36+
continue;
37+
}
38+
39+
if (BARE_HTTP_URL_RE.test(token)) {
40+
if (explicitUrl !== undefined) {
41+
return {
42+
kind: 'error',
43+
message: `Only one catalog URL can be provided. Got "${explicitUrl}" and "${token}".`,
44+
};
45+
}
46+
explicitUrl = token;
47+
continue;
48+
}
49+
50+
if (token.startsWith('--')) {
51+
return {
52+
kind: 'error',
53+
message: `Unexpected flag "${token}". Use /connect [url] [refresh] instead.`,
54+
};
55+
}
56+
3657
return {
3758
kind: 'error',
38-
message:
39-
'--url requires an http(s) URL value, e.g. /connect --url=https://example.com/catalog.json',
59+
message: `Unknown argument "${token}". Usage: /connect [url] [refresh]`,
60+
};
61+
}
62+
63+
if (explicitUrl !== undefined) {
64+
return {
65+
kind: 'ok',
66+
request: {
67+
url: explicitUrl,
68+
preferBuiltIn: false,
69+
allowBuiltInFallback: false,
70+
},
4071
};
4172
}
4273

43-
const refreshRequested = REFRESH_FLAG_RE.test(trimmed);
4474
return {
4575
kind: 'ok',
4676
request: {

apps/kimi-code/test/tui/utils/connect-catalog.test.ts

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,18 @@ describe('resolveConnectCatalogRequest', () => {
2020
allowBuiltInFallback: true,
2121
},
2222
});
23-
expect(resolveConnectCatalogRequest('ignored text')).toEqual({
24-
kind: 'ok',
25-
request: {
26-
url: DEFAULT_CATALOG_URL,
27-
preferBuiltIn: true,
28-
allowBuiltInFallback: true,
29-
},
30-
});
3123
});
3224

33-
it('forces an online fetch when --refresh is requested', () => {
34-
expect(resolveConnectCatalogRequest('--refresh')).toEqual({
25+
it('forces an online fetch when refresh is requested', () => {
26+
expect(resolveConnectCatalogRequest('refresh')).toEqual({
3527
kind: 'ok',
3628
request: {
3729
url: DEFAULT_CATALOG_URL,
3830
preferBuiltIn: false,
3931
allowBuiltInFallback: true,
4032
},
4133
});
42-
expect(resolveConnectCatalogRequest(' --refresh ')).toEqual({
34+
expect(resolveConnectCatalogRequest(' refresh ')).toEqual({
4335
kind: 'ok',
4436
request: {
4537
url: DEFAULT_CATALOG_URL,
@@ -49,24 +41,18 @@ describe('resolveConnectCatalogRequest', () => {
4941
});
5042
});
5143

52-
it('treats explicit catalog URLs as authoritative and ignores --refresh on them', () => {
53-
expect(resolveConnectCatalogRequest('--url=https://internal.example/catalog.json')).toEqual({
54-
kind: 'ok',
55-
request: {
56-
url: 'https://internal.example/catalog.json',
57-
preferBuiltIn: false,
58-
allowBuiltInFallback: false,
59-
},
60-
});
61-
expect(resolveConnectCatalogRequest('--url https://internal.example/catalog.json')).toEqual({
44+
it('treats explicit catalog URLs as authoritative and ignores refresh on them', () => {
45+
expect(resolveConnectCatalogRequest('https://internal.example/catalog.json')).toEqual({
6246
kind: 'ok',
6347
request: {
6448
url: 'https://internal.example/catalog.json',
6549
preferBuiltIn: false,
6650
allowBuiltInFallback: false,
6751
},
6852
});
69-
expect(resolveConnectCatalogRequest('https://internal.example/catalog.json')).toEqual({
53+
expect(
54+
resolveConnectCatalogRequest('refresh https://internal.example/catalog.json'),
55+
).toEqual({
7056
kind: 'ok',
7157
request: {
7258
url: 'https://internal.example/catalog.json',
@@ -75,7 +61,7 @@ describe('resolveConnectCatalogRequest', () => {
7561
},
7662
});
7763
expect(
78-
resolveConnectCatalogRequest('--refresh --url=https://internal.example/catalog.json'),
64+
resolveConnectCatalogRequest('https://internal.example/catalog.json refresh'),
7965
).toEqual({
8066
kind: 'ok',
8167
request: {
@@ -86,38 +72,36 @@ describe('resolveConnectCatalogRequest', () => {
8672
});
8773
});
8874

89-
it('rejects --url when no value or a non-URL value is provided', () => {
90-
const expectedMessage =
91-
'--url requires an http(s) URL value, e.g. /connect --url=https://example.com/catalog.json';
92-
expect(resolveConnectCatalogRequest('--url')).toEqual({
93-
kind: 'error',
94-
message: expectedMessage,
95-
});
96-
expect(resolveConnectCatalogRequest('--url=')).toEqual({
97-
kind: 'error',
98-
message: expectedMessage,
99-
});
100-
expect(resolveConnectCatalogRequest(' --url ')).toEqual({
75+
it('rejects unsupported flags', () => {
76+
const flagMessage = (flag: string) =>
77+
`Unexpected flag "${flag}". Use /connect [url] [refresh] instead.`;
78+
expect(resolveConnectCatalogRequest('--refresh')).toEqual({
10179
kind: 'error',
102-
message: expectedMessage,
80+
message: flagMessage('--refresh'),
10381
});
104-
expect(resolveConnectCatalogRequest('--refresh --url')).toEqual({
82+
expect(resolveConnectCatalogRequest('--url=https://internal.example/catalog.json')).toEqual({
10583
kind: 'error',
106-
message: expectedMessage,
84+
message: flagMessage('--url=https://internal.example/catalog.json'),
10785
});
108-
// Flag-like tokens after --url must not be swallowed as the URL value.
109-
expect(resolveConnectCatalogRequest('--url --refresh')).toEqual({
86+
expect(resolveConnectCatalogRequest('--url https://internal.example/catalog.json')).toEqual({
11087
kind: 'error',
111-
message: expectedMessage,
88+
message: flagMessage('--url'),
11289
});
113-
// Plain non-URL tokens must also be rejected, not silently used.
114-
expect(resolveConnectCatalogRequest('--url not-a-url')).toEqual({
90+
});
91+
92+
it('rejects non-URL bare tokens', () => {
93+
expect(resolveConnectCatalogRequest('ignored text')).toEqual({
11594
kind: 'error',
116-
message: expectedMessage,
95+
message: 'Unknown argument "ignored". Usage: /connect [url] [refresh]',
11796
});
118-
expect(resolveConnectCatalogRequest('--url=ftp://example.com/x')).toEqual({
97+
});
98+
99+
it('rejects multiple URLs', () => {
100+
expect(
101+
resolveConnectCatalogRequest('https://a.com/x.json https://b.com/y.json'),
102+
).toEqual({
119103
kind: 'error',
120-
message: expectedMessage,
104+
message: 'Only one catalog URL can be provided. Got "https://a.com/x.json" and "https://b.com/y.json".',
121105
});
122106
});
123107
});

0 commit comments

Comments
 (0)