prjct-cli 0.6.0 → 0.7.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +67 -6
  2. package/CLAUDE.md +442 -36
  3. package/README.md +47 -54
  4. package/bin/prjct +174 -240
  5. package/core/agentic/command-executor.js +113 -0
  6. package/core/agentic/context-builder.js +85 -0
  7. package/core/agentic/prompt-builder.js +86 -0
  8. package/core/agentic/template-loader.js +104 -0
  9. package/core/agentic/tool-registry.js +117 -0
  10. package/core/command-registry.js +109 -65
  11. package/core/commands.js +2213 -2173
  12. package/core/domain/agent-generator.js +118 -0
  13. package/core/domain/analyzer.js +211 -0
  14. package/core/domain/architect-session.js +300 -0
  15. package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
  16. package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
  17. package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
  18. package/core/{command-installer.js → infrastructure/command-installer.js} +5 -3
  19. package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
  20. package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
  21. package/core/{migrator.js → infrastructure/migrator.js} +34 -19
  22. package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
  23. package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
  24. package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
  25. package/core/{animations-simple.js → utils/animations.js} +3 -23
  26. package/core/utils/date-helper.js +238 -0
  27. package/core/utils/file-helper.js +327 -0
  28. package/core/utils/jsonl-helper.js +206 -0
  29. package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
  30. package/core/utils/session-helper.js +277 -0
  31. package/core/{version.js → utils/version.js} +1 -1
  32. package/package.json +4 -12
  33. package/templates/agents/AGENTS.md +101 -27
  34. package/templates/analysis/analyze.md +84 -0
  35. package/templates/commands/analyze.md +9 -2
  36. package/templates/commands/bug.md +79 -0
  37. package/templates/commands/build.md +5 -2
  38. package/templates/commands/cleanup.md +5 -2
  39. package/templates/commands/design.md +5 -2
  40. package/templates/commands/done.md +4 -2
  41. package/templates/commands/feature.md +113 -0
  42. package/templates/commands/fix.md +41 -10
  43. package/templates/commands/git.md +7 -2
  44. package/templates/commands/help.md +2 -2
  45. package/templates/commands/idea.md +14 -5
  46. package/templates/commands/init.md +62 -7
  47. package/templates/commands/next.md +4 -2
  48. package/templates/commands/now.md +4 -2
  49. package/templates/commands/progress.md +27 -5
  50. package/templates/commands/recap.md +39 -10
  51. package/templates/commands/roadmap.md +19 -5
  52. package/templates/commands/ship.md +118 -16
  53. package/templates/commands/status.md +4 -2
  54. package/templates/commands/sync.md +19 -15
  55. package/templates/commands/task.md +4 -2
  56. package/templates/commands/test.md +5 -2
  57. package/templates/commands/workflow.md +4 -2
  58. package/core/agent-generator.js +0 -525
  59. package/core/analyzer.js +0 -600
  60. package/core/animations.js +0 -277
  61. package/core/ascii-graphics.js +0 -433
  62. package/core/git-integration.js +0 -401
  63. package/core/task-schema.js +0 -342
  64. package/core/workflow-engine.js +0 -213
  65. package/core/workflow-prompts.js +0 -192
  66. package/core/workflow-rules.js +0 -147
  67. package/scripts/post-install.js +0 -121
  68. package/scripts/preuninstall.js +0 -94
  69. package/scripts/verify-installation.sh +0 -158
  70. package/templates/agents/be.template.md +0 -27
  71. package/templates/agents/coordinator.template.md +0 -34
  72. package/templates/agents/data.template.md +0 -27
  73. package/templates/agents/devops.template.md +0 -27
  74. package/templates/agents/fe.template.md +0 -27
  75. package/templates/agents/mobile.template.md +0 -27
  76. package/templates/agents/qa.template.md +0 -27
  77. package/templates/agents/scribe.template.md +0 -29
  78. package/templates/agents/security.template.md +0 -27
  79. package/templates/agents/ux.template.md +0 -27
  80. package/templates/commands/context.md +0 -36
  81. package/templates/commands/stuck.md +0 -36
  82. package/templates/examples/natural-language-examples.md +0 -532
  83. /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
@@ -1,14 +1,14 @@
1
- const https = require('https');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
1
+ const https = require('https')
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+ const os = require('os')
5
5
 
