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
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Sync Client - HTTP client for prjct API
3
+ *
4
+ * Handles communication with the backend API for push/pull operations.
5
+ * Uses native fetch API (available in Node 18+ and Bun).
6
+ */
7
+
8
+ import authConfig from './auth-config'
9
+ import type { SyncEvent } from '../events'
10
+
11
+ // ============================================
12
+ // Types
13
+ // ============================================
14
+
15
+ export interface SyncBatchResult {
16
+ success: boolean
17
+ processed: number
18
+ errors: Array<{ index: number; error: string }>
19
+ syncedAt: string
20
+ }
21
+
22
+ export interface SyncPullResult {
23
+ events: Array<{
24
+ type: string
25
+ path: string[]
26
+ data: unknown
27
+ timestamp: string
28
+ }>
29
+ syncedAt: string
30
+ }
31
+
32
+ export interface SyncStatus {
33
+ projectId: string
34
+ lastSync: string | null
35
+ pendingCount: number
36
+ hasConflicts: boolean
37
+ }
38
+
39
+ export interface SyncClientError {
40
+ code: 'AUTH_REQUIRED' | 'NETWORK_ERROR' | 'API_ERROR' | 'UNKNOWN'
41
+ message: string
42
+ status?: number
43
+ }
44
+
45
+ // ============================================
46
+ // Sync Client
47
+ // ============================================
48
+
49
+ class SyncClient {
50
+ private retryConfig = {
51
+ maxRetries: 3,
52
+ baseDelayMs: 1000,
53
+ maxDelayMs: 30000,
54
+ }
55
+
56
+ /**
57
+ * Push local events to the server
58
+ */
59
+ async pushEvents(projectId: string, events: SyncEvent[]): Promise<SyncBatchResult> {
60
+ const { apiUrl, apiKey } = await this.getAuthHeaders()
61
+
62
+ if (!apiKey) {
63
+ throw this.createError('AUTH_REQUIRED', 'No API key configured')
64
+ }
65
+
66
+ const response = await this.fetchWithRetry(`${apiUrl}/sync/batch`, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ 'X-Api-Key': apiKey,
71
+ },
72
+ body: JSON.stringify({
73
+ projectId,
74
+ events: events.map((e) => ({
75
+ type: e.type,
76
+ path: e.path,
77
+ data: e.data,
78
+ timestamp: e.timestamp,
79
+ })),
80
+ }),
81
+ })
82
+
83
+ if (!response.ok) {
84
+ const error = await this.parseErrorResponse(response)
85
+ throw error
86
+ }
87
+
88
+ return (await response.json()) as SyncBatchResult
89
+ }
90
+
91
+ /**
92
+ * Pull events from the server since a timestamp
93
+ */
94
+ async pullEvents(projectId: string, since?: string): Promise<SyncPullResult> {
95
+ const { apiUrl, apiKey } = await this.getAuthHeaders()
96
+
97
+ if (!apiKey) {
98
+ throw this.createError('AUTH_REQUIRED', 'No API key configured')
99
+ }
100
+
101
+ const response = await this.fetchWithRetry(`${apiUrl}/sync/pull`, {
102
+ method: 'POST',
103
+ headers: {
104
+ 'Content-Type': 'application/json',
105
+ 'X-Api-Key': apiKey,
106
+ },
107
+ body: JSON.stringify({
108
+ projectId,
109
+ since,
110
+ }),
111
+ })
112
+
113
+ if (!response.ok) {
114
+ const error = await this.parseErrorResponse(response)
115
+ throw error
116
+ }
117
+
118
+ return (await response.json()) as SyncPullResult
119
+ }
120
+
121
+ /**
122
+ * Get sync status for a project
123
+ */
124
+ async getStatus(projectId: string): Promise<SyncStatus> {
125
+ const { apiUrl, apiKey } = await this.getAuthHeaders()
126
+
127
+ if (!apiKey) {
128
+ throw this.createError('AUTH_REQUIRED', 'No API key configured')
129
+ }
130
+
131
+ const response = await this.fetchWithRetry(`${apiUrl}/sync/status/${projectId}`, {
132
+ method: 'GET',
133
+ headers: {
134
+ 'X-Api-Key': apiKey,
135
+ },
136
+ })
137
+
138
+ if (!response.ok) {
139
+ const error = await this.parseErrorResponse(response)
140
+ throw error
141
+ }
142
+
143
+ return (await response.json()) as SyncStatus
144
+ }
145
+
146
+ /**
147
+ * Test connection to the API
148
+ */
149
+ async testConnection(): Promise<boolean> {
150
+ try {
151
+ const { apiUrl, apiKey } = await this.getAuthHeaders()
152
+
153
+ if (!apiKey) {
154
+ return false
155
+ }
156
+
157
+ const response = await fetch(`${apiUrl}/health`, {
158
+ method: 'GET',
159
+ headers: {
160
+ 'X-Api-Key': apiKey,
161
+ },
162
+ })
163
+
164
+ return response.ok
165
+ } catch {
166
+ return false
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Check if we have valid authentication
172
+ */
173
+ async hasAuth(): Promise<boolean> {
174
+ return await authConfig.hasAuth()
175
+ }
176
+
177
+ // ============================================
178
+ // Private helpers
179
+ // ============================================
180
+
181
+ private async getAuthHeaders(): Promise<{ apiUrl: string; apiKey: string | null }> {
182
+ const [apiUrl, apiKey] = await Promise.all([authConfig.getApiUrl(), authConfig.getApiKey()])
183
+ return { apiUrl, apiKey }
184
+ }
185
+
186
+ private async fetchWithRetry(
187
+ url: string,
188
+ options: RequestInit,
189
+ retryCount = 0
190
+ ): Promise<Response> {
191
+ try {
192
+ const response = await fetch(url, options)
193
+
194
+ // Retry on server errors (5xx) but not client errors (4xx)
195
+ if (response.status >= 500 && retryCount < this.retryConfig.maxRetries) {
196
+ const delay = Math.min(
197
+ this.retryConfig.baseDelayMs * Math.pow(2, retryCount),
198
+ this.retryConfig.maxDelayMs
199
+ )
200
+ await this.sleep(delay)
201
+ return this.fetchWithRetry(url, options, retryCount + 1)
202
+ }
203
+
204
+ return response
205
+ } catch (error) {
206
+ // Retry on network errors
207
+ if (retryCount < this.retryConfig.maxRetries) {
208
+ const delay = Math.min(
209
+ this.retryConfig.baseDelayMs * Math.pow(2, retryCount),
210
+ this.retryConfig.maxDelayMs
211
+ )
212
+ await this.sleep(delay)
213
+ return this.fetchWithRetry(url, options, retryCount + 1)
214
+ }
215
+
216
+ throw this.createError(
217
+ 'NETWORK_ERROR',
218
+ error instanceof Error ? error.message : 'Network request failed'
219
+ )
220
+ }
221
+ }
222
+
223
+ private async parseErrorResponse(response: Response): Promise<SyncClientError> {
224
+ try {
225
+ const body = (await response.json()) as { message?: string; error?: string }
226
+ const message = body.message || body.error || `HTTP ${response.status}`
227
+
228
+ if (response.status === 401 || response.status === 403) {
229
+ return this.createError('AUTH_REQUIRED', message, response.status)
230
+ }
231
+
232
+ return this.createError('API_ERROR', message, response.status)
233
+ } catch {
234
+ return this.createError('API_ERROR', `HTTP ${response.status}`, response.status)
235
+ }
236
+ }
237
+
238
+ private createError(
239
+ code: SyncClientError['code'],
240
+ message: string,
241
+ status?: number
242
+ ): SyncClientError {
243
+ return { code, message, status }
244
+ }
245
+
246
+ private sleep(ms: number): Promise<void> {
247
+ return new Promise((resolve) => setTimeout(resolve, ms))
248
+ }
249
+ }
250
+
251
+ export const syncClient = new SyncClient()
252
+ export default syncClient
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Sync Manager - Orchestrates push/pull operations
3
+ *
4
+ * Main entry point for sync operations.
5
+ * Handles the coordination between local storage (EventBus) and remote API (SyncClient).
6
+ */
7
+
8
+ import { syncClient, type SyncBatchResult, type SyncPullResult, type SyncStatus } from './sync-client'
9
+ import authConfig from './auth-config'
10
+ import eventBus, { type SyncEvent } from '../events'
11
+ import { stateStorage } from '../storage/state-storage'
12
+ import { queueStorage } from '../storage/queue-storage'
13
+ import { ideasStorage, type IdeaPriority } from '../storage/ideas-storage'
14
+ import { shippedStorage } from '../storage/shipped-storage'
15
+ import type { TaskType, Priority, TaskSection } from '../schemas/state'
16
+
17
+ // ============================================
18
+ // Types
19
+ // ============================================
20
+
21
+ export interface SyncResult {
22
+ success: boolean
23
+ skipped: boolean
24
+ reason?: 'no_auth' | 'no_pending' | 'error'
25
+ pushed?: {
26
+ count: number
27
+ syncedAt: string
28
+ }
29
+ pulled?: {
30
+ count: number
31
+ syncedAt: string
32
+ }
33
+ error?: string
34
+ }
35
+
36
+ export interface PushResult {
37
+ success: boolean
38
+ skipped: boolean
39
+ reason?: 'no_auth' | 'no_pending' | 'error'
40
+ count?: number
41
+ syncedAt?: string
42
+ error?: string
43
+ }
44
+
45
+ export interface PullResult {
46
+ success: boolean
47
+ skipped: boolean
48
+ reason?: 'no_auth' | 'error'
49
+ count?: number
50
+ applied?: number
51
+ syncedAt?: string
52
+ error?: string
53
+ }
54
+
55
+ // ============================================
56
+ // Sync Manager
57
+ // ============================================
58
+
59
+ class SyncManager {
60
+ /**
61
+ * Check if user is authenticated
62
+ */
63
+ async hasAuth(): Promise<boolean> {
64
+ return await authConfig.hasAuth()
65
+ }
66
+
67
+ /**
68
+ * Get sync status from API
69
+ */
70
+ async getStatus(projectId: string): Promise<SyncStatus | null> {
71
+ if (!(await this.hasAuth())) {
72
+ return null
73
+ }
74
+
75
+ try {
76
+ return await syncClient.getStatus(projectId)
77
+ } catch {
78
+ return null
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Full sync: push local changes, then pull remote changes
84
+ */
85
+ async sync(projectId: string): Promise<SyncResult> {
86
+ // Check auth first
87
+ if (!(await this.hasAuth())) {
88
+ return { success: true, skipped: true, reason: 'no_auth' }
89
+ }
90
+
91
+ const result: SyncResult = { success: true, skipped: false }
92
+
93
+ // Push first
94
+ const pushResult = await this.push(projectId)
95
+ if (pushResult.success && !pushResult.skipped) {
96
+ result.pushed = {
97
+ count: pushResult.count || 0,
98
+ syncedAt: pushResult.syncedAt || new Date().toISOString(),
99
+ }
100
+ }
101
+
102
+ // Then pull
103
+ const pullResult = await this.pull(projectId)
104
+ if (pullResult.success && !pullResult.skipped) {
105
+ result.pulled = {
106
+ count: pullResult.count || 0,
107
+ syncedAt: pullResult.syncedAt || new Date().toISOString(),
108
+ }
109
+ }
110
+
111
+ // Determine overall success
112
+ if (!pushResult.success || !pullResult.success) {
113
+ result.success = false
114
+ result.error = pushResult.error || pullResult.error
115
+ }
116
+
117
+ return result
118
+ }
119
+
120
+ /**
121
+ * Push local pending events to the server
122
+ */
123
+ async push(projectId: string): Promise<PushResult> {
124
+ // Check auth first
125
+ if (!(await this.hasAuth())) {
126
+ return { success: true, skipped: true, reason: 'no_auth' }
127
+ }
128
+
129
+ try {
130
+ // Get pending events
131
+ const pending = await eventBus.getPending(projectId)
132
+
133
+ if (pending.length === 0) {
134
+ return { success: true, skipped: true, reason: 'no_pending' }
135
+ }
136
+
137
+ // Push to server
138
+ const result: SyncBatchResult = await syncClient.pushEvents(projectId, pending)
139
+
140
+ if (result.success) {
141
+ // Clear pending events on success
142
+ await eventBus.clearPending(projectId)
143
+ await eventBus.updateLastSync(projectId)
144
+
145
+ return {
146
+ success: true,
147
+ skipped: false,
148
+ count: result.processed,
149
+ syncedAt: result.syncedAt,
150
+ }
151
+ } else {
152
+ // Partial success - some events failed
153
+ const successCount = result.processed
154
+ const errorCount = result.errors.length
155
+ const errorMessages = result.errors.map((e) => e.error).join(', ')
156
+
157
+ return {
158
+ success: false,
159
+ skipped: false,
160
+ count: successCount,
161
+ syncedAt: result.syncedAt,
162
+ error: `${errorCount} events failed: ${errorMessages}`,
163
+ }
164
+ }
165
+ } catch (error) {
166
+ const message = error instanceof Error ? error.message : 'Unknown error'
167
+ return {
168
+ success: false,
169
+ skipped: false,
170
+ reason: 'error',
171
+ error: message,
172
+ }
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Pull remote changes from the server
178
+ */
179
+ async pull(projectId: string): Promise<PullResult> {
180
+ // Check auth first
181
+ if (!(await this.hasAuth())) {
182
+ return { success: true, skipped: true, reason: 'no_auth' }
183
+ }
184
+
185
+ try {
186
+ // Get last sync timestamp
187
+ const lastSync = await eventBus.getLastSync(projectId)
188
+ const since = lastSync?.timestamp
189
+
190
+ // Pull from server
191
+ const result: SyncPullResult = await syncClient.pullEvents(projectId, since)
192
+
193
+ if (result.events.length === 0) {
194
+ return {
195
+ success: true,
196
+ skipped: false,
197
+ count: 0,
198
+ applied: 0,
199
+ syncedAt: result.syncedAt,
200
+ }
201
+ }
202
+
203
+ // Apply pulled events to local storage
204
+ const applied = await this.applyPulledEvents(projectId, result.events)
205
+
206
+ // Update last sync timestamp
207
+ await eventBus.updateLastSync(projectId)
208
+
209
+ return {
210
+ success: true,
211
+ skipped: false,
212
+ count: result.events.length,
213
+ applied,
214
+ syncedAt: result.syncedAt,
215
+ }
216
+ } catch (error) {
217
+ const message = error instanceof Error ? error.message : 'Unknown error'
218
+ return {
219
+ success: false,
220
+ skipped: false,
221
+ reason: 'error',
222
+ error: message,
223
+ }
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Apply pulled events to local storage
229
+ * Returns number of events successfully applied
230
+ */
231
+ async applyPulledEvents(
232
+ projectId: string,
233
+ events: Array<{ type: string; path: string[]; data: unknown; timestamp: string }>
234
+ ): Promise<number> {
235
+ let applied = 0
236
+
237
+ for (const event of events) {
238
+ try {
239
+ await this.applyEvent(projectId, event)
240
+ applied++
241
+ } catch (error) {
242
+ // Log but continue with other events
243
+ console.error(`Failed to apply event ${event.type}:`, error)
244
+ }
245
+ }
246
+
247
+ return applied
248
+ }
249
+
250
+ /**
251
+ * Apply a single event to local storage
252
+ */
253
+ private async applyEvent(
254
+ projectId: string,
255
+ event: { type: string; path: string[]; data: unknown; timestamp: string }
256
+ ): Promise<void> {
257
+ const [entity, action] = event.type.split('.') as [string, string]
258
+ const data = event.data as Record<string, unknown>
259
+
260
+ switch (entity) {
261
+ case 'task':
262
+ await this.applyTaskEvent(projectId, action, data)
263
+ break
264
+ case 'idea':
265
+ await this.applyIdeaEvent(projectId, action, data)
266
+ break
267
+ case 'shipped':
268
+ await this.applyShippedEvent(projectId, action, data)
269
+ break
270
+ // Add more entity handlers as needed
271
+ }
272
+ }
273
+
274
+ private async applyTaskEvent(
275
+ projectId: string,
276
+ action: string,
277
+ data: Record<string, unknown>
278
+ ): Promise<void> {
279
+ switch (action) {
280
+ case 'started':
281
+ // Update state if this is a newer task
282
+ await stateStorage.update(projectId, (state) => {
283
+ if (!state.currentTask || (data.id as string) !== state.currentTask.id) {
284
+ return {
285
+ ...state,
286
+ currentTask: {
287
+ id: data.id as string,
288
+ description: data.description as string,
289
+ startedAt: data.startedAt as string,
290
+ sessionId: data.sessionId as string,
291
+ },
292
+ }
293
+ }
294
+ return state
295
+ })
296
+ break
297
+ case 'completed':
298
+ // Clear current task if it matches
299
+ await stateStorage.update(projectId, (state) => {
300
+ if (state.currentTask?.id === data.id) {
301
+ return { ...state, currentTask: null }
302
+ }
303
+ return state
304
+ })
305
+ break
306
+ case 'created':
307
+ // Add to queue
308
+ await queueStorage.addTask(projectId, {
309
+ description: data.description as string,
310
+ priority: (data.priority as Priority) || 'medium',
311
+ type: (data.type as TaskType) || 'feature',
312
+ section: 'backlog' as TaskSection,
313
+ })
314
+ break
315
+ }
316
+ }
317
+
318
+ private async applyIdeaEvent(
319
+ projectId: string,
320
+ action: string,
321
+ data: Record<string, unknown>
322
+ ): Promise<void> {
323
+ switch (action) {
324
+ case 'created':
325
+ await ideasStorage.addIdea(
326
+ projectId,
327
+ (data.title as string) || (data.text as string),
328
+ { priority: (data.priority as IdeaPriority) || 'medium' }
329
+ )
330
+ break
331
+ case 'archived':
332
+ await ideasStorage.update(projectId, (ideas) => ({
333
+ ...ideas,
334
+ ideas: ideas.ideas.map((idea) =>
335
+ idea.id === data.id ? { ...idea, status: 'archived' as const } : idea
336
+ ),
337
+ }))
338
+ break
339
+ }
340
+ }
341
+
342
+ private async applyShippedEvent(
343
+ projectId: string,
344
+ action: string,
345
+ data: Record<string, unknown>
346
+ ): Promise<void> {
347
+ if (action === 'created') {
348
+ await shippedStorage.addShipped(projectId, {
349
+ name: (data.name as string) || (data.title as string),
350
+ version: data.version as string,
351
+ description: data.description as string,
352
+ })
353
+ }
354
+ }
355
+ }
356
+
357
+ export const syncManager = new SyncManager()
358
+ export default syncManager
@@ -30,21 +30,28 @@ interface Logger {
30
30
 
31
31
  const LEVELS: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 }
32
32
 
33
- const debugEnv = process.env.PRJCT_DEBUG || process.env.DEBUG || ''
34
- const isEnabled = debugEnv === '1' || debugEnv === 'true' || debugEnv.includes('prjct')
35
-
36
- // Determine log level
37
- let currentLevel = -1 // disabled by default
38
- if (isEnabled) {
39
- if (debugEnv === '1' || debugEnv === 'true' || debugEnv === 'prjct') {
40
- currentLevel = LEVELS.debug // all logs
41
- } else if (LEVELS[debugEnv as LogLevel] !== undefined) {
42
- currentLevel = LEVELS[debugEnv as LogLevel]
43
- } else {
44
- currentLevel = LEVELS.debug
33
+ /**
34
+ * Determine log level from environment variables
35
+ * Returns -1 (disabled) or a level from LEVELS
36
+ */
37
+ function getLogLevel(): number {
38
+ const debugEnv = process.env.PRJCT_DEBUG || process.env.DEBUG || ''
39
+
40
+ // Disabled if empty
41
+ if (!debugEnv) return -1
42
+
43
+ // Enable all logs for common truthy values
44
+ if (debugEnv === '1' || debugEnv === 'true' || debugEnv === 'prjct' || debugEnv.includes('prjct')) {
45
+ return LEVELS.debug
45
46
  }
47
+
48
+ // Check for specific level name (error, warn, info, debug)
49
+ const level = LEVELS[debugEnv as LogLevel]
50
+ return level !== undefined ? level : -1
46
51
  }
47
52
 
53
+ const currentLevel = getLogLevel()
54
+
48
55
  // No-op function for disabled logs
49
56
  const noop: LogFunction = () => {}
50
57
 
package/package.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.15.1",
3
+ "version": "0.18.0",
4
4
  "description": "Built for Claude - Ship fast, track progress, stay focused. Developer momentum tool for indie hackers.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {
7
7
  "prjct": "bin/prjct",
8
8
  "prjct-dev": "bin/dev.js",
9
- "prjct-serve": "bin/serve.js",
10
- "prjct-generate-views": "bin/generate-views.js",
11
- "prjct-migrate": "bin/migrate-to-json.js"
9
+ "prjct-serve": "bin/serve.js"
12
10
  },
13
11
  "workspaces": [
14
12
  "packages/*"