prjct-cli 0.31.0 → 0.34.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 (121) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/CLAUDE.md +41 -0
  3. package/core/__tests__/agentic/memory-system.test.ts +2 -2
  4. package/core/__tests__/types/fs.test.ts +125 -0
  5. package/core/agentic/agent-router.ts +16 -4
  6. package/core/agentic/chain-of-thought.ts +4 -12
  7. package/core/agentic/command-executor.ts +10 -11
  8. package/core/agentic/context-builder.ts +24 -10
  9. package/core/agentic/ground-truth.ts +139 -55
  10. package/core/agentic/prompt-builder.ts +20 -7
  11. package/core/agentic/smart-context.ts +1 -1
  12. package/core/agentic/template-loader.ts +1 -1
  13. package/core/agentic/tool-registry.ts +4 -2
  14. package/core/bus/bus.ts +1 -1
  15. package/core/commands/cleanup.ts +24 -8
  16. package/core/commands/planning.ts +4 -2
  17. package/core/commands/setup.ts +4 -4
  18. package/core/commands/shipping.ts +34 -8
  19. package/core/commands/snapshots.ts +27 -13
  20. package/core/context/generator.ts +9 -5
  21. package/core/domain/agent-generator.ts +1 -1
  22. package/core/domain/agent-loader.ts +1 -1
  23. package/core/domain/analyzer.ts +76 -31
  24. package/core/domain/context-estimator.ts +1 -1
  25. package/core/domain/snapshot-manager.ts +55 -21
  26. package/core/domain/task-stack.ts +16 -7
  27. package/core/infrastructure/author-detector.ts +1 -1
  28. package/core/infrastructure/claude-agent.ts +12 -8
  29. package/core/infrastructure/command-installer.ts +42 -21
  30. package/core/infrastructure/editors-config.ts +1 -1
  31. package/core/infrastructure/path-manager.ts +27 -2
  32. package/core/infrastructure/permission-manager.ts +1 -1
  33. package/core/infrastructure/setup.ts +31 -13
  34. package/core/infrastructure/update-checker.ts +5 -5
  35. package/core/integrations/jira/client.ts +91 -30
  36. package/core/integrations/jira/index.ts +29 -5
  37. package/core/integrations/jira/mcp-adapter.ts +451 -0
  38. package/core/integrations/linear/client.ts +23 -3
  39. package/core/plugin/loader.ts +16 -6
  40. package/core/plugin/registry.ts +16 -6
  41. package/core/server/routes-extended.ts +13 -6
  42. package/core/server/routes.ts +15 -5
  43. package/core/server/sse.ts +4 -3
  44. package/core/services/agent-service.ts +4 -2
  45. package/core/services/memory-service.ts +16 -5
  46. package/core/services/project-service.ts +11 -2
  47. package/core/services/skill-service.ts +4 -3
  48. package/core/session/compaction.ts +4 -5
  49. package/core/session/metrics.ts +11 -4
  50. package/core/session/task-session-manager.ts +27 -9
  51. package/core/storage/storage-manager.ts +12 -5
  52. package/core/storage/storage.ts +26 -10
  53. package/core/sync/auth-config.ts +2 -2
  54. package/core/sync/oauth-handler.ts +1 -1
  55. package/core/sync/sync-client.ts +4 -2
  56. package/core/sync/sync-manager.ts +1 -1
  57. package/core/types/agentic.ts +8 -18
  58. package/core/types/config.ts +1 -1
  59. package/core/types/index.ts +3 -2
  60. package/core/types/integrations.ts +4 -48
  61. package/core/types/storage.ts +0 -8
  62. package/core/types/task.ts +0 -4
  63. package/core/utils/file-helper.ts +10 -4
  64. package/core/utils/jsonl-helper.ts +4 -4
  65. package/core/utils/keychain.ts +130 -0
  66. package/core/utils/logger.ts +27 -25
  67. package/core/utils/runtime.ts +1 -1
  68. package/core/utils/session-helper.ts +4 -4
  69. package/core/utils/version.ts +1 -1
  70. package/package.json +1 -1
  71. package/packages/shared/src/utils.ts +1 -1
  72. package/scripts/postinstall.js +26 -2
  73. package/templates/agentic/orchestrator.md +303 -0
  74. package/templates/commands/bug.md +2 -0
  75. package/templates/commands/enrich.md +601 -195
  76. package/templates/commands/github.md +287 -0
  77. package/templates/commands/init.md +45 -26
  78. package/templates/commands/jira.md +198 -258
  79. package/templates/commands/linear.md +186 -172
  80. package/templates/commands/monday.md +232 -0
  81. package/templates/commands/p.md +57 -10
  82. package/templates/commands/setup.md +4 -1
  83. package/templates/commands/sync.md +133 -97
  84. package/templates/commands/task.md +12 -0
  85. package/templates/config/skill-mappings.json +95 -63
  86. package/templates/mcp-config.json +42 -39
  87. package/core/integrations/notion/client.ts +0 -413
  88. package/core/integrations/notion/index.ts +0 -46
  89. package/core/integrations/notion/setup.ts +0 -235
  90. package/core/integrations/notion/sync.ts +0 -818
  91. package/core/integrations/notion/templates.ts +0 -246
  92. package/core/plugin/builtin/notion.ts +0 -178
  93. package/dist/bin/prjct.mjs +0 -13581
  94. package/dist/core/infrastructure/command-installer.js +0 -473
  95. package/dist/core/infrastructure/editors-config.js +0 -157
  96. package/dist/core/infrastructure/setup.js +0 -893
  97. package/dist/core/utils/version.js +0 -142
  98. package/packages/shared/.turbo/turbo-build.log +0 -14
  99. package/packages/shared/dist/index.d.ts +0 -10
  100. package/packages/shared/dist/index.d.ts.map +0 -1
  101. package/packages/shared/dist/index.js +0 -4196
  102. package/packages/shared/dist/schemas.d.ts +0 -408
  103. package/packages/shared/dist/schemas.d.ts.map +0 -1
  104. package/packages/shared/dist/types.d.ts +0 -144
  105. package/packages/shared/dist/types.d.ts.map +0 -1
  106. package/packages/shared/dist/unified.d.ts +0 -139
  107. package/packages/shared/dist/unified.d.ts.map +0 -1
  108. package/packages/shared/dist/utils.d.ts +0 -60
  109. package/packages/shared/dist/utils.d.ts.map +0 -1
  110. package/templates/commands/ask.md +0 -128
  111. package/templates/commands/dashboard.md +0 -686
  112. package/templates/commands/feature.md +0 -46
  113. package/templates/commands/now.md +0 -53
  114. package/templates/commands/suggest.md +0 -116
  115. package/templates/global/docs/agents.md +0 -88
  116. package/templates/global/docs/architecture.md +0 -103
  117. package/templates/global/docs/commands.md +0 -96
  118. package/templates/global/docs/validation.md +0 -95
  119. package/templates/skills/notion-push.md +0 -116
  120. package/templates/skills/notion-setup.md +0 -199
  121. package/templates/skills/notion-sync.md +0 -290
