Skip to content

Commit 316e1eb

Browse files
committed
Fix UI layout, AI renaming logic, bank statement extraction, and duplicate detection entity-skipping bug
1 parent 0831dff commit 316e1eb

10 files changed

Lines changed: 46 additions & 23 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"node": ">=22"
2727
},
2828
"scripts": {
29+
"start": "node src/cli.js",
2930
"plan": "node src/cli.js plan",
3031
"doctor": "node src/cli.js doctor",
3132
"demo": "node src/cli.js demo",

src/core/entity-detector.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ export async function detectCohesiveEntity(absoluteDirPath) {
5454
}
5555

5656
const prompt = `You are a strict, deterministic file system analysis AI. Decide if a directory is a "Cohesive Entity".
57-
A Cohesive Entity is a folder that MUST NOT have its internal files separated or moved.
57+
A Cohesive Entity is a strict, machine-generated folder that MUST NOT have its internal files separated or moved.
5858
Examples:
59-
- Software projects (contain code, package.json)
60-
- Installed applications (contain .exe, .dll)
59+
- Software projects (contain code, package.json, build configs)
60+
- Installed applications (contain .exe, .dll, binary assets)
6161
- System caches or hidden metadata folders (e.g., .thumbnails, .cache, .git)
62-
- Device backups or OS images
62+
63+
CRITICAL RULES:
64+
- A folder containing mostly user documents (PDFs, Word docs, Excel, media, loose files) is NEVER a cohesive entity.
65+
- If it looks like a user's personal organization folder, return false.
66+
- Do NOT flag device backups as entities unless they are raw disk images.
6367
6468
Example 1:
6569
Directory Name: ".thumbnails"

src/organization/purpose-rules.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ export function inferPurposeDetails({ absolutePath = "", baseName = "", extensio
128128
};
129129
}
130130

131+
// Specific Deep Content Rule: Bank Statement
132+
if (normalizedBaseName.includes("statement") || /account\s*statement/i.test(extractedText) || /bank\s*statement/i.test(extractedText) || (/account\s*summary/i.test(extractedText) && /balance/i.test(extractedText))) {
133+
return {
134+
purpose: "finance",
135+
expectedFolders: ["Finance/Bank_Statements"],
136+
matchedByRule: true,
137+
renameLabel: "Bank_Statement"
138+
};
139+
}
140+
131141
// Specific Deep Content Rule: Aadhaar Card
132142
if (normalizedBaseName.includes("aadhaar") || /unique identification authority of india/i.test(extractedText) || (/government of india/i.test(extractedText) && /aadhaar/i.test(extractedText))) {
133143
return {

src/organization/structure.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { classifyFile } from "../core/classify.js";
33
import { inferPurposeDetails } from "./purpose-rules.js";
44

55
const GENERIC_NAME_PATTERNS = [
6-
/^(file|document|scan|copy|duplicate|new|untitled)[-_ ]?\d*$/i,
6+
/^(file|document|scan|copy|duplicate|new|untitled)[-_ ]?[a-z0-9]*$/i,
77
/^(img|image|photo|vid|video|media|whatsapp|screenshot)[-_ ]?[a-z0-9-_() ]+$/i,
88
/^\d{8,}$/, // long numeric strings
99
/^[a-f0-9]{16,}$/i // long hex hashes

src/server.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,20 @@ Give me ONLY a raw JSON string like {"exclusions": ["folder1"], "reasoning": "wh
187187
// Step 5: AI Renaming reasoning
188188
app.post("/api/ai/rename", async (req, res) => {
189189
try {
190-
const { fileInfo } = req.body;
191-
const prompt = `I have a file with info ${JSON.stringify(fileInfo)}. Propose a rename and give reasoning.
192-
Ensure names are deterministic (e.g., lowercase, underscores).
193-
If it's a Finance/Form_16, ensure the year is prominent.
194-
Return ONLY a raw JSON string like {"proposedName": "file.pdf", "reasoning": "why"}. No markdown.`;
195-
const aiResponse = await askAI(prompt, "You are a highly deterministic file renaming assistant. You must follow standard naming conventions and prioritize semantic clarity based on file content and metadata.");
190+
const { fileInfo, subjectPath } = req.body;
191+
const file = catalog.getFileByPath(subjectPath);
192+
const textSample = file?.extractedText ? file.extractedText.slice(0, 800) : "No text available.";
193+
194+
const prompt = `I have a file named "${fileInfo.currentName}" with category "${fileInfo.category}" and purpose "${fileInfo.purpose}".
195+
Here is a sample of its extracted text content:
196+
---
197+
${textSample}
198+
---
199+
Analyze the text to determine exactly what this file is (e.g. Bank Statement, Aadhaar Card, Offer Letter, etc.) and who it belongs to if applicable.
200+
Propose a highly descriptive and structured file name. Use Spaces, Title Case, and clear descriptors (e.g., "Pratik Vaibhav - Aadhaar Card.pdf" or "HDFC Bank Statement - Jan 2024.pdf").
201+
Do not just return the original name or "document_123.pdf".
202+
Return ONLY a raw JSON string like {"proposedName": "New Name.pdf", "reasoning": "why"}. No markdown.`;
203+
const aiResponse = await askAI(prompt, "You are a highly intelligent file renaming assistant. You must analyze the text content to extract the semantic meaning of the document and propose a human-readable, descriptive name.");
196204
const clean = cleanJSON(aiResponse);
197205
res.json(JSON.parse(clean));
198206
} catch (error) {

test/local-audit.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,5 @@ Important folder candidates once structure exists:
9090

9191
const unstructuredGeneric = audit.unstructuredFiles.find((file) => file.relativePath === "file123.pdf");
9292
assert.ok(unstructuredGeneric);
93-
assert.equal(unstructuredGeneric.renameRecommended, false);
93+
assert.equal(unstructuredGeneric.renameRecommended, true);
9494
});

test/review-queue.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ test("buildOrganizationProposals creates approval-gated move and rename proposal
1818
category: "document"
1919
},
2020
structure: {
21-
purpose: "document",
22-
expectedFolders: ["Documents"],
21+
purpose: "finance",
22+
expectedFolders: ["Finance"],
2323
moveRecommended: true,
2424
renameRecommended: true,
2525
reasons: [
@@ -39,13 +39,13 @@ test("buildOrganizationProposals creates approval-gated move and rename proposal
3939
assert.ok(move);
4040
assert.equal(move.status, "pending_user_approval");
4141
assert.equal(move.approvalGate, "moving files in batch");
42-
assert.equal(move.evidence.proposedRelativePath, "Documents/file123.pdf");
42+
assert.equal(move.evidence.proposedRelativePath, "Finance/file123.pdf");
4343

4444
const rename = proposals.find((proposal) => proposal.action === "rename_file");
4545
assert.ok(rename);
4646
assert.equal(rename.status, "pending_user_approval");
4747
assert.equal(rename.approvalGate, "renaming files");
48-
assert.equal(rename.proposedName, "Document_abcdef12.pdf");
48+
assert.equal(rename.proposedName, "Finance_Record_file123.pdf");
4949
assert.equal(rename.evidence.sha256, "abcdef1234567890");
5050
});
5151

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/dist/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>ui</title>
8-
<script type="module" crossorigin src="/assets/index-B8Ts6tKM.js"></script>
8+
<script type="module" crossorigin src="/assets/index-Bef_zoun.js"></script>
99
<link rel="stylesheet" crossorigin href="/assets/index-BMzdelOc.css">
1010
</head>
1111
<body>

ui/src/App.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,13 +518,13 @@ function App() {
518518
</div>
519519
<div>
520520
<div className="text-xs font-bold text-green-500 uppercase tracking-widest mb-3 flex items-center gap-2"><CheckCircle className="w-4 h-4"/> Keep Original</div>
521-
<div className="p-5 bg-green-500/5 border border-green-500/20 rounded-xl text-sm font-mono truncate shadow-inner" title={item.evidence.proposedKeepPath || item.evidence.keptPath}>
521+
<div className="p-5 bg-green-500/5 border border-green-500/20 rounded-xl text-sm font-mono break-all shadow-inner" title={item.evidence.proposedKeepPath || item.evidence.keptPath}>
522522
{(item.evidence.proposedKeepPath || item.evidence.keptPath)?.split('\\').pop()}
523523
</div>
524524
</div>
525525
<div>
526526
<div className="text-xs font-bold text-rose-500 uppercase tracking-widest mb-3 flex items-center gap-2"><Trash2 className="w-4 h-4"/> Delete Duplicate</div>
527-
<div className="p-5 bg-rose-500/5 border border-rose-500/20 rounded-xl text-sm font-mono truncate opacity-60 line-through decoration-rose-500/50 shadow-inner" title={item.evidence.proposedDeletePaths?.[0] || item.evidence.deletedPaths?.[0]}>
527+
<div className="p-5 bg-rose-500/5 border border-rose-500/20 rounded-xl text-sm font-mono break-all opacity-60 line-through decoration-rose-500/50 shadow-inner" title={item.evidence.proposedDeletePaths?.[0] || item.evidence.deletedPaths?.[0]}>
528528
{(item.evidence.proposedDeletePaths?.[0] || item.evidence.deletedPaths?.[0])?.split('\\').pop()}
529529
</div>
530530
</div>
@@ -582,11 +582,11 @@ function App() {
582582
{item.action.replace('_', ' ')}
583583
</span>
584584
</td>
585-
<td className="px-6 py-5 pr-8 truncate">
585+
<td className="px-6 py-5 pr-8">
586586
<div className="flex flex-col gap-3 mb-4">
587587
<div className="flex items-center gap-3 text-slate-500 line-through decoration-rose-500/50 overflow-hidden">
588588
<div className="p-2 bg-slate-950 rounded border border-slate-800 shrink-0"><FileMinus className="w-4 h-4 text-rose-400"/></div>
589-
<span className="text-xs font-mono truncate w-full" title={item.subjectPath}>{item.subjectPath}</span>
589+
<span className="text-xs font-mono break-all w-full" title={item.subjectPath}>{item.subjectPath}</span>
590590
<a href={`/api/file?path=${encodeURIComponent(item.subjectPath)}`} target="_blank" rel="noreferrer" className="text-sky-400 hover:text-sky-300 transition-colors ml-auto shrink-0" title="View File">
591591
<Eye className="w-4 h-4" />
592592
</a>
@@ -610,7 +610,7 @@ function App() {
610610
</div>
611611
) : (
612612
<>
613-
<span className="text-sm font-bold text-sky-400 truncate w-full" title={item.proposedPath || item.evidence?.proposedName}>
613+
<span className="text-sm font-bold text-sky-400 break-all w-full" title={item.proposedPath || item.evidence?.proposedName}>
614614
{item.proposedPath || item.evidence?.proposedName}
615615
</span>
616616
<button

0 commit comments

Comments
 (0)