prjct-cli 0.7.2 → 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.
@@ -0,0 +1,258 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import * as fileHelper from '../../utils/file-helper.js'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+ import os from 'os'
6
+
7
+ describe('File Helper', () => {
8
+ const testDir = path.join(os.tmpdir(), 'prjct-file-helper-test-' + Date.now())
9
+
10
+ beforeEach(async () => {
11
+ await fs.mkdir(testDir, { recursive: true })
12
+ })
13
+
14
+ afterEach(async () => {
15
+ try {
16
+ await fs.rm(testDir, { recursive: true, force: true })
17
+ } catch (error) {
18
+ // Ignore cleanup errors
19
+ }
20
+ })
21
+
22
+ describe('readJson()', () => {
23
+ it('should read and parse JSON file', async () => {
24
+ const testFile = path.join(testDir, 'test.json')
25
+ const data = { name: 'test', value: 123 }
26
+ await fs.writeFile(testFile, JSON.stringify(data))
27
+
28
+ const result = await fileHelper.readJson(testFile)
29
+
30
+ expect(result).toEqual(data)
31
+ })
32
+
33
+ it('should return default value for non-existent file', async () => {
34
+ const result = await fileHelper.readJson('/nonexistent/file.json', { default: true })
35
+
36
+ expect(result).toEqual({ default: true })
37
+ })
38
+
39
+ it('should return null by default for non-existent file', async () => {
40
+ const result = await fileHelper.readJson('/nonexistent/file.json')
41
+
42
+ expect(result).toBeNull()
43
+ })
44
+
45
+ it('should handle complex JSON structures', async () => {
46
+ const testFile = path.join(testDir, 'complex.json')
47
+ const data = {
48
+ nested: {
49
+ array: [1, 2, 3],
50
+ object: { key: 'value' },
51
+ },
52
+ boolean: true,
53
+ number: 42,
54
+ }
55
+ await fs.writeFile(testFile, JSON.stringify(data))
56
+
57
+ const result = await fileHelper.readJson(testFile)
58
+
59
+ expect(result).toEqual(data)
60
+ })
61
+ })
62
+
63
+ describe('writeJson()', () => {
64
+ it('should write JSON file with pretty formatting', async () => {
65
+ const testFile = path.join(testDir, 'write.json')
66
+ const data = { name: 'test', value: 123 }
67
+
68
+ await fileHelper.writeJson(testFile, data)
69
+
70
+ const content = await fs.readFile(testFile, 'utf-8')
71
+ expect(JSON.parse(content)).toEqual(data)
72
+ expect(content).toContain('\n') // Should be pretty-printed
73
+ })
74
+
75
+ it('should use custom indentation', async () => {
76
+ const testFile = path.join(testDir, 'indent.json')
77
+ const data = { key: 'value' }
78
+
79
+ await fileHelper.writeJson(testFile, data, 4)
80
+
81
+ const content = await fs.readFile(testFile, 'utf-8')
82
+ expect(content).toContain(' ') // 4 spaces
83
+ })
84
+
85
+ it('should handle nested objects', async () => {
86
+ const testFile = path.join(testDir, 'nested.json')
87
+ const data = {
88
+ level1: {
89
+ level2: {
90
+ level3: 'deep',
91
+ },
92
+ },
93
+ }
94
+
95
+ await fileHelper.writeJson(testFile, data)
96
+
97
+ const result = await fileHelper.readJson(testFile)
98
+ expect(result).toEqual(data)
99
+ })
100
+ })
101
+
102
+ describe('readFile()', () => {
103
+ it('should read text file', async () => {
104
+ const testFile = path.join(testDir, 'text.txt')
105
+ const content = 'Hello, World!'
106
+ await fs.writeFile(testFile, content)
107
+
108
+ const result = await fileHelper.readFile(testFile)
109
+
110
+ expect(result).toBe(content)
111
+ })
112
+
113
+ it('should return default value for non-existent file', async () => {
114
+ const result = await fileHelper.readFile('/nonexistent/file.txt', 'default content')
115
+
116
+ expect(result).toBe('default content')
117
+ })
118
+
119
+ it('should return empty string by default', async () => {
120
+ const result = await fileHelper.readFile('/nonexistent/file.txt')
121
+
122
+ expect(result).toBe('')
123
+ })
124
+
125
+ it('should handle multi-line content', async () => {
126
+ const testFile = path.join(testDir, 'multiline.txt')
127
+ const content = 'Line 1\nLine 2\nLine 3'
128
+ await fs.writeFile(testFile, content)
129
+
130
+ const result = await fileHelper.readFile(testFile)
131
+
132
+ expect(result).toBe(content)
133
+ })
134
+ })
135
+
136
+ describe('writeFile()', () => {
137
+ it('should write text file', async () => {
138
+ const testFile = path.join(testDir, 'write.txt')
139
+ const content = 'Test content'
140
+
141
+ await fileHelper.writeFile(testFile, content)
142
+
143
+ const result = await fs.readFile(testFile, 'utf-8')
144
+ expect(result).toBe(content)
145
+ })
146
+
147
+ it('should overwrite existing file', async () => {
148
+ const testFile = path.join(testDir, 'overwrite.txt')
149
+
150
+ await fileHelper.writeFile(testFile, 'First')
151
+ await fileHelper.writeFile(testFile, 'Second')
152
+
153
+ const result = await fs.readFile(testFile, 'utf-8')
154
+ expect(result).toBe('Second')
155
+ })
156
+
157
+ it('should create directory if needed', async () => {
158
+ const nestedFile = path.join(testDir, 'nested', 'dir', 'file.txt')
159
+
160
+ await fileHelper.writeFile(nestedFile, 'content')
161
+
162
+ const exists = await fs
163
+ .access(nestedFile)
164
+ .then(() => true)
165
+ .catch(() => false)
166
+ expect(exists).toBe(true)
167
+ })
168
+ })
169
+
170
+ describe('fileExists()', () => {
171
+ it('should return true for existing file', async () => {
172
+ const testFile = path.join(testDir, 'exists.txt')
173
+ await fs.writeFile(testFile, 'content')
174
+
175
+ const exists = await fileHelper.fileExists(testFile)
176
+
177
+ expect(exists).toBe(true)
178
+ })
179
+
180
+ it('should return false for non-existent file', async () => {
181
+ const exists = await fileHelper.fileExists('/nonexistent/file.txt')
182
+
183
+ expect(exists).toBe(false)
184
+ })
185
+
186
+ it('should work with directories', async () => {
187
+ const exists = await fileHelper.fileExists(testDir)
188
+
189
+ expect(exists).toBe(true)
190
+ })
191
+ })
192
+
193
+ describe('ensureDir()', () => {
194
+ it('should create directory if not exists', async () => {
195
+ const newDir = path.join(testDir, 'new', 'nested', 'dir')
196
+
197
+ await fileHelper.ensureDir(newDir)
198
+
199
+ const exists = await fs
200
+ .access(newDir)
201
+ .then(() => true)
202
+ .catch(() => false)
203
+ expect(exists).toBe(true)
204
+ })
205
+
206
+ it('should not fail if directory exists', async () => {
207
+ await fileHelper.ensureDir(testDir)
208
+
209
+ // Should not throw
210
+ await expect(fileHelper.ensureDir(testDir)).resolves.not.toThrow()
211
+ })
212
+ })
213
+
214
+ describe('Integration', () => {
215
+ it('should read and write JSON files', async () => {
216
+ const testFile = path.join(testDir, 'integration.json')
217
+ const data = { test: 'data', number: 42 }
218
+
219
+ await fileHelper.writeJson(testFile, data)
220
+ const result = await fileHelper.readJson(testFile)
221
+
222
+ expect(result).toEqual(data)
223
+ })
224
+
225
+ it('should handle file operations pipeline', async () => {
226
+ const testFile = path.join(testDir, 'pipeline.txt')
227
+
228
+ // Write
229
+ await fileHelper.writeFile(testFile, 'original')
230
+
231
+ // Check exists
232
+ const exists = await fileHelper.fileExists(testFile)
233
+ expect(exists).toBe(true)
234
+
235
+ // Read
236
+ const content = await fileHelper.readFile(testFile)
237
+ expect(content).toBe('original')
238
+
239
+ // Update
240
+ await fileHelper.writeFile(testFile, 'updated')
241
+
242
+ // Read again
243
+ const updated = await fileHelper.readFile(testFile)
244
+ expect(updated).toBe('updated')
245
+ })
246
+
247
+ it('should create nested structure and write files', async () => {
248
+ const nestedDir = path.join(testDir, 'level1', 'level2', 'level3')
249
+ const nestedFile = path.join(nestedDir, 'deep.json')
250
+
251
+ await fileHelper.ensureDir(nestedDir)
252
+ await fileHelper.writeJson(nestedFile, { deep: true })
253
+
254
+ const result = await fileHelper.readJson(nestedFile)
255
+ expect(result).toEqual({ deep: true })
256
+ })
257
+ })
258
+ })
package/core/commands.js CHANGED
@@ -2632,8 +2632,6 @@ Agent: ${agent}
2632
2632
  const skipped = []