@@ -17,6 +17,7 @@ import os from 'os'
17
17
  import { execSync } from 'child_process'
18
18
 
19
19
  import type { GroundTruthContext, VerificationResult, Verifier } from '../types'
20
+ import { isNotFoundError } from '../types/fs'
20
21
 
21
22
  // =============================================================================
22
23
  // Utilities
@@ -103,10 +104,14 @@ export async function verifyDone(context: GroundTruthContext): Promise<Verificat
103
104
  actual.durationFormatted = formatDuration(actual.durationMs as number)
104
105
  }
105
106
  }
106
- } catch {
107
- actual.nowExists = false
108
- warnings.push('now.md does not exist')
109
- recommendations.push('Create a task with /p:now "task"')
107
+ } catch (error) {
108
+ if (isNotFoundError(error)) {
109
+ actual.nowExists = false
110
+ warnings.push('now.md does not exist')
111
+ recommendations.push('Create a task with /p:now "task"')
112
+ } else {
113
+ throw error
114
+ }
110
115
  }
111
116
 
112
117
  // 2. Verify next.md for auto-start
@@ -116,9 +121,13 @@ export async function verifyDone(context: GroundTruthContext): Promise<Verificat
116
121
  actual.nextExists = true
117
122
  const tasks = nextContent.match(/- \[ \]/g) || []
