@prmichaelsen/acp-visualizer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/agent/commands/acp.clarification-address.md +417 -0
- package/agent/commands/acp.clarification-capture.md +386 -0
- package/agent/commands/acp.clarification-create.md +437 -0
- package/agent/commands/acp.clarifications-research.md +326 -0
- package/agent/commands/acp.command-create.md +432 -0
- package/agent/commands/acp.design-create.md +286 -0
- package/agent/commands/acp.design-reference.md +355 -0
- package/agent/commands/acp.handoff.md +270 -0
- package/agent/commands/acp.index.md +423 -0
- package/agent/commands/acp.init.md +546 -0
- package/agent/commands/acp.package-create.md +895 -0
- package/agent/commands/acp.package-info.md +212 -0
- package/agent/commands/acp.package-install.md +539 -0
- package/agent/commands/acp.package-list.md +280 -0
- package/agent/commands/acp.package-publish.md +541 -0
- package/agent/commands/acp.package-remove.md +293 -0
- package/agent/commands/acp.package-search.md +307 -0
- package/agent/commands/acp.package-update.md +361 -0
- package/agent/commands/acp.package-validate.md +540 -0
- package/agent/commands/acp.pattern-create.md +386 -0
- package/agent/commands/acp.plan.md +587 -0
- package/agent/commands/acp.proceed.md +882 -0
- package/agent/commands/acp.project-create.md +675 -0
- package/agent/commands/acp.project-info.md +312 -0
- package/agent/commands/acp.project-list.md +226 -0
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-set.md +227 -0
- package/agent/commands/acp.project-update.md +307 -0
- package/agent/commands/acp.projects-restore.md +228 -0
- package/agent/commands/acp.projects-sync.md +347 -0
- package/agent/commands/acp.report.md +407 -0
- package/agent/commands/acp.resume.md +239 -0
- package/agent/commands/acp.sessions.md +301 -0
- package/agent/commands/acp.status.md +293 -0
- package/agent/commands/acp.sync.md +364 -0
- package/agent/commands/acp.task-create.md +500 -0
- package/agent/commands/acp.update.md +302 -0
- package/agent/commands/acp.validate.md +466 -0
- package/agent/commands/acp.version-check-for-updates.md +276 -0
- package/agent/commands/acp.version-check.md +191 -0
- package/agent/commands/acp.version-update.md +289 -0
- package/agent/commands/command.template.md +339 -0
- package/agent/commands/git.commit.md +526 -0
- package/agent/commands/git.init.md +514 -0
- package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
- package/agent/commands/tanstack-cloudflare.tail.md +275 -0
- package/agent/design/.gitkeep +0 -0
- package/agent/design/design.template.md +154 -0
- package/agent/design/local.dashboard-layout-routing.md +288 -0
- package/agent/design/local.data-model-yaml-parsing.md +310 -0
- package/agent/design/local.search-filtering.md +331 -0
- package/agent/design/local.server-api-auto-refresh.md +235 -0
- package/agent/design/local.table-tree-views.md +299 -0
- package/agent/design/local.visualizer-requirements.md +349 -0
- package/agent/design/requirements.template.md +387 -0
- package/agent/index/.gitkeep +0 -0
- package/agent/index/acp.core.yaml +137 -0
- package/agent/index/local.main.template.yaml +37 -0
- package/agent/manifest.template.yaml +13 -0
- package/agent/manifest.yaml +302 -0
- package/agent/milestones/.gitkeep +0 -0
- package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
- package/agent/milestones/milestone-1-{title}.template.md +206 -0
- package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
- package/agent/package.template.yaml +86 -0
- package/agent/patterns/.gitkeep +0 -0
- package/agent/patterns/bootstrap.template.md +1237 -0
- package/agent/patterns/pattern.template.md +382 -0
- package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
- package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
- package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
- package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
- package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
- package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
- package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
- package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
- package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
- package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
- package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
- package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
- package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
- package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
- package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
- package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
- package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
- package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
- package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
- package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
- package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
- package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
- package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
- package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
- package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
- package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
- package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
- package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
- package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
- package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
- package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
- package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
- package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
- package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
- package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
- package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
- package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
- package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
- package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
- package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
- package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
- package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
- package/agent/progress.template.yaml +161 -0
- package/agent/progress.yaml +145 -0
- package/agent/schemas/package.schema.yaml +276 -0
- package/agent/scripts/acp.common.sh +1781 -0
- package/agent/scripts/acp.install.sh +333 -0
- package/agent/scripts/acp.package-create.sh +924 -0
- package/agent/scripts/acp.package-info.sh +288 -0
- package/agent/scripts/acp.package-install.sh +893 -0
- package/agent/scripts/acp.package-list.sh +311 -0
- package/agent/scripts/acp.package-publish.sh +420 -0
- package/agent/scripts/acp.package-remove.sh +348 -0
- package/agent/scripts/acp.package-search.sh +156 -0
- package/agent/scripts/acp.package-update.sh +517 -0
- package/agent/scripts/acp.package-validate.sh +1018 -0
- package/agent/scripts/acp.uninstall.sh +85 -0
- package/agent/scripts/acp.version-check-for-updates.sh +98 -0
- package/agent/scripts/acp.version-check.sh +47 -0
- package/agent/scripts/acp.version-update.sh +176 -0
- package/agent/scripts/acp.yaml-parser.sh +985 -0
- package/agent/scripts/acp.yaml-validate.sh +205 -0
- package/agent/tasks/.gitkeep +0 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
- package/agent/tasks/task-1-{title}.template.md +244 -0
- package/bin/visualize.mjs +84 -0
- package/package.json +48 -0
- package/src/components/ExtraFieldsBadge.tsx +15 -0
- package/src/components/FilterBar.tsx +33 -0
- package/src/components/Header.tsx +23 -0
- package/src/components/MilestoneTable.tsx +167 -0
- package/src/components/MilestoneTree.tsx +84 -0
- package/src/components/ProgressBar.tsx +20 -0
- package/src/components/SearchInput.tsx +22 -0
- package/src/components/Sidebar.tsx +54 -0
- package/src/components/StatusBadge.tsx +23 -0
- package/src/components/StatusDot.tsx +12 -0
- package/src/components/TaskList.tsx +36 -0
- package/src/components/ViewToggle.tsx +31 -0
- package/src/lib/config.ts +8 -0
- package/src/lib/file-watcher.ts +43 -0
- package/src/lib/search.ts +48 -0
- package/src/lib/types.ts +73 -0
- package/src/lib/useAutoRefresh.ts +31 -0
- package/src/lib/useCollapse.ts +31 -0
- package/src/lib/useFilteredData.ts +55 -0
- package/src/lib/yaml-loader-real.spec.ts +47 -0
- package/src/lib/yaml-loader.spec.ts +201 -0
- package/src/lib/yaml-loader.ts +265 -0
- package/src/routeTree.gen.ts +140 -0
- package/src/router.tsx +10 -0
- package/src/routes/__root.tsx +75 -0
- package/src/routes/api/watch.ts +29 -0
- package/src/routes/index.tsx +115 -0
- package/src/routes/milestones.tsx +50 -0
- package/src/routes/search.tsx +84 -0
- package/src/routes/tasks.tsx +63 -0
- package/src/services/progress-database.service.ts +46 -0
- package/src/styles.css +25 -0
- package/tsconfig.json +24 -0
- package/vite.config.ts +16 -0
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Task 3: Build Server API & Data Loading
|
|
2
|
+
|
|
3
|
+
**Milestone**: [M1 - Project Scaffold & Data Pipeline](../../milestones/milestone-1-project-scaffold-data-pipeline.md)
|
|
4
|
+
**Design Reference**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
|
|
5
|
+
**Estimated Time**: 2 hours
|
|
6
|
+
**Dependencies**: Task 2
|
|
7
|
+
**Status**: Not Started
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
Create the server-side data loading pipeline using TanStack Start server functions and the library-services pattern. The pipeline reads progress.yaml from disk, parses it through the lenient YAML parser, and delivers typed ProgressData to the client via SSR preloading.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
TanStack Start supports server-side data loading via `beforeLoad` in route definitions, which enables SSR preloading so the dashboard renders with data on first paint (no loading spinner). The data loading follows the library-services pattern: a static service class (`ProgressDatabaseService`) encapsulates all data access logic, and routes call service methods in their `beforeLoad` hooks. The file path is configurable via environment variable, CLI argument, or default convention.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
### 1. Create configuration module
|
|
26
|
+
|
|
27
|
+
Create `app/lib/config.ts` to resolve the progress.yaml file path:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// app/lib/config.ts
|
|
31
|
+
|
|
32
|
+
export function getProgressYamlPath(): string {
|
|
33
|
+
// Priority: env var > CLI arg > default
|
|
34
|
+
if (process.env.PROGRESS_YAML_PATH) {
|
|
35
|
+
return process.env.PROGRESS_YAML_PATH;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const cliArg = process.argv.find((arg) => arg.startsWith("--progress-yaml="));
|
|
39
|
+
if (cliArg) {
|
|
40
|
+
return cliArg.split("=")[1];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return "./agent/progress.yaml";
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Create the ProgressDatabaseService
|
|
48
|
+
|
|
49
|
+
Create `app/services/progress-database.service.ts` following the library-services pattern (static methods, no instantiation):
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// app/services/progress-database.service.ts
|
|
53
|
+
import fs from "node:fs";
|
|
54
|
+
import { getProgressYamlPath } from "../lib/config";
|
|
55
|
+
import { parseProgressYaml } from "../lib/yaml-loader";
|
|
56
|
+
import type { ProgressData } from "../lib/types";
|
|
57
|
+
|
|
58
|
+
export interface ProgressResult {
|
|
59
|
+
success: true;
|
|
60
|
+
data: ProgressData;
|
|
61
|
+
} | {
|
|
62
|
+
success: false;
|
|
63
|
+
error: "FILE_NOT_FOUND" | "PARSE_ERROR";
|
|
64
|
+
message: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class ProgressDatabaseService {
|
|
68
|
+
static getProgressData(): ProgressResult {
|
|
69
|
+
const filePath = getProgressYamlPath();
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
73
|
+
const data = parseProgressYaml(raw);
|
|
74
|
+
return { success: true, data };
|
|
75
|
+
} catch (err: unknown) {
|
|
76
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: "FILE_NOT_FOUND",
|
|
80
|
+
message: `progress.yaml not found at: ${filePath}`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: "PARSE_ERROR",
|
|
86
|
+
message: `Failed to parse progress.yaml: ${(err as Error).message}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Implement error handling with structured errors
|
|
94
|
+
|
|
95
|
+
The service returns a discriminated union (`ProgressResult`) so the UI can render appropriate error states:
|
|
96
|
+
|
|
97
|
+
- `FILE_NOT_FOUND`: Show a helpful message with the expected file path and how to configure it
|
|
98
|
+
- `PARSE_ERROR`: Show the error message so the user can fix the YAML
|
|
99
|
+
|
|
100
|
+
### 4. Update the index route with SSR data preloading
|
|
101
|
+
|
|
102
|
+
Update `app/routes/index.tsx` to load data server-side using `beforeLoad`:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// app/routes/index.tsx
|
|
106
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
107
|
+
import { ProgressDatabaseService } from "../services/progress-database.service";
|
|
108
|
+
|
|
109
|
+
export const Route = createFileRoute("/")({
|
|
110
|
+
beforeLoad: async () => {
|
|
111
|
+
const result = ProgressDatabaseService.getProgressData();
|
|
112
|
+
return { progressResult: result };
|
|
113
|
+
},
|
|
114
|
+
component: HomePage,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function HomePage() {
|
|
118
|
+
const { progressResult } = Route.useRouteContext();
|
|
119
|
+
|
|
120
|
+
if (!progressResult.success) {
|
|
121
|
+
return (
|
|
122
|
+
<div className="p-8">
|
|
123
|
+
<h1 className="text-xl font-bold text-red-400">
|
|
124
|
+
Error: {progressResult.error}
|
|
125
|
+
</h1>
|
|
126
|
+
<p className="mt-2 text-gray-400 font-mono text-sm">
|
|
127
|
+
{progressResult.message}
|
|
128
|
+
</p>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="p-8">
|
|
135
|
+
<h1 className="text-2xl font-bold">ACP Progress Visualizer</h1>
|
|
136
|
+
<pre className="mt-4 p-4 bg-gray-900 rounded text-xs font-mono overflow-auto max-h-[80vh]">
|
|
137
|
+
{JSON.stringify(progressResult.data, null, 2)}
|
|
138
|
+
</pre>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 5. Access data via Route.useRouteContext()
|
|
145
|
+
|
|
146
|
+
The `beforeLoad` return value is available in the component via `Route.useRouteContext()`. This pattern ensures:
|
|
147
|
+
|
|
148
|
+
- Data is loaded server-side during SSR (no loading spinner)
|
|
149
|
+
- The component receives typed data
|
|
150
|
+
- Error states are handled before rendering
|
|
151
|
+
|
|
152
|
+
### 6. Display raw JSON for pipeline verification
|
|
153
|
+
|
|
154
|
+
The initial implementation renders raw JSON of the parsed data. This serves as a visual verification that the entire pipeline works: file reading, YAML parsing, SSR delivery, and client rendering. This will be replaced with proper dashboard components in Milestone 2.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Verification
|
|
159
|
+
|
|
160
|
+
- [ ] `app/lib/config.ts` exists and exports `getProgressYamlPath()`
|
|
161
|
+
- [ ] `app/services/progress-database.service.ts` exists and exports `ProgressDatabaseService`
|
|
162
|
+
- [ ] Page loads with SSR data (no loading spinner, data visible on first paint)
|
|
163
|
+
- [ ] Setting `PROGRESS_YAML_PATH` env var changes the file path
|
|
164
|
+
- [ ] Missing file shows structured FILE_NOT_FOUND error with the attempted path
|
|
165
|
+
- [ ] Malformed YAML shows PARSE_ERROR with error message
|
|
166
|
+
- [ ] Valid progress.yaml renders as formatted JSON on the page
|
|
167
|
+
- [ ] No client-side fetch waterfall (data arrives with SSR)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Expected Output
|
|
172
|
+
|
|
173
|
+
**Key Files Created**:
|
|
174
|
+
- `app/lib/config.ts`: Configuration module for resolving progress.yaml path
|
|
175
|
+
- `app/services/progress-database.service.ts`: Service class for reading and parsing progress data
|
|
176
|
+
|
|
177
|
+
**Key Files Modified**:
|
|
178
|
+
- `app/routes/index.tsx`: Updated with `beforeLoad` SSR data preloading and JSON display
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Notes
|
|
183
|
+
|
|
184
|
+
- The library-services pattern uses static methods on a class rather than module-level functions. This provides a clear namespace and makes it easy to add methods later (e.g., `getProgressDataForMilestone`)
|
|
185
|
+
- The `beforeLoad` hook runs on the server during SSR, which means `fs.readFileSync` is safe to use here
|
|
186
|
+
- The raw JSON display is temporary and will be replaced by dashboard components in Milestone 2 tasks
|
|
187
|
+
- Consider adding caching to `ProgressDatabaseService` if performance becomes a concern (not needed for local dev tool)
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
**Next Task**: [Task 4: Add Auto-Refresh via SSE](./task-4-add-auto-refresh-sse.md)
|
|
192
|
+
**Related Design Docs**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
|
|
193
|
+
**Estimated Completion Date**: TBD
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Task 4: Add Auto-Refresh via SSE
|
|
2
|
+
|
|
3
|
+
**Milestone**: [M1 - Project Scaffold & Data Pipeline](../../milestones/milestone-1-project-scaffold-data-pipeline.md)
|
|
4
|
+
**Design Reference**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
|
|
5
|
+
**Estimated Time**: 2 hours
|
|
6
|
+
**Dependencies**: Task 3
|
|
7
|
+
**Status**: Not Started
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
Implement file watching and Server-Sent Events (SSE) so the dashboard auto-refreshes when progress.yaml changes on disk. This enables a live development experience where agents updating progress.yaml see their changes reflected in the browser without manual reload.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
The progress.yaml file is updated frequently by agents during work sessions. Rather than requiring manual browser refresh, the dashboard should detect file changes and re-render automatically. The implementation uses Node.js `fs.watch` on the server to detect changes, SSE to push notifications to the browser, and TanStack Router's `router.invalidate()` to trigger data re-fetching. This is a local development tool, so the SSE approach is appropriate (no need for WebSocket complexity or production-scale polling).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
### 1. Create the file watcher module
|
|
26
|
+
|
|
27
|
+
Create `app/lib/file-watcher.ts` that manages file watching and connected SSE clients:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// app/lib/file-watcher.ts
|
|
31
|
+
import fs from "node:fs";
|
|
32
|
+
import { getProgressYamlPath } from "./config";
|
|
33
|
+
|
|
34
|
+
type ClientCallback = () => void;
|
|
35
|
+
|
|
36
|
+
let clients: Set<ClientCallback> = new Set();
|
|
37
|
+
let watcher: fs.FSWatcher | null = null;
|
|
38
|
+
|
|
39
|
+
export function createFileWatcher() {
|
|
40
|
+
if (watcher) return; // Already watching
|
|
41
|
+
|
|
42
|
+
const filePath = getProgressYamlPath();
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
watcher = fs.watch(filePath, { persistent: false }, (eventType) => {
|
|
46
|
+
if (eventType === "change") {
|
|
47
|
+
// Notify all connected clients
|
|
48
|
+
for (const client of clients) {
|
|
49
|
+
client();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
watcher.on("error", (err) => {
|
|
55
|
+
console.error("[file-watcher] Watch error:", err.message);
|
|
56
|
+
watcher?.close();
|
|
57
|
+
watcher = null;
|
|
58
|
+
});
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error("[file-watcher] Failed to start watching:", (err as Error).message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function registerClient(callback: ClientCallback): () => void {
|
|
65
|
+
clients.add(callback);
|
|
66
|
+
// Return unregister function
|
|
67
|
+
return () => {
|
|
68
|
+
clients.delete(callback);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getClientCount(): number {
|
|
73
|
+
return clients.size;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Key design decisions:
|
|
78
|
+
- Uses `fs.watch` (not `fs.watchFile`) for efficiency since this is a single known file path
|
|
79
|
+
- `persistent: false` so the watcher does not keep the Node process alive
|
|
80
|
+
- Singleton pattern: only one watcher instance regardless of how many clients connect
|
|
81
|
+
- Returns an unregister function for clean client removal
|
|
82
|
+
|
|
83
|
+
### 2. Create the SSE API route
|
|
84
|
+
|
|
85
|
+
Create `app/routes/api/watch.ts` that serves the SSE endpoint:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// app/routes/api/watch.ts
|
|
89
|
+
import { createAPIFileRoute } from "@tanstack/react-start/api";
|
|
90
|
+
import { createFileWatcher, registerClient } from "../../lib/file-watcher";
|
|
91
|
+
|
|
92
|
+
export const APIRoute = createAPIFileRoute("/api/watch")({
|
|
93
|
+
GET: async () => {
|
|
94
|
+
// Ensure watcher is running
|
|
95
|
+
createFileWatcher();
|
|
96
|
+
|
|
97
|
+
const stream = new ReadableStream({
|
|
98
|
+
start(controller) {
|
|
99
|
+
const encoder = new TextEncoder();
|
|
100
|
+
|
|
101
|
+
// Send initial connection confirmation
|
|
102
|
+
controller.enqueue(encoder.encode("data: connected\n\n"));
|
|
103
|
+
|
|
104
|
+
// Register for file change notifications
|
|
105
|
+
const unregister = registerClient(() => {
|
|
106
|
+
try {
|
|
107
|
+
controller.enqueue(encoder.encode("data: refresh\n\n"));
|
|
108
|
+
} catch {
|
|
109
|
+
// Client disconnected
|
|
110
|
+
unregister();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Handle client disconnect (stream cancellation)
|
|
115
|
+
// Note: cleanup is handled when enqueue throws
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return new Response(stream, {
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "text/event-stream",
|
|
122
|
+
"Cache-Control": "no-cache",
|
|
123
|
+
Connection: "keep-alive",
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 3. Create the useAutoRefresh hook
|
|
131
|
+
|
|
132
|
+
Create `app/lib/useAutoRefresh.ts` that connects to the SSE endpoint and triggers router invalidation:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// app/lib/useAutoRefresh.ts
|
|
136
|
+
import { useEffect } from "react";
|
|
137
|
+
import { useRouter } from "@tanstack/react-router";
|
|
138
|
+
|
|
139
|
+
export function useAutoRefresh() {
|
|
140
|
+
const router = useRouter();
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
const eventSource = new EventSource("/api/watch");
|
|
144
|
+
|
|
145
|
+
eventSource.onmessage = (event) => {
|
|
146
|
+
if (event.data === "refresh") {
|
|
147
|
+
// Invalidate all route loaders, triggering re-fetch
|
|
148
|
+
router.invalidate();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
eventSource.onerror = () => {
|
|
153
|
+
// EventSource will automatically reconnect on error
|
|
154
|
+
// (built-in browser behavior)
|
|
155
|
+
console.warn("[auto-refresh] SSE connection lost, reconnecting...");
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return () => {
|
|
159
|
+
eventSource.close();
|
|
160
|
+
};
|
|
161
|
+
}, [router]);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Key behaviors:
|
|
166
|
+
- `EventSource` has built-in reconnection on connection loss (browser standard behavior)
|
|
167
|
+
- `router.invalidate()` re-runs all `beforeLoad` hooks, which re-reads and re-parses progress.yaml
|
|
168
|
+
- Cleanup on unmount prevents memory leaks
|
|
169
|
+
|
|
170
|
+
### 4. Add useAutoRefresh to root layout
|
|
171
|
+
|
|
172
|
+
Update `app/routes/__root.tsx` to activate auto-refresh on all pages:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// app/routes/__root.tsx
|
|
176
|
+
import { createRootRoute, Outlet } from "@tanstack/react-router";
|
|
177
|
+
import { useAutoRefresh } from "../lib/useAutoRefresh";
|
|
178
|
+
|
|
179
|
+
export const Route = createRootRoute({
|
|
180
|
+
component: RootLayout,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
function RootLayout() {
|
|
184
|
+
useAutoRefresh();
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<div className="dark min-h-screen bg-gray-950 text-gray-100">
|
|
188
|
+
<Outlet />
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 5. Test the auto-refresh flow
|
|
195
|
+
|
|
196
|
+
End-to-end verification:
|
|
197
|
+
|
|
198
|
+
1. Start the dev server with `npm run dev`
|
|
199
|
+
2. Open the dashboard in a browser
|
|
200
|
+
3. Verify the SSE connection is established (check Network tab for `/api/watch` request with `text/event-stream` type)
|
|
201
|
+
4. Modify the project's `progress.yaml` file (e.g., change a task status)
|
|
202
|
+
5. Verify the browser updates automatically without manual refresh
|
|
203
|
+
6. Check that there are no errors in the browser console or server logs
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Verification
|
|
208
|
+
|
|
209
|
+
- [ ] `app/lib/file-watcher.ts` exists and exports `createFileWatcher`, `registerClient`, `getClientCount`
|
|
210
|
+
- [ ] `app/routes/api/watch.ts` exists and serves SSE responses
|
|
211
|
+
- [ ] `app/lib/useAutoRefresh.ts` exists and exports `useAutoRefresh` hook
|
|
212
|
+
- [ ] SSE endpoint responds with `Content-Type: text/event-stream`
|
|
213
|
+
- [ ] SSE endpoint sends `data: connected` on initial connection
|
|
214
|
+
- [ ] Modifying progress.yaml triggers `data: refresh` SSE event
|
|
215
|
+
- [ ] Browser re-renders with updated data after file change
|
|
216
|
+
- [ ] EventSource reconnects automatically on connection loss (verify by restarting server)
|
|
217
|
+
- [ ] No errors in browser console during normal operation
|
|
218
|
+
- [ ] Root layout includes `useAutoRefresh()` call
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Expected Output
|
|
223
|
+
|
|
224
|
+
**Key Files Created**:
|
|
225
|
+
- `app/lib/file-watcher.ts`: Node.js file watcher with client management
|
|
226
|
+
- `app/routes/api/watch.ts`: SSE API endpoint for pushing file change notifications
|
|
227
|
+
- `app/lib/useAutoRefresh.ts`: React hook that connects to SSE and triggers router invalidation
|
|
228
|
+
|
|
229
|
+
**Key Files Modified**:
|
|
230
|
+
- `app/routes/__root.tsx`: Updated to include `useAutoRefresh()` hook
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Common Issues and Solutions
|
|
235
|
+
|
|
236
|
+
### Issue 1: fs.watch fires multiple events for a single save
|
|
237
|
+
**Symptom**: Dashboard refreshes multiple times when progress.yaml is saved once
|
|
238
|
+
**Solution**: Add a debounce (e.g., 100ms) to the file watcher notification. Ignore events that fire within the debounce window of the last event.
|
|
239
|
+
|
|
240
|
+
### Issue 2: fs.watch not available or not working on certain filesystems
|
|
241
|
+
**Symptom**: File changes are not detected
|
|
242
|
+
**Solution**: Fall back to `fs.watchFile` (polling-based) if `fs.watch` fails. Log a warning about reduced performance.
|
|
243
|
+
|
|
244
|
+
### Issue 3: SSE connection shows as pending indefinitely in DevTools
|
|
245
|
+
**Symptom**: Network tab shows `/api/watch` request stuck in "pending" state
|
|
246
|
+
**Solution**: This is expected behavior for SSE connections. The connection stays open to receive events. It is not an error.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Notes
|
|
251
|
+
|
|
252
|
+
- This is a local development tool, so SSE is appropriate. For production, consider WebSocket or polling with ETags
|
|
253
|
+
- The file watcher is a singleton: multiple browser tabs share the same watcher but each has its own SSE connection
|
|
254
|
+
- `fs.watch` behavior varies by OS; test on the target platform (Linux with inotify is reliable)
|
|
255
|
+
- Consider adding a debounce to the file watcher if double-fires become an issue during testing
|
|
256
|
+
- The `useAutoRefresh` hook is placed in the root layout so it works across all routes, including future milestone and task detail pages
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
**Next Task**: [Milestone 2 Tasks](../milestone-2-dashboard-views-interaction/)
|
|
261
|
+
**Related Design Docs**: [Server API & Auto-Refresh](../../design/local.server-api-auto-refresh.md)
|
|
262
|
+
**Estimated Completion Date**: TBD
|
package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Task 10: Polish & Integration Testing
|
|
2
|
+
|
|
3
|
+
**Milestone**: [M2 - Dashboard Views & Interaction](../../milestones/milestone-2-dashboard-views-interaction.md)
|
|
4
|
+
**Design Reference**: [Visualizer Requirements](../../design/local.visualizer-requirements.md)
|
|
5
|
+
**Estimated Time**: 2 hours
|
|
6
|
+
**Dependencies**: Task 9
|
|
7
|
+
**Status**: Not Started
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
Final polish pass — ensure all views work together, error states are handled, empty states are graceful, and the full data flow works end-to-end. This task validates that all M2 features are functional and ready for use.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
Tasks 5–9 build individual features (layout, overview, table, tree, search/filter) in isolation. This task validates they work together as a cohesive application. It focuses on integration testing, edge cases, error handling, and visual polish. Real-world progress.yaml files can be large (1800+ lines) and may contain unexpected data shapes, so testing with real data is critical. This is the final task in Milestone 2 and should result in a fully functional dashboard.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
### 1. Test full data flow end-to-end
|
|
26
|
+
|
|
27
|
+
Verify the complete data pipeline works across all views:
|
|
28
|
+
|
|
29
|
+
- `progress.yaml` file is read from configured path
|
|
30
|
+
- YAML is parsed into typed ProgressData
|
|
31
|
+
- Data is served via SSR (server function → route loader)
|
|
32
|
+
- Overview page renders project metadata, milestones, next steps, blockers
|
|
33
|
+
- Milestones page renders table view with all milestones and tasks
|
|
34
|
+
- Milestones page renders tree view when toggled
|
|
35
|
+
- Search page finds and displays matching results
|
|
36
|
+
- Auto-refresh updates all views when progress.yaml changes
|
|
37
|
+
|
|
38
|
+
### 2. Verify error states
|
|
39
|
+
|
|
40
|
+
Test error handling for common failure scenarios:
|
|
41
|
+
|
|
42
|
+
- **File not found**: When `PROGRESS_YAML_PATH` points to a non-existent file, display a configuration prompt explaining how to set the path
|
|
43
|
+
- **Parse error**: When progress.yaml contains invalid YAML, display a helpful error message showing the parse error details
|
|
44
|
+
- **Missing fields**: When progress.yaml is valid but missing expected fields, degrade gracefully (show available data, skip missing sections)
|
|
45
|
+
|
|
46
|
+
### 3. Verify empty states
|
|
47
|
+
|
|
48
|
+
Test empty data scenarios:
|
|
49
|
+
|
|
50
|
+
- **No milestones**: Overview and milestones pages show "No milestones found" message
|
|
51
|
+
- **No tasks in milestone**: Expanded milestone row shows "No tasks" message
|
|
52
|
+
- **No search results**: Search page shows "No results found for '{query}'" message
|
|
53
|
+
- **No blockers**: BlockersDisplay renders nothing (no empty card)
|
|
54
|
+
- **No next steps**: NextSteps renders nothing (no empty card)
|
|
55
|
+
|
|
56
|
+
### 4. Verify auto-refresh across all views
|
|
57
|
+
|
|
58
|
+
- File watcher detects changes to progress.yaml
|
|
59
|
+
- All currently visible views update with new data
|
|
60
|
+
- Expanded state in table/tree views is preserved after refresh
|
|
61
|
+
- Search/filter state is preserved after refresh
|
|
62
|
+
- No flicker or layout shift during refresh
|
|
63
|
+
|
|
64
|
+
### 5. Test with real progress.yaml data
|
|
65
|
+
|
|
66
|
+
Load a real-world progress.yaml file (e.g., from ACP core project, 1800+ lines):
|
|
67
|
+
|
|
68
|
+
- Verify all milestones and tasks render without errors
|
|
69
|
+
- Verify performance is acceptable (no lag when scrolling, sorting, searching)
|
|
70
|
+
- Verify ExtraFieldsBadge shows correct counts for tasks with custom fields
|
|
71
|
+
- Verify long text (names, notes) truncates gracefully
|
|
72
|
+
- Verify large numbers of tasks don't break layout
|
|
73
|
+
|
|
74
|
+
### 6. Fix styling inconsistencies
|
|
75
|
+
|
|
76
|
+
Review all views for visual polish:
|
|
77
|
+
|
|
78
|
+
- Consistent spacing between sections (space-y-6)
|
|
79
|
+
- Consistent card styling (bg-gray-900/50, border-gray-800, rounded-xl)
|
|
80
|
+
- Consistent text hierarchy (headings, subheadings, body, captions)
|
|
81
|
+
- Hover states on interactive elements (nav links, table rows, buttons)
|
|
82
|
+
- Focus states for keyboard navigation (search input, filter buttons)
|
|
83
|
+
- No orphaned borders, extra padding, or alignment issues
|
|
84
|
+
|
|
85
|
+
### 7. Verify route navigation
|
|
86
|
+
|
|
87
|
+
Test all navigation paths:
|
|
88
|
+
|
|
89
|
+
- Sidebar links navigate to correct pages
|
|
90
|
+
- Browser back/forward navigation works
|
|
91
|
+
- Direct URL access works (e.g., navigating directly to `/milestones`)
|
|
92
|
+
- Active nav item in sidebar matches current route
|
|
93
|
+
- No 404 or blank pages for any defined route
|
|
94
|
+
|
|
95
|
+
### 8. Test edge cases
|
|
96
|
+
|
|
97
|
+
Verify the app handles unusual data shapes:
|
|
98
|
+
|
|
99
|
+
- Single milestone with no tasks
|
|
100
|
+
- Single task with no notes
|
|
101
|
+
- Milestone with many tasks (20+)
|
|
102
|
+
- Task with many extra fields (10+)
|
|
103
|
+
- Very long milestone/task names (100+ characters)
|
|
104
|
+
- Special characters in names and notes (quotes, angle brackets, unicode)
|
|
105
|
+
- Status values beyond the standard set (should render as gray/unknown)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Verification
|
|
110
|
+
|
|
111
|
+
- [ ] All P0 features from requirements are functional (overview, table, tree, search, filter)
|
|
112
|
+
- [ ] No console errors or warnings in browser developer tools
|
|
113
|
+
- [ ] All views render correctly with real progress.yaml data (1800+ lines)
|
|
114
|
+
- [ ] Error state: file-not-found shows configuration prompt
|
|
115
|
+
- [ ] Error state: parse error shows helpful message with details
|
|
116
|
+
- [ ] Empty state: no milestones shows appropriate message
|
|
117
|
+
- [ ] Empty state: no search results shows appropriate message
|
|
118
|
+
- [ ] Auto-refresh works across overview, table, tree, and search views
|
|
119
|
+
- [ ] Search finds milestones and tasks by name with fuzzy matching
|
|
120
|
+
- [ ] Status filter narrows results correctly
|
|
121
|
+
- [ ] All sidebar navigation links work correctly
|
|
122
|
+
- [ ] Browser back/forward navigation works
|
|
123
|
+
- [ ] No styling inconsistencies or visual bugs
|
|
124
|
+
- [ ] Edge cases handled: long text, special characters, missing fields, many items
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Expected Output
|
|
129
|
+
|
|
130
|
+
**Key Files Modified**:
|
|
131
|
+
- Bug fixes and polish across existing files created in Tasks 5–9
|
|
132
|
+
- No major new files expected — this is a testing and polish task
|
|
133
|
+
|
|
134
|
+
**Potential files touched**:
|
|
135
|
+
- `app/routes/__root.tsx` — error boundary, layout adjustments
|
|
136
|
+
- `app/routes/index.tsx` — empty state handling
|
|
137
|
+
- `app/routes/milestones.tsx` — error/empty states
|
|
138
|
+
- `app/routes/search.tsx` — empty state messaging
|
|
139
|
+
- `app/components/*.tsx` — styling fixes, edge case handling
|
|
140
|
+
- `app/lib/*.ts` — search/filter edge case fixes
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Notes
|
|
145
|
+
|
|
146
|
+
- This is a testing and polish task, not a feature-building task — the goal is quality, not new functionality
|
|
147
|
+
- Keep a list of issues found and fixed for the commit message
|
|
148
|
+
- If any P0 feature is broken or missing, fix it before marking this task complete
|
|
149
|
+
- Performance testing with large files is important — if search or rendering is slow, optimize before closing
|
|
150
|
+
- This task marks the end of Milestone 2; after completion, the dashboard should be fully usable for viewing progress.yaml data
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
**Next Task**: Milestone 3 tasks (TBD)
|
|
155
|
+
**Related Design Docs**: [Visualizer Requirements](../../design/local.visualizer-requirements.md)
|
|
156
|
+
**Estimated Completion Date**: TBD
|