chore: update .gitignore and remove obsolete automaker files

- Added .automaker/ to .gitignore to prevent tracking of the entire directory.
- Deleted outdated files including app_spec.txt, categories.json, memory.md, clean-code.md, and gemini.md from the .automaker context.
- Enhanced the mcp-server-factory.js and spec-regeneration-service.js to enforce status management for new features, ensuring they default to "backlog" and clarifying status handling in comments.
- Introduced a new file browsing endpoint in fs.ts to improve directory navigation while maintaining security constraints.
This commit is contained in:
Alec Koifman
2025-12-12 17:34:16 -05:00
parent a4c5567768
commit 28bbc3e0e1
10 changed files with 161 additions and 947 deletions

View File

@@ -22,7 +22,7 @@ class McpServerFactory {
"Create or update a feature. Use this tool to create new features with detailed information or update existing feature status. When creating features, provide comprehensive description, category, and implementation steps.",
{
featureId: z.string().describe("The ID of the feature (lowercase, hyphens for spaces). Example: 'user-authentication', 'budget-tracking'"),
status: z.enum(["backlog", "todo", "in_progress", "verified"]).describe("The status for the feature. Use 'backlog' or 'todo' for new features."),
status: z.enum(["backlog", "todo", "in_progress", "verified"]).describe("The status for the feature. For NEW features, ONLY use 'backlog' or 'verified'. NEVER use 'in_progress' for new features - the user will manually start them."),
summary: z.string().optional().describe("A brief summary of what was implemented/changed or what the feature does."),
description: z.string().optional().describe("A detailed description of the feature. Be comprehensive - explain what the feature does, its purpose, and key functionality."),
category: z.string().optional().describe("The category/phase for this feature. Example: 'Phase 1: Foundation', 'Phase 2: Core Logic', 'Phase 3: Polish', 'Authentication', 'UI/UX'"),
@@ -38,14 +38,16 @@ class McpServerFactory {
const feature = features.find((f) => f.id === args.featureId);
if (!feature) {
console.log(`[Feature Creation] Feature ${args.featureId} not found - this might be a new feature being created`);
// This might be a new feature - try to proceed anyway
console.log(`[Feature Creation] Feature ${args.featureId} not found - this is a new feature being created`);
// This is a new feature - enforce backlog status for any non-verified features
}
// If agent tries to mark as verified but feature has skipTests=true, convert to waiting_approval
let finalStatus = args.status;
// Convert 'todo' to 'backlog' for consistency, but only for new features
if (!feature && finalStatus === "todo") {
// For NEW features: Convert 'todo' or 'in_progress' to 'backlog' for consistency
// New features should ALWAYS go to backlog first, user must manually start them
if (!feature && (finalStatus === "todo" || finalStatus === "in_progress")) {
console.log(`[Feature Creation] New feature ${args.featureId} - converting "${finalStatus}" to "backlog" (user must manually start features)`);
finalStatus = "backlog";
}
if (feature && args.status === "verified" && feature.skipTests === true) {

View File

@@ -390,15 +390,13 @@ class SpecRegenerationService {
3. For EACH feature in the implementation_roadmap:
- Determine if it's ALREADY IMPLEMENTED (fully or partially)
- If fully implemented: Create with status "verified" and note what's done
- If partially implemented: Create with status "in_progress" and note remaining work
- If not started: Create with status "backlog"
- If partially implemented OR not started: Create with status "backlog" and note what still needs to be done
**IMPORTANT - For each feature you MUST provide:**
- **featureId**: A descriptive ID (lowercase, hyphens for spaces). Example: "user-authentication", "budget-tracking"
- **status**:
- "verified" if feature is fully implemented in the codebase
- "in_progress" if partially implemented
- "backlog" if not yet started
- **status**:
- "verified" ONLY if feature is 100% fully implemented in the codebase
- "backlog" for ALL features that need ANY work (partial or not started) - the user will manually start these
- **description**: A DETAILED description (2-4 sentences) explaining what the feature does, its purpose, and key functionality
- **category**: The phase from the roadmap (e.g., "Phase 1: Foundation", "Phase 2: Core Logic", "Phase 3: Polish")
- **steps**: An array of 4-8 clear, actionable implementation steps. For verified features, these are what WAS done. For backlog, these are what NEEDS to be done.
@@ -407,10 +405,12 @@ class SpecRegenerationService {
**Example of analyzing existing code:**
If you find NextAuth.js configured in the codebase with working login pages, the user-authentication feature should be "verified" not "backlog".
**Example of a well-defined feature:**
**IMPORTANT: NEVER use "in_progress" status when creating features. Only use "verified" or "backlog".**
**Example of a well-defined feature (verified - fully complete):**
{
"featureId": "user-authentication",
"status": "verified", // Because we found it's already implemented
"status": "verified", // Because we found it's 100% already implemented
"description": "Secure user authentication system with email/password login and session management. Already implemented using NextAuth.js with email provider.",
"category": "Phase 1: Foundation",
"steps": [
@@ -422,6 +422,21 @@ If you find NextAuth.js configured in the codebase with working login pages, the
"summary": "Authentication implemented with NextAuth.js email provider"
}
**Example of a feature that needs work (backlog):**
{
"featureId": "user-profile",
"status": "backlog", // Needs work - user will manually start this
"description": "User profile page where users can view and edit their account settings, change password, and manage preferences.",
"category": "Phase 2: Core Features",
"steps": [
"Create profile page component",
"Add form for editing user details",
"Implement password change functionality",
"Add avatar upload feature"
],
"summary": "User profile management - needs implementation"
}
**Feature Storage:**
Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder.
Use the UpdateFeatureStatus tool to create features with ALL the fields above.`,
@@ -453,13 +468,15 @@ Use the UpdateFeatureStatus tool to create features with ALL the fields above.`,
2. **Then, read .automaker/app_spec.txt** to see the implementation roadmap
3. **For EACH feature in the roadmap, determine its status:**
- Is it ALREADY IMPLEMENTED in the codebase? → status: "verified"
- Is it PARTIALLY IMPLEMENTED? → status: "in_progress"
- Is it NOT STARTED? → status: "backlog"
- Is it 100% FULLY IMPLEMENTED in the codebase? → status: "verified"
- Is it PARTIALLY IMPLEMENTED or NOT STARTED? → status: "backlog"
**CRITICAL: NEVER use "in_progress" status. Only use "verified" or "backlog".**
The user will manually move features from backlog to in_progress when they want to start working on them.
4. **Create each feature with UpdateFeatureStatus including ALL fields:**
- featureId: Descriptive ID (lowercase, hyphens)
- status: "verified", "in_progress", or "backlog" based on your analysis
- status: "verified" or "backlog" ONLY (never in_progress)
- description: 2-4 sentences explaining the feature
- category: The phase name from the roadmap
- steps: Array of 4-8 implementation steps

View File

@@ -5,6 +5,7 @@
import { Router, type Request, type Response } from "express";
import fs from "fs/promises";
import os from "os";
import path from "path";
import { validatePath, addAllowedPath, isPathAllowed } from "../lib/security.js";
import type { EventEmitter } from "../lib/events.js";
@@ -422,5 +423,123 @@ export function createFsRoutes(_events: EventEmitter): Router {
}
});
// Browse directories for file picker
// SECURITY: Restricted to home directory, allowed paths, and drive roots on Windows
router.post("/browse", async (req: Request, res: Response) => {
try {
const { dirPath } = req.body as { dirPath?: string };
const homeDir = os.homedir();
// Detect available drives on Windows
const detectDrives = async (): Promise<string[]> => {
if (os.platform() !== "win32") {
return [];
}
const drives: string[] = [];
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (const letter of letters) {
const drivePath = `${letter}:\\`;
try {
await fs.access(drivePath);
drives.push(drivePath);
} catch {
// Drive doesn't exist, skip it
}
}
return drives;
};
// Check if a path is safe to browse
const isSafePath = (targetPath: string): boolean => {
const resolved = path.resolve(targetPath);
const normalizedHome = path.resolve(homeDir);
// Allow browsing within home directory
if (resolved === normalizedHome || resolved.startsWith(normalizedHome + path.sep)) {
return true;
}
// Allow browsing already-allowed paths
if (isPathAllowed(resolved)) {
return true;
}
// On Windows, allow drive roots for initial navigation
if (os.platform() === "win32") {
const driveRootMatch = /^[A-Z]:\\$/i.test(resolved);
if (driveRootMatch) {
return true;
}
}
// On Unix, allow root for initial navigation (but only list, not read files)
if (os.platform() !== "win32" && resolved === "/") {
return true;
}
return false;
};
// Default to home directory if no path provided
const targetPath = dirPath ? path.resolve(dirPath) : homeDir;
// Security check: validate the path is safe to browse
if (!isSafePath(targetPath)) {
res.status(403).json({
success: false,
error: "Access denied: browsing is restricted to your home directory and allowed project paths",
});
return;
}
try {
const stats = await fs.stat(targetPath);
if (!stats.isDirectory()) {
res.status(400).json({ success: false, error: "Path is not a directory" });
return;
}
// Read directory contents
const entries = await fs.readdir(targetPath, { withFileTypes: true });
// Filter for directories only and exclude hidden directories
const directories = entries
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
.map((entry) => ({
name: entry.name,
path: path.join(targetPath, entry.name),
}))
.sort((a, b) => a.name.localeCompare(b.name));
// Get parent directory (only if parent is also safe to browse)
const parentPath = path.dirname(targetPath);
const hasParent = parentPath !== targetPath && isSafePath(parentPath);
// Get available drives on Windows
const drives = await detectDrives();
res.json({
success: true,
currentPath: targetPath,
parentPath: hasParent ? parentPath : null,
directories,
drives,
});
} catch (error) {
res.status(400).json({
success: false,
error: error instanceof Error ? error.message : "Failed to read directory",
});
}
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
res.status(500).json({ success: false, error: message });
}
});
return router;
}

View File

@@ -255,7 +255,7 @@ Format your response as markdown. Be specific and actionable.`;
// Save spec
const specDir = path.join(projectPath, ".automaker");
const specPath = path.join(specDir, "project-spec.md");
const specPath = path.join(specDir, "app_spec.txt");
await fs.mkdir(specDir, { recursive: true });
await fs.writeFile(specPath, responseText);
@@ -278,7 +278,7 @@ async function generateFeaturesFromSpec(
abortController: AbortController
) {
// Read existing spec
const specPath = path.join(projectPath, ".automaker", "project-spec.md");
const specPath = path.join(projectPath, ".automaker", "app_spec.txt");
let spec: string;
try {
@@ -382,7 +382,7 @@ async function parseAndCreateFeatures(
id: feature.id,
title: feature.title,
description: feature.description,
status: "pending",
status: "backlog", // Features go to backlog - user must manually start them
priority: feature.priority || 2,
complexity: feature.complexity || "moderate",
dependencies: feature.dependencies || [],