118
123
  actual.pendingTasks = tasks.length
119
- } catch {
120
- actual.nextExists = false
121
- actual.pendingTasks = 0
124
+ } catch (error) {
125
+ if (isNotFoundError(error)) {
126
+ actual.nextExists = false
127
+ actual.pendingTasks = 0
128
+ } else {
129
+ throw error
130
+ }
122
131
  }
123
132
 
124
133
  // 3. Verify metrics.md is writable
@@ -126,9 +135,13 @@ export async function verifyDone(context: GroundTruthContext): Promise<Verificat
126
135
  try {
127
136
  await fs.access(path.dirname(metricsPath), fs.constants.W_OK)
128
137
  actual.metricsWritable = true
129
- } catch {
130
- actual.metricsWritable = false
131
- warnings.push('Cannot write to metrics directory')
138
+ } catch (error) {
139
+ if (isNotFoundError(error)) {
140
+ actual.metricsWritable = false
141
+ warnings.push('Cannot write to metrics directory')
142
+ } else {
143
+ throw error
144
+ }
132
145
  }
133
146
 
134
147
  return {
@@ -161,9 +174,9 @@ export async function verifyShip(context: GroundTruthContext): Promise<Verificat
161
174
  warnings.push(`${actual.uncommittedFiles} uncommitted file(s)`)
162
175
  recommendations.push('Commit changes before shipping')
163
176
  }
164
- } catch {
177
+ } catch (error) {
178
+ // Git errors (not a repo, git not installed) are not blockers
165
179
  actual.gitAvailable = false
166
- // Not a git repo or git not available - not a blocker
167
180
  }
168
181
 
169
182
  // 2. Check for package.json version (if exists)
@@ -173,8 +186,15 @@ export async function verifyShip(context: GroundTruthContext): Promise<Verificat
173
186
  const pkg = JSON.parse(pkgContent)
174
187
  actual.currentVersion = pkg.version
175
188
  actual.hasPackageJson = true
176
- } catch {
177
- actual.hasPackageJson = false
189
+ } catch (error) {
190
+ if (isNotFoundError(error)) {
191
+ actual.hasPackageJson = false
192
+ } else if (error instanceof SyntaxError) {
193
+ actual.hasPackageJson = false
194
+ warnings.push('package.json has invalid JSON')
195
+ } else {
196
+ throw error
197
+ }
178
198
  }
179
199
 
180
200
  // 3. Check shipped.md for duplicate feature names
@@ -193,8 +213,12 @@ export async function verifyShip(context: GroundTruthContext): Promise<Verificat
193
213
  recommendations.push('Use a different feature name or skip /p:ship')
194
214
  }
195
215
  }
196
- } catch {
197
- actual.shippedExists = false
216
+ } catch (error) {
217
+ if (isNotFoundError(error)) {
218
+ actual.shippedExists = false
219
+ } else {
220
+ throw error
221
+ }
198
222
  }
199
223
 
200
224
  // 4. Check for test failures (if test script exists)
@@ -205,8 +229,12 @@ export async function verifyShip(context: GroundTruthContext): Promise<Verificat
205
229
  actual.hasTestScript = !!pkg.scripts?.test
206
230
  // Note: We don't run tests here, just check if they exist
207
231
  // Running tests is the user's responsibility
208
- } catch {
209
- actual.hasTestScript = false
232
+ } catch (error) {
233
+ if (isNotFoundError(error) || error instanceof SyntaxError) {
234
+ actual.hasTestScript = false
235
+ } else {
236
+ throw error
237
+ }
210
238
  }
211
239
  }
212
240
 
@@ -240,9 +268,13 @@ export async function verifyFeature(context: GroundTruthContext): Promise<Verifi
240
268
  warnings.push(`Queue nearly full (${actual.taskCount}/100 tasks)`)
241
269
  recommendations.push('Complete some tasks before adding more')
242
270
  }
243
- } catch {
244
- actual.nextExists = false
245
- actual.taskCount = 0
271
+ } catch (error) {
272
+ if (isNotFoundError(error)) {
273
+ actual.nextExists = false
274
+ actual.taskCount = 0
275
+ } else {
276
+ throw error
277
+ }
246
278
  }
