prjct-cli 0.45.0 → 0.45.4

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 +82 -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
@@ -32,7 +32,7 @@ export const VarianceReasonSchema = z.enum([
32
32
  'requirements_changed',
33
33
  'optimistic_estimate',
34
34
  'team_changes',
35
- 'other'
35
+ 'other',
36
36
  ])
37
37
 
38
38
  // -----------------------------------------------------------------------------
@@ -50,11 +50,11 @@ export const EffortComparisonSchema = z.object({
50
50
  commits: z.number().optional(),
51
51
  linesAdded: z.number().optional(),
52
52
  linesRemoved: z.number().optional(),
53
- sessions: z.number().optional(), // Number of work sessions
53
+ sessions: z.number().optional(), // Number of work sessions
54
54
  }),
55
55
  variance: z.object({
56
- hours: z.number(), // actual - estimated
57
- percentage: z.number(), // ((actual - estimated) / estimated) * 100
56
+ hours: z.number(), // actual - estimated
57
+ percentage: z.number(), // ((actual - estimated) / estimated) * 100
58
58
  reason: VarianceReasonSchema.optional(),
59
59
  explanation: z.string().optional(),
60
60
  }),
@@ -70,8 +70,8 @@ export const MetricResultSchema = z.object({
70
70
  target: z.number(),
71
71
  actual: z.number(),
72
72
  unit: z.string(),
73
- achieved: z.boolean(), // actual >= target (or <= for decrease metrics)
74
- percentOfTarget: z.number(), // (actual / target) * 100
73
+ achieved: z.boolean(), // actual >= target (or <= for decrease metrics)
74
+ percentOfTarget: z.number(), // (actual / target) * 100
75
75
  })
76
76
 
77
77
  export const AcceptanceCriteriaResultSchema = z.object({
@@ -84,7 +84,7 @@ export const SuccessTrackingSchema = z.object({
84
84
  metrics: z.array(MetricResultSchema),
85
85
  acceptanceCriteria: z.array(AcceptanceCriteriaResultSchema),
86
86
  overallSuccess: SuccessLevelSchema,
87
- successScore: z.number().min(0).max(100), // Percentage of metrics/criteria met
87
+ successScore: z.number().min(0).max(100), // Percentage of metrics/criteria met
88
88
  })
89
89
 
90
90
  // -----------------------------------------------------------------------------
@@ -100,11 +100,11 @@ export const LearningSchema = z.object({
100
100
  'tooling',
101
101
  'architecture',
102
102
  'testing',
103
- 'other'
103
+ 'other',
104
104
  ]),
105
105
  insight: z.string(),
106
106
  actionable: z.boolean(),
107
- action: z.string().optional(), // What to do differently next time
107
+ action: z.string().optional(), // What to do differently next time
108
108
  })
109
109
 
