prjct-cli 0.19.0 → 0.20.1
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 +66 -6
- package/CLAUDE.md +56 -15
- package/README.md +5 -6
- package/bin/prjct +59 -42
- package/bin/prjct.ts +60 -0
- package/core/__tests__/agentic/memory-system.test.ts +18 -3
- package/core/__tests__/agentic/plan-mode.test.ts +55 -26
- package/core/__tests__/agentic/prompt-builder.test.ts +6 -6
- package/core/__tests__/utils/project-commands.test.ts +72 -0
- package/core/agentic/agent-router.ts +3 -12
- package/core/agentic/command-executor.ts +372 -3
- package/core/agentic/context-builder.ts +7 -27
- package/core/agentic/ground-truth.ts +604 -5
- package/core/agentic/index.ts +180 -0
- package/core/agentic/loop-detector.ts +418 -4
- package/core/agentic/memory-system.ts +857 -3
- package/core/agentic/plan-mode.ts +491 -4
- package/core/agentic/prompt-builder.ts +44 -65
- package/core/agentic/services.ts +13 -5
- package/core/agentic/skill-loader.ts +112 -0
- package/core/agentic/smart-context.ts +37 -122
- package/core/agentic/template-loader.ts +79 -122
- package/core/agentic/tool-registry.ts +5 -11
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +4 -2
- package/core/bus/bus.ts +262 -0
- package/core/bus/index.ts +3 -313
- package/core/commands/analysis.ts +5 -5
- package/core/commands/analytics.ts +11 -11
- package/core/commands/base.ts +33 -209
- package/core/commands/cleanup.ts +148 -0
- package/core/commands/command-data.ts +346 -0
- package/core/commands/commands.ts +216 -0
- package/core/commands/design.ts +83 -0
- package/core/commands/index.ts +13 -207
- package/core/commands/maintenance.ts +52 -473
- package/core/commands/planning.ts +3 -3
- package/core/commands/register.ts +104 -0
- package/core/commands/registry.ts +441 -0
- package/core/commands/setup.ts +25 -9
- package/core/commands/shipping.ts +48 -11
- package/core/commands/snapshots.ts +299 -0
- package/core/commands/workflow.ts +2 -2
- package/core/constants/index.ts +254 -4
- package/core/domain/agent-loader.ts +5 -6
- package/core/domain/task-stack.ts +555 -4
- package/core/errors.ts +127 -1
- package/core/events/events.ts +87 -0
- package/core/events/index.ts +4 -138
- package/core/index.ts +15 -23
- package/core/infrastructure/agent-detector.ts +126 -201
- package/core/infrastructure/author-detector.ts +99 -171
- package/core/infrastructure/command-installer.ts +476 -4
- package/core/infrastructure/config-manager.ts +41 -37
- package/core/infrastructure/path-manager.ts +59 -9
- package/core/infrastructure/permission-manager.ts +286 -0
- package/core/outcomes/analyzer.ts +7 -41
- package/core/outcomes/index.ts +1 -1
- package/core/outcomes/recorder.ts +1 -1
- package/core/{plugins → plugin/builtin}/webhook.ts +6 -22
- package/core/plugin/loader.ts +5 -5
- package/core/plugin/registry.ts +2 -2
- package/core/schemas/ideas.ts +85 -54
- package/core/schemas/index.ts +14 -33
- package/core/schemas/permissions.ts +177 -0
- package/core/schemas/project.ts +39 -12
- package/core/schemas/roadmap.ts +94 -59
- package/core/schemas/schemas.ts +39 -0
- package/core/schemas/shipped.ts +87 -60
- package/core/schemas/state.ts +110 -70
- package/core/server/index.ts +21 -0
- package/core/server/routes.ts +165 -0
- package/core/server/server.ts +136 -0
- package/core/server/sse.ts +135 -0
- package/core/services/agent-service.ts +170 -0
- package/core/services/breakdown-service.ts +126 -0
- package/core/services/index.ts +21 -0
- package/core/services/memory-service.ts +108 -0
- package/core/services/project-service.ts +146 -0
- package/core/services/skill-service.ts +253 -0
- package/core/session/compaction.ts +257 -0
- package/core/session/index.ts +20 -8
- package/core/{infrastructure/session-manager/migration.ts → session/log-migration.ts} +9 -9
- package/core/{infrastructure/session-manager/session-manager.ts → session/session-log-manager.ts} +27 -26
- package/core/session/{session-manager.ts → task-session-manager.ts} +7 -4
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +10 -26
- package/core/storage/index.ts +14 -162
- package/core/storage/queue-storage.ts +13 -11
- package/core/storage/shipped-storage.ts +4 -17
- package/core/storage/state-storage.ts +35 -43
- package/core/storage/storage-manager.ts +42 -52
- package/core/storage/storage.ts +160 -0
- package/core/sync/auth-config.ts +1 -8
- package/core/sync/index.ts +17 -10
- package/core/sync/oauth-handler.ts +1 -6
- package/core/sync/sync-client.ts +6 -34
- package/core/sync/sync-manager.ts +11 -40
- package/core/types/agentic.ts +577 -0
- package/core/types/agents.ts +145 -0
- package/core/types/bus.ts +82 -0
- package/core/types/commands.ts +366 -0
- package/core/types/config.ts +66 -0
- package/core/types/core.ts +96 -0
- package/core/types/domain.ts +71 -0
- package/core/types/events.ts +42 -0
- package/core/types/fs.ts +56 -0
- package/core/types/index.ts +387 -500
- package/core/types/infrastructure.ts +196 -0
- package/core/{agentic/memory-system/types.ts → types/memory.ts} +33 -8
- package/core/{outcomes/types.ts → types/outcomes.ts} +53 -8
- package/core/types/plugin.ts +25 -0
- package/core/types/server.ts +54 -0
- package/core/types/services.ts +65 -0
- package/core/types/session.ts +135 -0
- package/core/types/storage.ts +148 -0
- package/core/types/sync.ts +121 -0
- package/core/types/task.ts +72 -0
- package/core/types/template.ts +24 -0
- package/core/types/utils.ts +90 -0
- package/core/utils/cache.ts +195 -0
- package/core/utils/collection-filters.ts +245 -0
- package/core/utils/date-helper.ts +1 -5
- package/core/utils/file-helper.ts +20 -10
- package/core/utils/jsonl-helper.ts +5 -8
- package/core/utils/markdown-builder.ts +277 -0
- package/core/utils/project-commands.ts +132 -0
- package/core/utils/runtime.ts +119 -0
- package/dist/bin/prjct.mjs +12568 -0
- package/package.json +13 -8
- package/scripts/build.js +106 -0
- package/scripts/postinstall.js +50 -8
- package/templates/agentic/agents/uxui.md +210 -0
- package/templates/agentic/subagent-generation.md +1 -1
- package/templates/commands/bug.md +219 -41
- package/templates/commands/feature.md +368 -80
- package/templates/commands/serve.md +118 -0
- package/templates/commands/ship.md +152 -14
- package/templates/commands/skill.md +110 -0
- package/templates/commands/sync.md +63 -4
- package/templates/commands/test.md +40 -188
- package/templates/mcp-config.json +0 -36
- package/templates/permissions/default.jsonc +60 -0
- package/templates/permissions/permissive.jsonc +49 -0
- package/templates/permissions/strict.jsonc +62 -0
- package/templates/skills/code-review.md +47 -0
- package/templates/skills/debug.md +61 -0
- package/templates/skills/refactor.md +47 -0
- package/templates/subagents/domain/devops.md +1 -1
- package/templates/subagents/domain/testing.md +6 -10
- package/templates/subagents/workflow/prjct-shipper.md +16 -7
- package/templates/tools/bash.txt +22 -0
- package/templates/tools/edit.txt +18 -0
- package/templates/tools/glob.txt +19 -0
- package/templates/tools/grep.txt +21 -0
- package/templates/tools/read.txt +14 -0
- package/templates/tools/task.txt +20 -0
- package/templates/tools/webfetch.txt +16 -0
- package/templates/tools/websearch.txt +18 -0
- package/templates/tools/write.txt +17 -0
- package/core/agentic/command-executor/command-executor.ts +0 -312
- package/core/agentic/command-executor/index.ts +0 -16
- package/core/agentic/command-executor/status-signal.ts +0 -38
- package/core/agentic/command-executor/types.ts +0 -79
- package/core/agentic/ground-truth/index.ts +0 -76
- package/core/agentic/ground-truth/types.ts +0 -33
- package/core/agentic/ground-truth/utils.ts +0 -48
- package/core/agentic/ground-truth/verifiers/analyze.ts +0 -54
- package/core/agentic/ground-truth/verifiers/done.ts +0 -75
- package/core/agentic/ground-truth/verifiers/feature.ts +0 -70
- package/core/agentic/ground-truth/verifiers/index.ts +0 -37
- package/core/agentic/ground-truth/verifiers/init.ts +0 -52
- package/core/agentic/ground-truth/verifiers/now.ts +0 -57
- package/core/agentic/ground-truth/verifiers/ship.ts +0 -85
- package/core/agentic/ground-truth/verifiers/spec.ts +0 -45
- package/core/agentic/ground-truth/verifiers/sync.ts +0 -47
- package/core/agentic/ground-truth/verifiers.ts +0 -6
- package/core/agentic/loop-detector/error-analysis.ts +0 -97
- package/core/agentic/loop-detector/hallucination.ts +0 -71
- package/core/agentic/loop-detector/index.ts +0 -41
- package/core/agentic/loop-detector/loop-detector.ts +0 -222
- package/core/agentic/loop-detector/types.ts +0 -66
- package/core/agentic/memory-system/history.ts +0 -53
- package/core/agentic/memory-system/index.ts +0 -192
- package/core/agentic/memory-system/patterns.ts +0 -156
- package/core/agentic/memory-system/semantic-memories.ts +0 -278
- package/core/agentic/memory-system/session.ts +0 -21
- package/core/agentic/plan-mode/approval.ts +0 -57
- package/core/agentic/plan-mode/constants.ts +0 -44
- package/core/agentic/plan-mode/index.ts +0 -28
- package/core/agentic/plan-mode/plan-mode.ts +0 -407
- package/core/agentic/plan-mode/types.ts +0 -193
- package/core/agents/types.ts +0 -126
- package/core/command-registry/categories.ts +0 -23
- package/core/command-registry/commands.ts +0 -15
- package/core/command-registry/core-commands.ts +0 -344
- package/core/command-registry/index.ts +0 -158
- package/core/command-registry/optional-commands.ts +0 -163
- package/core/command-registry/setup-commands.ts +0 -83
- package/core/command-registry/types.ts +0 -59
- package/core/command-registry.ts +0 -9
- package/core/commands/types.ts +0 -185
- package/core/commands.ts +0 -11
- package/core/constants/formats.ts +0 -187
- package/core/context-sync.ts +0 -18
- package/core/data/index.ts +0 -27
- package/core/data/md-base-manager.ts +0 -203
- package/core/data/md-ideas-manager.ts +0 -155
- package/core/data/md-queue-manager.ts +0 -180
- package/core/data/md-shipped-manager.ts +0 -90
- package/core/data/md-state-manager.ts +0 -137
- package/core/domain/task-stack/index.ts +0 -19
- package/core/domain/task-stack/parser.ts +0 -86
- package/core/domain/task-stack/storage.ts +0 -123
- package/core/domain/task-stack/task-stack.ts +0 -340
- package/core/domain/task-stack/types.ts +0 -51
- package/core/infrastructure/command-installer/command-installer.ts +0 -327
- package/core/infrastructure/command-installer/global-config.ts +0 -136
- package/core/infrastructure/command-installer/index.ts +0 -25
- package/core/infrastructure/command-installer/types.ts +0 -41
- package/core/infrastructure/session-manager/index.ts +0 -23
- package/core/infrastructure/session-manager/types.ts +0 -45
- package/core/infrastructure/session-manager.ts +0 -8
- package/core/serializers/ideas-serializer.ts +0 -187
- package/core/serializers/index.ts +0 -36
- package/core/serializers/queue-serializer.ts +0 -210
- package/core/serializers/shipped-serializer.ts +0 -108
- package/core/serializers/state-serializer.ts +0 -136
- package/core/session/types.ts +0 -29
- /package/core/infrastructure/{agents/claude-agent.ts → claude-agent.ts} +0 -0
|
@@ -1,495 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maintenance Commands
|
|
3
|
-
*
|
|
2
|
+
* Maintenance Commands
|
|
3
|
+
*
|
|
4
|
+
* Composed from individual modules:
|
|
5
|
+
* - cleanup: Memory and project file cleanup
|
|
6
|
+
* - design: System architecture and component design
|
|
7
|
+
* - snapshots: Git-based undo/redo and session recovery
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
|
-
import
|
|
10
|
+
import type { CommandResult, CleanupOptions, DesignOptions } from '../types'
|
|
11
|
+
import { PrjctCommandsBase } from './base'
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
configManager,
|
|
13
|
-
fileHelper,
|
|
14
|
-
jsonlHelper,
|
|
15
|
-
dateHelper,
|
|
16
|
-
out
|
|
17
|
-
} from './base'
|
|
18
|
-
import { mdIdeasManager, mdQueueManager } from '../data'
|
|
13
|
+
// Import individual command functions
|
|
14
|
+
import { cleanup, cleanupMemory, cleanupMemoryInternal } from './cleanup'
|
|
15
|
+
import { design } from './design'
|
|
16
|
+
import { recover, undo, redo, history } from './snapshots'
|
|
19
17
|
|
|
18
|
+
/**
|
|
19
|
+
* MaintenanceCommands - Facade class for maintenance operations
|
|
20
|
+
*
|
|
21
|
+
* Delegates to individual modules for implementation.
|
|
22
|
+
*/
|
|
20
23
|
export class MaintenanceCommands extends PrjctCommandsBase {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
pathManager.getFilePath(projectId!, 'memory', 'context.jsonl'),
|
|
30
|
-
pathManager.getFilePath(projectId!, 'progress', 'shipped.md'),
|
|
31
|
-
pathManager.getFilePath(projectId!, 'planning', 'ideas.md'),
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
for (const filePath of jsonlFiles) {
|
|
35
|
-
try {
|
|
36
|
-
const sizeMB = await jsonlHelper.getFileSizeMB(filePath)
|
|
37
|
-
if (sizeMB > 0) {
|
|
38
|
-
results.totalSize += sizeMB
|
|
39
|
-
const rotated = await jsonlHelper.rotateJsonLinesIfNeeded(filePath, 10)
|
|
40
|
-
if (rotated) {
|
|
41
|
-
results.rotated.push(path.basename(filePath))
|
|
42
|
-
results.freedSpace += sizeMB
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// skip
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return { success: true, results }
|
|
24
|
+
// Cleanup operations
|
|
25
|
+
_cleanupMemory = cleanupMemory
|
|
26
|
+
_cleanupMemoryInternal = cleanupMemoryInternal
|
|
27
|
+
|
|
28
|
+
async cleanup(options: CleanupOptions = {}, projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
29
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
30
|
+
if (!initResult.success) return initResult
|
|
31
|
+
return cleanup(options, projectPath)
|
|
51
32
|
}
|
|
52
33
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
await
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* /p:cleanup - Clean temp files and old entries
|
|
64
|
-
*/
|
|
65
|
-
async cleanup(_options: CleanupOptions = {}, projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
66
|
-
try {
|
|
67
|
-
const initResult = await this.ensureProjectInit(projectPath)
|
|
68
|
-
if (!initResult.success) return initResult
|
|
69
|
-
|
|
70
|
-
const isMemoryMode = _options.memory === true || _options.type === 'memory'
|
|
71
|
-
|
|
72
|
-
if (isMemoryMode) {
|
|
73
|
-
out.spin('cleaning memory...')
|
|
74
|
-
const result = await this._cleanupMemory(projectPath)
|
|
75
|
-
out.done('memory cleaned')
|
|
76
|
-
return result
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
out.spin('cleaning up...')
|
|
80
|
-
|
|
81
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
82
|
-
if (!projectId) {
|
|
83
|
-
out.fail('no project ID')
|
|
84
|
-
return { success: false, error: 'No project ID found' }
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const cleaned: string[] = []
|
|
88
|
-
|
|
89
|
-
// Clean memory (keep last 100 entries)
|
|
90
|
-
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
91
|
-
try {
|
|
92
|
-
const entries = await jsonlHelper.readJsonLines(memoryPath)
|
|
93
|
-
|
|
94
|
-
if (entries.length > 100) {
|
|
95
|
-
const kept = entries.slice(-100)
|
|
96
|
-
await jsonlHelper.writeJsonLines(memoryPath, kept)
|
|
97
|
-
cleaned.push(`Memory: ${entries.length - 100} old entries removed`)
|
|
98
|
-
} else {
|
|
99
|
-
cleaned.push('Memory: No cleanup needed')
|
|
100
|
-
}
|
|
101
|
-
} catch {
|
|
102
|
-
cleaned.push('Memory: No file found')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Clean ideas using mdIdeasManager
|
|
106
|
-
try {
|
|
107
|
-
const result = await mdIdeasManager.cleanup(projectId)
|
|
108
|
-
if (result.removed > 0) {
|
|
109
|
-
cleaned.push(`Ideas: ${result.removed} old archived ideas removed`)
|
|
110
|
-
} else {
|
|
111
|
-
cleaned.push('Ideas: No cleanup needed')
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
cleaned.push('Ideas: No file found')
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Check queue for completed tasks using mdQueueManager
|
|
118
|
-
try {
|
|
119
|
-
const tasks = await mdQueueManager.getActiveTasks(projectId)
|
|
120
|
-
const completedTasks = tasks.filter(t => t.completed).length
|
|
121
|
-
|
|
122
|
-
if (completedTasks > 0) {
|
|
123
|
-
cleaned.push(
|
|
124
|
-
`Queue: ${completedTasks} completed tasks found (not removed - use /p:done to clear)`
|
|
125
|
-
)
|
|
126
|
-
} else {
|
|
127
|
-
cleaned.push('Queue: No completed tasks')
|
|
128
|
-
}
|
|
129
|
-
} catch {
|
|
130
|
-
cleaned.push('Queue: No file found')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
await this._cleanupMemoryInternal(projectPath)
|
|
134
|
-
|
|
135
|
-
await this.logToMemory(projectPath, 'cleanup_performed', {
|
|
136
|
-
items: cleaned.length,
|
|
137
|
-
timestamp: dateHelper.getTimestamp(),
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
out.done(`${cleaned.length} items cleaned`)
|
|
141
|
-
return { success: true, cleaned }
|
|
142
|
-
} catch (error) {
|
|
143
|
-
out.fail((error as Error).message)
|
|
144
|
-
return { success: false, error: (error as Error).message }
|
|
145
|
-
}
|
|
34
|
+
// Design operations
|
|
35
|
+
async design(
|
|
36
|
+
target: string | null = null,
|
|
37
|
+
options: DesignOptions = {},
|
|
38
|
+
projectPath: string = process.cwd()
|
|
39
|
+
): Promise<CommandResult> {
|
|
40
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
41
|
+
if (!initResult.success) return initResult
|
|
42
|
+
return design(target, options, projectPath)
|
|
146
43
|
}
|
|
147
44
|
|
|
148
|
-
|
|
149
|
-
* /p:design - Design system architecture, APIs, and components
|
|
150
|
-
*/
|
|
151
|
-
async design(target: string | null = null, options: DesignOptions = {}, projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
152
|
-
try {
|
|
153
|
-
const initResult = await this.ensureProjectInit(projectPath)
|
|
154
|
-
if (!initResult.success) return initResult
|
|
155
|
-
|
|
156
|
-
const designType = options.type || 'architecture'
|
|
157
|
-
const validTypes = ['architecture', 'api', 'component', 'database', 'flow']
|
|
158
|
-
|
|
159
|
-
if (!validTypes.includes(designType)) {
|
|
160
|
-
out.fail(`invalid type: ${designType}`)
|
|
161
|
-
return { success: false, error: 'Invalid design type' }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const designTarget = target || 'system'
|
|
165
|
-
out.spin(`designing ${designType}...`)
|
|
166
|
-
|
|
167
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
168
|
-
const designsPath = path.join(
|
|
169
|
-
pathManager.getGlobalProjectPath(projectId!),
|
|
170
|
-
'planning',
|
|
171
|
-
'designs'
|
|
172
|
-
)
|
|
173
|
-
await fileHelper.ensureDir(designsPath)
|
|
174
|
-
|
|
175
|
-
let designContent = ''
|
|
176
|
-
|
|
177
|
-
switch (designType) {
|
|
178
|
-
case 'architecture':
|
|
179
|
-
designContent = `# Architecture Design: ${designTarget}\n\n*Use templates/design/architecture.md for full design*\n`
|
|
180
|
-
break
|
|
181
|
-
case 'api':
|
|
182
|
-
designContent = `# API Design: ${designTarget}\n\n*Use templates/design/api.md for full design*\n`
|
|
183
|
-
break
|
|
184
|
-
case 'component':
|
|
185
|
-
designContent = `# Component Design: ${designTarget}\n\n*Use templates/design/component.md for full design*\n`
|
|
186
|
-
break
|
|
187
|
-
case 'database':
|
|
188
|
-
designContent = `# Database Design: ${designTarget}\n\n*Use templates/design/database.md for full design*\n`
|
|
189
|
-
break
|
|
190
|
-
case 'flow':
|
|
191
|
-
designContent = `# Flow Design: ${designTarget}\n\n*Use templates/design/flow.md for full design*\n`
|
|
192
|
-
break
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const designFileName = `${designType}-${designTarget.toLowerCase().replace(/\s+/g, '-')}.md`
|
|
196
|
-
const designFilePath = path.join(designsPath, designFileName)
|
|
197
|
-
await fileHelper.writeFile(designFilePath, designContent)
|
|
198
|
-
|
|
199
|
-
await this.logToMemory(projectPath, 'design_created', {
|
|
200
|
-
type: designType,
|
|
201
|
-
target: designTarget,
|
|
202
|
-
timestamp: dateHelper.getTimestamp(),
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
out.done(`${designType} design created`)
|
|
206
|
-
return { success: true, designPath: designFilePath, type: designType, target: designTarget }
|
|
207
|
-
} catch (error) {
|
|
208
|
-
out.fail((error as Error).message)
|
|
209
|
-
return { success: false, error: (error as Error).message }
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* /p:recover - Recover abandoned session with context restoration
|
|
215
|
-
*/
|
|
45
|
+
// Snapshot operations
|
|
216
46
|
async recover(projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
222
|
-
if (!projectId) {
|
|
223
|
-
out.fail('no project ID')
|
|
224
|
-
return { success: false, error: 'No project ID found' }
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
out.spin('checking for abandoned sessions...')
|
|
228
|
-
|
|
229
|
-
// Check for current session file
|
|
230
|
-
const sessionPath = pathManager.getFilePath(projectId, 'progress', 'sessions/current.json')
|
|
231
|
-
|
|
232
|
-
let sessionData: { task?: string; startedAt?: string; context?: string } | null = null
|
|
233
|
-
try {
|
|
234
|
-
const content = await fileHelper.readFile(sessionPath)
|
|
235
|
-
sessionData = JSON.parse(content)
|
|
236
|
-
} catch {
|
|
237
|
-
sessionData = null
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (!sessionData || !sessionData.task) {
|
|
241
|
-
out.warn('no abandoned session found')
|
|
242
|
-
return { success: true, message: 'No abandoned session found' }
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
console.log('\n🔍 Found abandoned session:\n')
|
|
246
|
-
console.log(` Task: ${sessionData.task}`)
|
|
247
|
-
if (sessionData.startedAt) {
|
|
248
|
-
const elapsed = dateHelper.calculateDuration(new Date(sessionData.startedAt))
|
|
249
|
-
console.log(` Started: ${elapsed} ago`)
|
|
250
|
-
}
|
|
251
|
-
if (sessionData.context) {
|
|
252
|
-
console.log(` Context: ${sessionData.context.slice(0, 100)}...`)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
console.log('\n💡 Options:')
|
|
256
|
-
console.log(' 1. Use /p:work to resume working')
|
|
257
|
-
console.log(' 2. Use /p:done to mark as complete')
|
|
258
|
-
console.log(' 3. Delete session file to discard\n')
|
|
259
|
-
|
|
260
|
-
return { success: true, session: sessionData }
|
|
261
|
-
} catch (error) {
|
|
262
|
-
out.fail((error as Error).message)
|
|
263
|
-
return { success: false, error: (error as Error).message }
|
|
264
|
-
}
|
|
47
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
48
|
+
if (!initResult.success) return initResult
|
|
49
|
+
return recover(projectPath)
|
|
265
50
|
}
|
|
266
51
|
|
|
267
|
-
/**
|
|
268
|
-
* /p:undo - Git-based undo (stash current changes)
|
|
269
|
-
*/
|
|
270
52
|
async undo(projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
out.spin('creating undo point...')
|
|
276
|
-
|
|
277
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
278
|
-
if (!projectId) {
|
|
279
|
-
out.fail('no project ID')
|
|
280
|
-
return { success: false, error: 'No project ID found' }
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Create snapshots directory
|
|
284
|
-
const snapshotsPath = path.join(
|
|
285
|
-
pathManager.getGlobalProjectPath(projectId),
|
|
286
|
-
'snapshots'
|
|
287
|
-
)
|
|
288
|
-
await fileHelper.ensureDir(snapshotsPath)
|
|
289
|
-
|
|
290
|
-
// Check git status
|
|
291
|
-
const { execSync } = await import('child_process')
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
const status = execSync('git status --porcelain', {
|
|
295
|
-
cwd: projectPath,
|
|
296
|
-
encoding: 'utf-8'
|
|
297
|
-
}).trim()
|
|
298
|
-
|
|
299
|
-
if (!status) {
|
|
300
|
-
out.warn('nothing to undo (no changes)')
|
|
301
|
-
return { success: true, message: 'No changes to undo' }
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Create stash with timestamp
|
|
305
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
306
|
-
const stashMessage = `prjct-undo-${timestamp}`
|
|
307
|
-
|
|
308
|
-
execSync(`git stash push -m "${stashMessage}"`, {
|
|
309
|
-
cwd: projectPath,
|
|
310
|
-
encoding: 'utf-8'
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
// Save snapshot metadata
|
|
314
|
-
const snapshotFile = path.join(snapshotsPath, 'history.json')
|
|
315
|
-
let history: { snapshots: { id: string; timestamp: string; message: string }[]; current: number } = { snapshots: [], current: -1 }
|
|
316
|
-
|
|
317
|
-
try {
|
|
318
|
-
const content = await fileHelper.readFile(snapshotFile)
|
|
319
|
-
history = JSON.parse(content)
|
|
320
|
-
} catch {
|
|
321
|
-
// New history
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
history.snapshots.push({
|
|
325
|
-
id: stashMessage,
|
|
326
|
-
timestamp: new Date().toISOString(),
|
|
327
|
-
message: stashMessage
|
|
328
|
-
})
|
|
329
|
-
history.current = history.snapshots.length - 1
|
|
330
|
-
|
|
331
|
-
await fileHelper.writeFile(snapshotFile, JSON.stringify(history, null, 2))
|
|
332
|
-
|
|
333
|
-
await this.logToMemory(projectPath, 'undo_performed', {
|
|
334
|
-
snapshotId: stashMessage,
|
|
335
|
-
timestamp: dateHelper.getTimestamp(),
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
out.done('changes stashed (use /p:redo to restore)')
|
|
339
|
-
return { success: true, snapshotId: stashMessage }
|
|
340
|
-
} catch (gitError) {
|
|
341
|
-
out.fail('git operation failed')
|
|
342
|
-
return { success: false, error: (gitError as Error).message }
|
|
343
|
-
}
|
|
344
|
-
} catch (error) {
|
|
345
|
-
out.fail((error as Error).message)
|
|
346
|
-
return { success: false, error: (error as Error).message }
|
|
347
|
-
}
|
|
53
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
54
|
+
if (!initResult.success) return initResult
|
|
55
|
+
return undo(projectPath)
|
|
348
56
|
}
|
|
349
57
|
|
|
350
|
-
/**
|
|
351
|
-
* /p:redo - Restore previously undone changes
|
|
352
|
-
*/
|
|
353
58
|
async redo(projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
out.spin('restoring changes...')
|
|
359
|
-
|
|
360
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
361
|
-
if (!projectId) {
|
|
362
|
-
out.fail('no project ID')
|
|
363
|
-
return { success: false, error: 'No project ID found' }
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const snapshotsPath = path.join(
|
|
367
|
-
pathManager.getGlobalProjectPath(projectId),
|
|
368
|
-
'snapshots'
|
|
369
|
-
)
|
|
370
|
-
const snapshotFile = path.join(snapshotsPath, 'history.json')
|
|
371
|
-
|
|
372
|
-
let history: { snapshots: { id: string; timestamp: string; message: string }[]; current: number }
|
|
373
|
-
|
|
374
|
-
try {
|
|
375
|
-
const content = await fileHelper.readFile(snapshotFile)
|
|
376
|
-
history = JSON.parse(content)
|
|
377
|
-
} catch {
|
|
378
|
-
out.warn('no undo history found')
|
|
379
|
-
return { success: false, message: 'No undo history found' }
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (history.snapshots.length === 0) {
|
|
383
|
-
out.warn('nothing to redo')
|
|
384
|
-
return { success: false, message: 'Nothing to redo' }
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const { execSync } = await import('child_process')
|
|
388
|
-
|
|
389
|
-
try {
|
|
390
|
-
// Get latest stash
|
|
391
|
-
const stashList = execSync('git stash list', {
|
|
392
|
-
cwd: projectPath,
|
|
393
|
-
encoding: 'utf-8'
|
|
394
|
-
}).trim()
|
|
395
|
-
|
|
396
|
-
if (!stashList) {
|
|
397
|
-
out.warn('no stashed changes')
|
|
398
|
-
return { success: false, message: 'No stashed changes found' }
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Find prjct stash
|
|
402
|
-
const prjctStash = stashList.split('\n').find(line => line.includes('prjct-undo-'))
|
|
403
|
-
|
|
404
|
-
if (!prjctStash) {
|
|
405
|
-
out.warn('no prjct undo point found')
|
|
406
|
-
return { success: false, message: 'No prjct undo point found' }
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Pop the stash
|
|
410
|
-
execSync('git stash pop', {
|
|
411
|
-
cwd: projectPath,
|
|
412
|
-
encoding: 'utf-8'
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
// Remove from history
|
|
416
|
-
history.snapshots.pop()
|
|
417
|
-
history.current = Math.max(0, history.current - 1)
|
|
418
|
-
|
|
419
|
-
await fileHelper.writeFile(snapshotFile, JSON.stringify(history, null, 2))
|
|
420
|
-
|
|
421
|
-
await this.logToMemory(projectPath, 'redo_performed', {
|
|
422
|
-
timestamp: dateHelper.getTimestamp(),
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
out.done('changes restored')
|
|
426
|
-
return { success: true }
|
|
427
|
-
} catch (gitError) {
|
|
428
|
-
out.fail('git operation failed')
|
|
429
|
-
return { success: false, error: (gitError as Error).message }
|
|
430
|
-
}
|
|
431
|
-
} catch (error) {
|
|
432
|
-
out.fail((error as Error).message)
|
|
433
|
-
return { success: false, error: (error as Error).message }
|
|
434
|
-
}
|
|
59
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
60
|
+
if (!initResult.success) return initResult
|
|
61
|
+
return redo(projectPath)
|
|
435
62
|
}
|
|
436
63
|
|
|
437
|
-
/**
|
|
438
|
-
* /p:history - Show snapshot history for undo/redo
|
|
439
|
-
*/
|
|
440
64
|
async history(projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const projectId = await configManager.getProjectId(projectPath)
|
|
446
|
-
if (!projectId) {
|
|
447
|
-
out.fail('no project ID')
|
|
448
|
-
return { success: false, error: 'No project ID found' }
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const snapshotsPath = path.join(
|
|
452
|
-
pathManager.getGlobalProjectPath(projectId),
|
|
453
|
-
'snapshots'
|
|
454
|
-
)
|
|
455
|
-
const snapshotFile = path.join(snapshotsPath, 'history.json')
|
|
456
|
-
|
|
457
|
-
let history: { snapshots: { id: string; timestamp: string; message: string }[]; current: number }
|
|
458
|
-
|
|
459
|
-
try {
|
|
460
|
-
const content = await fileHelper.readFile(snapshotFile)
|
|
461
|
-
history = JSON.parse(content)
|
|
462
|
-
} catch {
|
|
463
|
-
console.log('\n📜 SNAPSHOT HISTORY\n')
|
|
464
|
-
console.log('═'.repeat(50))
|
|
465
|
-
console.log(' No snapshots yet.')
|
|
466
|
-
console.log(' Use /p:undo to create a snapshot.\n')
|
|
467
|
-
return { success: true, snapshots: [] }
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
console.log('\n📜 SNAPSHOT HISTORY\n')
|
|
471
|
-
console.log('═'.repeat(50))
|
|
472
|
-
|
|
473
|
-
if (history.snapshots.length === 0) {
|
|
474
|
-
console.log(' No snapshots yet.')
|
|
475
|
-
console.log(' Use /p:undo to create a snapshot.\n')
|
|
476
|
-
} else {
|
|
477
|
-
history.snapshots.forEach((snap, i) => {
|
|
478
|
-
const marker = i === history.current ? '→' : ' '
|
|
479
|
-
const date = new Date(snap.timestamp).toLocaleString()
|
|
480
|
-
console.log(` ${marker} ${i + 1}. ${date}`)
|
|
481
|
-
})
|
|
482
|
-
console.log('')
|
|
483
|
-
console.log(` ${history.snapshots.length} snapshot(s) available`)
|
|
484
|
-
console.log(' Use /p:redo to restore the latest\n')
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
console.log('═'.repeat(50) + '\n')
|
|
488
|
-
|
|
489
|
-
return { success: true, snapshots: history.snapshots, current: history.current }
|
|
490
|
-
} catch (error) {
|
|
491
|
-
out.fail((error as Error).message)
|
|
492
|
-
return { success: false, error: (error as Error).message }
|
|
493
|
-
}
|
|
65
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
66
|
+
if (!initResult.success) return initResult
|
|
67
|
+
return history(projectPath)
|
|
494
68
|
}
|
|
495
69
|
}
|
|
70
|
+
|
|
71
|
+
// Re-export individual functions for direct use
|
|
72
|
+
export { cleanup, cleanupMemory, cleanupMemoryInternal } from './cleanup'
|
|
73
|
+
export { design } from './design'
|
|
74
|
+
export { recover, undo, redo, history } from './snapshots'
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import path from 'path'
|
|
7
7
|
|
|
8
|
-
import type { CommandResult,
|
|
8
|
+
import type { CommandResult, ProjectContext } from '../types'
|
|
9
9
|
import { generateUUID } from '../schemas'
|
|
10
10
|
import type { Priority, TaskType, TaskSection } from '../schemas/state'
|
|
11
11
|
import {
|
|
@@ -151,7 +151,7 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
151
151
|
|
|
152
152
|
out.spin(`planning ${description}...`)
|
|
153
153
|
|
|
154
|
-
const context = await contextBuilder.build(projectPath, { description }) as
|
|
154
|
+
const context = await contextBuilder.build(projectPath, { description }) as ProjectContext
|
|
155
155
|
const tasks = this._breakdownFeatureTasks(description)
|
|
156
156
|
const featureId = generateUUID()
|
|
157
157
|
|
|
@@ -217,7 +217,7 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
217
217
|
|
|
218
218
|
out.spin('tracking bug...')
|
|
219
219
|
|
|
220
|
-
const context = await contextBuilder.build(projectPath, { description }) as
|
|
220
|
+
const context = await contextBuilder.build(projectPath, { description }) as ProjectContext
|
|
221
221
|
const severity = this._detectBugSeverity(description)
|
|
222
222
|
|
|
223
223
|
const agentResult = await this._assignAgentForTask(`fix bug: ${description}`, projectPath, context)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Registration - Bridges existing command groups to the registry
|
|
3
|
+
*
|
|
4
|
+
* This module registers all commands from the existing command groups
|
|
5
|
+
* into the central CommandRegistry. This enables:
|
|
6
|
+
* - Uniform command execution via registry.execute()
|
|
7
|
+
* - Command introspection and metadata
|
|
8
|
+
* - Future migration to pure handler pattern
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { commandRegistry } from './registry'
|
|
12
|
+
import { COMMANDS, CATEGORIES } from './command-data'
|
|
13
|
+
import { WorkflowCommands } from './workflow'
|
|
14
|
+
import { PlanningCommands } from './planning'
|
|
15
|
+
import { ShippingCommands } from './shipping'
|
|
16
|
+
import { AnalyticsCommands } from './analytics'
|
|
17
|
+
import { MaintenanceCommands } from './maintenance'
|
|
18
|
+
import { AnalysisCommands } from './analysis'
|
|
19
|
+
import { SetupCommands } from './setup'
|
|
20
|
+
|
|
21
|
+
// Singleton instances of command groups
|
|
22
|
+
const workflow = new WorkflowCommands()
|
|
23
|
+
const planning = new PlanningCommands()
|
|
24
|
+
const shipping = new ShippingCommands()
|
|
25
|
+
const analytics = new AnalyticsCommands()
|
|
26
|
+
const maintenance = new MaintenanceCommands()
|
|
27
|
+
const analysis = new AnalysisCommands()
|
|
28
|
+
const setup = new SetupCommands()
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Register categories
|
|
32
|
+
*/
|
|
33
|
+
function registerCategories(): void {
|
|
34
|
+
for (const [name, info] of Object.entries(CATEGORIES)) {
|
|
35
|
+
commandRegistry.registerCategory(name, info)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Register all commands from existing command groups
|
|
41
|
+
*/
|
|
42
|
+
export function registerAllCommands(): void {
|
|
43
|
+
// Skip if already registered
|
|
44
|
+
if (commandRegistry.has('work')) return
|
|
45
|
+
|
|
46
|
+
// Register categories first
|
|
47
|
+
registerCategories()
|
|
48
|
+
|
|
49
|
+
// Helper to get metadata from COMMANDS
|
|
50
|
+
const getMeta = (name: string) => COMMANDS.find(c => c.name === name)
|
|
51
|
+
|
|
52
|
+
// Workflow commands
|
|
53
|
+
commandRegistry.registerMethod('work', workflow, 'now', getMeta('work'))
|
|
54
|
+
commandRegistry.registerMethod('now', workflow, 'now', getMeta('now'))
|
|
55
|
+
commandRegistry.registerMethod('done', workflow, 'done', getMeta('done'))
|
|
56
|
+
commandRegistry.registerMethod('next', workflow, 'next', getMeta('next'))
|
|
57
|
+
commandRegistry.registerMethod('pause', workflow, 'pause', getMeta('pause'))
|
|
58
|
+
commandRegistry.registerMethod('resume', workflow, 'resume', getMeta('resume'))
|
|
59
|
+
|
|
60
|
+
// Planning commands
|
|
61
|
+
commandRegistry.registerMethod('init', planning, 'init', getMeta('init'))
|
|
62
|
+
commandRegistry.registerMethod('feature', planning, 'feature', getMeta('feature'))
|
|
63
|
+
commandRegistry.registerMethod('bug', planning, 'bug', getMeta('bug'))
|
|
64
|
+
commandRegistry.registerMethod('idea', planning, 'idea', getMeta('idea'))
|
|
65
|
+
commandRegistry.registerMethod('spec', planning, 'spec', getMeta('spec'))
|
|
66
|
+
|
|
67
|
+
// Shipping commands
|
|
68
|
+
commandRegistry.registerMethod('ship', shipping, 'ship', getMeta('ship'))
|
|
69
|
+
|
|
70
|
+
// Analytics commands
|
|
71
|
+
commandRegistry.registerMethod('dash', analytics, 'dash', getMeta('dash'))
|
|
72
|
+
commandRegistry.registerMethod('help', analytics, 'help', getMeta('help'))
|
|
73
|
+
|
|
74
|
+
// Maintenance commands
|
|
75
|
+
commandRegistry.registerMethod('cleanup', maintenance, 'cleanup', getMeta('cleanup'))
|
|
76
|
+
commandRegistry.registerMethod('design', maintenance, 'design', getMeta('design'))
|
|
77
|
+
commandRegistry.registerMethod('recover', maintenance, 'recover', getMeta('recover'))
|
|
78
|
+
commandRegistry.registerMethod('undo', maintenance, 'undo', getMeta('undo'))
|
|
79
|
+
commandRegistry.registerMethod('redo', maintenance, 'redo', getMeta('redo'))
|
|
80
|
+
commandRegistry.registerMethod('history', maintenance, 'history', getMeta('history'))
|
|
81
|
+
|
|
82
|
+
// Analysis commands
|
|
83
|
+
commandRegistry.registerMethod('analyze', analysis, 'analyze', getMeta('analyze'))
|
|
84
|
+
commandRegistry.registerMethod('sync', analysis, 'sync', getMeta('sync'))
|
|
85
|
+
|
|
86
|
+
// Setup commands
|
|
87
|
+
commandRegistry.registerMethod('start', setup, 'start', getMeta('start'))
|
|
88
|
+
commandRegistry.registerMethod('setup', setup, 'setup', getMeta('setup'))
|
|
89
|
+
commandRegistry.registerMethod('migrateAll', setup, 'migrateAll', getMeta('migrate-all'))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Auto-register on import
|
|
93
|
+
registerAllCommands()
|
|
94
|
+
|
|
95
|
+
// Export command group instances for direct access (legacy support)
|
|
96
|
+
export {
|
|
97
|
+
workflow,
|
|
98
|
+
planning,
|
|
99
|
+
shipping,
|
|
100
|
+
analytics,
|
|
101
|
+
maintenance,
|
|
102
|
+
analysis,
|
|
103
|
+
setup
|
|
104
|
+
}
|