247
279
 
248
280
  // 2. Check roadmap.md for duplicate features
@@ -259,8 +291,12 @@ export async function verifyFeature(context: GroundTruthContext): Promise<Verifi
259
291
  recommendations.push('Check roadmap for duplicates with /p:roadmap')
260
292
  }
261
293
  }
262
- } catch {
263
- actual.roadmapExists = false
294
+ } catch (error) {
295
+ if (isNotFoundError(error)) {
296
+ actual.roadmapExists = false
297
+ } else {
298
+ throw error
299
+ }
264
300
  }
265
301
 
266
302
  // 3. Check if there's an active task (should complete first?)
@@ -272,8 +308,12 @@ export async function verifyFeature(context: GroundTruthContext): Promise<Verifi
272
308
  if (actual.hasActiveTask) {
273
309
  recommendations.push('Consider completing current task first with /p:done')
274
310
  }
275
- } catch {
276
- actual.hasActiveTask = false
311
+ } catch (error) {
312
+ if (isNotFoundError(error)) {
313
+ actual.hasActiveTask = false
314
+ } else {
315
+ throw error
316
+ }
277
317
  }
278
318
 
279
319
  return {
@@ -311,9 +351,13 @@ export async function verifyNow(context: GroundTruthContext): Promise<Verificati
311
351
  warnings.push(`Replacing existing task: "${taskPreview}..."`)
312
352
  recommendations.push('Use /p:done first to track completion')
313
353
  }
314
- } catch {
315
- actual.nowExists = false
316
- actual.hasActiveTask = false
354
+ } catch (error) {
355
+ if (isNotFoundError(error)) {
356
+ actual.nowExists = false
357
+ actual.hasActiveTask = false
358
+ } else {
359
+ throw error
360
+ }
317
361
  }
318
362
 
319
363
  // 2. Check next.md for available tasks
@@ -326,8 +370,12 @@ export async function verifyNow(context: GroundTruthContext): Promise<Verificati
326
370
  if (!context.params.task && pendingTasks > 0) {
327
371
  recommendations.push(`${pendingTasks} tasks available in queue`)
328
372
  }
329
- } catch {
330
- actual.pendingTasks = 0
373
+ } catch (error) {
374
+ if (isNotFoundError(error)) {
375
+ actual.pendingTasks = 0
376
+ } else {
377
+ throw error
378
+ }
331
379
  }
332
380
 
333
381
  return {
@@ -355,8 +403,15 @@ export async function verifyInit(context: GroundTruthContext): Promise<Verificat
355
403
  actual.existingConfig = JSON.parse(configContent)
356
404
  warnings.push('Project already initialized')
357
405
  recommendations.push('Use /p:analyze to refresh analysis or delete .prjct/ to reinitialize')
358
- } catch {
359
- actual.alreadyInitialized = false
406
+ } catch (error) {
407
+ if (isNotFoundError(error)) {
408
+ actual.alreadyInitialized = false
409
+ } else if (error instanceof SyntaxError) {
410
+ actual.alreadyInitialized = false
411
+ warnings.push('Existing config has invalid JSON')
412
+ } else {
413
+ throw error
414
+ }
360
415
  }
361
416
 
362
417
  // 2. Check if global storage path is writable
