Software Planning Tool
by NightTrek
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { StorageData, Goal, ImplementationPlan, Todo } from './types.js';
export class Storage {
private storagePath: string;
private data: StorageData;
constructor() {
// Store data in user's home directory under .software-planning-tool
const dataDir = path.join(os.homedir(), '.software-planning-tool');
this.storagePath = path.join(dataDir, 'data.json');
this.data = {
goals: {},
plans: {},
};
}
async initialize(): Promise<void> {
try {
// Create data directory if it doesn't exist
const dataDir = path.dirname(this.storagePath);
await fs.mkdir(dataDir, { recursive: true });
// Try to read existing data
const data = await fs.readFile(this.storagePath, 'utf-8');
this.data = JSON.parse(data);
} catch (error) {
// If file doesn't exist or can't be read, use default empty data
await this.save();
}
}
private async save(): Promise<void> {
await fs.writeFile(this.storagePath, JSON.stringify(this.data, null, 2));
}
async createGoal(description: string): Promise<Goal> {
const goal: Goal = {
id: Date.now().toString(),
description,
createdAt: new Date().toISOString(),
};
this.data.goals[goal.id] = goal;
await this.save();
return goal;
}
async getGoal(id: string): Promise<Goal | null> {
return this.data.goals[id] || null;
}
async createPlan(goalId: string): Promise<ImplementationPlan> {
const plan: ImplementationPlan = {
goalId,
todos: [],
updatedAt: new Date().toISOString(),
};
this.data.plans[goalId] = plan;
await this.save();
return plan;
}
async getPlan(goalId: string): Promise<ImplementationPlan | null> {
return this.data.plans[goalId] || null;
}
async addTodo(
goalId: string,
{ title, description, complexity, codeExample }: Omit<Todo, 'id' | 'isComplete' | 'createdAt' | 'updatedAt'>
): Promise<Todo> {
const plan = await this.getPlan(goalId);
if (!plan) {
throw new Error(`No plan found for goal ${goalId}`);
}
const todo: Todo = {
id: Date.now().toString(),
title,
description,
complexity,
codeExample,
isComplete: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
plan.todos.push(todo);
plan.updatedAt = new Date().toISOString();
await this.save();
return todo;
}
async removeTodo(goalId: string, todoId: string): Promise<void> {
const plan = await this.getPlan(goalId);
if (!plan) {
throw new Error(`No plan found for goal ${goalId}`);
}
plan.todos = plan.todos.filter((todo: Todo) => todo.id !== todoId);
plan.updatedAt = new Date().toISOString();
await this.save();
}
async updateTodoStatus(goalId: string, todoId: string, isComplete: boolean): Promise<Todo> {
const plan = await this.getPlan(goalId);
if (!plan) {
throw new Error(`No plan found for goal ${goalId}`);
}
const todo = plan.todos.find((t: Todo) => t.id === todoId);
if (!todo) {
throw new Error(`No todo found with id ${todoId}`);
}
todo.isComplete = isComplete;
todo.updatedAt = new Date().toISOString();
plan.updatedAt = new Date().toISOString();
await this.save();
return todo;
}
async getTodos(goalId: string): Promise<Todo[]> {
const plan = await this.getPlan(goalId);
return plan?.todos || [];
}
}
export const storage = new Storage();