prjct-cli 0.15.1 → 0.18.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 (72) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/bin/dev.js +0 -1
  3. package/bin/serve.js +19 -20
  4. package/core/__tests__/agentic/memory-system.test.ts +2 -1
  5. package/core/__tests__/agentic/plan-mode.test.ts +2 -1
  6. package/core/agentic/agent-router.ts +79 -14
  7. package/core/agentic/command-executor/command-executor.ts +2 -74
  8. package/core/agentic/services.ts +0 -48
  9. package/core/agentic/template-loader.ts +35 -1
  10. package/core/command-registry/setup-commands.ts +15 -0
  11. package/core/commands/base.ts +96 -77
  12. package/core/commands/planning.ts +13 -2
  13. package/core/commands/setup.ts +3 -85
  14. package/core/domain/agent-generator.ts +9 -17
  15. package/core/errors.ts +209 -0
  16. package/core/infrastructure/config-manager.ts +22 -5
  17. package/core/infrastructure/path-manager.ts +23 -1
  18. package/core/infrastructure/setup.ts +5 -50
  19. package/core/storage/ideas-storage.ts +4 -0
  20. package/core/storage/queue-storage.ts +4 -0
  21. package/core/storage/shipped-storage.ts +4 -0
  22. package/core/storage/state-storage.ts +4 -0
  23. package/core/storage/storage-manager.ts +52 -13
  24. package/core/sync/auth-config.ts +145 -0
  25. package/core/sync/index.ts +30 -0
  26. package/core/sync/oauth-handler.ts +148 -0
  27. package/core/sync/sync-client.ts +252 -0
  28. package/core/sync/sync-manager.ts +358 -0
  29. package/core/utils/logger.ts +19 -12
  30. package/package.json +2 -4
  31. package/templates/agentic/subagent-generation.md +109 -0
  32. package/templates/commands/auth.md +234 -0
  33. package/templates/commands/sync.md +129 -13
  34. package/templates/subagents/domain/backend.md +105 -0
  35. package/templates/subagents/domain/database.md +118 -0
  36. package/templates/subagents/domain/devops.md +148 -0
  37. package/templates/subagents/domain/frontend.md +99 -0
  38. package/templates/subagents/domain/testing.md +169 -0
  39. package/templates/subagents/workflow/prjct-planner.md +158 -0
  40. package/templates/subagents/workflow/prjct-shipper.md +179 -0
  41. package/templates/subagents/workflow/prjct-workflow.md +98 -0
  42. package/bin/generate-views.js +0 -209
  43. package/bin/migrate-to-json.js +0 -742
  44. package/core/agentic/context-filter.ts +0 -365
  45. package/core/agentic/parallel-tools.ts +0 -165
  46. package/core/agentic/response-templates.ts +0 -164
  47. package/core/agentic/semantic-compression.ts +0 -273
  48. package/core/agentic/think-blocks.ts +0 -202
  49. package/core/agentic/validation-rules.ts +0 -313
  50. package/core/domain/agent-matcher.ts +0 -130
  51. package/core/domain/agent-validator.ts +0 -250
  52. package/core/domain/architect-session.ts +0 -315
  53. package/core/domain/product-standards.ts +0 -106
  54. package/core/domain/smart-cache.ts +0 -167
  55. package/core/domain/task-analyzer.ts +0 -296
  56. package/core/infrastructure/legacy-installer-detector/cleanup.ts +0 -216
  57. package/core/infrastructure/legacy-installer-detector/detection.ts +0 -95
  58. package/core/infrastructure/legacy-installer-detector/index.ts +0 -171
  59. package/core/infrastructure/legacy-installer-detector/migration.ts +0 -87
  60. package/core/infrastructure/legacy-installer-detector/types.ts +0 -42
  61. package/core/infrastructure/legacy-installer-detector.ts +0 -7
  62. package/core/infrastructure/migrator/file-operations.ts +0 -125
  63. package/core/infrastructure/migrator/index.ts +0 -288
  64. package/core/infrastructure/migrator/project-scanner.ts +0 -90
  65. package/core/infrastructure/migrator/reports.ts +0 -117
  66. package/core/infrastructure/migrator/types.ts +0 -124
  67. package/core/infrastructure/migrator/validation.ts +0 -94
  68. package/core/infrastructure/migrator/version-migration.ts +0 -117
  69. package/core/infrastructure/migrator.ts +0 -10
  70. package/core/infrastructure/uuid-migration.ts +0 -750
  71. package/templates/commands/migrate-all.md +0 -96
  72. package/templates/commands/migrate.md +0 -140
