create-arete-workspace 0.2.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 +77 -0
  2. package/bin/arete.js +156 -0
  3. package/bin/create.js +111 -0
  4. package/lib/install-openclaw.js +50 -0
  5. package/lib/scaffold.js +213 -0
  6. package/lib/setup-wizard.js +88 -0
  7. package/lib/updater.js +130 -0
  8. package/package.json +34 -0
  9. package/packages/gatsaeng-os/README.md +36 -0
  10. package/packages/gatsaeng-os/components.json +23 -0
  11. package/packages/gatsaeng-os/eslint.config.mjs +18 -0
  12. package/packages/gatsaeng-os/next.config.ts +7 -0
  13. package/packages/gatsaeng-os/package.json +59 -0
  14. package/packages/gatsaeng-os/postcss.config.mjs +7 -0
  15. package/packages/gatsaeng-os/public/file.svg +1 -0
  16. package/packages/gatsaeng-os/public/globe.svg +1 -0
  17. package/packages/gatsaeng-os/public/next.svg +1 -0
  18. package/packages/gatsaeng-os/public/vercel.svg +1 -0
  19. package/packages/gatsaeng-os/public/window.svg +1 -0
  20. package/packages/gatsaeng-os/python/api_server.py +248 -0
  21. package/packages/gatsaeng-os/python/briefing.py +145 -0
  22. package/packages/gatsaeng-os/python/config.py +55 -0
  23. package/packages/gatsaeng-os/python/goal_context_agent.py +193 -0
  24. package/packages/gatsaeng-os/python/gyeokguk.py +171 -0
  25. package/packages/gatsaeng-os/python/proactive.py +158 -0
  26. package/packages/gatsaeng-os/python/requirements.txt +11 -0
  27. package/packages/gatsaeng-os/python/run.py +28 -0
  28. package/packages/gatsaeng-os/python/scoring.py +44 -0
  29. package/packages/gatsaeng-os/python/streak.py +70 -0
  30. package/packages/gatsaeng-os/python/telegram_bot.py +331 -0
  31. package/packages/gatsaeng-os/python/timing_engine.py +117 -0
  32. package/packages/gatsaeng-os/python/vault_io.py +423 -0
  33. package/packages/gatsaeng-os/src/app/(dashboard)/areas/[id]/page.tsx +215 -0
  34. package/packages/gatsaeng-os/src/app/(dashboard)/areas/page.tsx +161 -0
  35. package/packages/gatsaeng-os/src/app/(dashboard)/books/[id]/page.tsx +215 -0
  36. package/packages/gatsaeng-os/src/app/(dashboard)/books/page.tsx +268 -0
  37. package/packages/gatsaeng-os/src/app/(dashboard)/calendar/page.tsx +379 -0
  38. package/packages/gatsaeng-os/src/app/(dashboard)/error.tsx +30 -0
  39. package/packages/gatsaeng-os/src/app/(dashboard)/focus/page.tsx +293 -0
  40. package/packages/gatsaeng-os/src/app/(dashboard)/goals/[id]/page.tsx +426 -0
  41. package/packages/gatsaeng-os/src/app/(dashboard)/goals/page.tsx +178 -0
  42. package/packages/gatsaeng-os/src/app/(dashboard)/layout.tsx +29 -0
  43. package/packages/gatsaeng-os/src/app/(dashboard)/notes/[id]/page.tsx +147 -0
  44. package/packages/gatsaeng-os/src/app/(dashboard)/notes/page.tsx +254 -0
  45. package/packages/gatsaeng-os/src/app/(dashboard)/page.tsx +26 -0
  46. package/packages/gatsaeng-os/src/app/(dashboard)/projects/[id]/page.tsx +86 -0
  47. package/packages/gatsaeng-os/src/app/(dashboard)/projects/page.tsx +215 -0
  48. package/packages/gatsaeng-os/src/app/(dashboard)/review/page.tsx +475 -0
  49. package/packages/gatsaeng-os/src/app/(dashboard)/routines/page.tsx +436 -0
  50. package/packages/gatsaeng-os/src/app/(dashboard)/tasks/[id]/page.tsx +210 -0
  51. package/packages/gatsaeng-os/src/app/(dashboard)/tasks/page.tsx +307 -0
  52. package/packages/gatsaeng-os/src/app/(dashboard)/voice/page.tsx +212 -0
  53. package/packages/gatsaeng-os/src/app/api/areas/[id]/route.ts +26 -0
  54. package/packages/gatsaeng-os/src/app/api/areas/route.ts +22 -0
  55. package/packages/gatsaeng-os/src/app/api/auth/login/route.ts +52 -0
  56. package/packages/gatsaeng-os/src/app/api/auth/logout/route.ts +8 -0
  57. package/packages/gatsaeng-os/src/app/api/books/[id]/route.ts +27 -0
  58. package/packages/gatsaeng-os/src/app/api/books/route.ts +20 -0
  59. package/packages/gatsaeng-os/src/app/api/calendar/[id]/route.ts +24 -0
  60. package/packages/gatsaeng-os/src/app/api/calendar/import/route.ts +52 -0
  61. package/packages/gatsaeng-os/src/app/api/calendar/route.ts +37 -0
  62. package/packages/gatsaeng-os/src/app/api/daily/route.ts +51 -0
  63. package/packages/gatsaeng-os/src/app/api/goals/[id]/route.ts +34 -0
  64. package/packages/gatsaeng-os/src/app/api/goals/route.ts +30 -0
  65. package/packages/gatsaeng-os/src/app/api/logs/energy/route.ts +40 -0
  66. package/packages/gatsaeng-os/src/app/api/logs/focus/route.ts +22 -0
  67. package/packages/gatsaeng-os/src/app/api/logs/routine/route.ts +54 -0
  68. package/packages/gatsaeng-os/src/app/api/milestones/[id]/route.ts +26 -0
  69. package/packages/gatsaeng-os/src/app/api/milestones/route.ts +47 -0
  70. package/packages/gatsaeng-os/src/app/api/notes/[id]/route.ts +29 -0
  71. package/packages/gatsaeng-os/src/app/api/notes/route.ts +37 -0
  72. package/packages/gatsaeng-os/src/app/api/profile/route.ts +17 -0
  73. package/packages/gatsaeng-os/src/app/api/projects/[id]/route.ts +27 -0
  74. package/packages/gatsaeng-os/src/app/api/projects/route.ts +25 -0
  75. package/packages/gatsaeng-os/src/app/api/reviews/[id]/route.ts +26 -0
  76. package/packages/gatsaeng-os/src/app/api/reviews/route.ts +29 -0
  77. package/packages/gatsaeng-os/src/app/api/routines/[id]/route.ts +26 -0
  78. package/packages/gatsaeng-os/src/app/api/routines/route.ts +28 -0
  79. package/packages/gatsaeng-os/src/app/api/tasks/[id]/route.ts +28 -0
  80. package/packages/gatsaeng-os/src/app/api/tasks/route.ts +66 -0
  81. package/packages/gatsaeng-os/src/app/api/timing/current/route.ts +63 -0
  82. package/packages/gatsaeng-os/src/app/api/voice/chat/route.ts +50 -0
  83. package/packages/gatsaeng-os/src/app/api/voice/transcribe/route.ts +25 -0
  84. package/packages/gatsaeng-os/src/app/api/voice/tts/route.ts +36 -0
  85. package/packages/gatsaeng-os/src/app/error.tsx +30 -0
  86. package/packages/gatsaeng-os/src/app/favicon.ico +0 -0
  87. package/packages/gatsaeng-os/src/app/globals.css +208 -0
  88. package/packages/gatsaeng-os/src/app/layout.tsx +33 -0
  89. package/packages/gatsaeng-os/src/app/login/page.tsx +87 -0
  90. package/packages/gatsaeng-os/src/app/providers.tsx +27 -0
  91. package/packages/gatsaeng-os/src/components/ErrorBoundary.tsx +46 -0
  92. package/packages/gatsaeng-os/src/components/dashboard/DashboardGrid.tsx +86 -0
  93. package/packages/gatsaeng-os/src/components/dashboard/DdayWidget.tsx +88 -0
  94. package/packages/gatsaeng-os/src/components/dashboard/EnergyTracker.tsx +87 -0
  95. package/packages/gatsaeng-os/src/components/dashboard/FocusTimer.tsx +139 -0
  96. package/packages/gatsaeng-os/src/components/dashboard/GatsaengScore.tsx +30 -0
  97. package/packages/gatsaeng-os/src/components/dashboard/GoalRings.tsx +107 -0
  98. package/packages/gatsaeng-os/src/components/dashboard/ProactiveBar.tsx +98 -0
  99. package/packages/gatsaeng-os/src/components/dashboard/RoutineChecklist.tsx +81 -0
  100. package/packages/gatsaeng-os/src/components/dashboard/TimingWidget.tsx +86 -0
  101. package/packages/gatsaeng-os/src/components/dashboard/WidgetCustomizer.tsx +95 -0
  102. package/packages/gatsaeng-os/src/components/dashboard/WidgetWrapper.tsx +33 -0
  103. package/packages/gatsaeng-os/src/components/dashboard/ZeigarnikPanel.tsx +43 -0
  104. package/packages/gatsaeng-os/src/components/editor/EditorToolbar.tsx +186 -0
  105. package/packages/gatsaeng-os/src/components/editor/TiptapEditor.tsx +114 -0
  106. package/packages/gatsaeng-os/src/components/layout/Header.tsx +47 -0
  107. package/packages/gatsaeng-os/src/components/layout/MobileBottomNav.tsx +122 -0
  108. package/packages/gatsaeng-os/src/components/layout/MobileSidebar.tsx +29 -0
  109. package/packages/gatsaeng-os/src/components/layout/Sidebar.tsx +142 -0
  110. package/packages/gatsaeng-os/src/components/onboarding/OnboardingFlow.tsx +229 -0
  111. package/packages/gatsaeng-os/src/components/onboarding/OnboardingGate.tsx +78 -0
  112. package/packages/gatsaeng-os/src/components/projects/CalendarView.tsx +152 -0
  113. package/packages/gatsaeng-os/src/components/projects/KanbanView.tsx +180 -0
  114. package/packages/gatsaeng-os/src/components/projects/ListView.tsx +82 -0
  115. package/packages/gatsaeng-os/src/components/projects/TableView.tsx +206 -0
  116. package/packages/gatsaeng-os/src/components/projects/TaskCard.tsx +154 -0
  117. package/packages/gatsaeng-os/src/components/projects/TaskForm.tsx +128 -0
  118. package/packages/gatsaeng-os/src/components/projects/ViewSwitcher.tsx +40 -0
  119. package/packages/gatsaeng-os/src/components/search/GlobalSearch.tsx +179 -0
  120. package/packages/gatsaeng-os/src/components/shared/InlineEdit.tsx +77 -0
  121. package/packages/gatsaeng-os/src/components/shared/PinButton.tsx +42 -0
  122. package/packages/gatsaeng-os/src/components/tasks/DDayBadge.tsx +34 -0
  123. package/packages/gatsaeng-os/src/components/ui/badge.tsx +48 -0
  124. package/packages/gatsaeng-os/src/components/ui/button.tsx +64 -0
  125. package/packages/gatsaeng-os/src/components/ui/card.tsx +92 -0
  126. package/packages/gatsaeng-os/src/components/ui/checkbox.tsx +32 -0
  127. package/packages/gatsaeng-os/src/components/ui/command.tsx +184 -0
  128. package/packages/gatsaeng-os/src/components/ui/dialog.tsx +158 -0
  129. package/packages/gatsaeng-os/src/components/ui/input.tsx +21 -0
  130. package/packages/gatsaeng-os/src/components/ui/label.tsx +24 -0
  131. package/packages/gatsaeng-os/src/components/ui/popover.tsx +89 -0
  132. package/packages/gatsaeng-os/src/components/ui/progress.tsx +31 -0
  133. package/packages/gatsaeng-os/src/components/ui/select.tsx +190 -0
  134. package/packages/gatsaeng-os/src/components/ui/sheet.tsx +143 -0
  135. package/packages/gatsaeng-os/src/components/ui/tabs.tsx +91 -0
  136. package/packages/gatsaeng-os/src/components/ui/toggle-group.tsx +83 -0
  137. package/packages/gatsaeng-os/src/components/ui/toggle.tsx +47 -0
  138. package/packages/gatsaeng-os/src/components/ui/tooltip.tsx +57 -0
  139. package/packages/gatsaeng-os/src/hooks/useAreas.ts +53 -0
  140. package/packages/gatsaeng-os/src/hooks/useBooks.ts +62 -0
  141. package/packages/gatsaeng-os/src/hooks/useCalendar.ts +59 -0
  142. package/packages/gatsaeng-os/src/hooks/useDaily.ts +15 -0
  143. package/packages/gatsaeng-os/src/hooks/useGlobalTasks.ts +45 -0
  144. package/packages/gatsaeng-os/src/hooks/useGoals.ts +53 -0
  145. package/packages/gatsaeng-os/src/hooks/useMilestones.ts +75 -0
  146. package/packages/gatsaeng-os/src/hooks/useNotes.ts +65 -0
  147. package/packages/gatsaeng-os/src/hooks/useProjects.ts +102 -0
  148. package/packages/gatsaeng-os/src/hooks/useRoutines.ts +76 -0
  149. package/packages/gatsaeng-os/src/hooks/useTiming.ts +27 -0
  150. package/packages/gatsaeng-os/src/lib/apiFetch.ts +14 -0
  151. package/packages/gatsaeng-os/src/lib/auth.ts +32 -0
  152. package/packages/gatsaeng-os/src/lib/date.ts +7 -0
  153. package/packages/gatsaeng-os/src/lib/editor/markdown.ts +35 -0
  154. package/packages/gatsaeng-os/src/lib/llm-governor.ts +167 -0
  155. package/packages/gatsaeng-os/src/lib/neuroscience/energyCycle.ts +35 -0
  156. package/packages/gatsaeng-os/src/lib/neuroscience/habitStack.ts +22 -0
  157. package/packages/gatsaeng-os/src/lib/neuroscience/scoring.ts +32 -0
  158. package/packages/gatsaeng-os/src/lib/routes.ts +15 -0
  159. package/packages/gatsaeng-os/src/lib/utils.ts +6 -0
  160. package/packages/gatsaeng-os/src/lib/vault/config.ts +29 -0
  161. package/packages/gatsaeng-os/src/lib/vault/frontmatter.ts +84 -0
  162. package/packages/gatsaeng-os/src/lib/vault/index.ts +180 -0
  163. package/packages/gatsaeng-os/src/lib/vault/schemas.ts +274 -0
  164. package/packages/gatsaeng-os/src/middleware.ts +34 -0
  165. package/packages/gatsaeng-os/src/stores/dashboardStore.ts +26 -0
  166. package/packages/gatsaeng-os/src/stores/favoritesStore.ts +47 -0
  167. package/packages/gatsaeng-os/src/stores/timerStore.ts +65 -0
  168. package/packages/gatsaeng-os/src/types/index.ts +320 -0
  169. package/packages/gatsaeng-os/tsconfig.json +34 -0
  170. package/templates/scripts/forge_qa.sh.tmpl +237 -0
  171. package/templates/scripts/forge_ship.sh.tmpl +183 -0
  172. package/templates/scripts/session_indexer.py.tmpl +420 -0
  173. package/templates/scripts/tracer.py.tmpl +266 -0
  174. package/templates/workspace/AGENTS.md.tmpl +190 -0
  175. package/templates/workspace/BOOTSTRAP.md.tmpl +27 -0
  176. package/templates/workspace/HEARTBEAT.md.tmpl +23 -0
  177. package/templates/workspace/MEMORY.md.tmpl +35 -0
  178. package/templates/workspace/SOUL.md.tmpl +258 -0
  179. package/templates/workspace/TOOLS.md.tmpl +28 -0
  180. package/templates/workspace/USER.md.tmpl +43 -0
