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
This commit is contained in:
7Sageer 2026-05-26 14:24:42 +08:00 committed by GitHub
parent cf5270ce6a
commit c473823a4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 82 additions and 68 deletions

View file

@ -5224,7 +5224,7 @@ export class KimiTUI {
if (preferBuiltIn) {
const builtIn = loadBuiltInCatalog(BUILT_IN_CATALOG_JSON);
if (builtIn !== undefined) {
this.showStatus('Loaded built-in catalog. Run /connect --refresh for the latest.');
this.showStatus('Loaded built-in catalog. Run /connect refresh for the latest.');
catalog = builtIn;
}
}

View file

@ -1,8 +1,5 @@
import { DEFAULT_CATALOG_URL } from '@moonshot-ai/kimi-code-sdk';
const CATALOG_URL_FLAG_RE = /--url(?:=|\s+)(https?:\/\/\S+)/;
const URL_FLAG_PRESENT_RE = /(?:^|\s)--url(?=\s|=|$)/;
const REFRESH_FLAG_RE = /(?:^|\s)--refresh(?=\s|$)/;
const BARE_HTTP_URL_RE = /^https?:\/\/\S+$/;
export interface ConnectCatalogRequest {
@ -17,9 +14,51 @@ export type ConnectCatalogResolution =
export function resolveConnectCatalogRequest(args: string): ConnectCatalogResolution {
const trimmed = args.trim();
const urlMatch = CATALOG_URL_FLAG_RE.exec(trimmed);
const bareUrl = BARE_HTTP_URL_RE.test(trimmed) ? trimmed : undefined;
const explicitUrl = urlMatch?.[1] ?? bareUrl;
if (trimmed === '') {
return {
kind: 'ok',
request: {
url: DEFAULT_CATALOG_URL,
preferBuiltIn: true,
allowBuiltInFallback: true,
},
};
}
const tokens = trimmed.split(/\s+/).filter(Boolean);
let explicitUrl: string | undefined;
let refreshRequested = false;
for (const token of tokens) {
if (token.toLowerCase() === 'refresh') {
refreshRequested = true;
continue;
}
if (BARE_HTTP_URL_RE.test(token)) {
if (explicitUrl !== undefined) {
return {
kind: 'error',
message: `Only one catalog URL can be provided. Got "${explicitUrl}" and "${token}".`,
};
}
explicitUrl = token;
continue;
}
if (token.startsWith('--')) {
return {
kind: 'error',
message: `Unexpected flag "${token}". Use /connect [url] [refresh] instead.`,
};
}
return {
kind: 'error',
message: `Unknown argument "${token}". Usage: /connect [url] [refresh]`,
};
}
if (explicitUrl !== undefined) {
return {
@ -32,15 +71,6 @@ export function resolveConnectCatalogRequest(args: string): ConnectCatalogResolu
};
}
if (URL_FLAG_PRESENT_RE.test(trimmed)) {
return {
kind: 'error',
message:
'--url requires an http(s) URL value, e.g. /connect --url=https://example.com/catalog.json',
};
}
const refreshRequested = REFRESH_FLAG_RE.test(trimmed);
return {
kind: 'ok',
request: {

View file

@ -20,18 +20,10 @@ describe('resolveConnectCatalogRequest', () => {
allowBuiltInFallback: true,
},
});
expect(resolveConnectCatalogRequest('ignored text')).toEqual({
kind: 'ok',
request: {
url: DEFAULT_CATALOG_URL,
preferBuiltIn: true,
allowBuiltInFallback: true,
},
});
});
it('forces an online fetch when --refresh is requested', () => {
expect(resolveConnectCatalogRequest('--refresh')).toEqual({
it('forces an online fetch when refresh is requested', () => {
expect(resolveConnectCatalogRequest('refresh')).toEqual({
kind: 'ok',
request: {
url: DEFAULT_CATALOG_URL,
@ -39,7 +31,7 @@ describe('resolveConnectCatalogRequest', () => {
allowBuiltInFallback: true,
},
});
expect(resolveConnectCatalogRequest(' --refresh ')).toEqual({
expect(resolveConnectCatalogRequest(' refresh ')).toEqual({
kind: 'ok',
request: {
url: DEFAULT_CATALOG_URL,
@ -49,23 +41,7 @@ describe('resolveConnectCatalogRequest', () => {
});
});
it('treats explicit catalog URLs as authoritative and ignores --refresh on them', () => {
expect(resolveConnectCatalogRequest('--url=https://internal.example/catalog.json')).toEqual({
kind: 'ok',
request: {
url: 'https://internal.example/catalog.json',
preferBuiltIn: false,
allowBuiltInFallback: false,
},
});
expect(resolveConnectCatalogRequest('--url https://internal.example/catalog.json')).toEqual({
kind: 'ok',
request: {
url: 'https://internal.example/catalog.json',
preferBuiltIn: false,
allowBuiltInFallback: false,
},
});
it('treats explicit catalog URLs as authoritative and ignores refresh on them', () => {
expect(resolveConnectCatalogRequest('https://internal.example/catalog.json')).toEqual({
kind: 'ok',
request: {
@ -75,7 +51,17 @@ describe('resolveConnectCatalogRequest', () => {
},
});
expect(
resolveConnectCatalogRequest('--refresh --url=https://internal.example/catalog.json'),
resolveConnectCatalogRequest('refresh https://internal.example/catalog.json'),
).toEqual({
kind: 'ok',
request: {
url: 'https://internal.example/catalog.json',
preferBuiltIn: false,
allowBuiltInFallback: false,
},
});
expect(
resolveConnectCatalogRequest('https://internal.example/catalog.json refresh'),
).toEqual({
kind: 'ok',
request: {
@ -86,38 +72,36 @@ describe('resolveConnectCatalogRequest', () => {
});
});
it('rejects --url when no value or a non-URL value is provided', () => {
const expectedMessage =
'--url requires an http(s) URL value, e.g. /connect --url=https://example.com/catalog.json';
expect(resolveConnectCatalogRequest('--url')).toEqual({
it('rejects unsupported flags', () => {
const flagMessage = (flag: string) =>
`Unexpected flag "${flag}". Use /connect [url] [refresh] instead.`;
expect(resolveConnectCatalogRequest('--refresh')).toEqual({
kind: 'error',
message: expectedMessage,
message: flagMessage('--refresh'),
});
expect(resolveConnectCatalogRequest('--url=')).toEqual({
expect(resolveConnectCatalogRequest('--url=https://internal.example/catalog.json')).toEqual({
kind: 'error',
message: expectedMessage,
message: flagMessage('--url=https://internal.example/catalog.json'),
});
expect(resolveConnectCatalogRequest(' --url ')).toEqual({
expect(resolveConnectCatalogRequest('--url https://internal.example/catalog.json')).toEqual({
kind: 'error',
message: expectedMessage,
message: flagMessage('--url'),
});
expect(resolveConnectCatalogRequest('--refresh --url')).toEqual({
});
it('rejects non-URL bare tokens', () => {
expect(resolveConnectCatalogRequest('ignored text')).toEqual({
kind: 'error',
message: expectedMessage,
message: 'Unknown argument "ignored". Usage: /connect [url] [refresh]',
});
// Flag-like tokens after --url must not be swallowed as the URL value.
expect(resolveConnectCatalogRequest('--url --refresh')).toEqual({
});
it('rejects multiple URLs', () => {
expect(
resolveConnectCatalogRequest('https://a.com/x.json https://b.com/y.json'),
).toEqual({
kind: 'error',
message: expectedMessage,
});
// Plain non-URL tokens must also be rejected, not silently used.
expect(resolveConnectCatalogRequest('--url not-a-url')).toEqual({
kind: 'error',
message: expectedMessage,
});
expect(resolveConnectCatalogRequest('--url=ftp://example.com/x')).toEqual({
kind: 'error',
message: expectedMessage,
message: 'Only one catalog URL can be provided. Got "https://a.com/x.json" and "https://b.com/y.json".',
});
});
});