Skip to content

Commit 3adcceb

Browse files
sharpninjaCopilot
andcommitted
refactor: migrate all commands from app Mediator to McpServer.Cqrs.Dispatcher
- Convert 37 command/handler pairs (AllCommands, AsyncCommands, ChatCommands) to McpServer.Cqrs.ICommand<T>/ICommandHandler<T,TResult> with Result<T> returns - Handlers receive ICommandTarget via DI constructor injection (not from command) - Wire AddCqrsHandlers() assembly scanning in UiCoreServiceProviderFactory - Replace _mediator with Dispatcher in MainWindowViewModel and ChatWindowViewModel - Implement TrackBackgroundWork directly in MainWindowViewModel - Delete app-side Mediator, Abstractions, and AppMediatorFactory - Build: 0 errors. Compliance: 0 violations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6b5e8f6 commit 3adcceb

18 files changed

Lines changed: 967 additions & 854 deletions

.codex-session.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"sourceType": "Codex",
3+
"sessionId": "Codex-33b62c9c-bffa-40cd-af9a-1d3c1a693ea4",
4+
"title": "Inspect RequestTracker workspace flow bridge to UI.Core",
5+
"model": "gpt-5-codex",
6+
"started": "2026-03-04T00:02:36.0485539Z",
7+
"lastUpdated": "2026-03-04T00:02:36.2087076Z",
8+
"status": "in_progress",
9+
"entries": [
10+
{
11+
"requestId": "req-001",
12+
"timestamp": "2026-03-04T00:02:36.1985638Z",
13+
"queryText": "Inspect the current workspace flow in RequestTracker app host. Focus on src/McpServerManager.Core/ViewModels/WorkspaceViewModel.cs, MainWindowViewModel interactions, Desktop/Android Workspace views, and UI.Core workspace VMs in lib/McpServer. Report: 1) app-facing properties/commands/events the views and MainWindow depend on, 2) what UI.Core WorkspaceList/Detail/GlobalPrompt/HealthProbe/Context VMs provide, 3) the smallest host bridge shape needed to swap runtime logic onto UI.Core while preserving current UI behavior.",
14+
"queryTitle": "Inspect workspace flow in app host",
15+
"response": "",
16+
"interpretation": "Map the existing host-side workspace VM contract, compare it to UI.Core workspace VM capabilities, and identify the minimal adapter surface to move runtime behavior into UI.Core without changing current desktop/android UI bindings.",
17+
"status": "in_progress",
18+
"model": "gpt-5-codex",
19+
"tags": [
20+
"analysis",
21+
"workspace",
22+
"ui-core"
23+
],
24+
"contextList": [
25+
"src/McpServerManager.Core/ViewModels/WorkspaceViewModel.cs",
26+
"src/McpServerManager.Core/ViewModels/MainWindowViewModel.cs",
27+
"src/McpServerManager.Desktop",
28+
"src/McpServerManager.Android",
29+
"lib/McpServer"
30+
],
31+
"designDecisions": [],
32+
"requirementsDiscovered": [],
33+
"filesModified": [],
34+
"blockers": [],
35+
"actions": [],
36+
"processingDialog": []
37+
}
38+
]
39+
}

docs/todo.yaml

