prjct-cli 0.9.2 → 0.10.2
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 +142 -0
- package/core/__tests__/agentic/memory-system.test.js +263 -0
- package/core/__tests__/agentic/plan-mode.test.js +336 -0
- package/core/agentic/agent-router.js +253 -186
- package/core/agentic/chain-of-thought.js +578 -0
- package/core/agentic/command-executor.js +299 -17
- package/core/agentic/context-builder.js +208 -8
- package/core/agentic/context-filter.js +83 -83
- package/core/agentic/ground-truth.js +591 -0
- package/core/agentic/loop-detector.js +406 -0
- package/core/agentic/memory-system.js +850 -0
- package/core/agentic/parallel-tools.js +366 -0
- package/core/agentic/plan-mode.js +572 -0
- package/core/agentic/prompt-builder.js +127 -2
- package/core/agentic/response-templates.js +290 -0
- package/core/agentic/semantic-compression.js +517 -0
- package/core/agentic/think-blocks.js +657 -0
- package/core/agentic/tool-registry.js +32 -0
- package/core/agentic/validation-rules.js +380 -0
- package/core/command-registry.js +48 -0
- package/core/commands.js +128 -60
- package/core/context-sync.js +183 -0
- package/core/domain/agent-generator.js +77 -46
- package/core/domain/agent-loader.js +183 -0
- package/core/domain/agent-matcher.js +217 -0
- package/core/domain/agent-validator.js +217 -0
- package/core/domain/context-estimator.js +175 -0
- package/core/domain/product-standards.js +92 -0
- package/core/domain/smart-cache.js +157 -0
- package/core/domain/task-analyzer.js +353 -0
- package/core/domain/tech-detector.js +365 -0
- package/package.json +8 -16
- package/templates/commands/done.md +7 -0
- package/templates/commands/feature.md +8 -0
- package/templates/commands/ship.md +8 -0
- package/templates/commands/spec.md +128 -0
- package/templates/global/CLAUDE.md +17 -0
- package/core/__tests__/agentic/agent-router.test.js +0 -398
- package/core/__tests__/agentic/command-executor.test.js +0 -223
- package/core/__tests__/agentic/context-builder.test.js +0 -160
- package/core/__tests__/agentic/context-filter.test.js +0 -494
- package/core/__tests__/agentic/prompt-builder.test.js +0 -212
- package/core/__tests__/agentic/template-loader.test.js +0 -164
- package/core/__tests__/agentic/tool-registry.test.js +0 -243
- package/core/__tests__/domain/agent-generator.test.js +0 -296
- package/core/__tests__/domain/analyzer.test.js +0 -324
- package/core/__tests__/infrastructure/author-detector.test.js +0 -103
- package/core/__tests__/infrastructure/config-manager.test.js +0 -454
- package/core/__tests__/infrastructure/path-manager.test.js +0 -412
- package/core/__tests__/setup.test.js +0 -15
- package/core/__tests__/utils/date-helper.test.js +0 -169
- package/core/__tests__/utils/file-helper.test.js +0 -258
- package/core/__tests__/utils/jsonl-helper.test.js +0 -387
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
-
import { createRequire } from 'module'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import fs from 'fs/promises'
|
|
5
|
-
import os from 'os'
|
|
6
|
-
|
|
7
|
-
const require = createRequire(import.meta.url)
|
|
8
|
-
|
|
9
|
-
describe('Config Manager', () => {
|
|
10
|
-
let configManager
|
|
11
|
-
let pathManager
|
|
12
|
-
let testProjectPath
|
|
13
|
-
let tempDir
|
|
14
|
-
|
|
15
|
-
beforeEach(async () => {
|
|
16
|
-
configManager = require('../../infrastructure/config-manager.js')
|
|
17
|
-
pathManager = require('../../infrastructure/path-manager.js')
|
|
18
|
-
|
|
19
|
-
// Create temporary test directory
|
|
20
|
-
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'prjct-test-'))
|
|
21
|
-
testProjectPath = tempDir
|
|
22
|
-
|
|
23
|
-
// Create .prjct directory
|
|
24
|
-
await fs.mkdir(path.join(testProjectPath, '.prjct'), { recursive: true })
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
afterEach(async () => {
|
|
28
|
-
// Cleanup temp directory
|
|
29
|
-
if (tempDir) {
|
|
30
|
-
try {
|
|
31
|
-
await fs.rm(tempDir, { recursive: true, force: true })
|
|
32
|
-
} catch (error) {
|
|
33
|
-
// Ignore cleanup errors
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe('readConfig()', () => {
|
|
39
|
-
it('should read existing config', async () => {
|
|
40
|
-
const config = {
|
|
41
|
-
projectId: 'test-id-123',
|
|
42
|
-
dataPath: '~/.prjct-cli/projects/test-id-123'
|
|
43
|
-
}
|
|
44
|
-
const configPath = pathManager.getLocalConfigPath(testProjectPath)
|
|
45
|
-
await fs.writeFile(configPath, JSON.stringify(config, null, 2))
|
|
46
|
-
|
|
47
|
-
const result = await configManager.readConfig(testProjectPath)
|
|
48
|
-
|
|
49
|
-
expect(result).toBeDefined()
|
|
50
|
-
expect(result.projectId).toBe('test-id-123')
|
|
51
|
-
expect(result.dataPath).toBeDefined()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should return null for non-existent config', async () => {
|
|
55
|
-
const result = await configManager.readConfig(testProjectPath)
|
|
56
|
-
|
|
57
|
-
expect(result).toBeNull()
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should return null for invalid JSON', async () => {
|
|
61
|
-
const configPath = pathManager.getLocalConfigPath(testProjectPath)
|
|
62
|
-
await fs.writeFile(configPath, 'invalid json{')
|
|
63
|
-
|
|
64
|
-
const result = await configManager.readConfig(testProjectPath)
|
|
65
|
-
|
|
66
|
-
expect(result).toBeNull()
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
describe('writeConfig()', () => {
|
|
71
|
-
it('should write config file', async () => {
|
|
72
|
-
const config = {
|
|
73
|
-
projectId: 'test-id-456',
|
|
74
|
-
dataPath: '~/.prjct-cli/projects/test-id-456'
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
await configManager.writeConfig(testProjectPath, config)
|
|
78
|
-
|
|
79
|
-
const configPath = pathManager.getLocalConfigPath(testProjectPath)
|
|
80
|
-
const content = await fs.readFile(configPath, 'utf-8')
|
|
81
|
-
const parsed = JSON.parse(content)
|
|
82
|
-
|
|
83
|
-
expect(parsed.projectId).toBe('test-id-456')
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('should create .prjct directory if it does not exist', async () => {
|
|
87
|
-
const newPath = path.join(tempDir, 'new-project')
|
|
88
|
-
const config = { projectId: 'test', dataPath: '~/.prjct-cli/projects/test' }
|
|
89
|
-
|
|
90
|
-
await configManager.writeConfig(newPath, config)
|
|
91
|
-
|
|
92
|
-
const configPath = pathManager.getLocalConfigPath(newPath)
|
|
93
|
-
const exists = await fs.access(configPath).then(() => true).catch(() => false)
|
|
94
|
-
|
|
95
|
-
expect(exists).toBe(true)
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
describe('readGlobalConfig()', () => {
|
|
100
|
-
it('should read global config', async () => {
|
|
101
|
-
const projectId = 'test-global-123'
|
|
102
|
-
const globalConfig = {
|
|
103
|
-
projectId,
|
|
104
|
-
authors: [],
|
|
105
|
-
version: '0.9.1',
|
|
106
|
-
lastSync: new Date().toISOString()
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
await configManager.writeGlobalConfig(projectId, globalConfig)
|
|
110
|
-
const result = await configManager.readGlobalConfig(projectId)
|
|
111
|
-
|
|
112
|
-
expect(result).toBeDefined()
|
|
113
|
-
expect(result.projectId).toBe(projectId)
|
|
114
|
-
expect(result.authors).toBeDefined()
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it('should return null for non-existent global config', async () => {
|
|
118
|
-
const result = await configManager.readGlobalConfig('non-existent-id')
|
|
119
|
-
|
|
120
|
-
expect(result).toBeNull()
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
describe('writeGlobalConfig()', () => {
|
|
125
|
-
it('should write global config file', async () => {
|
|
126
|
-
const projectId = 'test-global-456'
|
|
127
|
-
const globalConfig = {
|
|
128
|
-
projectId,
|
|
129
|
-
authors: [],
|
|
130
|
-
version: '0.9.1'
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
await configManager.writeGlobalConfig(projectId, globalConfig)
|
|
134
|
-
|
|
135
|
-
const result = await configManager.readGlobalConfig(projectId)
|
|
136
|
-
expect(result.projectId).toBe(projectId)
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('should create directory structure if needed', async () => {
|
|
140
|
-
const projectId = 'test-new-global'
|
|
141
|
-
const globalConfig = { projectId, authors: [] }
|
|
142
|
-
|
|
143
|
-
await configManager.writeGlobalConfig(projectId, globalConfig)
|
|
144
|
-
|
|
145
|
-
const result = await configManager.readGlobalConfig(projectId)
|
|
146
|
-
expect(result).toBeDefined()
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
describe('ensureGlobalConfig()', () => {
|
|
151
|
-
it('should return existing global config', async () => {
|
|
152
|
-
const projectId = 'test-ensure-123'
|
|
153
|
-
const existingConfig = {
|
|
154
|
-
projectId,
|
|
155
|
-
authors: [],
|
|
156
|
-
version: '0.9.1'
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
await configManager.writeGlobalConfig(projectId, existingConfig)
|
|
160
|
-
const result = await configManager.ensureGlobalConfig(projectId)
|
|
161
|
-
|
|
162
|
-
expect(result.projectId).toBe(projectId)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('should create new global config if not exists', async () => {
|
|
166
|
-
const projectId = 'test-ensure-new'
|
|
167
|
-
|
|
168
|
-
const result = await configManager.ensureGlobalConfig(projectId)
|
|
169
|
-
|
|
170
|
-
expect(result).toBeDefined()
|
|
171
|
-
expect(result.projectId).toBe(projectId)
|
|
172
|
-
expect(result.authors).toEqual([])
|
|
173
|
-
expect(result.version).toBeDefined()
|
|
174
|
-
expect(result.lastSync).toBeDefined()
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
describe('createConfig()', () => {
|
|
179
|
-
it('should create both local and global config', async () => {
|
|
180
|
-
const author = {
|
|
181
|
-
name: 'Test User',
|
|
182
|
-
email: 'test@example.com',
|
|
183
|
-
github: 'testuser'
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const localConfig = await configManager.createConfig(testProjectPath, author)
|
|
187
|
-
|
|
188
|
-
expect(localConfig).toBeDefined()
|
|
189
|
-
expect(localConfig.projectId).toBeDefined()
|
|
190
|
-
expect(localConfig.dataPath).toBeDefined()
|
|
191
|
-
|
|
192
|
-
// Verify local config was written
|
|
193
|
-
const readLocal = await configManager.readConfig(testProjectPath)
|
|
194
|
-
expect(readLocal.projectId).toBe(localConfig.projectId)
|
|
195
|
-
|
|
196
|
-
// Verify global config was written
|
|
197
|
-
const globalConfig = await configManager.readGlobalConfig(localConfig.projectId)
|
|
198
|
-
expect(globalConfig).toBeDefined()
|
|
199
|
-
expect(globalConfig.authors.length).toBe(1)
|
|
200
|
-
expect(globalConfig.authors[0].github).toBe('testuser')
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
it('should handle missing author fields', async () => {
|
|
204
|
-
const author = {}
|
|
205
|
-
|
|
206
|
-
const localConfig = await configManager.createConfig(testProjectPath, author)
|
|
207
|
-
|
|
208
|
-
expect(localConfig).toBeDefined()
|
|
209
|
-
const globalConfig = await configManager.readGlobalConfig(localConfig.projectId)
|
|
210
|
-
expect(globalConfig.authors[0].name).toBe('Unknown')
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
describe('validateConfig()', () => {
|
|
215
|
-
it('should validate correct config', () => {
|
|
216
|
-
const config = {
|
|
217
|
-
projectId: 'test-123',
|
|
218
|
-
dataPath: '~/.prjct-cli/projects/test-123'
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
expect(configManager.validateConfig(config)).toBe(true)
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
it('should reject config without projectId', () => {
|
|
225
|
-
const config = { dataPath: '~/.prjct-cli/projects/test' }
|
|
226
|
-
|
|
227
|
-
expect(configManager.validateConfig(config)).toBe(false)
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('should reject config without dataPath', () => {
|
|
231
|
-
const config = { projectId: 'test-123' }
|
|
232
|
-
|
|
233
|
-
expect(configManager.validateConfig(config)).toBe(false)
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
it('should reject null config', () => {
|
|
237
|
-
expect(configManager.validateConfig(null)).toBe(false)
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('should reject undefined config', () => {
|
|
241
|
-
expect(configManager.validateConfig(undefined)).toBe(false)
|
|
242
|
-
})
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
describe('getProjectId()', () => {
|
|
246
|
-
it('should return projectId from config', async () => {
|
|
247
|
-
const config = {
|
|
248
|
-
projectId: 'test-from-config',
|
|
249
|
-
dataPath: '~/.prjct-cli/projects/test-from-config'
|
|
250
|
-
}
|
|
251
|
-
await configManager.writeConfig(testProjectPath, config)
|
|
252
|
-
|
|
253
|
-
const projectId = await configManager.getProjectId(testProjectPath)
|
|
254
|
-
|
|
255
|
-
expect(projectId).toBe('test-from-config')
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
it('should generate projectId if config does not exist', async () => {
|
|
259
|
-
const projectId = await configManager.getProjectId(testProjectPath)
|
|
260
|
-
|
|
261
|
-
expect(projectId).toBeDefined()
|
|
262
|
-
expect(typeof projectId).toBe('string')
|
|
263
|
-
expect(projectId.length).toBeGreaterThan(0)
|
|
264
|
-
})
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
describe('isConfigured()', () => {
|
|
268
|
-
it('should return true for valid config', async () => {
|
|
269
|
-
const config = {
|
|
270
|
-
projectId: 'test-123',
|
|
271
|
-
dataPath: '~/.prjct-cli/projects/test-123'
|
|
272
|
-
}
|
|
273
|
-
await configManager.writeConfig(testProjectPath, config)
|
|
274
|
-
|
|
275
|
-
const isConfigured = await configManager.isConfigured(testProjectPath)
|
|
276
|
-
|
|
277
|
-
expect(isConfigured).toBe(true)
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
it('should return false for missing config', async () => {
|
|
281
|
-
const isConfigured = await configManager.isConfigured(testProjectPath)
|
|
282
|
-
|
|
283
|
-
expect(isConfigured).toBe(false)
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
it('should return false for invalid config', async () => {
|
|
287
|
-
const config = { projectId: 'test' } // Missing dataPath
|
|
288
|
-
await configManager.writeConfig(testProjectPath, config)
|
|
289
|
-
|
|
290
|
-
const isConfigured = await configManager.isConfigured(testProjectPath)
|
|
291
|
-
|
|
292
|
-
expect(isConfigured).toBe(false)
|
|
293
|
-
})
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
describe('Author Management', () => {
|
|
297
|
-
const projectId = 'test-authors'
|
|
298
|
-
|
|
299
|
-
beforeEach(async () => {
|
|
300
|
-
await configManager.ensureGlobalConfig(projectId)
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
describe('addAuthor()', () => {
|
|
304
|
-
it('should add new author', async () => {
|
|
305
|
-
const author = {
|
|
306
|
-
name: 'New Author',
|
|
307
|
-
email: 'new@example.com',
|
|
308
|
-
github: 'newauthor'
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
await configManager.addAuthor(projectId, author)
|
|
312
|
-
|
|
313
|
-
const globalConfig = await configManager.readGlobalConfig(projectId)
|
|
314
|
-
expect(globalConfig.authors.length).toBe(1)
|
|
315
|
-
expect(globalConfig.authors[0].github).toBe('newauthor')
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
it('should not add duplicate author', async () => {
|
|
319
|
-
const author = {
|
|
320
|
-
name: 'Duplicate',
|
|
321
|
-
github: 'duplicate'
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
await configManager.addAuthor(projectId, author)
|
|
325
|
-
await configManager.addAuthor(projectId, author)
|
|
326
|
-
|
|
327
|
-
const globalConfig = await configManager.readGlobalConfig(projectId)
|
|
328
|
-
expect(globalConfig.authors.length).toBe(1)
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
it('should set timestamps for new author', async () => {
|
|
332
|
-
const author = { github: 'timestamp-test' }
|
|
333
|
-
|
|
334
|
-
await configManager.addAuthor(projectId, author)
|
|
335
|
-
|
|
336
|
-
const globalConfig = await configManager.readGlobalConfig(projectId)
|
|
337
|
-
const addedAuthor = globalConfig.authors[0]
|
|
338
|
-
expect(addedAuthor.firstContribution).toBeDefined()
|
|
339
|
-
expect(addedAuthor.lastActivity).toBeDefined()
|
|
340
|
-
})
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
describe('findAuthor()', () => {
|
|
344
|
-
it('should find existing author', async () => {
|
|
345
|
-
const author = { github: 'findme' }
|
|
346
|
-
await configManager.addAuthor(projectId, author)
|
|
347
|
-
|
|
348
|
-
const found = await configManager.findAuthor(projectId, 'findme')
|
|
349
|
-
|
|
350
|
-
expect(found).toBeDefined()
|
|
351
|
-
expect(found.github).toBe('findme')
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
it('should return null for non-existent author', async () => {
|
|
355
|
-
const found = await configManager.findAuthor(projectId, 'notfound')
|
|
356
|
-
|
|
357
|
-
expect(found).toBeNull()
|
|
358
|
-
})
|
|
359
|
-
|
|
360
|
-
it('should return null for non-existent project', async () => {
|
|
361
|
-
const found = await configManager.findAuthor('non-existent', 'anyone')
|
|
362
|
-
|
|
363
|
-
expect(found).toBeNull()
|
|
364
|
-
})
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
describe('updateAuthorActivity()', () => {
|
|
368
|
-
it('should update last activity timestamp', async () => {
|
|
369
|
-
const author = { github: 'activity-test' }
|
|
370
|
-
await configManager.addAuthor(projectId, author)
|
|
371
|
-
|
|
372
|
-
const before = await configManager.findAuthor(projectId, 'activity-test')
|
|
373
|
-
const beforeTime = before.lastActivity
|
|
374
|
-
|
|
375
|
-
// Wait a bit to ensure different timestamp
|
|
376
|
-
await new Promise(resolve => setTimeout(resolve, 10))
|
|
377
|
-
|
|
378
|
-
await configManager.updateAuthorActivity(projectId, 'activity-test')
|
|
379
|
-
|
|
380
|
-
const after = await configManager.findAuthor(projectId, 'activity-test')
|
|
381
|
-
expect(after.lastActivity).not.toBe(beforeTime)
|
|
382
|
-
})
|
|
383
|
-
|
|
384
|
-
it('should not fail for non-existent author', async () => {
|
|
385
|
-
await expect(
|
|
386
|
-
configManager.updateAuthorActivity(projectId, 'nonexistent')
|
|
387
|
-
).resolves.not.toThrow()
|
|
388
|
-
})
|
|
389
|
-
})
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
describe('getConfigWithDefaults()', () => {
|
|
393
|
-
it('should return existing config', async () => {
|
|
394
|
-
const config = {
|
|
395
|
-
projectId: 'test-defaults',
|
|
396
|
-
dataPath: '~/.prjct-cli/projects/test-defaults'
|
|
397
|
-
}
|
|
398
|
-
await configManager.writeConfig(testProjectPath, config)
|
|
399
|
-
|
|
400
|
-
const result = await configManager.getConfigWithDefaults(testProjectPath)
|
|
401
|
-
|
|
402
|
-
expect(result.projectId).toBe('test-defaults')
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
it('should return defaults if config does not exist', async () => {
|
|
406
|
-
const result = await configManager.getConfigWithDefaults(testProjectPath)
|
|
407
|
-
|
|
408
|
-
expect(result).toBeDefined()
|
|
409
|
-
expect(result.projectId).toBeDefined()
|
|
410
|
-
expect(result.dataPath).toBeDefined()
|
|
411
|
-
})
|
|
412
|
-
})
|
|
413
|
-
|
|
414
|
-
describe('updateLastSync()', () => {
|
|
415
|
-
it('should update lastSync timestamp', async () => {
|
|
416
|
-
const projectId = 'test-sync'
|
|
417
|
-
await configManager.ensureGlobalConfig(projectId)
|
|
418
|
-
|
|
419
|
-
const before = await configManager.readGlobalConfig(projectId)
|
|
420
|
-
const beforeTime = before.lastSync
|
|
421
|
-
|
|
422
|
-
await new Promise(resolve => setTimeout(resolve, 10))
|
|
423
|
-
await configManager.updateLastSync(testProjectPath)
|
|
424
|
-
|
|
425
|
-
// Need to set projectId in local config for updateLastSync to work
|
|
426
|
-
await configManager.writeConfig(testProjectPath, { projectId, dataPath: '~/.prjct-cli/projects/test-sync' })
|
|
427
|
-
await configManager.updateLastSync(testProjectPath)
|
|
428
|
-
|
|
429
|
-
const after = await configManager.readGlobalConfig(projectId)
|
|
430
|
-
expect(after.lastSync).not.toBe(beforeTime)
|
|
431
|
-
})
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
describe('needsMigration()', () => {
|
|
435
|
-
it('should return false for new project', async () => {
|
|
436
|
-
const needs = await configManager.needsMigration(testProjectPath)
|
|
437
|
-
|
|
438
|
-
expect(needs).toBe(false)
|
|
439
|
-
})
|
|
440
|
-
|
|
441
|
-
it('should detect when migration is needed', async () => {
|
|
442
|
-
// Create legacy structure
|
|
443
|
-
const legacyPath = path.join(testProjectPath, '.prjct')
|
|
444
|
-
await fs.mkdir(legacyPath, { recursive: true })
|
|
445
|
-
await fs.writeFile(path.join(legacyPath, 'old-file.md'), 'content')
|
|
446
|
-
|
|
447
|
-
const needs = await configManager.needsMigration(testProjectPath)
|
|
448
|
-
|
|
449
|
-
// Should be true if legacy exists but no proper config/structure
|
|
450
|
-
expect(typeof needs).toBe('boolean')
|
|
451
|
-
})
|
|
452
|
-
})
|
|
453
|
-
})
|
|
454
|
-
|