prjct-cli 1.22.0 → 1.24.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.
Files changed (448) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/bin/prjct +30 -13
  3. package/dist/bin/prjct-core.mjs +1748 -0
  4. package/dist/bin/prjct.mjs +17 -36672
  5. package/dist/cli/linear.mjs +14 -0
  6. package/dist/daemon/entry.mjs +1429 -0
  7. package/dist/templates.json +1 -0
  8. package/package.json +4 -5
  9. package/bin/prjct.ts +0 -342
  10. package/core/__tests__/agentic/analysis-injection.test.ts +0 -377
  11. package/core/__tests__/agentic/cache-eviction.test.ts +0 -294
  12. package/core/__tests__/agentic/command-context.test.ts +0 -281
  13. package/core/__tests__/agentic/command-executor.test.ts +0 -659
  14. package/core/__tests__/agentic/domain-classifier.test.ts +0 -330
  15. package/core/__tests__/agentic/injection-validator.test.ts +0 -255
  16. package/core/__tests__/agentic/memory-system.test.ts +0 -281
  17. package/core/__tests__/agentic/plan-mode.test.ts +0 -386
  18. package/core/__tests__/agentic/prompt-assembly.test.ts +0 -298
  19. package/core/__tests__/agentic/prompt-builder.test.ts +0 -243
  20. package/core/__tests__/agentic/response-validator.test.ts +0 -263
  21. package/core/__tests__/agentic/semantic-matching.test.ts +0 -131
  22. package/core/__tests__/agentic/smart-context.test.ts +0 -372
  23. package/core/__tests__/agentic/tech-normalizer.test.ts +0 -136
  24. package/core/__tests__/agentic/token-budget.test.ts +0 -294
  25. package/core/__tests__/ai-tools/formatters.test.ts +0 -476
  26. package/core/__tests__/domain/bm25.test.ts +0 -225
  27. package/core/__tests__/domain/change-propagator.test.ts +0 -100
  28. package/core/__tests__/domain/fibonacci.test.ts +0 -113
  29. package/core/__tests__/domain/file-hasher.test.ts +0 -146
  30. package/core/__tests__/domain/file-ranker.test.ts +0 -169
  31. package/core/__tests__/domain/git-cochange.test.ts +0 -121
  32. package/core/__tests__/domain/import-graph.test.ts +0 -156
  33. package/core/__tests__/domain/velocity.test.ts +0 -623
  34. package/core/__tests__/infrastructure/performance-tracker.test.ts +0 -328
  35. package/core/__tests__/schemas/model.test.ts +0 -272
  36. package/core/__tests__/services/dependency-validator.test.ts +0 -175
  37. package/core/__tests__/services/hierarchical-agent-resolver.test.ts +0 -359
  38. package/core/__tests__/services/nested-context-resolver.test.ts +0 -443
  39. package/core/__tests__/services/project-index.test.ts +0 -355
  40. package/core/__tests__/services/staleness-checker.test.ts +0 -204
  41. package/core/__tests__/storage/analysis-storage.test.ts +0 -641
  42. package/core/__tests__/storage/archive-storage.test.ts +0 -455
  43. package/core/__tests__/storage/safe-reader.test.ts +0 -262
  44. package/core/__tests__/storage/sqlite-migration.test.ts +0 -1016
  45. package/core/__tests__/storage/state-storage-feedback.test.ts +0 -463
  46. package/core/__tests__/storage/state-storage-history.test.ts +0 -469
  47. package/core/__tests__/storage/storage-manager.test.ts +0 -383
  48. package/core/__tests__/storage/subtask-handoff.test.ts +0 -237
  49. package/core/__tests__/types/fs.test.ts +0 -125
  50. package/core/__tests__/utils/date-helper.test.ts +0 -449
  51. package/core/__tests__/utils/output.test.ts +0 -278
  52. package/core/__tests__/utils/preserve-sections.test.ts +0 -216
  53. package/core/__tests__/utils/project-commands.test.ts +0 -71
  54. package/core/__tests__/utils/retry.test.ts +0 -381
  55. package/core/__tests__/workflow/state-machine.test.ts +0 -216
  56. package/core/agentic/agent-router.ts +0 -150
  57. package/core/agentic/anti-hallucination.ts +0 -141
  58. package/core/agentic/chain-of-thought.ts +0 -234
  59. package/core/agentic/command-classifier.ts +0 -141
  60. package/core/agentic/command-context.ts +0 -168
  61. package/core/agentic/command-executor.ts +0 -471
  62. package/core/agentic/context-builder.ts +0 -285
  63. package/core/agentic/domain-classifier.ts +0 -525
  64. package/core/agentic/environment-block.ts +0 -102
  65. package/core/agentic/ground-truth.ts +0 -706
  66. package/core/agentic/index.ts +0 -193
  67. package/core/agentic/injection-validator.ts +0 -208
  68. package/core/agentic/loop-detector.ts +0 -451
  69. package/core/agentic/memory-system.ts +0 -1547
  70. package/core/agentic/orchestrator-executor.ts +0 -579
  71. package/core/agentic/plan-mode.ts +0 -525
  72. package/core/agentic/prompt-builder.ts +0 -1069
  73. package/core/agentic/response-validator.ts +0 -98
  74. package/core/agentic/services.ts +0 -167
  75. package/core/agentic/skill-loader.ts +0 -106
  76. package/core/agentic/smart-context.ts +0 -393
  77. package/core/agentic/tech-normalizer.ts +0 -167
  78. package/core/agentic/template-executor.ts +0 -272
  79. package/core/agentic/template-loader.ts +0 -109
  80. package/core/agentic/token-budget.ts +0 -226
  81. package/core/agentic/tool-registry.ts +0 -146
  82. package/core/agents/index.ts +0 -28
  83. package/core/agents/performance.ts +0 -429
  84. package/core/ai-tools/formatters.ts +0 -341
  85. package/core/ai-tools/generator.ts +0 -144
  86. package/core/ai-tools/index.ts +0 -15
  87. package/core/ai-tools/registry.ts +0 -201
  88. package/core/bus/bus.ts +0 -314
  89. package/core/bus/index.ts +0 -8
  90. package/core/cli/linear.ts +0 -500
  91. package/core/cli/lint-meta-commentary.ts +0 -177
  92. package/core/cli/start.ts +0 -386
  93. package/core/commands/analysis.ts +0 -1274
  94. package/core/commands/analytics.ts +0 -342
  95. package/core/commands/base.ts +0 -118
  96. package/core/commands/cleanup.ts +0 -157
  97. package/core/commands/command-data.ts +0 -463
  98. package/core/commands/commands.ts +0 -306
  99. package/core/commands/context.ts +0 -238
  100. package/core/commands/design.ts +0 -77
  101. package/core/commands/index.ts +0 -19
  102. package/core/commands/maintenance.ts +0 -77
  103. package/core/commands/performance.ts +0 -114
  104. package/core/commands/planning.ts +0 -662
  105. package/core/commands/register.ts +0 -127
  106. package/core/commands/registry.ts +0 -444
  107. package/core/commands/setup.ts +0 -280
  108. package/core/commands/shipping.ts +0 -267
  109. package/core/commands/snapshots.ts +0 -297
  110. package/core/commands/uninstall.ts +0 -542
  111. package/core/commands/velocity.ts +0 -149
  112. package/core/commands/workflow.ts +0 -505
  113. package/core/config/command-context.config.json +0 -66
  114. package/core/constants/index.ts +0 -379
  115. package/core/context/generator.ts +0 -368
  116. package/core/context-tools/files-tool.ts +0 -577
  117. package/core/context-tools/imports-tool.ts +0 -400
  118. package/core/context-tools/index.ts +0 -434
  119. package/core/context-tools/recent-tool.ts +0 -301
  120. package/core/context-tools/signatures-tool.ts +0 -495
  121. package/core/context-tools/summary-tool.ts +0 -301
  122. package/core/context-tools/token-counter.ts +0 -273
  123. package/core/context-tools/types.ts +0 -253
  124. package/core/domain/agent-generator.ts +0 -186
  125. package/core/domain/agent-loader.ts +0 -419
  126. package/core/domain/analyzer.ts +0 -387
  127. package/core/domain/architecture-generator.ts +0 -108
  128. package/core/domain/bm25.ts +0 -525
  129. package/core/domain/change-propagator.ts +0 -162
  130. package/core/domain/context-estimator.ts +0 -175
  131. package/core/domain/fibonacci.ts +0 -128
  132. package/core/domain/file-hasher.ts +0 -296
  133. package/core/domain/file-ranker.ts +0 -151
  134. package/core/domain/git-cochange.ts +0 -250
  135. package/core/domain/import-graph.ts +0 -315
  136. package/core/domain/snapshot-manager.ts +0 -415
  137. package/core/domain/task-stack.ts +0 -578
  138. package/core/domain/velocity.ts +0 -470
  139. package/core/errors.ts +0 -335
  140. package/core/events/events.ts +0 -85
  141. package/core/events/index.ts +0 -8
  142. package/core/index.ts +0 -481
  143. package/core/infrastructure/agent-detector.ts +0 -135
  144. package/core/infrastructure/ai-provider.ts +0 -578
  145. package/core/infrastructure/author-detector.ts +0 -133
  146. package/core/infrastructure/capability-installer.ts +0 -76
  147. package/core/infrastructure/claude-agent.ts +0 -297
  148. package/core/infrastructure/command-installer.ts +0 -752
  149. package/core/infrastructure/config-manager.ts +0 -364
  150. package/core/infrastructure/editors-config.ts +0 -172
  151. package/core/infrastructure/path-manager.ts +0 -571
  152. package/core/infrastructure/performance-tracker.ts +0 -326
  153. package/core/infrastructure/permission-manager.ts +0 -289
  154. package/core/infrastructure/setup.ts +0 -1061
  155. package/core/infrastructure/update-checker.ts +0 -246
  156. package/core/integrations/issue-tracker/enricher.ts +0 -271
  157. package/core/integrations/issue-tracker/index.ts +0 -8
  158. package/core/integrations/issue-tracker/manager.ts +0 -286
  159. package/core/integrations/issue-tracker/types.ts +0 -310
  160. package/core/integrations/jira/cache.ts +0 -57
  161. package/core/integrations/jira/client.ts +0 -688
  162. package/core/integrations/jira/index.ts +0 -23
  163. package/core/integrations/jira/service.ts +0 -244
  164. package/core/integrations/linear/cache.ts +0 -68
  165. package/core/integrations/linear/client.ts +0 -436
  166. package/core/integrations/linear/index.ts +0 -20
  167. package/core/integrations/linear/service.ts +0 -260
  168. package/core/integrations/linear/sync.ts +0 -314
  169. package/core/outcomes/analyzer.ts +0 -286
  170. package/core/outcomes/index.ts +0 -34
  171. package/core/outcomes/recorder.ts +0 -195
  172. package/core/plugin/builtin/webhook.ts +0 -148
  173. package/core/plugin/hooks.ts +0 -315
  174. package/core/plugin/index.ts +0 -50
  175. package/core/plugin/loader.ts +0 -354
  176. package/core/plugin/registry.ts +0 -326
  177. package/core/schemas/agents.ts +0 -27
  178. package/core/schemas/analysis.ts +0 -530
  179. package/core/schemas/classification.ts +0 -91
  180. package/core/schemas/command-context.ts +0 -29
  181. package/core/schemas/enriched-task.ts +0 -291
  182. package/core/schemas/ideas.ts +0 -114
  183. package/core/schemas/index.ts +0 -53
  184. package/core/schemas/issues.ts +0 -159
  185. package/core/schemas/llm-output.ts +0 -170
  186. package/core/schemas/metrics.ts +0 -143
  187. package/core/schemas/model.ts +0 -153
  188. package/core/schemas/outcomes.ts +0 -487
  189. package/core/schemas/performance.ts +0 -128
  190. package/core/schemas/permissions.ts +0 -180
  191. package/core/schemas/prd.ts +0 -450
  192. package/core/schemas/project.ts +0 -57
  193. package/core/schemas/roadmap.ts +0 -322
  194. package/core/schemas/schemas.ts +0 -38
  195. package/core/schemas/shipped.ts +0 -109
  196. package/core/schemas/state.ts +0 -284
  197. package/core/schemas/velocity.ts +0 -103
  198. package/core/server/index.ts +0 -21
  199. package/core/server/routes-extended.ts +0 -566
  200. package/core/server/routes.ts +0 -176
  201. package/core/server/server.ts +0 -149
  202. package/core/server/sse.ts +0 -192
  203. package/core/services/agent-generator.ts +0 -385
  204. package/core/services/agent-service.ts +0 -168
  205. package/core/services/breakdown-service.ts +0 -124
  206. package/core/services/context-generator.ts +0 -445
  207. package/core/services/context-selector.ts +0 -429
  208. package/core/services/dependency-validator.ts +0 -318
  209. package/core/services/diff-generator.ts +0 -313
  210. package/core/services/doctor-service.ts +0 -423
  211. package/core/services/file-categorizer.ts +0 -448
  212. package/core/services/file-scorer.ts +0 -270
  213. package/core/services/git-analyzer.ts +0 -293
  214. package/core/services/hierarchical-agent-resolver.ts +0 -236
  215. package/core/services/hooks-service.ts +0 -685
  216. package/core/services/index.ts +0 -46
  217. package/core/services/local-state-generator.ts +0 -158
  218. package/core/services/memory-service.ts +0 -181
  219. package/core/services/nested-context-resolver.ts +0 -842
  220. package/core/services/project-index.ts +0 -911
  221. package/core/services/project-service.ts +0 -155
  222. package/core/services/session-tracker.ts +0 -287
  223. package/core/services/skill-installer.ts +0 -447
  224. package/core/services/skill-lock.ts +0 -132
  225. package/core/services/skill-service.ts +0 -306
  226. package/core/services/stack-detector.ts +0 -229
  227. package/core/services/staleness-checker.ts +0 -327
  228. package/core/services/sync-service.ts +0 -1515
  229. package/core/services/sync-verifier.ts +0 -253
  230. package/core/services/watch-service.ts +0 -312
  231. package/core/session/compaction.ts +0 -248
  232. package/core/session/index.ts +0 -35
  233. package/core/session/log-migration.ts +0 -88
  234. package/core/session/metrics.ts +0 -323
  235. package/core/session/session-log-manager.ts +0 -307
  236. package/core/session/task-session-manager.ts +0 -404
  237. package/core/session/utils.ts +0 -51
  238. package/core/storage/analysis-storage.ts +0 -373
  239. package/core/storage/archive-storage.ts +0 -205
  240. package/core/storage/database.ts +0 -575
  241. package/core/storage/ideas-storage.ts +0 -298
  242. package/core/storage/index-storage.ts +0 -523
  243. package/core/storage/index.ts +0 -79
  244. package/core/storage/metrics-storage.ts +0 -321
  245. package/core/storage/migrate-json.ts +0 -720
  246. package/core/storage/queue-storage.ts +0 -336
  247. package/core/storage/safe-reader.ts +0 -105
  248. package/core/storage/shipped-storage.ts +0 -253
  249. package/core/storage/state-storage.ts +0 -1035
  250. package/core/storage/storage-manager.ts +0 -205
  251. package/core/storage/storage.ts +0 -177
  252. package/core/storage/velocity-storage.ts +0 -149
  253. package/core/sync/auth-config.ts +0 -138
  254. package/core/sync/index.ts +0 -31
  255. package/core/sync/oauth-handler.ts +0 -143
  256. package/core/sync/sync-client.ts +0 -251
  257. package/core/sync/sync-manager.ts +0 -327
  258. package/core/tsconfig.json +0 -22
  259. package/core/types/agentic.ts +0 -760
  260. package/core/types/agents.ts +0 -150
  261. package/core/types/bus.ts +0 -193
  262. package/core/types/citations.ts +0 -22
  263. package/core/types/commands.ts +0 -399
  264. package/core/types/config.ts +0 -92
  265. package/core/types/core.ts +0 -96
  266. package/core/types/diff.ts +0 -41
  267. package/core/types/domain.ts +0 -71
  268. package/core/types/errors.ts +0 -111
  269. package/core/types/events.ts +0 -42
  270. package/core/types/fs.ts +0 -72
  271. package/core/types/index.ts +0 -510
  272. package/core/types/infrastructure.ts +0 -210
  273. package/core/types/integrations.ts +0 -31
  274. package/core/types/jira.ts +0 -51
  275. package/core/types/logger.ts +0 -17
  276. package/core/types/memory.ts +0 -313
  277. package/core/types/outcomes.ts +0 -190
  278. package/core/types/output.ts +0 -47
  279. package/core/types/plugin.ts +0 -25
  280. package/core/types/project-sync.ts +0 -129
  281. package/core/types/provider.ts +0 -163
  282. package/core/types/server.ts +0 -71
  283. package/core/types/services.ts +0 -84
  284. package/core/types/session.ts +0 -135
  285. package/core/types/stack.ts +0 -19
  286. package/core/types/storage.ts +0 -318
  287. package/core/types/sync-verifier.ts +0 -33
  288. package/core/types/sync.ts +0 -121
  289. package/core/types/task.ts +0 -72
  290. package/core/types/template.ts +0 -24
  291. package/core/types/utils.ts +0 -92
  292. package/core/types/workflow.ts +0 -23
  293. package/core/utils/agent-stream.ts +0 -140
  294. package/core/utils/animations.ts +0 -251
  295. package/core/utils/branding.ts +0 -88
  296. package/core/utils/cache.ts +0 -187
  297. package/core/utils/citations.ts +0 -39
  298. package/core/utils/collection-filters.ts +0 -209
  299. package/core/utils/date-helper.ts +0 -176
  300. package/core/utils/error-messages.ts +0 -38
  301. package/core/utils/file-helper.ts +0 -277
  302. package/core/utils/fs-helpers.ts +0 -14
  303. package/core/utils/help.ts +0 -314
  304. package/core/utils/jsonl-helper.ts +0 -290
  305. package/core/utils/keychain.ts +0 -127
  306. package/core/utils/logger.ts +0 -77
  307. package/core/utils/markdown-builder.ts +0 -280
  308. package/core/utils/next-steps.ts +0 -95
  309. package/core/utils/output.ts +0 -403
  310. package/core/utils/preserve-sections.ts +0 -218
  311. package/core/utils/project-commands.ts +0 -126
  312. package/core/utils/project-credentials.ts +0 -143
  313. package/core/utils/provider-cache.ts +0 -49
  314. package/core/utils/retry.ts +0 -318
  315. package/core/utils/runtime.ts +0 -108
  316. package/core/utils/session-helper.ts +0 -278
  317. package/core/utils/subtask-table.ts +0 -227
  318. package/core/utils/version.ts +0 -128
  319. package/core/wizard/index.ts +0 -13
  320. package/core/wizard/onboarding.ts +0 -633
  321. package/core/workflow/index.ts +0 -7
  322. package/core/workflow/state-machine.ts +0 -198
  323. package/core/workflow/workflow-preferences.ts +0 -294
  324. package/dist/core/infrastructure/command-installer.js +0 -1141
  325. package/dist/core/infrastructure/editors-config.js +0 -177
  326. package/dist/core/infrastructure/setup.js +0 -2244
  327. package/dist/core/utils/version.js +0 -141
  328. package/templates/agentic/agent-routing.md +0 -45
  329. package/templates/agentic/agents/uxui.md +0 -63
  330. package/templates/agentic/checklist-routing.md +0 -98
  331. package/templates/agentic/orchestrator.md +0 -68
  332. package/templates/agentic/task-fragmentation.md +0 -89
  333. package/templates/agents/AGENTS.md +0 -68
  334. package/templates/analysis/analyze.md +0 -84
  335. package/templates/analysis/patterns.md +0 -60
  336. package/templates/antigravity/SKILL.md +0 -39
  337. package/templates/architect/discovery.md +0 -67
  338. package/templates/architect/phases.md +0 -59
  339. package/templates/checklists/architecture.md +0 -28
  340. package/templates/checklists/code-quality.md +0 -28
  341. package/templates/checklists/data.md +0 -33
  342. package/templates/checklists/documentation.md +0 -33
  343. package/templates/checklists/infrastructure.md +0 -33
  344. package/templates/checklists/performance.md +0 -33
  345. package/templates/checklists/security.md +0 -33
  346. package/templates/checklists/testing.md +0 -33
  347. package/templates/checklists/ux-ui.md +0 -37
  348. package/templates/commands/analyze.md +0 -56
  349. package/templates/commands/auth.md +0 -234
  350. package/templates/commands/bug.md +0 -163
  351. package/templates/commands/cleanup.md +0 -19
  352. package/templates/commands/dash.md +0 -99
  353. package/templates/commands/design.md +0 -15
  354. package/templates/commands/done.md +0 -291
  355. package/templates/commands/enrich.md +0 -174
  356. package/templates/commands/git.md +0 -295
  357. package/templates/commands/history.md +0 -389
  358. package/templates/commands/idea.md +0 -88
  359. package/templates/commands/impact.md +0 -864
  360. package/templates/commands/init.md +0 -54
  361. package/templates/commands/jira.md +0 -278
  362. package/templates/commands/linear.md +0 -288
  363. package/templates/commands/merge.md +0 -206
  364. package/templates/commands/next.md +0 -80
  365. package/templates/commands/p.md +0 -67
  366. package/templates/commands/p.toml +0 -37
  367. package/templates/commands/pause.md +0 -136
  368. package/templates/commands/plan.md +0 -696
  369. package/templates/commands/prd.md +0 -356
  370. package/templates/commands/resume.md +0 -171
  371. package/templates/commands/review.md +0 -276
  372. package/templates/commands/serve.md +0 -118
  373. package/templates/commands/setup.md +0 -91
  374. package/templates/commands/ship.md +0 -475
  375. package/templates/commands/skill.md +0 -259
  376. package/templates/commands/spec.md +0 -218
  377. package/templates/commands/status.md +0 -207
  378. package/templates/commands/sync.md +0 -104
  379. package/templates/commands/task.md +0 -312
  380. package/templates/commands/test.md +0 -93
  381. package/templates/commands/update.md +0 -63
  382. package/templates/commands/verify.md +0 -204
  383. package/templates/commands/workflow.md +0 -150
  384. package/templates/config/skill-mappings.json +0 -82
  385. package/templates/context/dashboard.md +0 -256
  386. package/templates/context/roadmap.md +0 -221
  387. package/templates/cursor/commands/bug.md +0 -8
  388. package/templates/cursor/commands/done.md +0 -4
  389. package/templates/cursor/commands/pause.md +0 -6
  390. package/templates/cursor/commands/resume.md +0 -4
  391. package/templates/cursor/commands/ship.md +0 -8
  392. package/templates/cursor/commands/sync.md +0 -4
  393. package/templates/cursor/commands/task.md +0 -8
  394. package/templates/cursor/p.md +0 -29
  395. package/templates/cursor/router.mdc +0 -28
  396. package/templates/design/api.md +0 -95
  397. package/templates/design/architecture.md +0 -77
  398. package/templates/design/component.md +0 -89
  399. package/templates/design/database.md +0 -78
  400. package/templates/design/flow.md +0 -94
  401. package/templates/global/ANTIGRAVITY.md +0 -254
  402. package/templates/global/CLAUDE.md +0 -497
  403. package/templates/global/CURSOR.mdc +0 -266
  404. package/templates/global/GEMINI.md +0 -293
  405. package/templates/global/STORAGE-SPEC.md +0 -391
  406. package/templates/global/WINDSURF.md +0 -266
  407. package/templates/global/modules/CLAUDE-commands.md +0 -70
  408. package/templates/global/modules/CLAUDE-core.md +0 -105
  409. package/templates/global/modules/CLAUDE-git.md +0 -50
  410. package/templates/global/modules/CLAUDE-intelligence.md +0 -92
  411. package/templates/global/modules/CLAUDE-storage.md +0 -50
  412. package/templates/global/modules/module-config.json +0 -36
  413. package/templates/mcp-config.json +0 -19
  414. package/templates/permissions/default.jsonc +0 -60
  415. package/templates/permissions/permissive.jsonc +0 -49
  416. package/templates/permissions/strict.jsonc +0 -58
  417. package/templates/planning-methodology.md +0 -195
  418. package/templates/skills/code-review.md +0 -47
  419. package/templates/skills/debug.md +0 -61
  420. package/templates/skills/refactor.md +0 -47
  421. package/templates/subagents/agent-base.md +0 -20
  422. package/templates/subagents/domain/backend.md +0 -109
  423. package/templates/subagents/domain/database.md +0 -121
  424. package/templates/subagents/domain/devops.md +0 -152
  425. package/templates/subagents/domain/frontend.md +0 -103
  426. package/templates/subagents/domain/testing.md +0 -169
  427. package/templates/subagents/pm-expert.md +0 -366
  428. package/templates/subagents/workflow/chief-architect.md +0 -657
  429. package/templates/subagents/workflow/prjct-planner.md +0 -159
  430. package/templates/subagents/workflow/prjct-shipper.md +0 -188
  431. package/templates/subagents/workflow/prjct-workflow.md +0 -98
  432. package/templates/tools/bash.txt +0 -22
  433. package/templates/tools/edit.txt +0 -18
  434. package/templates/tools/glob.txt +0 -19
  435. package/templates/tools/grep.txt +0 -21
  436. package/templates/tools/read.txt +0 -14
  437. package/templates/tools/task.txt +0 -20
  438. package/templates/tools/webfetch.txt +0 -16
  439. package/templates/tools/websearch.txt +0 -18
  440. package/templates/tools/write.txt +0 -17
  441. package/templates/windsurf/router.md +0 -28
  442. package/templates/windsurf/workflows/bug.md +0 -8
  443. package/templates/windsurf/workflows/done.md +0 -4
  444. package/templates/windsurf/workflows/pause.md +0 -4
  445. package/templates/windsurf/workflows/resume.md +0 -4
  446. package/templates/windsurf/workflows/ship.md +0 -8
  447. package/templates/windsurf/workflows/sync.md +0 -4
  448. package/templates/windsurf/workflows/task.md +0 -8