@@ -364,13 +419,19 @@ export async function verifyInit(context: GroundTruthContext): Promise<Verificat
364
419
  try {
365
420
  await fs.access(globalPath, fs.constants.W_OK)
366
421
  actual.globalPathWritable = true
367
- } catch {
368
- try {
369
- // Try to create it
370
- await fs.mkdir(globalPath, { recursive: true })
371
- actual.globalPathWritable = true
372
- actual.globalPathCreated = true
373
- } catch {
422
+ } catch (error) {
423
+ if (isNotFoundError(error)) {
424
+ try {
425
+ // Try to create it
426
+ await fs.mkdir(globalPath, { recursive: true })
427
+ actual.globalPathWritable = true
428
+ actual.globalPathCreated = true
429
+ } catch (mkdirError) {
430
+ actual.globalPathWritable = false
431
+ warnings.push('Cannot write to ~/.prjct-cli')
432
+ recommendations.push('Check directory permissions')
433
+ }
434
+ } else {
374
435
  actual.globalPathWritable = false
375
436
  warnings.push('Cannot write to ~/.prjct-cli')
376
437
  recommendations.push('Check directory permissions')
@@ -400,11 +461,20 @@ export async function verifySync(context: GroundTruthContext): Promise<Verificat
400
461
  const configContent = await fs.readFile(configPath, 'utf-8')
401
462
  actual.hasConfig = true
402
463
  actual.config = JSON.parse(configContent)
403
- } catch {
404
- actual.hasConfig = false
405
- warnings.push('Project not initialized')
406
- recommendations.push('Run /p:init first')
407
- return { verified: false, actual, warnings, recommendations }
464
+ } catch (error) {
465
+ if (isNotFoundError(error)) {
466
+ actual.hasConfig = false
467
+ warnings.push('Project not initialized')
468
+ recommendations.push('Run /p:init first')
469
+ return { verified: false, actual, warnings, recommendations }
470
+ } else if (error instanceof SyntaxError) {
471
+ actual.hasConfig = false
472
+ warnings.push('Config file has invalid JSON')
473
+ recommendations.push('Delete .prjct/ and run /p:init')
474
+ return { verified: false, actual, warnings, recommendations }
475
+ } else {
476
+ throw error
477
+ }
408
478
  }
409
479
 
410
480
  // 2. Check if global storage exists
@@ -413,10 +483,14 @@ export async function verifySync(context: GroundTruthContext): Promise<Verificat
413
483
  try {
414
484
  await fs.access(globalProjectPath)
415
485
  actual.globalStorageExists = true
416
- } catch {
417
- actual.globalStorageExists = false
418
- warnings.push('Global storage missing')
419
- recommendations.push('Run /p:init to recreate')
486
+ } catch (error) {
487
+ if (isNotFoundError(error)) {
488
+ actual.globalStorageExists = false
489
+ warnings.push('Global storage missing')
490
+ recommendations.push('Run /p:init to recreate')
491
+ } else {
492
+ throw error
493
+ }
420
494
  }
421
495
 
422
496
  return {
@@ -444,8 +518,11 @@ export async function verifyAnalyze(context: GroundTruthContext): Promise<Verifi
444
518
  try {
445
519
  await fs.access(path.join(context.projectPath, file))
446
520
  ;(actual.detectedFiles as string[]).push(file)
447
- } catch {
448
- // File doesn't exist
521
+ } catch (error) {
522
+ // ENOENT expected - file doesn't exist
523
+ if (!isNotFoundError(error)) {
524
+ throw error
525
+ }
449
526
  }
450
527
  }
451
528
 
@@ -464,8 +541,11 @@ export async function verifyAnalyze(context: GroundTruthContext): Promise<Verifi
464
541
  if (stat.isDirectory()) {
465
542
  ;(actual.detectedSrcDirs as string[]).push(dir)
466
543
  }
467
- } catch {
468
- // Directory doesn't exist
544
+ } catch (error) {
545
+ // ENOENT expected - directory doesn't exist
546
+ if (!isNotFoundError(error)) {
547
+ throw error
548
+ }
469
549
  }
470
550
  }
471
551
 
@@ -496,9 +576,13 @@ export async function verifySpec(context: GroundTruthContext): Promise<Verificat
496
576
  const files = await fs.readdir(specsPath)
497
577
  actual.existingSpecs = files.filter((f) => f.endsWith('.md'))
498
578
  actual.specCount = (actual.existingSpecs as string[]).length
499
- } catch {
500
- actual.specsExists = false
501
- actual.specCount = 0
579
+ } catch (error) {
580
+ if (isNotFoundError(error)) {
581
+ actual.specsExists = false
582
+ actual.specCount = 0
583
+ } else {
584
+ throw error
585
+ }
502
586
  }
503
587
 
504
588
  // 2. Check for duplicate spec name
@@ -11,6 +11,7 @@
11
11
 
12
12
  import fs from 'fs'
13
13
  import path from 'path'
14
+ import { isNotFoundError } from '../types/fs'
14
15
  import { stateStorage, queueStorage } from '../storage'
15
16
  import { outcomeAnalyzer } from '../outcomes'
