prjct-cli 0.20.0 → 0.20.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 (226) hide show
  1. package/CHANGELOG.md +24 -6
  2. package/CLAUDE.md +56 -15
  3. package/README.md +5 -6
  4. package/bin/prjct +59 -42
  5. package/bin/prjct.ts +60 -0
  6. package/core/__tests__/agentic/memory-system.test.ts +18 -3
  7. package/core/__tests__/agentic/plan-mode.test.ts +55 -26
  8. package/core/__tests__/agentic/prompt-builder.test.ts +6 -6
  9. package/core/__tests__/utils/project-commands.test.ts +72 -0
  10. package/core/agentic/agent-router.ts +3 -12
  11. package/core/agentic/command-executor.ts +372 -3
  12. package/core/agentic/context-builder.ts +7 -27
  13. package/core/agentic/ground-truth.ts +604 -5
  14. package/core/agentic/index.ts +180 -0
  15. package/core/agentic/loop-detector.ts +418 -4
  16. package/core/agentic/memory-system.ts +857 -3
  17. package/core/agentic/plan-mode.ts +491 -4
  18. package/core/agentic/prompt-builder.ts +44 -65
  19. package/core/agentic/services.ts +13 -5
  20. package/core/agentic/skill-loader.ts +112 -0
  21. package/core/agentic/smart-context.ts +37 -122
  22. package/core/agentic/template-loader.ts +79 -122
  23. package/core/agentic/tool-registry.ts +5 -11
  24. package/core/agents/index.ts +1 -1
  25. package/core/agents/performance.ts +4 -2
  26. package/core/bus/bus.ts +262 -0
  27. package/core/bus/index.ts +3 -313
  28. package/core/commands/analysis.ts +5 -5
  29. package/core/commands/analytics.ts +11 -11
  30. package/core/commands/base.ts +33 -209
  31. package/core/commands/cleanup.ts +148 -0
  32. package/core/commands/command-data.ts +346 -0
  33. package/core/commands/commands.ts +216 -0
  34. package/core/commands/design.ts +83 -0
  35. package/core/commands/index.ts +13 -207
  36. package/core/commands/maintenance.ts +52 -473
  37. package/core/commands/planning.ts +3 -3
  38. package/core/commands/register.ts +104 -0
  39. package/core/commands/registry.ts +441 -0
  40. package/core/commands/setup.ts +25 -9
  41. package/core/commands/shipping.ts +48 -11
  42. package/core/commands/snapshots.ts +299 -0
  43. package/core/commands/workflow.ts +2 -2
  44. package/core/constants/index.ts +254 -4
  45. package/core/domain/agent-loader.ts +5 -6
  46. package/core/domain/task-stack.ts +555 -4
  47. package/core/errors.ts +127 -1
  48. package/core/events/events.ts +87 -0
  49. package/core/events/index.ts +4 -138
  50. package/core/index.ts +15 -23
  51. package/core/infrastructure/agent-detector.ts +126 -201
  52. package/core/infrastructure/author-detector.ts +99 -171
  53. package/core/infrastructure/command-installer.ts +476 -4
  54. package/core/infrastructure/config-manager.ts +41 -37
  55. package/core/infrastructure/path-manager.ts +59 -9
  56. package/core/infrastructure/permission-manager.ts +286 -0
  57. package/core/outcomes/analyzer.ts +7 -41
  58. package/core/outcomes/index.ts +1 -1
  59. package/core/outcomes/recorder.ts +1 -1
  60. package/core/{plugins → plugin/builtin}/webhook.ts +6 -22
  61. package/core/plugin/loader.ts +5 -5
  62. package/core/plugin/registry.ts +2 -2
  63. package/core/schemas/ideas.ts +85 -54
  64. package/core/schemas/index.ts +14 -33
  65. package/core/schemas/permissions.ts +177 -0
  66. package/core/schemas/project.ts +39 -12
  67. package/core/schemas/roadmap.ts +94 -59
  68. package/core/schemas/schemas.ts +39 -0
  69. package/core/schemas/shipped.ts +87 -60
  70. package/core/schemas/state.ts +110 -70
  71. package/core/server/index.ts +21 -0
  72. package/core/server/routes.ts +165 -0
  73. package/core/server/server.ts +136 -0
  74. package/core/server/sse.ts +135 -0
  75. package/core/services/agent-service.ts +170 -0
  76. package/core/services/breakdown-service.ts +126 -0
  77. package/core/services/index.ts +21 -0
  78. package/core/services/memory-service.ts +108 -0
  79. package/core/services/project-service.ts +146 -0
  80. package/core/services/skill-service.ts +253 -0
  81. package/core/session/compaction.ts +257 -0
  82. package/core/session/index.ts +20 -8
  83. package/core/{infrastructure/session-manager/migration.ts → session/log-migration.ts} +9 -9
  84. package/core/{infrastructure/session-manager/session-manager.ts → session/session-log-manager.ts} +27 -26
  85. package/core/session/{session-manager.ts → task-session-manager.ts} +7 -4
  86. package/core/session/utils.ts +1 -1
  87. package/core/storage/ideas-storage.ts +10 -26
  88. package/core/storage/index.ts +14 -162
  89. package/core/storage/queue-storage.ts +13 -11
  90. package/core/storage/shipped-storage.ts +4 -17
  91. package/core/storage/state-storage.ts +35 -43
  92. package/core/storage/storage-manager.ts +42 -52
  93. package/core/storage/storage.ts +160 -0
  94. package/core/sync/auth-config.ts +1 -8
  95. package/core/sync/index.ts +17 -10
  96. package/core/sync/oauth-handler.ts +1 -6
  97. package/core/sync/sync-client.ts +6 -34
  98. package/core/sync/sync-manager.ts +11 -40
  99. package/core/types/agentic.ts +577 -0
  100. package/core/types/agents.ts +145 -0
  101. package/core/types/bus.ts +82 -0
  102. package/core/types/commands.ts +366 -0
  103. package/core/types/config.ts +66 -0
  104. package/core/types/core.ts +96 -0
  105. package/core/types/domain.ts +71 -0
  106. package/core/types/events.ts +42 -0
  107. package/core/types/fs.ts +56 -0
  108. package/core/types/index.ts +387 -500
  109. package/core/types/infrastructure.ts +196 -0
  110. package/core/{agentic/memory-system/types.ts → types/memory.ts} +33 -8
  111. package/core/{outcomes/types.ts → types/outcomes.ts} +53 -8
  112. package/core/types/plugin.ts +25 -0
  113. package/core/types/server.ts +54 -0
  114. package/core/types/services.ts +65 -0
  115. package/core/types/session.ts +135 -0
  116. package/core/types/storage.ts +148 -0
  117. package/core/types/sync.ts +121 -0
  118. package/core/types/task.ts +72 -0
  119. package/core/types/template.ts +24 -0
  120. package/core/types/utils.ts +90 -0
  121. package/core/utils/cache.ts +195 -0
  122. package/core/utils/collection-filters.ts +245 -0
  123. package/core/utils/date-helper.ts +1 -5
  124. package/core/utils/file-helper.ts +20 -10
  125. package/core/utils/jsonl-helper.ts +5 -8
  126. package/core/utils/markdown-builder.ts +277 -0
  127. package/core/utils/project-commands.ts +132 -0
  128. package/core/utils/runtime.ts +119 -0
  129. package/dist/bin/prjct.mjs +12568 -0
  130. package/package.json +13 -8
  131. package/scripts/build.js +106 -0
  132. package/scripts/postinstall.js +50 -8
  133. package/templates/agentic/subagent-generation.md +1 -1
  134. package/templates/commands/serve.md +118 -0
  135. package/templates/commands/ship.md +13 -2
  136. package/templates/commands/skill.md +110 -0
  137. package/templates/commands/sync.md +1 -1
  138. package/templates/commands/test.md +23 -4
  139. package/templates/permissions/default.jsonc +60 -0
  140. package/templates/permissions/permissive.jsonc +49 -0
  141. package/templates/permissions/strict.jsonc +62 -0
  142. package/templates/skills/code-review.md +47 -0
  143. package/templates/skills/debug.md +61 -0
  144. package/templates/skills/refactor.md +47 -0
  145. package/templates/subagents/domain/devops.md +1 -1
  146. package/templates/subagents/domain/testing.md +6 -10
  147. package/templates/subagents/workflow/prjct-shipper.md +16 -7
  148. package/templates/tools/bash.txt +22 -0
  149. package/templates/tools/edit.txt +18 -0
  150. package/templates/tools/glob.txt +19 -0
  151. package/templates/tools/grep.txt +21 -0
  152. package/templates/tools/read.txt +14 -0
  153. package/templates/tools/task.txt +20 -0
  154. package/templates/tools/webfetch.txt +16 -0
  155. package/templates/tools/websearch.txt +18 -0
  156. package/templates/tools/write.txt +17 -0
  157. package/core/agentic/command-executor/command-executor.ts +0 -312
  158. package/core/agentic/command-executor/index.ts +0 -16
  159. package/core/agentic/command-executor/status-signal.ts +0 -38
  160. package/core/agentic/command-executor/types.ts +0 -79
  161. package/core/agentic/ground-truth/index.ts +0 -76
  162. package/core/agentic/ground-truth/types.ts +0 -33
  163. package/core/agentic/ground-truth/utils.ts +0 -48
  164. package/core/agentic/ground-truth/verifiers/analyze.ts +0 -54
  165. package/core/agentic/ground-truth/verifiers/done.ts +0 -75
  166. package/core/agentic/ground-truth/verifiers/feature.ts +0 -70
  167. package/core/agentic/ground-truth/verifiers/index.ts +0 -37
  168. package/core/agentic/ground-truth/verifiers/init.ts +0 -52
  169. package/core/agentic/ground-truth/verifiers/now.ts +0 -57
  170. package/core/agentic/ground-truth/verifiers/ship.ts +0 -85
  171. package/core/agentic/ground-truth/verifiers/spec.ts +0 -45
  172. package/core/agentic/ground-truth/verifiers/sync.ts +0 -47
  173. package/core/agentic/ground-truth/verifiers.ts +0 -6
  174. package/core/agentic/loop-detector/error-analysis.ts +0 -97
  175. package/core/agentic/loop-detector/hallucination.ts +0 -71
  176. package/core/agentic/loop-detector/index.ts +0 -41
  177. package/core/agentic/loop-detector/loop-detector.ts +0 -222
  178. package/core/agentic/loop-detector/types.ts +0 -66
  179. package/core/agentic/memory-system/history.ts +0 -53
  180. package/core/agentic/memory-system/index.ts +0 -192
  181. package/core/agentic/memory-system/patterns.ts +0 -156
  182. package/core/agentic/memory-system/semantic-memories.ts +0 -278
  183. package/core/agentic/memory-system/session.ts +0 -21
  184. package/core/agentic/plan-mode/approval.ts +0 -57
  185. package/core/agentic/plan-mode/constants.ts +0 -44
  186. package/core/agentic/plan-mode/index.ts +0 -28
  187. package/core/agentic/plan-mode/plan-mode.ts +0 -407
  188. package/core/agentic/plan-mode/types.ts +0 -193
  189. package/core/agents/types.ts +0 -126
  190. package/core/command-registry/categories.ts +0 -23
  191. package/core/command-registry/commands.ts +0 -15
  192. package/core/command-registry/core-commands.ts +0 -344
  193. package/core/command-registry/index.ts +0 -158
  194. package/core/command-registry/optional-commands.ts +0 -163
  195. package/core/command-registry/setup-commands.ts +0 -83
  196. package/core/command-registry/types.ts +0 -59
  197. package/core/command-registry.ts +0 -9
  198. package/core/commands/types.ts +0 -185
  199. package/core/commands.ts +0 -11
  200. package/core/constants/formats.ts +0 -187
  201. package/core/context-sync.ts +0 -18
  202. package/core/data/index.ts +0 -27
  203. package/core/data/md-base-manager.ts +0 -203
  204. package/core/data/md-ideas-manager.ts +0 -155
  205. package/core/data/md-queue-manager.ts +0 -180
  206. package/core/data/md-shipped-manager.ts +0 -90
  207. package/core/data/md-state-manager.ts +0 -137
  208. package/core/domain/task-stack/index.ts +0 -19
  209. package/core/domain/task-stack/parser.ts +0 -86
  210. package/core/domain/task-stack/storage.ts +0 -123
  211. package/core/domain/task-stack/task-stack.ts +0 -340
  212. package/core/domain/task-stack/types.ts +0 -51
  213. package/core/infrastructure/command-installer/command-installer.ts +0 -327
  214. package/core/infrastructure/command-installer/global-config.ts +0 -136
  215. package/core/infrastructure/command-installer/index.ts +0 -25
  216. package/core/infrastructure/command-installer/types.ts +0 -41
  217. package/core/infrastructure/session-manager/index.ts +0 -23
  218. package/core/infrastructure/session-manager/types.ts +0 -45
  219. package/core/infrastructure/session-manager.ts +0 -8
  220. package/core/serializers/ideas-serializer.ts +0 -187
  221. package/core/serializers/index.ts +0 -36
  222. package/core/serializers/queue-serializer.ts +0 -210
  223. package/core/serializers/shipped-serializer.ts +0 -108
  224. package/core/serializers/state-serializer.ts +0 -136
  225. package/core/session/types.ts +0 -29
  226. /package/core/infrastructure/{agents/claude-agent.ts → claude-agent.ts} +0 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * EventBus - Lightweight Pub/Sub System for prjct-cli
