prjct-cli 0.45.0 → 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 +75 -0
- package/bin/prjct.ts +117 -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 +58 -39
- 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 +28 -4
- package/core/commands/commands.ts +57 -24
- 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 +13 -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 +18 -19
- package/core/context-tools/imports-tool.ts +13 -33
- package/core/context-tools/index.ts +29 -54
- package/core/context-tools/recent-tool.ts +16 -22
- package/core/context-tools/signatures-tool.ts +17 -26
- package/core/context-tools/summary-tool.ts +20 -22
- package/core/context-tools/token-counter.ts +25 -20
- package/core/context-tools/types.ts +5 -5
- 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 -16
- 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 +25 -25
- 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 +87 -345
- 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 -17
- package/core/storage/metrics-storage.ts +39 -34
- 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 -305
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +14 -14
- 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 +18755 -15574
- package/dist/core/infrastructure/command-installer.js +86 -79
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +246 -225
- 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
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
* - Daily trends for visualization
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { StorageManager } from './storage-manager'
|
|
15
|
-
import { getTimestamp } from '../utils/date-helper'
|
|
16
14
|
import {
|
|
17
|
-
type MetricsJson,
|
|
18
|
-
type DailyStats,
|
|
19
15
|
type AgentUsage,
|
|
16
|
+
type DailyStats,
|
|
20
17
|
DEFAULT_METRICS,
|
|
21
18
|
estimateCostSaved,
|
|
22
19
|
formatCost,
|
|
20
|
+
type MetricsJson,
|
|
23
21
|
} from '../schemas/metrics'
|
|
22
|
+
import { getTimestamp } from '../utils/date-helper'
|
|
23
|
+
import { StorageManager } from './storage-manager'
|
|
24
24
|
|
|
25
25
|
class MetricsStorage extends StorageManager<MetricsJson> {
|
|
26
26
|
constructor() {
|
|
@@ -56,8 +56,12 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
56
56
|
lines.push('## 💰 Token Savings')
|
|
57
57
|
lines.push('')
|
|
58
58
|
lines.push(`- **Total saved**: ${this.formatTokens(data.totalTokensSaved)} tokens`)
|
|
59
|
-
lines.push(
|
|
60
|
-
|
|
59
|
+
lines.push(
|
|
60
|
+
`- **Compression**: ${(data.avgCompressionRate * 100).toFixed(0)}% average reduction`
|
|
61
|
+
)
|
|
62
|
+
lines.push(
|
|
63
|
+
`- **Estimated cost saved**: ${formatCost(estimateCostSaved(data.totalTokensSaved))}`
|
|
64
|
+
)
|
|
61
65
|
lines.push('')
|
|
62
66
|
|
|
63
67
|
// Performance
|
|
@@ -77,7 +81,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
77
81
|
const sortedAgents = [...data.agentUsage].sort((a, b) => b.usageCount - a.usageCount)
|
|
78
82
|
const totalUsage = sortedAgents.reduce((sum, a) => sum + a.usageCount, 0)
|
|
79
83
|
|
|
80
|
-
sortedAgents.slice(0, 5).forEach(agent => {
|
|
84
|
+
sortedAgents.slice(0, 5).forEach((agent) => {
|
|
81
85
|
const pct = totalUsage > 0 ? ((agent.usageCount / totalUsage) * 100).toFixed(0) : 0
|
|
82
86
|
lines.push(`- **${agent.agentName}**: ${pct}% (${agent.usageCount} uses)`)
|
|
83
87
|
})
|
|
@@ -118,17 +122,15 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
118
122
|
async recordSync(
|
|
119
123
|
projectId: string,
|
|
120
124
|
metrics: {
|
|
121
|
-
originalSize: number
|
|
122
|
-
filteredSize: number
|
|
123
|
-
duration: number
|
|
124
|
-
isWatch?: boolean
|
|
125
|
-
agents?: string[]
|
|
125
|
+
originalSize: number // Tokens before compression
|
|
126
|
+
filteredSize: number // Tokens after compression
|
|
127
|
+
duration: number // Sync duration in ms
|
|
128
|
+
isWatch?: boolean // From watch mode?
|
|
129
|
+
agents?: string[] // Agents used
|
|
126
130
|
}
|
|
127
131
|
): Promise<void> {
|
|
128
132
|
const tokensSaved = Math.max(0, metrics.originalSize - metrics.filteredSize)
|
|
129
|
-
const compressionRate = metrics.originalSize > 0
|
|
130
|
-
? tokensSaved / metrics.originalSize
|
|
131
|
-
: 0
|
|
133
|
+
const compressionRate = metrics.originalSize > 0 ? tokensSaved / metrics.originalSize : 0
|
|
132
134
|
|
|
133
135
|
const today = new Date().toISOString().split('T')[0]
|
|
134
136
|
|
|
@@ -139,13 +141,14 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
139
141
|
const newTotalDuration = data.totalSyncDuration + metrics.duration
|
|
140
142
|
|
|
141
143
|
// Running average for compression rate
|
|
142
|
-
const newAvgCompression =
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
const newAvgCompression =
|
|
145
|
+
data.syncCount === 0
|
|
146
|
+
? compressionRate
|
|
147
|
+
: (data.avgCompressionRate * data.syncCount + compressionRate) / newSyncCount
|
|
145
148
|
|
|
146
149
|
// Update daily stats
|
|
147
150
|
const dailyStats = [...data.dailyStats]
|
|
148
|
-
const todayIndex = dailyStats.findIndex(d => d.date === today)
|
|
151
|
+
const todayIndex = dailyStats.findIndex((d) => d.date === today)
|
|
149
152
|
|
|
150
153
|
if (todayIndex >= 0) {
|
|
151
154
|
const existing = dailyStats[todayIndex]
|
|
@@ -153,7 +156,8 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
153
156
|
...existing,
|
|
154
157
|
tokensSaved: existing.tokensSaved + tokensSaved,
|
|
155
158
|
syncs: existing.syncs + 1,
|
|
156
|
-
avgCompressionRate:
|
|
159
|
+
avgCompressionRate:
|
|
160
|
+
(existing.avgCompressionRate * existing.syncs + compressionRate) / (existing.syncs + 1),
|
|
157
161
|
totalDuration: existing.totalDuration + metrics.duration,
|
|
158
162
|
}
|
|
159
163
|
} else {
|
|
@@ -170,18 +174,19 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
170
174
|
const cutoff = new Date()
|
|
171
175
|
cutoff.setDate(cutoff.getDate() - 90)
|
|
172
176
|
const cutoffStr = cutoff.toISOString().split('T')[0]
|
|
173
|
-
const trimmedStats = dailyStats.filter(d => d.date >= cutoffStr)
|
|
177
|
+
const trimmedStats = dailyStats.filter((d) => d.date >= cutoffStr)
|
|
174
178
|
|
|
175
179
|
// Update agent usage
|
|
176
180
|
const agentUsage = [...data.agentUsage]
|
|
177
181
|
if (metrics.agents) {
|
|
178
182
|
for (const agentName of metrics.agents) {
|
|
179
|
-
const idx = agentUsage.findIndex(a => a.agentName === agentName)
|
|
183
|
+
const idx = agentUsage.findIndex((a) => a.agentName === agentName)
|
|
180
184
|
if (idx >= 0) {
|
|
181
185
|
agentUsage[idx] = {
|
|
182
186
|
...agentUsage[idx],
|
|
183
187
|
usageCount: agentUsage[idx].usageCount + 1,
|
|
184
|
-
tokensSaved:
|
|
188
|
+
tokensSaved:
|
|
189
|
+
agentUsage[idx].tokensSaved + Math.floor(tokensSaved / metrics.agents.length),
|
|
185
190
|
}
|
|
186
191
|
} else {
|
|
187
192
|
agentUsage.push({
|
|
@@ -229,9 +234,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
229
234
|
const last30Tokens = last30.reduce((sum, d) => sum + d.tokensSaved, 0)
|
|
230
235
|
const prev30Tokens = prev30.reduce((sum, d) => sum + d.tokensSaved, 0)
|
|
231
236
|
|
|
232
|
-
const trend = prev30Tokens > 0
|
|
233
|
-
? ((last30Tokens - prev30Tokens) / prev30Tokens) * 100
|
|
234
|
-
: 0
|
|
237
|
+
const trend = prev30Tokens > 0 ? ((last30Tokens - prev30Tokens) / prev30Tokens) * 100 : 0
|
|
235
238
|
|
|
236
239
|
return {
|
|
237
240
|
totalTokensSaved: data.totalTokensSaved,
|
|
@@ -255,7 +258,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
255
258
|
const cutoffStr = cutoff.toISOString().split('T')[0]
|
|
256
259
|
|
|
257
260
|
return data.dailyStats
|
|
258
|
-
.filter(d => d.date >= cutoffStr)
|
|
261
|
+
.filter((d) => d.date >= cutoffStr)
|
|
259
262
|
.sort((a, b) => a.date.localeCompare(b.date))
|
|
260
263
|
}
|
|
261
264
|
|
|
@@ -265,7 +268,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
265
268
|
const cutoff = new Date()
|
|
266
269
|
cutoff.setDate(cutoff.getDate() - 30)
|
|
267
270
|
const cutoffStr = cutoff.toISOString().split('T')[0]
|
|
268
|
-
return dailyStats.filter(d => d.date >= cutoffStr)
|
|
271
|
+
return dailyStats.filter((d) => d.date >= cutoffStr)
|
|
269
272
|
}
|
|
270
273
|
|
|
271
274
|
private getPrev30Days(dailyStats: DailyStats[]): DailyStats[] {
|
|
@@ -277,7 +280,7 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
277
280
|
const startStr = start.toISOString().split('T')[0]
|
|
278
281
|
const endStr = end.toISOString().split('T')[0]
|
|
279
282
|
|
|
280
|
-
return dailyStats.filter(d => d.date >= startStr && d.date < endStr)
|
|
283
|
+
return dailyStats.filter((d) => d.date >= startStr && d.date < endStr)
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
private formatTokens(tokens: number): string {
|
|
@@ -301,13 +304,15 @@ class MetricsStorage extends StorageManager<MetricsJson> {
|
|
|
301
304
|
if (dailyStats.length === 0) return ''
|
|
302
305
|
|
|
303
306
|
const chars = '▁▂▃▄▅▆▇█'
|
|
304
|
-
const values = dailyStats.map(d => d.tokensSaved)
|
|
307
|
+
const values = dailyStats.map((d) => d.tokensSaved)
|
|
305
308
|
const max = Math.max(...values, 1)
|
|
306
309
|
|
|
307
|
-
return values
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
return values
|
|
311
|
+
.map((v) => {
|
|
312
|
+
const idx = Math.min(Math.floor((v / max) * (chars.length - 1)), chars.length - 1)
|
|
313
|
+
return chars[idx]
|
|
314
|
+
})
|
|
315
|
+
.join('')
|
|
311
316
|
}
|
|
312
317
|
}
|
|
313
318
|
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* Generates context/next.md for Claude
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { StorageManager } from './storage-manager'
|
|
9
8
|
import { generateUUID } from '../schemas'
|
|
9
|
+
import type { Priority, QueueJson, QueueTask, TaskSection } from '../schemas/state'
|
|
10
10
|
import { getTimestamp } from '../utils/date-helper'
|
|
11
|
-
import
|
|
11
|
+
import { StorageManager } from './storage-manager'
|
|
12
12
|
|
|
13
13
|
class QueueStorage extends StorageManager<QueueJson> {
|
|
14
14
|
constructor() {
|
|
@@ -18,7 +18,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
18
18
|
protected getDefault(): QueueJson {
|
|
19
19
|
return {
|
|
20
20
|
tasks: [],
|
|
21
|
-
lastUpdated: ''
|
|
21
|
+
lastUpdated: '',
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -37,9 +37,11 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
37
37
|
protected toMarkdown(data: QueueJson): string {
|
|
38
38
|
const lines = ['# Priority Queue', '']
|
|
39
39
|
|
|
40
|
-
const activeTasks = data.tasks.filter(t => t.section === 'active' && !t.completed)
|
|
41
|
-
const backlogTasks = data.tasks.filter(t => t.section === 'backlog' && !t.completed)
|
|
42
|
-
const previouslyActive = data.tasks.filter(
|
|
40
|
+
const activeTasks = data.tasks.filter((t) => t.section === 'active' && !t.completed)
|
|
41
|
+
const backlogTasks = data.tasks.filter((t) => t.section === 'backlog' && !t.completed)
|
|
42
|
+
const previouslyActive = data.tasks.filter(
|
|
43
|
+
(t) => t.section === 'previously_active' && !t.completed
|
|
44
|
+
)
|
|
43
45
|
|
|
44
46
|
// Active section
|
|
45
47
|
lines.push('## Active Tasks')
|
|
@@ -60,7 +62,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
60
62
|
// Previously active section (if any)
|
|
61
63
|
if (previouslyActive.length > 0) {
|
|
62
64
|
lines.push('## Previously Active')
|
|
63
|
-
previouslyActive.forEach(task => {
|
|
65
|
+
previouslyActive.forEach((task) => {
|
|
64
66
|
lines.push(`- [ ] ${task.description}`)
|
|
65
67
|
})
|
|
66
68
|
lines.push('')
|
|
@@ -69,7 +71,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
69
71
|
// Backlog section
|
|
70
72
|
lines.push('## Backlog')
|
|
71
73
|
if (backlogTasks.length > 0) {
|
|
72
|
-
backlogTasks.forEach(task => {
|
|
74
|
+
backlogTasks.forEach((task) => {
|
|
73
75
|
const priority = task.priority !== 'medium' ? ` [${task.priority.toUpperCase()}]` : ''
|
|
74
76
|
const bug = task.type === 'bug' ? ' \u{1F41B}' : ''
|
|
75
77
|
lines.push(`- [ ]${bug}${priority} ${task.description}`)
|
|
@@ -97,7 +99,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
97
99
|
*/
|
|
98
100
|
async getActiveTasks(projectId: string): Promise<QueueTask[]> {
|
|
99
101
|
const queue = await this.read(projectId)
|
|
100
|
-
return queue.tasks.filter(t => t.section === 'active' && !t.completed)
|
|
102
|
+
return queue.tasks.filter((t) => t.section === 'active' && !t.completed)
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
/**
|
|
@@ -105,7 +107,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
105
107
|
*/
|
|
106
108
|
async getBacklog(projectId: string): Promise<QueueTask[]> {
|
|
107
109
|
const queue = await this.read(projectId)
|
|
108
|
-
return queue.tasks.filter(t => t.section === 'backlog' && !t.completed)
|
|
110
|
+
return queue.tasks.filter((t) => t.section === 'backlog' && !t.completed)
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
/**
|
|
@@ -127,12 +129,12 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
127
129
|
...task,
|
|
128
130
|
id: generateUUID(),
|
|
129
131
|
createdAt: getTimestamp(),
|
|
130
|
-
completed: false
|
|
132
|
+
completed: false,
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
await this.update(projectId, (queue) => ({
|
|
134
136
|
tasks: [...queue.tasks, newTask],
|
|
135
|
-
lastUpdated: getTimestamp()
|
|
137
|
+
lastUpdated: getTimestamp(),
|
|
136
138
|
}))
|
|
137
139
|
|
|
138
140
|
// Publish incremental event
|
|
@@ -140,7 +142,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
140
142
|
taskId: newTask.id,
|
|
141
143
|
description: newTask.description,
|
|
142
144
|
priority: newTask.priority,
|
|
143
|
-
section: newTask.section
|
|
145
|
+
section: newTask.section,
|
|
144
146
|
})
|
|
145
147
|
|
|
146
148
|
return newTask
|
|
@@ -154,22 +156,22 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
154
156
|
tasks: Omit<QueueTask, 'id' | 'createdAt' | 'completed' | 'completedAt'>[]
|
|
155
157
|
): Promise<QueueTask[]> {
|
|
156
158
|
const now = getTimestamp()
|
|
157
|
-
const newTasks: QueueTask[] = tasks.map(task => ({
|
|
159
|
+
const newTasks: QueueTask[] = tasks.map((task) => ({
|
|
158
160
|
...task,
|
|
159
161
|
id: generateUUID(),
|
|
160
162
|
createdAt: now,
|
|
161
|
-
completed: false
|
|
163
|
+
completed: false,
|
|
162
164
|
}))
|
|
163
165
|
|
|
164
166
|
await this.update(projectId, (queue) => ({
|
|
165
167
|
tasks: [...queue.tasks, ...newTasks],
|
|
166
|
-
lastUpdated: now
|
|
168
|
+
lastUpdated: now,
|
|
167
169
|
}))
|
|
168
170
|
|
|
169
171
|
// Publish event for batch add
|
|
170
172
|
await this.publishEvent(projectId, 'queue.tasks_added', {
|
|
171
173
|
count: newTasks.length,
|
|
172
|
-
tasks: newTasks.map(t => ({ id: t.id, description: t.description }))
|
|
174
|
+
tasks: newTasks.map((t) => ({ id: t.id, description: t.description })),
|
|
173
175
|
})
|
|
174
176
|
|
|
175
177
|
return newTasks
|
|
@@ -180,8 +182,8 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
180
182
|
*/
|
|
181
183
|
async removeTask(projectId: string, taskId: string): Promise<void> {
|
|
182
184
|
await this.update(projectId, (queue) => ({
|
|
183
|
-
tasks: queue.tasks.filter(t => t.id !== taskId),
|
|
184
|
-
lastUpdated: getTimestamp()
|
|
185
|
+
tasks: queue.tasks.filter((t) => t.id !== taskId),
|
|
186
|
+
lastUpdated: getTimestamp(),
|
|
185
187
|
}))
|
|
186
188
|
|
|
187
189
|
await this.publishEvent(projectId, 'queue.task_removed', { taskId })
|
|
@@ -194,12 +196,12 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
194
196
|
let completedTask: QueueTask | null = null
|
|
195
197
|
|
|
196
198
|
await this.update(projectId, (queue) => {
|
|
197
|
-
const tasks = queue.tasks.map(t => {
|
|
199
|
+
const tasks = queue.tasks.map((t) => {
|
|
198
200
|
if (t.id === taskId) {
|
|
199
201
|
completedTask = {
|
|
200
202
|
...t,
|
|
201
203
|
completed: true,
|
|
202
|
-
completedAt: getTimestamp()
|
|
204
|
+
completedAt: getTimestamp(),
|
|
203
205
|
}
|
|
204
206
|
return completedTask
|
|
205
207
|
}
|
|
@@ -213,7 +215,7 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
213
215
|
await this.publishEvent(projectId, 'queue.task_completed', {
|
|
214
216
|
taskId,
|
|
215
217
|
description: task.description,
|
|
216
|
-
completedAt: task.completedAt
|
|
218
|
+
completedAt: task.completedAt,
|
|
217
219
|
})
|
|
218
220
|
}
|
|
219
221
|
|
|
@@ -223,32 +225,20 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
223
225
|
/**
|
|
224
226
|
* Move task to different section
|
|
225
227
|
*/
|
|
226
|
-
async moveToSection(
|
|
227
|
-
projectId: string,
|
|
228
|
-
taskId: string,
|
|
229
|
-
section: TaskSection
|
|
230
|
-
): Promise<void> {
|
|
228
|
+
async moveToSection(projectId: string, taskId: string, section: TaskSection): Promise<void> {
|
|
231
229
|
await this.update(projectId, (queue) => ({
|
|
232
|
-
tasks: queue.tasks.map(t =>
|
|
233
|
-
|
|
234
|
-
),
|
|
235
|
-
lastUpdated: getTimestamp()
|
|
230
|
+
tasks: queue.tasks.map((t) => (t.id === taskId ? { ...t, section } : t)),
|
|
231
|
+
lastUpdated: getTimestamp(),
|
|
236
232
|
}))
|
|
237
233
|
}
|
|
238
234
|
|
|
239
235
|
/**
|
|
240
236
|
* Set task priority
|
|
241
237
|
*/
|
|
242
|
-
async setPriority(
|
|
243
|
-
projectId: string,
|
|
244
|
-
taskId: string,
|
|
245
|
-
priority: Priority
|
|
246
|
-
): Promise<void> {
|
|
238
|
+
async setPriority(projectId: string, taskId: string, priority: Priority): Promise<void> {
|
|
247
239
|
await this.update(projectId, (queue) => ({
|
|
248
|
-
tasks: queue.tasks.map(t =>
|
|
249
|
-
|
|
250
|
-
),
|
|
251
|
-
lastUpdated: getTimestamp()
|
|
240
|
+
tasks: queue.tasks.map((t) => (t.id === taskId ? { ...t, priority } : t)),
|
|
241
|
+
lastUpdated: getTimestamp(),
|
|
252
242
|
}))
|
|
253
243
|
}
|
|
254
244
|
|
|
@@ -257,11 +247,11 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
257
247
|
*/
|
|
258
248
|
async clearCompleted(projectId: string): Promise<number> {
|
|
259
249
|
const queue = await this.read(projectId)
|
|
260
|
-
const completedCount = queue.tasks.filter(t => t.completed).length
|
|
250
|
+
const completedCount = queue.tasks.filter((t) => t.completed).length
|
|
261
251
|
|
|
262
252
|
await this.update(projectId, (q) => ({
|
|
263
|
-
tasks: q.tasks.filter(t => !t.completed),
|
|
264
|
-
lastUpdated: getTimestamp()
|
|
253
|
+
tasks: q.tasks.filter((t) => !t.completed),
|
|
254
|
+
lastUpdated: getTimestamp(),
|
|
265
255
|
}))
|
|
266
256
|
|
|
267
257
|
return completedCount
|
|
@@ -275,13 +265,13 @@ class QueueStorage extends StorageManager<QueueJson> {
|
|
|
275
265
|
critical: 0,
|
|
276
266
|
high: 1,
|
|
277
267
|
medium: 2,
|
|
278
|
-
low: 3
|
|
268
|
+
low: 3,
|
|
279
269
|
}
|
|
280
270
|
|
|
281
271
|
const sectionOrder: Record<TaskSection, number> = {
|
|
282
272
|
active: 0,
|
|
283
273
|
previously_active: 1,
|
|
284
|
-
backlog: 2
|
|
274
|
+
backlog: 2,
|
|
285
275
|
}
|
|
286
276
|
|
|
287
277
|
return [...tasks].sort((a, b) => {
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* Generates context/shipped.md for Claude
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { StorageManager } from './storage-manager'
|
|
9
8
|
import { generateUUID } from '../schemas'
|
|
10
|
-
import { getTimestamp } from '../utils/date-helper'
|
|
11
9
|
import type { ShippedFeature, ShippedJson } from '../types'
|
|
10
|
+
import { getTimestamp } from '../utils/date-helper'
|
|
11
|
+
import { StorageManager } from './storage-manager'
|
|
12
12
|
|
|
13
13
|
class ShippedStorage extends StorageManager<ShippedJson> {
|
|
14
14
|
constructor() {
|
|
@@ -18,7 +18,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
18
18
|
protected getDefault(): ShippedJson {
|
|
19
19
|
return {
|
|
20
20
|
shipped: [],
|
|
21
|
-
lastUpdated: ''
|
|
21
|
+
lastUpdated: '',
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -46,7 +46,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
46
46
|
// Group by month
|
|
47
47
|
const byMonth = new Map<string, ShippedFeature[]>()
|
|
48
48
|
|
|
49
|
-
data.shipped.forEach(ship => {
|
|
49
|
+
data.shipped.forEach((ship) => {
|
|
50
50
|
const date = new Date(ship.shippedAt)
|
|
51
51
|
const month = date.toLocaleDateString('en-US', { year: 'numeric', month: 'long' })
|
|
52
52
|
|
|
@@ -63,18 +63,18 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
63
63
|
return dateB.getTime() - dateA.getTime()
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
sortedMonths.forEach(month => {
|
|
66
|
+
sortedMonths.forEach((month) => {
|
|
67
67
|
lines.push(`## ${month}`)
|
|
68
68
|
lines.push('')
|
|
69
69
|
|
|
70
|
-
const ships = byMonth
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const ships = byMonth
|
|
71
|
+
.get(month)!
|
|
72
|
+
.sort((a, b) => new Date(b.shippedAt).getTime() - new Date(a.shippedAt).getTime())
|
|
73
73
|
|
|
74
|
-
ships.forEach(ship => {
|
|
74
|
+
ships.forEach((ship) => {
|
|
75
75
|
const date = new Date(ship.shippedAt).toLocaleDateString('en-US', {
|
|
76
76
|
month: 'short',
|
|
77
|
-
day: 'numeric'
|
|
77
|
+
day: 'numeric',
|
|
78
78
|
})
|
|
79
79
|
const version = ship.version ? ` v${ship.version}` : ''
|
|
80
80
|
const duration = ship.duration ? ` (${ship.duration})` : ''
|
|
@@ -126,12 +126,12 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
126
126
|
const shipped: ShippedFeature = {
|
|
127
127
|
...feature,
|
|
128
128
|
id: generateUUID(),
|
|
129
|
-
shippedAt: getTimestamp()
|
|
129
|
+
shippedAt: getTimestamp(),
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
await this.update(projectId, (data) => ({
|
|
133
133
|
shipped: [shipped, ...data.shipped], // Prepend
|
|
134
|
-
lastUpdated: getTimestamp()
|
|
134
|
+
lastUpdated: getTimestamp(),
|
|
135
135
|
}))
|
|
136
136
|
|
|
137
137
|
// Publish event
|
|
@@ -139,7 +139,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
139
139
|
shipId: shipped.id,
|
|
140
140
|
name: shipped.name,
|
|
141
141
|
version: shipped.version,
|
|
142
|
-
shippedAt: shipped.shippedAt
|
|
142
|
+
shippedAt: shipped.shippedAt,
|
|
143
143
|
})
|
|
144
144
|
|
|
145
145
|
return shipped
|
|
@@ -148,12 +148,9 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
148
148
|
/**
|
|
149
149
|
* Get shipped by version
|
|
150
150
|
*/
|
|
151
|
-
async getByVersion(
|
|
152
|
-
projectId: string,
|
|
153
|
-
version: string
|
|
154
|
-
): Promise<ShippedFeature | undefined> {
|
|
151
|
+
async getByVersion(projectId: string, version: string): Promise<ShippedFeature | undefined> {
|
|
155
152
|
const data = await this.read(projectId)
|
|
156
|
-
return data.shipped.find(s => s.version === version)
|
|
153
|
+
return data.shipped.find((s) => s.version === version)
|
|
157
154
|
}
|
|
158
155
|
|
|
159
156
|
/**
|
|
@@ -173,7 +170,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
173
170
|
endDate: Date
|
|
174
171
|
): Promise<ShippedFeature[]> {
|
|
175
172
|
const data = await this.read(projectId)
|
|
176
|
-
return data.shipped.filter(s => {
|
|
173
|
+
return data.shipped.filter((s) => {
|
|
177
174
|
const date = new Date(s.shippedAt)
|
|
178
175
|
return date >= startDate && date <= endDate
|
|
179
176
|
})
|
|
@@ -205,7 +202,7 @@ class ShippedStorage extends StorageManager<ShippedJson> {
|
|
|
205
202
|
|
|
206
203
|
return {
|
|
207
204
|
count: shipped.length,
|
|
208
|
-
period
|
|
205
|
+
period,
|
|
209
206
|
}
|
|
210
207
|
}
|
|
211
208
|
}
|