16
17
  import type {
@@ -85,8 +86,11 @@ class PromptBuilder {
85
86
  checklists[name] = content
86
87
  }
87
88
  }
88
- } catch {
89
+ } catch (error) {
89
90
  // Silent fail - checklists are optional enhancement
91
+ if (!isNotFoundError(error)) {
92
+ console.error(`Checklist loading warning: ${(error as Error).message}`)
93
+ }
90
94
  }
91
95
 
92
96
  this._checklistsCache = checklists
@@ -118,8 +122,11 @@ class PromptBuilder {
118
122
 
119
123
  this._stateCache.set(projectId, { state, timestamp: Date.now() })
120
124
  return state
121
- } catch {
122
- return null
125
+ } catch (error) {
126
+ if (isNotFoundError(error) || error instanceof SyntaxError) {
127
+ return null
128
+ }
129
+ throw error
123
130
  }
124
131
  }
125
132
 
@@ -175,8 +182,11 @@ class PromptBuilder {
175
182
  }
176
183
  parts.push('')
177
184
  }
178
- } catch {
179
- // Outcomes not available yet
185
+ } catch (error) {
186
+ // Outcomes not available yet - expected for new projects
187
+ if (!isNotFoundError(error) && !(error instanceof SyntaxError)) {
188
+ console.error(`Outcome detection warning: ${(error as Error).message}`)
189
+ }
180
190
  }
181
191
 
182
192
  parts.push('---')
@@ -214,8 +224,11 @@ class PromptBuilder {
214
224
  if (fs.existsSync(routingPath)) {
215
225
  this._checklistRoutingCache = fs.readFileSync(routingPath, 'utf-8')
216
226
  }
217
- } catch {
218
- // Silent fail
227
+ } catch (error) {
228
+ // Silent fail - checklist routing is optional
229
+ if (!isNotFoundError(error)) {
230
+ console.error(`Checklist routing warning: ${(error as Error).message}`)
231
+ }
219
232
  }
220
233
 
221
234
  return this._checklistRoutingCache || null
@@ -170,7 +170,7 @@ class SmartContext {
170
170
  confidence: pattern.confidence,
171
171
  })
172
172
  }
173
- } catch {
173
+ } catch (_error) {
174
174
  // Outcomes not available
175
175
  }
176
176
 
@@ -87,7 +87,7 @@ export async function load(commandName: string): Promise<ParsedTemplate> {
87
87
  cacheOrder.push(commandName)
88
88
 
89
89
  return parsed
90
- } catch {
90
+ } catch (_error) {
91
91
  throw TemplateError.notFound(commandName)
92
92
  }
93
93
  }
@@ -65,7 +65,8 @@ const toolRegistry: ToolRegistryInterface = {
65
65
  toolRegistry.register('Read', async (filePath: unknown): Promise<string | null> => {
66
66
  try {
67
67
  return await fs.readFile(filePath as string, 'utf-8')
68
- } catch {
68
+ } catch (_error) {
69
+ // File not found or read error - return null (expected)
69
70
  return null
70
71
  }
71
72
  })
@@ -75,7 +76,8 @@ toolRegistry.register('Write', async (filePath: unknown, content: unknown): Prom
75
76
  try {
76
77
  await fs.writeFile(filePath as string, content as string, 'utf-8')
77
78
  return true
78
- } catch {
79
+ } catch (_error) {
80
+ // Write error - return false (expected)
79
81
  return false
80
82
  }
81
83
  })
package/core/bus/bus.ts CHANGED
@@ -188,7 +188,7 @@ class EventBus {
188
188
  // Append event
189
189
  const line = JSON.stringify(eventData) + '\n'
190
190
  await fs.appendFile(eventsPath, line)
191
- } catch {
191
+ } catch (_error) {
192
192
  // Silently fail - logging should not break functionality
193
193
  }
194
194
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  import path from 'path'
8
8
 
9
+ import { isNotFoundError } from '../types/fs'
9
10
  import type { CommandResult, CleanupOptions } from '../types'
10
11
  import {
11
12
  pathManager,
@@ -44,8 +45,11 @@ export async function cleanupMemory(projectPath: string): Promise<{
44
45
  results.freedSpace += sizeMB
45
46
  }
46
47
  }