3
+ *
4
+ * Simple event bus for decoupled communication between components.
5
+ * Supports sync/async listeners, wildcards, and one-time subscriptions.
6
+ *
7
+ * @version 1.0.0
8
+ */
9
+
10
+ import fs from 'fs/promises'
11
+ import path from 'path'
12
+ import pathManager from '../infrastructure/path-manager'
13
+ import { EventTypes, type EventData, type EventCallback } from '../types'
14
+
15
+ class EventBus {
16
+ private listeners: Map<string, Set<EventCallback>>
17
+ private onceListeners: Map<string, Set<EventCallback>>
18
+ private history: EventData[]
19
+ private historyLimit: number
20
+ projectId: string | null
21
+
22
+ constructor() {
23
+ this.listeners = new Map()
24
+ this.onceListeners = new Map()
25
+ this.history = []
26
+ this.historyLimit = 100
27
+ this.projectId = null
28
+ }
29
+
30
+ /**
31
+ * Initialize event bus for a project
32
+ */
33
+ async initialize(projectId: string): Promise<void> {
34
+ this.projectId = projectId
35
+ }
36
+
37
+ /**
38
+ * Subscribe to an event
39
+ */
40
+ on(event: string, callback: EventCallback): () => void {
41
+ if (!this.listeners.has(event)) {
42
+ this.listeners.set(event, new Set())
43
+ }
44
+ this.listeners.get(event)!.add(callback)
45
+
46
+ // Return unsubscribe function
47
+ return () => this.off(event, callback)
48
+ }
49
+
50
+ /**
51
+ * Subscribe to an event once
52
+ */
53
+ once(event: string, callback: EventCallback): () => void {
54
+ if (!this.onceListeners.has(event)) {
55
+ this.onceListeners.set(event, new Set())
56
+ }
57
+ this.onceListeners.get(event)!.add(callback)
58
+
59
+ return () => {
60
+ const listeners = this.onceListeners.get(event)
61
+ if (listeners) {
62
+ listeners.delete(callback)
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Unsubscribe from an event
69
+ */
70
+ off(event: string, callback: EventCallback): void {
71
+ const listeners = this.listeners.get(event)
72
+ if (listeners) {
73
+ listeners.delete(callback)
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Emit an event
79
+ */
80
+ async emit(event: string, data: Record<string, unknown> = {}): Promise<void> {
81
+ const timestamp = new Date().toISOString()
82
+ const eventData: EventData = {
83
+ type: event,
84
+ timestamp,
85
+ projectId: this.projectId,
86
+ ...data
87
+ }
88
+
89
+ // Store in history
90
+ this.history.push(eventData)
91
+ if (this.history.length > this.historyLimit) {
92
+ this.history.shift()
93
+ }
94
+
95
+ // Log event if project initialized
96
+ if (this.projectId) {
97
+ await this.logEvent(eventData)
98
+ }
99
+
100
+ // Get all matching listeners
101
+ const callbacks = this.getMatchingListeners(event)
102
+
103
+ // Execute all callbacks
104
+ const results = await Promise.allSettled(
105
+ callbacks.map(cb => this.executeCallback(cb, eventData))
106
+ )
107
+
108
+ // Log any errors
109
+ results.forEach((result) => {
110
+ if (result.status === 'rejected') {
111
+ console.error(`Event listener error for ${event}:`, result.reason)
112
+ }
113
+ })
114
+
115
+ // Handle once listeners
116
+ const onceCallbacks = this.onceListeners.get(event)
117
+ if (onceCallbacks) {
118
+ for (const cb of onceCallbacks) {
119
+ await this.executeCallback(cb, eventData)
120
+ }
121
+ this.onceListeners.delete(event)
122
+ }
123
+
124
+ // Also trigger wildcard once listeners
125
+ const wildcardOnce = this.onceListeners.get(EventTypes.ALL)
126
+ if (wildcardOnce) {
127
+ for (const cb of wildcardOnce) {
128
+ await this.executeCallback(cb, eventData)
129
+ }
130
+ // Don't delete wildcard once - only for specific events
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Get all listeners that match an event (including wildcards)
136
+ */
137
+ getMatchingListeners(event: string): EventCallback[] {
138
+ const callbacks: EventCallback[] = []
139
+
140
+ // Exact match
141
+ const exact = this.listeners.get(event)
142
+ if (exact) {
143
+ callbacks.push(...exact)
144
+ }
145
+
146
+ // Wildcard match (*)
147
+ const wildcard = this.listeners.get(EventTypes.ALL)
148
+ if (wildcard) {
149
+ callbacks.push(...wildcard)
150
+ }
151
+
152
+ // Namespace wildcard (e.g., 'session.*' matches 'session.started')
153
+ const namespace = event.split('.')[0]
154
+ const namespaceWildcard = this.listeners.get(`${namespace}.*`)
155
+ if (namespaceWildcard) {
156
+ callbacks.push(...namespaceWildcard)
157
+ }
158
+
159
+ return callbacks
160
+ }
161
+
162
+ /**
163
+ * Execute a callback safely (handles sync and async)
164
+ */
165
+ async executeCallback(callback: EventCallback, data: EventData): Promise<void> {
166
+ try {
167
+ const result = callback(data)
168
+ if (result instanceof Promise) {
169
+ await result
170
+ }
171
+ } catch (error) {
172
+ console.error('Event callback error:', error)
173
+ throw error
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Log event to persistent storage
179
+ */
180
+ async logEvent(eventData: EventData): Promise<void> {
181
+ try {
182
+ const globalPath = pathManager.getGlobalProjectPath(this.projectId!)
183
+ const eventsPath = path.join(globalPath, 'memory', 'events.jsonl')
184
+
185
+ // Ensure directory exists
186
+ await fs.mkdir(path.dirname(eventsPath), { recursive: true })
187
+
188
+ // Append event
189
+ const line = JSON.stringify(eventData) + '\n'
190
+ await fs.appendFile(eventsPath, line)
191
+ } catch {
192
+ // Silently fail - logging should not break functionality
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Get recent events from history
198
+ */
199
+ getHistory(limit: number = 10, type: string | null = null): EventData[] {
200
+ let events = this.history
201
+ if (type) {
202
+ events = events.filter(e => e.type === type || e.type.startsWith(type))
203
+ }
204
+ return events.slice(-limit)
205
+ }
206
+
207
+ /**
208
+ * Clear all listeners
209
+ */
210
+ clear(): void {
211
+ this.listeners.clear()
212
+ this.onceListeners.clear()
213
+ }
214
+
215
+ /**
216
+ * Get count of listeners for an event
217
+ */
218
+ listenerCount(event: string): number {
219
+ const listeners = this.listeners.get(event)
220
+ return listeners ? listeners.size : 0
221
+ }
222
+
223
+ /**
224
+ * Get all registered event types
225
+ */
226
+ getRegisteredEvents(): string[] {
227
+ return Array.from(this.listeners.keys())
228
+ }
229
+ }
230
+
231
+ // Singleton instance
232
+ const eventBus = new EventBus()
233
+
234
+ // Convenience methods for common events
235
+ const emit = {
236
+ sessionStarted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_STARTED, data),
237
+ sessionPaused: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_PAUSED, data),
238
+ sessionResumed: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_RESUMED, data),
239
+ sessionCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_COMPLETED, data),
240
+
241
+ taskCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.TASK_CREATED, data),
242
+ taskCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.TASK_COMPLETED, data),
243
+
244
+ featureAdded: (data: Record<string, unknown>) => eventBus.emit(EventTypes.FEATURE_ADDED, data),
245
+ featureShipped: (data: Record<string, unknown>) => eventBus.emit(EventTypes.FEATURE_SHIPPED, data),
246
+
247
+ ideaCaptured: (data: Record<string, unknown>) => eventBus.emit(EventTypes.IDEA_CAPTURED, data),
248
+
249
+ snapshotCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SNAPSHOT_CREATED, data),
250
+ snapshotRestored: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SNAPSHOT_RESTORED, data),
251
+
252
+ commitCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.COMMIT_CREATED, data),
253
+ pushCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PUSH_COMPLETED, data),
254
+
255
+ projectInitialized: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PROJECT_INITIALIZED, data),
256
+ projectSynced: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PROJECT_SYNCED, data),
257
+ analysisCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.ANALYSIS_COMPLETED, data)
258
+ }
259
+
260
+ export { EventBus, eventBus, emit }
261
+ export default { EventBus, EventTypes, eventBus, emit }
262
+
package/core/bus/index.ts CHANGED
@@ -1,318 +1,8 @@
1
1
  /**
2
2
  * EventBus - Lightweight Pub/Sub System for prjct-cli
3
3
  *
4
- * Simple event bus for decoupled communication between components.
5
- * Supports sync/async listeners, wildcards, and one-time subscriptions.
6
- *
7
- * @version 1.0.0
4
+ * Barrel export for bus module
8
5
  */