6
6
  class UpdateChecker {
7
7
  constructor() {
8
- this.packageName = 'prjct-cli';
9
- this.cacheDir = path.join(os.homedir(), '.prjct-cli', 'config');
10
- this.cacheFile = path.join(this.cacheDir, 'update-cache.json');
11
- this.checkInterval = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
8
+ this.packageName = 'prjct-cli'
9
+ this.cacheDir = path.join(os.homedir(), '.prjct-cli', 'config')
10
+ this.cacheFile = path.join(this.cacheDir, 'update-cache.json')
11
+ this.checkInterval = 24 * 60 * 60 * 1000 // 24 hours in milliseconds
12
12
  }
13
13
 
14
14
  /**
@@ -16,12 +16,12 @@ class UpdateChecker {
16
16
  */
17
17
  getCurrentVersion() {
18
18
  try {
19
- const packageJsonPath = path.join(__dirname, '..', 'package.json');
20
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
21
- return packageJson.version;
19
+ const packageJsonPath = path.join(__dirname, '..', '..', 'package.json')
20
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
21
+ return packageJson.version
22
22
  } catch (error) {
23
- console.error('Error reading package version:', error.message);
24
- return null;
23
+ console.error('Error reading package version:', error.message)
24
+ return null
25
25
  }
26
26
  }
27
27
 
@@ -36,42 +36,42 @@ class UpdateChecker {
36
36
  method: 'GET',
37
37
  headers: {
38
38
  'User-Agent': 'prjct-cli-update-checker',
39
- Accept: 'application/json'
40
- }
41
- };
39
+ Accept: 'application/json',
40
+ },
41
+ }
42
42
 
43
- const req = https.request(options, res => {
44
- let data = '';
43
+ const req = https.request(options, (res) => {
44
+ let data = ''
45
45
 
46
- res.on('data', chunk => {
47
- data += chunk;
48
- });
46
+ res.on('data', (chunk) => {
47
+ data += chunk
48
+ })
49
49
 
50
50
  res.on('end', () => {
51
51
  try {
52
52
  if (res.statusCode === 200) {
53
- const packageData = JSON.parse(data);
54
- resolve(packageData.version);
53
+ const packageData = JSON.parse(data)
54
+ resolve(packageData.version)
55
55
  } else {
56
- reject(new Error(`npm registry returned status ${res.statusCode}`));
56
+ reject(new Error(`npm registry returned status ${res.statusCode}`))
57
57
  }
58
58
  } catch (error) {
59
- reject(error);
59
+ reject(error)
60
60
  }
61
- });
62
- });
61
+ })
62
+ })
63
63
 
64
- req.on('error', error => {
65
- reject(error);
66
- });
64
+ req.on('error', (error) => {
65
+ reject(error)
66
+ })
67
67
 
68
68
  req.setTimeout(5000, () => {
69
- req.destroy();
70
- reject(new Error('Request timeout'));
71
- });
69
+ req.destroy()
70
+ reject(new Error('Request timeout'))
71
+ })
72
72
 
73
- req.end();
74
- });
73
+ req.end()
74
+ })
75
75
  }
76
76
 
77
77
  /**
@@ -79,18 +79,18 @@ class UpdateChecker {
79
79
  * Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
80
80
  */
81
81
  compareVersions(v1, v2) {
82
- const parts1 = v1.split('.').map(Number);
83
- const parts2 = v2.split('.').map(Number);
82
+ const parts1 = v1.split('.').map(Number)
83
+ const parts2 = v2.split('.').map(Number)
84
84
 
85
85
  for (let i = 0; i < 3; i++) {
86
- const part1 = parts1[i] || 0;
87
- const part2 = parts2[i] || 0;
86
+ const part1 = parts1[i] || 0
87
+ const part2 = parts2[i] || 0
88
88
 
89
- if (part1 > part2) return 1;
90
- if (part1 < part2) return -1;
89
+ if (part1 > part2) return 1
90
+ if (part1 < part2) return -1
91
91
  }
92
92
 
93
- return 0;
93
+ return 0
94
94
  }
95
95
 
