prjct-cli 0.10.14 → 0.11.1

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 (105) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/bin/dev.js +217 -0
  3. package/bin/prjct +10 -0
  4. package/bin/serve.js +78 -0
  5. package/core/bus/index.js +322 -0
  6. package/core/command-registry.js +65 -0
  7. package/core/domain/snapshot-manager.js +375 -0
  8. package/core/plugin/hooks.js +313 -0
  9. package/core/plugin/index.js +52 -0
  10. package/core/plugin/loader.js +331 -0
  11. package/core/plugin/registry.js +325 -0
  12. package/core/plugins/webhook.js +143 -0
  13. package/core/session/index.js +449 -0
  14. package/core/session/metrics.js +293 -0
  15. package/package.json +28 -4
  16. package/packages/shared/dist/index.d.ts +615 -0
  17. package/packages/shared/dist/index.js +204 -0
  18. package/packages/shared/package.json +29 -0
  19. package/packages/shared/src/index.ts +9 -0
  20. package/packages/shared/src/schemas.ts +124 -0
  21. package/packages/shared/src/types.ts +187 -0
  22. package/packages/shared/src/utils.ts +148 -0
  23. package/packages/shared/tsconfig.json +18 -0
  24. package/packages/web/README.md +36 -0
  25. package/packages/web/app/api/claude/sessions/route.ts +44 -0
  26. package/packages/web/app/api/claude/status/route.ts +34 -0
  27. package/packages/web/app/api/projects/[id]/delete/route.ts +21 -0
  28. package/packages/web/app/api/projects/[id]/icon/route.ts +33 -0
  29. package/packages/web/app/api/projects/[id]/route.ts +29 -0
  30. package/packages/web/app/api/projects/[id]/stats/route.ts +36 -0
  31. package/packages/web/app/api/projects/[id]/status/route.ts +21 -0
  32. package/packages/web/app/api/projects/route.ts +16 -0
  33. package/packages/web/app/api/sessions/history/route.ts +122 -0
  34. package/packages/web/app/api/stats/route.ts +38 -0
  35. package/packages/web/app/error.tsx +34 -0
  36. package/packages/web/app/favicon.ico +0 -0
  37. package/packages/web/app/globals.css +155 -0
  38. package/packages/web/app/layout.tsx +43 -0
  39. package/packages/web/app/loading.tsx +7 -0
  40. package/packages/web/app/not-found.tsx +25 -0
  41. package/packages/web/app/page.tsx +227 -0
  42. package/packages/web/app/project/[id]/error.tsx +41 -0
  43. package/packages/web/app/project/[id]/loading.tsx +9 -0
  44. package/packages/web/app/project/[id]/not-found.tsx +27 -0
  45. package/packages/web/app/project/[id]/page.tsx +253 -0
  46. package/packages/web/app/project/[id]/stats/page.tsx +447 -0
  47. package/packages/web/app/sessions/page.tsx +165 -0
  48. package/packages/web/app/settings/page.tsx +150 -0
  49. package/packages/web/components/AppSidebar.tsx +113 -0
  50. package/packages/web/components/CommandButton.tsx +39 -0
  51. package/packages/web/components/ConnectionStatus.tsx +29 -0
  52. package/packages/web/components/Logo.tsx +65 -0
  53. package/packages/web/components/MarkdownContent.tsx +123 -0
  54. package/packages/web/components/ProjectAvatar.tsx +54 -0
  55. package/packages/web/components/TechStackBadges.tsx +20 -0
  56. package/packages/web/components/TerminalTab.tsx +84 -0
  57. package/packages/web/components/TerminalTabs.tsx +210 -0
  58. package/packages/web/components/charts/SessionsChart.tsx +172 -0
  59. package/packages/web/components/providers.tsx +45 -0
  60. package/packages/web/components/ui/alert-dialog.tsx +157 -0
  61. package/packages/web/components/ui/badge.tsx +46 -0
  62. package/packages/web/components/ui/button.tsx +60 -0
  63. package/packages/web/components/ui/card.tsx +92 -0
  64. package/packages/web/components/ui/chart.tsx +385 -0
  65. package/packages/web/components/ui/dropdown-menu.tsx +257 -0
  66. package/packages/web/components/ui/scroll-area.tsx +58 -0
  67. package/packages/web/components/ui/sheet.tsx +139 -0
  68. package/packages/web/components/ui/tabs.tsx +66 -0
  69. package/packages/web/components/ui/tooltip.tsx +61 -0
  70. package/packages/web/components.json +22 -0
  71. package/packages/web/context/TerminalContext.tsx +45 -0
  72. package/packages/web/context/TerminalTabsContext.tsx +136 -0
  73. package/packages/web/eslint.config.mjs +18 -0
  74. package/packages/web/hooks/useClaudeTerminal.ts +375 -0
  75. package/packages/web/hooks/useProjectStats.ts +38 -0
  76. package/packages/web/hooks/useProjects.ts +73 -0
  77. package/packages/web/hooks/useStats.ts +28 -0
  78. package/packages/web/lib/format.ts +23 -0
  79. package/packages/web/lib/parse-prjct-files.ts +1122 -0
  80. package/packages/web/lib/projects.ts +452 -0
  81. package/packages/web/lib/pty.ts +101 -0
  82. package/packages/web/lib/query-config.ts +44 -0
  83. package/packages/web/lib/utils.ts +6 -0
  84. package/packages/web/next-env.d.ts +6 -0
  85. package/packages/web/next.config.ts +7 -0
  86. package/packages/web/package.json +53 -0
  87. package/packages/web/postcss.config.mjs +7 -0
  88. package/packages/web/public/file.svg +1 -0
  89. package/packages/web/public/globe.svg +1 -0
  90. package/packages/web/public/next.svg +1 -0
  91. package/packages/web/public/vercel.svg +1 -0
  92. package/packages/web/public/window.svg +1 -0
  93. package/packages/web/server.ts +262 -0
  94. package/packages/web/tsconfig.json +34 -0
  95. package/templates/commands/done.md +176 -54
  96. package/templates/commands/history.md +176 -0
  97. package/templates/commands/init.md +28 -1
  98. package/templates/commands/now.md +191 -9
  99. package/templates/commands/pause.md +176 -12
  100. package/templates/commands/redo.md +142 -0
  101. package/templates/commands/resume.md +166 -62
  102. package/templates/commands/serve.md +121 -0
  103. package/templates/commands/ship.md +45 -1
  104. package/templates/commands/sync.md +34 -1
  105. package/templates/commands/undo.md +152 -0
