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
@@ -19,15 +19,14 @@
19
19
  */
20
20
 
21
21
  import type {
22
- IssueTrackerProvider,
23
- Issue,
24
22
  CreateIssueInput,
25
- UpdateIssueInput,
26
23
  FetchOptions,
27
- JiraConfig,
28
- IssueStatus,
24
+ Issue,
29
25
  IssuePriority,
26
+ IssueStatus,
27
+ IssueTrackerProvider,
30
28
  IssueType,
29
+ JiraConfig,
31
30
  } from '../issue-tracker/types'
32
31
 
33
32
  // =============================================================================
@@ -323,9 +322,7 @@ export class JiraMCPAdapter implements Partial<IssueTrackerProvider> {
323
322
  const statusCategory = mcpIssue.fields.status.statusCategory?.key || ''
324
323
 
325
324
  const status: IssueStatus =
326
- JIRA_STATUS_NAME_MAP[statusName] ||
327
- JIRA_STATUS_CATEGORY_MAP[statusCategory] ||
328
- 'backlog'
325
+ JIRA_STATUS_NAME_MAP[statusName] || JIRA_STATUS_CATEGORY_MAP[statusCategory] || 'backlog'
329
326
 
330
327
  const priorityName = mcpIssue.fields.priority?.name?.toLowerCase() || 'medium'
331
328
  const priority: IssuePriority = JIRA_PRIORITY_MAP[priorityName] || 'medium'
@@ -400,8 +397,6 @@ export class JiraMCPAdapter implements Partial<IssueTrackerProvider> {
400
397
  return 'Improvement'
401
398
  case 'epic':
402
399
  return 'Epic'
403
- case 'chore':
404
- case 'task':
405
400
  default:
406
401
  return 'Task'
407
402
  }
@@ -4,21 +4,21 @@
4
4
  * All operations are cached with 5-minute TTL.
5
5
  */
6
6
 
7
- import { jiraProvider } from './client'
8
- import {
9
- issueCache,
10
- assignedIssuesCache,
11
- projectsCache,
12
- clearJiraCache,
13
- getJiraCacheStats,
14
- } from './cache'
15
7
  import type {
16
- Issue,
17
8
  CreateIssueInput,
18
- UpdateIssueInput,
19
9
  FetchOptions,
10
+ Issue,
20
11
  JiraConfig,
12
+ UpdateIssueInput,
21
13
  } from '../issue-tracker/types'
14
+ import {
15
+ assignedIssuesCache,
16
+ clearJiraCache,
17
+ getJiraCacheStats,
18
+ issueCache,
19
+ projectsCache,
20
+ } from './cache'
21
+ import { jiraProvider } from './client'
22
22
 
23
23
  export class JiraService {
24
24
  private initialized = false
@@ -6,15 +6,15 @@
6
6
  import { LinearClient as LinearSDK } from '@linear/sdk'
7
7
  import { getCredential } from '../../utils/keychain'
8
8
  import type {
9
- IssueTrackerProvider,
10
- Issue,
11
9
  CreateIssueInput,
12
- UpdateIssueInput,
13
10
  FetchOptions,
14
- LinearConfig,
15
- IssueStatus,
11
+ Issue,
16
12
  IssuePriority,
13
+ IssueStatus,
14
+ IssueTrackerProvider,
17
15
  IssueType,
16
+ LinearConfig,
17
+ UpdateIssueInput,
18
18
  } from '../issue-tracker/types'
19
19
 
20
20
  // =============================================================================
@@ -74,9 +74,7 @@ export class LinearProvider implements IssueTrackerProvider {
74
74
  // Try config first, then keychain (which falls back to env var)
75
75
  const apiKey = config.apiKey || (await getCredential('linear-api-key'))
76
76
  if (!apiKey) {
77
- throw new Error(
78
- 'LINEAR_API_KEY not configured. Run `p. linear setup` to configure.'
79
- )
77
+ throw new Error('LINEAR_API_KEY not configured. Run `p. linear setup` to configure.')
80
78
  }
81
79
 
82
80
  this.sdk = new LinearSDK({ apiKey })
@@ -118,9 +116,7 @@ export class LinearProvider implements IssueTrackerProvider {
118
116
  filter: Object.keys(filter).length > 0 ? filter : undefined,
119
117
  })
120
118
 
121
- return Promise.all(
122
- assignedIssues.nodes.map((issue) => this.mapIssue(issue))
123
- )
119
+ return Promise.all(assignedIssues.nodes.map((issue) => this.mapIssue(issue)))
124
120
  }
125
121
 
126
122
  /**
@@ -356,7 +352,10 @@ export class LinearProvider implements IssueTrackerProvider {
356
352
  description: linearIssue.description || undefined,
357
353
  status: LINEAR_STATUS_MAP[state?.type || 'backlog'] || 'backlog',
358
354
  priority: LINEAR_PRIORITY_MAP[linearIssue.priority] || 'none',
359
- type: this.inferType(linearIssue.title, labels.nodes.map((l) => l.name)),
355
+ type: this.inferType(
356
+ linearIssue.title,
357
+ labels.nodes.map((l) => l.name)
358
+ ),
360
359
  assignee: assignee
361
360
  ? {
362
361
  id: assignee.id,
@@ -395,13 +394,25 @@ export class LinearProvider implements IssueTrackerProvider {
395
394
  if (labelsLower.includes('bug') || titleLower.includes('fix') || titleLower.includes('bug')) {
396
395
  return 'bug'
397
396
  }
398
- if (labelsLower.includes('feature') || titleLower.includes('add') || titleLower.includes('implement')) {
397
+ if (
398
+ labelsLower.includes('feature') ||
399
+ titleLower.includes('add') ||
400
+ titleLower.includes('implement')
401
+ ) {
399
402
  return 'feature'
400
403
  }
401
- if (labelsLower.includes('improvement') || titleLower.includes('improve') || titleLower.includes('enhance')) {
404
+ if (
405
+ labelsLower.includes('improvement') ||
406
+ titleLower.includes('improve') ||
407
+ titleLower.includes('enhance')
408
+ ) {
402
409
  return 'improvement'
403
410
  }
404
- if (labelsLower.includes('chore') || titleLower.includes('chore') || titleLower.includes('deps')) {
411
+ if (
412
+ labelsLower.includes('chore') ||
413
+ titleLower.includes('chore') ||
414
+ titleLower.includes('deps')
415
+ ) {
405
416
  return 'chore'
406
417
  }
407
418
 
@@ -417,9 +428,7 @@ export class LinearProvider implements IssueTrackerProvider {
417
428
  const team = await this.sdk.team(teamId)
418
429
  const labels = await team.labels()
419
430
 
420
- return labels.nodes
421
- .filter((label) => labelNames.includes(label.name))
422
- .map((label) => label.id)
431
+ return labels.nodes.filter((label) => labelNames.includes(label.name)).map((label) => label.id)
423
432
  }
424
433
  }
425
434
 
@@ -3,21 +3,18 @@
3
3
  * Issue tracker provider for Linear using @linear/sdk
4
4
  */
5
5
 
6
- // Core provider
7
- export { LinearProvider, linearProvider } from './client'
8
-
9
- // Service layer with caching (preferred API)
10
- export { LinearService, linearService } from './service'
11
-
12
- // Sync layer for bidirectional sync with issues.json
13
- export { LinearSync, linearSync } from './sync'
14
-
15
6
  // Cache utilities
16
7
  export {
17
- issueCache,
18
8
  assignedIssuesCache,
19
- teamsCache,
20
- projectsCache,
21
9
  clearLinearCache,
22
10
  getLinearCacheStats,
11
+ issueCache,
12
+ projectsCache,
13
+ teamsCache,
23
14
  } from './cache'
15
+ // Core provider
16
+ export { LinearProvider, linearProvider } from './client'
17
+ // Service layer with caching (preferred API)
18
+ export { LinearService, linearService } from './service'
19
+ // Sync layer for bidirectional sync with issues.json
20
+ export { LinearSync, linearSync } from './sync'
@@ -4,22 +4,22 @@
4
4
  * All operations are cached with 5-minute TTL.
5
5
  */
6
6
 
7
- import { linearProvider } from './client'
8
- import {
9
- issueCache,
10
- assignedIssuesCache,
11
- teamsCache,
12
- projectsCache,
13
- clearLinearCache,
14
- getLinearCacheStats,
15
- } from './cache'
16
7
  import type {
17
- Issue,
18
8
  CreateIssueInput,
19
- UpdateIssueInput,
20
9
  FetchOptions,
10
+ Issue,
21
11
  LinearConfig,
12
+ UpdateIssueInput,
22
13
  } from '../issue-tracker/types'
14
+ import {
15
+ assignedIssuesCache,
16
+ clearLinearCache,
17
+ getLinearCacheStats,
18
+ issueCache,
19
+ projectsCache,
20
+ teamsCache,
21
+ } from './cache'
22
+ import { linearProvider } from './client'
23
23
 
24
24
  export class LinearService {
25
25
  private initialized = false
@@ -14,19 +14,19 @@
14
14
  * state.json.currentTask.linearId ← DIRECT LINK
15
15
  */
16
16
 
17
- import { readFile, writeFile, mkdir } from 'fs/promises'
18
- import { existsSync } from 'fs'
19
- import { join } from 'path'
20
- import { linearService } from './service'
21
- import { getProjectPath } from '../../schemas/schemas'
17
+ import { existsSync } from 'node:fs'
18
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
19
+ import { join } from 'node:path'
22
20
  import {
23
- type IssuesJson,
24
21
  type CachedIssue,
25
- type SyncResult,
26
22
  createEmptyIssues,
23
+ type IssuesJson,
27
24
  parseIssues,
25
+ type SyncResult,
28
26
  } from '../../schemas/issues'
27
+ import { getProjectPath } from '../../schemas/schemas'
29
28
  import type { Issue } from '../issue-tracker/types'
29
+ import { linearService } from './service'
30
30
 
31
31
  // Default staleness threshold: 30 minutes
32
32
  const DEFAULT_STALE_AFTER = 30 * 60 * 1000
@@ -105,7 +105,7 @@ export class LinearSync {
105
105
  const issuesJson = await this.loadIssues(projectId)
106
106
 
107
107
  // Check local cache first
108
- if (issuesJson && issuesJson.issues[identifier]) {
108
+ if (issuesJson?.issues[identifier]) {
109
109
  const cachedIssue = issuesJson.issues[identifier]
110
110
 
111
111
  // Check if cached issue is still fresh (within fetchedAt + some grace period)
@@ -5,14 +5,8 @@
5
5
  * Powers the learning loop for better estimates and agent selection.
6
6
  */
7
7
 
8
+ import type { AgentMetrics, DetectedPattern, Outcome, OutcomeSummary } from '../types'
8
9
  import outcomeRecorder from './recorder'
9
- import type {
10
- Outcome,
11
- OutcomeSummary,
12
- QualityScore,
13
- DetectedPattern,
14
- AgentMetrics,
15
- } from '../types'
16
10
 
17
11
  /**
18
12
  * OutcomeAnalyzer - Extracts insights from outcomes.
@@ -36,8 +30,7 @@ export class OutcomeAnalyzer {
36
30
  }
37
31
 
38
32
  // Calculate average quality
39
- const avgQuality =
40
- outcomes.reduce((sum, o) => sum + o.qualityScore, 0) / outcomes.length
33
+ const avgQuality = outcomes.reduce((sum, o) => sum + o.qualityScore, 0) / outcomes.length
41
34
 
42
35
  // Calculate estimate accuracy
43
36
  const estimateAccuracy = await outcomeRecorder.getEstimateAccuracy(projectId)
@@ -98,8 +91,7 @@ export class OutcomeAnalyzer {
98
91
  const successful = agentOutcomes.filter((o) => o.completedAsPlanned)
99
92
  const successRate = Math.round((successful.length / tasksCompleted) * 100)
100
93
 
101
- const avgQuality =
102
- agentOutcomes.reduce((sum, o) => sum + o.qualityScore, 0) / tasksCompleted
94
+ const avgQuality = agentOutcomes.reduce((sum, o) => sum + o.qualityScore, 0) / tasksCompleted
103
95
 
104
96
  // Calculate estimate accuracy for this agent
105
97
  const accurateEstimates = agentOutcomes.filter((o) => {
@@ -109,9 +101,7 @@ export class OutcomeAnalyzer {
109
101
  if (estimated === 0) return false
110
102
  return Math.abs(variance) / estimated <= 0.2
111
103
  })
112
- const estimateAccuracy = Math.round(
113
- (accurateEstimates.length / tasksCompleted) * 100
114
- )
104
+ const estimateAccuracy = Math.round((accurateEstimates.length / tasksCompleted) * 100)
115
105
 
116
106
  // Find best task types
117
107
  const taskTypes = new Map<string, number>()
@@ -214,10 +204,7 @@ export class OutcomeAnalyzer {
214
204
  /**
215
205
  * Suggest estimate for a task type based on history.
216
206
  */
217
- async suggestEstimate(
218
- projectId: string,
219
- taskType: string
220
- ): Promise<string | null> {
207
+ async suggestEstimate(projectId: string, taskType: string): Promise<string | null> {
221
208
  const outcomes = await outcomeRecorder.getAll(projectId)
222
209
 
223
210
  // Filter by task type (using tags)
@@ -29,6 +29,6 @@
29
29
  * ```
30
30
  */
31
31
 
32
- export { OutcomeRecorder, default as outcomeRecorder } from './recorder'
33
- export { OutcomeAnalyzer, default as outcomeAnalyzer } from './analyzer'
34
32
  export * from '../types'
33
+ export { default as outcomeAnalyzer, OutcomeAnalyzer } from './analyzer'
34
+ export { default as outcomeRecorder, OutcomeRecorder } from './recorder'
@@ -5,11 +5,11 @@
5
5
  * Appends to JSONL files for efficient streaming.
6
6
  */
7
7
 
8
- import path from 'path'
9
- import * as fileHelper from '../utils/file-helper'
8
+ import path from 'node:path'
10
9
  import pathManager from '../infrastructure/path-manager'
11
10
  import { generateUUID } from '../schemas'
12
- import type { Outcome, OutcomeInput, OutcomeFilter } from '../types'
11
+ import type { Outcome, OutcomeFilter, OutcomeInput } from '../types'
12
+ import * as fileHelper from '../utils/file-helper'
13
13
 
14
14
  const OUTCOMES_DIR = 'outcomes'
15
15
  const OUTCOMES_FILE = 'outcomes.jsonl'
@@ -17,10 +17,10 @@
17
17
  * }
18
18
  */
19
19
 
20
- import crypto from 'crypto'
20
+ import crypto from 'node:crypto'
21
21
  import { EventTypes } from '../../bus'
22
+ import type { WebhookConfig, WebhookPayload, WebhookPluginContext } from '../../types'
22
23
  import { HookPoints } from '../hooks'
23
- import type { WebhookConfig, WebhookPluginContext, WebhookPayload } from '../../types'
24
24
 
25
25
  const plugin = {
26
26
  name: 'webhook',
@@ -47,7 +47,7 @@ const plugin = {
47
47
  plugin.enabledEvents = config.events || [
48
48
  EventTypes.SESSION_COMPLETED,
49
49
  EventTypes.FEATURE_SHIPPED,
50
- EventTypes.SNAPSHOT_CREATED
50
+ EventTypes.SNAPSHOT_CREATED,
51
51
  ]
52
52
  },
53
53
 
@@ -62,34 +62,38 @@ const plugin = {
62
62
  * Event handlers
63
63
  */
64
64
  events: {
65
- [EventTypes.SESSION_COMPLETED]: async function(data: unknown): Promise<void> {
65
+ [EventTypes.SESSION_COMPLETED]: async (data: unknown): Promise<void> => {
66
66
  await plugin.sendWebhook('session.completed', data)
67
67
  },
68
68
 
69
- [EventTypes.FEATURE_SHIPPED]: async function(data: unknown): Promise<void> {
69
+ [EventTypes.FEATURE_SHIPPED]: async (data: unknown): Promise<void> => {
70
70
  await plugin.sendWebhook('feature.shipped', data)
71
71
  },
72
72
 
73
- [EventTypes.SNAPSHOT_CREATED]: async function(data: unknown): Promise<void> {
73
+ [EventTypes.SNAPSHOT_CREATED]: async (data: unknown): Promise<void> => {
74
74
  await plugin.sendWebhook('snapshot.created', data)
75
75
  },
76
76
 
77
- [EventTypes.TASK_COMPLETED]: async function(data: unknown): Promise<void> {
77
+ [EventTypes.TASK_COMPLETED]: async (data: unknown): Promise<void> => {
78
78
  await plugin.sendWebhook('task.completed', data)
79
- }
79
+ },
80
80
  },
81
81
 
82
82
  /**
83
83
  * Hook handlers
84
84
  */
85
85
  hooks: {
86
- [HookPoints.AFTER_FEATURE_SHIP]: async function(data: { feature: string; version: string; timestamp: string }): Promise<void> {
86
+ [HookPoints.AFTER_FEATURE_SHIP]: async (data: {
87
+ feature: string
88
+ version: string
89
+ timestamp: string
90
+ }): Promise<void> => {
87
91
  await plugin.sendWebhook('feature.shipped', {
88
92
  feature: data.feature,
89
93
  version: data.version,
90
- timestamp: data.timestamp
94
+ timestamp: data.timestamp,
91
95
  })
92
- }
96
+ },
93
97
  },
94
98
 
95
99
  /**
@@ -107,13 +111,13 @@ const plugin = {
107
111
  event,
108
112
  timestamp: new Date().toISOString(),
109
113
  source: 'prjct-cli',
110
- data
114
+ data,
111
115
  }
112
116
 
113
117
  try {
114
118
  const headers: Record<string, string> = {
115
119
  'Content-Type': 'application/json',
116
- 'User-Agent': 'prjct-cli/webhook'
120
+ 'User-Agent': 'prjct-cli/webhook',
117
121
  }
118
122
 
119
123
  // Add signature if secret is configured
@@ -128,7 +132,7 @@ const plugin = {
128
132
  const response = await fetch(plugin.config.url, {
129
133
  method: 'POST',
130
134
  headers,
131
- body: JSON.stringify(payload)
135
+ body: JSON.stringify(payload),
132
136
  })
133
137
 
134
138
  if (!response.ok) {
@@ -137,7 +141,7 @@ const plugin = {
137
141
  } catch (error) {
138
142
  console.error(`[webhook] Error sending webhook:`, (error as Error).message)
139
143
  }
140
- }
144
+ },
141
145
  }
142
146
 
143
147
  export default plugin
@@ -7,7 +7,7 @@
7
7
  * @version 1.0.0
8
8
  */
9
9
 
10
- import { eventBus, EventTypes } from '../bus'
10
+ import { EventTypes, eventBus } from '../bus'
11
11
 
12
12
  /**
13
13
  * Hook Points - Where plugins can intercept
@@ -37,10 +37,10 @@ const HookPoints = {
37
37
  // Transform hooks (must return modified data)
38
38
  TRANSFORM_COMMIT_MESSAGE: 'transform:commit.message',
39
39
  TRANSFORM_TASK_DATA: 'transform:task.data',
40
- TRANSFORM_METRICS: 'transform:metrics'
40
+ TRANSFORM_METRICS: 'transform:metrics',
41
41
  } as const
42
42
 
43
- type HookPoint = typeof HookPoints[keyof typeof HookPoints]
43
+ type HookPoint = (typeof HookPoints)[keyof typeof HookPoints]
44
44
  type HookHandler = (data: unknown, context?: unknown) => unknown | Promise<unknown>
45
45
 
46
46
  interface HookEntry {
@@ -83,7 +83,7 @@ class HookSystem {
83
83
  handler,
84
84
  pluginName,
85
85
  priority,
86
- id: `${pluginName}:${Date.now()}`
86
+ id: `${pluginName}:${Date.now()}`,
87
87
  }
88
88
 
89
89
  this.hooks.get(hookPoint)!.push(hookEntry)
@@ -107,7 +107,7 @@ class HookSystem {
107
107
  unregister(hookPoint: string, id: string): void {
108
108
  const hooks = this.hooks.get(hookPoint)
109
109
  if (hooks) {
110
- const index = hooks.findIndex(h => h.id === id)
110
+ const index = hooks.findIndex((h) => h.id === id)
111
111
  if (index !== -1) {
112
112
  hooks.splice(index, 1)
113
113
  }
@@ -130,7 +130,10 @@ class HookSystem {
130
130
  /**
131
131
  * Execute a "before" hook (can modify data)
132
132
  */
133
- async executeBefore(hookPoint: string, data: Record<string, unknown>): Promise<Record<string, unknown>> {
133
+ async executeBefore(
134
+ hookPoint: string,
135
+ data: Record<string, unknown>
136
+ ): Promise<Record<string, unknown>> {
134
137
  const hooks = this.hooks.get(hookPoint) || []
135
138
  let result = { ...data }
136
139
 
@@ -157,11 +160,14 @@ class HookSystem {
157
160
 
158
161
  // Execute all hooks in parallel for after hooks
159
162
  await Promise.allSettled(
160
- hooks.map(async hook => {
163
+ hooks.map(async (hook) => {
161
164
  try {
162
165
  await hook.handler(data)
163
166
  } catch (error) {
164
- console.error(`Hook error [${hook.pluginName}] at ${hookPoint}:`, (error as Error).message)
167
+ console.error(
168
+ `Hook error [${hook.pluginName}] at ${hookPoint}:`,
169
+ (error as Error).message
170
+ )
165
171
  }
166
172
  })
167
173
  )
@@ -176,7 +182,11 @@ class HookSystem {
176
182
  /**
177
183
  * Execute a "transform" hook (must return modified value)
178
184
  */
179
- async executeTransform<T>(hookPoint: string, value: T, context: Record<string, unknown> = {}): Promise<T> {
185
+ async executeTransform<T>(
186
+ hookPoint: string,
187
+ value: T,
188
+ context: Record<string, unknown> = {}
189
+ ): Promise<T> {
180
190
  const hooks = this.hooks.get(hookPoint) || []
181
191
  let result = value
182
192
 
@@ -187,7 +197,10 @@ class HookSystem {
187
197
  result = transformed as T
188
198
  }
189
199
  } catch (error) {
190
- console.error(`Transform hook error [${hook.pluginName}] at ${hookPoint}:`, (error as Error).message)
200
+ console.error(
201
+ `Transform hook error [${hook.pluginName}] at ${hookPoint}:`,
202
+ (error as Error).message
203
+ )
191
204
  // Keep previous value on error
192
205
  }
193
206
  }
@@ -210,7 +223,7 @@ class HookSystem {
210
223
  [HookPoints.AFTER_SNAPSHOT_RESTORE]: EventTypes.SNAPSHOT_RESTORED,
211
224
  [HookPoints.AFTER_COMMIT]: EventTypes.COMMIT_CREATED,
212
225
  [HookPoints.AFTER_PUSH]: EventTypes.PUSH_COMPLETED,
213
- [HookPoints.AFTER_SYNC]: EventTypes.PROJECT_SYNCED
226
+ [HookPoints.AFTER_SYNC]: EventTypes.PROJECT_SYNCED,
214
227
  }
215
228
  return mapping[hookPoint] || null
216
229
  }
@@ -219,9 +232,9 @@ class HookSystem {
219
232
  * Get all registered hooks for a point
220
233
  */
221
234
  getHooks(hookPoint: string): Array<{ pluginName: string; priority: number }> {
222
- return (this.hooks.get(hookPoint) || []).map(h => ({
235
+ return (this.hooks.get(hookPoint) || []).map((h) => ({
223
236
  pluginName: h.pluginName,
224
- priority: h.priority
237
+ priority: h.priority,
225
238
  }))
226
239
  }
227
240
 
@@ -230,7 +243,7 @@ class HookSystem {
230
243
  */
231
244
  getPluginHooks(pluginName: string): string[] {
232
245
  const entries = this.pluginHooks.get(pluginName) || []
233
- return entries.map(e => e.hookPoint)
246
+ return entries.map((e) => e.hookPoint)
234
247
  }
235
248
 
236
249
  /**
@@ -298,14 +311,9 @@ const hooks = {
298
311
  */
299
312
  runTransform: <T>(point: string, value: T, context?: Record<string, unknown>) => {
300
313
  return hookSystem.executeTransform(`transform:${point}`, value, context)
301
- }
314
+ },
302
315
  }
303
316
 
304
- export {
305
- HookSystem,
306
- HookPoints,
307
- hookSystem,
308
- hooks
309
- }
317
+ export { HookSystem, HookPoints, hookSystem, hooks }
310
318
 
311
319
  export default { HookSystem, HookPoints, hookSystem, hooks }
@@ -6,14 +6,17 @@
6
6
  * @version 1.0.0
7
7
  */
8
8
 
9
- import { HookSystem, HookPoints, hookSystem, hooks } from './hooks'
9
+ import { HookPoints, HookSystem, hookSystem, hooks } from './hooks'
10
10
  import { PluginLoader, pluginLoader } from './loader'
11
11
  import { PluginRegistry, pluginRegistry } from './registry'
12
12
 
13
13
  /**
14
14
  * Initialize the complete plugin system
15
15
  */
16
- async function initializePlugins(projectPath: string, config: Record<string, unknown> = {}): Promise<void> {
16
+ async function initializePlugins(
17
+ projectPath: string,
18
+ config: Record<string, unknown> = {}
19
+ ): Promise<void> {
17
20
  await pluginRegistry.initialize()
18
21
  await pluginLoader.initialize(projectPath, config)
19
22
  }
@@ -35,18 +38,15 @@ export {
35
38
  HookPoints,
36
39
  hookSystem,
37
40
  hooks,
38
-
39
41
  // Plugin loader
40
42
  PluginLoader,
41
43
  pluginLoader,
42
-
43
44
  // Plugin registry
44
45
  PluginRegistry,
45
46
  pluginRegistry,
46
-
47
47
  // Convenience functions
48
48
  initializePlugins,
49
- shutdownPlugins
49
+ shutdownPlugins,
50
50
  }
51
51
 
52
52
  export default {
@@ -59,5 +59,5 @@ export default {
59
59
  PluginRegistry,
60
60
  pluginRegistry,
61
61
  initializePlugins,
62
- shutdownPlugins
62
+ shutdownPlugins,
63
63
  }