9
6
 
10
- import fs from 'fs/promises'
11
- import path from 'path'
12
- import pathManager from '../infrastructure/path-manager'
13
-
14
- /**
15
- * Event Types - All events that can be emitted
16
- */
17
- const EventTypes = {
18
- // Session events
19
- SESSION_STARTED: 'session.started',
20
- SESSION_PAUSED: 'session.paused',
21
- SESSION_RESUMED: 'session.resumed',
22
- SESSION_COMPLETED: 'session.completed',
23
-
24
- // Task events
25
- TASK_CREATED: 'task.created',
26
- TASK_COMPLETED: 'task.completed',
27
- TASK_UPDATED: 'task.updated',
28
-
29
- // Feature events
30
- FEATURE_ADDED: 'feature.added',
31
- FEATURE_SHIPPED: 'feature.shipped',
32
- FEATURE_UPDATED: 'feature.updated',
33
-
34
- // Idea events
35
- IDEA_CAPTURED: 'idea.captured',
36
- IDEA_PROMOTED: 'idea.promoted',
37
-
38
- // Snapshot events
39
- SNAPSHOT_CREATED: 'snapshot.created',
40
- SNAPSHOT_RESTORED: 'snapshot.restored',
41
-
42
- // Git events
43
- COMMIT_CREATED: 'git.commit',
44
- PUSH_COMPLETED: 'git.push',
45
-
46
- // System events
47
- PROJECT_INITIALIZED: 'project.init',
48
- PROJECT_SYNCED: 'project.sync',
49
- ANALYSIS_COMPLETED: 'analysis.completed',
50
-
51
- // Wildcard
52
- ALL: '*'
53
- } as const
54
-
55
- type EventType = typeof EventTypes[keyof typeof EventTypes]
56
-
57
- interface EventData {
58
- type: string
59
- timestamp: string
60
- projectId: string | null
61
- [key: string]: unknown
62
- }
63
-
64
- type EventCallback = (data: EventData) => void | Promise<void>
65
-
66
- class EventBus {
67
- private listeners: Map<string, Set<EventCallback>>
68
- private onceListeners: Map<string, Set<EventCallback>>
69
- private history: EventData[]
70
- private historyLimit: number
71
- projectId: string | null
72
-
73
- constructor() {
74
- this.listeners = new Map()
75
- this.onceListeners = new Map()
76
- this.history = []
77
- this.historyLimit = 100
78
- this.projectId = null
79
- }
80
-
81
- /**
82
- * Initialize event bus for a project
83
- */
84
- async initialize(projectId: string): Promise<void> {
85
- this.projectId = projectId
86
- }
87
-
88
- /**
89
- * Subscribe to an event
90
- */
91
- on(event: string, callback: EventCallback): () => void {
92
- if (!this.listeners.has(event)) {
93
- this.listeners.set(event, new Set())
94
- }
95
- this.listeners.get(event)!.add(callback)
96
-
97
- // Return unsubscribe function
98
- return () => this.off(event, callback)
99
- }
100
-
101
- /**
102
- * Subscribe to an event once
103
- */
104
- once(event: string, callback: EventCallback): () => void {
105
- if (!this.onceListeners.has(event)) {
106
- this.onceListeners.set(event, new Set())
107
- }
108
- this.onceListeners.get(event)!.add(callback)
109
-
110
- return () => {
111
- const listeners = this.onceListeners.get(event)
112
- if (listeners) {
113
- listeners.delete(callback)
114
- }
115
- }
116
- }
117
-
118
- /**
119
- * Unsubscribe from an event
120
- */
121
- off(event: string, callback: EventCallback): void {
122
- const listeners = this.listeners.get(event)
123
- if (listeners) {
124
- listeners.delete(callback)
125
- }
126
- }
127
-
128
- /**
129
- * Emit an event
130
- */
131
- async emit(event: string, data: Record<string, unknown> = {}): Promise<void> {
132
- const timestamp = new Date().toISOString()
133
- const eventData: EventData = {
134
- type: event,
135
- timestamp,
136
- projectId: this.projectId,
137
- ...data
138
- }
139
-
140
- // Store in history
141
- this.history.push(eventData)
142
- if (this.history.length > this.historyLimit) {
143
- this.history.shift()
144
- }
145
-
146
- // Log event if project initialized
147
- if (this.projectId) {
148
- await this.logEvent(eventData)
149
- }
150
-
151
- // Get all matching listeners
152
- const callbacks = this.getMatchingListeners(event)
153
-
154
- // Execute all callbacks
155
- const results = await Promise.allSettled(
156
- callbacks.map(cb => this.executeCallback(cb, eventData))
157
- )
158
-
159
- // Log any errors
160
- results.forEach((result) => {
161
- if (result.status === 'rejected') {
162
- console.error(`Event listener error for ${event}:`, result.reason)
163
- }
164
- })
165
-
166
- // Handle once listeners
167
- const onceCallbacks = this.onceListeners.get(event)
168
- if (onceCallbacks) {
169
- for (const cb of onceCallbacks) {
170
- await this.executeCallback(cb, eventData)
171
- }
172
- this.onceListeners.delete(event)
173
- }
174
-
175
- // Also trigger wildcard once listeners
176
- const wildcardOnce = this.onceListeners.get(EventTypes.ALL)
177
- if (wildcardOnce) {
178
- for (const cb of wildcardOnce) {
179
- await this.executeCallback(cb, eventData)
180
- }
181
- // Don't delete wildcard once - only for specific events
182
- }
183
- }
184
-
185
- /**
186
- * Get all listeners that match an event (including wildcards)
187
- */
188
- getMatchingListeners(event: string): EventCallback[] {
189
- const callbacks: EventCallback[] = []
190
-
191
- // Exact match
192
- const exact = this.listeners.get(event)
193
- if (exact) {
194
- callbacks.push(...exact)
195
- }
196
-
197
- // Wildcard match (*)
198
- const wildcard = this.listeners.get(EventTypes.ALL)
199
- if (wildcard) {
200
- callbacks.push(...wildcard)
201
- }
202
-
203
- // Namespace wildcard (e.g., 'session.*' matches 'session.started')
204
- const namespace = event.split('.')[0]
205
- const namespaceWildcard = this.listeners.get(`${namespace}.*`)
206
- if (namespaceWildcard) {
207
- callbacks.push(...namespaceWildcard)
208
- }
209
-
210
- return callbacks
211
- }
212
-
213
- /**
214
- * Execute a callback safely (handles sync and async)
215
- */
216
- async executeCallback(callback: EventCallback, data: EventData): Promise<void> {
217
- try {
218
- const result = callback(data)
219
- if (result instanceof Promise) {
220
- await result
221
- }
222
- } catch (error) {
223
- console.error('Event callback error:', error)
224
- throw error
225
- }
226
- }
227
-
228
- /**
229
- * Log event to persistent storage
230
- */
231
- async logEvent(eventData: EventData): Promise<void> {
232
- try {
233
- const globalPath = pathManager.getGlobalProjectPath(this.projectId!)
234
- const eventsPath = path.join(globalPath, 'memory', 'events.jsonl')
235
-
236
- // Ensure directory exists
237
- await fs.mkdir(path.dirname(eventsPath), { recursive: true })
238
-
239
- // Append event
240
- const line = JSON.stringify(eventData) + '\n'
241
- await fs.appendFile(eventsPath, line)
242
- } catch {
243
- // Silently fail - logging should not break functionality
244
- }
245
- }
246
-
247
- /**
248
- * Get recent events from history
249
- */
250
- getHistory(limit: number = 10, type: string | null = null): EventData[] {
251
- let events = this.history
252
- if (type) {
253
- events = events.filter(e => e.type === type || e.type.startsWith(type))
254
- }
255
- return events.slice(-limit)
256
- }
257
-
258
- /**
259
- * Clear all listeners
260
- */
261
- clear(): void {
262
- this.listeners.clear()
263
- this.onceListeners.clear()
264
- }
265
-
266
- /**
267
- * Get count of listeners for an event
268
- */
269
- listenerCount(event: string): number {
270
- const listeners = this.listeners.get(event)
271
- return listeners ? listeners.size : 0
272
- }
273
-
274
- /**
275
- * Get all registered event types
276
- */
277
- getRegisteredEvents(): string[] {
278
- return Array.from(this.listeners.keys())
279
- }
280
- }
281
-
282
- // Singleton instance
283
- const eventBus = new EventBus()
284
-
285
- // Convenience methods for common events
286
- const emit = {
287
- sessionStarted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_STARTED, data),
288
- sessionPaused: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_PAUSED, data),
289
- sessionResumed: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_RESUMED, data),
290
- sessionCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_COMPLETED, data),
291
-
292
- taskCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.TASK_CREATED, data),
293
- taskCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.TASK_COMPLETED, data),
294
-
295
- featureAdded: (data: Record<string, unknown>) => eventBus.emit(EventTypes.FEATURE_ADDED, data),
296
- featureShipped: (data: Record<string, unknown>) => eventBus.emit(EventTypes.FEATURE_SHIPPED, data),
297
-
298
- ideaCaptured: (data: Record<string, unknown>) => eventBus.emit(EventTypes.IDEA_CAPTURED, data),
299
-
300
- snapshotCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SNAPSHOT_CREATED, data),
301
- snapshotRestored: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SNAPSHOT_RESTORED, data),
302
-
303
- commitCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.COMMIT_CREATED, data),
304
- pushCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PUSH_COMPLETED, data),
305
-
306
- projectInitialized: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PROJECT_INITIALIZED, data),
307
- projectSynced: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PROJECT_SYNCED, data),
308
- analysisCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.ANALYSIS_COMPLETED, data)
309
- }
310
-
311
- export {
312
- EventBus,
313
- EventTypes,
314
- eventBus,
315
- emit
316
- }
317
-
318
- export default { EventBus, EventTypes, eventBus, emit }
7
+ export { EventBus, eventBus, emit, default } from './bus'
8
+ export { EventTypes, type EventData, type EventCallback, type BusEventType } from '../types'
@@ -4,7 +4,7 @@
4
4
 