@@ -1,315 +0,0 @@
1
- /**
2
- * Architect Session Manager
3
- * Handles conversational state for ARCHITECT MODE (Agent-based, not deterministic)
4
- */
5
-
6
- import fs from 'fs/promises'
7
- import path from 'path'
8
-
9
- interface ConversationEntry {
10
- question: string
11
- answer: string
12
- timestamp: string
13
- }
14
-
15
- interface ArchitectSessionData {
16
- idea: string
17
- projectType: string
18
- active: boolean
19
- startedAt: string
20
- completedAt?: string
21
- conversation: ConversationEntry[]
22
- answers: Record<string, string>
23
- }
24
-
25
- interface SessionSummary {
26
- idea: string
27
- projectType: string
28
- conversationLength: number
29
- insights: number
30
- startedAt: string
31
- completedAt: string
32
- }
33
-
34
- class ArchitectSession {
35
- /**
36
- * Initialize new architect session
37
- */
38
- async init(idea: string, projectType: string, globalPath: string): Promise<ArchitectSessionData> {
39
- const session: ArchitectSessionData = {
40
- idea,
41
- projectType,
42
- active: true,
43
- startedAt: new Date().toISOString(),
44
- conversation: [],
45
- answers: {},
46
- }
47
-
48
- await this.save(session, globalPath)
49
- return session
50
- }
51
-
52
- /**
53
- * Load active session
54
- */
55
- async load(globalPath: string): Promise<ArchitectSessionData | null> {
56
- try {
57
- const sessionPath = path.join(globalPath, 'planning', 'architect-session.json')
58
- const content = await fs.readFile(sessionPath, 'utf-8')
59
- return JSON.parse(content)
60
- } catch {
61
- return null
62
- }
63
- }
64
-
65
- /**
66
- * Save session to disk
67
- */
68
- async save(session: ArchitectSessionData, globalPath: string): Promise<void> {
69
- const planningDir = path.join(globalPath, 'planning')
70
- await fs.mkdir(planningDir, { recursive: true })
71
-
72
- const sessionPath = path.join(planningDir, 'architect-session.json')
73
- await fs.writeFile(sessionPath, JSON.stringify(session, null, 2))
74
- }
75
-
76
- /**
77
- * Log a Q&A pair to the conversation
78
- */
79
- async logQA(question: string, answer: string, globalPath: string): Promise<void> {
80
- const session = await this.load(globalPath)
81
-
82
- if (!session || !session.active) {
83
- throw new Error('No active architect session')
84
- }
85
-
86
- session.conversation.push({
87
- question,
88
- answer,
89
- timestamp: new Date().toISOString(),
90
- })
91
-
92
- await this.save(session, globalPath)
93
- }
94
-
95
- /**
96
- * Save key insight for plan generation
97
- */
98
- async saveInsight(key: string, value: string, globalPath: string): Promise<void> {
99
- const session = await this.load(globalPath)
100
-
101
- if (!session || !session.active) {
102
- throw new Error('No active architect session')
103
- }
104
-
105
- session.answers[key] = value
106
- await this.save(session, globalPath)
107
- }
108
-
109
- /**
110
- * Complete session and generate plan
111
- */
112
- async complete(globalPath: string): Promise<SessionSummary> {
113
- const session = await this.load(globalPath)
114
-
115
- if (!session || !session.active) {
116
- throw new Error('No active architect session')
117
- }
118
-
119
- // Generate plan MD
120
- await this.generatePlan(session, globalPath)
121
-
122
- // Mark session as complete
123
- session.active = false
124
- session.completedAt = new Date().toISOString()
125
- await this.save(session, globalPath)
126
-
127
- return this.buildSummary(session)
128
- }
129
-
130
- /**
131
- * Generate architect plan MD file
132
- */
133
- async generatePlan(session: ArchitectSessionData, globalPath: string): Promise<void> {
134
- const plan = this.buildPlanMarkdown(session)
135
-
136
- const planPath = path.join(globalPath, 'planning', 'architect-session.md')
137
- await fs.writeFile(planPath, plan)
138
- }
139
-
140
- /**
141
- * Build plan markdown content
142
- */
143
- buildPlanMarkdown(session: ArchitectSessionData): string {
144
- const { projectType, idea, conversation, answers } = session
145
-
146
- // Build conversation log
147
- const conversationLog = conversation
148
- .map((qa, i) => `### Q${i + 1}: ${qa.question}\n**A**: ${qa.answer}\n\n_${qa.timestamp}_`)
149
- .join('\n\n')
150
-
151
- // Build stack summary from answers
152
- const stackSummary = this.buildStackSummary(answers)
153
-
154
- // Build Context7 queries
155
- const context7Queries = this.buildContext7Queries(answers)
156
-
157
- // Build implementation steps
158
- const steps = this.buildImplementationSteps(session)
159
-
160
- return `# ARCHITECT SESSION: ${idea}
161
-
162
- ## Project Idea
163
- ${idea}
164
-
165
- ## Project Type
166
- ${projectType}
167
-
168
- ## Discovery Conversation
169
-
170
- ${conversationLog}
171
-
172
- ## Architecture Summary
173
-
174
- **Stack:**
175
- ${stackSummary}
176
-
177
- ## Implementation Plan
178
-
179
- **Context7 Queries:**
180
- ${context7Queries.map((q) => `- "${q}"`).join('\n')}
181
-
182
- **Implementation Steps:**
183
- ${steps.map((step, i) => `${i + 1}. ${step}`).join('\n')}
184
-
185
- ## Execution
186
-
187
- This plan is ready to be executed.
188
-
189
- **To generate code:**
190
- \`\`\`
191
- /p:architect execute
192
- \`\`\`
193
-
194
- The agent will:
195
- 1. Read this architectural plan
196
- 2. Use Context7 to fetch official documentation
197
- 3. Generate project structure following best practices
198
- 4. Create starter files with boilerplate code
199
-
200
- ---
201
- Generated: ${new Date().toISOString()}
202
- `
203
- }
204
-
205
- /**
206
- * Build stack summary from answers
207
- */
208
- buildStackSummary(answers: Record<string, string>): string {
209
- const parts: string[] = []
210
-
211
- for (const [key, value] of Object.entries(answers)) {
212
- if (value && value !== 'Ninguna' && value !== 'None' && value !== 'Otro') {
213
- parts.push(`- **${key}**: ${value}`)
214
- }
215
- }
216
-
217
- return parts.length > 0 ? parts.join('\n') : '- To be determined during implementation'
218
- }
219
-
220
- /**
221
- * Build Context7 queries from answers
222
- */
223
- buildContext7Queries(answers: Record<string, string>): string[] {
224
- const queries: string[] = []
225
-
226
- if (answers.framework) {
227
- queries.push(`${answers.framework} getting started`)
228
- queries.push(`${answers.framework} project structure`)
229
- }
230
-
231
- if (answers.language && answers.framework) {
232
- queries.push(`${answers.language} ${answers.framework} best practices`)
233
- }
234
-
235
- if (answers.database && answers.language) {
236
- queries.push(`${answers.database} ${answers.language} integration`)
237
- }
238
-
239
- if (answers.auth) {
240
- queries.push(`${answers.auth} implementation ${answers.language || ''}`.trim())
241
- }
242
-
243
- // Always include general queries if specific ones not available
244
- if (queries.length === 0 && answers.language) {
245
- queries.push(`${answers.language} project structure`)
246
- queries.push(`${answers.language} best practices`)
247
- }
248
-
249
- return queries
250
- }
251
-
252
- /**
253
- * Build implementation steps from session
254
- */
255
- buildImplementationSteps(session: ArchitectSessionData): string[] {
256
- const { answers } = session
257
- const steps: string[] = []
258
-
259
- // Generic steps - Claude will refine during execution
260
- if (answers.language) {
261
- steps.push(`Initialize ${answers.language} project`)
262
- }
263
-
264
- if (answers.framework) {
265
- steps.push(`Setup ${answers.framework}`)
266
- }
267
-
268
- if (answers.database) {
269
- steps.push(`Configure ${answers.database}`)
270
- }
271
-
272
- if (answers.auth) {
273
- steps.push(`Implement ${answers.auth}`)
274
- }
275
-
276
- steps.push('Create project structure')
277
- steps.push('Generate starter files')
278
-
279
- if (answers.deployment) {
280
- steps.push(`Setup ${answers.deployment} configuration`)
281
- }
282
-
283
- return steps
284
- }
285
-
286
- /**
287
- * Build summary of session
288
- */
289
- buildSummary(session: ArchitectSessionData): SessionSummary {
290
- return {
291
- idea: session.idea,
292
- projectType: session.projectType,
293
- conversationLength: session.conversation.length,
294
- insights: Object.keys(session.answers).length,
295
- startedAt: session.startedAt,
296
- completedAt: session.completedAt || new Date().toISOString(),
297
- }
298
- }
299
-
300
- /**
301
- * Clear architect session
302
- */
303
- async clear(globalPath: string): Promise<void> {
304
- try {
305
- const sessionPath = path.join(globalPath, 'planning', 'architect-session.json')
306
- await fs.unlink(sessionPath)
307
- } catch {
308
- // Ignore if doesn't exist
309
- }
310
- }
311
- }
312
-
313
- const architectSession = new ArchitectSession()
314
- export default architectSession
315
- export { ArchitectSession }
@@ -1,106 +0,0 @@
1
- /**
2
- * Modern Product Standards
3
- * Defines what "Good" looks like for each domain.
4
- * Used to inject high standards into agent prompts.
5
- */
6
-
7
- interface DomainStandard {
8
- title: string
9
- rules: string[]
10
- }
11
-
12
- interface Standards {
13
- title: string
14
- rules: string[]
15
- }
16
-
17
- interface ProductStandardsType {
18
- general: string[]
19
- domains: Record<string, DomainStandard>
20
- getStandards(domain?: string): Standards
21
- }
22
-
23
- const ProductStandards: ProductStandardsType = {
24
- // General standards applicable to all agents
25
- general: [
26
- 'SHIP IT: Bias for action. Better to ship and iterate than perfect and delay.',
27
- 'USER CENTRIC: Always ask "How does this help the user?"',
28
- 'CLEAN CODE: Write code that is easy to read, test, and maintain.',
29
- 'NO BS: Avoid over-engineering. Simple is better than complex.',
30
- ],
31
-
32
- // Domain-specific standards
33
- domains: {
34
- frontend: {
35
- title: 'Modern Frontend Standards',
36
- rules: [
37
- 'PERFORMANCE: Core Web Vitals matter. Optimize LCP, CLS, FID.',
38
- 'ACCESSIBILITY: Semantic HTML, ARIA labels, keyboard navigation (WCAG 2.1 AA).',
39
- 'RESPONSIVE: Mobile-first design. Works on all devices.',
40
- 'UX/UI: Smooth transitions, loading states, error boundaries. No dead clicks.',
41
- 'STATE: Local state for UI, Global state (Context/Zustand) for data. No prop drilling.',
42
- ],
43
- },
44
- backend: {
45
- title: 'Robust Backend Standards',
46
- rules: [
47
- 'SECURITY: Validate ALL inputs. Sanitize outputs. OWASP Top 10 awareness.',
48
- 'SCALABILITY: Stateless services. Caching strategies (Redis/CDN).',
49
- 'RELIABILITY: Graceful error handling. Structured logging. Health checks.',
50
- 'API DESIGN: RESTful or GraphQL best practices. Consistent response envelopes.',
51
- 'DB: Indexed queries. Migrations for schema changes. No N+1 queries.',
52
- ],
53
- },
54
- database: {
55
- title: 'Data Integrity Standards',
56
- rules: [
57
- 'INTEGRITY: Foreign keys, constraints, transactions.',
58
- 'PERFORMANCE: Index usage analysis. Query optimization.',
59
- 'BACKUPS: Point-in-time recovery awareness.',
60
- 'MIGRATIONS: Idempotent scripts. Zero-downtime changes.',
61
- ],
62
- },
63
- devops: {
64
- title: 'Modern Ops Standards',
65
- rules: [
66
- 'AUTOMATION: CI/CD for everything. No manual deployments.',
67
- 'IaC: Infrastructure as Code (Terraform/Pulumi).',
68
- 'OBSERVABILITY: Metrics, Logs, Traces (OpenTelemetry).',
69
- 'SECURITY: Least privilege access. Secrets management.',
70
- ],
71
- },
72
- qa: {
73
- title: 'Quality Assurance Standards',
74
- rules: [
75
- 'PYRAMID: Many unit tests, some integration, few E2E.',
76
- 'COVERAGE: Critical paths must be tested.',
77
- 'REALISM: Test with realistic data and scenarios.',
78
- 'SPEED: Fast feedback loops. Parallel execution.',
79
- ],
80
- },
81
- architecture: {
82
- title: 'System Architecture Standards',
83
- rules: [
84
- 'MODULARITY: High cohesion, low coupling.',
85
- 'EVOLVABILITY: Easy to change. Hard to break.',
86
- 'SIMPLICITY: Choose boring technology. Innovation tokens are limited.',
87
- 'DOCS: Architecture Decision Records (ADRs).',
88
- ],
89
- },
90
- },
91
-
92
- /**
93
- * Get standards for a specific domain
94
- */
95
- getStandards(domain?: string): Standards {
96
- const key = domain?.toLowerCase()
97
- const specific = (key && this.domains[key]) || { title: 'General Standards', rules: [] }
98
-
99
- return {
100
- title: specific.title,
101
- rules: [...this.general, ...specific.rules],
102
- }
103
- },
104
- }
105
-
106
- export default ProductStandards
@@ -1,167 +0,0 @@
1
- /**
2
- * SmartCache - Intelligent Persistent Cache for Agents
3
- *
4
- * Cache with specific keys: {projectId}-{domain}-{techStack}
5
- * Persists to disk for cross-session caching
6
- * Invalidates only when stack changes
7
- *
8
- * @version 1.0.0
9
- */
10
-
11
- import fs from 'fs/promises'
12
- import path from 'path'
13
- import os from 'os'
14
- import crypto from 'crypto'
15
- import log from '../utils/logger'
16
-
17
- interface CacheStats {
18
- size: number
19
- keys: string[]
20
- }
21
-
22
- class SmartCache {
23
- projectId: string | null
24
- memoryCache: Map<string, unknown>
25
- cacheDir: string
26
- cacheFile: string
27
-
28
- constructor(projectId: string | null = null) {
29
- this.projectId = projectId
30
- this.memoryCache = new Map()
31
- this.cacheDir = path.join(os.homedir(), '.prjct-cli', 'cache')
32
- this.cacheFile = projectId
33
- ? path.join(this.cacheDir, `agents-${projectId}.json`)
34
- : path.join(this.cacheDir, 'agents-global.json')
35
- }
36
-
37
- /**
38
- * Initialize cache - load from disk
39
- */
40
- async initialize(): Promise<void> {
41
- try {
42
- await fs.mkdir(this.cacheDir, { recursive: true })
43
- await this.loadFromDisk()
44
- } catch {
45
- // Cache file doesn't exist yet - that's ok
46
- this.memoryCache = new Map()
47
- }
48
- }
49
-
50
- /**
51
- * Generate cache key
52
- * Format: {projectId}-{domain}-{techStackHash}
53
- */
54
- generateKey(projectId: string | null, domain: string, techStack: Record<string, unknown> = {}): string {
55
- const techString = JSON.stringify(techStack)
56
- const techHash = crypto.createHash('md5').update(techString).digest('hex').substring(0, 8)
57
- return `${projectId || 'global'}-${domain}-${techHash}`
58
- }
59
-
60
- /**
61
- * Get from cache
62
- */
63
- async get<T = unknown>(key: string): Promise<T | null> {
64
- // Check memory first
65
- if (this.memoryCache.has(key)) {
66
- return this.memoryCache.get(key) as T
67
- }
68
-
69
- // Load from disk if not in memory
70
- await this.loadFromDisk()
71
- return (this.memoryCache.get(key) as T) || null
72
- }
73
-
74
- /**
75
- * Set in cache
76
- */
77
- async set(key: string, value: unknown): Promise<void> {
78
- // Set in memory
79
- this.memoryCache.set(key, value)
80
-
81
- // Persist to disk (async, don't wait)
82
- this.persistToDisk().catch((err) => {
83
- log.error('Cache persist error:', err.message)
84
- })
85
- }
86
-
87
- /**
88
- * Check if key exists
89
- */
90
- async has(key: string): Promise<boolean> {
91
- if (this.memoryCache.has(key)) {
92
- return true
93
- }
94
-
95
- await this.loadFromDisk()
96
- return this.memoryCache.has(key)
97
- }
98
-
99
- /**
100
- * Clear cache
101
- */
102
- async clear(): Promise<void> {
103
- this.memoryCache.clear()
104
- try {
105
- await fs.unlink(this.cacheFile)
106
- } catch {
107
- // File doesn't exist - that's ok
108
- }
109
- }
110
-
111
- /**
112
- * Invalidate cache for a project (when stack changes)
113
- */
114
- async invalidateProject(projectId: string): Promise<void> {
115
- const keysToDelete: string[] = []
116
- for (const key of this.memoryCache.keys()) {
117
- if (key.startsWith(`${projectId}-`)) {
118
- keysToDelete.push(key)
119
- }
120
- }
121
-
122
- keysToDelete.forEach((key) => this.memoryCache.delete(key))
123
- await this.persistToDisk()
124
- }
125
-
126
- /**
127
- * Load cache from disk
128
- */
129
- async loadFromDisk(): Promise<void> {
130
- try {
131
- const content = await fs.readFile(this.cacheFile, 'utf-8')
132
- const data = JSON.parse(content) as Record<string, unknown>
133
-
134
- // Restore to memory cache
135
- for (const [key, value] of Object.entries(data)) {
136
- this.memoryCache.set(key, value)
137
- }
138
- } catch {
139
- // File doesn't exist or invalid - start fresh
140
- this.memoryCache = new Map()
141
- }
142
- }
143
-
144
- /**
145
- * Persist cache to disk
146
- */
147
- async persistToDisk(): Promise<void> {
148
- try {
149
- const data = Object.fromEntries(this.memoryCache)
150
- await fs.writeFile(this.cacheFile, JSON.stringify(data, null, 2), 'utf-8')
151
- } catch {
152
- // Fail silently - cache is best effort
153
- }
154
- }
155
-
156
- /**
157
- * Get cache statistics
158
- */
159
- getStats(): CacheStats {
160
- return {
161
- size: this.memoryCache.size,
162
- keys: Array.from(this.memoryCache.keys()),
163
- }
164
- }
165
- }
166
-
167
- export default SmartCache