@@ -0,0 +1,142 @@
1
+ ---
2
+ allowed-tools: [Read, Write, Bash]
3
+ description: 'Redo previously undone changes'
4
+ timestamp-rule: 'GetTimestamp() for ALL timestamps'
5
+ ---
6
+
7
+ # /p:redo - Redo Previously Undone Snapshot
8
+
9
+ ## Overview
10
+ Restores a previously undone snapshot. Only works if you have recently used /p:undo.
11
+
12
+ ## Context Variables
13
+ - `{projectId}`: From `.prjct/prjct.config.json`
14
+ - `{globalPath}`: `~/.prjct-cli/projects/{projectId}`
15
+ - `{snapshotDir}`: `{globalPath}/snapshots`
16
+ - `{memoryPath}`: `{globalPath}/memory/context.jsonl`
17
+ - `{redoStackPath}`: `{snapshotDir}/redo-stack.json`
18
+
19
+ ## Step 1: Read Config
20
+
21
+ READ: `.prjct/prjct.config.json`
22
+ EXTRACT: `projectId`
23
+
24
+ IF file not found:
25
+ OUTPUT: "No prjct project. Run /p:init first."
26
+ STOP
27
+
28
+ ## Step 2: Check Redo Stack
29
+
30
+ READ: `{redoStackPath}`
31
+
32
+ IF file not found OR empty OR equals "[]":
33
+ OUTPUT: "⚠️ Nothing to redo. Use /p:undo first."
34
+ STOP
35
+
36
+ PARSE as JSON array
37
+ GET last item as {redoSnapshot}
38
+
39
+ IF array is empty:
40
+ OUTPUT: "⚠️ Nothing to redo. Use /p:undo first."
41
+ STOP
42
+
43
+ EXTRACT from {redoSnapshot}:
44
+ - `{redoHash}`: hash
45
+ - `{redoMessage}`: message
46
+ - `{redoTimestamp}`: timestamp
47
+
48
+ ## Step 3: Get Current State
49
+
50
+ BASH: `cd {snapshotDir} && git rev-parse HEAD`
51
+ CAPTURE as {currentHash}
52
+
53
+ BASH: `cd {snapshotDir} && git log -1 --pretty=format:'%s' {currentHash}`
54
+ CAPTURE as {currentMessage}
55
+
56
+ ## Step 4: Get Files That Will Change
57
+
58
+ BASH: `cd {snapshotDir} && git diff --name-only {currentHash} {redoHash}`
59
+ CAPTURE as {affectedFiles}
60
+
61
+ COUNT files as {fileCount}
62
+
63
+ ## Step 5: Restore Redo Snapshot
64
+
65
+ ### Checkout files from redo snapshot
66
+ BASH: `cd {snapshotDir} && git checkout {redoHash} -- .`
67
+
68
+ ### Copy files back to project
69
+ For each file in {affectedFiles}:
70
+ - Source: `{snapshotDir}/{file}`
71
+ - Destination: `{projectPath}/{file}`
72
+ - Copy content
73
+
74
+ ## Step 6: Remove from Redo Stack
75
+
76
+ READ: `{redoStackPath}`
77
+ PARSE as JSON array
78
+ REMOVE last item
79
+ WRITE: `{redoStackPath}`
80
+
81
+ ## Step 7: Log to Memory
82
+
83
+ APPEND to: `{memoryPath}`
84
+
85
+ Single line (JSONL):
86
+ ```json
87
+ {"timestamp":"{GetTimestamp()}","action":"snapshot_redo","from":"{currentHash}","to":"{redoHash}","files":{fileCount}}
88
+ ```
89
+
90
+ ## Step 8: Log to Manifest
91
+
92
+ APPEND to: `{snapshotDir}/manifest.jsonl`
93
+
94
+ Single line (JSONL):
95
+ ```json
96
+ {"type":"redo","from":"{currentHash}","to":"{redoHash}","timestamp":"{GetTimestamp()}","files":{fileCount}}
97
+ ```
98
+
99
+ ## Output
100
+
101
+ SUCCESS:
102
+ ```
103
+ ⏩ Redone: {redoMessage}
104
+
105
+ Restored from: {currentMessage}
106
+ Files affected: {fileCount}
107
+
108
+ /p:undo to undo again | /p:history to see all snapshots
109
+ ```
110
+
111
+ ## Error Handling
112
+
113
+ | Error | Response | Action |
114
+ |-------|----------|--------|
115
+ | No redo stack | "Nothing to redo" | STOP |
116
+ | Empty redo stack | "Nothing to redo" | STOP |
117
+ | Git error | Show error message | STOP |
118
+ | File copy fails | "Failed to restore {file}" | CONTINUE |
119
+
120
+ ## Examples
121
+
122
+ ### Example 1: Successful Redo
123
+ ```
124
+ ⏩ Redone: Ship authentication feature
125
+
126
+ Restored from: Add login form
127
+ Files affected: 5
128
+
129
+ /p:undo to undo again | /p:history to see all snapshots
130
+ ```
131
+
132
+ ### Example 2: Nothing to Redo
133
+ ```
134
+ ⚠️ Nothing to redo. Use /p:undo first.
135
+ ```
136
+
137
+ ## Notes
138
+
139
+ - Redo only works after /p:undo
140
+ - Creating a new snapshot clears the redo stack
141
+ - Multiple redos are possible if you undid multiple times
142
+ - Redo stack is project-specific
@@ -1,97 +1,201 @@
1
1
  ---
