desktop: Fix with AI on scan findings that need a code change (aiFix: true, AI_FIX_RULES). The host (src-tauri/src/ai_fix.rs) runs the owner's local Claude Code headless in the app folder with Read / Edit / Write / Grep / Glob only (Bash, web tools, sub-agents disallowed) and a prompt it builds from the finding's fields; it refuses while the folder has uncommitted files, streams progress, times out after 10 min with a heartbeat, kills the process group on Cancel or exit, and records the run's changed files so Undo restores exactly those; the window shows the diff with Keep / Undo and rescans. Without claude the button copies the prompt (Copy for AI). Only the claude binary, git and kill / taskkill are started there (tests/security.rs); tests/ai_fix.rs covers the prompt, dirty-tree refusal, undo scope, timeout and cancel

This commit is contained in:
CRTkafa 2026-10-15 06:26:29 +08:00
parent 992f5bcc0e
commit c95f2af8ff
24 changed files with 1023 additions and 10 deletions

View file

@ -5,7 +5,7 @@ import { existsSync, readFileSync } from 'node:fs';
import { join, relative, resolve } from 'node:path';
import { CONFIG_NAMES, loadConfig, type ShipConfig } from '../config';
import { reviewDemoAccount } from '../credentials';
import { scanFixAction } from '../fix-actions';
import { AI_FIX_RULES, scanFixAction } from '../fix-actions';
import { proposeDeclarations, type Proposal } from './declare';
import { detectProject, ECO_LABEL, type ProjectFacts, type Stack, where } from './project';
import type { OriginalityReport } from '../originality';
@ -66,6 +66,7 @@ export async function runScan(dir: string, o: { config?: string } = {}): Promise
for (const f of findings) {
const action = f.accepted ? undefined : scanFixAction(f.rule, { configPath, declarationsChanged: declarations.changed });
if (action) f.action = action;
else if (!f.accepted && AI_FIX_RULES.has(f.rule)) f.aiFix = true;
}
const counts = { blocker: 0, high: 0, medium: 0, low: 0, accepted: 0 };
for (const f of findings) { if (f.accepted) counts.accepted++; else counts[f.severity]++; }

View file

@ -65,6 +65,8 @@ export interface ScanFinding {
accepted?: string;
/** The desktop's Fix button: a write fully determined by an existing command (lib/fix-actions.ts). */
action?: FixAction;
/** A code / project-file change: the desktop offers Fix with AI (AI_FIX_RULES). */
aiFix?: boolean;
}
export interface ScanInput {