mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
Merge branch 'main' into feature/shared-packages
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import type { Feature } from "@automaker/types";
|
||||
import { createLogger } from "@automaker/utils";
|
||||
import * as secureFs from "../lib/secure-fs.js";
|
||||
import {
|
||||
getFeaturesDir,
|
||||
getFeatureDir,
|
||||
@@ -39,8 +39,12 @@ export class FeatureLoader {
|
||||
*/
|
||||
private async deleteOrphanedImages(
|
||||
projectPath: string,
|
||||
oldPaths: Array<string | { path: string; [key: string]: unknown }> | undefined,
|
||||
newPaths: Array<string | { path: string; [key: string]: unknown }> | undefined
|
||||
oldPaths:
|
||||
| Array<string | { path: string; [key: string]: unknown }>
|
||||
| undefined,
|
||||
newPaths:
|
||||
| Array<string | { path: string; [key: string]: unknown }>
|
||||
| undefined
|
||||
): Promise<void> {
|
||||
if (!oldPaths || oldPaths.length === 0) {
|
||||
return;
|
||||
@@ -59,8 +63,8 @@ export class FeatureLoader {
|
||||
if (!newPathSet.has(oldPath)) {
|
||||
try {
|
||||
// Paths are now absolute
|
||||
await fs.unlink(oldPath);
|
||||
logger.info(`Deleted orphaned image: ${oldPath}`);
|
||||
await secureFs.unlink(oldPath);
|
||||
console.log(`[FeatureLoader] Deleted orphaned image: ${oldPath}`);
|
||||
} catch (error) {
|
||||
// Ignore errors when deleting (file may already be gone)
|
||||
logger.warn(
|
||||
@@ -87,10 +91,11 @@ export class FeatureLoader {
|
||||
}
|
||||
|
||||
const featureImagesDir = this.getFeatureImagesDir(projectPath, featureId);
|
||||
await fs.mkdir(featureImagesDir, { recursive: true });
|
||||
await secureFs.mkdir(featureImagesDir, { recursive: true });
|
||||
|
||||
const updatedPaths: Array<string | { path: string; [key: string]: unknown }> =
|
||||
[];
|
||||
const updatedPaths: Array<
|
||||
string | { path: string; [key: string]: unknown }
|
||||
> = [];
|
||||
|
||||
for (const imagePath of imagePaths) {
|
||||
try {
|
||||
@@ -110,7 +115,7 @@ export class FeatureLoader {
|
||||
|
||||
// Check if file exists
|
||||
try {
|
||||
await fs.access(fullOriginalPath);
|
||||
await secureFs.access(fullOriginalPath);
|
||||
} catch {
|
||||
logger.warn(
|
||||
`[FeatureLoader] Image not found, skipping: ${fullOriginalPath}`
|
||||
@@ -123,14 +128,14 @@ export class FeatureLoader {
|
||||
const newPath = path.join(featureImagesDir, filename);
|
||||
|
||||
// Copy the file
|
||||
await fs.copyFile(fullOriginalPath, newPath);
|
||||
logger.info(
|
||||
await secureFs.copyFile(fullOriginalPath, newPath);
|
||||
console.log(
|
||||
`[FeatureLoader] Copied image: ${originalPath} -> ${newPath}`
|
||||
);
|
||||
|
||||
// Try to delete the original temp file
|
||||
try {
|
||||
await fs.unlink(fullOriginalPath);
|
||||
await secureFs.unlink(fullOriginalPath);
|
||||
} catch {
|
||||
// Ignore errors when deleting temp file
|
||||
}
|
||||
@@ -163,14 +168,20 @@ export class FeatureLoader {
|
||||
* Get the path to a feature's feature.json file
|
||||
*/
|
||||
getFeatureJsonPath(projectPath: string, featureId: string): string {
|
||||
return path.join(this.getFeatureDir(projectPath, featureId), "feature.json");
|
||||
return path.join(
|
||||
this.getFeatureDir(projectPath, featureId),
|
||||
"feature.json"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to a feature's agent-output.md file
|
||||
*/
|
||||
getAgentOutputPath(projectPath: string, featureId: string): string {
|
||||
return path.join(this.getFeatureDir(projectPath, featureId), "agent-output.md");
|
||||
return path.join(
|
||||
this.getFeatureDir(projectPath, featureId),
|
||||
"agent-output.md"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,13 +200,15 @@ export class FeatureLoader {
|
||||
|
||||
// Check if features directory exists
|
||||
try {
|
||||
await fs.access(featuresDir);
|
||||
await secureFs.access(featuresDir);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Read all feature directories
|
||||
const entries = await fs.readdir(featuresDir, { withFileTypes: true });
|
||||
const entries = (await secureFs.readdir(featuresDir, {
|
||||
withFileTypes: true,
|
||||
})) as any[];
|
||||
const featureDirs = entries.filter((entry) => entry.isDirectory());
|
||||
|
||||
// Load each feature
|
||||
@@ -205,7 +218,10 @@ export class FeatureLoader {
|
||||
const featureJsonPath = this.getFeatureJsonPath(projectPath, featureId);
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(featureJsonPath, "utf-8");
|
||||
const content = (await secureFs.readFile(
|
||||
featureJsonPath,
|
||||
"utf-8"
|
||||
)) as string;
|
||||
const feature = JSON.parse(content);
|
||||
|
||||
if (!feature.id) {
|
||||
@@ -252,7 +268,10 @@ export class FeatureLoader {
|
||||
async get(projectPath: string, featureId: string): Promise<Feature | null> {
|
||||
try {
|
||||
const featureJsonPath = this.getFeatureJsonPath(projectPath, featureId);
|
||||
const content = await fs.readFile(featureJsonPath, "utf-8");
|
||||
const content = (await secureFs.readFile(
|
||||
featureJsonPath,
|
||||
"utf-8"
|
||||
)) as string;
|
||||
return JSON.parse(content);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
||||
@@ -281,7 +300,7 @@ export class FeatureLoader {
|
||||
await ensureAutomakerDir(projectPath);
|
||||
|
||||
// Create feature directory
|
||||
await fs.mkdir(featureDir, { recursive: true });
|
||||
await secureFs.mkdir(featureDir, { recursive: true });
|
||||
|
||||
// Migrate images from temp directory to feature directory
|
||||
const migratedImagePaths = await this.migrateImages(
|
||||
@@ -300,7 +319,7 @@ export class FeatureLoader {
|
||||
};
|
||||
|
||||
// Write feature.json
|
||||
await fs.writeFile(
|
||||
await secureFs.writeFile(
|
||||
featureJsonPath,
|
||||
JSON.stringify(feature, null, 2),
|
||||
"utf-8"
|
||||
@@ -352,7 +371,7 @@ export class FeatureLoader {
|
||||
|
||||
// Write back to file
|
||||
const featureJsonPath = this.getFeatureJsonPath(projectPath, featureId);
|
||||
await fs.writeFile(
|
||||
await secureFs.writeFile(
|
||||
featureJsonPath,
|
||||
JSON.stringify(updatedFeature, null, 2),
|
||||
"utf-8"
|
||||
@@ -368,8 +387,8 @@ export class FeatureLoader {
|
||||
async delete(projectPath: string, featureId: string): Promise<boolean> {
|
||||
try {
|
||||
const featureDir = this.getFeatureDir(projectPath, featureId);
|
||||
await fs.rm(featureDir, { recursive: true, force: true });
|
||||
logger.info(`Deleted feature ${featureId}`);
|
||||
await secureFs.rm(featureDir, { recursive: true, force: true });
|
||||
console.log(`[FeatureLoader] Deleted feature ${featureId}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
@@ -389,7 +408,10 @@ export class FeatureLoader {
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const agentOutputPath = this.getAgentOutputPath(projectPath, featureId);
|
||||
const content = await fs.readFile(agentOutputPath, "utf-8");
|
||||
const content = (await secureFs.readFile(
|
||||
agentOutputPath,
|
||||
"utf-8"
|
||||
)) as string;
|
||||
return content;
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
||||
@@ -412,10 +434,10 @@ export class FeatureLoader {
|
||||
content: string
|
||||
): Promise<void> {
|
||||
const featureDir = this.getFeatureDir(projectPath, featureId);
|
||||
await fs.mkdir(featureDir, { recursive: true });
|
||||
await secureFs.mkdir(featureDir, { recursive: true });
|
||||
|
||||
const agentOutputPath = this.getAgentOutputPath(projectPath, featureId);
|
||||
await fs.writeFile(agentOutputPath, content, "utf-8");
|
||||
await secureFs.writeFile(agentOutputPath, content, "utf-8");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +449,7 @@ export class FeatureLoader {
|
||||
): Promise<void> {
|
||||
try {
|
||||
const agentOutputPath = this.getAgentOutputPath(projectPath, featureId);
|
||||
await fs.unlink(agentOutputPath);
|
||||
await secureFs.unlink(agentOutputPath);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user