2
- allowed-tools: [Read, Write, GetTimestamp]
3
- description: 'Resume paused task'
4
- timestamp-rule: 'GetTimestamp() for resumed'
2
+ allowed-tools: [Read, Write, Bash]
3
+ description: 'Resume paused session'
4
+ timestamp-rule: 'GetTimestamp() for all timestamps'
5
5
  ---
6
6
 
7
- # /p:resume
7
+ # /p:resume - Resume Paused Session
8
+
9
+ ## Context Variables
10
+ - `{projectId}`: From `.prjct/prjct.config.json`
11
+ - `{globalPath}`: `~/.prjct-cli/projects/{projectId}`
12
+ - `{sessionPath}`: `{globalPath}/sessions/current.json`
13
+ - `{nowPath}`: `{globalPath}/core/now.md`
14
+ - `{memoryPath}`: `{globalPath}/memory/context.jsonl`
15
+
16
+ ## Step 1: Read Config
17
+
18
+ READ: `.prjct/prjct.config.json`
19
+ EXTRACT: `projectId`
20
+
21
+ IF file not found:
22
+ OUTPUT: "No prjct project. Run /p:init first."
23
+ STOP
24
+
25
+ ## Step 2: Check Session State
26
+
27
+ READ: `{sessionPath}`
28
+
29
+ IF file not found OR empty:
30
+ OUTPUT:
31
+ ```
32
+ ⚠️ No paused session to resume.
33
+
34
+ Start a new task:
35
+ • /p:now <task>
36
+ ```
37
+ STOP
38
+
39
+ PARSE as JSON → {session}
40
+
41
+ IF {session.status} == "active":
42
+ CALCULATE: {elapsed} = time since last start/resume
43
+ OUTPUT:
44
+ ```
45
+ ▶️ Already active: {session.task}
46
+
47
+ Session: {session.id}
48
+ Working for: {elapsed}
49
+
50
+ /p:done to complete | /p:pause to pause
51
+ ```
52
+ STOP
53
+
54
+ IF {session.status} == "completed":
55
+ OUTPUT:
56
+ ```
57
+ ⚠️ Session already completed.
58
+
59
+ Start a new task:
60
+ • /p:now <task>
61
+ ```
62
+ STOP
63
+
64
+ IF {session.status} != "paused":
65
+ OUTPUT: "⚠️ No paused session to resume."
66
+ STOP
67
+
68
+ ## Step 3: Calculate Pause Duration
69
+
70
+ SET: {now} = GetTimestamp()
71
+ SET: {pauseDuration} = time since {session.pausedAt}
72
+ SET: {pauseFormatted} = format as "Xh Ym" or "Xm"
73
+
74
+ ## Step 4: Update Session
75
+
76
+ UPDATE {session}:
77
+ ```json
78
+ {
79
+ "id": "{session.id}",
80
+ "projectId": "{session.projectId}",
81
+ "task": "{session.task}",
82
+ "status": "active",
83
+ "startedAt": "{session.startedAt}",
84
+ "pausedAt": null,
85
+ "completedAt": null,
86
+ "duration": {session.duration},
87
+ "metrics": {session.metrics},
88
+ "timeline": [
89
+ ...{session.timeline},
90
+ {"type": "resume", "at": "{now}"}
91
+ ]
92
+ }
93
+ ```
8
94
 
