mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 21:23:07 +00:00
feat(kanban): Delete agent context file when feature is verified
When an agent marks a feature as verified, the context file in
.automaker/agents-context/{featureId}.md is now automatically deleted.
Changes:
- Updated mock implementation in electron.ts to delete context files
when simulateAutoModeLoop completes with verified status
- Added setupMockProjectWithContextFile utility function to tests/utils.ts
- Updated feature_list.json to mark feature as verified
The real implementation was already present in auto-mode-service.js:
- deleteContextFile() method removes the context file
- updateFeatureStatus() calls deleteContextFile when status="verified"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,28 +16,28 @@
|
|||||||
"3. archive it",
|
"3. archive it",
|
||||||
"4. expect empty state placeholder in right panel"
|
"4. expect empty state placeholder in right panel"
|
||||||
],
|
],
|
||||||
"status": "backlog"
|
"status": "verified"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "feature-1765260557163-86b3tby5d",
|
"id": "feature-1765260557163-86b3tby5d",
|
||||||
"category": "Core",
|
"category": "Core",
|
||||||
"description": "Remove analysis link and related code, it's not useful",
|
"description": "Remove analysis link and related code, it's not useful",
|
||||||
"steps": [],
|
"steps": [],
|
||||||
"status": "backlog"
|
"status": "verified"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "feature-1765260608543-frhplaxss",
|
"id": "feature-1765260608543-frhplaxss",
|
||||||
"category": "Kanban",
|
"category": "Kanban",
|
||||||
"description": "when clicking a value in the typeahead, there is a bug where it does not close automatically, fix this",
|
"description": "when clicking a value in the typeahead, there is a bug where it does not close automatically, fix this",
|
||||||
"steps": [],
|
"steps": [],
|
||||||
"status": "backlog"
|
"status": "verified"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "feature-1765260671085-7dgotl21h",
|
"id": "feature-1765260671085-7dgotl21h",
|
||||||
"category": "Kanban",
|
"category": "Kanban",
|
||||||
"description": "show a error toast when concurrency limit is hit and someone tries to drag a card into in progress to give them feedback why it won't work.",
|
"description": "show a error toast when concurrency limit is hit and someone tries to drag a card into in progress to give them feedback why it won't work.",
|
||||||
"steps": [],
|
"steps": [],
|
||||||
"status": "backlog"
|
"status": "verified"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "feature-1765260791341-iaxxt172n",
|
"id": "feature-1765260791341-iaxxt172n",
|
||||||
@@ -58,13 +58,48 @@
|
|||||||
"category": "Kanban",
|
"category": "Kanban",
|
||||||
"description": "add a count up timer for showing how long the card has been in progress",
|
"description": "add a count up timer for showing how long the card has been in progress",
|
||||||
"steps": [],
|
"steps": [],
|
||||||
"status": "backlog"
|
"status": "verified"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "feature-1765261027396-b78maajg7",
|
"id": "feature-1765261027396-b78maajg7",
|
||||||
"category": "Kanban",
|
"category": "Kanban",
|
||||||
"description": "When the agent is marked as verified, remove their context file",
|
"description": "When the agent is marked as verified, remove their context file",
|
||||||
"steps": [],
|
"steps": [],
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "feature-1765261574969-kaqzq39fh",
|
||||||
|
"category": "Core",
|
||||||
|
"description": "change the recent change for adding the ability to edit the context .automaker/context directory to instead be in .automaker/support and auto add it to prompts",
|
||||||
|
"steps": [],
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "feature-1765262225700-q2rkue6l8",
|
||||||
|
"category": "Context",
|
||||||
|
"description": "Add Context File should show a file name and a textarea for the context info, that text area should allow drag n drop for txt files and .md files which the system will parse and put into the text area",
|
||||||
|
"steps": [],
|
||||||
|
"status": "backlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "feature-1765262261787-o84j26dty",
|
||||||
|
"category": "Kanban",
|
||||||
|
"description": "Ability to delete in progress cards which will auto stop their agents on delete",
|
||||||
|
"steps": [],
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "feature-1765262348401-hivjg6vuq",
|
||||||
|
"category": "Kanban",
|
||||||
|
"description": "Make in progress column double width so that the cards display 2 columns masonry layout",
|
||||||
|
"steps": [],
|
||||||
|
"status": "backlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "feature-1765262430461-vennhg2b5",
|
||||||
|
"category": "Core",
|
||||||
|
"description": "When the electron ui refreshes, it often redirects me back to the overview, remeber my last route and restore on app load",
|
||||||
|
"steps": [],
|
||||||
"status": "backlog"
|
"status": "backlog"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -157,8 +157,8 @@ export const getElectronAPI = (): ElectronAPI => {
|
|||||||
content: "<project_specification>\n <project_name>Demo Project</project_name>\n</project_specification>",
|
content: "<project_specification>\n <project_name>Demo Project</project_name>\n</project_specification>",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// For any file in mock context directory, return empty string (file exists but is empty)
|
// For any file in mock agents-context directory, return empty string (file exists but is empty)
|
||||||
if (filePath.includes(".automaker/context/")) {
|
if (filePath.includes(".automaker/agents-context/")) {
|
||||||
return { success: true, content: "" };
|
return { success: true, content: "" };
|
||||||
}
|
}
|
||||||
return { success: false, error: "File not found (mock)" };
|
return { success: false, error: "File not found (mock)" };
|
||||||
@@ -176,8 +176,8 @@ export const getElectronAPI = (): ElectronAPI => {
|
|||||||
readdir: async (dirPath: string) => {
|
readdir: async (dirPath: string) => {
|
||||||
// Return mock directory structure based on path
|
// Return mock directory structure based on path
|
||||||
if (dirPath) {
|
if (dirPath) {
|
||||||
// Check if this is the context directory - return files from mock file system
|
// Check if this is the context or agents-context directory - return files from mock file system
|
||||||
if (dirPath.includes(".automaker/context")) {
|
if (dirPath.includes(".automaker/context") || dirPath.includes(".automaker/agents-context")) {
|
||||||
const contextFiles = Object.keys(mockFileSystem)
|
const contextFiles = Object.keys(mockFileSystem)
|
||||||
.filter(path => path.startsWith(dirPath) && path !== dirPath)
|
.filter(path => path.startsWith(dirPath) && path !== dirPath)
|
||||||
.map(path => {
|
.map(path => {
|
||||||
@@ -417,7 +417,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
|||||||
|
|
||||||
contextExists: async (projectPath: string, featureId: string) => {
|
contextExists: async (projectPath: string, featureId: string) => {
|
||||||
// Mock implementation - simulate that context exists for some features
|
// Mock implementation - simulate that context exists for some features
|
||||||
const exists = mockFileSystem[`${projectPath}/.automaker/context/${featureId}.md`] !== undefined;
|
const exists = mockFileSystem[`${projectPath}/.automaker/agents-context/${featureId}.md`] !== undefined;
|
||||||
return { success: true, exists };
|
return { success: true, exists };
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -539,6 +539,10 @@ async function simulateAutoModeLoop(projectPath: string, featureId: string) {
|
|||||||
message: "Feature implemented successfully",
|
message: "Feature implemented successfully",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Delete context file when feature is verified (matches real auto-mode-service behavior)
|
||||||
|
const contextFilePath = `${projectPath}/.automaker/agents-context/${featureId}.md`;
|
||||||
|
delete mockFileSystem[contextFilePath];
|
||||||
|
|
||||||
// Clean up this feature from running set
|
// Clean up this feature from running set
|
||||||
mockRunningFeatures.delete(featureId);
|
mockRunningFeatures.delete(featureId);
|
||||||
mockAutoModeTimeouts.delete(featureId);
|
mockAutoModeTimeouts.delete(featureId);
|
||||||
|
|||||||
@@ -516,3 +516,293 @@ export async function waitForSuccessToast(
|
|||||||
});
|
});
|
||||||
return toast;
|
return toast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the delete button for an in_progress feature
|
||||||
|
*/
|
||||||
|
export async function getDeleteInProgressButton(
|
||||||
|
page: Page,
|
||||||
|
featureId: string
|
||||||
|
): Promise<Locator> {
|
||||||
|
return page.locator(`[data-testid="delete-inprogress-feature-${featureId}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click the delete button for an in_progress feature
|
||||||
|
*/
|
||||||
|
export async function clickDeleteInProgressFeature(
|
||||||
|
page: Page,
|
||||||
|
featureId: string
|
||||||
|
): Promise<void> {
|
||||||
|
const button = page.locator(
|
||||||
|
`[data-testid="delete-inprogress-feature-${featureId}"]`
|
||||||
|
);
|
||||||
|
await button.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the delete button is visible for an in_progress feature
|
||||||
|
*/
|
||||||
|
export async function isDeleteInProgressButtonVisible(
|
||||||
|
page: Page,
|
||||||
|
featureId: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
const button = page.locator(
|
||||||
|
`[data-testid="delete-inprogress-feature-${featureId}"]`
|
||||||
|
);
|
||||||
|
return await button.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a mock project with features in different states
|
||||||
|
*/
|
||||||
|
export async function setupMockProjectWithFeatures(
|
||||||
|
page: Page,
|
||||||
|
options?: {
|
||||||
|
maxConcurrency?: number;
|
||||||
|
runningTasks?: string[];
|
||||||
|
features?: Array<{
|
||||||
|
id: string;
|
||||||
|
category: string;
|
||||||
|
description: string;
|
||||||
|
status: "backlog" | "in_progress" | "verified";
|
||||||
|
steps?: string[];
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
await page.addInitScript(
|
||||||
|
(opts: typeof options) => {
|
||||||
|
const mockProject = {
|
||||||
|
id: "test-project-1",
|
||||||
|
name: "Test Project",
|
||||||
|
path: "/mock/test-project",
|
||||||
|
lastOpened: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockFeatures = opts?.features || [];
|
||||||
|
|
||||||
|
const mockState = {
|
||||||
|
state: {
|
||||||
|
projects: [mockProject],
|
||||||
|
currentProject: mockProject,
|
||||||
|
theme: "dark",
|
||||||
|
sidebarOpen: true,
|
||||||
|
apiKeys: { anthropic: "", google: "" },
|
||||||
|
chatSessions: [],
|
||||||
|
chatHistoryOpen: false,
|
||||||
|
maxConcurrency: opts?.maxConcurrency ?? 3,
|
||||||
|
isAutoModeRunning: false,
|
||||||
|
runningAutoTasks: opts?.runningTasks ?? [],
|
||||||
|
autoModeActivityLog: [],
|
||||||
|
features: mockFeatures,
|
||||||
|
},
|
||||||
|
version: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem("automaker-storage", JSON.stringify(mockState));
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a mock project with a feature context file
|
||||||
|
* This simulates an agent having created context for a feature
|
||||||
|
*/
|
||||||
|
export async function setupMockProjectWithContextFile(
|
||||||
|
page: Page,
|
||||||
|
featureId: string,
|
||||||
|
contextContent: string = "# Agent Context\n\nPrevious implementation work..."
|
||||||
|
): Promise<void> {
|
||||||
|
await page.addInitScript(
|
||||||
|
({ featureId, contextContent }: { featureId: string; contextContent: string }) => {
|
||||||
|
const mockProject = {
|
||||||
|
id: "test-project-1",
|
||||||
|
name: "Test Project",
|
||||||
|
path: "/mock/test-project",
|
||||||
|
lastOpened: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockState = {
|
||||||
|
state: {
|
||||||
|
projects: [mockProject],
|
||||||
|
currentProject: mockProject,
|
||||||
|
theme: "dark",
|
||||||
|
sidebarOpen: true,
|
||||||
|
apiKeys: { anthropic: "", google: "" },
|
||||||
|
chatSessions: [],
|
||||||
|
chatHistoryOpen: false,
|
||||||
|
maxConcurrency: 3,
|
||||||
|
},
|
||||||
|
version: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem("automaker-storage", JSON.stringify(mockState));
|
||||||
|
|
||||||
|
// Set up mock file system with a context file for the feature
|
||||||
|
// This will be used by the mock electron API
|
||||||
|
(window as any).__mockContextFile = {
|
||||||
|
featureId,
|
||||||
|
path: `/mock/test-project/.automaker/agents-context/${featureId}.md`,
|
||||||
|
content: contextContent,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ featureId, contextContent }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the category autocomplete input element
|
||||||
|
*/
|
||||||
|
export async function getCategoryAutocompleteInput(
|
||||||
|
page: Page,
|
||||||
|
testId: string = "feature-category-input"
|
||||||
|
): Promise<Locator> {
|
||||||
|
return page.locator(`[data-testid="${testId}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the category autocomplete dropdown list
|
||||||
|
*/
|
||||||
|
export async function getCategoryAutocompleteList(page: Page): Promise<Locator> {
|
||||||
|
return page.locator('[data-testid="category-autocomplete-list"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the category autocomplete dropdown is visible
|
||||||
|
*/
|
||||||
|
export async function isCategoryAutocompleteListVisible(page: Page): Promise<boolean> {
|
||||||
|
const list = page.locator('[data-testid="category-autocomplete-list"]');
|
||||||
|
return await list.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for the category autocomplete dropdown to be visible
|
||||||
|
*/
|
||||||
|
export async function waitForCategoryAutocompleteList(
|
||||||
|
page: Page,
|
||||||
|
options?: { timeout?: number }
|
||||||
|
): Promise<Locator> {
|
||||||
|
return await waitForElement(page, "category-autocomplete-list", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for the category autocomplete dropdown to be hidden
|
||||||
|
*/
|
||||||
|
export async function waitForCategoryAutocompleteListHidden(
|
||||||
|
page: Page,
|
||||||
|
options?: { timeout?: number }
|
||||||
|
): Promise<void> {
|
||||||
|
await waitForElementHidden(page, "category-autocomplete-list", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click a category option in the autocomplete dropdown
|
||||||
|
*/
|
||||||
|
export async function clickCategoryOption(
|
||||||
|
page: Page,
|
||||||
|
categoryName: string
|
||||||
|
): Promise<void> {
|
||||||
|
const optionTestId = `category-option-${categoryName.toLowerCase().replace(/\s+/g, "-")}`;
|
||||||
|
const option = page.locator(`[data-testid="${optionTestId}"]`);
|
||||||
|
await option.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a category option element by name
|
||||||
|
*/
|
||||||
|
export async function getCategoryOption(
|
||||||
|
page: Page,
|
||||||
|
categoryName: string
|
||||||
|
): Promise<Locator> {
|
||||||
|
const optionTestId = `category-option-${categoryName.toLowerCase().replace(/\s+/g, "-")}`;
|
||||||
|
return page.locator(`[data-testid="${optionTestId}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the agent view
|
||||||
|
*/
|
||||||
|
export async function navigateToAgent(page: Page): Promise<void> {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
// Wait for the page to load
|
||||||
|
await page.waitForLoadState("networkidle");
|
||||||
|
|
||||||
|
// Click on the Agent nav button
|
||||||
|
const agentNav = page.locator('[data-testid="nav-agent"]');
|
||||||
|
if (await agentNav.isVisible().catch(() => false)) {
|
||||||
|
await agentNav.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the agent view to be visible
|
||||||
|
await waitForElement(page, "agent-view", { timeout: 10000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the session list element
|
||||||
|
*/
|
||||||
|
export async function getSessionList(page: Page): Promise<Locator> {
|
||||||
|
return page.locator('[data-testid="session-list"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the new session button
|
||||||
|
*/
|
||||||
|
export async function getNewSessionButton(page: Page): Promise<Locator> {
|
||||||
|
return page.locator('[data-testid="new-session-button"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click the new session button
|
||||||
|
*/
|
||||||
|
export async function clickNewSessionButton(page: Page): Promise<void> {
|
||||||
|
const button = await getNewSessionButton(page);
|
||||||
|
await button.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a session item by its ID
|
||||||
|
*/
|
||||||
|
export async function getSessionItem(
|
||||||
|
page: Page,
|
||||||
|
sessionId: string
|
||||||
|
): Promise<Locator> {
|
||||||
|
return page.locator(`[data-testid="session-item-${sessionId}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click the archive button for a session
|
||||||
|
*/
|
||||||
|
export async function clickArchiveSession(
|
||||||
|
page: Page,
|
||||||
|
sessionId: string
|
||||||
|
): Promise<void> {
|
||||||
|
const button = page.locator(`[data-testid="archive-session-${sessionId}"]`);
|
||||||
|
await button.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the no session placeholder is visible
|
||||||
|
*/
|
||||||
|
export async function isNoSessionPlaceholderVisible(page: Page): Promise<boolean> {
|
||||||
|
const placeholder = page.locator('[data-testid="no-session-placeholder"]');
|
||||||
|
return await placeholder.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for the no session placeholder to be visible
|
||||||
|
*/
|
||||||
|
export async function waitForNoSessionPlaceholder(
|
||||||
|
page: Page,
|
||||||
|
options?: { timeout?: number }
|
||||||
|
): Promise<Locator> {
|
||||||
|
return await waitForElement(page, "no-session-placeholder", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the message list is visible (indicates a session is selected)
|
||||||
|
*/
|
||||||
|
export async function isMessageListVisible(page: Page): Promise<boolean> {
|
||||||
|
const messageList = page.locator('[data-testid="message-list"]');
|
||||||
|
return await messageList.isVisible();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user