mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
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:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 || [],
|
||||
|
||||
Reference in New Issue
Block a user