@@ -0,0 +1,57 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Tooltip as TooltipPrimitive } from "radix-ui"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function TooltipProvider({
9
+ delayDuration = 0,
10
+ ...props
11
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
+ return (
13
+ <TooltipPrimitive.Provider
14
+ data-slot="tooltip-provider"
15
+ delayDuration={delayDuration}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function Tooltip({
22
+ ...props
23
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
+ }
26
+
27
+ function TooltipTrigger({
28
+ ...props
29
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
30
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
31
+ }
32
+
33
+ function TooltipContent({
34
+ className,
35
+ sideOffset = 0,
36
+ children,
37
+ ...props
38
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
39
+ return (
40
+ <TooltipPrimitive.Portal>
41
+ <TooltipPrimitive.Content
42
+ data-slot="tooltip-content"
43
+ sideOffset={sideOffset}
44
+ className={cn(
45
+ "z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
46
+ className
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
52
+ </TooltipPrimitive.Content>
53
+ </TooltipPrimitive.Portal>
54
+ )
55
+ }
56
+
57
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,53 @@
1
+ 'use client'
2
+
3
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
+ import { apiFetch } from '@/lib/apiFetch'
5
+ import type { Area } from '@/types'
6
+
7
+ export function useAreas() {
8
+ return useQuery({
9
+ queryKey: ['areas'],
10
+ queryFn: () => apiFetch<Area[]>('/api/areas'),
11
+ })
12
+ }
13
+
14
+ export function useCreateArea() {
15
+ const queryClient = useQueryClient()
16
+ return useMutation({
17
+ mutationFn: (data: Partial<Area>) =>
18
+ apiFetch<Area>('/api/areas', {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json' },
21
+ body: JSON.stringify(data),
22
+ }),
23
+ onSuccess: () => {
24
+ queryClient.invalidateQueries({ queryKey: ['areas'] })
25
+ },
26
+ })
27
+ }
28
+
29
+ export function useUpdateArea() {
30
+ const queryClient = useQueryClient()
31
+ return useMutation({
32
+ mutationFn: ({ id, ...data }: Partial<Area> & { id: string }) =>
33
+ apiFetch<Area>(`/api/areas/${id}`, {
34
+ method: 'PUT',
35
+ headers: { 'Content-Type': 'application/json' },
36
+ body: JSON.stringify(data),
37
+ }),
38
+ onSuccess: () => {
39
+ queryClient.invalidateQueries({ queryKey: ['areas'] })
40
+ },
41
+ })
42
+ }
43
+
44
+ export function useDeleteArea() {
45
+ const queryClient = useQueryClient()
46
+ return useMutation({
47
+ mutationFn: (id: string) =>
48
+ apiFetch(`/api/areas/${id}`, { method: 'DELETE' }),
49
+ onSuccess: () => {
50
+ queryClient.invalidateQueries({ queryKey: ['areas'] })
51
+ },
52
+ })
53
+ }
@@ -0,0 +1,62 @@
1
+ 'use client'
2
+
3
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
+ import { apiFetch } from '@/lib/apiFetch'
5
+ import type { Book } from '@/types'
6
+
7
+ export function useBook(id: string) {
8
+ return useQuery({
9
+ queryKey: ['book', id],
10
+ queryFn: () => apiFetch<Book & { _content?: string }>(`/api/books/${id}`),
11
+ enabled: !!id,
12
+ })
13
+ }
14
+
15
+ export function useBooks() {
16
+ return useQuery({
17
+ queryKey: ['books'],
18
+ queryFn: () => apiFetch<Book[]>('/api/books'),
19
+ })
20
+ }
21
+
22
+ export function useCreateBook() {
23
+ const queryClient = useQueryClient()
24
+ return useMutation({
25
+ mutationFn: (data: Partial<Book>) =>
26
+ apiFetch<Book>('/api/books', {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify(data),
30
+ }),
31
+ onSuccess: () => {
32
+ queryClient.invalidateQueries({ queryKey: ['books'] })
33
+ },
34
+ })
35
+ }
36
+
37
+ export function useUpdateBook() {
38
+ const queryClient = useQueryClient()
39
+ return useMutation({
40
+ mutationFn: ({ id, ...data }: Partial<Book> & { id: string; content?: string }) =>
41
+ apiFetch<Book>(`/api/books/${id}`, {
42
+ method: 'PUT',
43
+ headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify(data),
45
+ }),
46
+ onSuccess: (_, variables) => {
47
+ queryClient.invalidateQueries({ queryKey: ['books'] })
48
+ if (variables.id) queryClient.invalidateQueries({ queryKey: ['book', variables.id] })
49
+ },
50
+ })
51
+ }
52
+
53
+ export function useDeleteBook() {
54
+ const queryClient = useQueryClient()
55
+ return useMutation({
56
+ mutationFn: (id: string) =>
57
+ apiFetch(`/api/books/${id}`, { method: 'DELETE' }),
58
+ onSuccess: () => {
59
+ queryClient.invalidateQueries({ queryKey: ['books'] })
60
+ },
61
+ })
62
+ }
@@ -0,0 +1,59 @@
1
+ 'use client'
2
+
3
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
+ import { apiFetch } from '@/lib/apiFetch'
5
+ import type { CalendarEvent } from '@/types'
6
+
7
+ export function useCalendarEvents(weekStart?: string, weekEnd?: string) {
8
+ return useQuery({
9
+ queryKey: ['calendar', weekStart, weekEnd],
10
+ queryFn: () => {
11
+ const params = new URLSearchParams()
12
+ if (weekStart) params.set('week_start', weekStart)
13
+ if (weekEnd) params.set('week_end', weekEnd)
14
+ return apiFetch<CalendarEvent[]>(`/api/calendar?${params}`)
15
+ },
16
+ enabled: !!weekStart && !!weekEnd,
17
+ })
18
+ }
19
+
20
+ export function useCreateCalendarEvent() {
21
+ const queryClient = useQueryClient()
22
+ return useMutation({
23
+ mutationFn: (data: Partial<CalendarEvent>) =>
24
+ apiFetch<CalendarEvent>('/api/calendar', {
25
+ method: 'POST',
26
+ headers: { 'Content-Type': 'application/json' },
27
+ body: JSON.stringify(data),
28
+ }),
29
+ onSuccess: () => {
30
+ queryClient.invalidateQueries({ queryKey: ['calendar'] })
31
+ },
32
+ })
33
+ }
34
+
35
+ export function useUpdateCalendarEvent() {
36
+ const queryClient = useQueryClient()
37
+ return useMutation({
38
+ mutationFn: ({ id, ...data }: Partial<CalendarEvent> & { id: string }) =>
39
+ apiFetch<CalendarEvent>(`/api/calendar/${id}`, {
40
+ method: 'PUT',
41
+ headers: { 'Content-Type': 'application/json' },
42
+ body: JSON.stringify(data),
43
+ }),
44
+ onSuccess: () => {
45
+ queryClient.invalidateQueries({ queryKey: ['calendar'] })
46
+ },
47
+ })
48
+ }
49
+
50
+ export function useDeleteCalendarEvent() {
51
+ const queryClient = useQueryClient()
52
+ return useMutation({
53
+ mutationFn: (id: string) =>
54
+ apiFetch(`/api/calendar/${id}`, { method: 'DELETE' }),
55
+ onSuccess: () => {
56
+ queryClient.invalidateQueries({ queryKey: ['calendar'] })
57
+ },
58
+ })
59
+ }
@@ -0,0 +1,15 @@
1
+ 'use client'
2
+
3
+ import { useQuery } from '@tanstack/react-query'
4
+ import { apiFetch } from '@/lib/apiFetch'
5
+ import { getToday } from '@/lib/date'
6
+ import type { DailyManifest } from '@/types'
7
+
8
+ export function useDaily(date?: string) {
9
+ const today = date ?? getToday()
10
+
11
+ return useQuery({
12
+ queryKey: ['daily', today],
13
+ queryFn: () => apiFetch<DailyManifest>(`/api/daily?date=${today}`),
14
+ })
15
+ }
@@ -0,0 +1,45 @@
1
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
2
+ import { apiFetch } from '@/lib/apiFetch'
3
+ import type { Task } from '@/types'
4
+
5
+ export function useGlobalTasks(view?: string, sort?: string) {
6
+ return useQuery({
7
+ queryKey: ['tasks', 'global', view, sort],
8
+ queryFn: () => {
9
+ const params = new URLSearchParams()
10
+ if (view) params.set('view', view)
11
+ if (sort) params.set('sort', sort)
12
+ return apiFetch<Task[]>(`/api/tasks?${params}`)
13
+ },
14
+ })
15
+ }
16
+
17
+ export function useQuickCreateTask() {
18
+ const queryClient = useQueryClient()
19
+ return useMutation({
20
+ mutationFn: (data: { title: string; project_id?: string; due_date?: string; priority?: string }) =>
21
+ apiFetch<Task>('/api/tasks', {
22
+ method: 'POST',
23
+ headers: { 'Content-Type': 'application/json' },
24
+ body: JSON.stringify({ ...data, status: 'todo' }),
25
+ }),
26
+ onSuccess: () => {
27
+ queryClient.invalidateQueries({ queryKey: ['tasks'] })
28
+ },
29
+ })
30
+ }
31
+
32
+ export function useToggleTaskDone() {
33
+ const queryClient = useQueryClient()
34
+ return useMutation({
35
+ mutationFn: ({ id, currentStatus }: { id: string; currentStatus: string }) =>
36
+ apiFetch<Task>(`/api/tasks/${id}`, {
37
+ method: 'PUT',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({ status: currentStatus === 'done' ? 'todo' : 'done' }),
40
+ }),
41
+ onSuccess: () => {
42
+ queryClient.invalidateQueries({ queryKey: ['tasks'] })
43
+ },
44
+ })
45
+ }
@@ -0,0 +1,53 @@
1
+ 'use client'
2
+
3
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
+ import { apiFetch } from '@/lib/apiFetch'
5
+ import type { Goal } from '@/types'
6
+
7
+ export function useGoals() {
8
+ return useQuery({
9
+ queryKey: ['goals'],
10
+ queryFn: () => apiFetch<Goal[]>('/api/goals'),
11
+ })
12
+ }
13
+
14
+ export function useCreateGoal() {
15
+ const queryClient = useQueryClient()
16
+ return useMutation({
17
+ mutationFn: (data: Partial<Goal>) =>
18
+ apiFetch<Goal>('/api/goals', {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json' },
21
+ body: JSON.stringify(data),
22
+ }),
23
+ onSuccess: () => {
24
+ queryClient.invalidateQueries({ queryKey: ['goals'] })
25
+ },
26
+ })
27
+ }
28
+
29
+ export function useUpdateGoal() {
30
+ const queryClient = useQueryClient()
31
+ return useMutation({
32
+ mutationFn: ({ id, ...data }: Partial<Goal> & { id: string }) =>
33
+ apiFetch<Goal>(`/api/goals/${id}`, {
34
+ method: 'PUT',
35
+ headers: { 'Content-Type': 'application/json' },
36
+ body: JSON.stringify(data),
37
+ }),
38
+ onSuccess: () => {
39
+ queryClient.invalidateQueries({ queryKey: ['goals'] })
40
+ },
41
+ })
42
+ }
43
+
44
+ export function useDeleteGoal() {
45
+ const queryClient = useQueryClient()
46
+ return useMutation({
47
+ mutationFn: (id: string) =>
48
+ apiFetch(`/api/goals/${id}`, { method: 'DELETE' }),
49
+ onSuccess: () => {
50
+ queryClient.invalidateQueries({ queryKey: ['goals'] })
51
+ },
52
+ })
53
+ }
@@ -0,0 +1,75 @@
1
+ 'use client'
2
+
3
+ import { useMemo } from 'react'
4
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
5
+ import { apiFetch } from '@/lib/apiFetch'
6
+ import type { Milestone, MilestoneWithDDay } from '@/types'
7
+
8
+ export function useMilestones(goalId?: string) {
9
+ return useQuery({
10
+ queryKey: ['milestones', goalId],
11
+ queryFn: () => {
12
+ const url = goalId ? `/api/milestones?goal_id=${goalId}` : '/api/milestones'
13
+ return apiFetch<Milestone[]>(url)
14
+ },
15
+ })
16
+ }
17
+
18
+ export function useMilestonesWithDDay(goalId?: string) {
19
+ const query = useMilestones(goalId)
20
+
21
+ const withDDay = useMemo(() =>
22
+ (query.data ?? [])
23
+ .map(m => ({
24
+ ...m,
25
+ d_day: Math.ceil(
26
+ (new Date(m.due_date).getTime() - Date.now()) / (1000 * 60 * 60 * 24)
27
+ ),
28
+ }))
29
+ .sort((a, b) => a.d_day - b.d_day),
30
+ [query.data]
31
+ )
32
+
33
+ return { ...query, data: withDDay }
34
+ }
35
+
36
+ export function useCreateMilestone() {
37
+ const queryClient = useQueryClient()
38
+ return useMutation({
39
+ mutationFn: (data: Partial<Milestone>) =>
40
+ apiFetch<Milestone>('/api/milestones', {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/json' },
43
+ body: JSON.stringify(data),
44
+ }),
45
+ onSuccess: () => {
46
+ queryClient.invalidateQueries({ queryKey: ['milestones'] })
47
+ },
48
+ })
49
+ }
50
+
51
+ export function useUpdateMilestone() {
52
+ const queryClient = useQueryClient()
53
+ return useMutation({
54
+ mutationFn: ({ id, ...data }: Partial<Milestone> & { id: string }) =>
55
+ apiFetch<Milestone>(`/api/milestones/${id}`, {
56
+ method: 'PUT',
57
+ headers: { 'Content-Type': 'application/json' },
58
+ body: JSON.stringify(data),
59
+ }),
60
+ onSuccess: () => {
61
+ queryClient.invalidateQueries({ queryKey: ['milestones'] })
62
+ },
63
+ })
64
+ }
65
+
66
+ export function useDeleteMilestone() {
67
+ const queryClient = useQueryClient()
68
+ return useMutation({
69
+ mutationFn: (id: string) =>
70
+ apiFetch(`/api/milestones/${id}`, { method: 'DELETE' }),
71
+ onSuccess: () => {
72
+ queryClient.invalidateQueries({ queryKey: ['milestones'] })
73
+ },
74
+ })
75
+ }
@@ -0,0 +1,65 @@
1
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
2
+ import { apiFetch } from '@/lib/apiFetch'
3
+ import type { Note, NoteType } from '@/types'
4
+
5
+ export function useNote(id: string) {
6
+ return useQuery({
7
+ queryKey: ['note', id],
8
+ queryFn: () => apiFetch<Note & { _content?: string }>(`/api/notes/${id}`),
9
+ enabled: !!id,
10
+ })
11
+ }
12
+
13
+ export function useNotes(type?: NoteType, areaId?: string) {
14
+ return useQuery({
15
+ queryKey: ['notes', type, areaId],
16
+ queryFn: () => {
17
+ const params = new URLSearchParams()
18
+ if (type) params.set('type', type)
19
+ if (areaId) params.set('area_id', areaId)
20
+ return apiFetch<(Note & { _content?: string })[]>(`/api/notes?${params}`)
21
+ },
22
+ })
23
+ }
24
+
25
+ export function useCreateNote() {
26
+ const queryClient = useQueryClient()
27
+ return useMutation({
28
+ mutationFn: (data: Partial<Note> & { content?: string }) =>
29
+ apiFetch<Note>('/api/notes', {
30
+ method: 'POST',
31
+ headers: { 'Content-Type': 'application/json' },
32
+ body: JSON.stringify(data),
33
+ }),
34
+ onSuccess: () => {
35
+ queryClient.invalidateQueries({ queryKey: ['notes'] })
36
+ },
37
+ })
38
+ }
39
+
40
+ export function useUpdateNote() {
41
+ const queryClient = useQueryClient()
42
+ return useMutation({
43
+ mutationFn: ({ id, ...data }: Partial<Note> & { id: string; content?: string }) =>
44
+ apiFetch<Note>(`/api/notes/${id}`, {
45
+ method: 'PUT',
46
+ headers: { 'Content-Type': 'application/json' },
47
+ body: JSON.stringify(data),
48
+ }),
49
+ onSuccess: (_, variables) => {
50
+ queryClient.invalidateQueries({ queryKey: ['notes'] })
51
+ if (variables.id) queryClient.invalidateQueries({ queryKey: ['note', variables.id] })
52
+ },
53
+ })
54
+ }
55
+
56
+ export function useDeleteNote() {
57
+ const queryClient = useQueryClient()
58
+ return useMutation({
59
+ mutationFn: (id: string) =>
60
+ apiFetch(`/api/notes/${id}`, { method: 'DELETE' }),
61
+ onSuccess: () => {
62
+ queryClient.invalidateQueries({ queryKey: ['notes'] })
63
+ },
64
+ })
65
+ }
@@ -0,0 +1,102 @@
1
+ 'use client'
2
+
3
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
+ import { apiFetch } from '@/lib/apiFetch'
5
+ import type { Project, Task } from '@/types'
6
+
7
+ export function useProjects() {
8
+ return useQuery({
9
+ queryKey: ['projects'],
10
+ queryFn: () => apiFetch<Project[]>('/api/projects'),
11
+ })
12
+ }
13
+
14
+ export function useTask(id: string) {
15
+ return useQuery({
16
+ queryKey: ['task', id],
17
+ queryFn: () => apiFetch<Task & { _content?: string }>(`/api/tasks/${id}`),
18
+ enabled: !!id,
19
+ })
20
+ }
21
+
22
+ export function useTasks(projectId?: string) {
23
+ return useQuery({
24
+ queryKey: ['tasks', projectId],
25
+ queryFn: () => {
26
+ const url = projectId ? `/api/tasks?project_id=${projectId}` : '/api/tasks'
27
+ return apiFetch<Task[]>(url)
28
+ },
29
+ })
30
+ }
31
+
32
+ export function useCreateTask() {
33
+ const queryClient = useQueryClient()
34
+ return useMutation({
35
+ mutationFn: (data: Partial<Task>) =>
36
+ apiFetch<Task>('/api/tasks', {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify(data),
40
+ }),
41
+ onSuccess: (_, variables) => {
42
+ queryClient.invalidateQueries({ queryKey: ['tasks', variables.project_id] })
43
+ },
44
+ })
45
+ }
46
+
47
+ export function useUpdateTask() {
48
+ const queryClient = useQueryClient()
49
+ return useMutation({
50
+ mutationFn: ({ id, ...data }: Partial<Task> & { id: string; content?: string }) =>
51
+ apiFetch<Task>(`/api/tasks/${id}`, {
52
+ method: 'PUT',
53
+ headers: { 'Content-Type': 'application/json' },
54
+ body: JSON.stringify(data),
55
+ }),
56
+ onSuccess: (_, variables) => {
57
+ queryClient.invalidateQueries({ queryKey: ['tasks'] })
58
+ queryClient.invalidateQueries({ queryKey: ['task', variables.id] })
59
+ },
60
+ })
61
+ }
62
+
63
+ export function useDeleteTask() {
64
+ const queryClient = useQueryClient()
65
+ return useMutation({
66
+ mutationFn: (id: string) =>
67
+ apiFetch(`/api/tasks/${id}`, { method: 'DELETE' }),
68
+ onSuccess: () => {
69
+ queryClient.invalidateQueries({ queryKey: ['tasks'] })
70
+ },
71
+ })
72
+ }
73
+
74
+ export function useCreateProject() {
75
+ const queryClient = useQueryClient()
76
+ return useMutation({
77
+ mutationFn: (data: Partial<Project>) =>
78
+ apiFetch<Project>('/api/projects', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify(data),
82
+ }),
83
+ onSuccess: () => {
84
+ queryClient.invalidateQueries({ queryKey: ['projects'] })
85
+ },
86
+ })
87
+ }
88
+
89
+ export function useUpdateProject() {
90
+ const queryClient = useQueryClient()
91
+ return useMutation({
92
+ mutationFn: ({ id, ...data }: Partial<Project> & { id: string }) =>
93
+ apiFetch<Project>(`/api/projects/${id}`, {
94
+ method: 'PUT',
95
+ headers: { 'Content-Type': 'application/json' },
96
+ body: JSON.stringify(data),
97
+ }),
98
+ onSuccess: () => {
99
+ queryClient.invalidateQueries({ queryKey: ['projects'] })
100
+ },
101
+ })
102
+ }
@@ -0,0 +1,76 @@
1
+ 'use client'
2
+
3
+ import { useMemo } from 'react'
4
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
5
+ import { apiFetch } from '@/lib/apiFetch'
6
+ import { getToday } from '@/lib/date'
7
+ import { buildHabitChain } from '@/lib/neuroscience/habitStack'
8
+ import type { Routine, RoutineLog, RoutineWithStatus } from '@/types'
9
+
10
+ export function useRoutines() {
11
+ const today = getToday()
12
+
13
+ const routinesQuery = useQuery({
14
+ queryKey: ['routines'],
15
+ queryFn: () => apiFetch<Routine[]>('/api/routines'),
16
+ })
17
+
18
+ const logsQuery = useQuery({
19
+ queryKey: ['routine-logs', today],
20
+ queryFn: () => apiFetch<RoutineLog>(`/api/logs/routine?date=${today}`),
21
+ })
22
+
23
+ const routinesWithStatus = useMemo<RoutineWithStatus[]>(() =>
24
+ (routinesQuery.data ?? [])
25
+ .filter(r => r.is_active)
26
+ .map(r => ({
27
+ ...r,
28
+ completed_today: logsQuery.data?.completions?.some(c => c.routine_id === r.id) ?? false,
29
+ })),
30
+ [routinesQuery.data, logsQuery.data]
31
+ )
32
+
33
+ const chains = useMemo(() => buildHabitChain(routinesWithStatus), [routinesWithStatus])
34
+
35
+ return {
36
+ chains,
37
+ routines: routinesWithStatus,
38
+ isLoading: routinesQuery.isLoading || logsQuery.isLoading,
39
+ }
40
+ }
41
+
42
+ export function useToggleRoutine() {
43
+ const queryClient = useQueryClient()
44
+ const today = getToday()
45
+
46
+ return useMutation({
47
+ mutationFn: ({ routineId, completed }: { routineId: string; completed: boolean }) =>
48
+ apiFetch('/api/logs/routine', {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({ routine_id: routineId, undo: completed, date: today }),
52
+ }),
53
+ onMutate: async ({ routineId, completed }) => {
54
+ await queryClient.cancelQueries({ queryKey: ['routine-logs', today] })
55
+ const prev = queryClient.getQueryData<RoutineLog>(['routine-logs', today])
56
+
57
+ queryClient.setQueryData<RoutineLog>(['routine-logs', today], (old) => {
58
+ if (!old) return { date: today, completions: completed ? [] : [{ routine_id: routineId, completed_at: new Date().toISOString() }] }
59
+ return {
60
+ ...old,
61
+ completions: completed
62
+ ? old.completions.filter(c => c.routine_id !== routineId)
63
+ : [...old.completions, { routine_id: routineId, completed_at: new Date().toISOString() }],
64
+ }
65
+ })
66
+
67
+ return { prev }
68
+ },
69
+ onError: (_, __, ctx) => {
70
+ if (ctx?.prev) queryClient.setQueryData(['routine-logs', today], ctx.prev)
71
+ },
72
+ onSettled: () => {
73
+ queryClient.invalidateQueries({ queryKey: ['routine-logs', today] })
74
+ },
75
+ })
76
+ }