prjct-cli 0.45.0 → 0.45.3

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 (207) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/bin/prjct.ts +117 -10
  3. package/core/__tests__/agentic/memory-system.test.ts +39 -26
  4. package/core/__tests__/agentic/plan-mode.test.ts +64 -46
  5. package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
  6. package/core/__tests__/services/project-index.test.ts +353 -0
  7. package/core/__tests__/types/fs.test.ts +3 -3
  8. package/core/__tests__/utils/date-helper.test.ts +10 -10
  9. package/core/__tests__/utils/output.test.ts +9 -6
  10. package/core/__tests__/utils/project-commands.test.ts +5 -6
  11. package/core/agentic/agent-router.ts +9 -10
  12. package/core/agentic/chain-of-thought.ts +16 -4
  13. package/core/agentic/command-executor.ts +66 -40
  14. package/core/agentic/context-builder.ts +8 -5
  15. package/core/agentic/ground-truth.ts +15 -9
  16. package/core/agentic/index.ts +145 -152
  17. package/core/agentic/loop-detector.ts +40 -11
  18. package/core/agentic/memory-system.ts +98 -35
  19. package/core/agentic/orchestrator-executor.ts +135 -71
  20. package/core/agentic/plan-mode.ts +46 -16
  21. package/core/agentic/prompt-builder.ts +108 -42
  22. package/core/agentic/services.ts +10 -9
  23. package/core/agentic/skill-loader.ts +9 -15
  24. package/core/agentic/smart-context.ts +129 -79
  25. package/core/agentic/template-executor.ts +13 -12
  26. package/core/agentic/template-loader.ts +7 -4
  27. package/core/agentic/tool-registry.ts +16 -13
  28. package/core/agents/index.ts +1 -1
  29. package/core/agents/performance.ts +10 -27
  30. package/core/ai-tools/formatters.ts +8 -6
  31. package/core/ai-tools/generator.ts +4 -4
  32. package/core/ai-tools/index.ts +1 -1
  33. package/core/ai-tools/registry.ts +21 -11
  34. package/core/bus/bus.ts +23 -16
  35. package/core/bus/index.ts +2 -2
  36. package/core/cli/linear.ts +3 -5
  37. package/core/cli/start.ts +28 -25
  38. package/core/commands/analysis.ts +58 -39
  39. package/core/commands/analytics.ts +52 -44
  40. package/core/commands/base.ts +15 -13
  41. package/core/commands/cleanup.ts +6 -13
  42. package/core/commands/command-data.ts +28 -4
  43. package/core/commands/commands.ts +57 -24
  44. package/core/commands/context.ts +4 -4
  45. package/core/commands/design.ts +3 -10
  46. package/core/commands/index.ts +5 -8
  47. package/core/commands/maintenance.ts +7 -4
  48. package/core/commands/planning.ts +179 -56
  49. package/core/commands/register.ts +13 -9
  50. package/core/commands/registry.ts +15 -14
  51. package/core/commands/setup.ts +26 -14
  52. package/core/commands/shipping.ts +11 -16
  53. package/core/commands/snapshots.ts +16 -32
  54. package/core/commands/uninstall.ts +541 -0
  55. package/core/commands/workflow.ts +24 -28
  56. package/core/constants/index.ts +10 -22
  57. package/core/context/generator.ts +82 -33
  58. package/core/context-tools/files-tool.ts +18 -19
  59. package/core/context-tools/imports-tool.ts +13 -33
  60. package/core/context-tools/index.ts +29 -54
  61. package/core/context-tools/recent-tool.ts +16 -22
  62. package/core/context-tools/signatures-tool.ts +17 -26
  63. package/core/context-tools/summary-tool.ts +20 -22
  64. package/core/context-tools/token-counter.ts +25 -20
  65. package/core/context-tools/types.ts +5 -5
  66. package/core/domain/agent-generator.ts +7 -5
  67. package/core/domain/agent-loader.ts +2 -2
  68. package/core/domain/analyzer.ts +19 -16
  69. package/core/domain/architecture-generator.ts +6 -3
  70. package/core/domain/context-estimator.ts +3 -4
  71. package/core/domain/snapshot-manager.ts +25 -22
  72. package/core/domain/task-stack.ts +24 -14
  73. package/core/errors.ts +1 -1
  74. package/core/events/events.ts +2 -4
  75. package/core/events/index.ts +1 -2
  76. package/core/index.ts +28 -16
  77. package/core/infrastructure/agent-detector.ts +3 -3
  78. package/core/infrastructure/ai-provider.ts +23 -20
  79. package/core/infrastructure/author-detector.ts +16 -10
  80. package/core/infrastructure/capability-installer.ts +2 -2
  81. package/core/infrastructure/claude-agent.ts +6 -6
  82. package/core/infrastructure/command-installer.ts +22 -17
  83. package/core/infrastructure/config-manager.ts +18 -14
  84. package/core/infrastructure/editors-config.ts +8 -4
  85. package/core/infrastructure/path-manager.ts +8 -6
  86. package/core/infrastructure/permission-manager.ts +20 -17
  87. package/core/infrastructure/setup.ts +42 -38
  88. package/core/infrastructure/update-checker.ts +5 -5
  89. package/core/integrations/issue-tracker/enricher.ts +8 -19
  90. package/core/integrations/issue-tracker/index.ts +2 -2
  91. package/core/integrations/issue-tracker/manager.ts +15 -15
  92. package/core/integrations/issue-tracker/types.ts +5 -22
  93. package/core/integrations/jira/client.ts +67 -59
  94. package/core/integrations/jira/index.ts +11 -14
  95. package/core/integrations/jira/mcp-adapter.ts +5 -10
  96. package/core/integrations/jira/service.ts +10 -10
  97. package/core/integrations/linear/client.ts +27 -18
  98. package/core/integrations/linear/index.ts +9 -12
  99. package/core/integrations/linear/service.ts +11 -11
  100. package/core/integrations/linear/sync.ts +8 -8
  101. package/core/outcomes/analyzer.ts +5 -18
  102. package/core/outcomes/index.ts +2 -2
  103. package/core/outcomes/recorder.ts +3 -3
  104. package/core/plugin/builtin/webhook.ts +19 -15
  105. package/core/plugin/hooks.ts +29 -21
  106. package/core/plugin/index.ts +7 -7
  107. package/core/plugin/loader.ts +19 -19
  108. package/core/plugin/registry.ts +12 -23
  109. package/core/schemas/agents.ts +1 -1
  110. package/core/schemas/analysis.ts +1 -1
  111. package/core/schemas/enriched-task.ts +62 -49
  112. package/core/schemas/ideas.ts +13 -13
  113. package/core/schemas/index.ts +17 -27
  114. package/core/schemas/issues.ts +40 -25
  115. package/core/schemas/metrics.ts +25 -25
  116. package/core/schemas/outcomes.ts +70 -62
  117. package/core/schemas/permissions.ts +15 -12
  118. package/core/schemas/prd.ts +27 -14
  119. package/core/schemas/project.ts +3 -3
  120. package/core/schemas/roadmap.ts +47 -34
  121. package/core/schemas/schemas.ts +3 -4
  122. package/core/schemas/shipped.ts +3 -3
  123. package/core/schemas/state.ts +43 -29
  124. package/core/server/index.ts +5 -6
  125. package/core/server/routes-extended.ts +68 -72
  126. package/core/server/routes.ts +3 -3
  127. package/core/server/server.ts +31 -26
  128. package/core/services/agent-generator.ts +237 -0
  129. package/core/services/agent-service.ts +2 -2
  130. package/core/services/breakdown-service.ts +2 -4
  131. package/core/services/context-generator.ts +299 -0
  132. package/core/services/context-selector.ts +420 -0
  133. package/core/services/doctor-service.ts +426 -0
  134. package/core/services/file-categorizer.ts +448 -0
  135. package/core/services/file-scorer.ts +270 -0
  136. package/core/services/git-analyzer.ts +267 -0
  137. package/core/services/index.ts +27 -10
  138. package/core/services/memory-service.ts +3 -4
  139. package/core/services/project-index.ts +911 -0
  140. package/core/services/project-service.ts +4 -4
  141. package/core/services/skill-installer.ts +14 -17
  142. package/core/services/skill-lock.ts +3 -3
  143. package/core/services/skill-service.ts +12 -6
  144. package/core/services/stack-detector.ts +245 -0
  145. package/core/services/sync-service.ts +87 -345
  146. package/core/services/watch-service.ts +294 -0
  147. package/core/session/compaction.ts +23 -31
  148. package/core/session/index.ts +11 -5
  149. package/core/session/log-migration.ts +3 -3
  150. package/core/session/metrics.ts +19 -14
  151. package/core/session/session-log-manager.ts +12 -17
  152. package/core/session/task-session-manager.ts +25 -25
  153. package/core/session/utils.ts +1 -1
  154. package/core/storage/ideas-storage.ts +41 -57
  155. package/core/storage/index-storage.ts +514 -0
  156. package/core/storage/index.ts +41 -17
  157. package/core/storage/metrics-storage.ts +39 -34
  158. package/core/storage/queue-storage.ts +35 -45
  159. package/core/storage/shipped-storage.ts +17 -20
  160. package/core/storage/state-storage.ts +50 -30
  161. package/core/storage/storage-manager.ts +6 -6
  162. package/core/storage/storage.ts +18 -15
  163. package/core/sync/auth-config.ts +3 -3
  164. package/core/sync/index.ts +13 -19
  165. package/core/sync/oauth-handler.ts +3 -3
  166. package/core/sync/sync-client.ts +4 -9
  167. package/core/sync/sync-manager.ts +12 -14
  168. package/core/types/commands.ts +42 -7
  169. package/core/types/index.ts +284 -305
  170. package/core/types/integrations.ts +3 -3
  171. package/core/types/storage.ts +14 -14
  172. package/core/types/utils.ts +3 -3
  173. package/core/utils/agent-stream.ts +3 -1
  174. package/core/utils/animations.ts +14 -11
  175. package/core/utils/branding.ts +7 -7
  176. package/core/utils/cache.ts +1 -3
  177. package/core/utils/collection-filters.ts +3 -15
  178. package/core/utils/date-helper.ts +2 -7
  179. package/core/utils/file-helper.ts +13 -8
  180. package/core/utils/jsonl-helper.ts +13 -10
  181. package/core/utils/keychain.ts +4 -8
  182. package/core/utils/logger.ts +1 -1
  183. package/core/utils/next-steps.ts +3 -3
  184. package/core/utils/output.ts +58 -11
  185. package/core/utils/project-commands.ts +6 -6
  186. package/core/utils/project-credentials.ts +5 -12
  187. package/core/utils/runtime.ts +2 -2
  188. package/core/utils/session-helper.ts +3 -4
  189. package/core/utils/version.ts +3 -3
  190. package/core/wizard/index.ts +13 -0
  191. package/core/wizard/onboarding.ts +633 -0
  192. package/core/workflow/state-machine.ts +7 -7
  193. package/dist/bin/prjct.mjs +18755 -15574
  194. package/dist/core/infrastructure/command-installer.js +86 -79
  195. package/dist/core/infrastructure/editors-config.js +6 -6
  196. package/dist/core/infrastructure/setup.js +246 -225
  197. package/dist/core/utils/version.js +9 -9
  198. package/package.json +11 -12
  199. package/scripts/build.js +3 -3
  200. package/scripts/postinstall.js +2 -2
  201. package/templates/mcp-config.json +6 -1
  202. package/templates/permissions/permissive.jsonc +1 -1
  203. package/templates/permissions/strict.jsonc +5 -9
  204. package/templates/global/docs/agents.md +0 -88
  205. package/templates/global/docs/architecture.md +0 -103
  206. package/templates/global/docs/commands.md +0 -96
  207. package/templates/global/docs/validation.md +0 -95