5
5
  import path from 'path'
6
6
 
7
- import type { CommandResult, AnalyzeOptions, Context } from './types'
7
+ import type { CommandResult, AnalyzeOptions, ProjectContext } from '../types'
8
8
  import {
9
9
  PrjctCommandsBase,
10
10
  contextBuilder,
@@ -14,7 +14,7 @@ import {
14
14
  dateHelper
15
15
  } from './base'
16
16
  import analyzer from '../domain/analyzer'
17
- import contextSync from '../context-sync'
17
+ import { generateContext } from '../context/generator'
18
18
  import commandInstaller from '../infrastructure/command-installer'
19
19
 
20
20
  export class AnalysisCommands extends PrjctCommandsBase {
@@ -29,7 +29,7 @@ export class AnalysisCommands extends PrjctCommandsBase {
29
29
 
30
30
  analyzer.init(projectPath)
31
31
 
32
- const context = await contextBuilder.build(projectPath, options) as Context
32
+ const context = await contextBuilder.build(projectPath, options) as ProjectContext
33
33
 
34
34
  const analysisData = {
35
35
  packageJson: await analyzer.readPackageJson(),
@@ -71,7 +71,7 @@ export class AnalysisCommands extends PrjctCommandsBase {
71
71
  gitCommits: analysisData.gitStats.totalCommits,
72
72
  })
73
73
 
74
- await contextSync.generateLocalContext(projectPath, projectId)
74
+ await generateContext(projectId!, projectPath)
75
75
 
76
76
  const globalConfigResult = await commandInstaller.installGlobalConfig()
77
77
  if (globalConfigResult.success) {
@@ -217,7 +217,7 @@ export class AnalysisCommands extends PrjctCommandsBase {
217
217
 
218
218
  // 2. Generate CLAUDE.md with RAW DATA (no processing)
219
219
  // Claude will read this and decide what to do
220
- await contextSync.generateLocalContext(projectPath, projectId)
220
+ await generateContext(projectId!, projectPath)
221
221
 
222
222
  // 3. Update global config
223
223
  const globalConfigResult = await commandInstaller.installGlobalConfig()
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import path from 'path'
7
- import registry from '../command-registry'
8
7
 
9
- import type { CommandResult, Context } from './types'
8
+ import { commandRegistry } from './registry'
9
+ import type { CommandResult, ProjectContext } from '../types'
10
10
  import {
11
11
  PrjctCommandsBase,
12
12
  contextBuilder,
@@ -98,7 +98,7 @@ export class AnalyticsCommands extends PrjctCommandsBase {
98
98
 
99
99
  if (view === 'roadmap') {
100
100
  // Roadmap view
101
- const context = await contextBuilder.build(projectPath) as Context
101
+ const context = await contextBuilder.build(projectPath) as ProjectContext
102
102
  const roadmapContent = (await toolRegistry.get('Read')!(context.paths.roadmap)) as string | null
103
103
 
104
104
  console.log(`\n🗺️ ROADMAP - ${projectName}\n`)
@@ -199,22 +199,22 @@ export class AnalyticsCommands extends PrjctCommandsBase {
199
199
  try {
200
200
  if (!topic) {
201
201
  // Show command overview
202
- console.log('\n🔧 PRJCT COMMANDS\n')
203
- console.log(''.repeat(50))
202
+ console.log('\n PRJCT COMMANDS\n')
203
+ console.log('='.repeat(50))
204
204
 
205
- const categories = registry.getCategories()
206
- const commands = registry.getAll()
205
+ const categories = commandRegistry.getAllCategories()
206
+ const commands = commandRegistry.getAll()
207
207
 
208
208
  // Group by category
209
209
  const byCategory: Record<string, typeof commands> = {}
210
210
  commands.forEach(cmd => {
211
211
  if (cmd.deprecated) return
212
- if (!byCategory[cmd.category]) byCategory[cmd.category] = []
213
- byCategory[cmd.category].push(cmd)
212
+ if (!byCategory[cmd.group]) byCategory[cmd.group] = []
213
+ byCategory[cmd.group].push(cmd)
214
214
  })
215
215
 
216
216
  Object.entries(byCategory).forEach(([cat, cmds]) => {
217
- const catInfo = categories[cat]
217
+ const catInfo = categories.get(cat)
218
218
  console.log(`\n${catInfo?.title || cat}:`)
219
219
  cmds.forEach(cmd => {
220
220
  const params = cmd.params ? ` ${cmd.params}` : ''
@@ -230,7 +230,7 @@ export class AnalyticsCommands extends PrjctCommandsBase {
230
230
  }
231
231
 
232
232
  // Topic-specific help
233
- const command = registry.getByName(topic)
233
+ const command = commandRegistry.getByName(topic)
234
234
  if (command) {
235
235
  console.log(`\n📚 HELP: /p:${command.name}\n`)
236
236
  console.log('═'.repeat(50))