96
96
  /**
@@ -99,13 +99,13 @@ class UpdateChecker {
99
99
  readCache() {
100
100
  try {
101
101
  if (fs.existsSync(this.cacheFile)) {
102
- const cache = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'));
103
- return cache;
102
+ const cache = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'))
103
+ return cache
104
104
  }
105
105
  } catch (error) {
106
106
  // Cache file doesn't exist or is corrupted, ignore
107
107
  }
108
- return null;
108
+ return null
109
109
  }
110
110
 
111
111
  /**
@@ -115,10 +115,10 @@ class UpdateChecker {
115
115
  try {
116
116
  // Ensure cache directory exists
117
117
  if (!fs.existsSync(this.cacheDir)) {
118
- fs.mkdirSync(this.cacheDir, { recursive: true });
118
+ fs.mkdirSync(this.cacheDir, { recursive: true })
119
119
  }
120
120
 
121
- fs.writeFileSync(this.cacheFile, JSON.stringify(data, null, 2), 'utf8');
121
+ fs.writeFileSync(this.cacheFile, JSON.stringify(data, null, 2), 'utf8')
122
122
  } catch (error) {
123
123
  // Fail silently - cache is not critical
124
124
  }
@@ -130,14 +130,14 @@ class UpdateChecker {
130
130
  */
131
131
  async checkForUpdates() {
132
132
  try {
133
- const currentVersion = this.getCurrentVersion();
133
+ const currentVersion = this.getCurrentVersion()
134
134
  if (!currentVersion) {
135
- return null;
135
+ return null
136
136
  }
137
137
 
138
138
  // Check cache first
139
- const cache = this.readCache();
140
- const now = Date.now();
139
+ const cache = this.readCache()
140
+ const now = Date.now()
141
141
 
142
142
  if (cache && cache.lastCheck && now - cache.lastCheck < this.checkInterval) {
143
143
  // Cache is still valid
@@ -145,37 +145,37 @@ class UpdateChecker {
145
145
  return {
146
146
  updateAvailable: true,
147
147
  currentVersion,
148
- latestVersion: cache.latestVersion
149
- };
148
+ latestVersion: cache.latestVersion,
149
+ }
150
150
  }
151
151
  return {
152
152
  updateAvailable: false,
153
153
  currentVersion,
154
- latestVersion: currentVersion
155
- };
154
+ latestVersion: currentVersion,
155
+ }
156
156
  }
157
157
 
158
158
  // Cache expired or doesn't exist, fetch from npm
159
- const latestVersion = await this.getLatestVersion();
159
+ const latestVersion = await this.getLatestVersion()
160
160
 
161
161
  // Update cache
162
162
  this.writeCache({
163
163
  lastCheck: now,
164
- latestVersion
165
- });
164
+ latestVersion,
165
+ })
166
166
 
167
167
  // Compare versions
168
- const updateAvailable = this.compareVersions(latestVersion, currentVersion) > 0;
168
+ const updateAvailable = this.compareVersions(latestVersion, currentVersion) > 0
169
169
 
170
170
  return {
171
171
  updateAvailable,
172
172
  currentVersion,
173
- latestVersion
174
- };
173
+ latestVersion,
174
+ }
175
175
  } catch (error) {
176
176
  // Network error or other issue - fail silently
177
177
  // Return null to indicate check couldn't be performed
178
- return null;
178
+ return null
179
179
  }
180
180
  }
181
181
 
@@ -183,13 +183,13 @@ class UpdateChecker {
183
183
  * Get formatted update notification message
184
184
  */
185
185
  async getUpdateNotification() {
186
- const result = await this.checkForUpdates();
186
+ const result = await this.checkForUpdates()
187
187
 
188
188
  if (!result || !result.updateAvailable) {
189
- return null;
189
+ return null
190
190
  }
191
191
 
192
- const chalk = require('chalk');
192
+ const chalk = require('chalk')
193
193
 
194
194
  return (
195
195
  '\n' +
@@ -215,8 +215,8 @@ class UpdateChecker {
215
215
  '\n' +
216
216
  chalk.yellow('└───────────────────────────────────────────────────────────┘') +
217
217
  '\n'
218
- );
218
+ )
219
219
  }
220
220
  }
221
221
 
222
- module.exports = UpdateChecker;
222
+ module.exports = UpdateChecker
@@ -38,18 +38,7 @@ const frames = {
38
38
  '。゚☆: *.☽ .* :☆゚. ✨ ・ 。゚',
39
39
  '☆: *.☽ .* :☆゚. ✨ ・ 。゚☆:',
40
40
  ],
