mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
53b3faf72c
`$TOOL_INPUT_file_path` does not expand reliably in Claude Code PostToolUse context, so the init template's `--file "$TOOL_INPUT_file_path"` produced `--file ""` for ~88% of turns. Every captured_experiences row ended up tagged `task = "edit: "` with no path — embeddings were useless, pattern lookups matched nothing, and consolidation never fired. post-edit now mirrors the route hook: when --file is empty, read the Claude Code hook event JSON from stdin and pull tool_input.file_path. The shared readStdinJsonEvent helper moves from routing-hooks.ts into hooks-shared.ts alongside a new extractFilePathFromEvent helper that handles snake_case/camelCase envelopes and ignores non-string values. Verified end-to-end: piping a PostToolUse event to `aqe hooks post-edit --file "" --success --json` now produces `task = "edit: /workspaces/agentic-qe/src/example.ts"` in captured_experiences instead of the empty path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
89 lines
3.1 KiB
TypeScript
89 lines
3.1 KiB
TypeScript
/**
|
|
* Editing hook CLI - file_path extraction tests (#453)
|
|
*
|
|
* Regression coverage for the PostToolUse stdin path. Claude Code delivers
|
|
* the edited file path on stdin as a JSON event (tool_input.file_path), not
|
|
* as a reliable env var. Before this fix, the init template passed
|
|
* --file "$TOOL_INPUT_file_path" which expanded to "" in many hook surfaces
|
|
* — so 88% of captured_experiences rows ended up tagged `task = "edit: "`
|
|
* with no file path, useless embeddings, and zero pattern lookups.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { extractFilePathFromEvent } from '../../../../src/cli/commands/hooks-handlers/hooks-shared.js';
|
|
|
|
describe('extractFilePathFromEvent', () => {
|
|
it('returns empty string for empty input', () => {
|
|
expect(extractFilePathFromEvent('')).toBe('');
|
|
expect(extractFilePathFromEvent(' ')).toBe('');
|
|
});
|
|
|
|
it('extracts file_path from PostToolUse Edit events (snake_case)', () => {
|
|
const event = JSON.stringify({
|
|
hook_event_name: 'PostToolUse',
|
|
tool_name: 'Edit',
|
|
tool_input: {
|
|
file_path: '/workspaces/agentic-qe/src/example.ts',
|
|
old_string: 'foo',
|
|
new_string: 'bar',
|
|
},
|
|
});
|
|
expect(extractFilePathFromEvent(event)).toBe(
|
|
'/workspaces/agentic-qe/src/example.ts'
|
|
);
|
|
});
|
|
|
|
it('extracts file_path from Write events', () => {
|
|
const event = JSON.stringify({
|
|
hook_event_name: 'PostToolUse',
|
|
tool_name: 'Write',
|
|
tool_input: {
|
|
file_path: '/tmp/output.json',
|
|
content: '{}',
|
|
},
|
|
});
|
|
expect(extractFilePathFromEvent(event)).toBe('/tmp/output.json');
|
|
});
|
|
|
|
it('extracts file_path from camelCase toolInput envelopes', () => {
|
|
const event = JSON.stringify({
|
|
toolInput: { file_path: '/src/legacy.ts' },
|
|
});
|
|
expect(extractFilePathFromEvent(event)).toBe('/src/legacy.ts');
|
|
});
|
|
|
|
it('falls back to camelCase filePath inside snake_case envelope', () => {
|
|
const event = JSON.stringify({
|
|
tool_input: { filePath: '/src/mixed.ts' },
|
|
});
|
|
expect(extractFilePathFromEvent(event)).toBe('/src/mixed.ts');
|
|
});
|
|
|
|
it('falls back to top-level file_path when no tool_input envelope is present', () => {
|
|
const event = JSON.stringify({ file_path: '/src/top-level.ts' });
|
|
expect(extractFilePathFromEvent(event)).toBe('/src/top-level.ts');
|
|
});
|
|
|
|
it('returns empty string when no recognized field is present', () => {
|
|
const event = JSON.stringify({ unrelated: 'data', count: 5 });
|
|
expect(extractFilePathFromEvent(event)).toBe('');
|
|
});
|
|
|
|
it('ignores empty or whitespace-only file_path candidates', () => {
|
|
const event = JSON.stringify({
|
|
tool_input: { file_path: ' ' },
|
|
toolInput: { file_path: '/src/winner.ts' },
|
|
});
|
|
expect(extractFilePathFromEvent(event)).toBe('/src/winner.ts');
|
|
});
|
|
|
|
it('does not crash on malformed JSON — returns empty string', () => {
|
|
expect(extractFilePathFromEvent('{not valid json')).toBe('');
|
|
});
|
|
|
|
it('does not throw on non-string file_path values', () => {
|
|
const event = JSON.stringify({ tool_input: { file_path: 42 } });
|
|
expect(extractFilePathFromEvent(event)).toBe('');
|
|
});
|
|
});
|