prjct-cli 1.12.0 → 1.14.0

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 CHANGED
@@ -1,5 +1,94 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.14.0] - 2026-02-09
4
+
5
+ ### Features
6
+
7
+ - add sprint-based velocity calculation with trend detection (PRJ-296) (#156)
8
+
9
+
10
+ ## [1.14.0] - 2026-02-09
11
+
12
+ ### Features
13
+ - **Velocity Dashboard**: New `prjct velocity` command with sprint-by-sprint breakdown, trend detection, and estimation accuracy (PRJ-296)
14
+ - **Estimation Patterns**: Automatic detection of over/under estimation patterns by task category
15
+ - **Completion Projections**: Given remaining backlog points, projects estimated sprints and completion date
16
+ - **Velocity Context Injection**: Historical velocity data automatically injected into LLM task prompts for better estimation
17
+
18
+ ### Implementation Details
19
+
20
+ **PRJ-296 — Sprint-Based Velocity Calculation**
21
+ New velocity subsystem that aggregates completed task data (from outcomes.jsonl) into sprint periods, calculates rolling velocity metrics, detects trends, and identifies estimation patterns.
22
+
23
+ Key changes:
24
+ - `core/schemas/velocity.ts` — Zod schemas: SprintVelocity, VelocityMetrics, VelocityConfig, EstimationPattern, CompletionProjection
25
+ - `core/domain/velocity.ts` — Velocity engine: sprint bucketing, linear regression trend detection, accuracy tracking, pattern detection, duration parsing, LLM context formatting
26
+ - `core/storage/velocity-storage.ts` — Write-through storage extending StorageManager with markdown generation
27
+ - `core/commands/velocity.ts` — Dashboard command with chalk-formatted output + registration
28
+ - `core/types/agentic.ts` — Extended `OrchestratorContext` with `velocityContext` field
29
+ - `core/agentic/orchestrator-executor.ts` — Loads velocity context in parallel via `Promise.all`
30
+ - `core/agentic/prompt-builder.ts` — Injects velocity into Section 6 (task context)
31
+ - `core/__tests__/domain/velocity.test.ts` — 35 new tests
32
+
33
+ ### Learnings
34
+ - Derive story points from estimated duration via Fibonacci mapping when outcomes lack explicit point data
35
+ - Linear regression slope normalized by average velocity works well for trend detection (>10% = improving, <-10% = declining)
36
+ - Parallel loading pattern in orchestrator-executor (`Promise.all`) ensures zero-latency context enrichment
37
+
38
+ ### Test Plan
39
+
40
+ #### For QA
41
+ 1. Run `prjct velocity` on a project with outcomes data — verify sprint-by-sprint breakdown with points, tasks, accuracy
42
+ 2. Run `prjct velocity` with no outcomes — verify graceful "No velocity data yet" message
43
+ 3. Run `prjct velocity 89` — verify completion projection (sprints remaining + date)
44
+ 4. Run `bun test core/__tests__/domain/velocity.test.ts` — 35 tests pass
45
+ 5. Run `bun test` — all 805 tests pass
46
+
47
+ #### For Users
48
+ - **What changed:** New `prjct velocity` command shows sprint velocity, estimation accuracy trends, and completion projections. Velocity data is automatically injected into task prompts for better LLM estimation.
49
+ - **How to use:** Run `prjct velocity` after completing tasks with estimates. Add backlog points: `prjct velocity 89`
50
+ - **Breaking changes:** None
51
+
52
+ ## [1.13.0] - 2026-02-09
53
+
54
+ ### Features
55
+
56
+ - inject sealed analysis into task prompt context (PRJ-260) (#155)
57
+
58
+
59
+ ## [1.13.0] - 2026-02-09
60
+
61
+ ### Features
62
+ - **Analysis Injection**: Sealed analysis (languages, frameworks, patterns, anti-patterns) now automatically injected into LLM prompt context (PRJ-260)
63
+ - **Enriched Ground Truth**: Prompt section 3 renders full analysis data — languages, frameworks, package manager, source/test dirs, code patterns, and anti-patterns
64
+ - **Enhanced Anti-Hallucination**: AVAILABLE tech list enriched with analysis data (case-insensitive dedup), package manager constraint added
65
+
66
+ ### Implementation Details
67
+
68
+ **PRJ-260 — Inject Sync Analysis into Task Context**
69
+ Connected the analysis pipeline (from PRJ-263) to the prompt assembly pipeline (from PRJ-301). `analysisStorage.getActive()` returns sealed analysis (or draft fallback), loaded in parallel with real codebase context for zero latency impact.
70
+
71
+ Key changes:
72
+ - `core/types/agentic.ts` — New `SealedAnalysisContext` interface, extended `OrchestratorContext` with `sealedAnalysis` field
73
+ - `core/agentic/orchestrator-executor.ts` — Added `loadSealedAnalysis()`, loads in parallel with `gatherRealContext()`
74
+ - `core/agentic/prompt-builder.ts` — Section 3 (ground truth) renders analysis data, Section 5 passes analysis to anti-hallucination
75
+ - `core/agentic/anti-hallucination.ts` — Extended `ProjectGroundTruth` with `analysisLanguages`, `analysisFrameworks`, `analysisPackageManager`
76
+ - `core/__tests__/agentic/analysis-injection.test.ts` — 14 new tests
77
+
78
+ ### Test Plan
79
+
80
+ #### For QA
81
+ 1. Run `prjct sync` on a project with sealed analysis — verify prompt contains Languages, Frameworks, Patterns sections
82
+ 2. Run `prjct sync` on a project WITHOUT sealed analysis — verify no crash, fallback rules still present
83
+ 3. Check anti-hallucination block — verify AVAILABLE list includes analysis languages/frameworks (deduped)
84
+ 4. Run `bun test core/__tests__/agentic/analysis-injection.test.ts` — 14 tests pass
85
+ 5. Run `bun test` — all 770 tests pass
86
+
87
+ #### For Users
88
+ - **What changed:** AI prompts now include your project's detected languages, frameworks, code patterns, and anti-patterns from sealed analysis
89
+ - **How to use:** Run `prjct sync` then `prjct seal` — improvements are automatic in subsequent task prompts
90
+ - **Breaking changes:** None
91
+
3
92
  ## [1.12.0] - 2026-02-09
4
93
 
5
94
  ### Features
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Analysis Injection Tests (PRJ-260)
3
+ *
4
+ * Tests for injecting sealed analysis data into task context:
5
+ * - Prompt builder renders analysis in ground_truth section
6
+ * - Anti-hallucination block enriched with analysis data
7
+ * - Graceful degradation when no analysis available
8
+ */
9
+
10
+ import { beforeEach, describe, expect, it } from 'bun:test'
11
+ import {
12
+ buildAntiHallucinationBlock,
13
+ type ProjectGroundTruth,
14
+ } from '../../agentic/anti-hallucination'
15
+ import promptBuilder from '../../agentic/prompt-builder'
16
+ import type { OrchestratorContext, SealedAnalysisContext } from '../../types'
17
+
18
+ // =============================================================================
19
+ // Test Fixtures
20
+ // =============================================================================
21
+
22
+ const mockSealedAnalysis: SealedAnalysisContext = {
23
+ languages: ['TypeScript', 'JavaScript'],
24
+ frameworks: ['Hono', 'Zod'],
25
+ packageManager: 'bun',
26
+ sourceDir: 'core/',
27
+ testDir: 'core/__tests__/',
28
+ fileCount: 295,
29
+ patterns: [
30
+ {
31
+ name: 'StorageManager pattern',
32
+ description: 'All storage uses StorageManager base class with read/write/update',
33
+ location: 'core/storage/',
34
+ },
35
+ {
36
+ name: 'Zod schemas',
37
+ description: 'Runtime validation with Zod for all data structures',
38
+ },
39
+ ],
40
+ antiPatterns: [
41
+ {
42
+ issue: 'Direct fs.writeFile without StorageManager',
43
+ file: 'core/storage/legacy.ts',
44
+ suggestion: 'Use StorageManager.write() instead',
45
+ },
46
+ ],
47
+ status: 'sealed',
48
+ commitHash: 'abc123def456',
49
+ }
50
+
51
+ function makeOrchestratorContext(
52
+ sealedAnalysis: SealedAnalysisContext | null = mockSealedAnalysis
53
+ ): OrchestratorContext {
54
+ return {
55
+ detectedDomains: ['backend', 'testing'],
56
+ primaryDomain: 'backend',
57
+ agents: [],
58
+ skills: [],
59
+ requiresFragmentation: false,
60
+ subtasks: null,
61
+ project: {
62
+ id: 'test-project',
63
+ ecosystem: 'TypeScript',
64
+ conventions: ['Hono', 'Zod'],
65
+ },
66
+ sealedAnalysis,
67
+ }
68
+ }
69
+
70
+ // =============================================================================
71
+ // Prompt Builder — Ground Truth Section
72
+ // =============================================================================
73
+
74
+ describe('Analysis Injection in Prompt Builder (PRJ-260)', () => {
75
+ beforeEach(() => {
76
+ promptBuilder.resetContext()
77
+ })
78
+
79
+ it('should render sealed analysis in ground truth section', async () => {
80
+ const template = {
81
+ frontmatter: { description: 'Test task' },
82
+ content: '## Instructions\nDo the work',
83
+ }
84
+ const context = { projectPath: '/test/project', files: ['a.ts'] }
85
+ const orcCtx = makeOrchestratorContext()
86
+
87
+ const prompt = await promptBuilder.build(
88
+ template,
89
+ context,
90
+ {},
91
+ null,
92
+ null,
93
+ null,
94
+ null,
95
+ null,
96
+ orcCtx
97
+ )
98
+
99
+ // Should contain analysis data
100
+ expect(prompt).toContain('Languages**: TypeScript, JavaScript')
101
+ expect(prompt).toContain('Frameworks**: Hono, Zod')
102
+ expect(prompt).toContain('Package Manager**: bun')
103
+ expect(prompt).toContain('Source Dir**: core/')
104
+ expect(prompt).toContain('Test Dir**: core/__tests__/')
105
+ expect(prompt).toContain('Files Analyzed**: 295')
106
+ expect(prompt).toContain('Analysis Status**: sealed')
107
+ expect(prompt).toContain('abc123de') // truncated commit hash
108
+ })
109
+
110
+ it('should render code patterns from sealed analysis', async () => {
111
+ const template = {
112
+ frontmatter: { description: 'Test' },
113
+ content: '## Do',
114
+ }
115
+ const context = { projectPath: '/test', files: [] }
116
+ const orcCtx = makeOrchestratorContext()
117
+
118
+ const prompt = await promptBuilder.build(
119
+ template,
120
+ context,
121
+ {},
122
+ null,
123
+ null,
124
+ null,
125
+ null,
126
+ null,
127
+ orcCtx
128
+ )
129
+
130
+ expect(prompt).toContain('Code Patterns (Follow These)')
131
+ expect(prompt).toContain('StorageManager pattern')
132
+ expect(prompt).toContain('All storage uses StorageManager base class')
133
+ expect(prompt).toContain('core/storage/')
134
+ expect(prompt).toContain('Zod schemas')
135
+ })
136
+
137
+ it('should render anti-patterns from sealed analysis', async () => {
138
+ const template = {
139
+ frontmatter: { description: 'Test' },
140
+ content: '## Do',
141
+ }
142
+ const context = { projectPath: '/test', files: [] }
143
+ const orcCtx = makeOrchestratorContext()
144
+
145
+ const prompt = await promptBuilder.build(
146
+ template,
147
+ context,
148
+ {},
149
+ null,
150
+ null,
151
+ null,
152
+ null,
153
+ null,
154
+ orcCtx
155
+ )
156
+
157
+ expect(prompt).toContain('Anti-Patterns (Avoid These)')
158
+ expect(prompt).toContain('Direct fs.writeFile without StorageManager')
159
+ expect(prompt).toContain('core/storage/legacy.ts')
160
+ expect(prompt).toContain('Use StorageManager.write() instead')
161
+ })
162
+
163
+ it('should gracefully handle null sealed analysis', async () => {
164
+ const template = {
165
+ frontmatter: { description: 'Test' },
166
+ content: '## Do',
167
+ }
168
+ const context = { projectPath: '/test', files: [] }
169
+ const orcCtx = makeOrchestratorContext(null)
170
+
171
+ const prompt = await promptBuilder.build(
172
+ template,
173
+ context,
174
+ {},
175
+ null,
176
+ null,
177
+ null,
178
+ null,
179
+ null,
180
+ orcCtx
181
+ )
182
+
183
+ // Should still have basic project analysis
184
+ expect(prompt).toContain('PROJECT ANALYSIS')
185
+ expect(prompt).toContain('Ecosystem**: TypeScript')
186
+ // Should NOT have analysis-specific fields
187
+ expect(prompt).not.toContain('Languages**:')
188
+ expect(prompt).not.toContain('Code Patterns (Follow These)')
189
+ expect(prompt).not.toContain('Anti-Patterns (Avoid These)')
190
+ })
191
+
192
+ it('should handle empty patterns and anti-patterns', async () => {
193
+ const template = {
194
+ frontmatter: { description: 'Test' },
195
+ content: '## Do',
196
+ }
197
+ const context = { projectPath: '/test', files: [] }
198
+ const emptyAnalysis: SealedAnalysisContext = {
199
+ ...mockSealedAnalysis,
200
+ patterns: [],
201
+ antiPatterns: [],
202
+ }
203
+ const orcCtx = makeOrchestratorContext(emptyAnalysis)
204
+
205
+ const prompt = await promptBuilder.build(
206
+ template,
207
+ context,
208
+ {},
209
+ null,
210
+ null,
211
+ null,
212
+ null,
213
+ null,
214
+ orcCtx
215
+ )
216
+
217
+ expect(prompt).toContain('Languages**: TypeScript, JavaScript')
218
+ expect(prompt).not.toContain('Code Patterns (Follow These)')
219
+ expect(prompt).not.toContain('Anti-Patterns (Avoid These)')
220
+ })
221
+
222
+ it('should handle no orchestrator context at all', async () => {
223
+ const template = {
224
+ frontmatter: { description: 'Test' },
225
+ content: '## Do',
226
+ }
227
+ const context = { projectPath: '/test', files: [] }
228
+
229
+ const prompt = await promptBuilder.build(
230
+ template,
231
+ context,
232
+ {},
233
+ null,
234
+ null,
235
+ null,
236
+ null,
237
+ null,
238
+ null
239
+ )
240
+
241
+ // Should not crash, should have fallback rules
242
+ expect(prompt).toContain('CONSTRAINTS')
243
+ expect(prompt).not.toContain('PROJECT ANALYSIS')
244
+ })
245
+ })
246
+
247
+ // =============================================================================
248
+ // Anti-Hallucination Block — Analysis Enrichment
249
+ // =============================================================================
250
+
251
+ describe('Anti-Hallucination Block with Analysis (PRJ-260)', () => {
252
+ it('should include analysis languages in AVAILABLE list', () => {
253
+ const truth: ProjectGroundTruth = {
254
+ projectPath: '/test',
255
+ language: 'TypeScript',
256
+ techStack: ['Hono'],
257
+ analysisLanguages: ['TypeScript', 'JavaScript', 'Shell'],
258
+ analysisFrameworks: ['Hono', 'Vitest'],
259
+ }
260
+
261
+ const block = buildAntiHallucinationBlock(truth)
262
+
263
+ expect(block).toContain('AVAILABLE in this project:')
264
+ // TypeScript and Hono should not be duplicated
265
+ expect(block).toContain('JavaScript')
266
+ expect(block).toContain('Shell')
267
+ expect(block).toContain('Vitest')
268
+ })
269
+
270
+ it('should deduplicate analysis data with existing tech stack (case-insensitive)', () => {
271
+ const truth: ProjectGroundTruth = {
272
+ projectPath: '/test',
273
+ language: 'TypeScript',
274
+ framework: 'Hono',
275
+ techStack: ['Zod'],
276
+ analysisLanguages: ['typescript'], // lowercase duplicate
277
+ analysisFrameworks: ['hono', 'Zod'], // lowercase duplicates
278
+ }
279
+
280
+ const block = buildAntiHallucinationBlock(truth)
281
+
282
+ // Count occurrences — each should appear exactly once
283
+ const typescriptMatches = block.match(/typescript/gi)
284
+ expect(typescriptMatches?.length).toBe(1)
285
+
286
+ const honoMatches = block.match(/hono/gi)
287
+ expect(honoMatches?.length).toBe(1)
288
+ })
289
+
290
+ it('should show package manager from analysis', () => {
291
+ const truth: ProjectGroundTruth = {
292
+ projectPath: '/test',
293
+ analysisPackageManager: 'bun',
294
+ }
295
+
296
+ const block = buildAntiHallucinationBlock(truth)
297
+
298
+ expect(block).toContain('PACKAGE MANAGER: bun')
299
+ })
300
+
301
+ it('should work without any analysis data', () => {
302
+ const truth: ProjectGroundTruth = {
303
+ projectPath: '/test',
304
+ language: 'Python',
305
+ }
306
+
307
+ const block = buildAntiHallucinationBlock(truth)
308
+
309
+ expect(block).toContain('AVAILABLE in this project: Python')
310
+ expect(block).not.toContain('PACKAGE MANAGER:')
311
+ })
312
+
313
+ it('should handle empty analysis arrays gracefully', () => {
314
+ const truth: ProjectGroundTruth = {
315
+ projectPath: '/test',
316
+ analysisLanguages: [],
317
+ analysisFrameworks: [],
318
+ }
319
+
320
+ const block = buildAntiHallucinationBlock(truth)
321
+
322
+ // Should not contain AVAILABLE line (no tech to show)
323
+ expect(block).not.toContain('AVAILABLE in this project:')
324
+ expect(block).toContain('CONSTRAINTS')
325
+ })
326
+ })
327
+
328
+ // =============================================================================
329
+ // SealedAnalysisContext Type
330
+ // =============================================================================
331
+
332
+ describe('SealedAnalysisContext type (PRJ-260)', () => {
333
+ it('should accept valid sealed analysis data', () => {
334
+ const analysis: SealedAnalysisContext = {
335
+ languages: ['TypeScript'],
336
+ frameworks: ['Hono'],
337
+ fileCount: 100,
338
+ patterns: [{ name: 'test', description: 'test pattern' }],
339
+ antiPatterns: [{ issue: 'test', file: 'test.ts', suggestion: 'fix it' }],
340
+ status: 'sealed',
341
+ }
342
+
343
+ expect(analysis.languages).toEqual(['TypeScript'])
344
+ expect(analysis.status).toBe('sealed')
345
+ })
346
+
347
+ it('should accept draft status', () => {
348
+ const analysis: SealedAnalysisContext = {
349
+ languages: [],
350
+ frameworks: [],
351
+ fileCount: 0,
352
+ patterns: [],
353
+ antiPatterns: [],
354
+ status: 'draft',
355
+ }
356
+
357
+ expect(analysis.status).toBe('draft')
358
+ })
359
+
360
+ it('should accept optional fields', () => {
361
+ const analysis: SealedAnalysisContext = {
362
+ languages: ['Python'],
363
+ frameworks: [],
364
+ fileCount: 50,
365
+ patterns: [],
366
+ antiPatterns: [],
367
+ status: 'sealed',
368
+ packageManager: 'pip',
369
+ sourceDir: 'src/',
370
+ testDir: 'tests/',
371
+ commitHash: 'abc123',
372
+ }
373
+
374
+ expect(analysis.packageManager).toBe('pip')
375
+ expect(analysis.commitHash).toBe('abc123')
376
+ })
377
+ })