Files
claude-playground/src/cli.js
T
christianandClaude Opus 5.5 8dc5194438 Address review: safer save, stricter load and id parsing
- save() follows symlinks, keeps permissions, uses a unique temp file and cleans it up on failure
- load() rejects entries that aren't {id, text, done}
- add() finds the max id with reduce (no spread limit)
- CLI accepts only plain digits as ids and reports save errors readably
- Ignore *.tmp

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:26:22 +02:00

52 lines
1.2 KiB
JavaScript
Executable File

#!/usr/bin/env node
import { load, save, add, has, complete, remove, format } from "./todo.js";
const FILE = process.env.TODO_FILE ?? "todos.json";
const USAGE = "Usage: todo [ls | add <text> | done <id> | rm <id>]";
const [cmd = "ls", ...args] = process.argv.slice(2);
function fail(message) {
console.error(message);
process.exit(1);
}
function parseId(todos, arg) {
if (!/^\d+$/.test(arg ?? "")) fail(`Invalid id: ${arg ?? "(missing)"}\n${USAGE}`);
const id = Number(arg);
if (!has(todos, id)) fail(`No todo with id ${id}`);
return id;
}
if (!["ls", "add", "done", "rm"].includes(cmd)) fail(`Unknown command: ${cmd}\n${USAGE}`);
let todos;
try {
todos = load(FILE);
} catch (err) {
fail(err.message);
}
switch (cmd) {
case "add": {
const text = args.join(" ").trim();
if (!text) fail(`Missing todo text\n${USAGE}`);
todos = add(todos, text);
break;
}
case "done":
todos = complete(todos, parseId(todos, args[0]));
break;
case "rm":
todos = remove(todos, parseId(todos, args[0]));
break;
}
if (cmd !== "ls") {
try {
save(FILE, todos);
} catch (err) {
fail(err.message);
}
}
console.log(format(todos));