@@ -11,16 +11,16 @@
11
11
  * - Daily trends for visualization
12
12
  */
13
13
 
14
- import { StorageManager } from './storage-manager'
15
- import { getTimestamp } from '../utils/date-helper'
16
14
  import {
17
- type MetricsJson,
18
- type DailyStats,
19
15
  type AgentUsage,
16
+ type DailyStats,
20
17
  DEFAULT_METRICS,
21
18
  estimateCostSaved,
22
19
  formatCost,
20
+ type MetricsJson,
23
21
  } from '../schemas/metrics'
22
+ import { getTimestamp } from '../utils/date-helper'
23
+ import { StorageManager } from './storage-manager'
24
24
 
25
25
  class MetricsStorage extends StorageManager<MetricsJson> {
26
26
  constructor() {
@@ -56,8 +56,12 @@ class MetricsStorage extends StorageManager<MetricsJson> {
56
56
  lines.push('## 💰 Token Savings')
57
57
  lines.push('')
58
58
  lines.push(`- **Total saved**: ${this.formatTokens(data.totalTokensSaved)} tokens`)
59
- lines.push(`- **Compression**: ${(data.avgCompressionRate * 100).toFixed(0)}% average reduction`)
60
- lines.push(`- **Estimated cost saved**: ${formatCost(estimateCostSaved(data.totalTokensSaved))}`)
59
+ lines.push(
60
+ `- **Compression**: ${(data.avgCompressionRate * 100).toFixed(0)}% average reduction`
61
+ )
62
+ lines.push(
63
+ `- **Estimated cost saved**: ${formatCost(estimateCostSaved(data.totalTokensSaved))}`
64
+ )
61
65
  lines.push('')
62
66
 
63
67
  // Performance
@@ -77,7 +81,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
77
81
  const sortedAgents = [...data.agentUsage].sort((a, b) => b.usageCount - a.usageCount)
78
82
  const totalUsage = sortedAgents.reduce((sum, a) => sum + a.usageCount, 0)
79
83
 
80
- sortedAgents.slice(0, 5).forEach(agent => {
84
+ sortedAgents.slice(0, 5).forEach((agent) => {
81
85
  const pct = totalUsage > 0 ? ((agent.usageCount / totalUsage) * 100).toFixed(0) : 0
82
86
  lines.push(`- **${agent.agentName}**: ${pct}% (${agent.usageCount} uses)`)
83
87
  })
@@ -118,17 +122,15 @@ class MetricsStorage extends StorageManager<MetricsJson> {
118
122
  async recordSync(
119
123
  projectId: string,
120
124
  metrics: {
121
- originalSize: number // Tokens before compression
122
- filteredSize: number // Tokens after compression
123
- duration: number // Sync duration in ms
124
- isWatch?: boolean // From watch mode?
125
- agents?: string[] // Agents used
125
+ originalSize: number // Tokens before compression
126
+ filteredSize: number // Tokens after compression
127
+ duration: number // Sync duration in ms
128
+ isWatch?: boolean // From watch mode?
129
+ agents?: string[] // Agents used
126
130
  }
127
131
  ): Promise<void> {
128
132
  const tokensSaved = Math.max(0, metrics.originalSize - metrics.filteredSize)
129
- const compressionRate = metrics.originalSize > 0
130
- ? tokensSaved / metrics.originalSize
131
- : 0
133
+ const compressionRate = metrics.originalSize > 0 ? tokensSaved / metrics.originalSize : 0
132
134
 
133
135
  const today = new Date().toISOString().split('T')[0]
134
136
 
@@ -139,13 +141,14 @@ class MetricsStorage extends StorageManager<MetricsJson> {
139
141
  const newTotalDuration = data.totalSyncDuration + metrics.duration
140
142
 
141
143
  // Running average for compression rate
142
- const newAvgCompression = data.syncCount === 0
143
- ? compressionRate
144
- : (data.avgCompressionRate * data.syncCount + compressionRate) / newSyncCount
144
+ const newAvgCompression =
145
+ data.syncCount === 0
146
+ ? compressionRate
147
+ : (data.avgCompressionRate * data.syncCount + compressionRate) / newSyncCount
145
148
 
146
149
  // Update daily stats
147
150
  const dailyStats = [...data.dailyStats]
148
- const todayIndex = dailyStats.findIndex(d => d.date === today)
151
+ const todayIndex = dailyStats.findIndex((d) => d.date === today)
149
152
 
150
153
  if (todayIndex >= 0) {
151
154
  const existing = dailyStats[todayIndex]
@@ -153,7 +156,8 @@ class MetricsStorage extends StorageManager<MetricsJson> {
153
156
  ...existing,
154
157
  tokensSaved: existing.tokensSaved + tokensSaved,
155
158
  syncs: existing.syncs + 1,
156
- avgCompressionRate: (existing.avgCompressionRate * existing.syncs + compressionRate) / (existing.syncs + 1),
159
+ avgCompressionRate:
160
+ (existing.avgCompressionRate * existing.syncs + compressionRate) / (existing.syncs + 1),
157
161
  totalDuration: existing.totalDuration + metrics.duration,
158
162
  }
159
163
  } else {
@@ -170,18 +174,19 @@ class MetricsStorage extends StorageManager<MetricsJson> {
170
174
  const cutoff = new Date()
171
175
  cutoff.setDate(cutoff.getDate() - 90)
172
176
  const cutoffStr = cutoff.toISOString().split('T')[0]
173
- const trimmedStats = dailyStats.filter(d => d.date >= cutoffStr)
177
+ const trimmedStats = dailyStats.filter((d) => d.date >= cutoffStr)
174
178
 
175
179
  // Update agent usage
176
180
  const agentUsage = [...data.agentUsage]
177
181
  if (metrics.agents) {
178
182
  for (const agentName of metrics.agents) {
179
- const idx = agentUsage.findIndex(a => a.agentName === agentName)
183
+ const idx = agentUsage.findIndex((a) => a.agentName === agentName)
180
184
  if (idx >= 0) {
181
185
  agentUsage[idx] = {
182
186
  ...agentUsage[idx],
183
187
  usageCount: agentUsage[idx].usageCount + 1,
184
- tokensSaved: agentUsage[idx].tokensSaved + Math.floor(tokensSaved / metrics.agents.length),
188
+ tokensSaved:
189
+ agentUsage[idx].tokensSaved + Math.floor(tokensSaved / metrics.agents.length),
185
190
  }
186
191
  } else {
187
192
  agentUsage.push({
@@ -229,9 +234,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
229
234
  const last30Tokens = last30.reduce((sum, d) => sum + d.tokensSaved, 0)
230
235
  const prev30Tokens = prev30.reduce((sum, d) => sum + d.tokensSaved, 0)
231
236
 
232
- const trend = prev30Tokens > 0
233
- ? ((last30Tokens - prev30Tokens) / prev30Tokens) * 100
234
- : 0
237
+ const trend = prev30Tokens > 0 ? ((last30Tokens - prev30Tokens) / prev30Tokens) * 100 : 0
235
238
 
236
239
  return {
237
240
  totalTokensSaved: data.totalTokensSaved,
@@ -255,7 +258,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
255
258
  const cutoffStr = cutoff.toISOString().split('T')[0]
256
259
 
257
260
  return data.dailyStats
258
- .filter(d => d.date >= cutoffStr)
261
+ .filter((d) => d.date >= cutoffStr)
259
262
  .sort((a, b) => a.date.localeCompare(b.date))
260
263
  }
261
264
 
@@ -265,7 +268,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
265
268
  const cutoff = new Date()
266
269
  cutoff.setDate(cutoff.getDate() - 30)
267
270
  const cutoffStr = cutoff.toISOString().split('T')[0]
268
- return dailyStats.filter(d => d.date >= cutoffStr)
271
+ return dailyStats.filter((d) => d.date >= cutoffStr)
269
272
  }
270
273
 
271
274
  private getPrev30Days(dailyStats: DailyStats[]): DailyStats[] {
@@ -277,7 +280,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
277
280
  const startStr = start.toISOString().split('T')[0]
278
281
  const endStr = end.toISOString().split('T')[0]
279
282
 
280
- return dailyStats.filter(d => d.date >= startStr && d.date < endStr)
283
+ return dailyStats.filter((d) => d.date >= startStr && d.date < endStr)
281
284
  }
282
285
 
283
286
  private formatTokens(tokens: number): string {
@@ -301,13 +304,15 @@ class MetricsStorage extends StorageManager<MetricsJson> {
301
304
  if (dailyStats.length === 0) return ''
302
305
 
303
306
  const chars = '▁▂▃▄▅▆▇█'
304
- const values = dailyStats.map(d => d.tokensSaved)
307
+ const values = dailyStats.map((d) => d.tokensSaved)
305
308
  const max = Math.max(...values, 1)
306
309
 
307
- return values.map(v => {
308
- const idx = Math.min(Math.floor((v / max) * (chars.length - 1)), chars.length - 1)
309
- return chars[idx]
310
- }).join('')
310
+ return values
311
+ .map((v) => {
312
+ const idx = Math.min(Math.floor((v / max) * (chars.length - 1)), chars.length - 1)
313
+ return chars[idx]
314
+ })
315
+ .join('')
311
316
  }
312
317
  }
313
318
 
@@ -5,10 +5,10 @@
5
5
  * Generates context/next.md for Claude
6
6
  */
7
7
 
8
- import { StorageManager } from './storage-manager'
9
8
  import { generateUUID } from '../schemas'
9
+ import type { Priority, QueueJson, QueueTask, TaskSection } from '../schemas/state'
10
10
  import { getTimestamp } from '../utils/date-helper'
11
- import type { QueueJson, QueueTask, Priority, TaskType, TaskSection } from '../schemas/state'
11
+ import { StorageManager } from './storage-manager'
12
12
 
13
13
  class QueueStorage extends StorageManager<QueueJson> {
14
14
  constructor() {
@@ -18,7 +18,7 @@ class QueueStorage extends StorageManager<QueueJson> {
18
18
  protected getDefault(): QueueJson {
19
19
  return {
20
20
  tasks: [],
21
- lastUpdated: ''
21
+ lastUpdated: '',
22
22
  }
23
23
  }
24
24
 
@@ -37,9 +37,11 @@ class QueueStorage extends StorageManager<QueueJson> {
37
37
  protected toMarkdown(data: QueueJson): string {
38
38
  const lines = ['# Priority Queue', '']
39
39
 
40
- const activeTasks = data.tasks.filter(t => t.section === 'active' && !t.completed)
41
- const backlogTasks = data.tasks.filter(t => t.section === 'backlog' && !t.completed)
42
- const previouslyActive = data.tasks.filter(t => t.section === 'previously_active' && !t.completed)
40
+ const activeTasks = data.tasks.filter((t) => t.section === 'active' && !t.completed)
41
+ const backlogTasks = data.tasks.filter((t) => t.section === 'backlog' && !t.completed)
42
+ const previouslyActive = data.tasks.filter(
43
+ (t) => t.section === 'previously_active' && !t.completed
44
+ )
43
45
 
44
46
  // Active section
45
47
  lines.push('## Active Tasks')
@@ -60,7 +62,7 @@ class QueueStorage extends StorageManager<QueueJson> {
60
62
  // Previously active section (if any)
61
63
  if (previouslyActive.length > 0) {
62
64
  lines.push('## Previously Active')
63
- previouslyActive.forEach(task => {
65
+ previouslyActive.forEach((task) => {
64
66
  lines.push(`- [ ] ${task.description}`)
65
67
  })
66
68
  lines.push('')
@@ -69,7 +71,7 @@ class QueueStorage extends StorageManager<QueueJson> {
69
71
  // Backlog section
70
72
  lines.push('## Backlog')
71
73
  if (backlogTasks.length > 0) {
72
- backlogTasks.forEach(task => {
74
+ backlogTasks.forEach((task) => {
73
75
  const priority = task.priority !== 'medium' ? ` [${task.priority.toUpperCase()}]` : ''
74
76
  const bug = task.type === 'bug' ? ' \u{1F41B}' : ''
75
77
  lines.push(`- [ ]${bug}${priority} ${task.description}`)
@@ -97,7 +99,7 @@ class QueueStorage extends StorageManager<QueueJson> {
97
99
  */
98
100
  async getActiveTasks(projectId: string): Promise<QueueTask[]> {
99
101
  const queue = await this.read(projectId)
100
- return queue.tasks.filter(t => t.section === 'active' && !t.completed)
102
+ return queue.tasks.filter((t) => t.section === 'active' && !t.completed)
101
103
  }
102
104
 
103
105
  /**
@@ -105,7 +107,7 @@ class QueueStorage extends StorageManager<QueueJson> {
105
107
  */
106
108
  async getBacklog(projectId: string): Promise<QueueTask[]> {
107
109
  const queue = await this.read(projectId)
108
- return queue.tasks.filter(t => t.section === 'backlog' && !t.completed)
110
+ return queue.tasks.filter((t) => t.section === 'backlog' && !t.completed)
109
111
  }
110
112
 
111
113
  /**
@@ -127,12 +129,12 @@ class QueueStorage extends StorageManager<QueueJson> {
127
129
  ...task,
128
130
  id: generateUUID(),
129
131
  createdAt: getTimestamp(),
130
- completed: false
132
+ completed: false,
131
133
  }
132
134
 
133
135
  await this.update(projectId, (queue) => ({
134
136
  tasks: [...queue.tasks, newTask],
135
- lastUpdated: getTimestamp()
137
+ lastUpdated: getTimestamp(),
136
138
  }))
137
139
 
138
140
  // Publish incremental event
@@ -140,7 +142,7 @@ class QueueStorage extends StorageManager<QueueJson> {
140
142
  taskId: newTask.id,
141
143
  description: newTask.description,
142
144
  priority: newTask.priority,
143
- section: newTask.section
145
+ section: newTask.section,
144
146
  })
145
147
 
146
148
  return newTask
@@ -154,22 +156,22 @@ class QueueStorage extends StorageManager<QueueJson> {
154
156
  tasks: Omit<QueueTask, 'id' | 'createdAt' | 'completed' | 'completedAt'>[]
155
157
  ): Promise<QueueTask[]> {
156
158
  const now = getTimestamp()
157
- const newTasks: QueueTask[] = tasks.map(task => ({
159
+ const newTasks: QueueTask[] = tasks.map((task) => ({
158
160
  ...task,
159
161
  id: generateUUID(),
160
162
  createdAt: now,
161
- completed: false
163
+ completed: false,
162
164
  }))
163
165
 
164
166
  await this.update(projectId, (queue) => ({
165
167
  tasks: [...queue.tasks, ...newTasks],
166
- lastUpdated: now
168
+ lastUpdated: now,
167
169
  }))
168
170
 
169
171
  // Publish event for batch add
170
172
  await this.publishEvent(projectId, 'queue.tasks_added', {
171
173
  count: newTasks.length,
172
- tasks: newTasks.map(t => ({ id: t.id, description: t.description }))
174
+ tasks: newTasks.map((t) => ({ id: t.id, description: t.description })),
173
175
  })
174
176
 
175
177
  return newTasks
@@ -180,8 +182,8 @@ class QueueStorage extends StorageManager<QueueJson> {
180
182
  */
181
183
  async removeTask(projectId: string, taskId: string): Promise<void> {
182
184
  await this.update(projectId, (queue) => ({
183
- tasks: queue.tasks.filter(t => t.id !== taskId),
184
- lastUpdated: getTimestamp()
185
+ tasks: queue.tasks.filter((t) => t.id !== taskId),
186
+ lastUpdated: getTimestamp(),
185
187
  }))
186
188
 
187
189
  await this.publishEvent(projectId, 'queue.task_removed', { taskId })
@@ -194,12 +196,12 @@ class QueueStorage extends StorageManager<QueueJson> {
194
196
  let completedTask: QueueTask | null = null
195
197
 
196
198
  await this.update(projectId, (queue) => {
197
- const tasks = queue.tasks.map(t => {
199
+ const tasks = queue.tasks.map((t) => {
198
200
  if (t.id === taskId) {
199
201
  completedTask = {
200
202
  ...t,
201
203
  completed: true,
202
- completedAt: getTimestamp()
204
+ completedAt: getTimestamp(),
203
205
  }
204
206
  return completedTask
205
207
  }
@@ -213,7 +215,7 @@ class QueueStorage extends StorageManager<QueueJson> {
213
215
  await this.publishEvent(projectId, 'queue.task_completed', {
214
216
  taskId,
215
217
  description: task.description,
216
- completedAt: task.completedAt
218
+ completedAt: task.completedAt,
217
219
  })
218
220
  }
219
221
 
@@ -223,32 +225,20 @@ class QueueStorage extends StorageManager<QueueJson> {
223
225
  /**
224
226
  * Move task to different section
225
227
  */
226
- async moveToSection(
227
- projectId: string,
228
- taskId: string,
229
- section: TaskSection
230
- ): Promise<void> {
228
+ async moveToSection(projectId: string, taskId: string, section: TaskSection): Promise<void> {
231
229
  await this.update(projectId, (queue) => ({
232
- tasks: queue.tasks.map(t =>
233
- t.id === taskId ? { ...t, section } : t
234
- ),
235
- lastUpdated: getTimestamp()
230
+ tasks: queue.tasks.map((t) => (t.id === taskId ? { ...t, section } : t)),
231
+ lastUpdated: getTimestamp(),
236
232
  }))
237
233
  }
238
234
 
239
235
  /**
240
236
  * Set task priority
241
237
  */
242
- async setPriority(
243
- projectId: string,
244
- taskId: string,
245
- priority: Priority
246
- ): Promise<void> {
238
+ async setPriority(projectId: string, taskId: string, priority: Priority): Promise<void> {
247
239
  await this.update(projectId, (queue) => ({
248
- tasks: queue.tasks.map(t =>
249
- t.id === taskId ? { ...t, priority } : t
250
- ),
251
- lastUpdated: getTimestamp()
240
+ tasks: queue.tasks.map((t) => (t.id === taskId ? { ...t, priority } : t)),
241
+ lastUpdated: getTimestamp(),
252
242
  }))
253
243
  }
254
244
 
@@ -257,11 +247,11 @@ class QueueStorage extends StorageManager<QueueJson> {
257
247
  */
258
248
  async clearCompleted(projectId: string): Promise<number> {
259
249
  const queue = await this.read(projectId)
260
- const completedCount = queue.tasks.filter(t => t.completed).length
250
+ const completedCount = queue.tasks.filter((t) => t.completed).length
261
251
 
262
252
  await this.update(projectId, (q) => ({
263
- tasks: q.tasks.filter(t => !t.completed),
264
- lastUpdated: getTimestamp()
253
+ tasks: q.tasks.filter((t) => !t.completed),
254
+ lastUpdated: getTimestamp(),
265
255
  }))
266
256
 
267
257
  return completedCount
@@ -275,13 +265,13 @@ class QueueStorage extends StorageManager<QueueJson> {
275
265
  critical: 0,
276
266
  high: 1,
277
267
  medium: 2,
278
- low: 3
268
+ low: 3,
279
269
  }
280
270
 
281
271
  const sectionOrder: Record<TaskSection, number> = {
282
272
  active: 0,
283
273
  previously_active: 1,
284
- backlog: 2
274
+ backlog: 2,
285
275
  }
286
276
 
287
277
  return [...tasks].sort((a, b) => {
@@ -5,10 +5,10 @@
5
5
  * Generates context/shipped.md for Claude
6
6
  */
7
7
 
8
- import { StorageManager } from './storage-manager'
9
8
  import { generateUUID } from '../schemas'
10
- import { getTimestamp } from '../utils/date-helper'
11
9
  import type { ShippedFeature, ShippedJson } from '../types'
10
+ import { getTimestamp } from '../utils/date-helper'
11
+ import { StorageManager } from './storage-manager'
12
12
 
13
13
  class ShippedStorage extends StorageManager<ShippedJson> {
14
14
  constructor() {
@@ -18,7 +18,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
18
18
  protected getDefault(): ShippedJson {
19
19
  return {
20
20
  shipped: [],
21
- lastUpdated: ''
21
+ lastUpdated: '',
22
22
  }
23
23
  }
24
24
 
@@ -46,7 +46,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
46
46
  // Group by month
47
47
  const byMonth = new Map<string, ShippedFeature[]>()
48
48
 
49
- data.shipped.forEach(ship => {
49
+ data.shipped.forEach((ship) => {
50
50
  const date = new Date(ship.shippedAt)
51
51
  const month = date.toLocaleDateString('en-US', { year: 'numeric', month: 'long' })
52
52
 
@@ -63,18 +63,18 @@ class ShippedStorage extends StorageManager<ShippedJson> {
63
63
  return dateB.getTime() - dateA.getTime()
64
64
  })
65
65
 
66
- sortedMonths.forEach(month => {
66
+ sortedMonths.forEach((month) => {
67
67
  lines.push(`## ${month}`)
68
68
  lines.push('')
69
69
 
70
- const ships = byMonth.get(month)!.sort(
71
- (a, b) => new Date(b.shippedAt).getTime() - new Date(a.shippedAt).getTime()
72
- )
70
+ const ships = byMonth
71
+ .get(month)!
72
+ .sort((a, b) => new Date(b.shippedAt).getTime() - new Date(a.shippedAt).getTime())
73
73
 
74
- ships.forEach(ship => {
74
+ ships.forEach((ship) => {
75
75
  const date = new Date(ship.shippedAt).toLocaleDateString('en-US', {
76
76
  month: 'short',
77
- day: 'numeric'
77
+ day: 'numeric',
78
78
  })
79
79
  const version = ship.version ? ` v${ship.version}` : ''
80
80
  const duration = ship.duration ? ` (${ship.duration})` : ''
@@ -126,12 +126,12 @@ class ShippedStorage extends StorageManager<ShippedJson> {
126
126
  const shipped: ShippedFeature = {
127
127
  ...feature,
128
128
  id: generateUUID(),
129
- shippedAt: getTimestamp()
129
+ shippedAt: getTimestamp(),
130
130
  }
131
131
 
132
132
  await this.update(projectId, (data) => ({
133
133
  shipped: [shipped, ...data.shipped], // Prepend
134
- lastUpdated: getTimestamp()
134
+ lastUpdated: getTimestamp(),
135
135
  }))
136
136
 
137
137
  // Publish event
@@ -139,7 +139,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
139
139
  shipId: shipped.id,
140
140
  name: shipped.name,
141
141
  version: shipped.version,
142
- shippedAt: shipped.shippedAt
142
+ shippedAt: shipped.shippedAt,
143
143
  })
144
144
 
145
145
  return shipped
@@ -148,12 +148,9 @@ class ShippedStorage extends StorageManager<ShippedJson> {
148
148
  /**
149
149
  * Get shipped by version
150
150
  */
151
- async getByVersion(
152
- projectId: string,
153
- version: string
154
- ): Promise<ShippedFeature | undefined> {
151
+ async getByVersion(projectId: string, version: string): Promise<ShippedFeature | undefined> {
155
152
  const data = await this.read(projectId)
156
- return data.shipped.find(s => s.version === version)
153
+ return data.shipped.find((s) => s.version === version)
157
154
  }
158
155
 
159
156
  /**
@@ -173,7 +170,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
173
170
  endDate: Date
174
171
  ): Promise<ShippedFeature[]> {
175
172
  const data = await this.read(projectId)
176
- return data.shipped.filter(s => {
173
+ return data.shipped.filter((s) => {
177
174
  const date = new Date(s.shippedAt)
178
175
  return date >= startDate && date <= endDate
179
176
  })
@@ -205,7 +202,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
205
202
 
206
203
  return {
207
204
  count: shipped.length,
208
- period
205
+ period,
209
206
  }
210
207
  }
211
208
  }