@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.
Files changed (180) hide show
  1. package/README.md +68 -0
  2. package/agent/commands/acp.clarification-address.md +417 -0
  3. package/agent/commands/acp.clarification-capture.md +386 -0
  4. package/agent/commands/acp.clarification-create.md +437 -0
  5. package/agent/commands/acp.clarifications-research.md +326 -0
  6. package/agent/commands/acp.command-create.md +432 -0
  7. package/agent/commands/acp.design-create.md +286 -0
  8. package/agent/commands/acp.design-reference.md +355 -0
  9. package/agent/commands/acp.handoff.md +270 -0
  10. package/agent/commands/acp.index.md +423 -0
  11. package/agent/commands/acp.init.md +546 -0
  12. package/agent/commands/acp.package-create.md +895 -0
  13. package/agent/commands/acp.package-info.md +212 -0
  14. package/agent/commands/acp.package-install.md +539 -0
  15. package/agent/commands/acp.package-list.md +280 -0
  16. package/agent/commands/acp.package-publish.md +541 -0
  17. package/agent/commands/acp.package-remove.md +293 -0
  18. package/agent/commands/acp.package-search.md +307 -0
  19. package/agent/commands/acp.package-update.md +361 -0
  20. package/agent/commands/acp.package-validate.md +540 -0
  21. package/agent/commands/acp.pattern-create.md +386 -0
  22. package/agent/commands/acp.plan.md +587 -0
  23. package/agent/commands/acp.proceed.md +882 -0
  24. package/agent/commands/acp.project-create.md +675 -0
  25. package/agent/commands/acp.project-info.md +312 -0
  26. package/agent/commands/acp.project-list.md +226 -0
  27. package/agent/commands/acp.project-remove.md +379 -0
  28. package/agent/commands/acp.project-set.md +227 -0
  29. package/agent/commands/acp.project-update.md +307 -0
  30. package/agent/commands/acp.projects-restore.md +228 -0
  31. package/agent/commands/acp.projects-sync.md +347 -0
  32. package/agent/commands/acp.report.md +407 -0
  33. package/agent/commands/acp.resume.md +239 -0
  34. package/agent/commands/acp.sessions.md +301 -0
  35. package/agent/commands/acp.status.md +293 -0
  36. package/agent/commands/acp.sync.md +364 -0
  37. package/agent/commands/acp.task-create.md +500 -0
  38. package/agent/commands/acp.update.md +302 -0
  39. package/agent/commands/acp.validate.md +466 -0
  40. package/agent/commands/acp.version-check-for-updates.md +276 -0
  41. package/agent/commands/acp.version-check.md +191 -0
  42. package/agent/commands/acp.version-update.md +289 -0
  43. package/agent/commands/command.template.md +339 -0
  44. package/agent/commands/git.commit.md +526 -0
  45. package/agent/commands/git.init.md +514 -0
  46. package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
  47. package/agent/commands/tanstack-cloudflare.tail.md +275 -0
  48. package/agent/design/.gitkeep +0 -0
  49. package/agent/design/design.template.md +154 -0
  50. package/agent/design/local.dashboard-layout-routing.md +288 -0
  51. package/agent/design/local.data-model-yaml-parsing.md +310 -0
  52. package/agent/design/local.search-filtering.md +331 -0
  53. package/agent/design/local.server-api-auto-refresh.md +235 -0
  54. package/agent/design/local.table-tree-views.md +299 -0
  55. package/agent/design/local.visualizer-requirements.md +349 -0
  56. package/agent/design/requirements.template.md +387 -0
  57. package/agent/index/.gitkeep +0 -0
  58. package/agent/index/acp.core.yaml +137 -0
  59. package/agent/index/local.main.template.yaml +37 -0
  60. package/agent/manifest.template.yaml +13 -0
  61. package/agent/manifest.yaml +302 -0
  62. package/agent/milestones/.gitkeep +0 -0
  63. package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
  64. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  65. package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
  66. package/agent/package.template.yaml +86 -0
  67. package/agent/patterns/.gitkeep +0 -0
  68. package/agent/patterns/bootstrap.template.md +1237 -0
  69. package/agent/patterns/pattern.template.md +382 -0
  70. package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
  71. package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
  72. package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
  73. package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
  74. package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
  75. package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
  76. package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
  77. package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
  78. package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
  79. package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
  80. package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
  81. package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
  82. package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
  83. package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
  84. package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
  85. package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
  86. package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
  87. package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
  88. package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
  89. package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
  90. package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
  91. package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
  92. package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
  93. package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
  94. package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
  95. package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
  96. package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
  97. package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
  98. package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
  99. package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
  100. package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
  101. package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
  102. package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
  103. package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
  104. package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
  105. package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
  106. package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
  107. package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
  108. package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
  109. package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
  110. package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
  111. package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
  112. package/agent/progress.template.yaml +161 -0
  113. package/agent/progress.yaml +145 -0
  114. package/agent/schemas/package.schema.yaml +276 -0
  115. package/agent/scripts/acp.common.sh +1781 -0
  116. package/agent/scripts/acp.install.sh +333 -0
  117. package/agent/scripts/acp.package-create.sh +924 -0
  118. package/agent/scripts/acp.package-info.sh +288 -0
  119. package/agent/scripts/acp.package-install.sh +893 -0
  120. package/agent/scripts/acp.package-list.sh +311 -0
  121. package/agent/scripts/acp.package-publish.sh +420 -0
  122. package/agent/scripts/acp.package-remove.sh +348 -0
  123. package/agent/scripts/acp.package-search.sh +156 -0
  124. package/agent/scripts/acp.package-update.sh +517 -0
  125. package/agent/scripts/acp.package-validate.sh +1018 -0
  126. package/agent/scripts/acp.uninstall.sh +85 -0
  127. package/agent/scripts/acp.version-check-for-updates.sh +98 -0
  128. package/agent/scripts/acp.version-check.sh +47 -0
  129. package/agent/scripts/acp.version-update.sh +176 -0
  130. package/agent/scripts/acp.yaml-parser.sh +985 -0
  131. package/agent/scripts/acp.yaml-validate.sh +205 -0
  132. package/agent/tasks/.gitkeep +0 -0
  133. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
  134. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
  135. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
  136. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
  137. package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
  138. package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
  139. package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
  140. package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
  141. package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
  142. package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
  143. package/agent/tasks/task-1-{title}.template.md +244 -0
  144. package/bin/visualize.mjs +84 -0
  145. package/package.json +48 -0
  146. package/src/components/ExtraFieldsBadge.tsx +15 -0
  147. package/src/components/FilterBar.tsx +33 -0
  148. package/src/components/Header.tsx +23 -0
  149. package/src/components/MilestoneTable.tsx +167 -0
  150. package/src/components/MilestoneTree.tsx +84 -0
  151. package/src/components/ProgressBar.tsx +20 -0
  152. package/src/components/SearchInput.tsx +22 -0
  153. package/src/components/Sidebar.tsx +54 -0
  154. package/src/components/StatusBadge.tsx +23 -0
  155. package/src/components/StatusDot.tsx +12 -0
  156. package/src/components/TaskList.tsx +36 -0
  157. package/src/components/ViewToggle.tsx +31 -0
  158. package/src/lib/config.ts +8 -0
  159. package/src/lib/file-watcher.ts +43 -0
  160. package/src/lib/search.ts +48 -0
  161. package/src/lib/types.ts +73 -0
  162. package/src/lib/useAutoRefresh.ts +31 -0
  163. package/src/lib/useCollapse.ts +31 -0
  164. package/src/lib/useFilteredData.ts +55 -0
  165. package/src/lib/yaml-loader-real.spec.ts +47 -0
  166. package/src/lib/yaml-loader.spec.ts +201 -0
  167. package/src/lib/yaml-loader.ts +265 -0
  168. package/src/routeTree.gen.ts +140 -0
  169. package/src/router.tsx +10 -0
  170. package/src/routes/__root.tsx +75 -0
  171. package/src/routes/api/watch.ts +29 -0
  172. package/src/routes/index.tsx +115 -0
  173. package/src/routes/milestones.tsx +50 -0
  174. package/src/routes/search.tsx +84 -0
  175. package/src/routes/tasks.tsx +63 -0
  176. package/src/services/progress-database.service.ts +46 -0
  177. package/src/styles.css +25 -0
  178. package/tsconfig.json +24 -0
  179. package/vite.config.ts +16 -0
  180. package/vitest.config.ts +27 -0
