@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
package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Task 9: Implement Search & Filtering
|
|
2
|
+
|
|
3
|
+
**Milestone**: [M2 - Dashboard Views & Interaction](../../milestones/milestone-2-dashboard-views-interaction.md)
|
|
4
|
+
**Design Reference**: [Search & Filtering](../../design/local.search-filtering.md)
|
|
5
|
+
**Estimated Time**: 3 hours
|
|
6
|
+
**Dependencies**: Task 7, Task 8
|
|
7
|
+
**Status**: Not Started
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
Add fuse.js fuzzy search and status-based filtering across milestones and tasks, with shared state via GlobalSearchContext. This enables users to quickly find specific milestones or tasks by name and narrow results by status.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
As the progress.yaml data grows (real-world files can be 1800+ lines), users need efficient ways to find specific items. This task adds two complementary features: fuzzy search (powered by fuse.js) that matches against milestone and task names/notes, and status filters that narrow the view to items matching a selected status. Both features share state through a global context so the search query persists across navigation. The search and filter logic is encapsulated in reusable hooks that the table, tree, and search page views can all consume.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
### 1. Create `app/lib/search.ts`
|
|
26
|
+
|
|
27
|
+
Build the search index and query functions using fuse.js:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import Fuse from "fuse.js";
|
|
31
|
+
|
|
32
|
+
const fuseOptions: Fuse.IFuseOptions<SearchableItem> = {
|
|
33
|
+
threshold: 0.4,
|
|
34
|
+
ignoreLocation: true,
|
|
35
|
+
keys: [
|
|
36
|
+
{ name: "name", weight: 2 },
|
|
37
|
+
{ name: "notes", weight: 1 },
|
|
38
|
+
{ name: "extra", weight: 0.5 },
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function buildSearchIndex(data: ProgressData): Fuse<SearchableItem> {
|
|
43
|
+
// Flatten milestones and tasks into searchable items
|
|
44
|
+
// Each item includes: name, notes, extra fields, type (milestone/task), parentMilestone
|
|
45
|
+
// ...
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- Flatten milestones and tasks into a unified searchable array
|
|
50
|
+
- Each searchable item carries metadata about its type and parent milestone
|
|
51
|
+
- Threshold of 0.4 allows fuzzy matching (typos still return results)
|
|
52
|
+
- `ignoreLocation: true` matches anywhere in the string, not just near the start
|
|
53
|
+
|
|
54
|
+
### 2. Create `app/contexts/GlobalSearchContext.tsx`
|
|
55
|
+
|
|
56
|
+
Build a global search state context using useRef-based pub/sub:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface GlobalSearchState {
|
|
60
|
+
query: string;
|
|
61
|
+
statusFilter: string | null; // null = all, or "completed" | "in_progress" | "not_started"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function GlobalSearchProvider({ children }: { children: React.ReactNode }) {
|
|
65
|
+
// useRef-based store for performance (avoids re-rendering entire tree)
|
|
66
|
+
// Subscribers register with useGlobalSearch(key) hook
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function useGlobalSearch(key: keyof GlobalSearchState) {
|
|
70
|
+
// Returns [value, setValue] for the specified key
|
|
71
|
+
// Only re-renders when the subscribed key changes
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- Uses useRef internally to store state, with a subscriber pattern to notify consumers
|
|
76
|
+
- Avoids re-rendering the entire component tree when search state changes
|
|
77
|
+
- Provides both the query string and status filter as separate subscribable keys
|
|
78
|
+
|
|
79
|
+
### 3. Create `app/lib/useFilteredData.ts`
|
|
80
|
+
|
|
81
|
+
Build a hook that applies both search and status filter to progress data:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
export function useFilteredData(data: ProgressData): ProgressData {
|
|
85
|
+
const [query] = useGlobalSearch("query");
|
|
86
|
+
const [statusFilter] = useGlobalSearch("statusFilter");
|
|
87
|
+
|
|
88
|
+
return useMemo(() => {
|
|
89
|
+
let filtered = data;
|
|
90
|
+
|
|
91
|
+
// Apply status filter
|
|
92
|
+
if (statusFilter) {
|
|
93
|
+
filtered = filterByStatus(filtered, statusFilter);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Apply search query
|
|
97
|
+
if (query) {
|
|
98
|
+
filtered = filterBySearch(filtered, query);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return filtered;
|
|
102
|
+
}, [data, query, statusFilter]);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- Returns a filtered copy of ProgressData (does not mutate original)
|
|
107
|
+
- Status filter narrows milestones and tasks to matching status
|
|
108
|
+
- Search filter uses the fuse.js index to match by name/notes
|
|
109
|
+
- Filters compose: status filter + search query both apply simultaneously
|
|
110
|
+
|
|
111
|
+
### 4. Create `app/components/FilterBar.tsx`
|
|
112
|
+
|
|
113
|
+
Build the pill-style status filter toggle:
|
|
114
|
+
|
|
115
|
+
- Renders toggle buttons: All, In Progress, Not Started, Completed
|
|
116
|
+
- Active filter pill is highlighted (`bg-blue-500/20 text-blue-400 border-blue-500/50`)
|
|
117
|
+
- Inactive pills are subtle (`bg-gray-800 text-gray-400 border-gray-700`)
|
|
118
|
+
- Clicking a pill updates the statusFilter in GlobalSearchContext
|
|
119
|
+
- Clicking the active pill deselects it (returns to "All")
|
|
120
|
+
- Styled as inline-flex with gap between pills
|
|
121
|
+
|
|
122
|
+
### 5. Create `app/components/SearchBar.tsx`
|
|
123
|
+
|
|
124
|
+
Build the search input for the sidebar:
|
|
125
|
+
|
|
126
|
+
- Search icon (Search from lucide-react) on the left
|
|
127
|
+
- Text input with placeholder "Search milestones & tasks..."
|
|
128
|
+
- Connected to GlobalSearchContext query state
|
|
129
|
+
- Debounced input (300ms) to avoid excessive re-filtering
|
|
130
|
+
- Styled to match sidebar: `bg-gray-900 border border-gray-800 rounded-lg`
|
|
131
|
+
- Pressing Escape clears the search
|
|
132
|
+
- Pressing Enter navigates to `/search` for full results page
|
|
133
|
+
|
|
134
|
+
### 6. Add GlobalSearchProvider to root layout
|
|
135
|
+
|
|
136
|
+
Update `app/routes/__root.tsx` to wrap the app with the GlobalSearchProvider:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
function RootLayout() {
|
|
140
|
+
return (
|
|
141
|
+
<GlobalSearchProvider>
|
|
142
|
+
<div className="dark min-h-screen bg-gray-950 text-gray-100 flex">
|
|
143
|
+
<Sidebar />
|
|
144
|
+
{/* ... */}
|
|
145
|
+
</div>
|
|
146
|
+
</GlobalSearchProvider>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 7. Update milestones.tsx to use useFilteredData
|
|
152
|
+
|
|
153
|
+
Wire the filter and search hooks into the milestones page:
|
|
154
|
+
|
|
155
|
+
- Add FilterBar above the table/tree views
|
|
156
|
+
- Use `useFilteredData` to get filtered milestones
|
|
157
|
+
- Pass filtered data to MilestoneTable and MilestoneTree
|
|
158
|
+
- When filters result in empty data, show "No milestones match your filters" message
|
|
159
|
+
|
|
160
|
+
### 8. Update `app/routes/search.tsx`
|
|
161
|
+
|
|
162
|
+
Build the dedicated search results page:
|
|
163
|
+
|
|
164
|
+
- Shows search results grouped by milestone
|
|
165
|
+
- Each group shows the milestone name as a header, with matching tasks listed below
|
|
166
|
+
- Highlights which part of the result matched (milestone name vs task name/notes)
|
|
167
|
+
- Shows total result count
|
|
168
|
+
- Empty state: "No results found for '{query}'" with suggestion to try different terms
|
|
169
|
+
- Links to navigate to the milestone/task in the main views
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Verification
|
|
174
|
+
|
|
175
|
+
- [ ] Search finds milestones by name (exact and partial match)
|
|
176
|
+
- [ ] Search finds tasks by name (exact and partial match)
|
|
177
|
+
- [ ] Fuzzy matching works (typos like "milstone" still match "milestone")
|
|
178
|
+
- [ ] Status filter narrows results to only matching status items
|
|
179
|
+
- [ ] Combined search + filter works (e.g., search "auth" + filter "in_progress")
|
|
180
|
+
- [ ] Filter state persists when switching between table and tree views
|
|
181
|
+
- [ ] Search query persists when navigating between routes
|
|
182
|
+
- [ ] SearchBar in sidebar is debounced (no lag while typing)
|
|
183
|
+
- [ ] Pressing Escape in SearchBar clears the query
|
|
184
|
+
- [ ] Search results page shows grouped results by milestone
|
|
185
|
+
- [ ] Empty state shows appropriate message when no results found
|
|
186
|
+
- [ ] GlobalSearchProvider wraps the entire app in root layout
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Expected Output
|
|
191
|
+
|
|
192
|
+
**File Structure**:
|
|
193
|
+
```
|
|
194
|
+
app/
|
|
195
|
+
├── components/
|
|
196
|
+
│ ├── FilterBar.tsx (new)
|
|
197
|
+
│ └── SearchBar.tsx (new)
|
|
198
|
+
├── contexts/
|
|
199
|
+
│ └── GlobalSearchContext.tsx (new)
|
|
200
|
+
├── lib/
|
|
201
|
+
│ ├── search.ts (new)
|
|
202
|
+
│ └── useFilteredData.ts (new)
|
|
203
|
+
├── routes/
|
|
204
|
+
│ ├── __root.tsx (updated)
|
|
205
|
+
│ ├── milestones.tsx (updated)
|
|
206
|
+
│ └── search.tsx (updated)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Key Files Created/Modified**:
|
|
210
|
+
- `app/lib/search.ts`: Fuse.js search index builder and query functions
|
|
211
|
+
- `app/contexts/GlobalSearchContext.tsx`: Global search state with pub/sub pattern
|
|
212
|
+
- `app/lib/useFilteredData.ts`: Hook composing status filter and search query
|
|
213
|
+
- `app/components/FilterBar.tsx`: Pill-style status toggle buttons
|
|
214
|
+
- `app/components/SearchBar.tsx`: Search input for sidebar with debounce
|
|
215
|
+
- `app/routes/__root.tsx`: Updated to include GlobalSearchProvider
|
|
216
|
+
- `app/routes/milestones.tsx`: Updated to use useFilteredData and FilterBar
|
|
217
|
+
- `app/routes/search.tsx`: Updated with grouped search results page
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Notes
|
|
222
|
+
|
|
223
|
+
- fuse.js should already be installed from Task 1; verify with `npm ls fuse.js`
|
|
224
|
+
- The 300ms debounce on SearchBar prevents excessive re-filtering on every keystroke
|
|
225
|
+
- GlobalSearchContext uses useRef-based pub/sub (not useState in context) to avoid re-rendering the entire tree when search state changes — only subscribed components re-render
|
|
226
|
+
- The search index is rebuilt when progress data changes (e.g., on auto-refresh)
|
|
227
|
+
- Status filter values must match the status strings in progress.yaml exactly
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
**Next Task**: [Task 10: Polish & Integration Testing](./task-10-polish-integration-testing.md)
|
|
232
|
+
**Related Design Docs**: [Search & Filtering](../../design/local.search-filtering.md)
|
|
233
|
+
**Estimated Completion Date**: TBD
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Task {N}: {Descriptive Task Name}
|
|
2
|
+
|
|
3
|
+
**Milestone**: [M{N} - Milestone Name](../milestones/milestone-{N}-{name}.md)
|
|
4
|
+
**Design Reference**: [{Design Name}](../design/{namespace}.{design-name}.md) | None
|
|
5
|
+
**Estimated Time**: [e.g., "2 hours", "4 hours", "1 day"]
|
|
6
|
+
**Dependencies**: [List prerequisite tasks, or "None"]
|
|
7
|
+
**Status**: Not Started | In Progress | Completed
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
[Clearly state what this task accomplishes. Be specific and focused on a single, achievable goal.]
|
|
14
|
+
|
|
15
|
+
**Example**: "Create the basic project structure with all necessary configuration files and directory organization for a TypeScript-based MCP server."
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Context
|
|
20
|
+
|
|
21
|
+
[Provide background information that helps understand why this task is necessary and how it fits into the larger milestone.]
|
|
22
|
+
|
|
23
|
+
**Example**: "This task establishes the foundation for the project. Without proper structure and configuration, subsequent development tasks cannot proceed. The structure follows industry best practices for TypeScript projects and MCP server organization."
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Steps
|
|
28
|
+
|
|
29
|
+
[Provide a detailed, sequential list of actions to complete this task. Each step should be concrete and actionable.]
|
|
30
|
+
|
|
31
|
+
### 1. [Step Category or Action]
|
|
32
|
+
[Detailed description of what to do]
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Command examples if applicable
|
|
36
|
+
command --with-flags
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// Code examples if applicable
|
|
41
|
+
const example = "code";
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. [Next Step]
|
|
45
|
+
[Detailed description]
|
|
46
|
+
|
|
47
|
+
### 3. [Next Step]
|
|
48
|
+
[Detailed description]
|
|
49
|
+
|
|
50
|
+
**Example**:
|
|
51
|
+
|
|
52
|
+
### 1. Create Project Directory
|
|
53
|
+
Create the root directory for the project and navigate into it:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
mkdir -p my-project
|
|
57
|
+
cd my-project
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Initialize npm Project
|
|
61
|
+
Initialize a new npm project with default settings:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm init -y
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Update package.json
|
|
68
|
+
Edit the generated package.json to include proper metadata and scripts:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"name": "my-project",
|
|
73
|
+
"version": "0.1.0",
|
|
74
|
+
"description": "Description of what this project does",
|
|
75
|
+
"main": "dist/index.js",
|
|
76
|
+
"type": "module",
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build": "node esbuild.build.js",
|
|
79
|
+
"dev": "tsx watch src/index.ts",
|
|
80
|
+
"start": "node dist/index.js",
|
|
81
|
+
"test": "vitest",
|
|
82
|
+
"typecheck": "tsc --noEmit"
|
|
83
|
+
},
|
|
84
|
+
"keywords": ["mcp", "relevant", "keywords"],
|
|
85
|
+
"author": "Your Name",
|
|
86
|
+
"license": "MIT"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 4. Create Directory Structure
|
|
91
|
+
Create all necessary directories:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
mkdir -p src/{types,utils,tools}
|
|
95
|
+
mkdir -p tests/{unit,integration}
|
|
96
|
+
mkdir -p agent/{design,milestones,patterns,tasks}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 5. Create Configuration Files
|
|
100
|
+
Create TypeScript configuration, build scripts, and other config files.
|
|
101
|
+
|
|
102
|
+
**tsconfig.json**:
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"compilerOptions": {
|
|
106
|
+
"target": "ES2022",
|
|
107
|
+
"module": "Node16",
|
|
108
|
+
"moduleResolution": "Node16",
|
|
109
|
+
"lib": ["ES2022"],
|
|
110
|
+
"outDir": "./dist",
|
|
111
|
+
"rootDir": "./src",
|
|
112
|
+
"strict": true,
|
|
113
|
+
"esModuleInterop": true,
|
|
114
|
+
"skipLibCheck": true,
|
|
115
|
+
"forceConsistentCasingInFileNames": true,
|
|
116
|
+
"resolveJsonModule": true,
|
|
117
|
+
"declaration": true,
|
|
118
|
+
"declarationMap": true,
|
|
119
|
+
"sourceMap": true
|
|
120
|
+
},
|
|
121
|
+
"include": ["src/**/*"],
|
|
122
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
[Continue with other config files...]
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Verification
|
|
131
|
+
|
|
132
|
+
[Provide a checklist of items to verify the task is complete. Each item should be objectively verifiable.]
|
|
133
|
+
|
|
134
|
+
- [ ] Verification item 1: [Specific condition to check]
|
|
135
|
+
- [ ] Verification item 2: [Specific condition to check]
|
|
136
|
+
- [ ] Verification item 3: [Specific condition to check]
|
|
137
|
+
- [ ] Verification item 4: [Specific condition to check]
|
|
138
|
+
- [ ] Verification item 5: [Specific condition to check]
|
|
139
|
+
|
|
140
|
+
**Example**:
|
|
141
|
+
- [ ] Project directory created and contains expected subdirectories
|
|
142
|
+
- [ ] package.json exists and contains correct metadata
|
|
143
|
+
- [ ] tsconfig.json exists and is valid JSON
|
|
144
|
+
- [ ] All configuration files created (.gitignore, .env.example, etc.)
|
|
145
|
+
- [ ] Directory structure matches specification
|
|
146
|
+
- [ ] No syntax errors in configuration files
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Expected Output
|
|
151
|
+
|
|
152
|
+
[Describe what the project should look like after this task is complete.]
|
|
153
|
+
|
|
154
|
+
**File Structure**:
|
|
155
|
+
```
|
|
156
|
+
project-root/
|
|
157
|
+
├── file1
|
|
158
|
+
├── file2
|
|
159
|
+
└── directory/
|
|
160
|
+
└── file3
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Key Files Created**:
|
|
164
|
+
- `file1`: [Purpose]
|
|
165
|
+
- `file2`: [Purpose]
|
|
166
|
+
- `directory/file3`: [Purpose]
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Key Design Decisions (Optional)
|
|
171
|
+
|
|
172
|
+
<!-- This section is populated by @acp.clarification-capture when
|
|
173
|
+
create commands are invoked with --from-clar, --from-chat, or
|
|
174
|
+
--from-context. It can also be manually authored.
|
|
175
|
+
Omit this section entirely if no decisions to capture.
|
|
176
|
+
|
|
177
|
+
Group decisions by agent-inferred category using tables:
|
|
178
|
+
|
|
179
|
+
### {Category}
|
|
180
|
+
|
|
181
|
+
| Decision | Choice | Rationale |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| {decision} | {choice} | {rationale} |
|
|
184
|
+
-->
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Common Issues and Solutions
|
|
189
|
+
|
|
190
|
+
[Document potential problems and how to resolve them:]
|
|
191
|
+
|
|
192
|
+
### Issue 1: [Problem description]
|
|
193
|
+
**Symptom**: [What the user will see]
|
|
194
|
+
**Solution**: [How to fix it]
|
|
195
|
+
|
|
196
|
+
### Issue 2: [Problem description]
|
|
197
|
+
**Symptom**: [What the user will see]
|
|
198
|
+
**Solution**: [How to fix it]
|
|
199
|
+
|
|
200
|
+
**Example**:
|
|
201
|
+
|
|
202
|
+
### Issue 1: npm init fails
|
|
203
|
+
**Symptom**: Error message about permissions or missing npm
|
|
204
|
+
**Solution**: Ensure Node.js and npm are installed correctly. Run `node --version` and `npm --version` to verify.
|
|
205
|
+
|
|
206
|
+
### Issue 2: TypeScript configuration errors
|
|
207
|
+
**Symptom**: tsc complains about invalid configuration
|
|
208
|
+
**Solution**: Validate JSON syntax in tsconfig.json. Ensure all required fields are present.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Resources
|
|
213
|
+
|
|
214
|
+
[Link to relevant documentation, examples, or references:]
|
|
215
|
+
|
|
216
|
+
- [Resource 1 Name](URL): Description
|
|
217
|
+
- [Resource 2 Name](URL): Description
|
|
218
|
+
- [Resource 3 Name](URL): Description
|
|
219
|
+
|
|
220
|
+
**Example**:
|
|
221
|
+
- [TypeScript Handbook](https://www.typescriptlang.org/docs/): Official TypeScript documentation
|
|
222
|
+
- [esbuild Documentation](https://esbuild.github.io/): Build tool documentation
|
|
223
|
+
- [MCP SDK Documentation](https://github.com/modelcontextprotocol/sdk): MCP protocol reference
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Notes
|
|
228
|
+
|
|
229
|
+
[Any additional context, warnings, or considerations:]
|
|
230
|
+
|
|
231
|
+
- Note 1: [Important information]
|
|
232
|
+
- Note 2: [Important information]
|
|
233
|
+
- Note 3: [Important information]
|
|
234
|
+
|
|
235
|
+
**Example**:
|
|
236
|
+
- This task creates the foundation for all subsequent work
|
|
237
|
+
- Configuration files may need adjustment based on specific project requirements
|
|
238
|
+
- Keep .env files out of version control (ensure .gitignore is correct)
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
**Next Task**: [Link to next task: task-{N+1}-{name}.md]
|
|
243
|
+
**Related Design Docs**: [Links to relevant design documents]
|
|
244
|
+
**Estimated Completion Date**: [YYYY-MM-DD or "TBD"]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { resolve, dirname } from 'path'
|
|
4
|
+
import { existsSync } from 'fs'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
import { spawn } from 'child_process'
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
const packageRoot = resolve(__dirname, '..')
|
|
10
|
+
|
|
11
|
+
// Parse args
|
|
12
|
+
const args = process.argv.slice(2)
|
|
13
|
+
let progressPath = null
|
|
14
|
+
let port = '3400'
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
if (args[i] === '--port' || args[i] === '-p') {
|
|
18
|
+
port = args[++i]
|
|
19
|
+
} else if (args[i] === '--help' || args[i] === '-h') {
|
|
20
|
+
console.log(`
|
|
21
|
+
ACP Progress Visualizer
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
npx @prmichaelsen/acp-visualizer [options] [path]
|
|
25
|
+
|
|
26
|
+
Arguments:
|
|
27
|
+
path Path to progress.yaml (default: ./agent/progress.yaml)
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
-p, --port <port> Port to run on (default: 3400)
|
|
31
|
+
-h, --help Show this help message
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
npx @prmichaelsen/acp-visualizer
|
|
35
|
+
npx @prmichaelsen/acp-visualizer ./agent/progress.yaml
|
|
36
|
+
npx @prmichaelsen/acp-visualizer --port 4000
|
|
37
|
+
npx @prmichaelsen/acp-visualizer /path/to/other/progress.yaml
|
|
38
|
+
`)
|
|
39
|
+
process.exit(0)
|
|
40
|
+
} else if (!args[i].startsWith('-')) {
|
|
41
|
+
progressPath = args[i]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Resolve progress.yaml path
|
|
46
|
+
if (!progressPath) {
|
|
47
|
+
progressPath = resolve(process.cwd(), 'agent/progress.yaml')
|
|
48
|
+
} else {
|
|
49
|
+
progressPath = resolve(process.cwd(), progressPath)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!existsSync(progressPath)) {
|
|
53
|
+
console.error(`\n Error: progress.yaml not found at: ${progressPath}\n`)
|
|
54
|
+
console.error(` Make sure you're in an ACP project directory, or pass the path explicitly:\n`)
|
|
55
|
+
console.error(` npx @prmichaelsen/acp-visualizer /path/to/agent/progress.yaml\n`)
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(`\n ACP Progress Visualizer`)
|
|
60
|
+
console.log(` Loading: ${progressPath}`)
|
|
61
|
+
console.log(` Port: ${port}\n`)
|
|
62
|
+
|
|
63
|
+
// Start vite dev server from the package directory
|
|
64
|
+
const child = spawn('npx', ['vite', 'dev', '--port', port, '--host'], {
|
|
65
|
+
cwd: packageRoot,
|
|
66
|
+
stdio: 'inherit',
|
|
67
|
+
env: {
|
|
68
|
+
...process.env,
|
|
69
|
+
PROGRESS_YAML_PATH: progressPath,
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
child.on('error', (err) => {
|
|
74
|
+
console.error('Failed to start dev server:', err.message)
|
|
75
|
+
process.exit(1)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
child.on('exit', (code) => {
|
|
79
|
+
process.exit(code ?? 0)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Forward signals for clean shutdown
|
|
83
|
+
process.on('SIGINT', () => child.kill('SIGINT'))
|
|
84
|
+
process.on('SIGTERM', () => child.kill('SIGTERM'))
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prmichaelsen/acp-visualizer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Browser-based dashboard for visualizing ACP progress.yaml data",
|
|
6
|
+
"bin": {
|
|
7
|
+
"acp-visualizer": "./bin/visualize.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"agent/",
|
|
13
|
+
"vite.config.ts",
|
|
14
|
+
"tsconfig.json",
|
|
15
|
+
"vitest.config.ts"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "vite dev --port 3400 --host",
|
|
19
|
+
"build": "vite build",
|
|
20
|
+
"serve": "vite preview",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"test:run": "vitest run",
|
|
23
|
+
"test:coverage": "vitest run --coverage"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@tailwindcss/vite": "^4.0.6",
|
|
27
|
+
"@tanstack/react-router": "^1.132.0",
|
|
28
|
+
"@tanstack/react-start": "^1.132.0",
|
|
29
|
+
"@tanstack/react-table": "^8.21.3",
|
|
30
|
+
"@tanstack/router-plugin": "^1.132.0",
|
|
31
|
+
"fuse.js": "^7.1.0",
|
|
32
|
+
"js-yaml": "^4.1.0",
|
|
33
|
+
"lucide-react": "^0.544.0",
|
|
34
|
+
"react": "^19.0.0",
|
|
35
|
+
"react-dom": "^19.0.0",
|
|
36
|
+
"tailwindcss": "^4.0.6",
|
|
37
|
+
"vite-tsconfig-paths": "^5.1.4"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/js-yaml": "^4.0.9",
|
|
41
|
+
"@types/react": "^19.0.8",
|
|
42
|
+
"@types/react-dom": "^19.0.3",
|
|
43
|
+
"@vitejs/plugin-react": "^5.0.4",
|
|
44
|
+
"typescript": "^5.7.2",
|
|
45
|
+
"vite": "^7.1.7",
|
|
46
|
+
"vitest": "^4.0.18"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExtraFields } from '@/lib/types'
|
|
2
|
+
|
|
3
|
+
export function ExtraFieldsBadge({ fields }: { fields: ExtraFields }) {
|
|
4
|
+
const count = Object.keys(fields).length
|
|
5
|
+
if (count === 0) return null
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<span
|
|
9
|
+
className="text-xs text-gray-600 bg-gray-800 px-1.5 py-0.5 rounded cursor-help"
|
|
10
|
+
title={JSON.stringify(fields, null, 2)}
|
|
11
|
+
>
|
|
12
|
+
+{count}
|
|
13
|
+
</span>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Status } from '@/lib/types'
|
|
2
|
+
|
|
3
|
+
const statusOptions: Array<{ value: Status | 'all'; label: string }> = [
|
|
4
|
+
{ value: 'all', label: 'All' },
|
|
5
|
+
{ value: 'in_progress', label: 'In Progress' },
|
|
6
|
+
{ value: 'not_started', label: 'Not Started' },
|
|
7
|
+
{ value: 'completed', label: 'Completed' },
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
interface FilterBarProps {
|
|
11
|
+
status: Status | 'all'
|
|
12
|
+
onStatusChange: (status: Status | 'all') => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function FilterBar({ status, onStatusChange }: FilterBarProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="flex gap-1 p-1 bg-gray-800/50 rounded-lg">
|
|
18
|
+
{statusOptions.map((opt) => (
|
|
19
|
+
<button
|
|
20
|
+
key={opt.value}
|
|
21
|
+
onClick={() => onStatusChange(opt.value)}
|
|
22
|
+
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
|
23
|
+
status === opt.value
|
|
24
|
+
? 'bg-gray-700 text-gray-100 shadow-sm'
|
|
25
|
+
: 'text-gray-500 hover:text-gray-300'
|
|
26
|
+
}`}
|
|
27
|
+
>
|
|
28
|
+
{opt.label}
|
|
29
|
+
</button>
|
|
30
|
+
))}
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { StatusBadge } from './StatusBadge'
|
|
2
|
+
import { ProgressBar } from './ProgressBar'
|
|
3
|
+
import type { ProgressData } from '@/lib/types'
|
|
4
|
+
|
|
5
|
+
interface HeaderProps {
|
|
6
|
+
data: ProgressData | null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Header({ data }: HeaderProps) {
|
|
10
|
+
if (!data) return null
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<header className="h-14 border-b border-gray-800 flex items-center px-6 gap-4 shrink-0">
|
|
14
|
+
<h1 className="text-sm font-medium text-gray-200">{data.project.name}</h1>
|
|
15
|
+
<span className="text-xs text-gray-500 font-mono">v{data.project.version}</span>
|
|
16
|
+
<StatusBadge status={data.project.status} />
|
|
17
|
+
<div className="ml-auto flex items-center gap-3 w-48">
|
|
18
|
+
<ProgressBar value={data.progress.overall} size="sm" />
|
|
19
|
+
<span className="text-xs text-gray-400 font-mono">{data.progress.overall}%</span>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
)
|
|
23
|
+
}
|