[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
export interface AIQuickMessage {
id: string;
/** Display label shown in pickers and settings. */
name: string;
/** Slash-command token, e.g. `status` for `/status`. */
slug: string;
/** Prompt text inserted into the composer when selected. */
content: string;
/** Optional short hint shown in the slash picker. */
description?: string;
}
export interface UserSkillSlashOption {
id: string;
slug: string;
name: string;
description: string;
}
export type SystemSlashCommandSlug = 'stop' | 'compact';
export interface SystemSlashCommand {
slug: SystemSlashCommandSlug;
name: string;
descriptionKey: string;
}
export type SlashCommandItem =
| { kind: 'quickMessage'; message: AIQuickMessage }
| { kind: 'system'; command: SystemSlashCommand }
| { kind: 'skill'; skill: UserSkillSlashOption };
export const QUICK_MESSAGE_LIMITS = {
name: 120,
slug: 48,
description: 240,
content: 10000,
maxItems: 200,
} as const;
const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
export function normalizeQuickMessageSlug(input: string): string {
return input
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, QUICK_MESSAGE_LIMITS.slug);
}
export function isValidQuickMessageSlug(slug: string): boolean {
return slug.length > 0 && SLUG_PATTERN.test(slug);
}
export function slugFromQuickMessageName(name: string): string {
return normalizeQuickMessageSlug(name);
}
function clampString(value: unknown, maxLen: number): string {
if (typeof value !== 'string') return '';
return value.slice(0, maxLen);
}
/** Validate and dedupe quick messages from localStorage or cloud sync. */
export function sanitizeQuickMessages(raw: unknown): AIQuickMessage[] {
if (!Array.isArray(raw)) return [];
const seenSlugs = new Set<string>();
const result: AIQuickMessage[] = [];
for (const entry of raw) {
if (!entry || typeof entry !== 'object') continue;
const record = entry as Record<string, unknown>;
const idRaw = typeof record.id === 'string' ? record.id.trim() : '';
const id = idRaw.length > 0 ? idRaw.slice(0, 64) : createQuickMessageId();
const name = clampString(record.name, QUICK_MESSAGE_LIMITS.name).trim();
const slug = normalizeQuickMessageSlug(clampString(record.slug, QUICK_MESSAGE_LIMITS.slug));
const content = clampString(record.content, QUICK_MESSAGE_LIMITS.content).trim();
const description = clampString(record.description, QUICK_MESSAGE_LIMITS.description).trim();
if (!name || !isValidQuickMessageSlug(slug) || !content) continue;
if (seenSlugs.has(slug)) continue;
seenSlugs.add(slug);
result.push({
id,
name,
slug,
content,
description: description || undefined,
});
if (result.length >= QUICK_MESSAGE_LIMITS.maxItems) break;
}
return result;
}
export function filterQuickMessages(
messages: AIQuickMessage[],
query: string,
): AIQuickMessage[] {
const lowerQuery = query.trim().toLowerCase();
if (!lowerQuery) return messages;
return messages.filter((message) => {
const slug = message.slug.toLowerCase();
const name = message.name.toLowerCase();
return slug.startsWith(lowerQuery) || name.includes(lowerQuery);
});
}
export function filterUserSkillsForSlash(
skills: UserSkillSlashOption[],
query: string,
): UserSkillSlashOption[] {
return skills.filter((skill) => {
if (typeof skill.slug !== 'string' || skill.slug.length === 0) return false;
if (!slashQueryMatches(query, skill.slug, skill.name)) return false;
return true;
});
}
export const SYSTEM_BUILTIN_SLASH_COMMANDS: SystemSlashCommand[] = [
{ slug: 'compact', name: 'Compact context', descriptionKey: 'ai.chat.slashCompactDesc' },
{ slug: 'stop', name: 'Stop', descriptionKey: 'ai.chat.slashStopDesc' },
];
export function filterSystemSlashCommands(
commands: SystemSlashCommand[],
query: string,
): SystemSlashCommand[] {
const lowerQuery = query.trim().toLowerCase();
if (!lowerQuery) return commands;
return commands.filter((command) => (
command.slug.startsWith(lowerQuery) || command.name.toLowerCase().includes(lowerQuery)
));
}
function slashQueryMatches(query: string, slug: string, name: string): boolean {
const lowerQuery = query.trim().toLowerCase();
if (!lowerQuery) return true;
return slug.toLowerCase().startsWith(lowerQuery) || name.toLowerCase().includes(lowerQuery);
}
export function buildSlashCommandItems(
quickMessages: AIQuickMessage[],
userSkills: UserSkillSlashOption[],
query: string,
includeSystemCommands = false,
): SlashCommandItem[] {
const systemCommands = includeSystemCommands
? filterSystemSlashCommands(SYSTEM_BUILTIN_SLASH_COMMANDS, query)
: [];
const systemSlugs = new Set<string>(SYSTEM_BUILTIN_SLASH_COMMANDS.map((command) => command.slug));
const reservedSlugs = new Set([
...quickMessages.map((message) => message.slug),
...(includeSystemCommands ? [...systemSlugs] : []),
]);
const filteredMessages = filterQuickMessages(quickMessages, query)
.filter((message) => !systemSlugs.has(message.slug));
return [
...systemCommands.map((command) => ({
kind: 'system' as const,
command,
})),
...filteredMessages.map((message) => ({
kind: 'quickMessage' as const,
message,
})),
...filterUserSkillsForSlash(userSkills, query)
.filter((skill) => !reservedSlugs.has(skill.slug))
.map((skill) => ({
kind: 'skill' as const,
skill,
})),
];
}
export function getSlashCommandItemKey(item: SlashCommandItem): string {
if (item.kind === 'quickMessage') return `qm:${item.message.id}`;
if (item.kind === 'system') return `sys:${item.command.slug}`;
return `sk:${item.skill.id}`;
}
export function getSlashCommandItemId(item: SlashCommandItem): string {
if (item.kind === 'quickMessage') return item.message.id;
if (item.kind === 'system') return item.command.slug;
return item.skill.id;
}
export function createQuickMessageId(): string {
return `qm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}
export function isSystemStopSlashCommand(input: string): boolean {
const trimmed = input.trim();
return /^\/stop(?:\s|$)/i.test(trimmed) || trimmed.toLowerCase() === '/stop';
}
export function getSystemSlashCommand(input: string): SystemSlashCommandSlug | null {
const trimmed = input.trim().toLowerCase();
if (/^\/stop(?:\s|$)/.test(trimmed)) return 'stop';
if (trimmed === '/compact') return 'compact';
return null;
}