47
- } catch {
48
- // skip
48
+ } catch (error) {
49
+ // Skip file if not found, otherwise log unexpected errors
50
+ if (!isNotFoundError(error)) {
51
+ console.error(`Cleanup warning for ${filePath}: ${(error as Error).message}`)
52
+ }
49
53
  }
50
54
  }
51
55
 
@@ -100,8 +104,12 @@ export async function cleanup(
100
104
  } else {
101
105
  cleaned.push('Memory: No cleanup needed')
102
106
  }
103
- } catch {
104
- cleaned.push('Memory: No file found')
107
+ } catch (error) {
108
+ if (isNotFoundError(error)) {
109
+ cleaned.push('Memory: No file found')
110
+ } else {
111
+ cleaned.push(`Memory: Error - ${(error as Error).message}`)
112
+ }
105
113
  }
106
114
 
107
115
  // Clean ideas using ideasStorage
@@ -112,8 +120,12 @@ export async function cleanup(
112
120
  } else {
113
121
  cleaned.push('Ideas: No cleanup needed')
114
122
  }
115
- } catch {
116
- cleaned.push('Ideas: No file found')
123
+ } catch (error) {
124
+ if (isNotFoundError(error)) {
125
+ cleaned.push('Ideas: No file found')
126
+ } else {
127
+ cleaned.push(`Ideas: Error - ${(error as Error).message}`)
128
+ }
117
129
  }
118
130
 
119
131
  // Check queue for completed tasks using queueStorage
@@ -128,8 +140,12 @@ export async function cleanup(
128
140
  } else {
129
141
  cleaned.push('Queue: No completed tasks')
130
142
  }
131
- } catch {
132
- cleaned.push('Queue: No file found')
143
+ } catch (error) {
144
+ if (isNotFoundError(error)) {
145
+ cleaned.push('Queue: No file found')
146
+ } else {
147
+ cleaned.push(`Queue: Error - ${(error as Error).message}`)
148
+ }
133
149
  }
134
150
 
135
151
  await cleanupMemoryInternal(projectPath)