9
- ## Check
10
- - Requires paused tasks
11
- - Blocks if active exists
95
+ WRITE: `{sessionPath}`
96
+ Content: Updated session JSON
12
97
 
13
- ## Flow
14
- 1. Select: last/ID/# → Set status='active', resumed={GetTimestamp()}
15
- 2. Update stack.jsonl + now.md → Log resume
98
+ ## Step 5: Update Legacy now.md
16
99
 
17
- ## Response
18
- `▶️ {task} | Paused: {duration} | {agent} | Done: /p:done`
100
+ WRITE: `{nowPath}`
101
+ Content:
102
+ ```markdown
103
+ # NOW
19
104
 
20
- ## Examples
105
+ **{session.task}**
21
106
 
22
- ### Resume last paused
23
- ```
24
- /p:resume
25
- → Resumes most recently paused task
26
- → Picks up exactly where you left off
107
+ Started: {session.startedAt}
108
+ Resumed: {now}
109
+ Session: {session.id}
27
110
  ```
28
111
 
29
- ### Resume specific task
30
- ```
31
- /p:resume task-1234567890
32
- → Resumes task by ID
33
- ```
112
+ ## Step 6: Log to Memory
34
113
 
35
- ### Resume by number
114
+ APPEND to: `{memoryPath}`
115
+
116
+ Single line (JSONL):
117
+ ```json
118
+ {"timestamp":"{now}","action":"session_resumed","sessionId":"{session.id}","task":"{session.task}","pauseDuration":{pauseDurationSeconds}}
36
119
  ```
37
- /p:resume 2
38
- Resumes 2nd task from paused list
39
- → Useful when shown list by /p:stack
120
+
121
+ ## Output
122
+
123
+ SUCCESS:
40
124
  ```
125
+ ▶️ Resumed: {session.task}
41
126
 
42
- ## Stack Display Format
127
+ Session: {session.id}
128
+ Was paused: {pauseFormatted}
129
+ Total active: {session.duration} (before this stretch)
43
130
 
