prjct-cli 0.18.2 → 0.20.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 (246) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/CLAUDE.md +74 -211
  3. package/core/agentic/prompt-builder.ts +3 -7
  4. package/core/command-registry/optional-commands.ts +0 -20
  5. package/core/infrastructure/command-installer/command-installer.ts +8 -1
  6. package/core/infrastructure/command-installer/global-config.ts +31 -1
  7. package/core/infrastructure/command-installer/index.ts +1 -1
  8. package/core/infrastructure/setup.ts +3 -0
  9. package/package.json +3 -17
  10. package/templates/agentic/agents/uxui.md +210 -0
  11. package/templates/commands/bug.md +219 -41
  12. package/templates/commands/done.md +57 -258
  13. package/templates/commands/feature.md +368 -80
  14. package/templates/commands/now.md +72 -277
  15. package/templates/commands/ship.md +167 -246
  16. package/templates/commands/sync.md +62 -3
  17. package/templates/commands/test.md +160 -20
  18. package/templates/global/CLAUDE.md +40 -205
  19. package/templates/global/docs/agents.md +88 -0
  20. package/templates/global/docs/architecture.md +103 -0
  21. package/templates/global/docs/commands.md +98 -0
  22. package/templates/global/docs/validation.md +95 -0
  23. package/bin/dev.js +0 -216
  24. package/bin/serve.js +0 -361
  25. package/packages/web/README.md +0 -36
  26. package/packages/web/app/api/claude/sessions/route.ts +0 -44
  27. package/packages/web/app/api/claude/status/route.ts +0 -34
  28. package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
  29. package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
  30. package/packages/web/app/api/projects/[id]/route.ts +0 -29
  31. package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
  32. package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
  33. package/packages/web/app/api/projects/route.ts +0 -16
  34. package/packages/web/app/api/sessions/current/route.ts +0 -132
  35. package/packages/web/app/api/sessions/history/route.ts +0 -204
  36. package/packages/web/app/error.tsx +0 -34
  37. package/packages/web/app/favicon.ico +0 -0
  38. package/packages/web/app/globals.css +0 -198
  39. package/packages/web/app/layout.tsx +0 -53
  40. package/packages/web/app/loading.tsx +0 -7
  41. package/packages/web/app/not-found.tsx +0 -25
  42. package/packages/web/app/page.tsx +0 -12
  43. package/packages/web/app/project/[id]/code/layout.tsx +0 -18
  44. package/packages/web/app/project/[id]/code/page.tsx +0 -408
  45. package/packages/web/app/project/[id]/error.tsx +0 -41
  46. package/packages/web/app/project/[id]/loading.tsx +0 -9
  47. package/packages/web/app/project/[id]/not-found.tsx +0 -27
  48. package/packages/web/app/project/[id]/page.tsx +0 -384
  49. package/packages/web/app/project/[id]/reports/page.tsx +0 -59
  50. package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
  51. package/packages/web/app/sessions/page.tsx +0 -165
  52. package/packages/web/app/settings/page.tsx +0 -151
  53. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
  54. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
  55. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
  56. package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
  57. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
  58. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
  59. package/packages/web/components/ActivityTimeline/index.ts +0 -2
  60. package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
  61. package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
  62. package/packages/web/components/AgentsCard/index.ts +0 -2
  63. package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
  64. package/packages/web/components/AppSidebar/index.ts +0 -1
  65. package/packages/web/components/BackLink/BackLink.tsx +0 -18
  66. package/packages/web/components/BackLink/BackLink.types.ts +0 -5
  67. package/packages/web/components/BackLink/index.ts +0 -2
  68. package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
  69. package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
  70. package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
  71. package/packages/web/components/BentoCard/index.ts +0 -2
  72. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
  73. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
  74. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
  75. package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
  76. package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
  77. package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
  78. package/packages/web/components/BentoGrid/index.ts +0 -2
  79. package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
  80. package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
  81. package/packages/web/components/BlockersCard/index.ts +0 -2
  82. package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
  83. package/packages/web/components/CommandBar/index.ts +0 -1
  84. package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
  85. package/packages/web/components/CommandButton/index.ts +0 -1
  86. package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
  87. package/packages/web/components/ConnectionStatus/index.ts +0 -1
  88. package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
  89. package/packages/web/components/DashboardContent/index.ts +0 -1
  90. package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
  91. package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
  92. package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
  93. package/packages/web/components/DateGroup/index.ts +0 -2
  94. package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
  95. package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
  96. package/packages/web/components/EmptyState/index.ts +0 -2
  97. package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
  98. package/packages/web/components/EventRow/EventRow.tsx +0 -49
  99. package/packages/web/components/EventRow/EventRow.types.ts +0 -7
  100. package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
  101. package/packages/web/components/EventRow/index.ts +0 -2
  102. package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
  103. package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
  104. package/packages/web/components/ExpandButton/index.ts +0 -2
  105. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
  106. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
  107. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
  108. package/packages/web/components/HealthGradientBackground/index.ts +0 -2
  109. package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
  110. package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
  111. package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
  112. package/packages/web/components/HeroSection/hooks/index.ts +0 -2
  113. package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
  114. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
  115. package/packages/web/components/HeroSection/index.ts +0 -2
  116. package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
  117. package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
  118. package/packages/web/components/IdeasCard/index.ts +0 -2
  119. package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
  120. package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
  121. package/packages/web/components/InsightMessage/index.ts +0 -2
  122. package/packages/web/components/Logo/Logo.tsx +0 -65
  123. package/packages/web/components/Logo/index.ts +0 -1
  124. package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
  125. package/packages/web/components/MarkdownContent/index.ts +0 -1
  126. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
  127. package/packages/web/components/MasonryGrid/index.ts +0 -1
  128. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
  129. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
  130. package/packages/web/components/MomentumWidget/index.ts +0 -2
  131. package/packages/web/components/NowCard/NowCard.tsx +0 -118
  132. package/packages/web/components/NowCard/NowCard.types.ts +0 -16
  133. package/packages/web/components/NowCard/index.ts +0 -2
  134. package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
  135. package/packages/web/components/PageHeader/index.ts +0 -1
  136. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
  137. package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
  138. package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
  139. package/packages/web/components/ProgressRing/index.ts +0 -2
  140. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
  141. package/packages/web/components/ProjectAvatar/index.ts +0 -1
  142. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
  143. package/packages/web/components/ProjectColorDot/index.ts +0 -1
  144. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
  145. package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
  146. package/packages/web/components/Providers/Providers.tsx +0 -48
  147. package/packages/web/components/Providers/index.ts +0 -1
  148. package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
  149. package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
  150. package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
  151. package/packages/web/components/QueueCard/index.ts +0 -2
  152. package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
  153. package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
  154. package/packages/web/components/RecoverCard/index.ts +0 -2
  155. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
  156. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
  157. package/packages/web/components/RoadmapCard/index.ts +0 -2
  158. package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
  159. package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
  160. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
  161. package/packages/web/components/ShipsCard/index.ts +0 -2
  162. package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
  163. package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
  164. package/packages/web/components/SparklineChart/index.ts +0 -2
  165. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
  166. package/packages/web/components/StatsMasonry/index.ts +0 -1
  167. package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
  168. package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
  169. package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
  170. package/packages/web/components/StreakCard/index.ts +0 -2
  171. package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
  172. package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
  173. package/packages/web/components/TasksCounter/index.ts +0 -2
  174. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
  175. package/packages/web/components/TechStackBadges/index.ts +0 -1
  176. package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
  177. package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
  178. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
  179. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
  180. package/packages/web/components/TerminalDock/index.ts +0 -2
  181. package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
  182. package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
  183. package/packages/web/components/TerminalTabs/index.ts +0 -1
  184. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
  185. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
  186. package/packages/web/components/VelocityBadge/index.ts +0 -2
  187. package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
  188. package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
  189. package/packages/web/components/VelocityCard/index.ts +0 -2
  190. package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
  191. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
  192. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
  193. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
  194. package/packages/web/components/WeeklyReports/index.ts +0 -4
  195. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
  196. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
  197. package/packages/web/components/WeeklySparkline/index.ts +0 -2
  198. package/packages/web/components/charts/SessionsChart.tsx +0 -175
  199. package/packages/web/components/ui/alert-dialog.tsx +0 -157
  200. package/packages/web/components/ui/badge.tsx +0 -46
  201. package/packages/web/components/ui/button.tsx +0 -60
  202. package/packages/web/components/ui/card.tsx +0 -92
  203. package/packages/web/components/ui/chart.tsx +0 -385
  204. package/packages/web/components/ui/dialog.tsx +0 -143
  205. package/packages/web/components/ui/drawer.tsx +0 -135
  206. package/packages/web/components/ui/dropdown-menu.tsx +0 -257
  207. package/packages/web/components/ui/input.tsx +0 -21
  208. package/packages/web/components/ui/scroll-area.tsx +0 -58
  209. package/packages/web/components/ui/select.tsx +0 -187
  210. package/packages/web/components/ui/sheet.tsx +0 -139
  211. package/packages/web/components/ui/tabs.tsx +0 -66
  212. package/packages/web/components/ui/tooltip.tsx +0 -61
  213. package/packages/web/components.json +0 -22
  214. package/packages/web/context/GlobalTerminalContext.tsx +0 -538
  215. package/packages/web/context/TerminalContext.tsx +0 -45
  216. package/packages/web/context/TerminalTabsContext.tsx +0 -181
  217. package/packages/web/eslint.config.mjs +0 -18
  218. package/packages/web/hooks/useClaudeTerminal.ts +0 -425
  219. package/packages/web/hooks/useProjectStats.ts +0 -93
  220. package/packages/web/hooks/useProjects.ts +0 -73
  221. package/packages/web/lib/actions/projects.ts +0 -15
  222. package/packages/web/lib/commands.ts +0 -81
  223. package/packages/web/lib/format.ts +0 -23
  224. package/packages/web/lib/generate-week-report.ts +0 -285
  225. package/packages/web/lib/parse-prjct-files.ts +0 -1123
  226. package/packages/web/lib/project-colors.ts +0 -58
  227. package/packages/web/lib/projects.ts +0 -506
  228. package/packages/web/lib/pty.ts +0 -101
  229. package/packages/web/lib/query-config.ts +0 -44
  230. package/packages/web/lib/services/index.ts +0 -9
  231. package/packages/web/lib/services/projects.server.ts +0 -66
  232. package/packages/web/lib/services/stats.server.ts +0 -562
  233. package/packages/web/lib/unified-loader.ts +0 -396
  234. package/packages/web/lib/utils.ts +0 -6
  235. package/packages/web/next-env.d.ts +0 -6
  236. package/packages/web/next.config.ts +0 -7
  237. package/packages/web/package.json +0 -57
  238. package/packages/web/postcss.config.mjs +0 -7
  239. package/packages/web/public/file.svg +0 -1
  240. package/packages/web/public/globe.svg +0 -1
  241. package/packages/web/public/next.svg +0 -1
  242. package/packages/web/public/vercel.svg +0 -1
  243. package/packages/web/public/window.svg +0 -1
  244. package/packages/web/server.ts +0 -312
  245. package/packages/web/tsconfig.json +0 -34
  246. package/templates/commands/serve.md +0 -121
