diff --git a/src/domain/interfaces/IHookConfig.ts b/src/domain/interfaces/IHookConfig.ts index 49c688b5..13665072 100644 --- a/src/domain/interfaces/IHookConfig.ts +++ b/src/domain/interfaces/IHookConfig.ts @@ -27,8 +27,16 @@ export interface IPromptSubmitConfig { readonly enabled: boolean; /** Whether to record prompts as memories. Reserved — not yet wired to handler. */ readonly recordPrompts: boolean; - /** Whether to surface context memories per prompt. Reserved — not yet wired to handler. */ + /** Whether to surface context memories per prompt. */ readonly surfaceContext: boolean; + /** Enable LLM-based intent extraction for smarter memory retrieval. */ + readonly extractIntent: boolean; + /** Timeout in ms for intent extraction LLM call. Default: 3000. Must be under hook timeout (10s). */ + readonly intentTimeout: number; + /** Minimum word count to trigger intent extraction. Default: 5. */ + readonly minWords: number; + /** Maximum memories to return. Default: 20. */ + readonly memoryLimit: number; } export interface IPostCommitConfig { diff --git a/src/domain/interfaces/IIntentExtractor.ts b/src/domain/interfaces/IIntentExtractor.ts new file mode 100644 index 00000000..c583ce5f --- /dev/null +++ b/src/domain/interfaces/IIntentExtractor.ts @@ -0,0 +1,72 @@ +/** + * IIntentExtractor + * + * Interface for extracting searchable keywords from user prompts. + * Used by the prompt-submit hook to query memories with intent-based filtering. + */ + +/** + * Input to the intent extraction service. + */ +export interface IIntentExtractorInput { + /** The user's prompt text. */ + readonly prompt: string; +} + +/** + * Reason for skipping intent extraction. + */ +export type IntentSkipReason = + | 'too_short' + | 'confirmation' + | 'no_llm' + | 'llm_skip' + | 'timeout' + | 'error'; + +/** + * Result of intent extraction. + * + * This is a discriminated union keyed by `skipped`: + * - When `skipped` is `true`, `intent` is always `null` and `reason` is defined. + * - When `skipped` is `false`, `intent` may be a string, and `reason` is undefined. + */ +export type IIntentExtractorResult = + | { + /** Whether extraction was skipped. */ + readonly skipped: true; + /** Always null when extraction is skipped. */ + readonly intent: null; + /** + * Reason for skipping. + * - 'too_short': Prompt has fewer words than minWords threshold. + * - 'confirmation': Prompt is a simple confirmation (yes/no/ok/etc). + * - 'no_llm': No LLM client available. + * - 'llm_skip': LLM returned SKIP (no extractable keywords). + * - 'timeout': LLM call timed out. + * - 'error': LLM call failed with error. + */ + readonly reason: IntentSkipReason; + } + | { + /** Whether extraction was skipped. */ + readonly skipped: false; + /** Extracted keywords for memory search. */ + readonly intent: string; + /** Reason is not present when extraction succeeded. */ + readonly reason?: undefined; + }; + +/** + * Service for extracting searchable keywords from user prompts. + */ +export interface IIntentExtractor { + /** + * Extract searchable keywords from a user prompt. + * Returns keywords suitable for memory search, or null if skipped. + * + * @param input - The prompt to extract intent from. + * @returns Extracted keywords or skip indicator. + */ + extract(input: IIntentExtractorInput): Promise; +} diff --git a/src/hooks/utils/config.ts b/src/hooks/utils/config.ts index 8096a201..85d13bad 100644 --- a/src/hooks/utils/config.ts +++ b/src/hooks/utils/config.ts @@ -42,7 +42,15 @@ const DEFAULTS: IHookConfig = { enabled: true, sessionStart: { enabled: true, memoryLimit: 20 }, sessionStop: { enabled: true, autoExtract: true, threshold: 3 }, - promptSubmit: { enabled: false, recordPrompts: false, surfaceContext: true }, + promptSubmit: { + enabled: true, + recordPrompts: false, + surfaceContext: true, + extractIntent: true, + intentTimeout: 3000, + minWords: 5, + memoryLimit: 20, + }, postCommit: { enabled: true }, commitMsg: { enabled: true, diff --git a/tests/unit/hooks/utils/config.test.ts b/tests/unit/hooks/utils/config.test.ts index c487565f..05b7f8de 100644 --- a/tests/unit/hooks/utils/config.test.ts +++ b/tests/unit/hooks/utils/config.test.ts @@ -74,9 +74,13 @@ describe('config', () => { assert.equal(config.hooks.sessionStop.enabled, true); assert.equal(config.hooks.sessionStop.autoExtract, true); assert.equal(config.hooks.sessionStop.threshold, 3); - assert.equal(config.hooks.promptSubmit.enabled, false); + assert.equal(config.hooks.promptSubmit.enabled, true); assert.equal(config.hooks.promptSubmit.recordPrompts, false); assert.equal(config.hooks.promptSubmit.surfaceContext, true); + assert.equal(config.hooks.promptSubmit.extractIntent, true); + assert.equal(config.hooks.promptSubmit.intentTimeout, 3000); + assert.equal(config.hooks.promptSubmit.minWords, 5); + assert.equal(config.hooks.promptSubmit.memoryLimit, 20); }); it('should read and merge config from .git-mem/.git-mem.yaml', () => {