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.
- package/CHANGELOG.md +75 -0
- package/bin/prjct.ts +117 -10
- package/core/__tests__/agentic/memory-system.test.ts +39 -26
- package/core/__tests__/agentic/plan-mode.test.ts +64 -46
- package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
- package/core/__tests__/services/project-index.test.ts +353 -0
- package/core/__tests__/types/fs.test.ts +3 -3
- package/core/__tests__/utils/date-helper.test.ts +10 -10
- package/core/__tests__/utils/output.test.ts +9 -6
- package/core/__tests__/utils/project-commands.test.ts +5 -6
- package/core/agentic/agent-router.ts +9 -10
- package/core/agentic/chain-of-thought.ts +16 -4
- package/core/agentic/command-executor.ts +66 -40
- package/core/agentic/context-builder.ts +8 -5
- package/core/agentic/ground-truth.ts +15 -9
- package/core/agentic/index.ts +145 -152
- package/core/agentic/loop-detector.ts +40 -11
- package/core/agentic/memory-system.ts +98 -35
- package/core/agentic/orchestrator-executor.ts +135 -71
- package/core/agentic/plan-mode.ts +46 -16
- package/core/agentic/prompt-builder.ts +108 -42
- package/core/agentic/services.ts +10 -9
- package/core/agentic/skill-loader.ts +9 -15
- package/core/agentic/smart-context.ts +129 -79
- package/core/agentic/template-executor.ts +13 -12
- package/core/agentic/template-loader.ts +7 -4
- package/core/agentic/tool-registry.ts +16 -13
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +10 -27
- package/core/ai-tools/formatters.ts +8 -6
- package/core/ai-tools/generator.ts +4 -4
- package/core/ai-tools/index.ts +1 -1
- package/core/ai-tools/registry.ts +21 -11
- package/core/bus/bus.ts +23 -16
- package/core/bus/index.ts +2 -2
- package/core/cli/linear.ts +3 -5
- package/core/cli/start.ts +28 -25
- package/core/commands/analysis.ts +58 -39
- package/core/commands/analytics.ts +52 -44
- package/core/commands/base.ts +15 -13
- package/core/commands/cleanup.ts +6 -13
- package/core/commands/command-data.ts +28 -4
- package/core/commands/commands.ts +57 -24
- package/core/commands/context.ts +4 -4
- package/core/commands/design.ts +3 -10
- package/core/commands/index.ts +5 -8
- package/core/commands/maintenance.ts +7 -4
- package/core/commands/planning.ts +179 -56
- package/core/commands/register.ts +13 -9
- package/core/commands/registry.ts +15 -14
- package/core/commands/setup.ts +26 -14
- package/core/commands/shipping.ts +11 -16
- package/core/commands/snapshots.ts +16 -32
- package/core/commands/uninstall.ts +541 -0
- package/core/commands/workflow.ts +24 -28
- package/core/constants/index.ts +10 -22
- package/core/context/generator.ts +82 -33
- package/core/context-tools/files-tool.ts +18 -19
- package/core/context-tools/imports-tool.ts +13 -33
- package/core/context-tools/index.ts +29 -54
- package/core/context-tools/recent-tool.ts +16 -22
- package/core/context-tools/signatures-tool.ts +17 -26
- package/core/context-tools/summary-tool.ts +20 -22
- package/core/context-tools/token-counter.ts +25 -20
- package/core/context-tools/types.ts +5 -5
- package/core/domain/agent-generator.ts +7 -5
- package/core/domain/agent-loader.ts +2 -2
- package/core/domain/analyzer.ts +19 -16
- package/core/domain/architecture-generator.ts +6 -3
- package/core/domain/context-estimator.ts +3 -4
- package/core/domain/snapshot-manager.ts +25 -22
- package/core/domain/task-stack.ts +24 -14
- package/core/errors.ts +1 -1
- package/core/events/events.ts +2 -4
- package/core/events/index.ts +1 -2
- package/core/index.ts +28 -16
- package/core/infrastructure/agent-detector.ts +3 -3
- package/core/infrastructure/ai-provider.ts +23 -20
- package/core/infrastructure/author-detector.ts +16 -10
- package/core/infrastructure/capability-installer.ts +2 -2
- package/core/infrastructure/claude-agent.ts +6 -6
- package/core/infrastructure/command-installer.ts +22 -17
- package/core/infrastructure/config-manager.ts +18 -14
- package/core/infrastructure/editors-config.ts +8 -4
- package/core/infrastructure/path-manager.ts +8 -6
- package/core/infrastructure/permission-manager.ts +20 -17
- package/core/infrastructure/setup.ts +42 -38
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/enricher.ts +8 -19
- package/core/integrations/issue-tracker/index.ts +2 -2
- package/core/integrations/issue-tracker/manager.ts +15 -15
- package/core/integrations/issue-tracker/types.ts +5 -22
- package/core/integrations/jira/client.ts +67 -59
- package/core/integrations/jira/index.ts +11 -14
- package/core/integrations/jira/mcp-adapter.ts +5 -10
- package/core/integrations/jira/service.ts +10 -10
- package/core/integrations/linear/client.ts +27 -18
- package/core/integrations/linear/index.ts +9 -12
- package/core/integrations/linear/service.ts +11 -11
- package/core/integrations/linear/sync.ts +8 -8
- package/core/outcomes/analyzer.ts +5 -18
- package/core/outcomes/index.ts +2 -2
- package/core/outcomes/recorder.ts +3 -3
- package/core/plugin/builtin/webhook.ts +19 -15
- package/core/plugin/hooks.ts +29 -21
- package/core/plugin/index.ts +7 -7
- package/core/plugin/loader.ts +19 -19
- package/core/plugin/registry.ts +12 -23
- package/core/schemas/agents.ts +1 -1
- package/core/schemas/analysis.ts +1 -1
- package/core/schemas/enriched-task.ts +62 -49
- package/core/schemas/ideas.ts +13 -13
- package/core/schemas/index.ts +17 -27
- package/core/schemas/issues.ts +40 -25
- package/core/schemas/metrics.ts +25 -25
- package/core/schemas/outcomes.ts +70 -62
- package/core/schemas/permissions.ts +15 -12
- package/core/schemas/prd.ts +27 -14
- package/core/schemas/project.ts +3 -3
- package/core/schemas/roadmap.ts +47 -34
- package/core/schemas/schemas.ts +3 -4
- package/core/schemas/shipped.ts +3 -3
- package/core/schemas/state.ts +43 -29
- package/core/server/index.ts +5 -6
- package/core/server/routes-extended.ts +68 -72
- package/core/server/routes.ts +3 -3
- package/core/server/server.ts +31 -26
- package/core/services/agent-generator.ts +237 -0
- package/core/services/agent-service.ts +2 -2
- package/core/services/breakdown-service.ts +2 -4
- package/core/services/context-generator.ts +299 -0
- package/core/services/context-selector.ts +420 -0
- package/core/services/doctor-service.ts +426 -0
- package/core/services/file-categorizer.ts +448 -0
- package/core/services/file-scorer.ts +270 -0
- package/core/services/git-analyzer.ts +267 -0
- package/core/services/index.ts +27 -10
- package/core/services/memory-service.ts +3 -4
- package/core/services/project-index.ts +911 -0
- package/core/services/project-service.ts +4 -4
- package/core/services/skill-installer.ts +14 -17
- package/core/services/skill-lock.ts +3 -3
- package/core/services/skill-service.ts +12 -6
- package/core/services/stack-detector.ts +245 -0
- package/core/services/sync-service.ts +87 -345
- package/core/services/watch-service.ts +294 -0
- package/core/session/compaction.ts +23 -31
- package/core/session/index.ts +11 -5
- package/core/session/log-migration.ts +3 -3
- package/core/session/metrics.ts +19 -14
- package/core/session/session-log-manager.ts +12 -17
- package/core/session/task-session-manager.ts +25 -25
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +41 -57
- package/core/storage/index-storage.ts +514 -0
- package/core/storage/index.ts +41 -17
- package/core/storage/metrics-storage.ts +39 -34
- package/core/storage/queue-storage.ts +35 -45
- package/core/storage/shipped-storage.ts +17 -20
- package/core/storage/state-storage.ts +50 -30
- package/core/storage/storage-manager.ts +6 -6
- package/core/storage/storage.ts +18 -15
- package/core/sync/auth-config.ts +3 -3
- package/core/sync/index.ts +13 -19
- package/core/sync/oauth-handler.ts +3 -3
- package/core/sync/sync-client.ts +4 -9
- package/core/sync/sync-manager.ts +12 -14
- package/core/types/commands.ts +42 -7
- package/core/types/index.ts +284 -305
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +14 -14
- package/core/types/utils.ts +3 -3
- package/core/utils/agent-stream.ts +3 -1
- package/core/utils/animations.ts +14 -11
- package/core/utils/branding.ts +7 -7
- package/core/utils/cache.ts +1 -3
- package/core/utils/collection-filters.ts +3 -15
- package/core/utils/date-helper.ts +2 -7
- package/core/utils/file-helper.ts +13 -8
- package/core/utils/jsonl-helper.ts +13 -10
- package/core/utils/keychain.ts +4 -8
- package/core/utils/logger.ts +1 -1
- package/core/utils/next-steps.ts +3 -3
- package/core/utils/output.ts +58 -11
- package/core/utils/project-commands.ts +6 -6
- package/core/utils/project-credentials.ts +5 -12
- package/core/utils/runtime.ts +2 -2
- package/core/utils/session-helper.ts +3 -4
- package/core/utils/version.ts +3 -3
- package/core/wizard/index.ts +13 -0
- package/core/wizard/onboarding.ts +633 -0
- package/core/workflow/state-machine.ts +7 -7
- package/dist/bin/prjct.mjs +18755 -15574
- package/dist/core/infrastructure/command-installer.js +86 -79
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +246 -225
- package/dist/core/utils/version.js +9 -9
- package/package.json +11 -12
- package/scripts/build.js +3 -3
- package/scripts/postinstall.js +2 -2
- package/templates/mcp-config.json +6 -1
- package/templates/permissions/permissive.jsonc +1 -1
- package/templates/permissions/strict.jsonc +5 -9
- package/templates/global/docs/agents.md +0 -88
- package/templates/global/docs/architecture.md +0 -103
- package/templates/global/docs/commands.md +0 -96
- package/templates/global/docs/validation.md +0 -95
package/core/schemas/outcomes.ts
CHANGED
|
@@ -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(),
|
|
53
|
+
sessions: z.number().optional(), // Number of work sessions
|
|
54
54
|
}),
|
|
55
55
|
variance: z.object({
|
|
56
|
-
hours: z.number(),
|
|
57
|
-
percentage: z.number(),
|
|
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(),
|
|
74
|
-
percentOfTarget: z.number(),
|
|
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),
|
|
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(),
|
|
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),
|
|
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(),
|
|
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(),
|
|
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(),
|
|
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(),
|
|
203
|
+
reviewedAt: z.string().optional(), // When impact was captured
|
|
204
204
|
|
|
205
205
|
// Metadata
|
|
206
|
-
reviewedBy: z.string().optional(),
|
|
207
|
-
legacy: z.boolean().optional(),
|
|
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(),
|
|
217
|
-
averageSuccessRate: z.number(),
|
|
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(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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(
|
|
237
|
-
|
|
238
|
-
|
|
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(),
|
|
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 =>
|
|
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
|
-
|
|
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(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
acc[reason]
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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(
|
|
433
|
-
acc
|
|
434
|
-
|
|
435
|
-
|
|
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:
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
':(){ :|:& };:*',
|
|
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
|
package/core/schemas/prd.ts
CHANGED
|
@@ -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([
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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()),
|
|
184
|
-
p1: z.array(z.string()),
|
|
185
|
-
p2: z.array(z.string()),
|
|
186
|
-
p3: z.array(z.string()),
|
|
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(),
|
|
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(),
|
|
290
|
-
phase: z.string().nullable(),
|
|
291
|
-
quarter: z.string().nullable(),
|
|
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<
|
|
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,
|
package/core/schemas/project.ts
CHANGED
|
@@ -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(),
|
|
27
|
-
lastSync: z.string(),
|
|
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
|
}
|
package/core/schemas/roadmap.ts
CHANGED
|
@@ -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([
|
|
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(),
|
|
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(),
|
|
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
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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(),
|
|
110
|
+
bufferPercent: z.number().optional(), // % reserved for unknowns
|
|
102
111
|
})
|
|
103
112
|
|
|
104
113
|
export const QuarterSchema = z.object({
|
|
105
|
-
id: z.string(),
|
|
106
|
-
name: z.string(),
|
|
107
|
-
theme: z.string().optional(),
|
|
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()),
|
|
118
|
+
features: z.array(z.string()), // Feature IDs
|
|
110
119
|
capacity: QuarterCapacitySchema.optional(),
|
|
111
120
|
status: QuarterStatusSchema,
|
|
112
|
-
startDate: z.string().optional(),
|
|
113
|
-
endDate: z.string().optional(),
|
|
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(),
|
|
130
|
+
id: z.string(), // feat_xxxxxxxx
|
|
122
131
|
name: z.string(),
|
|
123
132
|
description: z.string().optional(),
|
|
124
|
-
date: z.string(),
|
|
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(),
|
|
137
|
+
progress: z.number(), // 0-100
|
|
129
138
|
// Enriched fields from MD
|
|
130
139
|
type: FeatureTypeSchema.optional(),
|
|
131
|
-
roi: z.number().optional(),
|
|
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(),
|
|
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(),
|
|
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(),
|
|
156
|
-
inferredFrom: InferredFromSchema.optional(),
|
|
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(),
|
|
168
|
+
quarter: z.string().nullable().optional(), // Q1-2026, etc.
|
|
160
169
|
|
|
161
170
|
// Dependency Tracking
|
|
162
|
-
dependencies: z.array(z.string()).optional(),
|
|
163
|
-
blockedBy: z.array(z.string()).optional(),
|
|
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(),
|
|
178
|
+
valueScore: z.number().optional(), // Calculated priority score
|
|
170
179
|
|
|
171
180
|
// Git Data (for legacy features)
|
|
172
|
-
commits: z.array(GitCommitSchema).optional(),
|
|
173
|
-
branch: z.string().optional(),
|
|
174
|
-
commitsAhead: z.number().optional(),
|
|
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(),
|
|
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])),
|
|
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
|
|
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
|
package/core/schemas/schemas.ts
CHANGED
|
@@ -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 {
|
|
9
|
-
import {
|
|
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
|
-
|
package/core/schemas/shipped.ts
CHANGED
|
@@ -50,7 +50,7 @@ export const CommitInfoSchema = z.object({
|
|
|
50
50
|
})
|
|
51
51
|
|
|
52
52
|
export const ShippedItemSchema = z.object({
|
|
53
|
-
id: z.string(),
|
|
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(),
|
|
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
|
}
|