Lines changed: 68 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,74 @@ Architecture:
215215
done: true
216216
- task: Verify GAP-001/002/010/011 closures after runtime consolidation (commit 5e68ad5)
217217
done: true
218+
Features:
219+
high-priority:
220+
- id: settings-tab-speech-filter-words
221+
title: Add settings tab with configurable speech filter word list
222+
estimate: 4h
223+
note: User-configurable word list; match is case-insensitive substring match per line
224+
done: true
225+
completed: 2026-03-01T07:05:34.5814169Z
226+
description:
227+
- Add a Settings tab to the app UI that allows the user to configure a list of filter words.
228+
- When streaming output from MCP tool calls, any line containing one or more of these filter words
229+
- should be excluded from text-to-speech (TTS) synthesis.
230+
- This allows users to suppress noisy or irrelevant lines from being read aloud.
231+
done-summary: Settings tab with configurable speech filter word list implemented and committed in 5e42d8f. SpeechFilterService, SettingsViewModel, SettingsView.axaml all created and integrated. Build verified.
232+
implementation-tasks:
233+
- task: Create SpeechFilterService in Core with persistence and ShouldFilter method
234+
done: true
235+
- task: Create SettingsViewModel in Core with filter word list binding
236+
done: true
237+
- task: Create SettingsView.axaml and code-behind in Desktop
238+
done: true
239+
- task: Add Settings tab to MainWindow.axaml
240+
done: true
241+
- task: Wire SettingsViewModel in MainWindowViewModel
242+
done: true
243+
- task: Integrate SpeechFilterService into Android SimplifiedVoiceView TTS flow
244+
done: true
245+
- task: Build and verify no compilation errors
246+
done: true
247+
DEPLOY-STATUS:
248+
high-priority:
249+
- id: deploy-status-20260302-1512z
250+
title: APK Published and Verified at 20260302-1512Z
251+
done: true
252+
description:
253+
- Pipeline run 22580481045 completed successfully.
254+
- F-Droid index-v1.json resolves to McpServerManager-0.0.1-160.apk (versionCode 101) at https://sharpninja.github.io/McpServerManager/repo.
255+
Infrastructure:
256+
high-priority:
257+
- id: android-crash-diagnostics-pipeline
258+
title: Android crash diagnostics pipeline and repro workflow
259+
estimate: 6h
260+
done: true
261+
completed: 2026-03-03T00:00:00Z
262+
description:
263+
- Implement early Android/global crash capture with persistent artifacts for phone crash diagnosis.
264+
- Add device-side Motorola repro workflow artifacts for logcat, tombstone, and process-exit-reason capture.
265+
done-summary: Implemented shared Android crash diagnostics with early application hookup, persistent fatal/exit/boundary artifacts, adb collection workflow script/docs, and verified the Android Debug build succeeds.
266+
technical-details:
267+
- Install Java uncaught-exception handling in MainApplication before Avalonia App initialization.
268+
- Persist fatal crash reports and native-boundary breadcrumbs under app files directory for recovery on next launch.
269+
- Add repository script/documentation for adb-based capture workflow.
270+
functional-requirements:
271+
- FR-RTUI-117
272+
technical-requirements:
273+
- TR-RTUI-127
274+
- TR-RTUI-128
275+
implementation-tasks:
276+
- task: Add Android crash diagnostics service and early MainApplication hookup
277+
done: true
278+
- task: Persist and replay fatal crash reports plus active native-operation breadcrumbs
279+
done: true
280+
- task: Add adb collection script and workflow documentation
281+
done: true
282+
- task: Validate Android project builds after diagnostics changes
283+
done: true
284+
refactoring:
285+
high-priority:
218286
- id: uicore-vm-relay-plan
219287
title: 'Excruciating plan: refactor apps to UI.Core ViewModels + RelayCommands'
220288
estimate: 5d
@@ -478,69 +546,3 @@ Architecture:
478546
done: false
479547
- task: 'M5.3 Verify and complete AgentApiClientAdapter coverage in McpServer.Web (GAP-018/019 partial): confirm agent-pool queue/job/stream/notifications adapter operations are wired or document remaining Web adapter gaps'
480548
done: false
481-
Features:
482-
high-priority:
483-
- id: settings-tab-speech-filter-words
484-
title: Add settings tab with configurable speech filter word list
485-
estimate: 4h
486-
note: User-configurable word list; match is case-insensitive substring match per line
487-
done: true
488-
completed: 2026-03-01T07:05:34.5814169Z
489-
description:
490-
- Add a Settings tab to the app UI that allows the user to configure a list of filter words.
491-
- When streaming output from MCP tool calls, any line containing one or more of these filter words
492-
- should be excluded from text-to-speech (TTS) synthesis.
493-
- This allows users to suppress noisy or irrelevant lines from being read aloud.
494-
done-summary: Settings tab with configurable speech filter word list implemented and committed in 5e42d8f. SpeechFilterService, SettingsViewModel, SettingsView.axaml all created and integrated. Build verified.
495-
implementation-tasks:
496-
- task: Create SpeechFilterService in Core with persistence and ShouldFilter method
497-
done: true
498-
- task: Create SettingsViewModel in Core with filter word list binding
499-
done: true
500-
- task: Create SettingsView.axaml and code-behind in Desktop
501-
done: true
502-
- task: Add Settings tab to MainWindow.axaml
503-
done: true
504-
- task: Wire SettingsViewModel in MainWindowViewModel
505-
done: true
506-
- task: Integrate SpeechFilterService into Android SimplifiedVoiceView TTS flow
507-
done: true
508-
- task: Build and verify no compilation errors
509-
done: true
510-
DEPLOY-STATUS:
511-
high-priority:
512-
- id: deploy-status-20260302-1512z
513-
title: APK Published and Verified at 20260302-1512Z
514-
done: true
515-
description:
516-
- Pipeline run 22580481045 completed successfully.
517-
- F-Droid index-v1.json resolves to McpServerManager-0.0.1-160.apk (versionCode 101) at https://sharpninja.github.io/McpServerManager/repo.
518-
Infrastructure:
519-
high-priority:
520-
- id: android-crash-diagnostics-pipeline
521-
title: Android crash diagnostics pipeline and repro workflow
522-
estimate: 6h
523-
done: true
524-
completed: 2026-03-03T00:00:00Z
525-
description:
526-
- Implement early Android/global crash capture with persistent artifacts for phone crash diagnosis.
527-
- Add device-side Motorola repro workflow artifacts for logcat, tombstone, and process-exit-reason capture.
528-
done-summary: Implemented shared Android crash diagnostics with early application hookup, persistent fatal/exit/boundary artifacts, adb collection workflow script/docs, and verified the Android Debug build succeeds.
529-
technical-details:
530-
- Install Java uncaught-exception handling in MainApplication before Avalonia App initialization.
531-
- Persist fatal crash reports and native-boundary breadcrumbs under app files directory for recovery on next launch.
532-
- Add repository script/documentation for adb-based capture workflow.
533-
functional-requirements:
534-
- FR-RTUI-117
535-
technical-requirements:
536-
- TR-RTUI-127
537-
- TR-RTUI-128
538-
implementation-tasks:
539-
- task: Add Android crash diagnostics service and early MainApplication hookup
540-
done: true
541-
- task: Persist and replay fatal crash reports plus active native-operation breadcrumbs
542-
done: true
543-
- task: Add adb collection script and workflow documentation
544-
done: true
545-
- task: Validate Android project builds after diagnostics changes
546-
done: true
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using FluentAssertions;
2+
using McpServerManager.Core.Services.Infrastructure;
3+
using Xunit;
4+
5+
namespace McpServerManager.Core.Tests.Services.Infrastructure;
6+
7+
public sealed class FileSystemServiceTests : IDisposable
8+
{
9+
private readonly FileSystemService _sut = new();
10+
private readonly string _tempDir;
11+
12+
public FileSystemServiceTests()
13+
{
14+
_tempDir = Path.Combine(Path.GetTempPath(), $"FsServiceTest_{Guid.NewGuid():N}");
15+
Directory.CreateDirectory(_tempDir);
16+
}
17+
18+
public void Dispose()
19+
{
20+
if (Directory.Exists(_tempDir))
21+
Directory.Delete(_tempDir, recursive: true);
22+
}
23+
24+
private string CreateTempFile(string name, string content)
25+
{
26+
var path = Path.Combine(_tempDir, name);
27+
File.WriteAllText(path, content);
28+
return path;
29+
}
30+
31+
// --- FileExists ---
32+
33+
[Fact]
34+
public void FileExists_ExistingFile_ReturnsTrue()
35+
{
36+
var path = CreateTempFile("exists.txt", "hello");
37+
_sut.FileExists(path).Should().BeTrue();
38+
}
39+
40+
[Fact]
41+
public void FileExists_NonExistentFile_ReturnsFalse()
42+
{
43+
_sut.FileExists(Path.Combine(_tempDir, "nope.txt")).Should().BeFalse();
44+
}
45+
46+
// --- DirectoryExists ---
47+
48+
[Fact]
49+
public void DirectoryExists_ExistingDirectory_ReturnsTrue()
50+
{
51+
_sut.DirectoryExists(_tempDir).Should().BeTrue();
52+
}
53+
54+
[Fact]
55+
public void DirectoryExists_NonExistentDirectory_ReturnsFalse()
56+
{
57+
_sut.DirectoryExists(Path.Combine(_tempDir, "nope")).Should().BeFalse();
58+
}
59+
60+
// --- ReadAllText / WriteAllText ---
61+
62+
[Fact]
63+
public void WriteAllText_ThenReadAllText_RoundTrips()
64+
{
65+
var path = Path.Combine(_tempDir, "rw.txt");
66+
_sut.WriteAllText(path, "round-trip");
67+
_sut.ReadAllText(path).Should().Be("round-trip");
68+
}
69+
70+
// --- ReadAllTextAsync / WriteAllTextAsync ---
71+
72+
[Fact]
73+
public async Task WriteAllTextAsync_ThenReadAllTextAsync_RoundTrips()
74+
{
75+
var path = Path.Combine(_tempDir, "rw_async.txt");
76+
await _sut.WriteAllTextAsync(path, "async-content");
77+
var result = await _sut.ReadAllTextAsync(path);
78+
result.Should().Be("async-content");
79+
}
80+
81+
// --- ReadAllLines ---
82+
83+
[Fact]
84+
public void ReadAllLines_MultipleLines_ReturnsArray()
85+
{
86+
var path = CreateTempFile("lines.txt", "a\nb\nc");
87+
_sut.ReadAllLines(path).Should().Equal("a", "b", "c");
88+
}
89+
90+
// --- MoveFile ---
91+
92+
[Fact]
93+
public void MoveFile_ExistingFile_MovesToDestination()
94+
{
95+
var src = CreateTempFile("src.txt", "data");
96+
var dst = Path.Combine(_tempDir, "dst.txt");
97+
_sut.MoveFile(src, dst);
98+
File.Exists(src).Should().BeFalse();
99+
File.ReadAllText(dst).Should().Be("data");
100+
}
101+
102+
// --- DeleteFile ---
103+
104+
[Fact]
105+
public void DeleteFile_ExistingFile_RemovesFile()
106+
{
107+
var path = CreateTempFile("del.txt", "bye");
108+
_sut.DeleteFile(path);
109+
File.Exists(path).Should().BeFalse();
110+
}
111+
112+
// --- GetLastWriteTimeUtc ---
113+
114+
[Fact]
115+
public void GetLastWriteTimeUtc_ExistingFile_ReturnsRecentTime()
116+
{
117+
var path = CreateTempFile("time.txt", "t");
118+
var time = _sut.GetLastWriteTimeUtc(path);
119+
time.Should().BeCloseTo(DateTime.UtcNow, precision: TimeSpan.FromSeconds(5));
120+
}
121+
122+
// --- EnumerateFiles ---
123+
124+
[Fact]
125+
public void EnumerateFiles_MatchingPattern_ReturnsEntries()
126+
{
127+
CreateTempFile("a.txt", "");
128+
CreateTempFile("b.txt", "");
129+
CreateTempFile("c.log", "");
130+
131+
var entries = _sut.EnumerateFiles(_tempDir, "*.txt", recursive: false).ToList();
132+
entries.Should().HaveCount(2);
133+
entries.Select(e => e.Name).Should().Contain("a.txt").And.Contain("b.txt");
134+
entries.Should().AllSatisfy(e => e.Extension.Should().Be(".txt"));
135+
entries.Should().AllSatisfy(e => e.IsDirectory.Should().BeFalse());
136+
}
137+
138+
// --- EnumerateDirectories ---
139+
140+
[Fact]
141+
public void EnumerateDirectories_SubDirs_ReturnsEntries()
142+
{
143+
Directory.CreateDirectory(Path.Combine(_tempDir, "sub1"));
144+
Directory.CreateDirectory(Path.Combine(_tempDir, "sub2"));
145+
146+
var entries = _sut.EnumerateDirectories(_tempDir, "*", recursive: false).ToList();
147+
entries.Should().HaveCount(2);
148+
entries.Select(e => e.Name).Should().Contain("sub1").And.Contain("sub2");
149+
}
150+
151+
// --- GetFullPath ---
152+
153+
[Fact]
154+
public void GetFullPath_RelativePath_ReturnsAbsolute()
155+
{
156+
var result = _sut.GetFullPath("relative.txt");
157+
Path.IsPathRooted(result).Should().BeTrue();
158+
}
159+
160+
// --- GetDirectoryName ---
161+
162+
[Fact]
163+
public void GetDirectoryName_FilePath_ReturnsParent()
164+
{
165+
var result = _sut.GetDirectoryName(Path.Combine(_tempDir, "child.txt"));
166+
result.Should().Be(_tempDir);
167+
}
168+
169+
// --- CombinePath ---
170+
171+
[Fact]
172+
public void CombinePath_MultipleSegments_CombinesCorrectly()
173+
{
174+
var result = _sut.CombinePath("a", "b", "c.txt");
175+
result.Should().Be(Path.Combine("a", "b", "c.txt"));
176+
}
177+
}

0 commit comments

Comments
 (0)