prjct-cli 0.10.8 → 0.10.10

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.
@@ -67,6 +67,7 @@ class ContextBuilder {
67
67
  memory: pathManager.getFilePath(projectId, 'memory', 'context.jsonl'),
68
68
  patterns: pathManager.getFilePath(projectId, 'memory', 'patterns.json'),
69
69
  analysis: pathManager.getFilePath(projectId, 'analysis', 'repo-summary.md'),
70
+ codePatterns: pathManager.getFilePath(projectId, 'analysis', 'patterns.md'),
70
71
  },
71
72
 
72
73
  // Command parameters
@@ -168,10 +169,10 @@ class ContextBuilder {
168
169
  async loadStateForCommand(context, commandName) {
169
170
  // Command-specific file requirements
170
171
  // Minimizes context window usage
171
- // CRITICAL: Always include 'analysis' for pattern detection
172
+ // CRITICAL: Include 'codePatterns' for ALL code-modifying commands
172
173
  const commandFileMap = {
173
174
  // Core workflow
174
- 'now': ['now', 'next', 'analysis'],
175
+ 'now': ['now', 'next', 'analysis', 'codePatterns'],
175
176
  'done': ['now', 'next', 'metrics', 'analysis'],
176
177
  'next': ['next', 'analysis'],
177
178
 
@@ -182,24 +183,24 @@ class ContextBuilder {
182
183
 
183
184
  // Planning
184
185
  'idea': ['ideas', 'next', 'analysis'],
185
- 'feature': ['roadmap', 'next', 'ideas', 'analysis'],
186
+ 'feature': ['roadmap', 'next', 'ideas', 'analysis', 'codePatterns'],
186
187
  'roadmap': ['roadmap', 'analysis'],
187
- 'spec': ['roadmap', 'next', 'specs', 'analysis'],
188
+ 'spec': ['roadmap', 'next', 'specs', 'analysis', 'codePatterns'],
188
189
 
189
190
  // Analysis
190
- 'analyze': ['analysis', 'context'],
191
- 'sync': ['analysis', 'context', 'now'],
192
-
193
- // Code modification commands - ALWAYS need analysis for patterns
194
- 'work': ['now', 'next', 'analysis', 'context'],
195
- 'build': ['now', 'next', 'analysis', 'context'],
196
- 'design': ['analysis', 'context'],
197
- 'cleanup': ['analysis', 'context'],
198
- 'fix': ['analysis', 'context'],
199
- 'test': ['analysis', 'context'],
200
-
201
- // All files (fallback)
202
- 'default': ['analysis'] // Always include analysis even for unknown commands
191
+ 'analyze': ['analysis', 'context', 'codePatterns'],
192
+ 'sync': ['analysis', 'context', 'now', 'codePatterns'],
193
+
194
+ // Code modification commands - ALWAYS need codePatterns
195
+ 'work': ['now', 'next', 'analysis', 'context', 'codePatterns'],
196
+ 'build': ['now', 'next', 'analysis', 'context', 'codePatterns'],
197
+ 'design': ['analysis', 'context', 'codePatterns'],
198
+ 'cleanup': ['analysis', 'context', 'codePatterns'],
199
+ 'fix': ['analysis', 'context', 'codePatterns'],
200
+ 'test': ['analysis', 'context', 'codePatterns'],
201
+
202
+ // All files (fallback) - include codePatterns for any code work
203
+ 'default': ['analysis', 'codePatterns']
203
204
  }
204
205
 
205
206
  const requiredFiles = commandFileMap[commandName] || commandFileMap.default
@@ -24,41 +24,18 @@ class PromptBuilder {
24
24
  // Store context for use in helper methods
25
25
  this._currentContext = context
26
26
 
27
- // Agent assignment (if applicable)
28
- // CRITICAL: Include full agent content, not just name
29
- if (agent) {
30
- parts.push(`# AGENT ASSIGNMENT\n`)
31
- parts.push(`Agent: ${agent.name}\n`)
32
-
33
- // Include role if available
34
- if (agent.role) {
35
- parts.push(`Role: ${agent.role}\n`)
36
- }
37
-
38
- // Include domain if available
39
- if (agent.domain) {
40
- parts.push(`Domain: ${agent.domain}\n`)
41
- }
42
-
43
- // Include skills if available
44
- if (agent.skills && agent.skills.length > 0) {
45
- parts.push(`Skills: ${agent.skills.join(', ')}\n`)
46
- }
47
-
48
- parts.push(`\n## AGENT INSTRUCTIONS\n`)
49
-
50
- // CRITICAL: Include full agent content
51
- // This is the specialized knowledge for this project
52
- if (agent.content) {
53
- parts.push(agent.content)
54
- parts.push(`\n`)
55
- } else if (agent.name) {
56
- // Fallback if content not loaded
57
- parts.push(`You are the ${agent.name} agent for this project.\n`)
58
- parts.push(`Apply your specialized expertise to complete the task.\n\n`)
59
- }
60
-
61
- parts.push(`CONTEXT: ${context.filteredSize || 'all'} files (${context.reduction || 0}% reduced)\n\n`)
27
+ // Agent assignment (CONDITIONAL - only for code-modifying commands)
28
+ // Commands like done, ship, recap, next don't need specialized agents
29
+ const commandName = template.frontmatter?.name?.replace('p:', '') || ''
30
+ const agentCommands = ['now', 'build', 'feature', 'design', 'fix', 'bug', 'test', 'work', 'cleanup', 'spec']
31
+ const needsAgent = agentCommands.includes(commandName)
32
+
33
+ if (agent && needsAgent) {
34
+ // COMPRESSED: Only essential agent info (500 bytes vs 3-5KB)
35
+ parts.push(`# AGENT: ${agent.name}\n`)
36
+ if (agent.role) parts.push(`Role: ${agent.role}\n`)
37
+ if (agent.skills?.length) parts.push(`Skills: ${agent.skills.join(', ')}\n`)
38
+ parts.push(`\nApply specialized expertise. Read agent file for details if needed.\n\n`)
62
39
  }
63
40
 
64
41
  // Core instruction (concise)
@@ -93,31 +70,32 @@ class PromptBuilder {
93
70
  parts.push('\n')
94
71
  }
95
72
 
96
- // CRITICAL: Include available project files for context
97
- if (context.files && context.files.length > 0) {
98
- parts.push('\n## AVAILABLE PROJECT FILES\n')
99
- parts.push(`You have ${context.files.length} relevant files available in this project.\n`)
100
- parts.push('**USE Read TOOL TO LOAD FILES BEFORE MODIFYING THEM.**\n')
101
- parts.push('\nTop relevant files:\n')
102
- const topFiles = context.files.slice(0, 20).map(f => `- ${f}`).join('\n')
103
- parts.push(topFiles)
104
- if (context.files.length > 20) {
105
- parts.push(`\n... and ${context.files.length - 20} more files. Use Read tool to access them.\n`)
106
- }
107
- parts.push('\n')
73
+ // COMPRESSED: File list (5 files vs 20, saves ~400 bytes)
74
+ if (context.files?.length > 0) {
75
+ const top5 = context.files.slice(0, 5).join(', ')
76
+ parts.push(`\n## FILES: ${context.files.length} available. Top: ${top5}\n`)
77
+ parts.push('Read BEFORE modifying. Use Glob/Grep to find more.\n\n')
108
78
  } else if (context.projectPath) {
109
- parts.push('\n## PROJECT FILES\n')
110
- parts.push(`Project path: ${context.projectPath}\n`)
111
- parts.push('**USE Read TOOL TO LOAD FILES YOU NEED TO UNDERSTAND OR MODIFY.**\n')
112
- parts.push('**NEVER MODIFY CODE WITHOUT READING IT FIRST.**\n\n')
79
+ parts.push(`\n## PROJECT: ${context.projectPath}\nRead files before modifying.\n\n`)
113
80
  }
114
81
 
115
82
  // OPTIMIZED: Only include patterns for code-modifying commands
116
- // Commands like now, done, ship, recap, next don't need full patterns
117
- const codeCommands = ['build', 'feature', 'design', 'cleanup', 'fix', 'bug', 'test', 'init']
118
- const commandName = template.frontmatter?.name?.replace('p:', '') || ''
83
+ // Commands like done, ship, recap, next don't need full patterns
84
+ const codeCommands = ['now', 'build', 'feature', 'design', 'cleanup', 'fix', 'bug', 'test', 'init', 'spec', 'work']
119
85
  const needsPatterns = codeCommands.includes(commandName)
120
86
 
87
+ // Include code patterns analysis for code-modifying commands
88
+ // COMPRESSED: Extract only conventions and anti-patterns (800 bytes max vs 6KB)
89
+ const codePatternsContent = state?.codePatterns || ''
90
+ if (needsPatterns && codePatternsContent && codePatternsContent.trim()) {
91
+ const patternSummary = this.extractPatternSummary(codePatternsContent)
92
+ if (patternSummary) {
93
+ parts.push('\n## CODE PATTERNS\n')
94
+ parts.push(patternSummary)
95
+ parts.push('\nFull patterns: Read analysis/patterns.md\n')
96
+ }
97
+ }
98
+
121
99
  const analysisContent = state?.analysis || ''
122
100
  if (needsPatterns && analysisContent && analysisContent.trim()) {
123
101
  // Extract stack info compactly
@@ -125,8 +103,10 @@ class PromptBuilder {
125
103
  analysisContent.match(/Technology[:\s]+([^\n]+)/i)
126
104
  const stack = stackMatch ? stackMatch[1].trim() : 'detected'
127
105
 
128
- parts.push(`\n## PATTERNS\nStack: ${stack}\n`)
129
- parts.push('Read analysis/repo-summary.md + similar files before coding. Match patterns exactly.\n')
106
+ parts.push(`\n## STACK\nStack: ${stack}\n`)
107
+ if (!codePatternsContent) {
108
+ parts.push('Read analysis/repo-summary.md + similar files before coding. Match patterns exactly.\n')
109
+ }
130
110
  }
131
111
 
132
112
  // CRITICAL: Compressed rules (replaces 78 lines with 12)
@@ -190,8 +170,8 @@ class PromptBuilder {
190
170
  const relevant = []
191
171
  for (const [key, content] of Object.entries(state)) {
192
172
  if (content && content.trim()) {
193
- // Include full content for critical files (now, next, context)
194
- const criticalFiles = ['now', 'next', 'context', 'analysis']
173
+ // Include full content for critical files (now, next, context, patterns)
174
+ const criticalFiles = ['now', 'next', 'context', 'analysis', 'codePatterns']
195
175
  if (criticalFiles.includes(key)) {
196
176
  // Include full content for critical files (up to 2000 chars)
197
177
  const display = content.length > 2000
@@ -232,6 +212,38 @@ class PromptBuilder {
232
212
  return parts.join('')
233
213
  }
234
214
 
215
+ /**
216
+ * Extract pattern summary from full patterns content
217
+ * OPTIMIZED: Returns only conventions + high-priority anti-patterns (800 bytes max)
218
+ */
219
+ extractPatternSummary(content) {
220
+ if (!content) return null
221
+
222
+ const parts = []
223
+
224
+ // Extract conventions section
225
+ const conventionsMatch = content.match(/## Conventions[\s\S]*?(?=##|$)/i)
226
+ if (conventionsMatch) {
227
+ // Compress to key lines only
228
+ const conventions = conventionsMatch[0]
229
+ .split('\n')
230
+ .filter(line => line.includes(':') || line.startsWith('-'))
231
+ .slice(0, 6)
232
+ .join('\n')
233
+ if (conventions) parts.push(conventions)
234
+ }
235
+
236
+ // Extract high priority anti-patterns only
237
+ const antiPatternsMatch = content.match(/### High Priority[\s\S]*?(?=###|##|$)/i)
238
+ if (antiPatternsMatch) {
239
+ const antiPatterns = antiPatternsMatch[0].substring(0, 300)
240
+ parts.push('\nAvoid:\n' + antiPatterns)
241
+ }
242
+
243
+ const result = parts.join('\n').substring(0, 800)
244
+ return result || null
245
+ }
246
+
235
247
  /**
236
248
  * Build critical rules - compressed anti-hallucination
237
249
  * OPTIMIZED: From 66 lines to 12 lines (~82% reduction)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.10.8",
3
+ "version": "0.10.10",
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": {
@@ -0,0 +1,60 @@
1
+ ---
2
+ allowed-tools: [Read, Glob, Grep]
3
+ description: 'Analyze code patterns and conventions'
4
+ ---
5
+
6
+ # Code Pattern Analysis
7
+
8
+ ## Detection Steps
9
+
10
+ 1. **Structure** (5-10 files): File org, exports, modules
11
+ 2. **Patterns**: SOLID, DRY, factory/singleton/observer
12
+ 3. **Conventions**: Naming, style, error handling, async
13
+ 4. **Anti-patterns**: God class, spaghetti, copy-paste, magic numbers
14
+ 5. **Performance**: Memoization, N+1 queries, leaks
15
+
16
+ ## Output: analysis/patterns.md
17
+
18
+ ```markdown
19
+ # Code Patterns - {Project}
20
+
21
+ > Generated: {GetTimestamp()}
22
+
23
+ ## Patterns Detected
24
+ - **{Pattern}**: {Where} - {Example}
25
+
26
+ ## SOLID Compliance
27
+ | Principle | Status | Evidence |
28
+ |-----------|--------|----------|
29
+ | Single Responsibility | ✅/⚠️/❌ | {evidence} |
30
+ | Open/Closed | ✅/⚠️/❌ | {evidence} |
31
+ | Liskov Substitution | ✅/⚠️/❌ | {evidence} |
32
+ | Interface Segregation | ✅/⚠️/❌ | {evidence} |
33
+ | Dependency Inversion | ✅/⚠️/❌ | {evidence} |
34
+
35
+ ## Conventions (MUST FOLLOW)
36
+ - Functions: {camelCase/snake_case}
37
+ - Classes: {PascalCase}
38
+ - Files: {kebab-case/camelCase}
39
+ - Quotes: {single/double}
40
+ - Async: {async-await/promises}
41
+
42
+ ## Anti-Patterns ⚠️
43
+
44
+ ### High Priority
45
+ 1. **{Issue}**: {file:line} - Fix: {action}
46
+
47
+ ### Medium Priority
48
+ 1. **{Issue}**: {file:line} - Fix: {action}
49
+
50
+ ## Recommendations
51
+ 1. {Immediate action}
52
+ 2. {Best practice}
53
+ ```
54
+
55
+ ## Rules
56
+
57
+ 1. Check patterns.md FIRST before writing code
58
+ 2. Match conventions exactly
59
+ 3. NEVER introduce anti-patterns
60
+ 4. Warn if asked to violate patterns
@@ -1,23 +1,137 @@
1
1
  ---
2
2
  allowed-tools: [Read, Write]
3
- description: 'Complete task'
4
- think-triggers: [report_complete]
3
+ description: 'Complete current task'
4
+ timestamp-rule: 'GetTimestamp() for all timestamps'
5
5
  ---
6
6
 
7
- # /p:done
7
+ # /p:done - Complete Current Task
8
8
 
9
- ## Think First
10
- Before marking complete, verify:
11
- 1. Is the task actually finished?
12
- 2. Were all acceptance criteria met?
13
- 3. Should this trigger a /p:ship?
9
+ ## Context Variables
10
+ - `{projectId}`: From `.prjct/prjct.config.json`
11
+ - `{globalPath}`: `~/.prjct-cli/projects/{projectId}`
12
+ - `{nowPath}`: `{globalPath}/core/now.md`
13
+ - `{memoryPath}`: `{globalPath}/memory/context.jsonl`
14
+ - `{metricsPath}`: `{globalPath}/progress/metrics.md`
14
15
 
15
- ## Check
16
- Requires: `core/now.md` has content
16
+ ## Step 1: Read Config
17
17
 
18
- ## Flow
19
- 1. Read `core/now.md` → calculate duration
20
- 2. Clear now.md → Update metrics → Log
18
+ READ: `.prjct/prjct.config.json`
19
+ EXTRACT: `projectId`
21
20
 
22
- ## Response
23
- `✅ {task} ({duration}) | Next: /p:now or /p:ship`
21
+ IF file not found:
22
+ OUTPUT: "No prjct project. Run /p:init first."
23
+ STOP
24
+
25
+ ## Step 2: Validate Active Task
26
+
27
+ READ: `{nowPath}`
28
+
29
+ IF empty OR contains "No current task":
30
+ OUTPUT: "⚠️ No active task to complete. Use /p:now to start one."
31
+ STOP
32
+
33
+ ## Step 3: Extract Task Data
34
+
35
+ From NOW file content, extract:
36
+
37
+ 1. **Task name**: Text between `**` markers
38
+ - Pattern: `**(.+?)**`
39
+ - Example: `**implement auth**` → "implement auth"
40
+
41
+ 2. **Start time**: Text after "Started:"
42
+ - Pattern: `Started: (.+)`
43
+ - Example: `Started: 11/28/2025, 2:30:00 PM`
44
+
45
+ 3. **Calculate duration**:
46
+ - Current: GetTimestamp()
47
+ - Duration: current - started
48
+ - Format: "Xh Ym" (e.g., "2h 15m")
49
+ - If < 1 hour: "Xm"
50
+ - If < 1 minute: "< 1m"
51
+
52
+ ## Step 4: Clear Task File
53
+
54
+ WRITE: `{nowPath}`
55
+
56
+ Content (exact):
57
+ ```markdown
58
+ # NOW
59
+
60
+ No current task. Use `/p:now` to set focus.
61
+ ```
62
+
63
+ ## Step 5: Log to Memory
64
+
65
+ APPEND to: `{memoryPath}`
66
+
67
+ Single line (JSONL format):
68
+ ```json
69
+ {"timestamp":"{GetTimestamp()}","action":"task_completed","task":"{task}","duration":"{duration}"}
70
+ ```
71
+
72
+ ## Step 6: Update Metrics (Optional)
73
+
74
+ IF `{metricsPath}` exists:
75
+ READ current content
76
+ APPEND new entry with task and duration
77
+
78
+ ## Output
79
+
80
+ SUCCESS:
81
+ ```
82
+ ✅ {task} ({duration})
83
+
84
+ Next:
85
+ • /p:now - Start next task
86
+ • /p:ship - Ship completed work
87
+ • /p:next - See priority queue
88
+ ```
89
+
90
+ ## Error Handling
91
+
92
+ | Error | Response |
93
+ |-------|----------|
94
+ | Config not found | "No prjct project. Run /p:init first." |
95
+ | Now.md empty | "⚠️ No active task. Use /p:now to start." |
96
+ | Parse fails | Use task = "task", duration = "unknown", continue |
97
+ | Write fails | Log warning, continue (non-critical) |
98
+
99
+ ## Examples
100
+
101
+ ### Example 1: Success
102
+ **now.md content:**
103
+ ```
104
+ # NOW
105
+
106
+ **implement authentication**
107
+
108
+ Started: 11/28/2025, 12:15:00 PM
109
+ ```
110
+
111
+ **Current time:** 11/28/2025, 2:30:00 PM
112
+ **Duration:** 2h 15m
113
+ **Output:** `✅ implement authentication (2h 15m)`
114
+
115
+ ### Example 2: No Task
116
+ **now.md content:**
117
+ ```
118
+ # NOW
119
+
120
+ No current task.
121
+ ```
122
+
123
+ **Output:** `⚠️ No active task to complete. Use /p:now to start one.`
124
+
125
+ ### Example 3: Quick Task
126
+ **now.md content:**
127
+ ```
128
+ # NOW
129
+
130
+ **fix typo in readme**
131
+
132
+ Started: 11/28/2025, 2:25:00 PM
133
+ ```
134
+
135
+ **Current time:** 11/28/2025, 2:30:00 PM
136
+ **Duration:** 5m
137
+ **Output:** `✅ fix typo in readme (5m)`