@@ -1,911 +0,0 @@
1
- /**
2
- * ProjectIndex - Persistent Project Scanner with Scoring
3
- *
4
- * Features:
5
- * - Full scan: Analyzes entire project, caches results
6
- * - Incremental update: Only re-scans changed files
7
- * - Relevance scoring: Prioritizes important files
8
- * - Pattern detection: Identifies project architecture
9
- *
10
- * Usage:
11
- * - sync-service calls fullScan() on first sync
12
- * - Subsequent syncs use incrementalUpdate() or cached index
13
- * - watch-service uses incrementalUpdate() for changed files
14
- *
15
- * Storage location: ~/.prjct-cli/projects/{projectId}/index/
16
- */
17
-
18
- import { exec } from 'node:child_process'
19
- import fs from 'node:fs/promises'
20
- import path from 'node:path'
21
- import { promisify } from 'node:util'
22
- import {
23
- type ConfigFileEntry,
24
- type DetectedPattern,
25
- type DetectedStack,
26
- type DirectoryEntry,
27
- getDefaultIndex,
28
- INDEX_VERSION,
29
- indexStorage,
30
- type LanguageStats,
31
- type ProjectIndex,
32
- type ScoredFile,
33
- } from '../storage/index-storage'
34
- import { getTimestamp } from '../utils/date-helper'
35
- import { type FileStats, fileScorer, RELEVANCE_THRESHOLD, type ScoringContext } from './file-scorer'
36
-
37
- const execAsync = promisify(exec)
38
-
39
- // ============================================================================
40
- // TYPES
41
- // ============================================================================
42
-
43
- export interface IndexOptions {
44
- forceFullScan?: boolean // Force full scan even if index exists
45
- maxFiles?: number // Limit number of files to scan (for large repos)
46
- excludePatterns?: string[] // Additional patterns to exclude
47
- }
48
-
49
- export interface ScanResult {
50
- index: ProjectIndex
51
- fromCache: boolean
52
- changedFiles: number
53
- scanDuration: number
54
- }
55
-
56
- export interface RelevantContext {
57
- files: ScoredFile[]
58
- estimatedTokens: number
59
- originalTokens: number
60
- compressionRate: number
61
- }
62
-
63
- // ============================================================================
64
- // CONSTANTS
65
- // ============================================================================
66
-
67
- // Source file extensions to scan
68
- const SOURCE_EXTENSIONS = new Set([
69
- '.ts',
70
- '.tsx',
71
- '.js',
72
- '.jsx',
73
- '.mjs',
74
- '.cjs', // JavaScript/TypeScript
75
- '.py',
76
- '.pyw', // Python
77
- '.go', // Go
78
- '.rs', // Rust
79
- '.java',
80
- '.kt',
81
- '.scala', // JVM
82
- '.c',
83
- '.cpp',
84
- '.h',
85
- '.hpp', // C/C++
86
- '.rb', // Ruby
87
- '.php', // PHP
88
- '.swift', // Swift
89
- '.cs', // C#
90
- '.vue',
91
- '.svelte', // Frontend frameworks
92
- ])
93
-
94
- // Config file names to track
95
- const CONFIG_FILES = new Set([
96
- 'package.json',
97
- 'tsconfig.json',
98
- 'vite.config.ts',
99
- 'vite.config.js',
100
- 'next.config.js',
101
- 'next.config.mjs',
102
- 'next.config.ts',
103
- 'webpack.config.js',
104
- 'rollup.config.js',
105
- 'esbuild.config.js',
106
- 'jest.config.js',
107
- 'jest.config.ts',
108
- 'vitest.config.ts',
109
- 'vitest.config.js',
110
- 'tailwind.config.js',
111
- 'tailwind.config.ts',
112
- 'postcss.config.js',
113
- '.eslintrc',
114
- '.eslintrc.js',
115
- '.eslintrc.json',
116
- '.prettierrc',
117
- '.prettierrc.js',
118
- '.prettierrc.json',
119
- 'Cargo.toml',
120
- 'go.mod',
121
- 'pyproject.toml',
122
- 'requirements.txt',
123
- 'setup.py',
124
- 'Dockerfile',
125
- 'docker-compose.yml',
126
- 'docker-compose.yaml',
127
- '.env',
128
- '.env.local',
129
- '.env.development',
130
- '.env.production',
131
- ])
132
-
133
- // Directories to ignore
134
- const IGNORE_DIRS = new Set([
135
- 'node_modules',
136
- '.git',
137
- '.next',
138
- '.nuxt',
139
- 'dist',
140
- 'build',
141
- 'out',
142
- 'coverage',
143
- '.turbo',
144
- '.cache',
145
- '.parcel-cache',
146
- '__pycache__',
147
- '.pytest_cache',
148
- 'target', // Rust
149
- 'vendor', // Go/PHP
150
- '.venv',
151
- 'venv', // Python
152
- 'eggs',
153
- '*.egg-info',
154
- ])
155
-
156
- // Directory type detection patterns
157
- const DIR_TYPE_PATTERNS: { type: DirectoryEntry['type']; patterns: RegExp[] }[] = [
158
- { type: 'test', patterns: [/^tests?$/i, /^__tests__$/i, /^spec$/i, /^e2e$/i] },
159
- {
160
- type: 'source',
161
- patterns: [
162
- /^src$/i,
163
- /^lib$/i,
164
- /^core$/i,
165
- /^app$/i,
166
- /^pages$/i,
167
- /^components$/i,
168
- /^services$/i,
169
- /^utils$/i,
170
- ],
171
- },
172
- { type: 'config', patterns: [/^config$/i, /^\.config$/i, /^settings$/i] },
173
- { type: 'build', patterns: [/^dist$/i, /^build$/i, /^out$/i, /^\.next$/i] },
174
- { type: 'vendor', patterns: [/^node_modules$/i, /^vendor$/i, /^packages$/i] },
175
- { type: 'docs', patterns: [/^docs?$/i, /^documentation$/i] },
176
- ]
177
-
178
- // Pattern detection rules
179
- const PATTERN_DETECTORS: {
180
- name: string
181
- detect: (index: ProjectIndex) => number
182
- evidence: (index: ProjectIndex) => string[]
183
- }[] = [
184
- {
185
- name: 'monorepo',
186
- detect: (idx) => {
187
- const hasWorkspaces = idx.configFiles.some(
188
- (cf) => cf.type === 'package.json' && cf.parsed?.workspaces
189
- )
190
- const hasPackages = idx.directories.some((d) => d.path === 'packages' || d.path === 'apps')
191
- return hasWorkspaces ? 0.9 : hasPackages ? 0.7 : 0
192
- },
193
- evidence: (idx) => {
194
- const ev: string[] = []
195
- if (idx.directories.some((d) => d.path === 'packages')) ev.push('packages/')
196
- if (idx.directories.some((d) => d.path === 'apps')) ev.push('apps/')
197
- return ev
198
- },
199
- },
200
- {
201
- name: 'api-first',
202
- detect: (idx) => {
203
- const hasApiDir = idx.directories.some(
204
- (d) => d.path.includes('api') || d.path.includes('routes')
205
- )
206
- const hasOpenApi = idx.configFiles.some(
207
- (cf) => cf.path.includes('openapi') || cf.path.includes('swagger')
208
- )
209
- return hasOpenApi ? 0.9 : hasApiDir ? 0.6 : 0
210
- },
211
- evidence: (idx) =>
212
- idx.directories
213
- .filter((d) => d.path.includes('api') || d.path.includes('routes'))
214
- .map((d) => `${d.path}/`),
215
- },
216
- {
217
- name: 'component-based',
218
- detect: (idx) => {
219
- const hasComponents = idx.directories.some((d) => d.path.includes('components'))
220
- const hasReact = idx.detectedStack.frameworks.includes('React')
221
- const hasVue = idx.detectedStack.frameworks.includes('Vue')
222
- return hasComponents && (hasReact || hasVue) ? 0.8 : hasComponents ? 0.5 : 0
223
- },
224
- evidence: (idx) =>
225
- idx.directories.filter((d) => d.path.includes('components')).map((d) => `${d.path}/`),
226
- },
227
- {
228
- name: 'serverless',
229
- detect: (idx) => {
230
- const hasServerless = idx.configFiles.some(
231
- (cf) =>
232
- cf.path.includes('serverless') ||
233
- cf.path.includes('netlify') ||
234
- cf.path.includes('vercel')
235
- )
236
- const hasLambda = idx.directories.some(
237
- (d) => d.path.includes('functions') || d.path.includes('lambda')
238
- )
239
- return hasServerless ? 0.9 : hasLambda ? 0.6 : 0
240
- },
241
- evidence: (idx) =>
242
- idx.configFiles
243
- .filter((cf) => cf.path.includes('serverless') || cf.path.includes('vercel'))
244
- .map((cf) => cf.path),
245
- },
246
- ]
247
-
248
- // ============================================================================
249
- // PROJECT INDEXER CLASS
250
- // ============================================================================
251
-
252
- export class ProjectIndexer {
253
- private projectPath: string
254
- private projectId: string
255
-
256
- constructor(projectPath: string, projectId: string) {
257
- this.projectPath = projectPath
258
- this.projectId = projectId
259
- }
260
-
261
- // ==========================================================================
262
- // MAIN METHODS
263
- // ==========================================================================
264
-
265
- /**
266
- * Perform a full project scan
267
- * Creates fresh index from scratch
268
- */
269
- async fullScan(options: IndexOptions = {}): Promise<ScanResult> {
270
- const startTime = Date.now()
271
-
272
- // Create fresh index
273
- const index = getDefaultIndex(this.projectPath)
274
-
275
- // Scan all files
276
- const allFiles = await this.scanAllFiles(options)
277
- const filesArray = Array.from(allFiles.values())
278
-
279
- // Build language stats
280
- index.languages = this.buildLanguageStats(filesArray)
281
-
282
- // Find and parse config files
283
- index.configFiles = await this.findConfigFiles()
284
-
285
- // Analyze directory structure
286
- index.directories = await this.analyzeDirectories()
287
-
288
- // Detect stack
289
- index.detectedStack = await this.detectStack(index.configFiles)
290
-
291
- // Calculate scores
292
- const context = this.buildScoringContext(allFiles)
293
- const scores = fileScorer.getRelevantFiles(context, RELEVANCE_THRESHOLD)
294
-
295
- index.relevantFiles = scores.map((s) => ({
296
- path: s.path,
297
- score: s.score,
298
- size: allFiles.get(s.path)?.size || 0,
299
- mtime: allFiles.get(s.path)?.mtime.toISOString() || '',
300
- }))
301
-
302
- // Detect patterns
303
- index.patterns = this.detectPatterns(index)
304
-
305
- // Set metrics
306
- index.totalFiles = allFiles.size
307
- index.totalSize = filesArray.reduce((sum, f) => sum + f.size, 0)
308
- index.totalLines = filesArray.reduce((sum, f) => sum + (f.lines || 0), 0)
309
- index.scanDuration = Date.now() - startTime
310
-
311
- // Set timestamps
312
- const now = getTimestamp()
313
- index.lastFullScan = now
314
- index.lastIncrementalUpdate = now
315
-
316
- // Persist
317
- await indexStorage.writeIndex(this.projectId, index)
318
- await this.saveChecksums(allFiles)
319
- await indexStorage.writeScores(this.projectId, index.relevantFiles)
320
-
321
- return {
322
- index,
323
- fromCache: false,
324
- changedFiles: allFiles.size,
325
- scanDuration: index.scanDuration,
326
- }
327
- }
328
-
329
- /**
330
- * Incremental update - only re-scan changed files
331
- */
332
- async incrementalUpdate(changedPaths?: string[]): Promise<ScanResult> {
333
- const startTime = Date.now()
334
-
335
- // Load existing index
336
- const index = await indexStorage.readIndex(this.projectId)
337
- if (!index) {
338
- // No index exists, do full scan
339
- return this.fullScan()
340
- }
341
-
342
- // If specific paths provided, use those; otherwise detect changes
343
- let filesToUpdate: string[]
344
- if (changedPaths && changedPaths.length > 0) {
345
- filesToUpdate = changedPaths
346
- } else {
347
- const changes = await this.detectFileChanges()
348
- filesToUpdate = [...changes.added, ...changes.modified]
349
-
350
- // Remove deleted files from index
351
- if (changes.deleted.length > 0) {
352
- index.relevantFiles = index.relevantFiles.filter((f) => !changes.deleted.includes(f.path))
353
- }
354
- }
355
-
356
- // If no changes, return cached
357
- if (filesToUpdate.length === 0) {
358
- return {
359
- index,
360
- fromCache: true,
361
- changedFiles: 0,
362
- scanDuration: Date.now() - startTime,
363
- }
364
- }
365
-
366
- // Scan only changed files
367
- const updatedFiles = await this.scanFiles(filesToUpdate)
368
-
369
- // Rebuild scoring context with updated files
370
- const existingFiles = await this.loadExistingFileStats(index)
371
- for (const [path, stats] of updatedFiles) {
372
- existingFiles.set(path, stats)
373
- }
374
-
375
- const context = this.buildScoringContext(existingFiles)
376
- const scores = fileScorer.getRelevantFiles(context, RELEVANCE_THRESHOLD)
377
-
378
- index.relevantFiles = scores.map((s) => ({
379
- path: s.path,
380
- score: s.score,
381
- size: existingFiles.get(s.path)?.size || 0,
382
- mtime: existingFiles.get(s.path)?.mtime.toISOString() || '',
383
- }))
384
-
385
- // Update timestamps
386
- index.lastIncrementalUpdate = getTimestamp()
387
- index.scanDuration = Date.now() - startTime
388
-
389
- // Persist
390
- await indexStorage.writeIndex(this.projectId, index)
391
- await indexStorage.writeScores(this.projectId, index.relevantFiles)
392
-
393
- return {
394
- index,
395
- fromCache: false,
396
- changedFiles: filesToUpdate.length,
397
- scanDuration: index.scanDuration,
398
- }
399
- }
400
-
401
- /**
402
- * Load index from cache if valid, otherwise full scan
403
- */
404
- async loadOrScan(options: IndexOptions = {}): Promise<ScanResult> {
405
- if (options.forceFullScan) {
406
- return this.fullScan(options)
407
- }
408
-
409
- const index = await indexStorage.readIndex(this.projectId)
410
- if (index?.lastFullScan) {
411
- // Check if index is fresh enough (< 24 hours old)
412
- const age = await indexStorage.getIndexAge(this.projectId)
413
- if (age < 24) {
414
- return {
415
- index,
416
- fromCache: true,
417
- changedFiles: 0,
418
- scanDuration: 0,
419
- }
420
- }
421
- }
422
-
423
- return this.fullScan(options)
424
- }
425
-
426
- /**
427
- * Get relevant context for LLM with token estimation
428
- */
429
- async getRelevantContext(maxTokens: number = 50000): Promise<RelevantContext> {
430
- const index = await indexStorage.readIndex(this.projectId)
431
- if (!index) {
432
- return {
433
- files: [],
434
- estimatedTokens: 0,
435
- originalTokens: 0,
436
- compressionRate: 0,
437
- }
438
- }
439
-
440
- const CHARS_PER_TOKEN = 4
441
- let estimatedTokens = 0
442
- const selectedFiles: ScoredFile[] = []
443
-
444
- // Select files by score until we hit token limit
445
- for (const file of index.relevantFiles) {
446
- const fileTokens = Math.ceil(file.size / CHARS_PER_TOKEN)
447
- if (estimatedTokens + fileTokens > maxTokens) {
448
- break
449
- }
450
- selectedFiles.push(file)
451
- estimatedTokens += fileTokens
452
- }
453
-
454
- // Original tokens = total project size
455
- const originalTokens = Math.ceil(index.totalSize / CHARS_PER_TOKEN)
456
- const compressionRate =
457
- originalTokens > 0 ? (originalTokens - estimatedTokens) / originalTokens : 0
458
-
459
- return {
460
- files: selectedFiles,
461
- estimatedTokens,
462
- originalTokens,
463
- compressionRate,
464
- }
465
- }
466
-
467
- // ==========================================================================
468
- // SCANNING METHODS
469
- // ==========================================================================
470
-
471
- /**
472
- * Scan all source files in the project
473
- */
474
- private async scanAllFiles(options: IndexOptions = {}): Promise<Map<string, FileStats>> {
475
- const files = new Map<string, FileStats>()
476
- const maxFiles = options.maxFiles || 10000
477
-
478
- // Use find command for speed
479
- try {
480
- const excludeDirs = Array.from(IGNORE_DIRS)
481
- .map((d) => `-not -path "*/${d}/*"`)
482
- .join(' ')
483
- const extensions = Array.from(SOURCE_EXTENSIONS)
484
- .map((e) => `-name "*${e}"`)
485
- .join(' -o ')
486
-
487
- const { stdout } = await execAsync(
488
- `find . -type f \\( ${extensions} \\) ${excludeDirs} | head -n ${maxFiles}`,
489
- { cwd: this.projectPath, maxBuffer: 10 * 1024 * 1024 }
490
- )
491
-
492
- const paths = stdout.trim().split('\n').filter(Boolean)
493
-
494
- // Process files in parallel batches
495
- const batchSize = 100
496
- for (let i = 0; i < paths.length; i += batchSize) {
497
- const batch = paths.slice(i, i + batchSize)
498
- const results = await Promise.all(
499
- batch.map((p) => this.getFileStats(p.replace(/^\.\//, '')))
500
- )
501
- for (const stats of results) {
502
- if (stats) {
503
- files.set(stats.path, stats)
504
- }
505
- }
506
- }
507
- } catch {
508
- // Fallback to recursive directory walk
509
- await this.walkDirectory('.', files, maxFiles)
510
- }
511
-
512
- return files
513
- }
514
-
515
- /**
516
- * Scan specific files
517
- */
518
- private async scanFiles(paths: string[]): Promise<Map<string, FileStats>> {
519
- const files = new Map<string, FileStats>()
520
-
521
- const results = await Promise.all(paths.map((p) => this.getFileStats(p)))
522
-
523
- for (const stats of results) {
524
- if (stats) {
525
- files.set(stats.path, stats)
526
- }
527
- }
528
-
529
- return files
530
- }
531
-
532
- /**
533
- * Get stats for a single file
534
- */
535
- private async getFileStats(relativePath: string): Promise<FileStats | null> {
536
- const fullPath = path.join(this.projectPath, relativePath)
537
-
538
- try {
539
- const stat = await fs.stat(fullPath)
540
- const content = await fs.readFile(fullPath, 'utf-8')
541
- const lines = content.split('\n').length
542
-
543
- return {
544
- path: relativePath,
545
- size: stat.size,
546
- mtime: stat.mtime,
547
- lines,
548
- }
549
- } catch {
550
- return null
551
- }
552
- }
553
-
554
- /**
555
- * Recursive directory walk (fallback)
556
- */
557
- private async walkDirectory(
558
- dir: string,
559
- files: Map<string, FileStats>,
560
- maxFiles: number
561
- ): Promise<void> {
562
- if (files.size >= maxFiles) return
563
-
564
- const fullDir = path.join(this.projectPath, dir)
565
-
566
- try {
567
- const entries = await fs.readdir(fullDir, { withFileTypes: true })
568
-
569
- for (const entry of entries) {
570
- if (files.size >= maxFiles) break
571
-
572
- const relativePath = path.join(dir, entry.name).replace(/^\.\//, '')
573
-
574
- if (entry.isDirectory()) {
575
- if (!IGNORE_DIRS.has(entry.name)) {
576
- await this.walkDirectory(relativePath, files, maxFiles)
577
- }
578
- } else if (entry.isFile()) {
579
- const ext = path.extname(entry.name)
580
- if (SOURCE_EXTENSIONS.has(ext)) {
581
- const stats = await this.getFileStats(relativePath)
582
- if (stats) {
583
- files.set(relativePath, stats)
584
- }
585
- }
586
- }
587
- }
588
- } catch {
589
- // Directory may not be accessible
590
- }
591
- }
592
-
593
- // ==========================================================================
594
- // CONFIG & DIRECTORY ANALYSIS
595
- // ==========================================================================
596
-
597
- /**
598
- * Find and parse config files
599
- */
600
- private async findConfigFiles(): Promise<ConfigFileEntry[]> {
601
- const configs: ConfigFileEntry[] = []
602
-
603
- for (const configName of CONFIG_FILES) {
604
- const configPath = path.join(this.projectPath, configName)
605
-
606
- try {
607
- await fs.access(configPath)
608
- const checksum = await indexStorage.calculateChecksum(configPath)
609
-
610
- const entry: ConfigFileEntry = {
611
- path: configName,
612
- type: configName,
613
- checksum,
614
- }
615
-
616
- // Parse JSON config files
617
- if (configName.endsWith('.json')) {
618
- try {
619
- const content = await fs.readFile(configPath, 'utf-8')
620
- entry.parsed = JSON.parse(content)
621
- } catch {
622
- // Invalid JSON
623
- }
624
- }
625
-
626
- configs.push(entry)
627
- } catch {
628
- // Config file doesn't exist
629
- }
630
- }
631
-
632
- return configs
633
- }
634
-
635
- /**
636
- * Analyze top-level directory structure
637
- */
638
- private async analyzeDirectories(): Promise<DirectoryEntry[]> {
639
- const directories: DirectoryEntry[] = []
640
-
641
- try {
642
- const entries = await fs.readdir(this.projectPath, { withFileTypes: true })
643
-
644
- for (const entry of entries) {
645
- if (!entry.isDirectory()) continue
646
- if (IGNORE_DIRS.has(entry.name)) continue
647
- if (entry.name.startsWith('.') && entry.name !== '.github') continue
648
-
649
- const dirPath = entry.name
650
- const type = this.classifyDirectory(dirPath)
651
- const fileCount = await this.countFilesInDir(dirPath)
652
-
653
- directories.push({
654
- path: dirPath,
655
- type,
656
- fileCount,
657
- })
658
- }
659
- } catch {
660
- // Project path may not be accessible
661
- }
662
-
663
- return directories
664
- }
665
-
666
- /**
667
- * Classify directory type
668
- */
669
- private classifyDirectory(dirName: string): DirectoryEntry['type'] {
670
- for (const { type, patterns } of DIR_TYPE_PATTERNS) {
671
- if (patterns.some((p) => p.test(dirName))) {
672
- return type
673
- }
674
- }
675
- return 'unknown'
676
- }
677
-
678
- /**
679
- * Count files in a directory
680
- */
681
- private async countFilesInDir(relativePath: string): Promise<number> {
682
- const fullPath = path.join(this.projectPath, relativePath)
683
-
684
- try {
685
- const { stdout } = await execAsync(`find . -type f | wc -l`, { cwd: fullPath })
686
- return parseInt(stdout.trim(), 10) || 0
687
- } catch {
688
- return 0
689
- }
690
- }
691
-
692
- // ==========================================================================
693
- // STACK DETECTION
694
- // ==========================================================================
695
-
696
- /**
697
- * Detect technology stack from config files
698
- */
699
- private async detectStack(configFiles: ConfigFileEntry[]): Promise<DetectedStack> {
700
- const stack: DetectedStack = {
701
- ecosystem: 'unknown',
702
- frameworks: [],
703
- hasTests: false,
704
- hasDocker: false,
705
- hasCi: false,
706
- buildTool: null,
707
- }
708
-
709
- // Find package.json for JS/TS projects
710
- const packageJson = configFiles.find((cf) => cf.type === 'package.json')
711
- if (packageJson?.parsed) {
712
- stack.ecosystem = 'JavaScript'
713
-
714
- const deps = {
715
- ...(((packageJson.parsed as Record<string, unknown>).dependencies as Record<
716
- string,
717
- string
718
- >) || {}),
719
- ...(((packageJson.parsed as Record<string, unknown>).devDependencies as Record<
720
- string,
721
- string
722
- >) || {}),
723
- }
724
-
725
- // Detect frameworks
726
- if (deps.react) stack.frameworks.push('React')
727
- if (deps.next) stack.frameworks.push('Next.js')
728
- if (deps.vue) stack.frameworks.push('Vue')
729
- if (deps.nuxt) stack.frameworks.push('Nuxt')
730
- if (deps.svelte) stack.frameworks.push('Svelte')
731
- if (deps['@angular/core']) stack.frameworks.push('Angular')
732
- if (deps.express) stack.frameworks.push('Express')
733
- if (deps.fastify) stack.frameworks.push('Fastify')
734
- if (deps.hono) stack.frameworks.push('Hono')
735
- if (deps['@nestjs/core']) stack.frameworks.push('NestJS')
736
-
737
- // Detect testing
738
- if (deps.jest || deps.vitest || deps.mocha) stack.hasTests = true
739
-
740
- // Detect build tool
741
- if (deps.vite) stack.buildTool = 'vite'
742
- else if (deps.webpack) stack.buildTool = 'webpack'
743
- else if (deps.esbuild) stack.buildTool = 'esbuild'
744
- else if (deps.rollup) stack.buildTool = 'rollup'
745
- }
746
-
747
- // Other ecosystems
748
- if (configFiles.some((cf) => cf.type === 'Cargo.toml')) {
749
- stack.ecosystem = 'Rust'
750
- }
751
- if (configFiles.some((cf) => cf.type === 'go.mod')) {
752
- stack.ecosystem = 'Go'
753
- }
754
- if (configFiles.some((cf) => cf.type === 'pyproject.toml' || cf.type === 'requirements.txt')) {
755
- stack.ecosystem = 'Python'
756
- }
757
-
758
- // Docker & CI
759
- stack.hasDocker = configFiles.some(
760
- (cf) => cf.type === 'Dockerfile' || cf.type.includes('docker-compose')
761
- )
762
-
763
- // Check for CI configs
764
- try {
765
- await fs.access(path.join(this.projectPath, '.github', 'workflows'))
766
- stack.hasCi = true
767
- } catch {
768
- // No GitHub Actions
769
- }
770
-
771
- return stack
772
- }
773
-
774
- // ==========================================================================
775
- // PATTERN DETECTION
776
- // ==========================================================================
777
-
778
- /**
779
- * Detect architectural patterns
780
- */
781
- private detectPatterns(index: ProjectIndex): DetectedPattern[] {
782
- const patterns: DetectedPattern[] = []
783
-
784
- for (const detector of PATTERN_DETECTORS) {
785
- const confidence = detector.detect(index)
786
- if (confidence > 0.3) {
787
- patterns.push({
788
- name: detector.name,
789
- confidence,
790
- evidence: detector.evidence(index),
791
- })
792
- }
793
- }
794
-
795
- return patterns.sort((a, b) => b.confidence - a.confidence)
796
- }
797
-
798
- // ==========================================================================
799
- // HELPER METHODS
800
- // ==========================================================================
801
-
802
- /**
803
- * Build language statistics
804
- */
805
- private buildLanguageStats(files: FileStats[]): Record<string, LanguageStats> {
806
- const stats: Record<string, LanguageStats> = {}
807
-
808
- for (const file of files) {
809
- const ext = path.extname(file.path)
810
- if (!ext) continue
811
-
812
- if (!stats[ext]) {
813
- stats[ext] = { count: 0, totalLines: 0, totalSize: 0 }
814
- }
815
-
816
- stats[ext].count++
817
- stats[ext].totalLines += file.lines || 0
818
- stats[ext].totalSize += file.size
819
- }
820
-
821
- return stats
822
- }
823
-
824
- /**
825
- * Build scoring context for all files
826
- */
827
- private buildScoringContext(files: Map<string, FileStats>): ScoringContext {
828
- const configFiles = new Set<string>()
829
- let maxRecentCommits = 0
830
-
831
- for (const file of files.values()) {
832
- if (CONFIG_FILES.has(path.basename(file.path))) {
833
- configFiles.add(file.path)
834
- }
835
- if (file.recentCommits && file.recentCommits > maxRecentCommits) {
836
- maxRecentCommits = file.recentCommits
837
- }
838
- }
839
-
840
- return {
841
- allFiles: files,
842
- configFiles,
843
- maxFileSize: Math.max(...Array.from(files.values()).map((f) => f.size)),
844
- maxRecentCommits,
845
- now: new Date(),
846
- }
847
- }
848
-
849
- /**
850
- * Load existing file stats from index
851
- */
852
- private async loadExistingFileStats(index: ProjectIndex): Promise<Map<string, FileStats>> {
853
- const files = new Map<string, FileStats>()
854
-
855
- for (const file of index.relevantFiles) {
856
- files.set(file.path, {
857
- path: file.path,
858
- size: file.size,
859
- mtime: new Date(file.mtime),
860
- })
861
- }
862
-
863
- return files
864
- }
865
-
866
- /**
867
- * Detect changed files using checksums
868
- */
869
- private async detectFileChanges(): Promise<{
870
- added: string[]
871
- modified: string[]
872
- deleted: string[]
873
- }> {
874
- // Scan current files and calculate checksums
875
- const currentFiles = new Map<string, string>()
876
-
877
- const allFiles = await this.scanAllFiles()
878
- for (const [filePath] of allFiles) {
879
- const fullPath = path.join(this.projectPath, filePath)
880
- const checksum = await indexStorage.calculateChecksum(fullPath)
881
- currentFiles.set(filePath, checksum)
882
- }
883
-
884
- return indexStorage.detectChangedFiles(this.projectId, currentFiles)
885
- }
886
-
887
- /**
888
- * Save checksums for all scanned files
889
- */
890
- private async saveChecksums(files: Map<string, FileStats>): Promise<void> {
891
- const checksums: Record<string, string> = {}
892
-
893
- for (const [filePath] of files) {
894
- const fullPath = path.join(this.projectPath, filePath)
895
- checksums[filePath] = await indexStorage.calculateChecksum(fullPath)
896
- }
897
-
898
- await indexStorage.writeChecksums(this.projectId, {
899
- version: INDEX_VERSION,
900
- lastUpdated: getTimestamp(),
901
- checksums,
902
- })
903
- }
904
- }
905
-
906
- // Factory function for convenience
907
- export function createProjectIndexer(projectPath: string, projectId: string): ProjectIndexer {
908
- return new ProjectIndexer(projectPath, projectId)
909
- }
910
-
911
- export { RELEVANCE_THRESHOLD }