@@ -29,6 +29,7 @@ export async function handleReview(
2929) : Promise < void > {
3030 const api = createApiClient ( apiUrl , apiKey ) ;
3131 const { execSync : revExec } = await import ( "node:child_process" ) ;
32+ const cwd = process . cwd ( ) ;
3233
3334 ui . intro ( ) ;
3435 const s = ui . createSpinner ( ) ;
@@ -42,6 +43,31 @@ export async function handleReview(
4243 if ( ! found ) { s . stop ( "Not found" ) ; ui . error ( `Task ${ taskId } not found` ) ; process . exit ( 1 ) ; }
4344 task = found ;
4445 s . stop ( `Reviewing: ${ task . title } ` ) ;
46+ } else if ( diffRange ) {
47+ // When --diff is provided without --task, try to match task from commit messages
48+ s . start ( "Matching task from commit messages..." ) ;
49+ try {
50+ const commitLog = revExec ( `git log --format=%s ${ diffRange } ` , { cwd, stdio : "pipe" } ) . toString ( ) . trim ( ) ;
51+ const tasks = await api . fetchTasks ( ) ;
52+ // Look for task ID patterns in commit messages (8-char hex prefix or full UUID)
53+ const idPattern = / \b ( [ 0 - 9 a - f ] { 8 } ) (?: - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 12 } ) ? \b / gi;
54+ const matches = commitLog . match ( idPattern ) || [ ] ;
55+ for ( const candidate of matches ) {
56+ const prefix = candidate . slice ( 0 , 8 ) . toLowerCase ( ) ;
57+ const found = tasks . find ( ( t : Task ) => t . id . toLowerCase ( ) . startsWith ( prefix ) ) ;
58+ if ( found ) {
59+ task = found ;
60+ break ;
61+ }
62+ }
63+ if ( task ) {
64+ s . stop ( `Matched task from commits: ${ task . title } ` ) ;
65+ } else {
66+ s . stop ( "No task matched — will do pure code quality review" ) ;
67+ }
68+ } catch {
69+ s . stop ( "Could not parse commits — will do pure code quality review" ) ;
70+ }
4571 } else {
4672 s . start ( "Finding latest review task..." ) ;
4773 const tasks = await api . fetchTasks ( ) ;
@@ -55,7 +81,6 @@ export async function handleReview(
5581 }
5682
5783 // Determine diff range
58- const cwd = process . cwd ( ) ;
5984 let diffRef = diffRange || "HEAD~1..HEAD" ;
6085 if ( ! diffRange ) {
6186 try {
@@ -80,15 +105,37 @@ export async function handleReview(
80105 ui . info ( `[review] Tags for skill matching: ${ reviewTags . join ( ", " ) } ` ) ;
81106 }
82107
83- const reviewSystem = interpolate ( PROMPT_TEMPLATES [ "reviewer-system" ] || "" , {
84- projectName : cwd . split ( "/" ) . pop ( ) || "unknown" ,
85- language : "English" ,
86- taskTitle : task ?. title || "Manual review request" ,
87- taskType,
88- taskDescription : task ?. description || "(manual review — no task description)" ,
89- taskTypeHint : typeHints [ taskType ] || typeHints . implementation || "" ,
90- customReviewRules : customRules ? `\n${ customRules } ` : "" ,
91- } ) ;
108+ let reviewSystem : string ;
109+ if ( task ) {
110+ // Task matched — use the full reviewer-system template with task context
111+ reviewSystem = interpolate ( PROMPT_TEMPLATES [ "reviewer-system" ] || "" , {
112+ projectName : cwd . split ( "/" ) . pop ( ) || "unknown" ,
113+ language : "English" ,
114+ taskTitle : task . title ,
115+ taskType,
116+ taskDescription : task . description || "(no description)" ,
117+ taskTypeHint : typeHints [ taskType ] || typeHints . implementation || "" ,
118+ customReviewRules : customRules ? `\n${ customRules } ` : "" ,
119+ } ) ;
120+ } else {
121+ // No task matched — pure code quality review (no requirement matching)
122+ reviewSystem = [
123+ `You are a strict code reviewer for project "${ cwd . split ( "/" ) . pop ( ) || "unknown" } ".` ,
124+ `Reply in English. Output JSON only, no markdown.` ,
125+ `` ,
126+ `## Review Mode: Pure Code Quality (no task context)` ,
127+ `No specific task was matched to this diff. Do NOT judge requirement fulfillment.` ,
128+ `Instead, focus exclusively on:` ,
129+ `1. CODE QUALITY: Readability, naming, structure, error handling` ,
130+ `2. SECURITY: Injection risks, credential leaks, unsafe operations` ,
131+ `3. CORRECTNESS: Logic errors, edge cases, null/undefined handling` ,
132+ `4. TEST COVERAGE: Are changes tested? Do existing tests still pass?` ,
133+ `5. STYLE: Consistency with surrounding code, no dead code or debug leftovers` ,
134+ `` ,
135+ `For requirement_match in your output, write "N/A — no task context, pure code quality review".` ,
136+ customRules ? `\n${ customRules } ` : "" ,
137+ ] . join ( "\n" ) ;
138+ }
92139 const outputFormat = PROMPT_TEMPLATES [ "reviewer-output-format" ] || '{"verdict":"APPROVE or NEEDS_CHANGES"}' ;
93140
94141 const prompt = [
0 commit comments