Files
stack-mos-dt-0/apps/gateway/src/commands/command-registry.service.ts
T
jason.woltjeandClaude Sonnet 4.6 85aeebbde2 feat(gateway): PreferencesService + /preferences REST + /system Valkey override (P8-011)
- PreferencesService: platform defaults, user overrides, IMMUTABLE_KEYS enforcement
- PreferencesController: GET /api/preferences, POST /api/preferences, DELETE /api/preferences/:key
- PreferencesModule: global module exporting PreferencesService and SystemOverrideService
- SystemOverrideService: Valkey-backed session-scoped system prompt override with 5-min TTL + renew
- CommandRegistryService: register /system command (socket execution)
- CommandExecutorService: handle /system command via SystemOverrideService
- AgentService: inject system override before each prompt turn, renew TTL; store userId in session
- ChatGateway: pass userId when creating agent sessions
- PreferencesService unit tests: 11 tests covering defaults, overrides, enforcement wins, immutable key errors

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-15 21:32:03 -05:00

194 lines
4.7 KiB
TypeScript

import { Injectable, type OnModuleInit } from '@nestjs/common';
import type { CommandDef, CommandManifest } from '@mosaic/types';
@Injectable()
export class CommandRegistryService implements OnModuleInit {
private readonly commands: CommandDef[] = [];
registerCommand(def: CommandDef): void {
const existing = this.commands.findIndex((c) => c.name === def.name);
if (existing >= 0) {
this.commands[existing] = def;
} else {
this.commands.push(def);
}
}
registerCommands(defs: CommandDef[]): void {
for (const def of defs) {
this.registerCommand(def);
}
}
getManifest(): CommandManifest {
return {
version: 1,
commands: [...this.commands],
skills: [],
};
}
onModuleInit(): void {
this.registerCommands([
{
name: 'model',
description: 'Switch the active model',
aliases: ['m'],
args: [
{
name: 'model-name',
type: 'string',
optional: false,
description: 'Model name to switch to',
},
],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'thinking',
description: 'Set thinking level (none/low/medium/high/auto)',
aliases: ['t'],
args: [
{
name: 'level',
type: 'enum',
optional: false,
values: ['none', 'low', 'medium', 'high', 'auto'],
description: 'Thinking level',
},
],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'new',
description: 'Start a new conversation',
aliases: ['n'],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'clear',
description: 'Clear conversation context and GC session artifacts',
aliases: [],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'compact',
description: 'Request context compaction',
aliases: [],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'retry',
description: 'Retry the last message',
aliases: [],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'rename',
description: 'Rename current conversation',
aliases: [],
args: [
{ name: 'name', type: 'string', optional: false, description: 'New conversation name' },
],
scope: 'core',
execution: 'rest',
available: true,
},
{
name: 'history',
description: 'Show conversation history',
aliases: [],
args: [
{
name: 'limit',
type: 'string',
optional: true,
description: 'Number of messages to show',
},
],
scope: 'core',
execution: 'rest',
available: true,
},
{
name: 'export',
description: 'Export conversation to markdown or JSON',
aliases: [],
args: [
{
name: 'format',
type: 'enum',
optional: true,
values: ['md', 'json'],
description: 'Export format',
},
],
scope: 'core',
execution: 'rest',
available: true,
},
{
name: 'preferences',
description: 'View or set user preferences',
aliases: ['pref'],
args: [
{
name: 'action',
type: 'enum',
optional: true,
values: ['show', 'set', 'reset'],
description: 'Action to perform',
},
],
scope: 'core',
execution: 'rest',
available: true,
},
{
name: 'system',
description: 'Set session-scoped system prompt override',
aliases: [],
args: [
{
name: 'override',
type: 'string',
optional: false,
description: 'System prompt text to inject for this session',
},
],
scope: 'core',
execution: 'socket',
available: true,
},
{
name: 'status',
description: 'Show session and connection status',
aliases: ['s'],
scope: 'core',
execution: 'hybrid',
available: true,
},
{
name: 'help',
description: 'Show available commands',
aliases: ['h'],
scope: 'core',
execution: 'local',
available: true,
},
]);
}
}