44
- When multiple paused tasks exist:
131
+ /p:done when finished | /p:pause for another break
45
132
  ```
46
- ⏸️ Paused tasks (3):
47
133
 
48
- 1. Fix login validation bug
49
- Paused 10m ago | Active time: 45m
134
+ ## Error Handling
50
135
 
51
- 2. Implement dark mode toggle
52
- Paused 2h ago | Active time: 1h 20m
136
+ | Error | Response | Action |
137
+ |-------|----------|--------|
138
+ | No project | "No prjct project" | STOP |
139
+ | No session | "No paused session" | STOP |
140
+ | Already active | Show active state | STOP |
141
+ | Already completed | Suggest /p:now | STOP |
142
+ | Write fails | Log warning | CONTINUE |
53
143
 
54
- 3. Update documentation
55
- Paused yesterday | Active time: 30m
144
+ ## Examples
56
145
 
57
- Resume: /p:resume {1-3}
146
+ ### Example 1: Resume Paused Session
147
+ **Session:**
148
+ ```json
149
+ {
150
+ "id": "sess_abc12345",
151
+ "task": "implement auth",
152
+ "status": "paused",
153
+ "pausedAt": "2025-12-07T12:30:00.000Z",
154
+ "duration": 9000,
155
+ "timeline": [
156
+ {"type": "start", "at": "2025-12-07T10:00:00.000Z"},
157
+ {"type": "pause", "at": "2025-12-07T12:30:00.000Z"}
158
+ ]
159
+ }
58
160
  ```
59
161
 
60
- ## Natural Language Support
162
+ **Current time:** 2:00 PM (paused 1.5h ago)
61
163
 
62
- Detect intent for resume:
63
- - "p. resume" → Resume last paused
64
- - "p. continue" → Resume last paused
65
- - "p. back to {task}" → Resume matching task
66
- - "p. resume 2" → Resume task #2
164
+ **Output:**
165
+ ```
166
+ ▶️ Resumed: implement auth
67
167
 
68
- ## Error Handling
168
+ Session: sess_abc12345
169
+ Was paused: 1h 30m
170
+ Total active: 2h 30m (before this stretch)
69
171
 
70
- ### No paused tasks
172
+ /p:done when finished | /p:pause for another break
71
173
  ```
72
- ❌ No paused tasks to resume
73
-
74
- See what's in queue:
75
- → /p:next
76
174
 
77
- Start new task:
78
- → /p:work "{task}"
175
+ ### Example 2: Already Active
176
+ **Output:**
79
177
  ```
178
+ ▶️ Already active: implement auth
80
179
 
81
- ### Active task exists
180
+ Session: sess_abc12345
181
+ Working for: 45m
182
+
183
+ /p:done to complete | /p:pause to pause
82
184
  ```
83
- ❌ Already working on: {current_task}
84
185
 
85
- Options:
86
- → /p:done (complete current)
87
- → /p:pause (pause current)
88
- → /p:switch {task_id} (atomic switch)
186
+ ### Example 3: No Paused Session
187
+ **Output:**
89
188
  ```
189
+ ⚠️ No paused session to resume.
90
190
 
91
- ### Invalid task ID
191
+ Start a new task:
192
+ • /p:now <task>
92
193
  ```
93
- ❌ Task {id} not found or not paused
94
194
 
95
- See paused tasks:
96
- → /p:stack
97
- ```
195
+ ## Natural Language Support
196
+
197
+ Detect intent for resume:
198
+ - "p. resume" → Resume paused session
199
+ - "p. continue" → Resume paused session
200
+ - "p. back to work" → Resume paused session
201
+ - "p. unpause" → Resume paused session
@@ -0,0 +1,121 @@
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
+ ```
@@ -11,6 +11,7 @@ timestamp-rule: 'GetTimestamp() and GetDate() for ALL timestamps'
11
11
  - `{globalPath}`: `~/.prjct-cli/projects/{projectId}`
12
12
  - `{shippedPath}`: `{globalPath}/progress/shipped.md`
13
13
  - `{memoryPath}`: `{globalPath}/memory/context.jsonl`
14
+ - `{snapshotDir}`: `{globalPath}/snapshots`
14
15
  - `{feature}`: User-provided feature name
15
16
 
16
17
  ## Step 1: Read Config
@@ -187,6 +188,48 @@ Single line (JSONL):
187
188
  {"timestamp":"{GetTimestamp()}","action":"feature_shipped","feature":"{feature}","version":"{newVersion}"}
188
189
  ```
189
190
 
