prjct-cli 0.6.0 → 0.7.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.
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 +170 -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 +106 -62
  11. package/core/commands.js +2030 -2211
  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} +4 -2
  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,401 +0,0 @@
1
- const { execSync } = require('child_process')
2
- const fs = require('fs').promises
3
-
4
- /**
5
- * GitIntegration - Git repository analysis and validation
6
- *
7
- * Provides git integration for prjct-cli to analyze repository state,
8
- * validate user claims against actual commits, and track project progress.
9
- *
10
- * @version 0.5.0
11
- */
12
- class GitIntegration {
13
- constructor(projectPath = process.cwd()) {
14
- this.projectPath = projectPath
15
- }
16
-
17
- /**
18
- * Check if current directory is a git repository
19
- * @returns {Promise<boolean>} True if git repo exists
20
- */
21
- async isGitRepo() {
22
- try {
23
- execSync('git rev-parse --git-dir', {
24
- cwd: this.projectPath,
25
- stdio: 'ignore',
26
- })
27
- return true
28
- } catch {
29
- return false
30
- }
31
- }
32
-
33
- /**
34
- * Get information about the last commit
35
- * @returns {Promise<Object|null>} Commit info or null if no commits
36
- */
37
- async getLastCommit() {
38
- if (!(await this.isGitRepo())) {
39
- return null
40
- }
41
-
42
- try {
43
- const output = execSync(
44
- 'git log -1 --format="%H|%s|%ar|%an"',
45
- {
46
- cwd: this.projectPath,
47
- encoding: 'utf-8',
48
- },
49
- )
50
-
51
- const [hash, message, timeAgo, author] = output.trim().split('|')
52
-
53
- return {
54
- hash: hash.substring(0, 7), // Short hash
55
- fullHash: hash,
56
- message,
57
- timeAgo,
58
- author,
59
- }
60
- } catch {
61
- return null // No commits yet
62
- }
63
- }
64
-
65
- /**
66
- * Get working directory status
67
- * @returns {Promise<Object|null>} Status info or null if not git repo
68
- */
69
- async getWorkingDirStatus() {
70
- if (!(await this.isGitRepo())) {
71
- return null
72
- }
73
-
74
- try {
75
- const status = execSync('git status --porcelain', {
76
- cwd: this.projectPath,
77
- encoding: 'utf-8',
78
- })
79
-
80
- const lines = status.trim().split('\n').filter(Boolean)
81
-
82
- const modified = lines.filter(
83
- l => l.startsWith(' M') || l.startsWith('M'),
84
- ).length
85
- const added = lines.filter(
86
- l => l.startsWith('A') || l.startsWith('??'),
87
- ).length
88
- const deleted = lines.filter(
89
- l => l.startsWith(' D') || l.startsWith('D'),
90
- ).length
91
- const renamed = lines.filter(l => l.startsWith('R')).length
92
-
93
- return {
94
- modified,
95
- added,
96
- deleted,
97
- renamed,
98
- totalChanges: lines.length,
99
- isClean: lines.length === 0,
100
- files: lines.map(l => l.substring(3)), // Remove status prefix
101
- }
102
- } catch {
103
- return null
104
- }
105
- }
106
-
107
- /**
108
- * Get diff summary between HEAD and working directory
109
- * @returns {Promise<Object|null>} Diff summary or null
110
- */
111
- async getDiffSummary() {
112
- if (!(await this.isGitRepo())) {
113
- return null
114
- }
115
-
116
- try {
117
- const diff = execSync('git diff HEAD --name-only', {
118
- cwd: this.projectPath,
119
- encoding: 'utf-8',
120
- })
121
-
122
- const files = diff.trim().split('\n').filter(Boolean)
123
-
124
- return {
125
- files,
126
- count: files.length,
127
- }
128
- } catch {
129
- return null
130
- }
131
- }
132
-
133
- /**
134
- * Get files changed since a specific time
135
- * @param {Date|number} since - Timestamp or Date object
136
- * @returns {Promise<Array>} Array of changed files
137
- */
138
- async getChangesSince(since) {
139
- if (!(await this.isGitRepo())) {
140
- return []
141
- }
142
-
143
- try {
144
- const timestamp =
145
- since instanceof Date ? since.toISOString() : new Date(since).toISOString()
146
-
147
- const files = execSync(
148
- `git log --since="${timestamp}" --name-only --pretty=format:`,
149
- {
150
- cwd: this.projectPath,
151
- encoding: 'utf-8',
152
- },
153
- )
154
-
155
- return [...new Set(files.trim().split('\n').filter(Boolean))]
156
- } catch {
157
- return []
158
- }
159
- }
160
-
161
- /**
162
- * Validate user claim against git state
163
- * @param {string} claim - User's claim (e.g., "login is complete")
164
- * @returns {Promise<Object>} Validation result
165
- */
166
- async validateUserClaim(claim) {
167
- if (!(await this.isGitRepo())) {
168
- return {
169
- valid: true,
170
- warning: null,
171
- note: 'Not a git repository - cannot validate against commits',
172
- }
173
- }
174
-
175
- const lastCommit = await this.getLastCommit()
176
- const workingStatus = await this.getWorkingDirStatus()
177
-
178
- if (!lastCommit) {
179
- return {
180
- valid: true,
181
- warning: null,
182
- note: 'No commits yet - cannot validate',
183
- }
184
- }
185
-
186
- // Extract keywords from claim
187
- const keywords = this.extractKeywords(claim)
188
- const completionClaimed = /complete|done|finished|ready|shipped/i.test(claim)
189
-
190
- // Check if keywords appear in last commit
191
- const inLastCommit = keywords.some(keyword =>
192
- lastCommit.message.toLowerCase().includes(keyword),
193
- )
194
-
195
- // Check if there are uncommitted changes
196
- const hasUncommittedChanges = !workingStatus.isClean
197
-
198
- // Validation logic
199
- if (completionClaimed && !inLastCommit && hasUncommittedChanges) {
200
- return {
201
- valid: false,
202
- warning: `⚠️ Discrepancy detected: You claim "${claim}" but it's not in the last commit`,
203
- details: {
204
- lastCommit: lastCommit.message,
205
- uncommittedFiles: workingStatus.totalChanges,
206
- suggestion:
207
- 'Consider committing your changes if the work is truly complete',
208
- },
209
- }
210
- }
211
-
212
- if (completionClaimed && !inLastCommit && !hasUncommittedChanges) {
213
- return {
214
- valid: true,
215
- warning: `ℹ️ Note: "${claim}" not mentioned in recent commits`,
216
- details: {
217
- lastCommit: lastCommit.message,
218
- note: 'Work may have been completed in earlier commits',
219
- },
220
- }
221
- }
222
-
223
- return {
224
- valid: true,
225
- warning: null,
226
- note: inLastCommit
227
- ? `✅ Confirmed in last commit: "${lastCommit.message}"`
228
- : null,
229
- }
230
- }
231
-
232
- /**
233
- * Extract meaningful keywords from a claim
234
- * @param {string} claim - User's claim
235
- * @returns {Array<string>} Extracted keywords
236
- */
237
- extractKeywords(claim) {
238
- // Remove common words
239
- const stopWords = new Set([
240
- 'the',
241
- 'is',
242
- 'are',
243
- 'was',
244
- 'were',
245
- 'a',
246
- 'an',
247
- 'and',
248
- 'or',
249
- 'but',
250
- 'in',
251
- 'on',
252
- 'at',
253
- 'to',
254
- 'for',
255
- 'of',
256
- 'with',
257
- 'by',
258
- 'from',
259
- 'up',
260
- 'about',
261
- 'into',
262
- 'through',
263
- 'during',
264
- 'before',
265
- 'after',
266
- 'above',
267
- 'below',
268
- 'between',
269
- 'under',
270
- 'complete',
271
- 'done',
272
- 'finished',
273
- 'ready',
274
- 'shipped',
275
- ])
276
-
277
- return claim
278
- .toLowerCase()
279
- .split(/\s+/)
280
- .filter(word => word.length > 2 && !stopWords.has(word))
281
- }
282
-
283
- /**
284
- * Get git statistics for analysis
285
- * @returns {Promise<Object>} Git statistics
286
- */
287
- async getGitStats() {
288
- if (!(await this.isGitRepo())) {
289
- return {
290
- isGitRepo: false,
291
- hasCommits: false,
292
- totalCommits: 0,
293
- contributors: [],
294
- lastCommit: null,
295
- workingStatus: null,
296
- }
297
- }
298
-
299
- try {
300
- // Total commits
301
- const totalCommits = parseInt(
302
- execSync('git rev-list --count HEAD', {
303
- cwd: this.projectPath,
304
- encoding: 'utf-8',
305
- }).trim(),
306
- )
307
-
308
- // Contributors
309
- const contributorsOutput = execSync(
310
- 'git log --format="%an" | sort -u',
311
- {
312
- cwd: this.projectPath,
313
- encoding: 'utf-8',
314
- },
315
- )
316
- const contributors = contributorsOutput.trim().split('\n').filter(Boolean)
317
-
318
- const lastCommit = await this.getLastCommit()
319
- const workingStatus = await this.getWorkingDirStatus()
320
-
321
- return {
322
- isGitRepo: true,
323
- hasCommits: totalCommits > 0,
324
- totalCommits,
325
- contributors,
326
- lastCommit,
327
- workingStatus,
328
- }
329
- } catch (error) {
330
- return {
331
- isGitRepo: true,
332
- hasCommits: false,
333
- totalCommits: 0,
334
- contributors: [],
335
- lastCommit: null,
336
- workingStatus: null,
337
- error: error.message,
338
- }
339
- }
340
- }
341
-
342
- /**
343
- * Check if a specific feature/file is in git history
344
- * @param {string} searchTerm - Term to search for
345
- * @returns {Promise<boolean>} True if found in history
346
- */
347
- async isInGitHistory(searchTerm) {
348
- if (!(await this.isGitRepo())) {
349
- return false
350
- }
351
-
352
- try {
353
- const result = execSync(
354
- `git log --all --grep="${searchTerm}" --oneline`,
355
- {
356
- cwd: this.projectPath,
357
- encoding: 'utf-8',
358
- },
359
- )
360
-
361
- return result.trim().length > 0
362
- } catch {
363
- return false
364
- }
365
- }
366
-
367
- /**
368
- * Get branch information
369
- * @returns {Promise<Object|null>} Branch info or null
370
- */
371
- async getBranchInfo() {
372
- if (!(await this.isGitRepo())) {
373
- return null
374
- }
375
-
376
- try {
377
- const currentBranch = execSync('git branch --show-current', {
378
- cwd: this.projectPath,
379
- encoding: 'utf-8',
380
- }).trim()
381
-
382
- const allBranches = execSync('git branch --list', {
383
- cwd: this.projectPath,
384
- encoding: 'utf-8',
385
- })
386
- .trim()
387
- .split('\n')
388
- .map(b => b.trim().replace('* ', ''))
389
-
390
- return {
391
- current: currentBranch,
392
- all: allBranches,
393
- count: allBranches.length,
394
- }
395
- } catch {
396
- return null
397
- }
398
- }
399
- }
400
-
401
- module.exports = new GitIntegration()