@@ -281,7 +281,8 @@ export class PlanningCommands extends PrjctCommandsBase {
281
281
  let planContent: string
282
282
  try {
283
283
  planContent = await fileHelper.readFile(planPath)
284
- } catch {
284
+ } catch (_error) {
285
+ // No plan file - expected for projects without architect mode
285
286
  return {
286
287
  success: false,
287
288
  message:
@@ -465,7 +466,8 @@ Generated: ${new Date().toLocaleString()}
465
466
  console.log('═'.repeat(50) + '\n')
466
467
 
467
468
  return { success: true, specs }
468
- } catch {
469
+ } catch (_error) {
470
+ // No specs directory - expected for new projects
469
471
  out.warn('no specs directory')
470
472
  return { success: true, specs: [] }
471
473
  }
@@ -4,10 +4,10 @@
4
4
 
5
5
  import path from 'path'
6
6
  import fs from 'fs'
7
- import os from 'os'
8
7
  import chalk from 'chalk'
9
8
 
10
9
  import commandInstaller from '../infrastructure/command-installer'
10
+ import pathManager from '../infrastructure/path-manager'
11
11
  import type { CommandResult, SetupOptions, MigrateOptions } from '../types'
12
12
  import { PrjctCommandsBase } from './base'
13
13
  import { VERSION } from '../utils/version'
@@ -126,8 +126,8 @@ export class SetupCommands extends PrjctCommandsBase {
126
126
  */
127
127
  async installStatusLine(): Promise<{ success: boolean; error?: string }> {
128
128
  try {
129
- const claudeDir = path.join(os.homedir(), '.claude')
130
- const settingsPath = path.join(claudeDir, 'settings.json')
129
+ const claudeDir = pathManager.getClaudeDir()
130
+ const settingsPath = pathManager.getClaudeSettingsPath()
131
131
  const statusLinePath = path.join(claudeDir, 'prjct-statusline.sh')
132
132
 
133
133
  // Version is embedded at install time
@@ -194,7 +194,7 @@ echo "⚡ prjct"
194
194
  if (fs.existsSync(settingsPath)) {
195
195
  try {
196
196
  settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
197
- } catch {
197
+ } catch (_error) {
198
198
  // Invalid JSON, start fresh
199
199
  }
200
200
  }
@@ -5,6 +5,7 @@
5
5
 
6
6
  import path from 'path'
7
7
 
8
+ import { isNotFoundError } from '../types/fs'
8
9
  import memorySystem from '../agentic/memory-system'
9
10
  import type { CommandResult } from '../types'
10
11
  import { detectProjectCommands } from '../utils/project-commands'
@@ -133,7 +134,11 @@ export class ShippingCommands extends PrjctCommandsBase {
133
134
 
134
135
  const { exitCode } = await this._runWithExitCode(detected.lint.command)
135
136
  return { success: exitCode === 0, message: exitCode === 0 ? 'passed' : 'failed' }
136
- } catch {
137
+ } catch (error) {
138
+ // Lint detection/execution failed - skip gracefully
139
+ if (isNotFoundError(error)) {
140
+ return { success: true, message: 'skipped (lint not found)' }
141
+ }
137
142
  return { success: true, message: 'skipped (lint detection failed)' }
138
143
  }
139
144
  }
@@ -148,7 +153,11 @@ export class ShippingCommands extends PrjctCommandsBase {
148
153
 
149
154
  const { exitCode } = await this._runWithExitCode(detected.test.command)
150
155
  return { success: exitCode === 0, message: exitCode === 0 ? 'passed' : 'failed' }
151
- } catch {
156
+ } catch (error) {
157
+ // Test detection/execution failed - skip gracefully
158
+ if (isNotFoundError(error)) {
159
+ return { success: true, message: 'skipped (tests not found)' }
160
+ }
152
161
  return { success: true, message: 'skipped (test detection failed)' }
153
162
  }
154
163
  }
@@ -168,8 +177,12 @@ export class ShippingCommands extends PrjctCommandsBase {
168
177
  await fileHelper.writeJson(pkgPath, pkg)
169
178
  }
170
179
  return newVersion
171
- } catch {
172
- return '0.0.1'
180
+ } catch (error) {
181
+ // No package.json or parse error - return default version
182
+ if (isNotFoundError(error) || error instanceof SyntaxError) {
183
+ return '0.0.1'
184
+ }
185
+ throw error
173
186
  }
174
187
  }
175
188
 
@@ -185,8 +198,13 @@ export class ShippingCommands extends PrjctCommandsBase {
185
198
  const updated = changelog.replace('# Changelog\n\n', `# Changelog\n\n${entry}`)
186
199
 
187
200
  await fileHelper.writeFile(changelogPath, updated)
188
- } catch {
189
- console.error(' Warning: Could not update CHANGELOG')
201
+ } catch (error) {
202
+ // CHANGELOG doesn't exist or can't be written - warn but continue
203
+ if (isNotFoundError(error)) {
204
+ console.error(' Warning: CHANGELOG.md not found')
205
+ } else {
206
+ console.error(' Warning: Could not update CHANGELOG')
207
+ }
190
208
  }
191
209
  }
192
210
 
@@ -202,7 +220,11 @@ export class ShippingCommands extends PrjctCommandsBase {
202
220
  await toolRegistry.get('Bash')!(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`)
203
221
 
204
222
  return { success: true, message: 'Committed' }
205
- } catch {
223
+ } catch (error) {
224
+ // Git commit failed - likely no changes or not a repo
225
+ if (isNotFoundError(error)) {
226
+ return { success: false, message: 'Git not found' }
227
+ }
206
228
  return { success: false, message: 'No changes to commit' }
207
229
  }
208
230
  }
@@ -214,7 +236,11 @@ export class ShippingCommands extends PrjctCommandsBase {
214
236
  try {
215
237
  await toolRegistry.get('Bash')!('git push')
216
238
  return { success: true, message: 'Pushed to remote' }
217
- } catch {
239
+ } catch (error) {
240
+ // Git push failed - no remote, auth issue, or git not found
241
+ if (isNotFoundError(error)) {
242
+ return { success: false, message: 'Git not found' }
243
+ }
218
244
  return { success: false, message: 'Push failed (no remote or auth issue)' }
219
245
  }
220
246
  }