191
+ ## Step 11: Create Snapshot (Undo/Redo Support)
192
+
193
+ This creates a snapshot for the undo/redo system.
194
+
195
+ ### Initialize Snapshot Directory
196
+ BASH: `mkdir -p {snapshotDir}`
197
+
198
+ ### Check if Git Repo Exists
199
+ BASH: `ls {snapshotDir}/.git 2>/dev/null || echo "INIT_NEEDED"`
200
+
201
+ IF output contains "INIT_NEEDED":
202
+ BASH: `cd {snapshotDir} && git init && git config user.email "prjct@local" && git config user.name "prjct-snapshots" && git commit --allow-empty -m "init: snapshot system"`
203
+
204
+ ### Copy Changed Files to Snapshot
205
+ BASH: `git diff --name-only HEAD~1 2>/dev/null || git diff --name-only`
206
+ CAPTURE as {changedFiles}
207
+
208
+ For each file in {changedFiles}:
209
+ - Source: `{projectPath}/{file}`
210
+ - Destination: `{snapshotDir}/{file}`
211
+ - Create parent directories if needed
212
+ - Copy file content
213
+
214
+ ### Commit Snapshot
215
+ BASH: `cd {snapshotDir} && git add -A && git commit -m "Ship: {feature} (v{newVersion})" 2>/dev/null || echo "NO_CHANGES"`
216
+
217
+ ### Get Snapshot Hash
218
+ BASH: `cd {snapshotDir} && git rev-parse --short HEAD`
219
+ CAPTURE as {snapshotHash}
220
+
221
+ ### Clear Redo Stack (new snapshot invalidates redo)
222
+ WRITE: `{snapshotDir}/redo-stack.json`
223
+ Content: `[]`
224
+
225
+ ### Log Snapshot
226
+ APPEND to: `{snapshotDir}/manifest.jsonl`
227
+
228
+ Single line (JSONL):
229
+ ```json
230
+ {"type":"snapshot","hash":"{snapshotHash}","message":"Ship: {feature}","version":"{newVersion}","timestamp":"{GetTimestamp()}"}
231
+ ```
232
+
190
233
  ## Output
191
234
 
192
235
  SUCCESS:
@@ -197,11 +240,12 @@ Version: {currentVersion} → {newVersion}
197
240
  Lint: {lintStatus}
198
241
  Tests: {testStatus}
199
242
  Commit: {commitHash}
243
+ Snapshot: {snapshotHash}
200
244
 
201
245
  Next:
246
+ • /p:undo - Revert this ship if needed
202
247
  • /p:feature - Plan next feature
203
248
  • /p:recap - See progress
204
- • compact - Clean up conversation
205
249
  ```
206
250
 
207
251
  ## Error Handling
@@ -181,7 +181,40 @@ IF exists:
181
181
 
182
182
  WRITE: `{globalPath}/CLAUDE.md`
183
183
 
184
- ## Step 6: Log to Memory
184
+ ## Step 6: Update project.json
185
+
186
+ This file is the source of truth for the web dashboard. It maps projectId → repoPath.
187
+
188
+ ### Determine Project Name
189
+ - Try package.json → `name` field
190
+ - Try Cargo.toml → `[package] name`
191
+ - Try pyproject.toml → `[project] name`
192
+ - Fallback to directory name (last segment of current path)
193
+
194
+ WRITE: `{globalPath}/project.json`
195
+
196
+ ```json
197
+ {
198
+ "projectId": "{projectId}",
199
+ "repoPath": "{cwd}",
200
+ "name": "{projectName}",
201
+ "techStack": ["{primaryLanguage}", "{primaryFramework}", ...],
202
+ "createdAt": "{existingCreatedAt || GetTimestamp()}",
203
+ "lastSync": "{GetTimestamp()}"
204
+ }
205
+ ```
206
+
207
+ ### techStack Array Rules
208
+ - Max 4 items for display in dashboard cards
209
+ - Order by relevance: primary language → framework → tools
210
+ - Examples:
211
+ - Node.js + React: `["TypeScript", "React", "Node.js", "Vitest"]`
212
+ - Python Django: `["Python", "Django", "PostgreSQL"]`
213
+ - CLI tool: `["Node.js", "CLI", "CommonJS"]`
214
+
215
+ NOTE: If project.json already exists, preserve `createdAt` field. Always update `lastSync` and `techStack`.
216
+
217
+ ## Step 7: Log to Memory
185
218
 
186
219
  APPEND to: `{memoryPath}`
187
220