prjct-cli 0.44.1 → 0.45.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/bin/prjct.ts +131 -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 +287 -29
  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 +49 -8
  43. package/core/commands/commands.ts +60 -23
  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 +14 -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 +583 -0
  59. package/core/context-tools/imports-tool.ts +403 -0
  60. package/core/context-tools/index.ts +433 -0
  61. package/core/context-tools/recent-tool.ts +307 -0
  62. package/core/context-tools/signatures-tool.ts +501 -0
  63. package/core/context-tools/summary-tool.ts +307 -0
  64. package/core/context-tools/token-counter.ts +284 -0
  65. package/core/context-tools/types.ts +253 -0
  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 -12
  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 +143 -0
  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 +170 -329
  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 -13
  157. package/core/storage/metrics-storage.ts +320 -0
  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 -302
  170. package/core/types/integrations.ts +3 -3
  171. package/core/types/storage.ts +49 -0
  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 +18907 -13189
  194. package/dist/core/infrastructure/command-installer.js +96 -111
  195. package/dist/core/infrastructure/editors-config.js +6 -6
  196. package/dist/core/infrastructure/setup.js +256 -257
  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
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Recent Tool - Find "hot" files based on git history
3
+ *
4
+ * Identifies recently and frequently modified files,
5
+ * which are likely to be relevant for current work.
6
+ *
7
+ * @module context-tools/recent-tool
8
+ * @version 1.0.0
9
+ */
10
+
11
+ import { exec as execCallback } from 'node:child_process'
12
+ import { promisify } from 'node:util'
13
+ import type { HotFile, RecentToolOutput } from './types'
14
+
15
+ const exec = promisify(execCallback)
16
+
17
+ // =============================================================================
18
+ // Constants
19
+ // =============================================================================
20
+
21
+ /**
22
+ * Files to ignore in recent analysis
23
+ */
24
+ const IGNORE_PATTERNS = [
25
+ 'package-lock.json',
26
+ 'yarn.lock',
27
+ 'pnpm-lock.yaml',
28
+ 'bun.lockb',
29
+ '.gitignore',
30
+ '.env',
31
+ '.env.local',
32
+ '*.md',
33
+ 'CHANGELOG.md',
34
+ 'LICENSE',
35
+ ]
36
+
37
+ // =============================================================================
38
+ // Main Functions
39
+ // =============================================================================
40
+
41
+ /**
42
+ * Get recently modified files based on git history
43
+ *
44
+ * @param projectPath - Project root path
45
+ * @param options - Analysis options
46
+ * @returns Hot files sorted by heat score
47
+ */
48
+ export async function getRecentFiles(
49
+ projectPath: string = process.cwd(),
50
+ options: {
51
+ commits?: number // Number of commits to analyze (default 30)
52
+ branch?: boolean // Only files changed in current branch vs main
53
+ maxFiles?: number // Max files to return (default 50)
54
+ } = {}
55
+ ): Promise<RecentToolOutput> {
56
+ const commits = options.commits ?? 30
57
+ const maxFiles = options.maxFiles ?? 50
58
+ const branchOnly = options.branch ?? false
59
+
60
+ try {
61
+ let hotFiles: HotFile[] = []
62
+ let branchOnlyFiles: string[] = []
63
+ let analysisWindow = `${commits} commits`
64
+
65
+ if (branchOnly) {
66
+ // Get files changed only in current branch
67
+ const result = await getBranchOnlyFiles(projectPath)
68
+ hotFiles = result.hotFiles
69
+ branchOnlyFiles = result.branchOnlyFiles
70
+ analysisWindow = result.analysisWindow
71
+ } else {
72
+ // Get files from recent commits
73
+ hotFiles = await getHotFilesFromCommits(projectPath, commits)
74
+ }
75
+
76
+ // Filter and limit
77
+ hotFiles = hotFiles.filter((f) => !shouldIgnore(f.path)).slice(0, maxFiles)
78
+
79
+ return {
80
+ hotFiles,
81
+ branchOnlyFiles,
82
+ metrics: {
83
+ commitsAnalyzed: commits,
84
+ totalFilesChanged: hotFiles.length,
85
+ filesReturned: Math.min(hotFiles.length, maxFiles),
86
+ analysisWindow,
87
+ },
88
+ }
89
+ } catch (_error) {
90
+ // Git not available or not a repo
91
+ return {
92
+ hotFiles: [],
93
+ branchOnlyFiles: [],
94
+ metrics: {
95
+ commitsAnalyzed: 0,
96
+ totalFilesChanged: 0,
97
+ filesReturned: 0,
98
+ analysisWindow: 'N/A (git error)',
99
+ },
100
+ }
101
+ }
102
+ }
103
+
104
+ // =============================================================================
105
+ // Helper Functions
106
+ // =============================================================================
107
+
108
+ /**
109
+ * Get hot files from recent commits
110
+ */
111
+ async function getHotFilesFromCommits(projectPath: string, commits: number): Promise<HotFile[]> {
112
+ // Get file change counts and last modified times
113
+ const { stdout } = await exec(
114
+ `git log -${commits} --pretty=format:"%ct" --name-only | awk '
115
+ /^[0-9]+$/ { timestamp=$1; next }
116
+ NF {
117
+ count[$0]++
118
+ if (!lastmod[$0] || timestamp > lastmod[$0]) lastmod[$0]=timestamp
119
+ }
120
+ END {
121
+ for (f in count) print count[f], lastmod[f], f
122
+ }
123
+ ' | sort -rn`,
124
+ { cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
125
+ )
126
+
127
+ const hotFiles: HotFile[] = []
128
+ const lines = stdout.trim().split('\n').filter(Boolean)
129
+ const now = Math.floor(Date.now() / 1000)
130
+
131
+ // Find max changes for normalization
132
+ let maxChanges = 1
133
+ for (const line of lines) {
134
+ const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/)
135
+ if (match) {
136
+ const changes = parseInt(match[1], 10)
137
+ if (changes > maxChanges) maxChanges = changes
138
+ }
139
+ }
140
+
141
+ for (const line of lines) {
142
+ const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/)
143
+ if (!match) continue
144
+
145
+ const changes = parseInt(match[1], 10)
146
+ const timestamp = parseInt(match[2], 10)
147
+ const filePath = match[3]
148
+
149
+ const secondsAgo = now - timestamp
150
+ const daysAgo = Math.floor(secondsAgo / 86400)
151
+ const hoursAgo = Math.floor(secondsAgo / 3600)
152
+
153
+ // Calculate heat score (combination of recency and frequency)
154
+ const recencyScore = Math.max(0, 1 - daysAgo / 30) // Decay over 30 days
155
+ const frequencyScore = changes / maxChanges
156
+ const heatScore = recencyScore * 0.6 + frequencyScore * 0.4
157
+
158
+ // Format last changed
159
+ let lastChanged: string
160
+ if (hoursAgo < 1) {
161
+ lastChanged = 'just now'
162
+ } else if (hoursAgo < 24) {
163
+ lastChanged = `${hoursAgo}h ago`
164
+ } else if (daysAgo < 7) {
165
+ lastChanged = `${daysAgo}d ago`
166
+ } else if (daysAgo < 30) {
167
+ lastChanged = `${Math.floor(daysAgo / 7)}w ago`
168
+ } else {
169
+ lastChanged = `${Math.floor(daysAgo / 30)}mo ago`
170
+ }
171
+
172
+ hotFiles.push({
173
+ path: filePath,
174
+ changes,
175
+ heatScore: Math.round(heatScore * 100) / 100,
176
+ lastChanged,
177
+ lastChangedAt: new Date(timestamp * 1000).toISOString(),
178
+ })
179
+ }
180
+
181
+ // Sort by heat score
182
+ return hotFiles.sort((a, b) => b.heatScore - a.heatScore)
183
+ }
184
+
185
+ /**
186
+ * Get files changed only in current branch vs main
187
+ */
188
+ async function getBranchOnlyFiles(projectPath: string): Promise<{
189
+ hotFiles: HotFile[]
190
+ branchOnlyFiles: string[]
191
+ analysisWindow: string
192
+ }> {
193
+ // Get current branch
194
+ const { stdout: branchOutput } = await exec('git branch --show-current', {
195
+ cwd: projectPath,
196
+ })
197
+ const _currentBranch = branchOutput.trim()
198
+
199
+ // Determine base branch (main or master)
200
+ let baseBranch = 'main'
201
+ try {
202
+ await exec('git rev-parse --verify main', { cwd: projectPath })
203
+ } catch {
204
+ baseBranch = 'master'
205
+ }
206
+
207
+ // Get files changed in this branch
208
+ const { stdout: diffOutput } = await exec(`git diff --name-only ${baseBranch}...HEAD`, {
209
+ cwd: projectPath,
210
+ })
211
+
212
+ const branchOnlyFiles = diffOutput.trim().split('\n').filter(Boolean)
213
+
214
+ // Get change counts for these files in the branch
215
+ const { stdout: logOutput } = await exec(
216
+ `git log ${baseBranch}..HEAD --pretty=format:"%ct" --name-only | awk '
217
+ /^[0-9]+$/ { timestamp=$1; next }
218
+ NF {
219
+ count[$0]++
220
+ if (!lastmod[$0] || timestamp > lastmod[$0]) lastmod[$0]=timestamp
221
+ }
222
+ END {
223
+ for (f in count) print count[f], lastmod[f], f
224
+ }
225
+ '`,
226
+ { cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
227
+ )
228
+
229
+ const hotFiles: HotFile[] = []
230
+ const lines = logOutput.trim().split('\n').filter(Boolean)
231
+ const now = Math.floor(Date.now() / 1000)
232
+
233
+ // Find max changes for normalization
234
+ let maxChanges = 1
235
+ for (const line of lines) {
236
+ const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/)
237
+ if (match) {
238
+ const changes = parseInt(match[1], 10)
239
+ if (changes > maxChanges) maxChanges = changes
240
+ }
241
+ }
242
+
243
+ for (const line of lines) {
244
+ const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/)
245
+ if (!match) continue
246
+
247
+ const changes = parseInt(match[1], 10)
248
+ const timestamp = parseInt(match[2], 10)
249
+ const filePath = match[3]
250
+
251
+ const secondsAgo = now - timestamp
252
+ const daysAgo = Math.floor(secondsAgo / 86400)
253
+ const hoursAgo = Math.floor(secondsAgo / 3600)
254
+
255
+ const recencyScore = Math.max(0, 1 - daysAgo / 14) // Faster decay for branch
256
+ const frequencyScore = changes / maxChanges
257
+ const heatScore = recencyScore * 0.5 + frequencyScore * 0.5
258
+
259
+ let lastChanged: string
260
+ if (hoursAgo < 1) {
261
+ lastChanged = 'just now'
262
+ } else if (hoursAgo < 24) {
263
+ lastChanged = `${hoursAgo}h ago`
264
+ } else {
265
+ lastChanged = `${daysAgo}d ago`
266
+ }
267
+
268
+ hotFiles.push({
269
+ path: filePath,
270
+ changes,
271
+ heatScore: Math.round(heatScore * 100) / 100,
272
+ lastChanged,
273
+ lastChangedAt: new Date(timestamp * 1000).toISOString(),
274
+ })
275
+ }
276
+
277
+ return {
278
+ hotFiles: hotFiles.sort((a, b) => b.heatScore - a.heatScore),
279
+ branchOnlyFiles,
280
+ analysisWindow: `${baseBranch}..HEAD`,
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Check if a file should be ignored
286
+ */
287
+ function shouldIgnore(filePath: string): boolean {
288
+ const fileName = filePath.split('/').pop() || ''
289
+
290
+ for (const pattern of IGNORE_PATTERNS) {
291
+ if (pattern.startsWith('*.')) {
292
+ // Extension pattern
293
+ if (fileName.endsWith(pattern.slice(1))) return true
294
+ } else {
295
+ // Exact match
296
+ if (fileName === pattern) return true
297
+ }
298
+ }
299
+
300
+ return false
301
+ }
302
+
303
+ // =============================================================================
304
+ // Exports
305
+ // =============================================================================
306
+
307
+ export default { getRecentFiles }