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
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* Tracks metrics, timeline, and archives completed sessions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import { promisify } from 'util'
|
|
11
|
-
import pathManager from '../infrastructure/path-manager'
|
|
12
|
-
import configManager from '../infrastructure/config-manager'
|
|
7
|
+
import { exec } from 'node:child_process'
|
|
8
|
+
import fs from 'node:fs/promises'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { promisify } from 'node:util'
|
|
13
11
|
import { emit } from '../bus'
|
|
14
|
-
import
|
|
12
|
+
import configManager from '../infrastructure/config-manager'
|
|
13
|
+
import pathManager from '../infrastructure/path-manager'
|
|
15
14
|
import type { Session, SessionMetrics } from '../types'
|
|
16
|
-
import {
|
|
15
|
+
import { isNotFoundError } from '../types/fs'
|
|
16
|
+
import { calculateDuration, formatDuration, generateId } from './utils'
|
|
17
17
|
|
|
18
18
|
const execAsync = promisify(exec)
|
|
19
19
|
|
|
@@ -100,11 +100,9 @@ export class TaskSessionManager {
|
|
|
100
100
|
linesAdded: 0,
|
|
101
101
|
linesRemoved: 0,
|
|
102
102
|
commits: 0,
|
|
103
|
-
snapshots: []
|
|
103
|
+
snapshots: [],
|
|
104
104
|
},
|
|
105
|
-
timeline: [
|
|
106
|
-
{ type: 'start', at: now }
|
|
107
|
-
]
|
|
105
|
+
timeline: [{ type: 'start', at: now }],
|
|
108
106
|
}
|
|
109
107
|
|
|
110
108
|
// Save as current session
|
|
@@ -117,7 +115,7 @@ export class TaskSessionManager {
|
|
|
117
115
|
await emit.sessionStarted({
|
|
118
116
|
sessionId: session.id,
|
|
119
117
|
task,
|
|
120
|
-
projectId: this.projectId
|
|
118
|
+
projectId: this.projectId,
|
|
121
119
|
})
|
|
122
120
|
|
|
123
121
|
return session
|
|
@@ -161,7 +159,7 @@ export class TaskSessionManager {
|
|
|
161
159
|
await emit.sessionResumed({
|
|
162
160
|
sessionId: current.id,
|
|
163
161
|
task: current.task,
|
|
164
|
-
projectId: this.projectId
|
|
162
|
+
projectId: this.projectId,
|
|
165
163
|
})
|
|
166
164
|
|
|
167
165
|
return current
|
|
@@ -196,7 +194,7 @@ export class TaskSessionManager {
|
|
|
196
194
|
sessionId: current.id,
|
|
197
195
|
task: current.task,
|
|
198
196
|
duration: current.duration,
|
|
199
|
-
projectId: this.projectId
|
|
197
|
+
projectId: this.projectId,
|
|
200
198
|
})
|
|
201
199
|
|
|
202
200
|
return current
|
|
@@ -231,7 +229,7 @@ export class TaskSessionManager {
|
|
|
231
229
|
sessionId: current.id,
|
|
232
230
|
task: current.task,
|
|
233
231
|
duration: current.duration,
|
|
234
|
-
metrics: current.metrics
|
|
232
|
+
metrics: current.metrics,
|
|
235
233
|
})
|
|
236
234
|
|
|
237
235
|
// Emit event for plugins
|
|
@@ -240,7 +238,7 @@ export class TaskSessionManager {
|
|
|
240
238
|
task: current.task,
|
|
241
239
|
duration: current.duration,
|
|
242
240
|
metrics: current.metrics,
|
|
243
|
-
projectId: this.projectId
|
|
241
|
+
projectId: this.projectId,
|
|
244
242
|
})
|
|
245
243
|
|
|
246
244
|
return current
|
|
@@ -268,7 +266,7 @@ export class TaskSessionManager {
|
|
|
268
266
|
`git rev-list --count --since="${since}" HEAD 2>/dev/null || echo "0"`,
|
|
269
267
|
{ cwd: this.projectPath }
|
|
270
268
|
)
|
|
271
|
-
metrics.commits = parseInt(commitCount.trim()) || 0
|
|
269
|
+
metrics.commits = parseInt(commitCount.trim(), 10) || 0
|
|
272
270
|
|
|
273
271
|
// Get diff stats
|
|
274
272
|
const { stdout: diffStat } = await execAsync(
|
|
@@ -279,12 +277,14 @@ export class TaskSessionManager {
|
|
|
279
277
|
// Parse diff stats
|
|
280
278
|
const lines = diffStat.split('\n')
|
|
281
279
|
const summaryLine = lines[lines.length - 2] || ''
|
|
282
|
-
const match = summaryLine.match(
|
|
280
|
+
const match = summaryLine.match(
|
|
281
|
+
/(\d+) files? changed(?:, (\d+) insertions?)?(?:, (\d+) deletions?)?/
|
|
282
|
+
)
|
|
283
283
|
|
|
284
284
|
if (match) {
|
|
285
|
-
metrics.filesChanged = parseInt(match[1]) || 0
|
|
286
|
-
metrics.linesAdded = parseInt(match[2]) || 0
|
|
287
|
-
metrics.linesRemoved = parseInt(match[3]) || 0
|
|
285
|
+
metrics.filesChanged = parseInt(match[1], 10) || 0
|
|
286
|
+
metrics.linesAdded = parseInt(match[2], 10) || 0
|
|
287
|
+
metrics.linesRemoved = parseInt(match[3], 10) || 0
|
|
288
288
|
}
|
|
289
289
|
} catch (error) {
|
|
290
290
|
// Keep existing metrics if git fails (not a repo, git not installed, etc.)
|
|
@@ -379,11 +379,11 @@ export class TaskSessionManager {
|
|
|
379
379
|
const globalPath = pathManager.getGlobalProjectPath(this.projectId!)
|
|
380
380
|
const memoryPath = path.join(globalPath, 'memory', 'context.jsonl')
|
|
381
381
|
|
|
382
|
-
const entry = JSON.stringify({
|
|
382
|
+
const entry = `${JSON.stringify({
|
|
383
383
|
timestamp: new Date().toISOString(),
|
|
384
384
|
action,
|
|
385
|
-
...data
|
|
386
|
-
})
|
|
385
|
+
...data,
|
|
386
|
+
})}\n`
|
|
387
387
|
|
|
388
388
|
try {
|
|
389
389
|
await fs.appendFile(memoryPath, entry)
|
package/core/session/utils.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* Generates context/ideas.md for Claude
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { StorageManager } from './storage-manager'
|
|
9
8
|
import { generateUUID } from '../schemas'
|
|
9
|
+
import type { Idea, IdeaPriority, IdeaStatus, IdeasJson } from '../types'
|
|
10
10
|
import { getTimestamp } from '../utils/date-helper'
|
|
11
|
-
import
|
|
11
|
+
import { StorageManager } from './storage-manager'
|
|
12
12
|
|
|
13
13
|
class IdeasStorage extends StorageManager<IdeasJson> {
|
|
14
14
|
constructor() {
|
|
@@ -18,7 +18,7 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
18
18
|
protected getDefault(): IdeasJson {
|
|
19
19
|
return {
|
|
20
20
|
ideas: [],
|
|
21
|
-
lastUpdated: ''
|
|
21
|
+
lastUpdated: '',
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -37,16 +37,16 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
37
37
|
protected toMarkdown(data: IdeasJson): string {
|
|
38
38
|
const lines = ['# IDEAS \u{1F4A1}', '']
|
|
39
39
|
|
|
40
|
-
const pending = data.ideas.filter(i => i.status === 'pending')
|
|
41
|
-
const converted = data.ideas.filter(i => i.status === 'converted')
|
|
42
|
-
const archived = data.ideas.filter(i => i.status === 'archived')
|
|
40
|
+
const pending = data.ideas.filter((i) => i.status === 'pending')
|
|
41
|
+
const converted = data.ideas.filter((i) => i.status === 'converted')
|
|
42
|
+
const archived = data.ideas.filter((i) => i.status === 'archived')
|
|
43
43
|
|
|
44
44
|
// Brain Dump (pending)
|
|
45
45
|
lines.push('## Brain Dump')
|
|
46
46
|
if (pending.length > 0) {
|
|
47
|
-
pending.forEach(idea => {
|
|
47
|
+
pending.forEach((idea) => {
|
|
48
48
|
const date = idea.addedAt.split('T')[0]
|
|
49
|
-
const tags = idea.tags.length > 0 ?
|
|
49
|
+
const tags = idea.tags.length > 0 ? ` ${idea.tags.map((t) => `#${t}`).join(' ')}` : ''
|
|
50
50
|
const priority = idea.priority !== 'medium' ? ` [${idea.priority.toUpperCase()}]` : ''
|
|
51
51
|
lines.push(`- ${idea.text}${priority} _(${date})_${tags}`)
|
|
52
52
|
})
|
|
@@ -58,7 +58,7 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
58
58
|
// Converted
|
|
59
59
|
if (converted.length > 0) {
|
|
60
60
|
lines.push('## Converted')
|
|
61
|
-
converted.forEach(idea => {
|
|
61
|
+
converted.forEach((idea) => {
|
|
62
62
|
const date = idea.addedAt.split('T')[0]
|
|
63
63
|
const feat = idea.convertedTo ? ` \u2192 ${idea.convertedTo}` : ''
|
|
64
64
|
lines.push(`- \u2713 ${idea.text}${feat} _(${date})_`)
|
|
@@ -69,7 +69,7 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
69
69
|
// Archived
|
|
70
70
|
if (archived.length > 0) {
|
|
71
71
|
lines.push('## Archived')
|
|
72
|
-
archived.forEach(idea => {
|
|
72
|
+
archived.forEach((idea) => {
|
|
73
73
|
const date = idea.addedAt.split('T')[0]
|
|
74
74
|
lines.push(`- ${idea.text} _(${date})_`)
|
|
75
75
|
})
|
|
@@ -94,7 +94,7 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
94
94
|
*/
|
|
95
95
|
async getPending(projectId: string): Promise<Idea[]> {
|
|
96
96
|
const data = await this.read(projectId)
|
|
97
|
-
return data.ideas.filter(i => i.status === 'pending')
|
|
97
|
+
return data.ideas.filter((i) => i.status === 'pending')
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
@@ -111,19 +111,19 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
111
111
|
status: 'pending',
|
|
112
112
|
priority: options.priority || 'medium',
|
|
113
113
|
tags: options.tags || [],
|
|
114
|
-
addedAt: getTimestamp()
|
|
114
|
+
addedAt: getTimestamp(),
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
await this.update(projectId, (data) => ({
|
|
118
118
|
ideas: [idea, ...data.ideas], // Prepend new ideas
|
|
119
|
-
lastUpdated: getTimestamp()
|
|
119
|
+
lastUpdated: getTimestamp(),
|
|
120
120
|
}))
|
|
121
121
|
|
|
122
122
|
// Publish event
|
|
123
123
|
await this.publishEvent(projectId, 'idea.created', {
|
|
124
124
|
ideaId: idea.id,
|
|
125
125
|
text: idea.text,
|
|
126
|
-
priority: idea.priority
|
|
126
|
+
priority: idea.priority,
|
|
127
127
|
})
|
|
128
128
|
|
|
129
129
|
return idea
|
|
@@ -134,29 +134,23 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
134
134
|
*/
|
|
135
135
|
async getById(projectId: string, id: string): Promise<Idea | undefined> {
|
|
136
136
|
const data = await this.read(projectId)
|
|
137
|
-
return data.ideas.find(i => i.id === id)
|
|
137
|
+
return data.ideas.find((i) => i.id === id)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* Convert idea to feature
|
|
142
142
|
*/
|
|
143
|
-
async convertToFeature(
|
|
144
|
-
projectId: string,
|
|
145
|
-
ideaId: string,
|
|
146
|
-
featureId: string
|
|
147
|
-
): Promise<void> {
|
|
143
|
+
async convertToFeature(projectId: string, ideaId: string, featureId: string): Promise<void> {
|
|
148
144
|
await this.update(projectId, (data) => ({
|
|
149
|
-
ideas: data.ideas.map(i =>
|
|
150
|
-
i.id === ideaId
|
|
151
|
-
? { ...i, status: 'converted' as IdeaStatus, convertedTo: featureId }
|
|
152
|
-
: i
|
|
145
|
+
ideas: data.ideas.map((i) =>
|
|
146
|
+
i.id === ideaId ? { ...i, status: 'converted' as IdeaStatus, convertedTo: featureId } : i
|
|
153
147
|
),
|
|
154
|
-
lastUpdated: getTimestamp()
|
|
148
|
+
lastUpdated: getTimestamp(),
|
|
155
149
|
}))
|
|
156
150
|
|
|
157
151
|
await this.publishEvent(projectId, 'idea.converted', {
|
|
158
152
|
ideaId,
|
|
159
|
-
featureId
|
|
153
|
+
featureId,
|
|
160
154
|
})
|
|
161
155
|
}
|
|
162
156
|
|
|
@@ -165,10 +159,10 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
165
159
|
*/
|
|
166
160
|
async archive(projectId: string, ideaId: string): Promise<void> {
|
|
167
161
|
await this.update(projectId, (data) => ({
|
|
168
|
-
ideas: data.ideas.map(i =>
|
|
162
|
+
ideas: data.ideas.map((i) =>
|
|
169
163
|
i.id === ideaId ? { ...i, status: 'archived' as IdeaStatus } : i
|
|
170
164
|
),
|
|
171
|
-
lastUpdated: getTimestamp()
|
|
165
|
+
lastUpdated: getTimestamp(),
|
|
172
166
|
}))
|
|
173
167
|
|
|
174
168
|
await this.publishEvent(projectId, 'idea.archived', { ideaId })
|
|
@@ -177,34 +171,22 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
177
171
|
/**
|
|
178
172
|
* Set priority
|
|
179
173
|
*/
|
|
180
|
-
async setPriority(
|
|
181
|
-
projectId: string,
|
|
182
|
-
ideaId: string,
|
|
183
|
-
priority: IdeaPriority
|
|
184
|
-
): Promise<void> {
|
|
174
|
+
async setPriority(projectId: string, ideaId: string, priority: IdeaPriority): Promise<void> {
|
|
185
175
|
await this.update(projectId, (data) => ({
|
|
186
|
-
ideas: data.ideas.map(i =>
|
|
187
|
-
|
|
188
|
-
),
|
|
189
|
-
lastUpdated: getTimestamp()
|
|
176
|
+
ideas: data.ideas.map((i) => (i.id === ideaId ? { ...i, priority } : i)),
|
|
177
|
+
lastUpdated: getTimestamp(),
|
|
190
178
|
}))
|
|
191
179
|
}
|
|
192
180
|
|
|
193
181
|
/**
|
|
194
182
|
* Add tags to an idea
|
|
195
183
|
*/
|
|
196
|
-
async addTags(
|
|
197
|
-
projectId: string,
|
|
198
|
-
ideaId: string,
|
|
199
|
-
tags: string[]
|
|
200
|
-
): Promise<void> {
|
|
184
|
+
async addTags(projectId: string, ideaId: string, tags: string[]): Promise<void> {
|
|
201
185
|
await this.update(projectId, (data) => ({
|
|
202
|
-
ideas: data.ideas.map(i =>
|
|
203
|
-
i.id === ideaId
|
|
204
|
-
? { ...i, tags: [...new Set([...i.tags, ...tags])] }
|
|
205
|
-
: i
|
|
186
|
+
ideas: data.ideas.map((i) =>
|
|
187
|
+
i.id === ideaId ? { ...i, tags: [...new Set([...i.tags, ...tags])] } : i
|
|
206
188
|
),
|
|
207
|
-
lastUpdated: getTimestamp()
|
|
189
|
+
lastUpdated: getTimestamp(),
|
|
208
190
|
}))
|
|
209
191
|
}
|
|
210
192
|
|
|
@@ -213,20 +195,22 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
213
195
|
*/
|
|
214
196
|
async removeIdea(projectId: string, ideaId: string): Promise<void> {
|
|
215
197
|
await this.update(projectId, (data) => ({
|
|
216
|
-
ideas: data.ideas.filter(i => i.id !== ideaId),
|
|
217
|
-
lastUpdated: getTimestamp()
|
|
198
|
+
ideas: data.ideas.filter((i) => i.id !== ideaId),
|
|
199
|
+
lastUpdated: getTimestamp(),
|
|
218
200
|
}))
|
|
219
201
|
}
|
|
220
202
|
|
|
221
203
|
/**
|
|
222
204
|
* Get counts by status
|
|
223
205
|
*/
|
|
224
|
-
async getCounts(
|
|
206
|
+
async getCounts(
|
|
207
|
+
projectId: string
|
|
208
|
+
): Promise<{ pending: number; converted: number; archived: number }> {
|
|
225
209
|
const data = await this.read(projectId)
|
|
226
210
|
return {
|
|
227
|
-
pending: data.ideas.filter(i => i.status === 'pending').length,
|
|
228
|
-
converted: data.ideas.filter(i => i.status === 'converted').length,
|
|
229
|
-
archived: data.ideas.filter(i => i.status === 'archived').length
|
|
211
|
+
pending: data.ideas.filter((i) => i.status === 'pending').length,
|
|
212
|
+
converted: data.ideas.filter((i) => i.status === 'converted').length,
|
|
213
|
+
archived: data.ideas.filter((i) => i.status === 'archived').length,
|
|
230
214
|
}
|
|
231
215
|
}
|
|
232
216
|
|
|
@@ -235,7 +219,7 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
235
219
|
*/
|
|
236
220
|
async cleanup(projectId: string): Promise<{ removed: number }> {
|
|
237
221
|
const data = await this.read(projectId)
|
|
238
|
-
const archived = data.ideas.filter(i => i.status === 'archived')
|
|
222
|
+
const archived = data.ideas.filter((i) => i.status === 'archived')
|
|
239
223
|
|
|
240
224
|
if (archived.length <= 50) {
|
|
241
225
|
return { removed: 0 }
|
|
@@ -245,12 +229,12 @@ class IdeasStorage extends StorageManager<IdeasJson> {
|
|
|
245
229
|
const sortedArchived = archived.sort(
|
|
246
230
|
(a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()
|
|
247
231
|
)
|
|
248
|
-
const toRemove = new Set(sortedArchived.slice(50).map(i => i.id))
|
|
232
|
+
const toRemove = new Set(sortedArchived.slice(50).map((i) => i.id))
|
|
249
233
|
const removed = toRemove.size
|
|
250
234
|
|
|
251
235
|
await this.update(projectId, (d) => ({
|
|
252
|
-
ideas: d.ideas.filter(i => !toRemove.has(i.id)),
|
|
253
|
-
lastUpdated: getTimestamp()
|
|
236
|
+
ideas: d.ideas.filter((i) => !toRemove.has(i.id)),
|
|
237
|
+
lastUpdated: getTimestamp(),
|
|
254
238
|
}))
|
|
255
239
|
|
|
256
240
|
return { removed }
|