110
110
  export const LearningsSchema = z.object({
@@ -119,7 +119,7 @@ export const LearningsSchema = z.object({
119
119
  // -----------------------------------------------------------------------------
120
120
 
121
121
  export const ROIAssessmentSchema = z.object({
122
- valueDelivered: z.number().min(1).max(10), // Subjective 1-10 score
122
+ valueDelivered: z.number().min(1).max(10), // Subjective 1-10 score
123
123
  userImpact: z.enum(['none', 'low', 'medium', 'high', 'critical']),
124
124
  businessImpact: z.enum(['none', 'low', 'medium', 'high', 'critical']),
125
125
 
@@ -140,7 +140,7 @@ export const ROIAssessmentSchema = z.object({
140
140
  // -----------------------------------------------------------------------------
141
141
 
142
142
  export const TaskOutcomeSchema = z.object({
143
- id: z.string(), // out_task_xxxxxxxx
143
+ id: z.string(), // out_task_xxxxxxxx
144
144
  taskId: z.string(),
145
145
  description: z.string(),
146
146
 
@@ -167,12 +167,12 @@ export const TaskOutcomeSchema = z.object({
167
167
  // -----------------------------------------------------------------------------
168
168
 
169
169
  export const FeatureOutcomeSchema = z.object({
170
- id: z.string(), // out_feat_xxxxxxxx
170
+ id: z.string(), // out_feat_xxxxxxxx
171
171
 
172
172
  // Links
173
173
  featureId: z.string(),
174
174
  featureName: z.string(),
175
- prdId: z.string().nullable(), // null for legacy features
175
+ prdId: z.string().nullable(), // null for legacy features
176
176
 
177
177
  // Version info
178
178
  version: z.string().optional(),
@@ -200,11 +200,11 @@ export const FeatureOutcomeSchema = z.object({
200
200
  // Timestamps
201
201
  startedAt: z.string(),
202
202
  shippedAt: z.string(),
203
- reviewedAt: z.string().optional(), // When impact was captured
203
+ reviewedAt: z.string().optional(), // When impact was captured
204
204
 
205
205
  // Metadata
206
- reviewedBy: z.string().optional(), // Who filled out the impact review
207
- legacy: z.boolean().optional(), // Legacy feature (no PRD)
206
+ reviewedBy: z.string().optional(), // Who filled out the impact review
207
+ legacy: z.boolean().optional(), // Legacy feature (no PRD)
208
208
  })
209
209
 
210
210
  // -----------------------------------------------------------------------------
@@ -213,8 +213,8 @@ export const FeatureOutcomeSchema = z.object({
213
213
 
214
214
  export const AggregateMetricsSchema = z.object({
215
215
  totalFeatures: z.number(),
216
- averageEstimationAccuracy: z.number(), // Percentage
217
- averageSuccessRate: z.number(), // Percentage
216
+ averageEstimationAccuracy: z.number(), // Percentage
217
+ averageSuccessRate: z.number(), // Percentage
218
218
  averageROI: z.number(),
219
219
 
220
220
  // By category
@@ -226,17 +226,21 @@ export const AggregateMetricsSchema = z.object({
226
226
  }),
227
227
 
228
228
  // Variance patterns
229
- variancePatterns: z.array(z.object({
230
- reason: VarianceReasonSchema,
231
- count: z.number(),
232
- averageVariance: z.number(),
233
- })),
229
+ variancePatterns: z.array(
230
+ z.object({
231
+ reason: VarianceReasonSchema,
232
+ count: z.number(),
233
+ averageVariance: z.number(),
234
+ })
235
+ ),
234
236
 
235
237
  // Top learnings (aggregated)
236
- topLearnings: z.array(z.object({
237
- insight: z.string(),
238
- frequency: z.number(),
239
- })),
238
+ topLearnings: z.array(
239
+ z.object({
240
+ insight: z.string(),
241
+ frequency: z.number(),
242
+ })
243
+ ),
240
244
  })
241
245
 
242
246
  // -----------------------------------------------------------------------------
@@ -245,7 +249,7 @@ export const AggregateMetricsSchema = z.object({
245
249
 
246
250
  export const OutcomesJsonSchema = z.object({
247
251
  outcomes: z.array(FeatureOutcomeSchema),
248
- taskOutcomes: z.array(TaskOutcomeSchema).optional(), // Standalone task outcomes
252
+ taskOutcomes: z.array(TaskOutcomeSchema).optional(), // Standalone task outcomes
249
253
  aggregates: AggregateMetricsSchema.optional(),
250
254
  lastUpdated: z.string(),
251
255
  lastAggregated: z.string().optional(),
@@ -286,7 +290,8 @@ export const parseOutcomes = (data: unknown): OutcomesJson => OutcomesJsonSchema
286
290
  export const safeParseOutcomes = (data: unknown) => OutcomesJsonSchema.safeParse(data)
287
291
 
288
292
  /** Parse a single feature outcome */
289
- export const parseFeatureOutcome = (data: unknown): FeatureOutcome => FeatureOutcomeSchema.parse(data)
293
+ export const parseFeatureOutcome = (data: unknown): FeatureOutcome =>
294
+ FeatureOutcomeSchema.parse(data)
290
295
  export const safeParseFeatureOutcome = (data: unknown) => FeatureOutcomeSchema.safeParse(data)
291
296
 
292
297
  // =============================================================================
@@ -335,7 +340,7 @@ export const calculateVariance = (
335
340
  */
336
341
  export const calculateROIScore = (valueDelivered: number, actualHours: number): number => {
337
342
  if (actualHours <= 0) return valueDelivered * 10
338
- return Math.round((valueDelivered * 10 / actualHours) * 100) / 100
343
+ return Math.round(((valueDelivered * 10) / actualHours) * 100) / 100
339
344
  }
340
345
 
341
346
  /**
@@ -348,8 +353,8 @@ export const calculateSuccessScore = (
348
353
  const totalItems = metrics.length + acceptanceCriteria.length
349
354
  if (totalItems === 0) return 100
350
355
 
351
- const metMetrics = metrics.filter(m => m.achieved).length
352
- const metCriteria = acceptanceCriteria.filter(ac => ac.met).length
356
+ const metMetrics = metrics.filter((m) => m.achieved).length
357
+ const metCriteria = acceptanceCriteria.filter((ac) => ac.met).length
353
358
 
354
359
  return Math.round(((metMetrics + metCriteria) / totalItems) * 100)
355
360
  }
@@ -389,34 +394,33 @@ export const aggregateOutcomes = (outcomes: FeatureOutcome[]): AggregateMetrics
389
394
  }
390
395
 
391
396
  // Calculate averages
392
- const accuracies = outcomes.map(o =>
393
- calculateEstimationAccuracy(o.effort.variance.percentage)
394
- )
395
- const successRates = outcomes
396
- .filter(o => o.success)
397
- .map(o => o.success!.successScore)
398
- const rois = outcomes.map(o => o.roi.roiScore)
397
+ const accuracies = outcomes.map((o) => calculateEstimationAccuracy(o.effort.variance.percentage))
398
+ const successRates = outcomes.filter((o) => o.success).map((o) => o.success!.successScore)
399
+ const rois = outcomes.map((o) => o.roi.roiScore)
399
400
 
400
401
  // Count by success level
401
402
  const bySuccessLevel = {
402
- exceeded: outcomes.filter(o => o.success?.overallSuccess === 'exceeded').length,
403
- met: outcomes.filter(o => o.success?.overallSuccess === 'met').length,
404
- partial: outcomes.filter(o => o.success?.overallSuccess === 'partial').length,
405
- failed: outcomes.filter(o => o.success?.overallSuccess === 'failed').length,
403
+ exceeded: outcomes.filter((o) => o.success?.overallSuccess === 'exceeded').length,
404
+ met: outcomes.filter((o) => o.success?.overallSuccess === 'met').length,
405
+ partial: outcomes.filter((o) => o.success?.overallSuccess === 'partial').length,
406
+ failed: outcomes.filter((o) => o.success?.overallSuccess === 'failed').length,
406
407
  }
407
408
 
408
409
  // Variance patterns
409
410
  const varianceReasons = outcomes
410
- .filter(o => o.effort.variance.reason)
411
- .reduce((acc, o) => {
412
- const reason = o.effort.variance.reason!
413
- if (!acc[reason]) {
414
- acc[reason] = { count: 0, totalVariance: 0 }
415
- }
416
- acc[reason].count++
417
- acc[reason].totalVariance += o.effort.variance.percentage
418
- return acc
419
- }, {} as Record<VarianceReason, { count: number; totalVariance: number }>)
411
+ .filter((o) => o.effort.variance.reason)
412
+ .reduce(
413
+ (acc, o) => {
414
+ const reason = o.effort.variance.reason!
415
+ if (!acc[reason]) {
416
+ acc[reason] = { count: 0, totalVariance: 0 }
417
+ }
418
+ acc[reason].count++
419
+ acc[reason].totalVariance += o.effort.variance.percentage
420
+ return acc
421
+ },
422
+ {} as Record<VarianceReason, { count: number; totalVariance: number }>
423
+ )
420
424
 
421
425
  const variancePatterns = Object.entries(varianceReasons).map(([reason, data]) => ({
422
426
  reason: reason as VarianceReason,
@@ -425,14 +429,17 @@ export const aggregateOutcomes = (outcomes: FeatureOutcome[]): AggregateMetrics
425
429
  }))
426
430
 
427
431
  // Top learnings
428
- const allLearnings = outcomes.flatMap(o => [
432
+ const allLearnings = outcomes.flatMap((o) => [
429
433
  ...o.learnings.whatWorked,
430
434
  ...o.learnings.whatDidnt,
431
435
  ])
432
- const learningCounts = allLearnings.reduce((acc, learning) => {
433
- acc[learning] = (acc[learning] || 0) + 1
434
- return acc
435
- }, {} as Record<string, number>)
436
+ const learningCounts = allLearnings.reduce(
437
+ (acc, learning) => {
438
+ acc[learning] = (acc[learning] || 0) + 1
439
+ return acc
440
+ },
441
+ {} as Record<string, number>
442
+ )
436
443
 
437
444
  const topLearnings = Object.entries(learningCounts)
438
445
  .sort((a, b) => b[1] - a[1])
@@ -444,10 +451,11 @@ export const aggregateOutcomes = (outcomes: FeatureOutcome[]): AggregateMetrics
444
451
  averageEstimationAccuracy: Math.round(
445
452
  accuracies.reduce((a, b) => a + b, 0) / accuracies.length
446
453
  ),
447
- averageSuccessRate: successRates.length > 0
448
- ? Math.round(successRates.reduce((a, b) => a + b, 0) / successRates.length)
449
- : 0,
450
- averageROI: Math.round(rois.reduce((a, b) => a + b, 0) / rois.length * 100) / 100,
454
+ averageSuccessRate:
455
+ successRates.length > 0
456
+ ? Math.round(successRates.reduce((a, b) => a + b, 0) / successRates.length)
457
+ : 0,
458
+ averageROI: Math.round((rois.reduce((a, b) => a + b, 0) / rois.length) * 100) / 100,
451
459
  bySuccessLevel,
452
460
  variancePatterns,
453
461
  topLearnings,
@@ -38,11 +38,13 @@ export const PermissionsConfigSchema = z.object({
38
38
  bash: BashPermissionSchema.optional(),
39
39
 
40
40
  /** File operation permissions - glob patterns */
41
- files: z.object({
42
- read: FilePermissionSchema.optional(),
43
- write: FilePermissionSchema.optional(),
44
- delete: FilePermissionSchema.optional(),
45
- }).optional(),
41
+ files: z
42
+ .object({
43
+ read: FilePermissionSchema.optional(),
44
+ write: FilePermissionSchema.optional(),
45
+ delete: FilePermissionSchema.optional(),
46
+ })
47
+ .optional(),
46
48
 
47
49
  /** Web fetch permissions */
48
50
  web: WebPermissionSchema.optional(),
@@ -51,10 +53,12 @@ export const PermissionsConfigSchema = z.object({
51
53
  skills: z.record(z.string(), PermissionLevelSchema).optional(),
52
54
 
53
55
  /** Doom loop protection - prevent infinite retries */
54
- doomLoop: z.object({
55
- enabled: z.boolean().default(true),
56
- maxRetries: z.number().default(3),
57
- }).optional(),
56
+ doomLoop: z
57
+ .object({
58
+ enabled: z.boolean().default(true),
59
+ maxRetries: z.number().default(3),
60
+ })
61
+ .optional(),
58
62
 
59
63
  /** External directory access */
60
64
  externalDirectories: PermissionLevelSchema.default('ask'),
@@ -117,7 +121,7 @@ export const DEFAULT_BASH_ASK: string[] = [
117
121
  export const DEFAULT_BASH_DENY: string[] = [
118
122
  'rm -rf /*',
119
123
  'rm -rf ~/*',
120
- ':(){ :|:& };:*', // Fork bomb
124
+ ':(){ :|:& };:*', // Fork bomb
121
125
  'mkfs*',
122
126
  'dd if=*of=/dev/*',
123
127
  ]
@@ -167,8 +171,7 @@ export function buildDefaultPermissions(): PermissionsConfig {
167
171
  export const parsePermissions = (data: unknown): PermissionsConfig =>
168
172
  PermissionsConfigSchema.parse(data)
169
173
 
170
- export const safeParsePermissions = (data: unknown) =>
171
- PermissionsConfigSchema.safeParse(data)
174
+ export const safeParsePermissions = (data: unknown) => PermissionsConfigSchema.safeParse(data)
172
175
 
173
176
  // =============================================================================
174
177
  // Defaults
@@ -16,7 +16,13 @@ import { z } from 'zod'
16
16
  // Zod Schemas - Source of Truth
17
17
  // =============================================================================
18
18
 
19
- export const PRDStatusSchema = z.enum(['draft', 'approved', 'in_progress', 'completed', 'cancelled'])
19
+ export const PRDStatusSchema = z.enum([
20
+ 'draft',
21
+ 'approved',
22
+ 'in_progress',
23
+ 'completed',
24
+ 'cancelled',
25
+ ])
20
26
  export const PRDSizeSchema = z.enum(['XS', 'S', 'M', 'L', 'XL'])
21
27
  export const ImpactLevelSchema = z.enum(['critical', 'high', 'medium', 'low'])
22
28
  export const ConfidenceLevelSchema = z.enum(['low', 'medium', 'high'])
@@ -87,10 +93,14 @@ export const APIEndpointSchema = z.object({
87
93
  auth: z.enum(['required', 'optional', 'none']),
88
94
  input: z.record(z.string()).optional(),
89
95
  output: z.record(z.string()).optional(),
90
- errors: z.array(z.object({
91
- code: z.number(),
92
- description: z.string(),
93
- })).optional(),
96
+ errors: z
97
+ .array(
98
+ z.object({
99
+ code: z.number(),
100
+ description: z.string(),
101
+ })
102
+ )
103
+ .optional(),
94
104
  })
95
105
 
96
106
  export const APIContractsSchema = z.object({
@@ -180,10 +190,10 @@ export const ImplementationPhaseSchema = z.object({
180
190
  })
181
191
 
182
192
  export const MVPScopeSchema = z.object({
183
- p0: z.array(z.string()), // Must-have
184
- p1: z.array(z.string()), // Should-have
185
- p2: z.array(z.string()), // Nice-to-have
186
- p3: z.array(z.string()), // Future
193
+ p0: z.array(z.string()), // Must-have
194
+ p1: z.array(z.string()), // Should-have
195
+ p2: z.array(z.string()), // Nice-to-have
196
+ p3: z.array(z.string()), // Future
187
197
  })
188
198
 
189
199
  export const ImplementationRoadmapSchema = z.object({
@@ -265,7 +275,7 @@ export const PRDOutcomesSchema = z.object({
265
275
  // -----------------------------------------------------------------------------
266
276
 
267
277
  export const PRDItemSchema = z.object({
268
- id: z.string(), // prd_xxxxxxxx
278
+ id: z.string(), // prd_xxxxxxxx
269
279
  title: z.string(),
270
280
  status: PRDStatusSchema,
271
281
  size: PRDSizeSchema,
@@ -286,9 +296,9 @@ export const PRDItemSchema = z.object({
286
296
  value: ValueAssessmentSchema.optional(),
287
297
 
288
298
  // Links (for integration)
289
- featureId: z.string().nullable(), // Link to roadmap feature
290
- phase: z.string().nullable(), // P0, P1, etc.
291
- quarter: z.string().nullable(), // Q1-2026, etc.
299
+ featureId: z.string().nullable(), // Link to roadmap feature
300
+ phase: z.string().nullable(), // P0, P1, etc.
301
+ quarter: z.string().nullable(), // Q1-2026, etc.
292
302
 
293
303
  // Metadata
294
304
  createdAt: z.string(),
@@ -358,7 +368,10 @@ export const safeParsePRD = (data: unknown) => PRDItemSchema.safeParse(data)
358
368
  // Defaults
359
369
  // =============================================================================
360
370
 
361
- export const DEFAULT_PRD: Omit<PRDItem, 'id' | 'title' | 'problem' | 'roadmap' | 'estimation' | 'successCriteria'> = {
371
+ export const DEFAULT_PRD: Omit<
372
+ PRDItem,
373
+ 'id' | 'title' | 'problem' | 'roadmap' | 'estimation' | 'successCriteria'
374
+ > = {
362
375
  status: 'draft',
363
376
  size: 'M',
364
377
  featureId: null,
@@ -23,8 +23,8 @@ export const ProjectItemSchema = z.object({
23
23
  techStack: z.array(z.string()),
24
24
  fileCount: z.number(),
25
25
  commitCount: z.number(),
26
- createdAt: z.string(), // ISO8601
27
- lastSync: z.string(), // ISO8601
26
+ createdAt: z.string(), // ISO8601
27
+ lastSync: z.string(), // ISO8601
28
28
  })
29
29
 
30
30
  // =============================================================================
@@ -50,5 +50,5 @@ export const DEFAULT_PROJECT: Omit<ProjectSchema, 'projectId' | 'name' | 'repoPa
50
50
  fileCount: 0,
51
51
  commitCount: 0,
52
52
  createdAt: new Date().toISOString(),
53
- lastSync: new Date().toISOString()
53
+ lastSync: new Date().toISOString(),
54
54
  }
@@ -23,20 +23,25 @@ import { z } from 'zod'
23
23
 
24
24
  export const FeatureStatusSchema = z.enum(['planned', 'active', 'completed', 'shipped'])
25
25
  export const FeatureImpactSchema = z.enum(['low', 'medium', 'high'])
26
- export const FeatureTypeSchema = z.enum(['feature', 'breaking_change', 'refactor', 'infrastructure'])
26
+ export const FeatureTypeSchema = z.enum([
27
+ 'feature',
28
+ 'breaking_change',
29
+ 'refactor',
30
+ 'infrastructure',
31
+ ])
27
32
  export const PhaseStatusSchema = z.enum(['completed', 'active', 'planned'])
28
33
  export const QuarterStatusSchema = z.enum(['planned', 'active', 'completed'])
29
34
  export const InferredFromSchema = z.enum(['git', 'git-branch', 'manual', 'prd'])
30
35
 
31
36
  export const FeatureTaskSchema = z.object({
32
- id: z.string(), // task_xxxxxxxx
37
+ id: z.string(), // task_xxxxxxxx
33
38
  description: z.string(),
34
39
  completed: z.boolean(),
35
40
  completedAt: z.string().optional(),
36
41
  })
37
42
 
38
43
  export const RoadmapPhaseSchema = z.object({
39
- id: z.string(), // P0, P1, etc.
44
+ id: z.string(), // P0, P1, etc.
40
45
  name: z.string(),
41
46
  status: PhaseStatusSchema,
42
47
  completedAt: z.string().optional(),
@@ -73,10 +78,14 @@ export const GitCommitSchema = z.object({
73
78
  export const EffortEstimateSchema = z.object({
74
79
  hours: z.number(),
75
80
  confidence: z.enum(['low', 'medium', 'high']).optional(),
76
- breakdown: z.array(z.object({
77
- area: z.string(),
78
- hours: z.number(),
79
- })).optional(),
81
+ breakdown: z
82
+ .array(
83
+ z.object({
84
+ area: z.string(),
85
+ hours: z.number(),
86
+ })
87
+ )
88
+ .optional(),
80
89
  })
81
90
 
82
91
  export const EffortActualSchema = z.object({
@@ -98,19 +107,19 @@ export const FeatureEffortSchema = z.object({
98
107
  export const QuarterCapacitySchema = z.object({
99
108
  totalHours: z.number(),
100
109
  allocatedHours: z.number(),
101
- bufferPercent: z.number().optional(), // % reserved for unknowns
110
+ bufferPercent: z.number().optional(), // % reserved for unknowns
102
111
  })
103
112
 
104
113
  export const QuarterSchema = z.object({
105
- id: z.string(), // Q1-2026
106
- name: z.string(), // "Q1 2026"
107
- theme: z.string().optional(), // "Foundation"
114
+ id: z.string(), // Q1-2026
115
+ name: z.string(), // "Q1 2026"
116
+ theme: z.string().optional(), // "Foundation"
108
117
  goals: z.array(z.string()).optional(),
109
- features: z.array(z.string()), // Feature IDs
118
+ features: z.array(z.string()), // Feature IDs
110
119
  capacity: QuarterCapacitySchema.optional(),
111
120
  status: QuarterStatusSchema,
112
- startDate: z.string().optional(), // ISO8601
113
- endDate: z.string().optional(), // ISO8601
121
+ startDate: z.string().optional(), // ISO8601
122
+ endDate: z.string().optional(), // ISO8601
114
123
  })
115
124
 
116
125
  // -----------------------------------------------------------------------------
@@ -118,23 +127,23 @@ export const QuarterSchema = z.object({
118
127
  // -----------------------------------------------------------------------------
119
128
 
120
129
  export const FeatureItemSchema = z.object({
121
- id: z.string(), // feat_xxxxxxxx
130
+ id: z.string(), // feat_xxxxxxxx
122
131
  name: z.string(),
123
132
  description: z.string().optional(),
124
- date: z.string(), // YYYY-MM-DD creation date
133
+ date: z.string(), // YYYY-MM-DD creation date
125
134
  status: FeatureStatusSchema,
126
135
  impact: FeatureImpactSchema,
127
136
  effort: z.string().optional(),
128
- progress: z.number(), // 0-100
137
+ progress: z.number(), // 0-100
129
138
  // Enriched fields from MD
130
139
  type: FeatureTypeSchema.optional(),
131
- roi: z.number().optional(), // 1-5 from star count
140
+ roi: z.number().optional(), // 1-5 from star count
132
141
  why: z.array(z.string()).optional(),
133
142
  technicalNotes: z.array(z.string()).optional(),
134
143
  compatibility: z.string().optional(),
135
144
  phase: z.string().optional(), // P0, P1, etc.
136
145
  tasks: z.array(FeatureTaskSchema),
137
- createdAt: z.string(), // ISO8601
146
+ createdAt: z.string(), // ISO8601
138
147
  shippedAt: z.string().optional(),
139
148
  version: z.string().optional(),
140
149
  // ZERO DATA LOSS - additional fields
@@ -149,29 +158,29 @@ export const FeatureItemSchema = z.object({
149
158
  // =========================================================================
150
159
 
151
160
  // PRD Integration
152
- prdId: z.string().nullable().optional(), // Link to PRD (prd_xxxxxxxx)
161
+ prdId: z.string().nullable().optional(), // Link to PRD (prd_xxxxxxxx)
153
162
 
154
163
  // Legacy Support (for existing projects)
155
- legacy: z.boolean().optional(), // true = no PRD required
156
- inferredFrom: InferredFromSchema.optional(), // git, git-branch, manual, prd
164
+ legacy: z.boolean().optional(), // true = no PRD required
165
+ inferredFrom: InferredFromSchema.optional(), // git, git-branch, manual, prd
157
166
 
158
167
  // Quarter Planning
159
- quarter: z.string().nullable().optional(), // Q1-2026, etc.
168
+ quarter: z.string().nullable().optional(), // Q1-2026, etc.
160
169
 
161
170
  // Dependency Tracking
162
- dependencies: z.array(z.string()).optional(), // Feature IDs this depends on
163
- blockedBy: z.array(z.string()).optional(), // Feature IDs blocking this
171
+ dependencies: z.array(z.string()).optional(), // Feature IDs this depends on
172
+ blockedBy: z.array(z.string()).optional(), // Feature IDs blocking this
164
173
 
165
174
  // Effort Tracking (for PRD comparison)
166
175
  effortTracking: FeatureEffortSchema.optional(),
167
176
 
168
177
  // Value Scoring (calculated from PRD)
169
- valueScore: z.number().optional(), // Calculated priority score
178
+ valueScore: z.number().optional(), // Calculated priority score
170
179
 
171
180
  // Git Data (for legacy features)
172
- commits: z.array(GitCommitSchema).optional(), // Commits for this feature
173
- branch: z.string().optional(), // Branch name (for active)
174
- commitsAhead: z.number().optional(), // Commits ahead of main
181
+ commits: z.array(GitCommitSchema).optional(), // Commits for this feature
182
+ branch: z.string().optional(), // Branch name (for active)
183
+ commitsAhead: z.number().optional(), // Commits ahead of main
175
184
  })
176
185
 
177
186
  // -----------------------------------------------------------------------------
@@ -184,7 +193,7 @@ export const BacklogItemSchema = z.object({
184
193
  prdId: z.string().nullable().optional(),
185
194
  valueScore: z.number().optional(),
186
195
  effortEstimate: z.number().optional(),
187
- reason: z.string().optional(), // Why in backlog
196
+ reason: z.string().optional(), // Why in backlog
188
197
  })
189
198
 
190
199
  // -----------------------------------------------------------------------------
@@ -194,7 +203,7 @@ export const BacklogItemSchema = z.object({
194
203
  export const RoadmapJsonSchema = z.object({
195
204
  strategy: RoadmapStrategySchema.nullable().optional(),
196
205
  features: z.array(FeatureItemSchema),
197
- backlog: z.array(z.union([z.string(), BacklogItemSchema])), // Support both formats
206
+ backlog: z.array(z.union([z.string(), BacklogItemSchema])), // Support both formats
198
207
  lastUpdated: z.string(),
199
208
 
200
209
  // AI ORCHESTRATION FIELDS (v0.29.0)
@@ -274,7 +283,11 @@ export const DEFAULT_ROADMAP: RoadmapJson = {
274
283
  * Check if a feature is legacy (no PRD required)
275
284
  */
276
285
  export const isLegacyFeature = (feature: FeatureSchema): boolean => {
277
- return feature.legacy === true || feature.inferredFrom === 'git' || feature.inferredFrom === 'git-branch'
286
+ return (
287
+ feature.legacy === true ||
288
+ feature.inferredFrom === 'git' ||
289
+ feature.inferredFrom === 'git-branch'
290
+ )
278
291
  }
279
292
 
280
293
  /**
@@ -294,14 +307,14 @@ export const calculateFeaturePriority = (feature: FeatureSchema): number => {
294
307
  * Get features for a specific quarter
295
308
  */
296
309
  export const getQuarterFeatures = (roadmap: RoadmapJson, quarterId: string): FeatureSchema[] => {
297
- return roadmap.features.filter(f => f.quarter === quarterId)
310
+ return roadmap.features.filter((f) => f.quarter === quarterId)
298
311
  }
299
312
 
300
313
  /**
301
314
  * Get quarter capacity utilization percentage
302
315
  */
303
316
  export const getQuarterUtilization = (roadmap: RoadmapJson, quarterId: string): number => {
304
- const quarter = roadmap.quarters?.find(q => q.id === quarterId)
317
+ const quarter = roadmap.quarters?.find((q) => q.id === quarterId)
305
318
  if (!quarter?.capacity) return 0
306
319
 
307
320
  const { allocatedHours, totalHours } = quarter.capacity
@@ -4,9 +4,9 @@
4
4
  * ID generators and path helpers for project data.
5
5
  */
6
6
 
7
- import crypto from 'crypto'
8
- import { join } from 'path'
9
- import { homedir } from 'os'
7
+ import crypto from 'node:crypto'
8
+ import { homedir } from 'node:os'
9
+ import { join } from 'node:path'
10
10
 
11
11
  // ============================================
12
12
  // ID GENERATORS - UUID ONLY
@@ -36,4 +36,3 @@ export const GLOBAL_STORAGE = join(homedir(), '.prjct-cli', 'projects')
36
36
  export function getProjectPath(projectId: string): string {
37
37
  return join(GLOBAL_STORAGE, projectId)
38
38
  }
39
-
@@ -50,7 +50,7 @@ export const CommitInfoSchema = z.object({
50
50
  })
51
51
 
52
52
  export const ShippedItemSchema = z.object({
53
- id: z.string(), // ship_xxxxxxxx
53
+ id: z.string(), // ship_xxxxxxxx
54
54
  name: z.string(),
55
55
  version: z.string().nullable().optional(),
56
56
  type: ShipTypeSchema,
@@ -64,7 +64,7 @@ export const ShippedItemSchema = z.object({
64
64
  quantitativeImpact: z.string().optional(),
65
65
  duration: DurationSchema.optional(),
66
66
  tasksCompleted: z.number().nullable().optional(),
67
- shippedAt: z.string(), // ISO8601
67
+ shippedAt: z.string(), // ISO8601
68
68
  featureId: z.string().optional(),
69
69
  })
70
70
 
@@ -105,5 +105,5 @@ export const safeParseShipped = (data: unknown) => ShippedJsonSchema.safeParse(d
105
105
 
106
106
  export const DEFAULT_SHIPPED: ShippedJson = {
107
107
  items: [],
108
- lastUpdated: ''
108
+ lastUpdated: '',
109
109
  }