@@ -1,312 +0,0 @@
1
- /**
2
- * Custom Next.js server with WebSocket support for PTY
3
- *
4
- * PTY sessions are managed here (not in API routes) to share memory context
5
- */
6
-
7
- import { createServer } from 'http'
8
- import next from 'next'
9
- import { WebSocketServer, WebSocket } from 'ws'
10
- import * as pty from 'node-pty'
11
- import type { IPty } from 'node-pty'
12
-
13
- const dev = process.env.NODE_ENV !== 'production'
14
- const hostname = 'localhost'
15
- // Dev: 9471, Prod: 9472 (allows running both simultaneously for testing)
16
- const defaultPort = dev ? 9471 : 9472
17
- const port = parseInt(process.env.PORT || String(defaultPort), 10)
18
-
19
- // PTY Sessions stored in server memory
20
- interface Session {
21
- pty: IPty
22
- projectDir: string
23
- createdAt: Date
24
- hasStartedClaude: boolean // Track if claude command was sent
25
- }
26
-
27
- const sessions = new Map<string, Session>()
28
-
29
- function createSession(sessionId: string, projectDir: string): { pty: IPty; isNew: boolean } {
30
- const existing = sessions.get(sessionId)
31
-
32
- // If session exists for this project, reuse it (allows multiple tabs)
33
- if (existing) {
34
- console.log(`[PTY] Reusing existing session: ${sessionId}`)
35
- return { pty: existing.pty, isNew: false }
36
- }
37
-
38
- // Use user's default shell (zsh, bash, etc.) - respects $SHELL env var
39
- const shell = process.platform === 'win32'
40
- ? 'cmd.exe'
41
- : process.env.SHELL || '/bin/zsh'
42
-
43
- // -l for login shell (loads .zshrc, .bashrc, etc.)
44
- // -i for interactive shell (enables job control, aliases)
45
- const args = process.platform === 'win32' ? [] : ['-l', '-i']
46
-
47
- const ptyProcess = pty.spawn(shell, args, {
48
- name: 'xterm-256color',
49
- cols: 120,
50
- rows: 30,
51
- cwd: projectDir,
52
- env: {
53
- ...process.env,
54
- TERM: 'xterm-256color',
55
- COLORTERM: 'truecolor',
56
- // Ensure shell knows it's interactive
57
- SHELL: shell
58
- }
59
- })
60
-
61
- sessions.set(sessionId, {
62
- pty: ptyProcess,
63
- projectDir,
64
- createdAt: new Date(),
65
- hasStartedClaude: false
66
- })
67
-
68
- // NOTE: Don't auto-start claude here - let the WebSocket handler do it
69
- // once the client is connected and ready to receive output
70
-
71
- return { pty: ptyProcess, isNew: true }
72
- }
73
-
74
- function getSession(sessionId: string): IPty | null {
75
- return sessions.get(sessionId)?.pty || null
76
- }
77
-
78
- function killSession(sessionId: string): void {
79
- const session = sessions.get(sessionId)
80
- if (session) {
81
- try { session.pty.kill() } catch {}
82
- sessions.delete(sessionId)
83
- }
84
- }
85
-
86
- function resizeSession(sessionId: string, cols: number, rows: number): void {
87
- const session = sessions.get(sessionId)
88
- if (session) {
89
- try { session.pty.resize(cols, rows) } catch {}
90
- }
91
- }
92
-
93
- const app = next({ dev, hostname, port })
94
- const handle = app.getRequestHandler()
95
-
96
- app.prepare().then(() => {
97
- const server = createServer(async (req, res) => {
98
- const url = new URL(req.url || '', `http://${req.headers.host}`)
99
-
100
- // Handle session creation directly in server (bypasses API route isolation)
101
- if (url.pathname === '/api/claude/sessions' && req.method === 'POST') {
102
- let body = ''
103
- let bodySize = 0
104
- const MAX_BODY_SIZE = 1024 * 1024 // 1MB limit
105
-
106
- req.on('data', chunk => {
107
- bodySize += chunk.length
108
- if (bodySize > MAX_BODY_SIZE) {
109
- res.statusCode = 413
110
- res.setHeader('Content-Type', 'application/json')
111
- res.end(JSON.stringify({ success: false, error: 'Request body too large' }))
112
- req.destroy()
113
- return
114
- }
115
- body += chunk
116
- })
117
- req.on('end', () => {
118
- try {
119
- const { sessionId, projectDir } = JSON.parse(body)
120
- if (!sessionId || !projectDir) {
121
- res.statusCode = 400
122
- res.setHeader('Content-Type', 'application/json')
123
- res.end(JSON.stringify({ success: false, error: 'sessionId and projectDir required' }))
124
- return
125
- }
126
-
127
- const { isNew } = createSession(sessionId, projectDir)
128
- console.log(`[PTY] ${isNew ? 'Created' : 'Reusing'} session: ${sessionId} for ${projectDir}`)
129
-
130
- res.statusCode = 200
131
- res.setHeader('Content-Type', 'application/json')
132
- res.end(JSON.stringify({ success: true, data: { sessionId, projectDir, isNew } }))
133
- } catch (err) {
134
- console.error('[PTY] Error creating session:', err)
135
- res.statusCode = 500
136
- res.setHeader('Content-Type', 'application/json')
137
- res.end(JSON.stringify({ success: false, error: 'Failed to create session' }))
138
- }
139
- })
140
- return
141
- }
142
-
143
- // Handle session list
144
- if (url.pathname === '/api/claude/sessions' && req.method === 'GET') {
145
- const list = Array.from(sessions.entries()).map(([id, s]) => ({
146
- sessionId: id,
147
- projectDir: s.projectDir,
148
- createdAt: s.createdAt
149
- }))
150
- res.statusCode = 200
151
- res.setHeader('Content-Type', 'application/json')
152
- res.end(JSON.stringify({ success: true, data: list }))
153
- return
154
- }
155
-
156
- // All other requests go to Next.js
157
- try {
158
- await handle(req, res)
159
- } catch (err) {
160
- console.error('Error handling request:', err)
161
- res.statusCode = 500
162
- res.end('Internal Server Error')
163
- }
164
- })
165
-
166
- // WebSocket server for PTY communication
167
- const wss = new WebSocketServer({ noServer: true })
168
-
169
- // Heartbeat interval to detect dead connections (30 seconds)
170
- const HEARTBEAT_INTERVAL = 30000
171
-
172
- const heartbeatInterval = setInterval(() => {
173
- wss.clients.forEach((ws) => {
174
- const extWs = ws as WebSocket & { isAlive?: boolean; sessionId?: string }
175
- if (extWs.isAlive === false) {
176
- console.log(`[WS] Terminating dead connection: ${extWs.sessionId}`)
177
- if (extWs.sessionId) {
178
- killSession(extWs.sessionId)
179
- }
180
- return ws.terminate()
181
- }
182
- extWs.isAlive = false
183
- ws.ping()
184
- })
185
- }, HEARTBEAT_INTERVAL)
186
-
187
- // Cleanup on WSS close
188
- wss.on('close', () => {
189
- clearInterval(heartbeatInterval)
190
- })
191
-
192
- // Cleanup on server close
193
- server.on('close', () => {
194
- clearInterval(heartbeatInterval)
195
- // Kill all active sessions
196
- sessions.forEach((_, sessionId) => killSession(sessionId))
197
- })
198
-
199
- server.on('upgrade', (request, socket, head) => {
200
- const url = new URL(request.url || '', `http://${request.headers.host}`)
201
-
202
- if (url.pathname.startsWith('/ws/claude/')) {
203
- wss.handleUpgrade(request, socket, head, (ws) => {
204
- wss.emit('connection', ws, request)
205
- })
206
- }
207
- // Other upgrades (HMR) pass through to Next.js
208
- })
209
-
210
- wss.on('connection', (ws: WebSocket, request) => {
211
- const url = new URL(request.url || '', `http://${request.headers.host}`)
212
- const sessionId = url.pathname.replace('/ws/claude/', '')
213
-
214
- console.log(`[WS] New PTY connection for session: ${sessionId}`)
215
-
216
- // Mark connection as alive and store sessionId for heartbeat
217
- const extWs = ws as WebSocket & { isAlive?: boolean; sessionId?: string }
218
- extWs.isAlive = true
219
- extWs.sessionId = sessionId
220
-
221
- // Handle pong response
222
- ws.on('pong', () => {
223
- extWs.isAlive = true
224
- })
225
-
226
- const session = sessions.get(sessionId)
227
-
228
- if (!session) {
229
- console.log(`[WS] Session not found: ${sessionId}`)
230
- ws.send(JSON.stringify({ type: 'error', message: 'Session not found' }))
231
- ws.close()
232
- return
233
- }
234
-
235
- const ptyProcess = session.pty
236
-
237
- // Output buffering for smooth terminal rendering
238
- // Batches PTY output and flushes every 16ms (one animation frame)
239
- const outputBuffer: string[] = []
240
- let flushTimer: NodeJS.Timeout | null = null
241
- const BATCH_MS = 16
242
-
243
- const flushBuffer = () => {
244
- if (outputBuffer.length > 0 && ws.readyState === WebSocket.OPEN) {
245
- ws.send(JSON.stringify({ type: 'output', data: outputBuffer.join('') }))
246
- outputBuffer.length = 0
247
- }
248
- flushTimer = null
249
- }
250
-
251
- // Register data handler FIRST before sending any commands
252
- const dataHandler = ptyProcess.onData((data: string) => {
253
- outputBuffer.push(data)
254
- if (!flushTimer) {
255
- flushTimer = setTimeout(flushBuffer, BATCH_MS)
256
- }
257
- })
258
-
259
- ws.send(JSON.stringify({ type: 'connected', sessionId }))
260
-
261
- // Auto-start Claude CLI only once, when first client connects
262
- if (!session.hasStartedClaude) {
263
- session.hasStartedClaude = true
264
- console.log(`[WS] Starting Claude CLI for session: ${sessionId}`)
265
- setTimeout(() => {
266
- ptyProcess.write('claude\r')
267
- }, 200) // Small delay to ensure client is ready
268
- }
269
-
270
- const exitHandler = ptyProcess.onExit(({ exitCode }) => {
271
- if (ws.readyState === WebSocket.OPEN) {
272
- ws.send(JSON.stringify({ type: 'exit', code: exitCode }))
273
- }
274
- killSession(sessionId)
275
- })
276
-
277
- ws.on('message', (message: Buffer) => {
278
- try {
279
- const { type, data, cols, rows } = JSON.parse(message.toString())
280
- switch (type) {
281
- case 'input':
282
- ptyProcess?.write(data)
283
- break
284
- case 'resize':
285
- if (cols && rows) resizeSession(sessionId, cols, rows)
286
- break
287
- }
288
- } catch (err) {
289
- console.error('[WS] Error:', err)
290
- }
291
- })
292
-
293
- ws.on('close', () => {
294
- console.log(`[WS] PTY connection closed: ${sessionId}`)
295
- // Clear buffer flush timer
296
- if (flushTimer) {
297
- clearTimeout(flushTimer)
298
- flushTimer = null
299
- }
300
- dataHandler.dispose()
301
- exitHandler.dispose()
302
- })
303
-
304
- ws.on('error', (error) => {
305
- console.error(`[WS] Error for ${sessionId}:`, error)
306
- })
307
- })
308
-
309
- server.listen(port, () => {
310
- console.log(`> prjct ready on http://${hostname}:${port}`)
311
- })
312
- })
@@ -1,34 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2017",
4
- "lib": ["dom", "dom.iterable", "esnext"],
5
- "allowJs": true,
6
- "skipLibCheck": true,
7
- "strict": true,
8
- "noEmit": true,
9
- "esModuleInterop": true,
10
- "module": "esnext",
11
- "moduleResolution": "bundler",
12
- "resolveJsonModule": true,
13
- "isolatedModules": true,
14
- "jsx": "react-jsx",
15
- "incremental": true,
16
- "plugins": [
17
- {
18
- "name": "next"
19
- }
20
- ],
21
- "paths": {
22
- "@/*": ["./*"]
23
- }
24
- },
25
- "include": [
26
- "next-env.d.ts",
27
- "**/*.ts",
28
- "**/*.tsx",
29
- ".next/types/**/*.ts",
30
- ".next/dev/types/**/*.ts",
31
- "**/*.mts"
32
- ],
33
- "exclude": ["node_modules"]
34
- }
@@ -1,121 +0,0 @@
1
- ---
2
- allowed-tools: [Bash]
3
- description: 'Start prjct web server'
4
- ---
5
-
6
- # /p:serve - Start Web Server
7
-
8
- ## Overview
9
- Starts the prjct web interface with Claude Code CLI integration.
10
-
11
- **CRITICAL**: Uses your Claude subscription via PTY - NO API costs!
12
-
13
- ## Usage
14
-
15
- ```bash
16
- # Start with defaults (port 3333)
17
- prjct-serve
18
-
19
- # Custom port
20
- prjct-serve --port=8080
21
- ```
22
-
23
- ## What It Does
24
-
25
- 1. **Starts API Server** (Hono)
26
- - REST endpoints for projects, sessions, tasks
27
- - WebSocket for Claude Code CLI interaction
28
- - SSE for real-time updates
29
-
30
- 2. **Starts Web App** (React + Vite)
31
- - Dashboard with metrics
32
- - Terminal with xterm.js
33
- - Session history
34
-
35
- 3. **PTY Manager**
36
- - Spawns Claude Code CLI in pseudo-terminal
37
- - Streams I/O via WebSocket
38
- - Uses YOUR subscription - $0 API costs
39
-
40
- ## Architecture
41
-
42
- ```
43
- ┌─────────────────────────────────────────────────────────┐
44
- │ Browser (localhost:3000) │
45
- │ ┌─────────────────────────────────────────────────┐ │
46
- │ │ React App │ │
47
- │ │ ├── Dashboard (metrics, projects) │ │
48
- │ │ ├── Terminal (xterm.js) │ │
49
- │ │ └── Sessions (history) │ │
50
- │ └─────────────────────────────────────────────────┘ │
51
- │ │ │
52
- │ WebSocket │
53
- │ │ │
54
- │ ┌─────────────────────────────────────────────────┐ │
55
- │ │ Hono Server (localhost:3333) │ │
56
- │ │ ├── /api/projects │ │
57
- │ │ ├── /api/sessions │ │
58
- │ │ ├── /api/claude/sessions │ │
59
- │ │ └── /ws/claude/:sessionId │ │
60
- │ └─────────────────────────────────────────────────┘ │
61
- │ │ │
62
- │ PTY (node-pty) │
63
- │ │ │
64
- │ ┌─────────────────────────────────────────────────┐ │
65
- │ │ Claude Code CLI │ │
66
- │ │ (your subscription, no API costs) │ │
67
- │ └─────────────────────────────────────────────────┘ │
68
- └─────────────────────────────────────────────────────────┘
69
- ```
70
-
71
- ## Endpoints
72
-
73
- ### REST API
74
-
75
- | Endpoint | Method | Description |
76
- |----------|--------|-------------|
77
- | `/api/projects` | GET | List all projects |
78
- | `/api/projects/:id` | GET | Get project details |
79
- | `/api/projects/:id/status` | GET | Get project status |
80
- | `/api/sessions` | GET | List sessions |
81
- | `/api/sessions/current` | GET | Get current session |
82
- | `/api/sessions` | POST | Create session |
83
- | `/api/sessions/:id/pause` | POST | Pause session |
84
- | `/api/sessions/:id/resume` | POST | Resume session |
85
- | `/api/sessions/:id/complete` | POST | Complete session |
86
- | `/api/claude/status` | GET | Check Claude CLI availability |
87
- | `/api/claude/sessions` | GET | List PTY sessions |
88
- | `/api/claude/sessions` | POST | Create PTY session |
89
-
90
- ### WebSocket
91
-
92
- | Endpoint | Description |
93
- |----------|-------------|
94
- | `/ws/claude/:sessionId` | Real-time Claude CLI interaction |
95
-
96
- **Messages:**
97
- - `{ type: 'input', data: string }` - Send input to CLI
98
- - `{ type: 'resize', cols: number, rows: number }` - Resize terminal
99
- - `{ type: 'output', data: string }` - Receive CLI output
100
-
101
- ## Requirements
102
-
103
- - Node.js 18+
104
- - Claude Code CLI installed (`claude` command available)
105
- - Active Claude subscription (Max or similar)
106
-
107
- ## Output
108
-
109
- ```
110
- ╔═══════════════════════════════════════════════════════════╗
111
- ║ ║
112
- ║ ⚡ prjct - Developer Momentum ║
113
- ║ ║
114
- ║ API: http://localhost:3333 ║
115
- ║ Web: http://localhost:3000 ║
116
- ║ Claude: ws://localhost:3333/ws/claude ║
117
- ║ ║
118
- ║ Using your Claude subscription - $0 API costs ║
119
- ║ ║
120
- ╚═══════════════════════════════════════════════════════════╝
121
- ```