41
- loading: [
42
- '⠋',
43
- '⠙',
44
- '⠹',
45
- '⠸',
46
- '⠼',
47
- '⠴',
48
- '⠦',
49
- '⠧',
50
- '⠇',
51
- '⠏',
52
- ],
41
+ loading: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
53
42
  progress: [
54
43
  '[ ]',
55
44
  '[▓ ]',
@@ -63,16 +52,7 @@ const frames = {
63
52
  '[▓▓▓▓▓▓▓▓▓ ]',
64
53
  '[▓▓▓▓▓▓▓▓▓▓]',
65
54
  ],
66
- celebration: [
67
- '🎉',
68
- '🎊',
69
- '✨',
70
- '🌟',
71
- '⭐',
72
- '💫',
73
- '🎆',
74
- '🎇',
75
- ],
55
+ celebration: ['🎉', '🎊', '✨', '🌟', '⭐', '💫', '🎆', '🎇'],
76
56
  }
77
57
 
78
58
  const banners = {
@@ -150,7 +130,7 @@ async function sparkle(message) {
150
130
  }
151
131
 
152
132
  function sleep(ms) {
153
- return new Promise(resolve => setTimeout(resolve, ms))
133
+ return new Promise((resolve) => setTimeout(resolve, ms))
154
134
  }
155
135
 
156
136
  function formatShip(feature, count) {
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Date Helper - Centralized date operations and formatting
3
+ *
4
+ * Eliminates duplicated date logic across:
5
+ * - session-manager.js (_getDateKey, _getTodayKey)
6
+ * - path-manager.js (getSessionPath date formatting)
7
+ * - commands.js (38+ inline date operations)
8
+ *
9
+ * @module date-helper
10
+ */
11
+
12
+ /**
13
+ * Format a date to YYYY-MM-DD format
14
+ *
15
+ * @param {Date} date - Date to format
16
+ * @returns {string} - Formatted date string (e.g., "2025-10-04")
17
+ */
18
+ function formatDate(date) {
19
+ const year = date.getFullYear()
20
+ const month = (date.getMonth() + 1).toString().padStart(2, '0')
21
+ const day = date.getDate().toString().padStart(2, '0')
22
+ return `${year}-${month}-${day}`
23
+ }
24
+
25
+ /**
26
+ * Format a date to YYYY-MM format
27
+ *
28
+ * @param {Date} date - Date to format
29
+ * @returns {string} - Formatted month string (e.g., "2025-10")
30
+ */
31
+ function formatMonth(date) {
32
+ const year = date.getFullYear()
33
+ const month = (date.getMonth() + 1).toString().padStart(2, '0')
34
+ return `${year}-${month}`
35
+ }
36
+
37
+ /**
38
+ * Get date key for today (YYYY-MM-DD)
39
+ *
40
+ * @returns {string} - Today's date key
41
+ */
42
+ function getTodayKey() {
43
+ return formatDate(new Date())
44
+ }
45
+
46
+ /**
47
+ * Get date key for any date (YYYY-MM-DD)
48
+ * Alias for formatDate for consistency with session-manager
49
+ *
50
+ * @param {Date} date - Date to format
51
+ * @returns {string} - Date key (e.g., "2025-10-04")
52
+ */
53
+ function getDateKey(date) {
54
+ return formatDate(date)
55
+ }
56
+
57
+ /**
58
+ * Get year, month, day components from a date
59
+ * Useful for path construction
60
+ *
61
+ * @param {Date} date - Date to parse
62
+ * @returns {{year: string, month: string, day: string}} - Date components
63
+ */
64
+ function getYearMonthDay(date) {
65
+ return {
66
+ year: date.getFullYear().toString(),
67
+ month: (date.getMonth() + 1).toString().padStart(2, '0'),
68
+ day: date.getDate().toString().padStart(2, '0'),
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Parse a date string to Date object
74
+ * Supports: YYYY-MM-DD, YYYY-MM, ISO strings
75
+ *
76
+ * @param {string} dateString - Date string to parse
77
+ * @returns {Date} - Parsed date
78
+ */
79
+ function parseDate(dateString) {
80
+ return new Date(dateString)
81
+ }
82
+
83
+ /**
84
+ * Get current timestamp in ISO format
85
+ *
86
+ * @returns {string} - ISO timestamp (e.g., "2025-10-04T14:30:00.000Z")
87
+ */
88
+ function getTimestamp() {
89
+ return new Date().toISOString()
90
+ }
91
+
92
+ /**
93
+ * Get date N days ago from today
94
+ *
95
+ * @param {number} days - Number of days to subtract
96
+ * @returns {Date} - Date in the past
97
+ */
98
+ function getDaysAgo(days) {
99
+ const date = new Date()
100
+ date.setDate(date.getDate() - days)
101
+ return date
102
+ }
103
+
104
+ /**
105
+ * Get date N days from today
106
+ *
107
+ * @param {number} days - Number of days to add
108
+ * @returns {Date} - Date in the future
109
+ */
110
+ function getDaysFromNow(days) {
111
+ const date = new Date()
112
+ date.setDate(date.getDate() + days)
113
+ return date
114
+ }
115
+
116
+ /**
117
+ * Get date range between two dates
118
+ *
119
+ * @param {Date} fromDate - Start date
120
+ * @param {Date} toDate - End date
121
+ * @returns {Array<Date>} - Array of dates in range
122
+ */
123
+ function getDateRange(fromDate, toDate) {
124
+ const dates = []
125
+ let current = new Date(fromDate)
126
+
127
+ while (current <= toDate) {
128
+ dates.push(new Date(current))
129
+ current = new Date(
130
+ current.getFullYear(),
131
+ current.getMonth(),
132
+ current.getDate() + 1,
133
+ )
134
+ }
135
+
136
+ return dates
137
+ }
138
+
139
+ /**
140
+ * Check if a date is today
141
+ *
142
+ * @param {Date} date - Date to check
143
+ * @returns {boolean} - True if date is today
144
+ */
145
+ function isToday(date) {
146
+ return formatDate(date) === getTodayKey()
147
+ }
148
+
149
+ /**
150
+ * Check if a date is within the last N days
151
+ *
152
+ * @param {Date} date - Date to check
153
+ * @param {number} days - Number of days
154
+ * @returns {boolean} - True if within range
155
+ */
156
+ function isWithinLastDays(date, days) {
157
+ const threshold = getDaysAgo(days)
158
+ return date >= threshold
159
+ }
160
+
161
+ /**
162
+ * Format duration in human-readable format
163
+ *
164
+ * @param {number} milliseconds - Duration in ms
165
+ * @returns {string} - Formatted duration (e.g., "2h 15m")
166
+ */
167
+ function formatDuration(milliseconds) {
168
+ const seconds = Math.floor(milliseconds / 1000)
169
+ const minutes = Math.floor(seconds / 60)
170
+ const hours = Math.floor(minutes / 60)
171
+ const days = Math.floor(hours / 24)
172
+
173
+ if (days > 0) {
174
+ return `${days}d ${hours % 24}h`
175
+ }
176
+ if (hours > 0) {
177
+ return `${hours}h ${minutes % 60}m`
178
+ }
179
+ if (minutes > 0) {
180
+ return `${minutes}m`
181
+ }
182
+ return `${seconds}s`
183
+ }
184
+
185
+ /**
186
+ * Calculate duration between two dates
187
+ *
188
+ * @param {Date} startDate - Start date
189
+ * @param {Date} endDate - End date (defaults to now)
190
+ * @returns {string} - Formatted duration
191
+ */
192
+ function calculateDuration(startDate, endDate = new Date()) {
193
+ const milliseconds = endDate - startDate
194
+ return formatDuration(milliseconds)
195
+ }
196
+
197
+ /**
198
+ * Get start of day (00:00:00.000)
199
+ *
200
+ * @param {Date} date - Date to process
201
+ * @returns {Date} - Start of day
202
+ */
203
+ function getStartOfDay(date) {
204
+ const result = new Date(date)
205
+ result.setHours(0, 0, 0, 0)
206
+ return result
207
+ }
208
+
209
+ /**
210
+ * Get end of day (23:59:59.999)
211
+ *
212
+ * @param {Date} date - Date to process
213
+ * @returns {Date} - End of day
214
+ */
215
+ function getEndOfDay(date) {
216
+ const result = new Date(date)
217
+ result.setHours(23, 59, 59, 999)
218
+ return result
219
+ }
220
+
221
+ module.exports = {
222
+ formatDate,
223
+ formatMonth,
224
+ getTodayKey,
225
+ getDateKey,
226
+ getYearMonthDay,
227
+ parseDate,
228
+ getTimestamp,
229
+ getDaysAgo,
230
+ getDaysFromNow,
231
+ getDateRange,
232
+ isToday,
233
+ isWithinLastDays,
234
+ formatDuration,
235
+ calculateDuration,
236
+ getStartOfDay,
237
+ getEndOfDay,
238
+ }