feat: register local agent project workspaces

This commit is contained in:
Hamza Ayed
2026-10-04 01:57:44 +03:00
parent d29275043a
commit 1feeb372a4
12 changed files with 806 additions and 45 deletions
@@ -17,6 +17,8 @@ class _VersionTestApi extends ApiRepository {
String? indexedWorkspace;
List<String> indexedFiles = const [];
List<String> deletedFiles = const [];
String? registeredProject;
String? unregisteredProject;
@override
Future<String> getModelName() async => 'gemma4:e2b';
@@ -62,6 +64,17 @@ class _VersionTestApi extends ApiRepository {
'README.md',
];
@override
Future<List<String>> registerWorkspaceProject(String workspacePath) async {
registeredProject = workspacePath;
return listWorkspaceFiles(workspacePath);
}
@override
Future<void> unregisterWorkspaceProject(String workspacePath) async {
unregisteredProject = workspacePath;
}
@override
Future<Map<String, dynamic>> indexWorkspaceKnowledge(
String workspacePath,
@@ -180,30 +193,50 @@ void main() {
await cubit.close();
});
test('registered project folders persist and can be removed from the list', () async {
final settings = _MemorySettingsStore();
final firstApi = _VersionTestApi();
final first = ChatCubit(firstApi, settingsStore: settings);
test(
'registered project folders persist and can be removed from the list',
() async {
final settings = _MemorySettingsStore();
final firstApi = _VersionTestApi();
final first = ChatCubit(firstApi, settingsStore: settings);
await Future<void>.delayed(Duration.zero);
await first.selectWorkspace(r'C:\Projects\sovereign-ai');
expect(firstApi.registeredProject, r'C:\Projects\sovereign-ai');
expect(first.state.savedWorkspacePaths, [r'C:\Projects\sovereign-ai']);
expect(settings.values['saved_workspace_paths'], [
r'C:\Projects\sovereign-ai',
]);
await first.close();
final restoredApi = _VersionTestApi();
final restored = ChatCubit(restoredApi, settingsStore: settings);
await Future<void>.delayed(Duration.zero);
expect(restored.state.savedWorkspacePaths, [r'C:\Projects\sovereign-ai']);
await restored.removeSavedWorkspace(r'C:\Projects\sovereign-ai');
expect(restoredApi.unregisteredProject, r'C:\Projects\sovereign-ai');
expect(restored.state.savedWorkspacePaths, isEmpty);
expect(settings.values['saved_workspace_paths'], isEmpty);
await restored.close();
},
);
test('removing the active project revokes its chat context', () async {
final api = _VersionTestApi();
final cubit = ChatCubit(api, settingsStore: LocalSettingsStore.inMemory());
await Future<void>.delayed(Duration.zero);
const project = r'C:\Projects\active-project';
await first.selectWorkspace(r'C:\Projects\sovereign-ai');
expect(first.state.savedWorkspacePaths, [r'C:\Projects\sovereign-ai']);
expect(settings.values['saved_workspace_paths'], [
r'C:\Projects\sovereign-ai',
]);
await first.close();
await cubit.selectWorkspace(project);
expect(cubit.state.isWorkspaceMode, isTrue);
await cubit.removeSavedWorkspace(project);
final restored = ChatCubit(
_VersionTestApi(),
settingsStore: settings,
);
await Future<void>.delayed(Duration.zero);
expect(restored.state.savedWorkspacePaths, [r'C:\Projects\sovereign-ai']);
await restored.removeSavedWorkspace(r'C:\Projects\sovereign-ai');
expect(restored.state.savedWorkspacePaths, isEmpty);
expect(settings.values['saved_workspace_paths'], isEmpty);
await restored.close();
expect(api.unregisteredProject, project);
expect(cubit.state.isWorkspaceMode, isFalse);
expect(cubit.state.selectedWorkspacePath, isNull);
expect(cubit.state.availableWorkspaceFiles, isEmpty);
await cubit.close();
});
test('regeneration keeps prior answers and selection is persisted', () async {
@@ -50,6 +50,13 @@ class _TestApiRepository extends ApiRepository {
'lib/main.dart',
];
@override
Future<List<String>> registerWorkspaceProject(String workspacePath) async =>
listWorkspaceFiles(workspacePath);
@override
Future<void> unregisterWorkspaceProject(String workspacePath) async {}
@override
Future<List<SavedConversation>> listConversations() async => [];
}
@@ -97,6 +104,13 @@ class _ProposalWidgetApi extends _TestApiRepository {
'src/hello.py',
];
@override
Future<List<String>> registerWorkspaceProject(String workspacePath) async =>
listWorkspaceFiles(workspacePath);
@override
Future<void> unregisterWorkspaceProject(String workspacePath) async {}
@override
Future<AgentRunResult> runAgent(
String task, {