Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/domain/interfaces/IHookConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Comment on lines +32 to +38
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on surfaceContext had "Reserved — not yet wired to handler" removed, but these new fields (extractIntent, intentTimeout, minWords, memoryLimit) are also not yet wired to PromptSubmitHandler. For consistency with other config fields in this file, consider marking these as "Reserved — not yet wired to handler" until the implementation is added. This follows the pattern used for recordPrompts on line 28 and other fields like memoryLimit in ISessionStartConfig on line 10.

Suggested change
/** Enable LLM-based intent extraction for smarter memory retrieval. */
readonly extractIntent: boolean;
/** Timeout in ms for intent extraction LLM call. Default: 3000. */
readonly intentTimeout: number;
/** Minimum word count to trigger intent extraction. Default: 5. */
readonly minWords: number;
/** Maximum memories to return. Default: 20. */
/** Enable LLM-based intent extraction for smarter memory retrieval. Reserved — not yet wired to handler. */
readonly extractIntent: boolean;
/** Timeout in ms for intent extraction LLM call. Default: 3000. Reserved — not yet wired to handler. */
readonly intentTimeout: number;
/** Minimum word count to trigger intent extraction. Default: 5. Reserved — not yet wired to handler. */
readonly minWords: number;
/** Maximum memories to return. Default: 20. Reserved — not yet wired to handler. */

Copilot uses AI. Check for mistakes.
readonly memoryLimit: number;
}

export interface IPostCommitConfig {
Expand Down
72 changes: 72 additions & 0 deletions src/domain/interfaces/IIntentExtractor.ts
Original file line number Diff line number Diff line change
@@ -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<IIntentExtractorResult>;
}
10 changes: 9 additions & 1 deletion src/hooks/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/hooks/utils/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down