2633
2633
 
2634
2634
  for (const projectId of projectIds) {
2635
- const globalProjectPath = path.join(globalRoot, projectId)
2636
-
2637
2635
  // Read global config to get project path
2638
2636
  const globalConfig = await configManager.readGlobalConfig(projectId)
2639
2637
  if (!globalConfig || !globalConfig.projectPath) {
@@ -2704,7 +2702,6 @@ Agent: ${agent}
2704
2702
  console.log('🏗️ Architect Mode - Code Generation\n')
2705
2703
 
2706
2704
  const globalPath = await this.getGlobalProjectPath(projectPath)
2707
- const architectSession = require('./domain/architect-session')
2708
2705
 
2709
2706
  // Check if there's a completed plan
2710
2707
  const planPath = path.join(globalPath, 'planning', 'architect-session.md')
@@ -1,4 +1,3 @@
1
- const fs = require('fs').promises
2
1
  const path = require('path')
3
2
  const pathManager = require('./path-manager')
4
3
  const { VERSION } = require('../utils/version')
@@ -70,6 +70,8 @@ async function readFile(filePath, defaultValue = '') {
70
70
  * @returns {Promise<void>}
71
71
  */
72
72
  async function writeFile(filePath, content) {
73
+ const dir = path.dirname(filePath)
74
+ await fs.mkdir(dir, { recursive: true })
73
75
  await fs.writeFile(filePath, content, 'utf-8')
74
76
  }
75
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Built for Claude - Ship fast, track progress, stay focused. Developer momentum tool for indie hackers.",
5
5
  "main": "core/index.js",
6
6
  "bin": {
@@ -12,7 +12,9 @@
12
12
  },
13
13
  "scripts": {
14
14
  "install-global": "./scripts/install.sh",
15
- "test": "echo 'No tests configured'",
15
+ "test": "vitest run --workspace=vitest.workspace.js",
16
+ "test:watch": "vitest --workspace=vitest.workspace.js",
17
+ "test:coverage": "vitest run --coverage --workspace=vitest.workspace.js",
16
18
  "validate": "node scripts/validate-commands.js",
17
19
  "lint": "eslint \"**/*.js\" --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
18
20
  "lint:fix": "eslint \"**/*.js\" --fix --ignore-pattern \"node_modules/**\" --ignore-pattern \"website/**\"",
@@ -43,15 +45,18 @@
43
45
  },
44
46
  "devDependencies": {
45
47
  "@types/node": "^20.0.0",
48
+ "@vitest/coverage-v8": "^3.2.4",
46
49
  "eslint": "^8.57.1",
47
50
  "eslint-config-prettier": "^10.1.8",
48
51
  "eslint-config-standard": "^17.1.0",
49
52
  "eslint-plugin-import": "^2.32.0",
50
53
  "eslint-plugin-n": "^16.6.2",
51
54
  "eslint-plugin-promise": "^6.6.0",
55
+ "jsdom": "^27.0.0",
52
56
  "prettier": "^3.6.2",
53
57
  "prettier-plugin-tailwindcss": "^0.6.14",
54
- "typescript": "^5.0.0"
58
+ "typescript": "^5.0.0",
59
+ "vitest": "^3.2.4"
55
60
  },
56
61
  "repository": {
57
62
  "type": "git",