Skip to content

Commit e1f40dd

Browse files
koki-developclaude
andcommitted
feat: Add --safe flag for file edit preview mode
- Add --safe CLI flag to prevent actual file modifications - Implement CatConfig and FileEditorConfig for type-safe configuration - Display safe mode indicator in EditAction component - FileEditor creates diffs but skips file replacement in safe mode 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 6feffa9 commit e1f40dd

File tree

6 files changed

+43
-10
lines changed

6 files changed

+43
-10
lines changed

src/App.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import { Spinner } from "./components/Spinner";
77
import type { Message } from "./components/types";
88
import { Cat } from "./lib/cat";
99

10-
const cat = new Cat();
10+
type AppProps = {
11+
safeMode: boolean;
12+
};
1113

12-
export const App: React.FC = () => {
14+
export const App: React.FC<AppProps> = ({ safeMode }) => {
15+
const cat = new Cat({ safeMode });
1316
const [messages, setMessages] = useState<Message[]>([]);
1417
const [input, setInput] = useState("");
1518
const [isLoading, setIsLoading] = useState(false);

src/components/EditAction.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ export const EditAction: React.FC<EditActionProps> = ({ action }) => {
5252
<Text>
5353
<Text color="green"></Text> <Text bold>Update</Text>(
5454
{action.diff.fileName})
55+
{action.safeMode && (
56+
<Text color="yellow"> [SAFE MODE - No actual changes]</Text>
57+
)}
5558
</Text>
5659
<Box paddingLeft={2} paddingY={1}>
5760
<Text>{formatDiffs(action.diff.diffs)}</Text>

src/components/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export type Action = EditAction;
33
export type EditAction = {
44
type: "edit";
55
diff: FileDiff;
6+
safeMode: boolean;
67
};
78

89
export type FileDiff = {
@@ -22,3 +23,11 @@ export type Message = {
2223
sender: "user" | "cat";
2324
action?: Action;
2425
};
26+
27+
export type CatConfig = {
28+
safeMode: boolean;
29+
};
30+
31+
export type FileEditorConfig = {
32+
safeMode: boolean;
33+
};

src/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ const program = new Command();
1818
program
1919
.name("cat-code")
2020
.version(packageJson.version)
21-
.action(() => {
21+
.option("--safe", "enable safe mode (no actual file modifications)")
22+
.action((options) => {
2223
console.log(logo);
2324

24-
render(<App />, {
25+
render(<App safeMode={!!options.safe} />, {
2526
exitOnCtrlC: false, // Disable default Ctrl+C exit behavior
2627
});
2728
});

src/lib/cat.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Action } from "../components/types";
1+
import type { Action, CatConfig } from "../components/types";
22
import { FileEditor } from "./fileEditor";
33

44
export type CatResponse = {
@@ -60,7 +60,13 @@ const emotionKeywords: Record<Emotion, string[]> = {
6060
} as const;
6161

6262
export class Cat {
63-
private fileEditor = new FileEditor();
63+
private fileEditor: FileEditor;
64+
private safeMode: boolean;
65+
66+
constructor(config: CatConfig) {
67+
this.safeMode = config.safeMode;
68+
this.fileEditor = new FileEditor({ safeMode: config.safeMode });
69+
}
6470

6571
private detectEmotion(message: string): Emotion | null {
6672
const lowerMessage = message.toLowerCase();
@@ -99,7 +105,7 @@ export class Cat {
99105
if (diff) {
100106
return {
101107
text: catText,
102-
action: { type: "edit", diff },
108+
action: { type: "edit", diff, safeMode: this.safeMode },
103109
};
104110
}
105111
}

src/lib/fileEditor.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createReadStream, createWriteStream } from "node:fs";
22
import { rename } from "node:fs/promises";
33
import { createInterface } from "node:readline";
44
import { glob } from "glob";
5-
import type { Diff, FileDiff } from "../components/types";
5+
import type { Diff, FileDiff, FileEditorConfig } from "../components/types";
66
import { Git } from "./git";
77

88
const textFileExtensions: string[] = [
@@ -54,6 +54,11 @@ const textFileExtensions: string[] = [
5454

5555
export class FileEditor {
5656
private git = new Git();
57+
private safeMode: boolean;
58+
59+
constructor(config: FileEditorConfig) {
60+
this.safeMode = config.safeMode;
61+
}
5762
// Text file extension patterns
5863
private readonly textFilePatterns = textFileExtensions.map(
5964
(ext) => `**/*.${ext}`,
@@ -204,8 +209,14 @@ export class FileEditor {
204209
writeStream.end();
205210
await new Promise<void>((resolve) => writeStream.on("finish", resolve));
206211

207-
// Atomically replace original file
208-
await rename(tempPath, filePath);
212+
// Atomically replace original file (only if not in safe mode)
213+
if (!this.safeMode) {
214+
await rename(tempPath, filePath);
215+
} else {
216+
// In safe mode, delete the temporary file
217+
const { unlink } = await import("node:fs/promises");
218+
await unlink(tempPath);
219+
}
209220

210221
return { diffs };
211222
}

0 commit comments

Comments
 (0)