prjct-cli 0.15.1 → 0.18.0
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 +35 -0
- package/bin/dev.js +0 -1
- package/bin/serve.js +19 -20
- package/core/__tests__/agentic/memory-system.test.ts +2 -1
- package/core/__tests__/agentic/plan-mode.test.ts +2 -1
- package/core/agentic/agent-router.ts +79 -14
- package/core/agentic/command-executor/command-executor.ts +2 -74
- package/core/agentic/services.ts +0 -48
- package/core/agentic/template-loader.ts +35 -1
- package/core/command-registry/setup-commands.ts +15 -0
- package/core/commands/base.ts +96 -77
- package/core/commands/planning.ts +13 -2
- package/core/commands/setup.ts +3 -85
- package/core/domain/agent-generator.ts +9 -17
- package/core/errors.ts +209 -0
- package/core/infrastructure/config-manager.ts +22 -5
- package/core/infrastructure/path-manager.ts +23 -1
- package/core/infrastructure/setup.ts +5 -50
- package/core/storage/ideas-storage.ts +4 -0
- package/core/storage/queue-storage.ts +4 -0
- package/core/storage/shipped-storage.ts +4 -0
- package/core/storage/state-storage.ts +4 -0
- package/core/storage/storage-manager.ts +52 -13
- package/core/sync/auth-config.ts +145 -0
- package/core/sync/index.ts +30 -0
- package/core/sync/oauth-handler.ts +148 -0
- package/core/sync/sync-client.ts +252 -0
- package/core/sync/sync-manager.ts +358 -0
- package/core/utils/logger.ts +19 -12
- package/package.json +2 -4
- package/templates/agentic/subagent-generation.md +109 -0
- package/templates/commands/auth.md +234 -0
- package/templates/commands/sync.md +129 -13
- package/templates/subagents/domain/backend.md +105 -0
- package/templates/subagents/domain/database.md +118 -0
- package/templates/subagents/domain/devops.md +148 -0
- package/templates/subagents/domain/frontend.md +99 -0
- package/templates/subagents/domain/testing.md +169 -0
- package/templates/subagents/workflow/prjct-planner.md +158 -0
- package/templates/subagents/workflow/prjct-shipper.md +179 -0
- package/templates/subagents/workflow/prjct-workflow.md +98 -0
- package/bin/generate-views.js +0 -209
- package/bin/migrate-to-json.js +0 -742
- package/core/agentic/context-filter.ts +0 -365
- package/core/agentic/parallel-tools.ts +0 -165
- package/core/agentic/response-templates.ts +0 -164
- package/core/agentic/semantic-compression.ts +0 -273
- package/core/agentic/think-blocks.ts +0 -202
- package/core/agentic/validation-rules.ts +0 -313
- package/core/domain/agent-matcher.ts +0 -130
- package/core/domain/agent-validator.ts +0 -250
- package/core/domain/architect-session.ts +0 -315
- package/core/domain/product-standards.ts +0 -106
- package/core/domain/smart-cache.ts +0 -167
- package/core/domain/task-analyzer.ts +0 -296
- package/core/infrastructure/legacy-installer-detector/cleanup.ts +0 -216
- package/core/infrastructure/legacy-installer-detector/detection.ts +0 -95
- package/core/infrastructure/legacy-installer-detector/index.ts +0 -171
- package/core/infrastructure/legacy-installer-detector/migration.ts +0 -87
- package/core/infrastructure/legacy-installer-detector/types.ts +0 -42
- package/core/infrastructure/legacy-installer-detector.ts +0 -7
- package/core/infrastructure/migrator/file-operations.ts +0 -125
- package/core/infrastructure/migrator/index.ts +0 -288
- package/core/infrastructure/migrator/project-scanner.ts +0 -90
- package/core/infrastructure/migrator/reports.ts +0 -117
- package/core/infrastructure/migrator/types.ts +0 -124
- package/core/infrastructure/migrator/validation.ts +0 -94
- package/core/infrastructure/migrator/version-migration.ts +0 -117
- package/core/infrastructure/migrator.ts +0 -10
- package/core/infrastructure/uuid-migration.ts +0 -750
- package/templates/commands/migrate-all.md +0 -96
- package/templates/commands/migrate.md +0 -140
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migrator
|
|
3
|
-
* Handles migrations between prjct versions and structures.
|
|
4
|
-
*
|
|
5
|
-
* @module infrastructure/migrator
|
|
6
|
-
* @version 0.3.0
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'fs/promises'
|
|
10
|
-
import path from 'path'
|
|
11
|
-
import pathManager from '../path-manager'
|
|
12
|
-
import configManager from '../config-manager'
|
|
13
|
-
import authorDetector from '../author-detector'
|
|
14
|
-
|
|
15
|
-
import type {
|
|
16
|
-
Author,
|
|
17
|
-
MigrationResult,
|
|
18
|
-
MigrationOptions,
|
|
19
|
-
MigrationSummary,
|
|
20
|
-
MigrateAllOptions,
|
|
21
|
-
ProjectInfo,
|
|
22
|
-
LayerCounts
|
|
23
|
-
} from './types'
|
|
24
|
-
|
|
25
|
-
import { needsMigration, migrateConfigTo030 } from './version-migration'
|
|
26
|
-
import { migrateFiles } from './file-operations'
|
|
27
|
-
import { validateMigration, cleanupLegacyDirectories, checkStatus } from './validation'
|
|
28
|
-
import { findAllProjects } from './project-scanner'
|
|
29
|
-
import { generateReport, generateMigrationSummary } from './reports'
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Handles version migrations and project structure updates.
|
|
33
|
-
* Supports legacy -> global storage migration and config version upgrades.
|
|
34
|
-
*/
|
|
35
|
-
class Migrator {
|
|
36
|
-
// Re-export helper methods
|
|
37
|
-
needsMigration = needsMigration
|
|
38
|
-
migrateConfigTo030 = migrateConfigTo030
|
|
39
|
-
validateMigration = validateMigration
|
|
40
|
-
cleanupLegacyDirectories = cleanupLegacyDirectories
|
|
41
|
-
checkStatus = checkStatus
|
|
42
|
-
findAllProjects = findAllProjects
|
|
43
|
-
generateReport = generateReport
|
|
44
|
-
generateMigrationSummary = generateMigrationSummary
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Perform the complete migration process
|
|
48
|
-
*/
|
|
49
|
-
async migrate(projectPath: string, options: MigrationOptions = {}): Promise<MigrationResult> {
|
|
50
|
-
const result: MigrationResult = {
|
|
51
|
-
success: false,
|
|
52
|
-
projectId: null,
|
|
53
|
-
filesCopied: 0,
|
|
54
|
-
layerCounts: {
|
|
55
|
-
core: 0,
|
|
56
|
-
progress: 0,
|
|
57
|
-
planning: 0,
|
|
58
|
-
analysis: 0,
|
|
59
|
-
memory: 0,
|
|
60
|
-
other: 0,
|
|
61
|
-
},
|
|
62
|
-
config: null,
|
|
63
|
-
author: null,
|
|
64
|
-
issues: [],
|
|
65
|
-
dryRun: options.dryRun || false,
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const config = await configManager.readConfig(projectPath)
|
|
70
|
-
if (config && config.version && config.version.startsWith('0.2.')) {
|
|
71
|
-
const versionMigration = await migrateConfigTo030(projectPath)
|
|
72
|
-
result.success = versionMigration.success
|
|
73
|
-
result.projectId = config.projectId
|
|
74
|
-
result.filesCopied = 0
|
|
75
|
-
result.issues = versionMigration.success ? [] : [versionMigration.message]
|
|
76
|
-
return result
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const needsStructuralMigration = await configManager.needsMigration(projectPath)
|
|
80
|
-
if (!needsStructuralMigration) {
|
|
81
|
-
result.success = false
|
|
82
|
-
result.issues.push('No migration needed - either no legacy structure or already migrated')
|
|
83
|
-
return result
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const detectedAuthor = await authorDetector.detect()
|
|
87
|
-
result.author = {
|
|
88
|
-
name: detectedAuthor.name,
|
|
89
|
-
email: detectedAuthor.email,
|
|
90
|
-
github: detectedAuthor.github
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const projectId = pathManager.generateProjectId(projectPath)
|
|
94
|
-
result.projectId = projectId
|
|
95
|
-
|
|
96
|
-
if (options.dryRun) {
|
|
97
|
-
result.success = true
|
|
98
|
-
result.issues.push('DRY RUN - No changes were made')
|
|
99
|
-
return result
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
await pathManager.ensureProjectStructure(projectId)
|
|
103
|
-
|
|
104
|
-
// Convert null to undefined for createConfig
|
|
105
|
-
const authorForConfig = {
|
|
106
|
-
name: result.author.name || undefined,
|
|
107
|
-
email: result.author.email || undefined,
|
|
108
|
-
github: result.author.github || undefined
|
|
109
|
-
}
|
|
110
|
-
result.config = await configManager.createConfig(projectPath, authorForConfig)
|
|
111
|
-
|
|
112
|
-
const legacyPath = pathManager.getLegacyPrjctPath(projectPath)
|
|
113
|
-
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
114
|
-
|
|
115
|
-
const migrationStats = await migrateFiles(legacyPath, globalPath)
|
|
116
|
-
result.filesCopied = migrationStats.fileCount
|
|
117
|
-
result.layerCounts = migrationStats.layerCounts
|
|
118
|
-
|
|
119
|
-
const validation = await validateMigration(projectId)
|
|
120
|
-
result.issues = validation.issues
|
|
121
|
-
|
|
122
|
-
if (!validation.valid) {
|
|
123
|
-
result.success = false
|
|
124
|
-
return result
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (options.removeLegacy) {
|
|
128
|
-
await fs.rm(legacyPath, { recursive: true, force: true })
|
|
129
|
-
result.legacyRemoved = true
|
|
130
|
-
} else if (options.cleanupLegacy) {
|
|
131
|
-
await cleanupLegacyDirectories(projectPath)
|
|
132
|
-
result.legacyCleaned = true
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
result.success = true
|
|
136
|
-
return result
|
|
137
|
-
} catch (error) {
|
|
138
|
-
result.success = false
|
|
139
|
-
result.issues.push(`Migration error: ${(error as Error).message}`)
|
|
140
|
-
return result
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Migrate all projects with legacy .prjct directories
|
|
146
|
-
*/
|
|
147
|
-
async migrateAll(options: MigrateAllOptions = {}): Promise<MigrationSummary> {
|
|
148
|
-
const {
|
|
149
|
-
deepScan = false,
|
|
150
|
-
removeLegacy = false,
|
|
151
|
-
cleanupLegacy = false,
|
|
152
|
-
dryRun = false,
|
|
153
|
-
interactive = false,
|
|
154
|
-
onProgress = null,
|
|
155
|
-
} = options
|
|
156
|
-
|
|
157
|
-
const summary: MigrationSummary = {
|
|
158
|
-
success: false,
|
|
159
|
-
totalFound: 0,
|
|
160
|
-
alreadyMigrated: 0,
|
|
161
|
-
successfullyMigrated: 0,
|
|
162
|
-
failed: 0,
|
|
163
|
-
skipped: 0,
|
|
164
|
-
projects: [],
|
|
165
|
-
errors: [],
|
|
166
|
-
dryRun,
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
if (onProgress) onProgress({ phase: 'scanning', message: 'Searching for projects...' })
|
|
171
|
-
const projectPaths = await findAllProjects({ deepScan })
|
|
172
|
-
summary.totalFound = projectPaths.length
|
|
173
|
-
|
|
174
|
-
if (projectPaths.length === 0) {
|
|
175
|
-
summary.success = true
|
|
176
|
-
return summary
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (let i = 0; i < projectPaths.length; i++) {
|
|
180
|
-
const projectPath = projectPaths[i]
|
|
181
|
-
const projectName = path.basename(projectPath)
|
|
182
|
-
|
|
183
|
-
if (onProgress) {
|
|
184
|
-
onProgress({
|
|
185
|
-
phase: 'checking',
|
|
186
|
-
message: `Checking ${projectName} (${i + 1}/${projectPaths.length})`,
|
|
187
|
-
current: i + 1,
|
|
188
|
-
total: projectPaths.length,
|
|
189
|
-
})
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
const status = await checkStatus(projectPath)
|
|
194
|
-
|
|
195
|
-
const projectInfo: ProjectInfo = {
|
|
196
|
-
path: projectPath,
|
|
197
|
-
name: projectName,
|
|
198
|
-
status: status.status,
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (status.status === 'migrated' || status.status === 'new') {
|
|
202
|
-
projectInfo.result = 'skipped'
|
|
203
|
-
projectInfo.reason =
|
|
204
|
-
status.status === 'migrated' ? 'Already migrated' : 'Not initialized'
|
|
205
|
-
summary.alreadyMigrated++
|
|
206
|
-
} else if (status.needsMigration) {
|
|
207
|
-
if (interactive && onProgress) {
|
|
208
|
-
const shouldMigrate = await onProgress({
|
|
209
|
-
phase: 'confirm',
|
|
210
|
-
message: `Migrate ${projectName}?`,
|
|
211
|
-
projectPath,
|
|
212
|
-
})
|
|
213
|
-
if (!shouldMigrate) {
|
|
214
|
-
projectInfo.result = 'skipped'
|
|
215
|
-
projectInfo.reason = 'User skipped'
|
|
216
|
-
summary.skipped++
|
|
217
|
-
summary.projects.push(projectInfo)
|
|
218
|
-
continue
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (onProgress) {
|
|
223
|
-
onProgress({
|
|
224
|
-
phase: 'migrating',
|
|
225
|
-
message: `Migrating ${projectName}...`,
|
|
226
|
-
current: i + 1,
|
|
227
|
-
total: projectPaths.length,
|
|
228
|
-
})
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const migrationResult = await this.migrate(projectPath, {
|
|
232
|
-
removeLegacy,
|
|
233
|
-
cleanupLegacy,
|
|
234
|
-
dryRun,
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
projectInfo.projectId = migrationResult.projectId || undefined
|
|
238
|
-
projectInfo.filesCopied = migrationResult.filesCopied
|
|
239
|
-
projectInfo.layerCounts = migrationResult.layerCounts
|
|
240
|
-
|
|
241
|
-
if (migrationResult.success) {
|
|
242
|
-
projectInfo.result = 'success'
|
|
243
|
-
summary.successfullyMigrated++
|
|
244
|
-
} else {
|
|
245
|
-
projectInfo.result = 'failed'
|
|
246
|
-
projectInfo.errors = migrationResult.issues
|
|
247
|
-
summary.failed++
|
|
248
|
-
summary.errors.push({
|
|
249
|
-
project: projectName,
|
|
250
|
-
path: projectPath,
|
|
251
|
-
issues: migrationResult.issues,
|
|
252
|
-
})
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
summary.projects.push(projectInfo)
|
|
257
|
-
} catch (error) {
|
|
258
|
-
summary.failed++
|
|
259
|
-
summary.errors.push({
|
|
260
|
-
project: projectName,
|
|
261
|
-
path: projectPath,
|
|
262
|
-
issues: [(error as Error).message],
|
|
263
|
-
})
|
|
264
|
-
summary.projects.push({
|
|
265
|
-
path: projectPath,
|
|
266
|
-
name: projectName,
|
|
267
|
-
result: 'failed',
|
|
268
|
-
errors: [(error as Error).message],
|
|
269
|
-
})
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
summary.success = summary.failed === 0
|
|
274
|
-
return summary
|
|
275
|
-
} catch (error) {
|
|
276
|
-
summary.success = false
|
|
277
|
-
summary.errors.push({
|
|
278
|
-
project: 'global',
|
|
279
|
-
issues: [(error as Error).message],
|
|
280
|
-
})
|
|
281
|
-
return summary
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const migrator = new Migrator()
|
|
287
|
-
export default migrator
|
|
288
|
-
export * from './types'
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project Scanner
|
|
3
|
-
* Finds projects with .prjct directories for migration.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from 'fs/promises'
|
|
7
|
-
import { accessSync } from 'fs'
|
|
8
|
-
import path from 'path'
|
|
9
|
-
import os from 'os'
|
|
10
|
-
import type { FindProjectsOptions } from './types'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Find all projects with .prjct directories on the user's machine
|
|
14
|
-
*/
|
|
15
|
-
export async function findAllProjects(options: FindProjectsOptions = {}): Promise<string[]> {
|
|
16
|
-
const { deepScan = true } = options
|
|
17
|
-
const projectDirs: string[] = []
|
|
18
|
-
|
|
19
|
-
let searchPaths: string[] = []
|
|
20
|
-
if (deepScan) {
|
|
21
|
-
searchPaths = [os.homedir()]
|
|
22
|
-
} else {
|
|
23
|
-
const commonDirs = [
|
|
24
|
-
'Projects',
|
|
25
|
-
'Documents',
|
|
26
|
-
'Developer',
|
|
27
|
-
'Code',
|
|
28
|
-
'dev',
|
|
29
|
-
'workspace',
|
|
30
|
-
'repos',
|
|
31
|
-
'src',
|
|
32
|
-
'Apps',
|
|
33
|
-
]
|
|
34
|
-
searchPaths = commonDirs
|
|
35
|
-
.map((dir) => path.join(os.homedir(), dir))
|
|
36
|
-
.filter((dirPath) => {
|
|
37
|
-
try {
|
|
38
|
-
accessSync(dirPath)
|
|
39
|
-
return true
|
|
40
|
-
} catch {
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const shouldSkip = (dirName: string): boolean => {
|
|
47
|
-
const skipDirs = [
|
|
48
|
-
'node_modules',
|
|
49
|
-
'.git',
|
|
50
|
-
'.next',
|
|
51
|
-
'dist',
|
|
52
|
-
'build',
|
|
53
|
-
'.cache',
|
|
54
|
-
'coverage',
|
|
55
|
-
'.vscode',
|
|
56
|
-
'.idea',
|
|
57
|
-
'vendor',
|
|
58
|
-
'__pycache__',
|
|
59
|
-
]
|
|
60
|
-
return skipDirs.includes(dirName) || (dirName.startsWith('.') && dirName !== '.prjct')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const searchDirectory = async (dirPath: string, depth: number = 0): Promise<void> => {
|
|
64
|
-
if (depth > 10) return
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const entries = await fs.readdir(dirPath, { withFileTypes: true })
|
|
68
|
-
|
|
69
|
-
if (entries.some((entry) => entry.name === '.prjct' && entry.isDirectory())) {
|
|
70
|
-
projectDirs.push(dirPath)
|
|
71
|
-
return // Don't search subdirectories if we found a project
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (const entry of entries) {
|
|
75
|
-
if (entry.isDirectory() && !shouldSkip(entry.name)) {
|
|
76
|
-
const subPath = path.join(dirPath, entry.name)
|
|
77
|
-
await searchDirectory(subPath, depth + 1)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} catch {
|
|
81
|
-
// Ignore errors
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
for (const searchPath of searchPaths) {
|
|
86
|
-
await searchDirectory(searchPath)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return projectDirs
|
|
90
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reports
|
|
3
|
-
* Generates migration reports.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import pathManager from '../path-manager'
|
|
7
|
-
import authorDetector from '../author-detector'
|
|
8
|
-
import type { MigrationResult, MigrationSummary } from './types'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generate a migration report
|
|
12
|
-
*/
|
|
13
|
-
export function generateReport(result: MigrationResult): string {
|
|
14
|
-
const lines: string[] = []
|
|
15
|
-
|
|
16
|
-
lines.push('Migration Report')
|
|
17
|
-
lines.push('---')
|
|
18
|
-
|
|
19
|
-
if (result.dryRun) {
|
|
20
|
-
lines.push('DRY RUN MODE - No changes were made')
|
|
21
|
-
lines.push('')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (result.success) {
|
|
25
|
-
lines.push('Migration successful!')
|
|
26
|
-
lines.push('')
|
|
27
|
-
lines.push(`Project ID: ${result.projectId}`)
|
|
28
|
-
lines.push(`Author: ${authorDetector.formatAuthor(result.author!)}`)
|
|
29
|
-
lines.push(`Files migrated: ${result.filesCopied}`)
|
|
30
|
-
lines.push('')
|
|
31
|
-
lines.push('Files by layer:')
|
|
32
|
-
for (const [layer, count] of Object.entries(result.layerCounts)) {
|
|
33
|
-
if (count > 0) {
|
|
34
|
-
lines.push(` - ${layer}: ${count} files`)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
lines.push('')
|
|
38
|
-
lines.push(`Data location: ${result.config?.dataPath}`)
|
|
39
|
-
|
|
40
|
-
if (result.legacyRemoved) {
|
|
41
|
-
lines.push('')
|
|
42
|
-
lines.push('Legacy .prjct directory removed')
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
lines.push('Migration failed!')
|
|
46
|
-
lines.push('')
|
|
47
|
-
if (result.issues.length > 0) {
|
|
48
|
-
lines.push('Issues:')
|
|
49
|
-
for (const issue of result.issues) {
|
|
50
|
-
lines.push(` - ${issue}`)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
lines.push('---')
|
|
56
|
-
|
|
57
|
-
return lines.join('\n')
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Generate a summary report for migrateAll results
|
|
62
|
-
*/
|
|
63
|
-
export function generateMigrationSummary(summary: MigrationSummary): string {
|
|
64
|
-
const lines: string[] = []
|
|
65
|
-
|
|
66
|
-
lines.push('Global Migration Report')
|
|
67
|
-
lines.push('---')
|
|
68
|
-
|
|
69
|
-
if (summary.dryRun) {
|
|
70
|
-
lines.push('DRY RUN MODE - No changes were made')
|
|
71
|
-
lines.push('')
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
lines.push(`Found: ${summary.totalFound} projects`)
|
|
75
|
-
lines.push(`Successfully migrated: ${summary.successfullyMigrated}`)
|
|
76
|
-
lines.push(`Already migrated: ${summary.alreadyMigrated}`)
|
|
77
|
-
if (summary.skipped > 0) {
|
|
78
|
-
lines.push(`Skipped: ${summary.skipped}`)
|
|
79
|
-
}
|
|
80
|
-
if (summary.failed > 0) {
|
|
81
|
-
lines.push(`Failed: ${summary.failed}`)
|
|
82
|
-
}
|
|
83
|
-
lines.push('')
|
|
84
|
-
|
|
85
|
-
if (summary.successfullyMigrated > 0) {
|
|
86
|
-
lines.push('Successfully Migrated:')
|
|
87
|
-
summary.projects
|
|
88
|
-
.filter((p) => p.result === 'success')
|
|
89
|
-
.forEach((project) => {
|
|
90
|
-
lines.push(` - ${project.name}`)
|
|
91
|
-
lines.push(` Files: ${project.filesCopied} | ID: ${project.projectId}`)
|
|
92
|
-
})
|
|
93
|
-
lines.push('')
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (summary.errors.length > 0) {
|
|
97
|
-
lines.push('Errors:')
|
|
98
|
-
summary.errors.forEach((error) => {
|
|
99
|
-
lines.push(` - ${error.project}`)
|
|
100
|
-
error.issues.forEach((issue) => lines.push(` - ${issue}`))
|
|
101
|
-
})
|
|
102
|
-
lines.push('')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (summary.success && summary.successfullyMigrated > 0) {
|
|
106
|
-
lines.push('All projects migrated successfully!')
|
|
107
|
-
lines.push(`Global data location: ${pathManager.getGlobalBasePath()}`)
|
|
108
|
-
} else if (summary.totalFound === 0) {
|
|
109
|
-
lines.push('No legacy projects found')
|
|
110
|
-
} else if (summary.alreadyMigrated === summary.totalFound) {
|
|
111
|
-
lines.push('All projects already migrated')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
lines.push('---')
|
|
115
|
-
|
|
116
|
-
return lines.join('\n')
|
|
117
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migrator Types
|
|
3
|
-
* Type definitions for migration operations.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface Author {
|
|
7
|
-
name: string | null
|
|
8
|
-
email: string | null
|
|
9
|
-
github?: string | null
|
|
10
|
-
firstContribution?: string
|
|
11
|
-
lastActivity?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface LocalConfig {
|
|
15
|
-
projectId: string
|
|
16
|
-
dataPath: string
|
|
17
|
-
authors?: Author[]
|
|
18
|
-
author?: Author
|
|
19
|
-
version?: string
|
|
20
|
-
created?: string
|
|
21
|
-
lastSync?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface LayerCounts {
|
|
25
|
-
core: number
|
|
26
|
-
progress: number
|
|
27
|
-
planning: number
|
|
28
|
-
analysis: number
|
|
29
|
-
memory: number
|
|
30
|
-
other: number
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface MigrationResult {
|
|
34
|
-
success: boolean
|
|
35
|
-
projectId: string | null
|
|
36
|
-
filesCopied?: number
|
|
37
|
-
filescopied?: number
|
|
38
|
-
layerCounts: LayerCounts
|
|
39
|
-
config: LocalConfig | null
|
|
40
|
-
author: Author | null
|
|
41
|
-
issues: string[]
|
|
42
|
-
dryRun: boolean
|
|
43
|
-
legacyRemoved?: boolean
|
|
44
|
-
legacyCleaned?: boolean
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface VersionMigrationResult {
|
|
48
|
-
success: boolean
|
|
49
|
-
message: string
|
|
50
|
-
oldVersion: string | null
|
|
51
|
-
newVersion: string
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ValidationResult {
|
|
55
|
-
valid: boolean
|
|
56
|
-
issues: string[]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface FileMapping {
|
|
60
|
-
layer: string
|
|
61
|
-
filename: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface MigrationStats {
|
|
65
|
-
fileCount: number
|
|
66
|
-
layerCounts: LayerCounts
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface MigrationOptions {
|
|
70
|
-
removeLegacy?: boolean
|
|
71
|
-
cleanupLegacy?: boolean
|
|
72
|
-
dryRun?: boolean
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface StatusResult {
|
|
76
|
-
status: string
|
|
77
|
-
hasLegacy: boolean
|
|
78
|
-
hasConfig: boolean
|
|
79
|
-
needsMigration: boolean
|
|
80
|
-
version: string
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface ProjectInfo {
|
|
84
|
-
path: string
|
|
85
|
-
name: string
|
|
86
|
-
status?: string
|
|
87
|
-
result?: string
|
|
88
|
-
reason?: string
|
|
89
|
-
projectId?: string
|
|
90
|
-
filesCopied?: number
|
|
91
|
-
layerCounts?: LayerCounts
|
|
92
|
-
errors?: string[]
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface MigrationSummary {
|
|
96
|
-
success: boolean
|
|
97
|
-
totalFound: number
|
|
98
|
-
alreadyMigrated: number
|
|
99
|
-
successfullyMigrated: number
|
|
100
|
-
failed: number
|
|
101
|
-
skipped: number
|
|
102
|
-
projects: ProjectInfo[]
|
|
103
|
-
errors: Array<{ project: string; path?: string; issues: string[] }>
|
|
104
|
-
dryRun: boolean
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export interface MigrateAllOptions {
|
|
108
|
-
deepScan?: boolean
|
|
109
|
-
removeLegacy?: boolean
|
|
110
|
-
cleanupLegacy?: boolean
|
|
111
|
-
dryRun?: boolean
|
|
112
|
-
interactive?: boolean
|
|
113
|
-
onProgress?: (progress: {
|
|
114
|
-
phase: string
|
|
115
|
-
message: string
|
|
116
|
-
current?: number
|
|
117
|
-
total?: number
|
|
118
|
-
projectPath?: string
|
|
119
|
-
}) => boolean | void | Promise<boolean | void>
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface FindProjectsOptions {
|
|
123
|
-
deepScan?: boolean
|
|
124
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation
|
|
3
|
-
* Handles migration validation and legacy cleanup.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from 'fs/promises'
|
|
7
|
-
import path from 'path'
|
|
8
|
-
import pathManager from '../path-manager'
|
|
9
|
-
import type { ValidationResult, StatusResult } from './types'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Validate that migration was successful
|
|
13
|
-
*/
|
|
14
|
-
export async function validateMigration(projectId: string): Promise<ValidationResult> {
|
|
15
|
-
const issues: string[] = []
|
|
16
|
-
|
|
17
|
-
const exists = await pathManager.projectExists(projectId)
|
|
18
|
-
if (!exists) {
|
|
19
|
-
issues.push('Global project directory not found')
|
|
20
|
-
return { valid: false, issues }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
24
|
-
const requiredLayers = ['core', 'progress', 'planning', 'analysis', 'memory']
|
|
25
|
-
|
|
26
|
-
for (const layer of requiredLayers) {
|
|
27
|
-
try {
|
|
28
|
-
await fs.access(path.join(globalPath, layer))
|
|
29
|
-
} catch {
|
|
30
|
-
issues.push(`Missing layer directory: ${layer}`)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const coreFiles = await fs.readdir(path.join(globalPath, 'core'))
|
|
36
|
-
if (coreFiles.length === 0) {
|
|
37
|
-
issues.push('No files found in core directory')
|
|
38
|
-
}
|
|
39
|
-
} catch {
|
|
40
|
-
issues.push('Cannot read core directory')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
valid: issues.length === 0,
|
|
45
|
-
issues,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Cleanup legacy directories while preserving config
|
|
51
|
-
* Removes: analysis/, core/, memory/, planning/, progress/, sessions/
|
|
52
|
-
* Keeps: prjct.config.json
|
|
53
|
-
*/
|
|
54
|
-
export async function cleanupLegacyDirectories(projectPath: string): Promise<void> {
|
|
55
|
-
const legacyPath = pathManager.getLegacyPrjctPath(projectPath)
|
|
56
|
-
const layersToRemove = ['analysis', 'core', 'memory', 'planning', 'progress', 'sessions']
|
|
57
|
-
|
|
58
|
-
for (const layer of layersToRemove) {
|
|
59
|
-
const layerPath = path.join(legacyPath, layer)
|
|
60
|
-
try {
|
|
61
|
-
await fs.rm(layerPath, { recursive: true, force: true })
|
|
62
|
-
} catch {
|
|
63
|
-
// Ignore errors
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Check migration status for a project
|
|
70
|
-
*/
|
|
71
|
-
export async function checkStatus(projectPath: string): Promise<StatusResult> {
|
|
72
|
-
const hasLegacy = await pathManager.hasLegacyStructure(projectPath)
|
|
73
|
-
const hasConfig = await pathManager.hasConfig(projectPath)
|
|
74
|
-
const needsMigration = hasLegacy && !hasConfig
|
|
75
|
-
|
|
76
|
-
let status = 'unknown'
|
|
77
|
-
if (!hasLegacy && !hasConfig) {
|
|
78
|
-
status = 'new' // New project, not initialized
|
|
79
|
-
} else if (!hasLegacy && hasConfig) {
|
|
80
|
-
status = 'migrated' // Already migrated to v0.2.0
|
|
81
|
-
} else if (hasLegacy && !hasConfig) {
|
|
82
|
-
status = 'legacy' // v0.1.0, needs migration
|
|
83
|
-
} else if (hasLegacy && hasConfig) {
|
|
84
|
-
status = 'both' // Has both (migration incomplete or manual setup)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
status,
|
|
89
|
-
hasLegacy,
|
|
90
|
-
hasConfig,
|
|
91
|
-
needsMigration,
|
|
92
|
-
version: hasConfig ? '0.2.0' : hasLegacy ? '0.1.0' : 'none',
|
|
93
|
-
}
|
|
94
|
-
}
|