@@ -0,0 +1,140 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as TasksRouteImport } from './routes/tasks'
13
+ import { Route as SearchRouteImport } from './routes/search'
14
+ import { Route as MilestonesRouteImport } from './routes/milestones'
15
+ import { Route as IndexRouteImport } from './routes/index'
16
+ import { Route as ApiWatchRouteImport } from './routes/api/watch'
17
+
18
+ const TasksRoute = TasksRouteImport.update({
19
+ id: '/tasks',
20
+ path: '/tasks',
21
+ getParentRoute: () => rootRouteImport,
22
+ } as any)
23
+ const SearchRoute = SearchRouteImport.update({
24
+ id: '/search',
25
+ path: '/search',
26
+ getParentRoute: () => rootRouteImport,
27
+ } as any)
28
+ const MilestonesRoute = MilestonesRouteImport.update({
29
+ id: '/milestones',
30
+ path: '/milestones',
31
+ getParentRoute: () => rootRouteImport,
32
+ } as any)
33
+ const IndexRoute = IndexRouteImport.update({
34
+ id: '/',
35
+ path: '/',
36
+ getParentRoute: () => rootRouteImport,
37
+ } as any)
38
+ const ApiWatchRoute = ApiWatchRouteImport.update({
39
+ id: '/api/watch',
40
+ path: '/api/watch',
41
+ getParentRoute: () => rootRouteImport,
42
+ } as any)
43
+
44
+ export interface FileRoutesByFullPath {
45
+ '/': typeof IndexRoute
46
+ '/milestones': typeof MilestonesRoute
47
+ '/search': typeof SearchRoute
48
+ '/tasks': typeof TasksRoute
49
+ '/api/watch': typeof ApiWatchRoute
50
+ }
51
+ export interface FileRoutesByTo {
52
+ '/': typeof IndexRoute
53
+ '/milestones': typeof MilestonesRoute
54
+ '/search': typeof SearchRoute
55
+ '/tasks': typeof TasksRoute
56
+ '/api/watch': typeof ApiWatchRoute
57
+ }
58
+ export interface FileRoutesById {
59
+ __root__: typeof rootRouteImport
60
+ '/': typeof IndexRoute
61
+ '/milestones': typeof MilestonesRoute
62
+ '/search': typeof SearchRoute
63
+ '/tasks': typeof TasksRoute
64
+ '/api/watch': typeof ApiWatchRoute
65
+ }
66
+ export interface FileRouteTypes {
67
+ fileRoutesByFullPath: FileRoutesByFullPath
68
+ fullPaths: '/' | '/milestones' | '/search' | '/tasks' | '/api/watch'
69
+ fileRoutesByTo: FileRoutesByTo
70
+ to: '/' | '/milestones' | '/search' | '/tasks' | '/api/watch'
71
+ id: '__root__' | '/' | '/milestones' | '/search' | '/tasks' | '/api/watch'
72
+ fileRoutesById: FileRoutesById
73
+ }
74
+ export interface RootRouteChildren {
75
+ IndexRoute: typeof IndexRoute
76
+ MilestonesRoute: typeof MilestonesRoute
77
+ SearchRoute: typeof SearchRoute
78
+ TasksRoute: typeof TasksRoute
79
+ ApiWatchRoute: typeof ApiWatchRoute
80
+ }
81
+
82
+ declare module '@tanstack/react-router' {
83
+ interface FileRoutesByPath {
84
+ '/tasks': {
85
+ id: '/tasks'
86
+ path: '/tasks'
87
+ fullPath: '/tasks'
88
+ preLoaderRoute: typeof TasksRouteImport
89
+ parentRoute: typeof rootRouteImport
90
+ }
91
+ '/search': {
92
+ id: '/search'
93
+ path: '/search'
94
+ fullPath: '/search'
95
+ preLoaderRoute: typeof SearchRouteImport
96
+ parentRoute: typeof rootRouteImport
97
+ }
98
+ '/milestones': {
99
+ id: '/milestones'
100
+ path: '/milestones'
101
+ fullPath: '/milestones'
102
+ preLoaderRoute: typeof MilestonesRouteImport
103
+ parentRoute: typeof rootRouteImport
104
+ }
105
+ '/': {
106
+ id: '/'
107
+ path: '/'
108
+ fullPath: '/'
109
+ preLoaderRoute: typeof IndexRouteImport
110
+ parentRoute: typeof rootRouteImport
111
+ }
112
+ '/api/watch': {
113
+ id: '/api/watch'
114
+ path: '/api/watch'
115
+ fullPath: '/api/watch'
116
+ preLoaderRoute: typeof ApiWatchRouteImport
117
+ parentRoute: typeof rootRouteImport
118
+ }
119
+ }
120
+ }
121
+
122
+ const rootRouteChildren: RootRouteChildren = {
123
+ IndexRoute: IndexRoute,
124
+ MilestonesRoute: MilestonesRoute,
125
+ SearchRoute: SearchRoute,
126
+ TasksRoute: TasksRoute,
127
+ ApiWatchRoute: ApiWatchRoute,
128
+ }
129
+ export const routeTree = rootRouteImport
130
+ ._addFileChildren(rootRouteChildren)
131
+ ._addFileTypes<FileRouteTypes>()
132
+
133
+ import type { getRouter } from './router.tsx'
134
+ import type { createStart } from '@tanstack/react-start'
135
+ declare module '@tanstack/react-start' {
136
+ interface Register {
137
+ ssr: true
138
+ router: Awaited<ReturnType<typeof getRouter>>
139
+ }
140
+ }
package/src/router.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import { createRouter } from '@tanstack/react-router'
2
+ import { routeTree } from './routeTree.gen'
3
+
4
+ export const getRouter = () => {
5
+ return createRouter({
6
+ routeTree,
7
+ scrollRestoration: true,
8
+ defaultPreloadStaleTime: 30_000,
9
+ })
10
+ }
@@ -0,0 +1,75 @@
1
+ import { HeadContent, Scripts, createRootRoute, Outlet } from '@tanstack/react-router'
2
+ import { useAutoRefresh } from '@/lib/useAutoRefresh'
3
+ import { Sidebar } from '@/components/Sidebar'
4
+ import { Header } from '@/components/Header'
5
+ import { ProgressDatabaseService } from '@/services/progress-database.service'
6
+ import type { ProgressData } from '@/lib/types'
7
+
8
+ import appCss from '../styles.css?url'
9
+
10
+ export const Route = createRootRoute({
11
+ beforeLoad: async () => {
12
+ let progressData: ProgressData | null = null
13
+
14
+ try {
15
+ const result = ProgressDatabaseService.getProgressData()
16
+ if (result.ok) {
17
+ progressData = result.data
18
+ }
19
+ } catch (error) {
20
+ console.error('[Root] Failed to load progress data:', error)
21
+ }
22
+
23
+ return { progressData }
24
+ },
25
+
26
+ head: () => ({
27
+ meta: [
28
+ { charSet: 'utf-8' },
29
+ {
30
+ name: 'viewport',
31
+ content: 'width=device-width, initial-scale=1',
32
+ },
33
+ { title: 'ACP Progress Visualizer' },
34
+ {
35
+ name: 'description',
36
+ content: 'Browser-based dashboard for visualizing ACP progress.yaml data',
37
+ },
38
+ ],
39
+ links: [
40
+ { rel: 'stylesheet', href: appCss },
41
+ ],
42
+ }),
43
+
44
+ shellComponent: RootDocument,
45
+ })
46
+
47
+ function AutoRefresh() {
48
+ useAutoRefresh()
49
+ return null
50
+ }
51
+
52
+ function RootDocument({ children }: { children: React.ReactNode }) {
53
+ const { progressData } = Route.useRouteContext()
54
+
55
+ return (
56
+ <html lang="en">
57
+ <head>
58
+ <HeadContent />
59
+ </head>
60
+ <body>
61
+ <AutoRefresh />
62
+ <div className="flex h-screen bg-gray-950 text-gray-100">
63
+ <Sidebar />
64
+ <div className="flex-1 flex flex-col overflow-hidden">
65
+ <Header data={progressData} />
66
+ <main className="flex-1 overflow-auto">
67
+ {children}
68
+ </main>
69
+ </div>
70
+ </div>
71
+ <Scripts />
72
+ </body>
73
+ </html>
74
+ )
75
+ }
@@ -0,0 +1,29 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getFileWatcher } from '@/lib/file-watcher'
3
+
4
+ export const Route = createFileRoute('/api/watch')({
5
+ server: {
6
+ handlers: {
7
+ GET: async () => {
8
+ const watcher = getFileWatcher()
9
+
10
+ const stream = new ReadableStream({
11
+ start(controller) {
12
+ watcher.addClient(controller)
13
+ },
14
+ cancel(controller) {
15
+ watcher.removeClient(controller)
16
+ },
17
+ })
18
+
19
+ return new Response(stream, {
20
+ headers: {
21
+ 'Content-Type': 'text/event-stream',
22
+ 'Cache-Control': 'no-cache',
23
+ 'Connection': 'keep-alive',
24
+ },
25
+ })
26
+ },
27
+ },
28
+ },
29
+ })
@@ -0,0 +1,115 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { StatusBadge } from '@/components/StatusBadge'
3
+ import { ProgressBar } from '@/components/ProgressBar'
4
+
5
+ export const Route = createFileRoute('/')({
6
+ component: HomePage,
7
+ })
8
+
9
+ function HomePage() {
10
+ const { progressData } = Route.useRouteContext()
11
+
12
+ if (!progressData) {
13
+ return (
14
+ <div className="p-6">
15
+ <div className="p-4 bg-red-900/20 border border-red-800 rounded-lg">
16
+ <p className="text-red-400 font-medium">Failed to load progress data</p>
17
+ <p className="text-red-400/70 text-sm mt-1">
18
+ Check that progress.yaml exists and is valid YAML
19
+ </p>
20
+ </div>
21
+ </div>
22
+ )
23
+ }
24
+
25
+ const data = progressData
26
+
27
+ return (
28
+ <div className="p-6 space-y-6">
29
+ {/* Project Metadata */}
30
+ <div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5">
31
+ <div className="flex items-start justify-between">
32
+ <div>
33
+ <h2 className="text-xl font-bold">{data.project.name}</h2>
34
+ <p className="text-gray-400 text-sm mt-1">{data.project.description}</p>
35
+ </div>
36
+ <div className="text-right">
37
+ <span className="text-xs text-gray-500 font-mono">v{data.project.version}</span>
38
+ <div className="mt-1">
39
+ <StatusBadge status={data.project.status} />
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <div className="mt-4">
44
+ <div className="flex items-center gap-3">
45
+ <span className="text-xs text-gray-500">Overall Progress</span>
46
+ <div className="flex-1">
47
+ <ProgressBar value={data.progress.overall} />
48
+ </div>
49
+ <span className="text-sm font-mono text-gray-300">{data.progress.overall}%</span>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ {/* Milestones Summary */}
55
+ <div>
56
+ <h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
57
+ Milestones ({data.milestones.length})
58
+ </h3>
59
+ <div className="space-y-2">
60
+ {data.milestones.map((m) => (
61
+ <div
62
+ key={m.id}
63
+ className="bg-gray-900/50 border border-gray-800 rounded-lg px-4 py-3 flex items-center gap-4"
64
+ >
65
+ <span className="flex-1 text-sm font-medium">{m.name}</span>
66
+ <StatusBadge status={m.status} />
67
+ <div className="w-24">
68
+ <ProgressBar value={m.progress} size="sm" />
69
+ </div>
70
+ <span className="text-xs text-gray-500 font-mono w-16 text-right">
71
+ {m.tasks_completed}/{m.tasks_total}
72
+ </span>
73
+ </div>
74
+ ))}
75
+ {data.milestones.length === 0 && (
76
+ <p className="text-gray-600 text-sm">No milestones defined</p>
77
+ )}
78
+ </div>
79
+ </div>
80
+
81
+ {/* Next Steps */}
82
+ {data.next_steps.length > 0 && (
83
+ <div>
84
+ <h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
85
+ Next Steps
86
+ </h3>
87
+ <ul className="space-y-1.5">
88
+ {data.next_steps.map((step, i) => (
89
+ <li key={i} className="text-sm text-gray-300 flex items-start gap-2">
90
+ <span className="text-gray-600 mt-0.5">•</span>
91
+ {step}
92
+ </li>
93
+ ))}
94
+ </ul>
95
+ </div>
96
+ )}
97
+
98
+ {/* Blockers */}
99
+ {data.current_blockers.length > 0 && (
100
+ <div className="bg-red-900/10 border border-red-800/30 rounded-lg p-4">
101
+ <h3 className="text-sm font-semibold text-red-400 uppercase tracking-wider mb-2">
102
+ Blockers
103
+ </h3>
104
+ <ul className="space-y-1">
105
+ {data.current_blockers.map((blocker, i) => (
106
+ <li key={i} className="text-sm text-red-300">
107
+ {blocker}
108
+ </li>
109
+ ))}
110
+ </ul>
111
+ </div>
112
+ )}
113
+ </div>
114
+ )
115
+ }
@@ -0,0 +1,50 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { useState } from 'react'
3
+ import { MilestoneTable } from '@/components/MilestoneTable'
4
+ import { MilestoneTree } from '@/components/MilestoneTree'
5
+ import { ViewToggle } from '@/components/ViewToggle'
6
+ import { FilterBar } from '@/components/FilterBar'
7
+ import { SearchInput } from '@/components/SearchInput'
8
+ import { useFilteredData } from '@/lib/useFilteredData'
9
+ import type { Status } from '@/lib/types'
10
+
11
+ export const Route = createFileRoute('/milestones')({
12
+ component: MilestonesPage,
13
+ })
14
+
15
+ function MilestonesPage() {
16
+ const { progressData } = Route.useRouteContext()
17
+ const [view, setView] = useState<'table' | 'tree'>('table')
18
+ const [status, setStatus] = useState<Status | 'all'>('all')
19
+ const [search, setSearch] = useState('')
20
+
21
+ const filtered = useFilteredData(progressData, { status, search })
22
+
23
+ if (!filtered) {
24
+ return (
25
+ <div className="p-6">
26
+ <p className="text-gray-600 text-sm">No data loaded</p>
27
+ </div>
28
+ )
29
+ }
30
+
31
+ return (
32
+ <div className="p-6">
33
+ <div className="flex items-center justify-between mb-4">
34
+ <h2 className="text-lg font-semibold">Milestones</h2>
35
+ <ViewToggle value={view} onChange={setView} />
36
+ </div>
37
+ <div className="flex items-center gap-3 mb-4">
38
+ <FilterBar status={status} onStatusChange={setStatus} />
39
+ <div className="w-64">
40
+ <SearchInput value={search} onChange={setSearch} placeholder="Filter milestones..." />
41
+ </div>
42
+ </div>
43
+ {view === 'table' ? (
44
+ <MilestoneTable milestones={filtered.milestones} tasks={filtered.tasks} />
45
+ ) : (
46
+ <MilestoneTree milestones={filtered.milestones} tasks={filtered.tasks} />
47
+ )}
48
+ </div>
49
+ )
50
+ }
@@ -0,0 +1,84 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { useState, useMemo } from 'react'
3
+ import { SearchInput } from '@/components/SearchInput'
4
+ import { StatusBadge } from '@/components/StatusBadge'
5
+ import { StatusDot } from '@/components/StatusDot'
6
+ import { buildSearchIndex } from '@/lib/search'
7
+
8
+ export const Route = createFileRoute('/search')({
9
+ component: SearchPage,
10
+ })
11
+
12
+ function SearchPage() {
13
+ const { progressData } = Route.useRouteContext()
14
+ const [query, setQuery] = useState('')
15
+
16
+ const results = useMemo(() => {
17
+ if (!progressData || !query.trim()) return []
18
+ const index = buildSearchIndex(progressData)
19
+ return index.search(query).slice(0, 20)
20
+ }, [progressData, query])
21
+
22
+ return (
23
+ <div className="p-6">
24
+ <h2 className="text-lg font-semibold mb-4">Search</h2>
25
+ <div className="max-w-md mb-6">
26
+ <SearchInput
27
+ value={query}
28
+ onChange={setQuery}
29
+ placeholder="Search milestones and tasks..."
30
+ />
31
+ </div>
32
+
33
+ {query.trim() && results.length === 0 && (
34
+ <p className="text-gray-500 text-sm">
35
+ No results for "{query}"
36
+ </p>
37
+ )}
38
+
39
+ {results.length > 0 && (
40
+ <div className="space-y-2">
41
+ <p className="text-xs text-gray-500 mb-3">
42
+ {results.length} result{results.length !== 1 ? 's' : ''}
43
+ </p>
44
+ {results.map((result, i) => (
45
+ <div
46
+ key={i}
47
+ className="bg-gray-900/50 border border-gray-800 rounded-lg px-4 py-3"
48
+ >
49
+ <div className="flex items-center gap-3">
50
+ {result.item.type === 'task' && result.item.task ? (
51
+ <>
52
+ <StatusDot status={result.item.task.status} />
53
+ <span className="text-sm">{result.item.task.name}</span>
54
+ <span className="text-xs text-gray-600 ml-auto">
55
+ {result.item.milestone.name}
56
+ </span>
57
+ </>
58
+ ) : (
59
+ <>
60
+ <span className="text-sm font-medium">
61
+ {result.item.milestone.name}
62
+ </span>
63
+ <StatusBadge status={result.item.milestone.status} />
64
+ </>
65
+ )}
66
+ </div>
67
+ {result.item.notes && (
68
+ <p className="text-xs text-gray-500 mt-1 truncate">
69
+ {result.item.notes}
70
+ </p>
71
+ )}
72
+ </div>
73
+ ))}
74
+ </div>
75
+ )}
76
+
77
+ {!query.trim() && (
78
+ <p className="text-gray-600 text-sm">
79
+ Type to search across milestones and tasks
80
+ </p>
81
+ )}
82
+ </div>
83
+ )
84
+ }
@@ -0,0 +1,63 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { StatusDot } from '@/components/StatusDot'
3
+ import { ExtraFieldsBadge } from '@/components/ExtraFieldsBadge'
4
+ import type { Task } from '@/lib/types'
5
+
6
+ export const Route = createFileRoute('/tasks')({
7
+ component: TasksPage,
8
+ })
9
+
10
+ function TasksPage() {
11
+ const { progressData } = Route.useRouteContext()
12
+
13
+ if (!progressData) {
14
+ return (
15
+ <div className="p-6">
16
+ <p className="text-gray-600 text-sm">No data loaded</p>
17
+ </div>
18
+ )
19
+ }
20
+
21
+ const allTasks: Array<Task & { milestoneName: string }> = []
22
+ for (const milestone of progressData.milestones) {
23
+ const tasks = progressData.tasks[milestone.id] || []
24
+ for (const task of tasks) {
25
+ allTasks.push({ ...task, milestoneName: milestone.name })
26
+ }
27
+ }
28
+
29
+ return (
30
+ <div className="p-6">
31
+ <h2 className="text-lg font-semibold mb-4">
32
+ All Tasks ({allTasks.length})
33
+ </h2>
34
+ <div className="border border-gray-800 rounded-lg overflow-hidden">
35
+ {allTasks.map((task) => (
36
+ <div
37
+ key={task.id}
38
+ className="flex items-center gap-3 px-4 py-2.5 border-b border-gray-800/50 hover:bg-gray-800/30 transition-colors"
39
+ >
40
+ <StatusDot status={task.status} />
41
+ <span
42
+ className={`flex-1 text-sm ${
43
+ task.status === 'completed' ? 'text-gray-500' : 'text-gray-200'
44
+ }`}
45
+ >
46
+ {task.name}
47
+ </span>
48
+ <span className="text-xs text-gray-600">{task.milestoneName}</span>
49
+ <span className="text-xs text-gray-500 font-mono w-8 text-right">
50
+ {task.estimated_hours}h
51
+ </span>
52
+ <ExtraFieldsBadge fields={task.extra} />
53
+ </div>
54
+ ))}
55
+ {allTasks.length === 0 && (
56
+ <div className="px-4 py-6 text-center">
57
+ <p className="text-gray-600 text-sm">No tasks defined</p>
58
+ </div>
59
+ )}
60
+ </div>
61
+ </div>
62
+ )
63
+ }
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from 'fs'
2
+ import { parseProgressYaml } from '@/lib/yaml-loader'
3
+ import { getProgressYamlPath } from '@/lib/config'
4
+ import type { ProgressData } from '@/lib/types'
5
+
6
+ export type ProgressResult =
7
+ | { ok: true; data: ProgressData }
8
+ | { ok: false; error: 'FILE_NOT_FOUND' | 'PARSE_ERROR'; message: string; path: string }
9
+
10
+ export class ProgressDatabaseService {
11
+ static getProgressData(): ProgressResult {
12
+ const filePath = getProgressYamlPath()
13
+
14
+ try {
15
+ const raw = readFileSync(filePath, 'utf-8')
16
+
17
+ try {
18
+ const data = parseProgressYaml(raw)
19
+ return { ok: true, data }
20
+ } catch (err) {
21
+ return {
22
+ ok: false,
23
+ error: 'PARSE_ERROR',
24
+ message: err instanceof Error ? err.message : 'Failed to parse YAML',
25
+ path: filePath,
26
+ }
27
+ }
28
+ } catch (err) {
29
+ const code = (err as NodeJS.ErrnoException).code
30
+ if (code === 'ENOENT') {
31
+ return {
32
+ ok: false,
33
+ error: 'FILE_NOT_FOUND',
34
+ message: `progress.yaml not found at: ${filePath}`,
35
+ path: filePath,
36
+ }
37
+ }
38
+ return {
39
+ ok: false,
40
+ error: 'PARSE_ERROR',
41
+ message: err instanceof Error ? err.message : 'Failed to read file',
42
+ path: filePath,
43
+ }
44
+ }
45
+ }
46
+ }
package/src/styles.css ADDED
@@ -0,0 +1,25 @@
1
+ @import "tailwindcss";
2
+
3
+ html {
4
+ @apply bg-gray-950;
5
+ }
6
+
7
+ body {
8
+ @apply m-0 bg-gray-950 min-h-screen text-gray-100;
9
+ font-family: "Inter", system-ui, -apple-system, sans-serif;
10
+ -webkit-font-smoothing: antialiased;
11
+ -moz-osx-font-smoothing: grayscale;
12
+ }
13
+
14
+ code {
15
+ font-family: "JetBrains Mono", "Fira Code", "Consolas", monospace;
16
+ }
17
+
18
+ /* Hide scrollbar while keeping scroll functionality */
19
+ .scrollbar-hide {
20
+ -ms-overflow-style: none;
21
+ scrollbar-width: none;
22
+ }
23
+ .scrollbar-hide::-webkit-scrollbar {
24
+ display: none;
25
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "include": ["**/*.ts", "**/*.tsx"],
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "jsx": "react-jsx",
6
+ "module": "ESNext",
7
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
8
+ "types": ["vite/client"],
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "verbatimModuleSyntax": false,
12
+ "noEmit": true,
13
+ "skipLibCheck": true,
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUncheckedSideEffectImports": true,
19
+ "baseUrl": ".",
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ }
23
+ }
24
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from 'vite'
2
+ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
3
+ import viteReact from '@vitejs/plugin-react'
4
+ import viteTsConfigPaths from 'vite-tsconfig-paths'
5
+ import tailwindcss from '@tailwindcss/vite'
6
+
7
+ export default defineConfig({
8
+ plugins: [
9
+ viteTsConfigPaths({
10
+ projects: ['./tsconfig.json'],
11
+ }),
12
+ tailwindcss(),
13
+ tanstackStart(),
14
+ viteReact(),
15
+ ],
16
+ })