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.
- package/CHANGELOG.md +114 -0
- package/bin/prjct.ts +131 -10
- package/core/__tests__/agentic/memory-system.test.ts +39 -26
- package/core/__tests__/agentic/plan-mode.test.ts +64 -46
- package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
- package/core/__tests__/services/project-index.test.ts +353 -0
- package/core/__tests__/types/fs.test.ts +3 -3
- package/core/__tests__/utils/date-helper.test.ts +10 -10
- package/core/__tests__/utils/output.test.ts +9 -6
- package/core/__tests__/utils/project-commands.test.ts +5 -6
- package/core/agentic/agent-router.ts +9 -10
- package/core/agentic/chain-of-thought.ts +16 -4
- package/core/agentic/command-executor.ts +66 -40
- package/core/agentic/context-builder.ts +8 -5
- package/core/agentic/ground-truth.ts +15 -9
- package/core/agentic/index.ts +145 -152
- package/core/agentic/loop-detector.ts +40 -11
- package/core/agentic/memory-system.ts +98 -35
- package/core/agentic/orchestrator-executor.ts +135 -71
- package/core/agentic/plan-mode.ts +46 -16
- package/core/agentic/prompt-builder.ts +108 -42
- package/core/agentic/services.ts +10 -9
- package/core/agentic/skill-loader.ts +9 -15
- package/core/agentic/smart-context.ts +129 -79
- package/core/agentic/template-executor.ts +13 -12
- package/core/agentic/template-loader.ts +7 -4
- package/core/agentic/tool-registry.ts +16 -13
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +10 -27
- package/core/ai-tools/formatters.ts +8 -6
- package/core/ai-tools/generator.ts +4 -4
- package/core/ai-tools/index.ts +1 -1
- package/core/ai-tools/registry.ts +21 -11
- package/core/bus/bus.ts +23 -16
- package/core/bus/index.ts +2 -2
- package/core/cli/linear.ts +3 -5
- package/core/cli/start.ts +28 -25
- package/core/commands/analysis.ts +287 -29
- package/core/commands/analytics.ts +52 -44
- package/core/commands/base.ts +15 -13
- package/core/commands/cleanup.ts +6 -13
- package/core/commands/command-data.ts +49 -8
- package/core/commands/commands.ts +60 -23
- package/core/commands/context.ts +4 -4
- package/core/commands/design.ts +3 -10
- package/core/commands/index.ts +5 -8
- package/core/commands/maintenance.ts +7 -4
- package/core/commands/planning.ts +179 -56
- package/core/commands/register.ts +14 -9
- package/core/commands/registry.ts +15 -14
- package/core/commands/setup.ts +26 -14
- package/core/commands/shipping.ts +11 -16
- package/core/commands/snapshots.ts +16 -32
- package/core/commands/uninstall.ts +541 -0
- package/core/commands/workflow.ts +24 -28
- package/core/constants/index.ts +10 -22
- package/core/context/generator.ts +82 -33
- package/core/context-tools/files-tool.ts +583 -0
- package/core/context-tools/imports-tool.ts +403 -0
- package/core/context-tools/index.ts +433 -0
- package/core/context-tools/recent-tool.ts +307 -0
- package/core/context-tools/signatures-tool.ts +501 -0
- package/core/context-tools/summary-tool.ts +307 -0
- package/core/context-tools/token-counter.ts +284 -0
- package/core/context-tools/types.ts +253 -0
- package/core/domain/agent-generator.ts +7 -5
- package/core/domain/agent-loader.ts +2 -2
- package/core/domain/analyzer.ts +19 -16
- package/core/domain/architecture-generator.ts +6 -3
- package/core/domain/context-estimator.ts +3 -4
- package/core/domain/snapshot-manager.ts +25 -22
- package/core/domain/task-stack.ts +24 -14
- package/core/errors.ts +1 -1
- package/core/events/events.ts +2 -4
- package/core/events/index.ts +1 -2
- package/core/index.ts +28 -12
- package/core/infrastructure/agent-detector.ts +3 -3
- package/core/infrastructure/ai-provider.ts +23 -20
- package/core/infrastructure/author-detector.ts +16 -10
- package/core/infrastructure/capability-installer.ts +2 -2
- package/core/infrastructure/claude-agent.ts +6 -6
- package/core/infrastructure/command-installer.ts +22 -17
- package/core/infrastructure/config-manager.ts +18 -14
- package/core/infrastructure/editors-config.ts +8 -4
- package/core/infrastructure/path-manager.ts +8 -6
- package/core/infrastructure/permission-manager.ts +20 -17
- package/core/infrastructure/setup.ts +42 -38
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/enricher.ts +8 -19
- package/core/integrations/issue-tracker/index.ts +2 -2
- package/core/integrations/issue-tracker/manager.ts +15 -15
- package/core/integrations/issue-tracker/types.ts +5 -22
- package/core/integrations/jira/client.ts +67 -59
- package/core/integrations/jira/index.ts +11 -14
- package/core/integrations/jira/mcp-adapter.ts +5 -10
- package/core/integrations/jira/service.ts +10 -10
- package/core/integrations/linear/client.ts +27 -18
- package/core/integrations/linear/index.ts +9 -12
- package/core/integrations/linear/service.ts +11 -11
- package/core/integrations/linear/sync.ts +8 -8
- package/core/outcomes/analyzer.ts +5 -18
- package/core/outcomes/index.ts +2 -2
- package/core/outcomes/recorder.ts +3 -3
- package/core/plugin/builtin/webhook.ts +19 -15
- package/core/plugin/hooks.ts +29 -21
- package/core/plugin/index.ts +7 -7
- package/core/plugin/loader.ts +19 -19
- package/core/plugin/registry.ts +12 -23
- package/core/schemas/agents.ts +1 -1
- package/core/schemas/analysis.ts +1 -1
- package/core/schemas/enriched-task.ts +62 -49
- package/core/schemas/ideas.ts +13 -13
- package/core/schemas/index.ts +17 -27
- package/core/schemas/issues.ts +40 -25
- package/core/schemas/metrics.ts +143 -0
- package/core/schemas/outcomes.ts +70 -62
- package/core/schemas/permissions.ts +15 -12
- package/core/schemas/prd.ts +27 -14
- package/core/schemas/project.ts +3 -3
- package/core/schemas/roadmap.ts +47 -34
- package/core/schemas/schemas.ts +3 -4
- package/core/schemas/shipped.ts +3 -3
- package/core/schemas/state.ts +43 -29
- package/core/server/index.ts +5 -6
- package/core/server/routes-extended.ts +68 -72
- package/core/server/routes.ts +3 -3
- package/core/server/server.ts +31 -26
- package/core/services/agent-generator.ts +237 -0
- package/core/services/agent-service.ts +2 -2
- package/core/services/breakdown-service.ts +2 -4
- package/core/services/context-generator.ts +299 -0
- package/core/services/context-selector.ts +420 -0
- package/core/services/doctor-service.ts +426 -0
- package/core/services/file-categorizer.ts +448 -0
- package/core/services/file-scorer.ts +270 -0
- package/core/services/git-analyzer.ts +267 -0
- package/core/services/index.ts +27 -10
- package/core/services/memory-service.ts +3 -4
- package/core/services/project-index.ts +911 -0
- package/core/services/project-service.ts +4 -4
- package/core/services/skill-installer.ts +14 -17
- package/core/services/skill-lock.ts +3 -3
- package/core/services/skill-service.ts +12 -6
- package/core/services/stack-detector.ts +245 -0
- package/core/services/sync-service.ts +170 -329
- package/core/services/watch-service.ts +294 -0
- package/core/session/compaction.ts +23 -31
- package/core/session/index.ts +11 -5
- package/core/session/log-migration.ts +3 -3
- package/core/session/metrics.ts +19 -14
- package/core/session/session-log-manager.ts +12 -17
- package/core/session/task-session-manager.ts +25 -25
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +41 -57
- package/core/storage/index-storage.ts +514 -0
- package/core/storage/index.ts +41 -13
- package/core/storage/metrics-storage.ts +320 -0
- package/core/storage/queue-storage.ts +35 -45
- package/core/storage/shipped-storage.ts +17 -20
- package/core/storage/state-storage.ts +50 -30
- package/core/storage/storage-manager.ts +6 -6
- package/core/storage/storage.ts +18 -15
- package/core/sync/auth-config.ts +3 -3
- package/core/sync/index.ts +13 -19
- package/core/sync/oauth-handler.ts +3 -3
- package/core/sync/sync-client.ts +4 -9
- package/core/sync/sync-manager.ts +12 -14
- package/core/types/commands.ts +42 -7
- package/core/types/index.ts +284 -302
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +49 -0
- package/core/types/utils.ts +3 -3
- package/core/utils/agent-stream.ts +3 -1
- package/core/utils/animations.ts +14 -11
- package/core/utils/branding.ts +7 -7
- package/core/utils/cache.ts +1 -3
- package/core/utils/collection-filters.ts +3 -15
- package/core/utils/date-helper.ts +2 -7
- package/core/utils/file-helper.ts +13 -8
- package/core/utils/jsonl-helper.ts +13 -10
- package/core/utils/keychain.ts +4 -8
- package/core/utils/logger.ts +1 -1
- package/core/utils/next-steps.ts +3 -3
- package/core/utils/output.ts +58 -11
- package/core/utils/project-commands.ts +6 -6
- package/core/utils/project-credentials.ts +5 -12
- package/core/utils/runtime.ts +2 -2
- package/core/utils/session-helper.ts +3 -4
- package/core/utils/version.ts +3 -3
- package/core/wizard/index.ts +13 -0
- package/core/wizard/onboarding.ts +633 -0
- package/core/workflow/state-machine.ts +7 -7
- package/dist/bin/prjct.mjs +18907 -13189
- package/dist/core/infrastructure/command-installer.js +96 -111
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +256 -257
- package/dist/core/utils/version.js +9 -9
- package/package.json +11 -12
- package/scripts/build.js +3 -3
- package/scripts/postinstall.js +2 -2
- package/templates/mcp-config.json +6 -1
- package/templates/permissions/permissive.jsonc +1 -1
- package/templates/permissions/strict.jsonc +5 -9
- package/templates/global/docs/agents.md +0 -88
- package/templates/global/docs/architecture.md +0 -103
- package/templates/global/docs/commands.md +0 -96
- package/templates/global/docs/validation.md +0 -95
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
397
|
+
if (
|
|
398
|
+
labelsLower.includes('feature') ||
|
|
399
|
+
titleLower.includes('add') ||
|
|
400
|
+
titleLower.includes('implement')
|
|
401
|
+
) {
|
|
399
402
|
return 'feature'
|
|
400
403
|
}
|
|
401
|
-
if (
|
|
404
|
+
if (
|
|
405
|
+
labelsLower.includes('improvement') ||
|
|
406
|
+
titleLower.includes('improve') ||
|
|
407
|
+
titleLower.includes('enhance')
|
|
408
|
+
) {
|
|
402
409
|
return 'improvement'
|
|
403
410
|
}
|
|
404
|
-
if (
|
|
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 {
|
|
18
|
-
import {
|
|
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
|
|
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)
|
package/core/outcomes/index.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
package/core/plugin/hooks.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @version 1.0.0
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
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(
|
|
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(
|
|
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>(
|
|
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(
|
|
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 }
|
package/core/plugin/index.ts
CHANGED
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
* @version 1.0.0
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
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(
|
|
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
|
}
|