prjct-cli 0.7.1 → 0.7.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 +111 -0
- package/README.md +32 -0
- package/core/__tests__/agentic/command-executor.test.js +223 -0
- package/core/__tests__/agentic/context-builder.test.js +160 -0
- package/core/__tests__/agentic/prompt-builder.test.js +212 -0
- package/core/__tests__/agentic/template-loader.test.js +164 -0
- package/core/__tests__/agentic/tool-registry.test.js +243 -0
- package/core/__tests__/domain/agent-generator.test.js +296 -0
- package/core/__tests__/setup.test.js +15 -0
- package/core/__tests__/utils/date-helper.test.js +169 -0
- package/core/__tests__/utils/file-helper.test.js +258 -0
- package/core/commands.js +0 -3
- package/core/infrastructure/agent-detector.js +2 -0
- package/core/infrastructure/session-manager.js +0 -1
- package/core/utils/file-helper.js +2 -0
- package/package.json +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,117 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.3] - 2025-10-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Comprehensive Testing Documentation** - Complete testing guide and setup documentation
|
|
15
|
+
- **TESTING.md** - Full testing guide with:
|
|
16
|
+
- Quick start commands for running tests
|
|
17
|
+
- Vitest workspace architecture (core + website)
|
|
18
|
+
- Detailed configuration for both Node.js and React environments
|
|
19
|
+
- Testing best practices and examples
|
|
20
|
+
- CI/CD integration documentation
|
|
21
|
+
- Troubleshooting guide
|
|
22
|
+
- Complete command reference
|
|
23
|
+
- **README.md Testing Section** - Quick reference with:
|
|
24
|
+
- Quick start commands
|
|
25
|
+
- Test suites overview (Core CLI + Website)
|
|
26
|
+
- CI/CD automation status
|
|
27
|
+
- Link to comprehensive TESTING.md guide
|
|
28
|
+
- **Documentation Quality**: Professional-grade testing documentation for contributors and developers
|
|
29
|
+
- **Coverage**: Documents all 283 tests across both core and website projects
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **Lint Errors** - Removed 3 unused variables to pass ESLint
|
|
34
|
+
- Removed unused `globalProjectPath` in commands.js
|
|
35
|
+
- Removed unused `architectSession` require in commands.js
|
|
36
|
+
- Removed unused `fs` import in session-manager.js
|
|
37
|
+
|
|
38
|
+
### Technical Details
|
|
39
|
+
|
|
40
|
+
- **Testing Stack**: Vitest with workspace configuration for dual environments
|
|
41
|
+
- **Core Tests** (Node.js): 179 tests for agentic system, commands, and utilities
|
|
42
|
+
- **Website Tests** (React): 104 tests with Testing Library for components
|
|
43
|
+
- **CI/CD**: GitHub Actions workflow with parallel test execution
|
|
44
|
+
- **Coverage**: V8 coverage provider with HTML reports
|
|
45
|
+
|
|
46
|
+
## [0.7.2] - 2025-10-04
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **Vercel Deployment Configuration** - Fixed deployment build and configuration
|
|
51
|
+
- Updated vercel build commands to use npm prefix syntax
|
|
52
|
+
- Moved and simplified vercel.json config
|
|
53
|
+
- Added Vercel deployment config and removed CNAME file
|
|
54
|
+
- Fixed build output directory configuration
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- **Version Bump** - Updated from 0.7.1 to 0.7.2
|
|
59
|
+
|
|
60
|
+
## [0.7.1] - 2025-10-04
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **Command Implementation Status** - Added setup and migrate-all commands
|
|
65
|
+
- Marked roadmap, status, and build commands as implemented in registry
|
|
66
|
+
- Added isSupported flag to agent detection for Claude and Terminal agents
|
|
67
|
+
- Better clarity on which commands are available in each environment
|
|
68
|
+
|
|
69
|
+
## [0.7.0] - 2025-10-04
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **Vercel Analytics Integration** - Added Vercel Analytics and Speed Insights tracking
|
|
74
|
+
- Real-time performance monitoring for website
|
|
75
|
+
- User analytics and insights
|
|
76
|
+
- Speed metrics tracking
|
|
77
|
+
|
|
78
|
+
- **AI Policy Page** - New dedicated page for AI usage policies
|
|
79
|
+
- Redesigned footer layout with AI Policy link
|
|
80
|
+
- Clear transparency about AI integration
|
|
81
|
+
- Updated navigation structure
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
- **Repository Privacy** - Transitioned from open source to proprietary
|
|
86
|
+
- Removed all GitHub repository links from website
|
|
87
|
+
- Removed open source references
|
|
88
|
+
- Updated Terms of Use with proprietary license
|
|
89
|
+
- Repository made private due to AI agent privacy concerns and terms of use compliance
|
|
90
|
+
- FREE tier remains available to all users indefinitely
|
|
91
|
+
- Contact: jlopezlira@gmail.com or jlopezlira.dev
|
|
92
|
+
|
|
93
|
+
- **Deployment Migration** - Migrated from GitHub Pages to Vercel
|
|
94
|
+
- Better build performance and CDN
|
|
95
|
+
- Automatic deployments
|
|
96
|
+
- Improved website hosting
|
|
97
|
+
|
|
98
|
+
- **Workflow Simplification** - Reduced to 5 essential commands
|
|
99
|
+
- Streamlined developer experience
|
|
100
|
+
- Clearer command structure
|
|
101
|
+
- Focus on core functionality
|
|
102
|
+
|
|
103
|
+
- **Documentation Updates**
|
|
104
|
+
- Added session-based architecture documentation with auto-archiving
|
|
105
|
+
- JSONL logs for better performance
|
|
106
|
+
- Moved Changelog link from header to footer
|
|
107
|
+
|
|
108
|
+
### Fixed
|
|
109
|
+
|
|
110
|
+
- **TypeScript Build Errors** - Resolved all TypeScript errors in website build
|
|
111
|
+
- Removed unused Globe import from Privacy.tsx
|
|
112
|
+
- Fixed type errors across components
|
|
113
|
+
|
|
114
|
+
### Technical Details
|
|
115
|
+
|
|
116
|
+
- **Session Architecture**: JSONL-based logging with auto-archiving after 30 days
|
|
117
|
+
- **Deployment**: Vercel platform with automatic builds
|
|
118
|
+
- **Analytics**: Vercel Analytics + Speed Insights integration
|
|
119
|
+
- **Privacy**: Repository now private, free tier remains available
|
|
120
|
+
|
|
10
121
|
## [0.6.0] - 2025-10-03
|
|
11
122
|
|
|
12
123
|
### Changed
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ Developer momentum tool for indie hackers and small teams.
|
|
|
14
14
|
|
|
15
15
|
[](CLAUDE.md)
|
|
16
16
|
[]()
|
|
17
|
+
[](https://github.com/jlopezlira/prjct-cli/actions/workflows/test.yml)
|
|
18
|
+
[](https://prjct.app)
|
|
17
19
|
|
|
18
20
|
## 🤖 Claude-Native Architecture
|
|
19
21
|
|
|
@@ -361,6 +363,36 @@ prjct recap # Show progress
|
|
|
361
363
|
|
|
362
364
|
> 📚 **More workflows:** Visit [prjct.app/workflows-guide](https://prjct.app/workflows-guide) for detailed interactive examples
|
|
363
365
|
|
|
366
|
+
## 🧪 Testing
|
|
367
|
+
|
|
368
|
+
prjct-cli uses **Vitest** with comprehensive test coverage for both the CLI core and website.
|
|
369
|
+
|
|
370
|
+
### Quick Start
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
# Run all tests
|
|
374
|
+
npm test
|
|
375
|
+
|
|
376
|
+
# Run tests in watch mode
|
|
377
|
+
npm run test:watch
|
|
378
|
+
|
|
379
|
+
# Generate coverage report
|
|
380
|
+
npm run test:coverage
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Test Suites
|
|
384
|
+
|
|
385
|
+
- **Core CLI** (Node.js) - Agentic system, commands, and utilities
|
|
386
|
+
- **Website** (React + TypeScript) - Component tests with Testing Library
|
|
387
|
+
|
|
388
|
+
### CI/CD
|
|
389
|
+
|
|
390
|
+
Tests run automatically on every push and pull request via GitHub Actions.
|
|
391
|
+
|
|
392
|
+
[](https://github.com/jlopezlira/prjct-cli/actions/workflows/test.yml)
|
|
393
|
+
|
|
394
|
+
> 📖 **Full testing guide:** See [TESTING.md](TESTING.md) for detailed documentation, configuration, and best practices.
|
|
395
|
+
|
|
364
396
|
## ❓ FAQ
|
|
365
397
|
|
|
366
398
|
**Can I work on multiple tasks?**
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import commandExecutor from '../../agentic/command-executor.js'
|
|
3
|
+
|
|
4
|
+
describe('Command Executor', () => {
|
|
5
|
+
const testProjectPath = process.cwd()
|
|
6
|
+
|
|
7
|
+
describe('execute()', () => {
|
|
8
|
+
it('should execute command successfully', async () => {
|
|
9
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
10
|
+
|
|
11
|
+
expect(result).toBeDefined()
|
|
12
|
+
expect(result.success).toBe(true)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should load template', async () => {
|
|
16
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
17
|
+
|
|
18
|
+
expect(result.template).toBeDefined()
|
|
19
|
+
expect(result.template).toHaveProperty('content')
|
|
20
|
+
expect(result.template).toHaveProperty('frontmatter')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should build context', async () => {
|
|
24
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
25
|
+
|
|
26
|
+
expect(result.context).toBeDefined()
|
|
27
|
+
expect(result.context).toHaveProperty('projectId')
|
|
28
|
+
expect(result.context).toHaveProperty('projectPath')
|
|
29
|
+
expect(result.context).toHaveProperty('paths')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should load state', async () => {
|
|
33
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
34
|
+
|
|
35
|
+
expect(result.state).toBeDefined()
|
|
36
|
+
expect(typeof result.state).toBe('object')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should build prompt', async () => {
|
|
40
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
41
|
+
|
|
42
|
+
expect(result.prompt).toBeDefined()
|
|
43
|
+
expect(typeof result.prompt).toBe('string')
|
|
44
|
+
expect(result.prompt.length).toBeGreaterThan(0)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should pass parameters to context', async () => {
|
|
48
|
+
const params = { taskName: 'Test Task', feature: 'Test Feature' }
|
|
49
|
+
const result = await commandExecutor.execute('now', params, testProjectPath)
|
|
50
|
+
|
|
51
|
+
expect(result.context.params).toEqual(params)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should handle different commands', async () => {
|
|
55
|
+
const commands = ['now', 'done', 'next', 'ship']
|
|
56
|
+
|
|
57
|
+
for (const cmd of commands) {
|
|
58
|
+
const result = await commandExecutor.execute(cmd, {}, testProjectPath)
|
|
59
|
+
expect(result.success).toBe(true)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should handle non-existent command', async () => {
|
|
64
|
+
const result = await commandExecutor.execute('nonexistent', {}, testProjectPath)
|
|
65
|
+
|
|
66
|
+
expect(result.success).toBe(false)
|
|
67
|
+
expect(result.error).toBeDefined()
|
|
68
|
+
expect(result.error).toContain('Template not found')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should include all execution data', async () => {
|
|
72
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
73
|
+
|
|
74
|
+
expect(result).toHaveProperty('success')
|
|
75
|
+
expect(result).toHaveProperty('template')
|
|
76
|
+
expect(result).toHaveProperty('context')
|
|
77
|
+
expect(result).toHaveProperty('state')
|
|
78
|
+
expect(result).toHaveProperty('prompt')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('executeTool()', () => {
|
|
83
|
+
it('should execute allowed tool', async () => {
|
|
84
|
+
const allowedTools = ['Read', 'Write', 'Bash']
|
|
85
|
+
const result = await commandExecutor.executeTool('Read', [__filename], allowedTools)
|
|
86
|
+
|
|
87
|
+
expect(result).toBeDefined()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should throw error for disallowed tool', async () => {
|
|
91
|
+
const allowedTools = ['Read']
|
|
92
|
+
|
|
93
|
+
await expect(commandExecutor.executeTool('Write', ['file.txt', 'content'], allowedTools)).rejects.toThrow(
|
|
94
|
+
'Tool Write not allowed for this command'
|
|
95
|
+
)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should execute Read tool', async () => {
|
|
99
|
+
const allowedTools = ['Read']
|
|
100
|
+
const content = await commandExecutor.executeTool('Read', [__filename], allowedTools)
|
|
101
|
+
|
|
102
|
+
expect(content).toBeDefined()
|
|
103
|
+
expect(content).toContain('Command Executor')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should handle tool errors', async () => {
|
|
107
|
+
const allowedTools = ['Read']
|
|
108
|
+
|
|
109
|
+
await expect(commandExecutor.executeTool('UnknownTool', [], allowedTools)).rejects.toThrow()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should check permissions before execution', async () => {
|
|
113
|
+
const allowedTools = ['Read']
|
|
114
|
+
|
|
115
|
+
// Bash not allowed
|
|
116
|
+
await expect(commandExecutor.executeTool('Bash', ['echo test'], allowedTools)).rejects.toThrow(
|
|
117
|
+
'not allowed'
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('executeSimple()', () => {
|
|
123
|
+
it('should execute simple command', async () => {
|
|
124
|
+
const executionFn = async (_tools, _context) => {
|
|
125
|
+
return { message: 'Executed successfully' }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const result = await commandExecutor.executeSimple('now', executionFn, testProjectPath)
|
|
129
|
+
|
|
130
|
+
expect(result.success).toBe(true)
|
|
131
|
+
expect(result.result).toEqual({ message: 'Executed successfully' })
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('should provide tools to execution function', async () => {
|
|
135
|
+
const executionFn = async (_tools, _context) => {
|
|
136
|
+
expect(_tools).toHaveProperty('read')
|
|
137
|
+
expect(_tools).toHaveProperty('write')
|
|
138
|
+
expect(_tools).toHaveProperty('bash')
|
|
139
|
+
return { tools: 'available' }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const result = await commandExecutor.executeSimple('now', executionFn, testProjectPath)
|
|
143
|
+
|
|
144
|
+
expect(result.success).toBe(true)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('should provide context to execution function', async () => {
|
|
148
|
+
const executionFn = async (_tools, _context) => {
|
|
149
|
+
expect(_context).toBeDefined()
|
|
150
|
+
expect(_context).toHaveProperty('projectId')
|
|
151
|
+
expect(_context).toHaveProperty('projectPath')
|
|
152
|
+
return { context: 'received' }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = await commandExecutor.executeSimple('now', executionFn, testProjectPath)
|
|
156
|
+
|
|
157
|
+
expect(result.success).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should enforce tool permissions', async () => {
|
|
161
|
+
const executionFn = async (_tools, _context) => {
|
|
162
|
+
// Try to use a disallowed tool
|
|
163
|
+
await _tools.write('/test/file.txt', 'content')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const result = await commandExecutor.executeSimple('now', executionFn, testProjectPath)
|
|
167
|
+
|
|
168
|
+
// Should fail if Write is not in allowed tools for 'now'
|
|
169
|
+
expect(result.success).toBe(false)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('should handle execution errors', async () => {
|
|
173
|
+
const executionFn = async (_tools, _context) => {
|
|
174
|
+
throw new Error('Execution failed')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const result = await commandExecutor.executeSimple('now', executionFn, testProjectPath)
|
|
178
|
+
|
|
179
|
+
expect(result.success).toBe(false)
|
|
180
|
+
expect(result.error).toContain('Execution failed')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('should work with allowed tools', async () => {
|
|
184
|
+
const executionFn = async (_tools, _context) => {
|
|
185
|
+
// Read is typically allowed
|
|
186
|
+
const content = await _tools.read(__filename)
|
|
187
|
+
return { readSuccess: content !== null }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const result = await commandExecutor.executeSimple('now', executionFn, testProjectPath)
|
|
191
|
+
|
|
192
|
+
expect(result.success).toBe(true)
|
|
193
|
+
expect(result.result.readSuccess).toBe(true)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
describe('Integration', () => {
|
|
198
|
+
it('should execute full command flow', async () => {
|
|
199
|
+
const result = await commandExecutor.execute('now', { task: 'Test Task' }, testProjectPath)
|
|
200
|
+
|
|
201
|
+
expect(result.success).toBe(true)
|
|
202
|
+
expect(result.template).toBeDefined()
|
|
203
|
+
expect(result.context).toBeDefined()
|
|
204
|
+
expect(result.state).toBeDefined()
|
|
205
|
+
expect(result.prompt).toBeDefined()
|
|
206
|
+
|
|
207
|
+
// Prompt should include the template content
|
|
208
|
+
expect(result.prompt).toContain(result.template.content)
|
|
209
|
+
|
|
210
|
+
// Context should include parameters
|
|
211
|
+
expect(result.context.params.task).toBe('Test Task')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should build proper prompt structure', async () => {
|
|
215
|
+
const result = await commandExecutor.execute('now', {}, testProjectPath)
|
|
216
|
+
|
|
217
|
+
expect(result.prompt).toContain('# Command Instructions')
|
|
218
|
+
expect(result.prompt).toContain('## Project Context')
|
|
219
|
+
expect(result.prompt).toContain('## Current State')
|
|
220
|
+
expect(result.prompt).toContain('## Execute')
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
})
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import contextBuilder from '../../agentic/context-builder.js'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
describe('Context Builder', () => {
|
|
6
|
+
const testProjectPath = process.cwd()
|
|
7
|
+
|
|
8
|
+
describe('build()', () => {
|
|
9
|
+
it('should build context with all required fields', async () => {
|
|
10
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
11
|
+
|
|
12
|
+
expect(context).toBeDefined()
|
|
13
|
+
expect(context).toHaveProperty('projectId')
|
|
14
|
+
expect(context).toHaveProperty('projectPath')
|
|
15
|
+
expect(context).toHaveProperty('globalPath')
|
|
16
|
+
expect(context).toHaveProperty('paths')
|
|
17
|
+
expect(context).toHaveProperty('params')
|
|
18
|
+
expect(context).toHaveProperty('timestamp')
|
|
19
|
+
expect(context).toHaveProperty('date')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should include all file paths', async () => {
|
|
23
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
24
|
+
|
|
25
|
+
expect(context.paths).toHaveProperty('now')
|
|
26
|
+
expect(context.paths).toHaveProperty('next')
|
|
27
|
+
expect(context.paths).toHaveProperty('context')
|
|
28
|
+
expect(context.paths).toHaveProperty('shipped')
|
|
29
|
+
expect(context.paths).toHaveProperty('metrics')
|
|
30
|
+
expect(context.paths).toHaveProperty('ideas')
|
|
31
|
+
expect(context.paths).toHaveProperty('roadmap')
|
|
32
|
+
expect(context.paths).toHaveProperty('memory')
|
|
33
|
+
expect(context.paths).toHaveProperty('analysis')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should use correct project path', async () => {
|
|
37
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
38
|
+
|
|
39
|
+
expect(context.projectPath).toBe(testProjectPath)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should include command parameters', async () => {
|
|
43
|
+
const params = { taskName: 'test task', feature: 'test feature' }
|
|
44
|
+
const context = await contextBuilder.build(testProjectPath, params)
|
|
45
|
+
|
|
46
|
+
expect(context.params).toEqual(params)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should include timestamp', async () => {
|
|
50
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
51
|
+
|
|
52
|
+
expect(context.timestamp).toBeDefined()
|
|
53
|
+
expect(typeof context.timestamp).toBe('string')
|
|
54
|
+
expect(new Date(context.timestamp).toString()).not.toBe('Invalid Date')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should include date', async () => {
|
|
58
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
59
|
+
|
|
60
|
+
expect(context.date).toBeDefined()
|
|
61
|
+
expect(typeof context.date).toBe('string')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should build global path from project ID', async () => {
|
|
65
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
66
|
+
|
|
67
|
+
expect(context.globalPath).toContain('.prjct-cli')
|
|
68
|
+
expect(context.globalPath).toContain('projects')
|
|
69
|
+
expect(context.globalPath).toContain(context.projectId)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('loadState()', () => {
|
|
74
|
+
it('should load state from context', async () => {
|
|
75
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
76
|
+
const state = await contextBuilder.loadState(context)
|
|
77
|
+
|
|
78
|
+
expect(state).toBeDefined()
|
|
79
|
+
expect(typeof state).toBe('object')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should return null for non-existent files', async () => {
|
|
83
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
84
|
+
const state = await contextBuilder.loadState(context)
|
|
85
|
+
|
|
86
|
+
// Some files might not exist
|
|
87
|
+
Object.values(state).forEach((value) => {
|
|
88
|
+
expect(value === null || typeof value === 'string').toBe(true)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should load existing files as strings', async () => {
|
|
93
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
94
|
+
const state = await contextBuilder.loadState(context)
|
|
95
|
+
|
|
96
|
+
// At least some state values should be strings (if files exist)
|
|
97
|
+
const hasStrings = Object.values(state).some((value) => typeof value === 'string')
|
|
98
|
+
expect(typeof hasStrings).toBe('boolean')
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('fileExists()', () => {
|
|
103
|
+
it('should return true for existing file', async () => {
|
|
104
|
+
const exists = await contextBuilder.fileExists(__filename)
|
|
105
|
+
|
|
106
|
+
expect(exists).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should return false for non-existent file', async () => {
|
|
110
|
+
const exists = await contextBuilder.fileExists('/nonexistent/path/file.txt')
|
|
111
|
+
|
|
112
|
+
expect(exists).toBe(false)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should work with package.json', async () => {
|
|
116
|
+
const packagePath = path.join(process.cwd(), 'package.json')
|
|
117
|
+
const exists = await contextBuilder.fileExists(packagePath)
|
|
118
|
+
|
|
119
|
+
expect(exists).toBe(true)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('Path Construction', () => {
|
|
124
|
+
it('should construct paths with correct structure', async () => {
|
|
125
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
126
|
+
|
|
127
|
+
expect(context.paths.now).toContain('core/now.md')
|
|
128
|
+
expect(context.paths.next).toContain('core/next.md')
|
|
129
|
+
expect(context.paths.context).toContain('core/context.md')
|
|
130
|
+
expect(context.paths.shipped).toContain('progress/shipped.md')
|
|
131
|
+
expect(context.paths.metrics).toContain('progress/metrics.md')
|
|
132
|
+
expect(context.paths.ideas).toContain('planning/ideas.md')
|
|
133
|
+
expect(context.paths.roadmap).toContain('planning/roadmap.md')
|
|
134
|
+
expect(context.paths.memory).toContain('memory/context.jsonl')
|
|
135
|
+
expect(context.paths.analysis).toContain('analysis/repo-summary.md')
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('should use global path for all file paths', async () => {
|
|
139
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
140
|
+
|
|
141
|
+
Object.values(context.paths).forEach((filePath) => {
|
|
142
|
+
expect(filePath).toContain(context.globalPath)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('Empty Parameters', () => {
|
|
148
|
+
it('should handle empty command params', async () => {
|
|
149
|
+
const context = await contextBuilder.build(testProjectPath, {})
|
|
150
|
+
|
|
151
|
+
expect(context.params).toEqual({})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('should handle undefined command params', async () => {
|
|
155
|
+
const context = await contextBuilder.build(testProjectPath)
|
|
156
|
+
|
|
157
|
+
expect(context.params).toEqual({})
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
})
|