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.
- package/CHANGELOG.md +82 -0
- package/CLAUDE.md +74 -211
- package/core/agentic/prompt-builder.ts +3 -7
- package/core/command-registry/optional-commands.ts +0 -20
- package/core/infrastructure/command-installer/command-installer.ts +8 -1
- package/core/infrastructure/command-installer/global-config.ts +31 -1
- package/core/infrastructure/command-installer/index.ts +1 -1
- package/core/infrastructure/setup.ts +3 -0
- package/package.json +3 -17
- package/templates/agentic/agents/uxui.md +210 -0
- package/templates/commands/bug.md +219 -41
- package/templates/commands/done.md +57 -258
- package/templates/commands/feature.md +368 -80
- package/templates/commands/now.md +72 -277
- package/templates/commands/ship.md +167 -246
- package/templates/commands/sync.md +62 -3
- package/templates/commands/test.md +160 -20
- package/templates/global/CLAUDE.md +40 -205
- package/templates/global/docs/agents.md +88 -0
- package/templates/global/docs/architecture.md +103 -0
- package/templates/global/docs/commands.md +98 -0
- package/templates/global/docs/validation.md +95 -0
- package/bin/dev.js +0 -216
- package/bin/serve.js +0 -361
- package/packages/web/README.md +0 -36
- package/packages/web/app/api/claude/sessions/route.ts +0 -44
- package/packages/web/app/api/claude/status/route.ts +0 -34
- package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
- package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
- package/packages/web/app/api/projects/[id]/route.ts +0 -29
- package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
- package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
- package/packages/web/app/api/projects/route.ts +0 -16
- package/packages/web/app/api/sessions/current/route.ts +0 -132
- package/packages/web/app/api/sessions/history/route.ts +0 -204
- package/packages/web/app/error.tsx +0 -34
- package/packages/web/app/favicon.ico +0 -0
- package/packages/web/app/globals.css +0 -198
- package/packages/web/app/layout.tsx +0 -53
- package/packages/web/app/loading.tsx +0 -7
- package/packages/web/app/not-found.tsx +0 -25
- package/packages/web/app/page.tsx +0 -12
- package/packages/web/app/project/[id]/code/layout.tsx +0 -18
- package/packages/web/app/project/[id]/code/page.tsx +0 -408
- package/packages/web/app/project/[id]/error.tsx +0 -41
- package/packages/web/app/project/[id]/loading.tsx +0 -9
- package/packages/web/app/project/[id]/not-found.tsx +0 -27
- package/packages/web/app/project/[id]/page.tsx +0 -384
- package/packages/web/app/project/[id]/reports/page.tsx +0 -59
- package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
- package/packages/web/app/sessions/page.tsx +0 -165
- package/packages/web/app/settings/page.tsx +0 -151
- package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
- package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
- package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
- package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
- package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
- package/packages/web/components/ActivityTimeline/index.ts +0 -2
- package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
- package/packages/web/components/AgentsCard/index.ts +0 -2
- package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
- package/packages/web/components/AppSidebar/index.ts +0 -1
- package/packages/web/components/BackLink/BackLink.tsx +0 -18
- package/packages/web/components/BackLink/BackLink.types.ts +0 -5
- package/packages/web/components/BackLink/index.ts +0 -2
- package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
- package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
- package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
- package/packages/web/components/BentoCard/index.ts +0 -2
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
- package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
- package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
- package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
- package/packages/web/components/BentoGrid/index.ts +0 -2
- package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
- package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
- package/packages/web/components/BlockersCard/index.ts +0 -2
- package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
- package/packages/web/components/CommandBar/index.ts +0 -1
- package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
- package/packages/web/components/CommandButton/index.ts +0 -1
- package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
- package/packages/web/components/ConnectionStatus/index.ts +0 -1
- package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
- package/packages/web/components/DashboardContent/index.ts +0 -1
- package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
- package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
- package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
- package/packages/web/components/DateGroup/index.ts +0 -2
- package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
- package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
- package/packages/web/components/EmptyState/index.ts +0 -2
- package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
- package/packages/web/components/EventRow/EventRow.tsx +0 -49
- package/packages/web/components/EventRow/EventRow.types.ts +0 -7
- package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
- package/packages/web/components/EventRow/index.ts +0 -2
- package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
- package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
- package/packages/web/components/ExpandButton/index.ts +0 -2
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
- package/packages/web/components/HealthGradientBackground/index.ts +0 -2
- package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
- package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
- package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
- package/packages/web/components/HeroSection/hooks/index.ts +0 -2
- package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
- package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
- package/packages/web/components/HeroSection/index.ts +0 -2
- package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
- package/packages/web/components/IdeasCard/index.ts +0 -2
- package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
- package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
- package/packages/web/components/InsightMessage/index.ts +0 -2
- package/packages/web/components/Logo/Logo.tsx +0 -65
- package/packages/web/components/Logo/index.ts +0 -1
- package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
- package/packages/web/components/MarkdownContent/index.ts +0 -1
- package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
- package/packages/web/components/MasonryGrid/index.ts +0 -1
- package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
- package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
- package/packages/web/components/MomentumWidget/index.ts +0 -2
- package/packages/web/components/NowCard/NowCard.tsx +0 -118
- package/packages/web/components/NowCard/NowCard.types.ts +0 -16
- package/packages/web/components/NowCard/index.ts +0 -2
- package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
- package/packages/web/components/PageHeader/index.ts +0 -1
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
- package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
- package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
- package/packages/web/components/ProgressRing/index.ts +0 -2
- package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
- package/packages/web/components/ProjectAvatar/index.ts +0 -1
- package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
- package/packages/web/components/ProjectColorDot/index.ts +0 -1
- package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
- package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
- package/packages/web/components/Providers/Providers.tsx +0 -48
- package/packages/web/components/Providers/index.ts +0 -1
- package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
- package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
- package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
- package/packages/web/components/QueueCard/index.ts +0 -2
- package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
- package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
- package/packages/web/components/RecoverCard/index.ts +0 -2
- package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
- package/packages/web/components/RoadmapCard/index.ts +0 -2
- package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
- package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
- package/packages/web/components/ShipsCard/index.ts +0 -2
- package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
- package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
- package/packages/web/components/SparklineChart/index.ts +0 -2
- package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
- package/packages/web/components/StatsMasonry/index.ts +0 -1
- package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
- package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
- package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
- package/packages/web/components/StreakCard/index.ts +0 -2
- package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
- package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
- package/packages/web/components/TasksCounter/index.ts +0 -2
- package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
- package/packages/web/components/TechStackBadges/index.ts +0 -1
- package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
- package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
- package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
- package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
- package/packages/web/components/TerminalDock/index.ts +0 -2
- package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
- package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
- package/packages/web/components/TerminalTabs/index.ts +0 -1
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
- package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
- package/packages/web/components/VelocityBadge/index.ts +0 -2
- package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
- package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
- package/packages/web/components/VelocityCard/index.ts +0 -2
- package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
- package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
- package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
- package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
- package/packages/web/components/WeeklyReports/index.ts +0 -4
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
- package/packages/web/components/WeeklySparkline/index.ts +0 -2
- package/packages/web/components/charts/SessionsChart.tsx +0 -175
- package/packages/web/components/ui/alert-dialog.tsx +0 -157
- package/packages/web/components/ui/badge.tsx +0 -46
- package/packages/web/components/ui/button.tsx +0 -60
- package/packages/web/components/ui/card.tsx +0 -92
- package/packages/web/components/ui/chart.tsx +0 -385
- package/packages/web/components/ui/dialog.tsx +0 -143
- package/packages/web/components/ui/drawer.tsx +0 -135
- package/packages/web/components/ui/dropdown-menu.tsx +0 -257
- package/packages/web/components/ui/input.tsx +0 -21
- package/packages/web/components/ui/scroll-area.tsx +0 -58
- package/packages/web/components/ui/select.tsx +0 -187
- package/packages/web/components/ui/sheet.tsx +0 -139
- package/packages/web/components/ui/tabs.tsx +0 -66
- package/packages/web/components/ui/tooltip.tsx +0 -61
- package/packages/web/components.json +0 -22
- package/packages/web/context/GlobalTerminalContext.tsx +0 -538
- package/packages/web/context/TerminalContext.tsx +0 -45
- package/packages/web/context/TerminalTabsContext.tsx +0 -181
- package/packages/web/eslint.config.mjs +0 -18
- package/packages/web/hooks/useClaudeTerminal.ts +0 -425
- package/packages/web/hooks/useProjectStats.ts +0 -93
- package/packages/web/hooks/useProjects.ts +0 -73
- package/packages/web/lib/actions/projects.ts +0 -15
- package/packages/web/lib/commands.ts +0 -81
- package/packages/web/lib/format.ts +0 -23
- package/packages/web/lib/generate-week-report.ts +0 -285
- package/packages/web/lib/parse-prjct-files.ts +0 -1123
- package/packages/web/lib/project-colors.ts +0 -58
- package/packages/web/lib/projects.ts +0 -506
- package/packages/web/lib/pty.ts +0 -101
- package/packages/web/lib/query-config.ts +0 -44
- package/packages/web/lib/services/index.ts +0 -9
- package/packages/web/lib/services/projects.server.ts +0 -66
- package/packages/web/lib/services/stats.server.ts +0 -562
- package/packages/web/lib/unified-loader.ts +0 -396
- package/packages/web/lib/utils.ts +0 -6
- package/packages/web/next-env.d.ts +0 -6
- package/packages/web/next.config.ts +0 -7
- package/packages/web/package.json +0 -57
- package/packages/web/postcss.config.mjs +0 -7
- package/packages/web/public/file.svg +0 -1
- package/packages/web/public/globe.svg +0 -1
- package/packages/web/public/next.svg +0 -1
- package/packages/web/public/vercel.svg +0 -1
- package/packages/web/public/window.svg +0 -1
- package/packages/web/server.ts +0 -312
- package/packages/web/tsconfig.json +0 -34
- package/templates/commands/serve.md +0 -121
package/packages/web/server.ts
DELETED
|
@@ -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
|
-
```
|