mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
Merge remote-tracking branch 'origin/main' into feat/extend-models-support
This commit is contained in:
@@ -1,347 +1 @@
|
|||||||
[
|
[]
|
||||||
{
|
|
||||||
"id": "feature-1765288408132-3pmld0an6",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "Can you add a shortcut key for O to open up a new project? To click on the open new project button that's in like the logo header area.",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765300273422-a8ovhdlwq",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "I want the ability to press P which will automatically select my projects drop down and show all my projects. And then for each one, put a hotkey in the left that says 12345 and selecting one of those with my keyboard should automatically select that project.\n",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765301095506-cpy06q9u0",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "It seems like there's only a limit of five of how many things show up in the project select drop down. I need to show everything.",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765301127030-a4nnqp0ja",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "In creating new cards in Kanban, I need the ability to drag and drop images into the description section, which will attach the image as context in store in the temp directory, so that later on when the agent runs, it can know where to fetch that image from.",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765301184184-ttvhd8kkt",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "-o should actually open the select folder prompt. Right now when you click o it goes to like the overview page. That's not the correct experience I'm looking for. Also just clicking on the top left open folder icon should do the same thing of opening the system prompt so they can select a project.",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765305181443-qze22t1hl",
|
|
||||||
"category": "Other",
|
|
||||||
"description": "the settings view is not allowing us to scroll to see rest of the content ",
|
|
||||||
"steps": [
|
|
||||||
"start the project",
|
|
||||||
"open Setting view",
|
|
||||||
"try to scroll "
|
|
||||||
],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765310151816-plx1pxl0z",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "So i want to improve the look of the view of agent output modal its just plain text and im thinking to parse it better and kinda make it look like the last image of coolify logs nice colorded and somehow grouped into some types of info / debug so in our case like prompt / tool call etc",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765318148517-715isvwwb",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "When agent finish work the cards is moved either to waiting approval or into verified one But mostly its include some type of summary at the end i want you to modify our prompts and ui so when its in both states we can see the feature summary of what was done / modified instead of relying on going to code editor to see what got changed etc.",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-09T22:09:13.684Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765319491258-x933j6kbq",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "When running new feature in skip automated testing once its got finished its moved to waiting approval for us to manual test it / follow up prompt. Once we are satisfied we can click commit button so ai agent can commit it work this is only hapening in this scenerio because if we have unchecked the skip automated testing its do it automaticly and commit already. But the issue is when its going to commit we move it to in progress state where we can use stop button and if user use that button its moved to backlog column and. that kinda break what we are doing becase we have no longer even abbility to move it back to waiting approval or to run commit button / follow up again so if user use manual one and stop the commit i want it to be again moved back to waiting approval state / column",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-09T22:31:41.946Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765321570899-oefrfast6",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "I would like to have abbility to set correct model for new feautres. so inteas of only using claude opus we could use other models like sonnet or haiku for easier / light one tasks as well to add abbility how much thinking lvl we wanna use on each task as well",
|
|
||||||
"steps": [
|
|
||||||
"User add new feature",
|
|
||||||
"User Describe it",
|
|
||||||
"Select the automated testing or manual one",
|
|
||||||
"If the task is light / easy to implement he use lighter model from anthropi sdk such as sonnet / haiku",
|
|
||||||
"agent execute task with correct model "
|
|
||||||
],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-09T23:07:37.223Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": false,
|
|
||||||
"summary": "Added model selection (Haiku/Sonnet/Opus) and thinking level (None/Low/Medium/High) controls to feature creation and edit dialogs. Modified: app-store.ts (added AgentModel and ThinkingLevel types), board-view.tsx (UI controls), feature-executor.js (dynamic model/thinking config), feature-loader.js (field persistence). Agent now executes with user-selected model and extended thinking settings.",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765332414638-8jt9o8v06",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "I want you to refactor the add new feature modal there are to many settings going on and its hard / annoyig to navigate lets split the settings in modal into tabs \nprompt icon - prompt and category\ngear icon - model and thinking ( here i would also like to split somehow the claude with thinking and codex that dont use it )\ntest icon - skip automated testing and verification steps\n",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T02:17:18.943Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Made model selection buttons compact. Removed descriptions and badges from cards, now shows short model names (Haiku, Sonnet, Opus, Max, Codex, Mini) in horizontal row. Full description available on hover. Modified: board-view.tsx (renderModelOptions function).",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "high"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765333519168-1z97a3dcq",
|
|
||||||
"category": "Uncategorized",
|
|
||||||
"description": "Make the add new feature modal widther ",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T02:25:21.328Z",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765333063064-qygrbjul4",
|
|
||||||
"path": "/var/folders/lh/zcj91n9j4vj4_wg8jsvpqr9m0000gn/T/automaker-images/1765333063058_SCR-20251210-dseh.png",
|
|
||||||
"filename": "SCR-20251210-dseh.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Increased dialog max-width from max-w-md/max-w-lg to max-w-2xl. Modified: app/src/components/ui/dialog.tsx. This makes the add new feature modal and all other dialogs wider (from 448-512px to 672px) for better content display.",
|
|
||||||
"model": "haiku",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765334441225-h3j832sbz",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "For example i got haiku model running or codex one but we can still see opus 4.5 check if it not hardcoded and fix it to use proper model name that was used in this task",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T02:40:43.706Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Kanban cards now render the agent info model badge using feature.model so the displayed model matches the one selected for the task.",
|
|
||||||
"model": "gpt-5.1-codex",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765331813319-jzlk7eku2",
|
|
||||||
"category": "Uncategorized",
|
|
||||||
"description": "describe the attached image do not change code",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T02:02:54.785Z",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765331797511-v4ssc1hha",
|
|
||||||
"path": "/Users/webdevcody/Library/Application Support/automaker/images/1765331797510-ypiiz13rt_Screenshot_2025-12-09_at_8.56.34_PM.png",
|
|
||||||
"filename": "Screenshot 2025-12-09 at 8.56.34 PM.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765358858108-r8ay1p5mj",
|
|
||||||
"category": "Settings",
|
|
||||||
"description": "Add claude and codex to the left sidebar of settings so its will scroll to thoes sections as well",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T09:32:31.638Z",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765358823366-6vchdhwsj",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765358823364-e0ir8jy9l_SCR-20251210-jooz.png",
|
|
||||||
"filename": "SCR-20251210-jooz.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "sonnet",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765360739103-3h218d1nn",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "When u write new feature for ai agent and attacht context images and change tab to choose diff model and go back to prompt tab the image preview break and im not sure if it even saved properly in state to be later attached check it out for me",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T09:59:02.988Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Fixed image preview breaking when switching tabs in Add Feature modal. Added previewMap/onPreviewMapChange props to DescriptionImageDropZone component to lift preview state up to parent. Modified: description-image-dropzone.tsx (added parent-controlled state support), board-view.tsx (added newFeaturePreviewMap and followUpPreviewMap state, wired up to DescriptionImageDropZone). Image paths were already stored correctly in state - only the preview thumbnails (base64) were lost on tab switch due to component unmounting.",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "high"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765363330908-vu0hip87r",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "Take a look at waiting aproval column in kanban board and fix the card that render in it u can see in attached images that they text is overlaping check other columns how we have them",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T10:46:42.494Z",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765363296205-e4cwlj2j8",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765363296203-bud50snjv_SCR-20251210-kosr.png",
|
|
||||||
"filename": "SCR-20251210-kosr.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "img-1765363296214-lloeta38x",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765363296212-zg03hi120_SCR-20251210-kotn.png",
|
|
||||||
"filename": "SCR-20251210-kotn.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "img-1765363296221-oupa9su2f",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765363296219-7kndnuzr2_SCR-20251210-koud.png",
|
|
||||||
"filename": "SCR-20251210-koud.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "sonnet",
|
|
||||||
"thinkingLevel": "low"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765365064255-wk51aoz3e",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "I want to have some abbility when executing a task on project to have some type of rewing / checkpoint system so if the changes made by agent in the project dont satisfy me / break something i can click in the ui to revert them. The best way for it would be to implement github worktress so when spin up new task claude take a look at it generate new branch that fit task issue and make it as gihub worktree then we would create a a new folder in project .automaker/worktree with branch name and clone of repo so agent can freely work one something like that ",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T11:11:06.115Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Implemented Git Worktree Checkpoint/Revert System. Created: worktree-manager.js service. Modified: auto-mode-service.js (worktree integration, revert/merge methods), feature-loader.js (worktree tracking), main.js (IPC handlers), preload.js (API exposure), app-store.ts (Feature type), electron.d.ts (types), electron.ts (mock API), kanban-card.tsx (branch badge, revert/merge buttons), board-view.tsx (handlers). Features: isolated git branches per feature, branch badge on cards, revert changes button, merge to main button, file diff APIs.",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "ultrathink"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765365411551-87ww2zw6g",
|
|
||||||
"category": "Kanban",
|
|
||||||
"description": "When a agent is workig on task or when its in waiting approval column its would be nice to have some type of git diff panel and see what files got changed as well as reusing our custom themes we have in settings for the editor view of it take a look at codebase and create implementation for it",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T11:16:54.069Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Added git diff panel for in-progress and waiting approval features. Created GitDiffPanel component with themed syntax highlighting. Modified: git-diff-panel.tsx (new), agent-output-modal.tsx, worktree-manager.js, auto-mode-service.js, main.js, preload.js, electron.d.ts. The panel shows changed files with +/- stats and expandable unified diff view using CSS theme variables.",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "ultrathink"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765370691119-yz43i6m6a",
|
|
||||||
"category": "Core",
|
|
||||||
"description": "i want you to run 'npx tsc --noEmi' on our app and fix / get rid of typescript errors that are left ",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": false,
|
|
||||||
"summary": "Fixed all TypeScript errors in the codebase. Modified: src/app/api/claude/test/route.ts (rewrote to use fetch instead of missing @anthropic-ai/sdk), src/lib/electron.ts (fixed saveImageToTemp signature, removed duplicate Window declaration), src/types/electron.d.ts (added deleteFile, resumeFeature, contextExists, analyzeProject methods), src/store/app-store.ts (added FileTreeNode, ProjectAnalysis types and analysis state/actions), src/components/views/analysis-view.tsx (added type annotations for implicit any), src/hooks/use-electron-agent.ts (fixed return type to include queue properties). Created: src/components/ui/badge.tsx (new component required by chat-history.tsx).",
|
|
||||||
"model": "opus",
|
|
||||||
"thinkingLevel": "ultrathink"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765371638849-1gfiifuqy",
|
|
||||||
"category": "Uncategorized",
|
|
||||||
"description": "So currently we have both predefined profiles and custom one created by users and custom tweaking in modal tab when creatign new feature for ai agent to run i want you to add an abbility in our settings view to have enabled only showing profiels by default and what that mean that the model tweaking like selecting bewteen claude / codex and thinking mode is hidden and only profiles are shown so the ui wont be ower head and if users want to tweaks seetings they can always reenabled it in setting view ",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765371471290-6jcmwkviv",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765371471287-jejcmoc8q_SCR-20251210-lpki.png",
|
|
||||||
"filename": "SCR-20251210-lpki.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "sonnet",
|
|
||||||
"thinkingLevel": "medium"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765372497502-kxppqqv3k",
|
|
||||||
"category": "Uncategorized",
|
|
||||||
"description": "As u can see in our git diff panel on each new file its show modified or untracked if it untracked and that not good for ui / ux what if user / agett change over 40-50 files in one go this wiould be complete mess i would rather do 1 type for each with number and icon",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T13:15:19.417Z",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765372428239-jg07w3zxv",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765372428236-6t02kvclp_SCR-20251210-mplr.png",
|
|
||||||
"filename": "SCR-20251210-mplr.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": false,
|
|
||||||
"model": "sonnet",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765374057950-og96suob2",
|
|
||||||
"category": "Other",
|
|
||||||
"description": "I found an issue u can reorder custom profiles created by our self but the pre build in when u try to drag nothing happend",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T13:40:58.991Z",
|
|
||||||
"imagePaths": [
|
|
||||||
{
|
|
||||||
"id": "img-1765374055642-rdjksh8g1",
|
|
||||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765374055641-o7pu97zuz_SCR-20251210-namv.png",
|
|
||||||
"filename": "SCR-20251210-namv.png",
|
|
||||||
"mimeType": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skipTests": true,
|
|
||||||
"model": "sonnet",
|
|
||||||
"thinkingLevel": "medium"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "feature-1765375218276-4sgru3lca",
|
|
||||||
"category": "Other",
|
|
||||||
"description": "I want you to add a shortcuts to ai profies in siedbar to be M and insite that view the new profile should be also shortuct to open to crate a new profile by shortcat N",
|
|
||||||
"steps": [],
|
|
||||||
"status": "verified",
|
|
||||||
"startedAt": "2025-12-10T14:00:32.639Z",
|
|
||||||
"imagePaths": [],
|
|
||||||
"skipTests": true,
|
|
||||||
"summary": "Added keyboard shortcuts for AI Profiles:\n- Press M to navigate to AI Profiles view from sidebar\n- Press N in profiles view to create new profile\n- Visual shortcut indicators added to UI\nModified: use-keyboard-shortcuts.ts (added profiles:M and addProfile:N shortcuts), sidebar.tsx (added shortcut to profiles nav item), profiles-view.tsx (imported keyboard shortcut hooks, implemented N shortcut handler, added visual indicator to New Profile button)",
|
|
||||||
"model": "haiku",
|
|
||||||
"thinkingLevel": "none"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -27,7 +27,7 @@ class ProjectAnalyzer {
|
|||||||
systemPrompt: promptBuilder.getProjectAnalysisSystemPrompt(),
|
systemPrompt: promptBuilder.getProjectAnalysisSystemPrompt(),
|
||||||
maxTurns: 50,
|
maxTurns: 50,
|
||||||
cwd: projectPath,
|
cwd: projectPath,
|
||||||
allowedTools: ["Read", "Glob", "Grep", "Bash"],
|
allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash"],
|
||||||
permissionMode: "acceptEdits",
|
permissionMode: "acceptEdits",
|
||||||
sandbox: {
|
sandbox: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -359,6 +359,7 @@ Analyze this project's codebase and update the .automaker/app_spec.txt file with
|
|||||||
3. **Technology Stack** - Languages, frameworks, libraries detected
|
3. **Technology Stack** - Languages, frameworks, libraries detected
|
||||||
4. **Core Capabilities** - Main features and functionality
|
4. **Core Capabilities** - Main features and functionality
|
||||||
5. **Implemented Features** - What features are already built
|
5. **Implemented Features** - What features are already built
|
||||||
|
6. **Implementation Roadmap** - Break down remaining work into phases with individual features
|
||||||
|
|
||||||
**Steps to Follow:**
|
**Steps to Follow:**
|
||||||
|
|
||||||
@@ -406,10 +407,42 @@ Analyze this project's codebase and update the .automaker/app_spec.txt file with
|
|||||||
<implemented_features>
|
<implemented_features>
|
||||||
<!-- List specific features that appear to be implemented -->
|
<!-- List specific features that appear to be implemented -->
|
||||||
</implemented_features>
|
</implemented_features>
|
||||||
|
|
||||||
|
<implementation_roadmap>
|
||||||
|
<phase_1_foundation>
|
||||||
|
<!-- List foundational features to build first -->
|
||||||
|
</phase_1_foundation>
|
||||||
|
<phase_2_core_logic>
|
||||||
|
<!-- List core logic features -->
|
||||||
|
</phase_2_core_logic>
|
||||||
|
<phase_3_polish>
|
||||||
|
<!-- List polish and enhancement features -->
|
||||||
|
</phase_3_polish>
|
||||||
|
</implementation_roadmap>
|
||||||
</project_specification>
|
</project_specification>
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
4. Ensure .automaker/feature_list.json exists (create as empty array [] if not)
|
4. **IMPORTANT - Generate Feature List:**
|
||||||
|
After writing the app_spec.txt, you MUST update .automaker/feature_list.json with features from the implementation_roadmap section:
|
||||||
|
- Read the app_spec.txt you just created
|
||||||
|
- For EVERY feature in each phase of the implementation_roadmap, create an entry
|
||||||
|
- Write ALL features to .automaker/feature_list.json
|
||||||
|
|
||||||
|
The feature_list.json format should be:
|
||||||
|
\`\`\`json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "feature-<timestamp>-<index>",
|
||||||
|
"category": "<phase name, e.g., 'Phase 1: Foundation'>",
|
||||||
|
"description": "<feature description>",
|
||||||
|
"status": "backlog",
|
||||||
|
"steps": ["Step 1", "Step 2", "..."],
|
||||||
|
"skipTests": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Generate unique IDs using the current timestamp and index (e.g., "feature-1234567890-0", "feature-1234567890-1", etc.)
|
||||||
|
|
||||||
5. Ensure .automaker/context/ directory exists
|
5. Ensure .automaker/context/ directory exists
|
||||||
|
|
||||||
@@ -420,6 +453,7 @@ Analyze this project's codebase and update the .automaker/app_spec.txt file with
|
|||||||
- Only include information you can verify from the codebase
|
- Only include information you can verify from the codebase
|
||||||
- If unsure about something, note it as "to be determined"
|
- If unsure about something, note it as "to be determined"
|
||||||
- Don't make up features that don't exist
|
- Don't make up features that don't exist
|
||||||
|
- Include EVERY feature from the roadmap in feature_list.json - do not skip any
|
||||||
|
|
||||||
Begin by exploring the project structure.`;
|
Begin by exploring the project structure.`;
|
||||||
}
|
}
|
||||||
@@ -618,6 +652,7 @@ Your goal is to:
|
|||||||
- Identify programming languages, frameworks, and libraries
|
- Identify programming languages, frameworks, and libraries
|
||||||
- Detect existing features and capabilities
|
- Detect existing features and capabilities
|
||||||
- Update the .automaker/app_spec.txt with accurate information
|
- Update the .automaker/app_spec.txt with accurate information
|
||||||
|
- Generate a feature list in .automaker/feature_list.json based on the implementation roadmap
|
||||||
- Ensure all required .automaker files and directories exist
|
- Ensure all required .automaker files and directories exist
|
||||||
|
|
||||||
Be efficient - don't read every file, focus on:
|
Be efficient - don't read every file, focus on:
|
||||||
@@ -626,7 +661,13 @@ Be efficient - don't read every file, focus on:
|
|||||||
- Directory structure
|
- Directory structure
|
||||||
- README and documentation
|
- README and documentation
|
||||||
|
|
||||||
You have read access to files and can run basic bash commands to explore the structure.`;
|
**CRITICAL - Feature List Generation:**
|
||||||
|
After creating/updating the app_spec.txt, you MUST also update .automaker/feature_list.json:
|
||||||
|
1. Read the app_spec.txt you just wrote
|
||||||
|
2. Extract all features from the implementation_roadmap section
|
||||||
|
3. Write them to .automaker/feature_list.json in the correct format
|
||||||
|
|
||||||
|
You have access to Read, Write, Edit, Glob, Grep, and Bash tools. Use them to explore the structure and write the necessary files.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,29 @@ export function SessionManager({
|
|||||||
const [editingName, setEditingName] = useState("");
|
const [editingName, setEditingName] = useState("");
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const [newSessionName, setNewSessionName] = useState("");
|
const [newSessionName, setNewSessionName] = useState("");
|
||||||
|
const [runningSessions, setRunningSessions] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
// Check running state for all sessions
|
||||||
|
const checkRunningSessions = async (sessionList: SessionListItem[]) => {
|
||||||
|
if (!window.electronAPI?.agent) return;
|
||||||
|
|
||||||
|
const runningIds = new Set<string>();
|
||||||
|
|
||||||
|
// Check each session's running state
|
||||||
|
for (const session of sessionList) {
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI.agent.getHistory(session.id);
|
||||||
|
if (result.success && result.isRunning) {
|
||||||
|
runningIds.add(session.id);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore errors for individual session checks
|
||||||
|
console.warn(`[SessionManager] Failed to check running state for ${session.id}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRunningSessions(runningIds);
|
||||||
|
};
|
||||||
|
|
||||||
// Load sessions
|
// Load sessions
|
||||||
const loadSessions = async () => {
|
const loadSessions = async () => {
|
||||||
@@ -76,6 +99,8 @@ export function SessionManager({
|
|||||||
const result = await window.electronAPI.sessions.list(true);
|
const result = await window.electronAPI.sessions.list(true);
|
||||||
if (result.success && result.sessions) {
|
if (result.success && result.sessions) {
|
||||||
setSessions(result.sessions);
|
setSessions(result.sessions);
|
||||||
|
// Check running state for all sessions
|
||||||
|
await checkRunningSessions(result.sessions);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,6 +108,20 @@ export function SessionManager({
|
|||||||
loadSessions();
|
loadSessions();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Periodically check running state for sessions (useful for detecting when agents finish)
|
||||||
|
useEffect(() => {
|
||||||
|
// Only poll if there are running sessions
|
||||||
|
if (runningSessions.size === 0 && !isCurrentSessionThinking) return;
|
||||||
|
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
if (sessions.length > 0) {
|
||||||
|
await checkRunningSessions(sessions);
|
||||||
|
}
|
||||||
|
}, 3000); // Check every 3 seconds
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [sessions, runningSessions.size, isCurrentSessionThinking]);
|
||||||
|
|
||||||
// Create new session with random name
|
// Create new session with random name
|
||||||
const handleCreateSession = async () => {
|
const handleCreateSession = async () => {
|
||||||
if (!window.electronAPI?.sessions) return;
|
if (!window.electronAPI?.sessions) return;
|
||||||
@@ -328,13 +367,14 @@ export function SessionManager({
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
{currentSessionId === session.id && isCurrentSessionThinking ? (
|
{/* Show loading indicator if this session is running (either current session thinking or any session in runningSessions) */}
|
||||||
|
{((currentSessionId === session.id && isCurrentSessionThinking) || runningSessions.has(session.id)) ? (
|
||||||
<Loader2 className="w-4 h-4 text-primary animate-spin shrink-0" />
|
<Loader2 className="w-4 h-4 text-primary animate-spin shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<MessageSquare className="w-4 h-4 text-muted-foreground shrink-0" />
|
<MessageSquare className="w-4 h-4 text-muted-foreground shrink-0" />
|
||||||
)}
|
)}
|
||||||
<h3 className="font-medium truncate">{session.name}</h3>
|
<h3 className="font-medium truncate">{session.name}</h3>
|
||||||
{currentSessionId === session.id && isCurrentSessionThinking && (
|
{((currentSessionId === session.id && isCurrentSessionThinking) || runningSessions.has(session.id)) && (
|
||||||
<span className="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full">
|
<span className="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full">
|
||||||
thinking...
|
thinking...
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ export function DescriptionImageDropZone({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleBrowseClick}
|
onClick={handleBrowseClick}
|
||||||
className="text-blue-500 hover:text-blue-400 underline"
|
className="text-primary hover:text-primary/80 underline"
|
||||||
disabled={disabled || isProcessing}
|
disabled={disabled || isProcessing}
|
||||||
>
|
>
|
||||||
browse
|
browse
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useElectronAgent } from "@/hooks/use-electron-agent";
|
import { useElectronAgent } from "@/hooks/use-electron-agent";
|
||||||
import { SessionManager } from "@/components/session-manager";
|
import { SessionManager } from "@/components/session-manager";
|
||||||
|
import { Markdown } from "@/components/ui/markdown";
|
||||||
import type { ImageAttachment } from "@/store/app-store";
|
import type { ImageAttachment } from "@/store/app-store";
|
||||||
import {
|
import {
|
||||||
useKeyboardShortcuts,
|
useKeyboardShortcuts,
|
||||||
@@ -30,7 +31,7 @@ import {
|
|||||||
} from "@/hooks/use-keyboard-shortcuts";
|
} from "@/hooks/use-keyboard-shortcuts";
|
||||||
|
|
||||||
export function AgentView() {
|
export function AgentView() {
|
||||||
const { currentProject } = useAppStore();
|
const { currentProject, setLastSelectedSession, getLastSelectedSession } = useAppStore();
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
const [selectedImages, setSelectedImages] = useState<ImageAttachment[]>([]);
|
const [selectedImages, setSelectedImages] = useState<ImageAttachment[]>([]);
|
||||||
const [showImageDropZone, setShowImageDropZone] = useState(false);
|
const [showImageDropZone, setShowImageDropZone] = useState(false);
|
||||||
@@ -39,6 +40,9 @@ export function AgentView() {
|
|||||||
const [showSessionManager, setShowSessionManager] = useState(true);
|
const [showSessionManager, setShowSessionManager] = useState(true);
|
||||||
const [isDragOver, setIsDragOver] = useState(false);
|
const [isDragOver, setIsDragOver] = useState(false);
|
||||||
|
|
||||||
|
// Track if initial session has been loaded
|
||||||
|
const initialSessionLoadedRef = useRef(false);
|
||||||
|
|
||||||
// Scroll management for auto-scroll
|
// Scroll management for auto-scroll
|
||||||
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const [isUserAtBottom, setIsUserAtBottom] = useState(true);
|
const [isUserAtBottom, setIsUserAtBottom] = useState(true);
|
||||||
@@ -66,6 +70,40 @@ export function AgentView() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle session selection with persistence
|
||||||
|
const handleSelectSession = useCallback((sessionId: string | null) => {
|
||||||
|
setCurrentSessionId(sessionId);
|
||||||
|
// Persist the selection for this project
|
||||||
|
if (currentProject?.path) {
|
||||||
|
setLastSelectedSession(currentProject.path, sessionId);
|
||||||
|
}
|
||||||
|
}, [currentProject?.path, setLastSelectedSession]);
|
||||||
|
|
||||||
|
// Restore last selected session when switching to Agent view or when project changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!currentProject?.path) {
|
||||||
|
// No project, reset
|
||||||
|
setCurrentSessionId(null);
|
||||||
|
initialSessionLoadedRef.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only restore once per project
|
||||||
|
if (initialSessionLoadedRef.current) return;
|
||||||
|
initialSessionLoadedRef.current = true;
|
||||||
|
|
||||||
|
const lastSessionId = getLastSelectedSession(currentProject.path);
|
||||||
|
if (lastSessionId) {
|
||||||
|
console.log("[AgentView] Restoring last selected session:", lastSessionId);
|
||||||
|
setCurrentSessionId(lastSessionId);
|
||||||
|
}
|
||||||
|
}, [currentProject?.path, getLastSelectedSession]);
|
||||||
|
|
||||||
|
// Reset initialSessionLoadedRef when project changes
|
||||||
|
useEffect(() => {
|
||||||
|
initialSessionLoadedRef.current = false;
|
||||||
|
}, [currentProject?.path]);
|
||||||
|
|
||||||
const handleSend = useCallback(async () => {
|
const handleSend = useCallback(async () => {
|
||||||
if ((!input.trim() && selectedImages.length === 0) || isProcessing) return;
|
if ((!input.trim() && selectedImages.length === 0) || isProcessing) return;
|
||||||
|
|
||||||
@@ -441,7 +479,7 @@ export function AgentView() {
|
|||||||
<div className="w-80 border-r flex-shrink-0">
|
<div className="w-80 border-r flex-shrink-0">
|
||||||
<SessionManager
|
<SessionManager
|
||||||
currentSessionId={currentSessionId}
|
currentSessionId={currentSessionId}
|
||||||
onSelectSession={setCurrentSessionId}
|
onSelectSession={handleSelectSession}
|
||||||
projectPath={currentProject.path}
|
projectPath={currentProject.path}
|
||||||
isCurrentSessionThinking={isProcessing}
|
isCurrentSessionThinking={isProcessing}
|
||||||
onQuickCreateRef={quickCreateSessionRef}
|
onQuickCreateRef={quickCreateSessionRef}
|
||||||
@@ -559,9 +597,13 @@ export function AgentView() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CardContent className="p-3">
|
<CardContent className="p-3">
|
||||||
<p className="text-sm whitespace-pre-wrap">
|
{message.role === "assistant" ? (
|
||||||
{message.content}
|
<Markdown className="text-sm">{message.content}</Markdown>
|
||||||
</p>
|
) : (
|
||||||
|
<p className="text-sm whitespace-pre-wrap">
|
||||||
|
{message.content}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<p
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs mt-2",
|
"text-xs mt-2",
|
||||||
|
|||||||
@@ -230,6 +230,10 @@ export function BoardView() {
|
|||||||
const [showEditAdvancedOptions, setShowEditAdvancedOptions] = useState(false);
|
const [showEditAdvancedOptions, setShowEditAdvancedOptions] = useState(false);
|
||||||
const [showSuggestionsDialog, setShowSuggestionsDialog] = useState(false);
|
const [showSuggestionsDialog, setShowSuggestionsDialog] = useState(false);
|
||||||
const [suggestionsCount, setSuggestionsCount] = useState(0);
|
const [suggestionsCount, setSuggestionsCount] = useState(0);
|
||||||
|
const [featureSuggestions, setFeatureSuggestions] = useState<
|
||||||
|
import("@/lib/electron").FeatureSuggestion[]
|
||||||
|
>([]);
|
||||||
|
const [isGeneratingSuggestions, setIsGeneratingSuggestions] = useState(false);
|
||||||
|
|
||||||
// Make current project available globally for modal
|
// Make current project available globally for modal
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -241,7 +245,7 @@ export function BoardView() {
|
|||||||
};
|
};
|
||||||
}, [currentProject]);
|
}, [currentProject]);
|
||||||
|
|
||||||
// Listen for suggestions events to update count
|
// Listen for suggestions events to update count (persists even when dialog is closed)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.suggestions) return;
|
if (!api?.suggestions) return;
|
||||||
@@ -249,6 +253,10 @@ export function BoardView() {
|
|||||||
const unsubscribe = api.suggestions.onEvent((event) => {
|
const unsubscribe = api.suggestions.onEvent((event) => {
|
||||||
if (event.type === "suggestions_complete" && event.suggestions) {
|
if (event.type === "suggestions_complete" && event.suggestions) {
|
||||||
setSuggestionsCount(event.suggestions.length);
|
setSuggestionsCount(event.suggestions.length);
|
||||||
|
setFeatureSuggestions(event.suggestions);
|
||||||
|
setIsGeneratingSuggestions(false);
|
||||||
|
} else if (event.type === "suggestions_error") {
|
||||||
|
setIsGeneratingSuggestions(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2567,9 +2575,15 @@ export function BoardView() {
|
|||||||
open={showSuggestionsDialog}
|
open={showSuggestionsDialog}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setShowSuggestionsDialog(false);
|
setShowSuggestionsDialog(false);
|
||||||
// Clear the count when dialog is closed (suggestions were either imported or dismissed)
|
|
||||||
}}
|
}}
|
||||||
projectPath={currentProject.path}
|
projectPath={currentProject.path}
|
||||||
|
suggestions={featureSuggestions}
|
||||||
|
setSuggestions={(suggestions) => {
|
||||||
|
setFeatureSuggestions(suggestions);
|
||||||
|
setSuggestionsCount(suggestions.length);
|
||||||
|
}}
|
||||||
|
isGenerating={isGeneratingSuggestions}
|
||||||
|
setIsGenerating={setIsGeneratingSuggestions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,16 +28,23 @@ interface FeatureSuggestionsDialogProps {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
projectPath: string;
|
projectPath: string;
|
||||||
|
// Props to persist state across dialog open/close
|
||||||
|
suggestions: FeatureSuggestion[];
|
||||||
|
setSuggestions: (suggestions: FeatureSuggestion[]) => void;
|
||||||
|
isGenerating: boolean;
|
||||||
|
setIsGenerating: (generating: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeatureSuggestionsDialog({
|
export function FeatureSuggestionsDialog({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
projectPath,
|
projectPath,
|
||||||
|
suggestions,
|
||||||
|
setSuggestions,
|
||||||
|
isGenerating,
|
||||||
|
setIsGenerating,
|
||||||
}: FeatureSuggestionsDialogProps) {
|
}: FeatureSuggestionsDialogProps) {
|
||||||
const [isGenerating, setIsGenerating] = useState(false);
|
|
||||||
const [progress, setProgress] = useState<string[]>([]);
|
const [progress, setProgress] = useState<string[]>([]);
|
||||||
const [suggestions, setSuggestions] = useState<FeatureSuggestion[]>([]);
|
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
||||||
const [isImporting, setIsImporting] = useState(false);
|
const [isImporting, setIsImporting] = useState(false);
|
||||||
@@ -46,6 +53,13 @@ export function FeatureSuggestionsDialog({
|
|||||||
|
|
||||||
const { features, setFeatures } = useAppStore();
|
const { features, setFeatures } = useAppStore();
|
||||||
|
|
||||||
|
// Initialize selectedIds when suggestions change
|
||||||
|
useEffect(() => {
|
||||||
|
if (suggestions.length > 0 && selectedIds.size === 0) {
|
||||||
|
setSelectedIds(new Set(suggestions.map((s) => s.id)));
|
||||||
|
}
|
||||||
|
}, [suggestions, selectedIds.size]);
|
||||||
|
|
||||||
// Auto-scroll progress when new content arrives
|
// Auto-scroll progress when new content arrives
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (autoScrollRef.current && scrollRef.current && isGenerating) {
|
if (autoScrollRef.current && scrollRef.current && isGenerating) {
|
||||||
@@ -53,7 +67,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
}
|
}
|
||||||
}, [progress, isGenerating]);
|
}, [progress, isGenerating]);
|
||||||
|
|
||||||
// Listen for suggestion events
|
// Listen for suggestion events when dialog is open
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
|
|
||||||
@@ -85,7 +99,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [open]);
|
}, [open, setSuggestions, setIsGenerating]);
|
||||||
|
|
||||||
// Start generating suggestions
|
// Start generating suggestions
|
||||||
const handleGenerate = useCallback(async () => {
|
const handleGenerate = useCallback(async () => {
|
||||||
@@ -111,7 +125,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
toast.error("Failed to start generation");
|
toast.error("Failed to start generation");
|
||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
}
|
}
|
||||||
}, [projectPath]);
|
}, [projectPath, setIsGenerating, setSuggestions]);
|
||||||
|
|
||||||
// Stop generating
|
// Stop generating
|
||||||
const handleStop = useCallback(async () => {
|
const handleStop = useCallback(async () => {
|
||||||
@@ -125,7 +139,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to stop generation:", error);
|
console.error("Failed to stop generation:", error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [setIsGenerating]);
|
||||||
|
|
||||||
// Toggle suggestion selection
|
// Toggle suggestion selection
|
||||||
const toggleSelection = useCallback((id: string) => {
|
const toggleSelection = useCallback((id: string) => {
|
||||||
@@ -198,6 +212,12 @@ export function FeatureSuggestionsDialog({
|
|||||||
setFeatures(updatedFeatures);
|
setFeatures(updatedFeatures);
|
||||||
|
|
||||||
toast.success(`Imported ${newFeatures.length} features to backlog!`);
|
toast.success(`Imported ${newFeatures.length} features to backlog!`);
|
||||||
|
|
||||||
|
// Clear suggestions after importing
|
||||||
|
setSuggestions([]);
|
||||||
|
setSelectedIds(new Set());
|
||||||
|
setProgress([]);
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to import features:", error);
|
console.error("Failed to import features:", error);
|
||||||
@@ -205,7 +225,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
} finally {
|
} finally {
|
||||||
setIsImporting(false);
|
setIsImporting(false);
|
||||||
}
|
}
|
||||||
}, [selectedIds, suggestions, features, setFeatures, projectPath, onClose]);
|
}, [selectedIds, suggestions, features, setFeatures, setSuggestions, projectPath, onClose]);
|
||||||
|
|
||||||
// Handle scroll to detect if user scrolled up
|
// Handle scroll to detect if user scrolled up
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
|
|||||||
@@ -137,8 +137,7 @@ export function useElectronAgent({
|
|||||||
let mounted = true;
|
let mounted = true;
|
||||||
|
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
// Reset state when switching sessions
|
// Reset error state when switching sessions
|
||||||
setIsProcessing(false);
|
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -154,13 +153,23 @@ export function useElectronAgent({
|
|||||||
console.log("[useElectronAgent] Loaded", result.messages.length, "messages");
|
console.log("[useElectronAgent] Loaded", result.messages.length, "messages");
|
||||||
setMessages(result.messages);
|
setMessages(result.messages);
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
|
|
||||||
|
// Check if the agent is currently running for this session
|
||||||
|
const historyResult = await window.electronAPI.agent.getHistory(sessionId);
|
||||||
|
if (mounted && historyResult.success) {
|
||||||
|
const isRunning = historyResult.isRunning || false;
|
||||||
|
console.log("[useElectronAgent] Session running state:", isRunning);
|
||||||
|
setIsProcessing(isRunning);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setError(result.error || "Failed to start session");
|
setError(result.error || "Failed to start session");
|
||||||
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
console.error("[useElectronAgent] Failed to initialize:", err);
|
console.error("[useElectronAgent] Failed to initialize:", err);
|
||||||
setError(err instanceof Error ? err.message : "Failed to initialize");
|
setError(err instanceof Error ? err.message : "Failed to initialize");
|
||||||
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ export const ACTION_SHORTCUTS: Record<string, string> = {
|
|||||||
addFeature: "N", // N for New feature
|
addFeature: "N", // N for New feature
|
||||||
addContextFile: "F", // F for File (add context file)
|
addContextFile: "F", // F for File (add context file)
|
||||||
startNext: "G", // G for Grab (start next features from backlog)
|
startNext: "G", // G for Grab (start next features from backlog)
|
||||||
newSession: "W", // W for new session (in agent view)
|
newSession: "N", // N for New session (in agent view)
|
||||||
openProject: "O", // O for Open project (navigate to welcome view)
|
openProject: "O", // O for Open project (navigate to welcome view)
|
||||||
projectPicker: "P", // P for Project picker
|
projectPicker: "P", // P for Project picker
|
||||||
cyclePrevProject: "Q", // Q for previous project (cycle back through MRU)
|
cyclePrevProject: "Q", // Q for previous project (cycle back through MRU)
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ export interface AppState {
|
|||||||
currentView: ViewMode;
|
currentView: ViewMode;
|
||||||
sidebarOpen: boolean;
|
sidebarOpen: boolean;
|
||||||
|
|
||||||
|
// Agent Session state (per-project, keyed by project path)
|
||||||
|
lastSelectedSessionByProject: Record<string, string>; // projectPath -> sessionId
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
theme: ThemeMode;
|
theme: ThemeMode;
|
||||||
|
|
||||||
@@ -308,6 +311,10 @@ export interface AppActions {
|
|||||||
setIsAnalyzing: (analyzing: boolean) => void;
|
setIsAnalyzing: (analyzing: boolean) => void;
|
||||||
clearAnalysis: () => void;
|
clearAnalysis: () => void;
|
||||||
|
|
||||||
|
// Agent Session actions
|
||||||
|
setLastSelectedSession: (projectPath: string, sessionId: string | null) => void;
|
||||||
|
getLastSelectedSession: (projectPath: string) => string | null;
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
}
|
}
|
||||||
@@ -374,6 +381,7 @@ const initialState: AppState = {
|
|||||||
projectHistoryIndex: -1,
|
projectHistoryIndex: -1,
|
||||||
currentView: "welcome",
|
currentView: "welcome",
|
||||||
sidebarOpen: true,
|
sidebarOpen: true,
|
||||||
|
lastSelectedSessionByProject: {},
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
features: [],
|
features: [],
|
||||||
appSpec: "",
|
appSpec: "",
|
||||||
@@ -532,17 +540,33 @@ export const useAppStore = create<AppState & AppActions>()(
|
|||||||
|
|
||||||
cyclePrevProject: () => {
|
cyclePrevProject: () => {
|
||||||
const { projectHistory, projectHistoryIndex, projects } = get();
|
const { projectHistory, projectHistoryIndex, projects } = get();
|
||||||
if (projectHistory.length <= 1) return; // Need at least 2 projects to cycle
|
|
||||||
|
|
||||||
// Move to the next index (going back in history = higher index)
|
// Filter history to only include valid projects
|
||||||
const newIndex = (projectHistoryIndex + 1) % projectHistory.length;
|
const validHistory = projectHistory.filter((id) =>
|
||||||
const targetProjectId = projectHistory[newIndex];
|
projects.some((p) => p.id === id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (validHistory.length <= 1) return; // Need at least 2 valid projects to cycle
|
||||||
|
|
||||||
|
// Find current position in valid history
|
||||||
|
const currentProjectId = get().currentProject?.id;
|
||||||
|
let currentIndex = currentProjectId
|
||||||
|
? validHistory.indexOf(currentProjectId)
|
||||||
|
: projectHistoryIndex;
|
||||||
|
|
||||||
|
// If current project not found in valid history, start from 0
|
||||||
|
if (currentIndex === -1) currentIndex = 0;
|
||||||
|
|
||||||
|
// Move to the next index (going back in history = higher index), wrapping around
|
||||||
|
const newIndex = (currentIndex + 1) % validHistory.length;
|
||||||
|
const targetProjectId = validHistory[newIndex];
|
||||||
const targetProject = projects.find((p) => p.id === targetProjectId);
|
const targetProject = projects.find((p) => p.id === targetProjectId);
|
||||||
|
|
||||||
if (targetProject) {
|
if (targetProject) {
|
||||||
// Update the index but don't modify history order when cycling
|
// Update history to only include valid projects and set new index
|
||||||
set({
|
set({
|
||||||
currentProject: targetProject,
|
currentProject: targetProject,
|
||||||
|
projectHistory: validHistory,
|
||||||
projectHistoryIndex: newIndex,
|
projectHistoryIndex: newIndex,
|
||||||
currentView: "board"
|
currentView: "board"
|
||||||
});
|
});
|
||||||
@@ -551,19 +575,35 @@ export const useAppStore = create<AppState & AppActions>()(
|
|||||||
|
|
||||||
cycleNextProject: () => {
|
cycleNextProject: () => {
|
||||||
const { projectHistory, projectHistoryIndex, projects } = get();
|
const { projectHistory, projectHistoryIndex, projects } = get();
|
||||||
if (projectHistory.length <= 1) return; // Need at least 2 projects to cycle
|
|
||||||
|
|
||||||
// Move to the previous index (going forward = lower index, wrapping around)
|
// Filter history to only include valid projects
|
||||||
const newIndex = projectHistoryIndex <= 0
|
const validHistory = projectHistory.filter((id) =>
|
||||||
? projectHistory.length - 1
|
projects.some((p) => p.id === id)
|
||||||
: projectHistoryIndex - 1;
|
);
|
||||||
const targetProjectId = projectHistory[newIndex];
|
|
||||||
|
if (validHistory.length <= 1) return; // Need at least 2 valid projects to cycle
|
||||||
|
|
||||||
|
// Find current position in valid history
|
||||||
|
const currentProjectId = get().currentProject?.id;
|
||||||
|
let currentIndex = currentProjectId
|
||||||
|
? validHistory.indexOf(currentProjectId)
|
||||||
|
: projectHistoryIndex;
|
||||||
|
|
||||||
|
// If current project not found in valid history, start from 0
|
||||||
|
if (currentIndex === -1) currentIndex = 0;
|
||||||
|
|
||||||
|
// Move to the previous index (going forward = lower index), wrapping around
|
||||||
|
const newIndex = currentIndex <= 0
|
||||||
|
? validHistory.length - 1
|
||||||
|
: currentIndex - 1;
|
||||||
|
const targetProjectId = validHistory[newIndex];
|
||||||
const targetProject = projects.find((p) => p.id === targetProjectId);
|
const targetProject = projects.find((p) => p.id === targetProjectId);
|
||||||
|
|
||||||
if (targetProject) {
|
if (targetProject) {
|
||||||
// Update the index but don't modify history order when cycling
|
// Update history to only include valid projects and set new index
|
||||||
set({
|
set({
|
||||||
currentProject: targetProject,
|
currentProject: targetProject,
|
||||||
|
projectHistory: validHistory,
|
||||||
projectHistoryIndex: newIndex,
|
projectHistoryIndex: newIndex,
|
||||||
currentView: "board"
|
currentView: "board"
|
||||||
});
|
});
|
||||||
@@ -869,6 +909,26 @@ export const useAppStore = create<AppState & AppActions>()(
|
|||||||
setIsAnalyzing: (analyzing) => set({ isAnalyzing: analyzing }),
|
setIsAnalyzing: (analyzing) => set({ isAnalyzing: analyzing }),
|
||||||
clearAnalysis: () => set({ projectAnalysis: null }),
|
clearAnalysis: () => set({ projectAnalysis: null }),
|
||||||
|
|
||||||
|
// Agent Session actions
|
||||||
|
setLastSelectedSession: (projectPath, sessionId) => {
|
||||||
|
const current = get().lastSelectedSessionByProject;
|
||||||
|
if (sessionId === null) {
|
||||||
|
// Remove the entry for this project
|
||||||
|
const { [projectPath]: _, ...rest } = current;
|
||||||
|
set({ lastSelectedSessionByProject: rest });
|
||||||
|
} else {
|
||||||
|
set({
|
||||||
|
lastSelectedSessionByProject: {
|
||||||
|
...current,
|
||||||
|
[projectPath]: sessionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getLastSelectedSession: (projectPath) => {
|
||||||
|
return get().lastSelectedSessionByProject[projectPath] || null;
|
||||||
|
},
|
||||||
// Reset
|
// Reset
|
||||||
reset: () => set(initialState),
|
reset: () => set(initialState),
|
||||||
}),
|
}),
|
||||||
@@ -892,6 +952,7 @@ export const useAppStore = create<AppState & AppActions>()(
|
|||||||
useWorktrees: state.useWorktrees,
|
useWorktrees: state.useWorktrees,
|
||||||
showProfilesOnly: state.showProfilesOnly,
|
showProfilesOnly: state.showProfilesOnly,
|
||||||
aiProfiles: state.aiProfiles,
|
aiProfiles: state.aiProfiles,
|
||||||
|
lastSelectedSessionByProject: state.lastSelectedSessionByProject,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user