prjct-cli 0.31.0 → 0.33.5

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 (89) hide show
  1. package/CHANGELOG.md +118 -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/dist/bin/prjct.mjs +1 -1
  71. package/package.json +1 -1
  72. package/packages/shared/src/utils.ts +1 -1
  73. package/templates/commands/enrich.md +601 -195
  74. package/templates/commands/github.md +231 -0
  75. package/templates/commands/init.md +45 -26
  76. package/templates/commands/jira.md +161 -261
  77. package/templates/commands/linear.md +159 -177
  78. package/templates/commands/monday.md +196 -0
  79. package/templates/commands/setup.md +4 -1
  80. package/templates/mcp-config.json +42 -39
  81. package/core/integrations/notion/client.ts +0 -413
  82. package/core/integrations/notion/index.ts +0 -46
  83. package/core/integrations/notion/setup.ts +0 -235
  84. package/core/integrations/notion/sync.ts +0 -818
  85. package/core/integrations/notion/templates.ts +0 -246
  86. package/core/plugin/builtin/notion.ts +0 -178
  87. package/templates/skills/notion-push.md +0 -116
  88. package/templates/skills/notion-setup.md +0 -199
  89. package/templates/skills/notion-sync.md +0 -290
package/CHANGELOG.md CHANGED
@@ -1,5 +1,123 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.33.5] - 2026-01-13
4
+
5
+ ### Fix: Type Safety Improvements (PRJ-54)
6
+
7
+ Remove unsafe `as unknown` type casts with proper TypeScript interfaces.
8
+
9
+ **Changes:**
10
+ - `GroundTruthContext` now uses `ContextPaths` directly
11
+ - `chain-of-thought.ts` uses `Pick<ProjectContext, ...>` type alias
12
+ - `command-executor.ts` uses `PromptContext` instead of `Record<string, unknown>`
13
+
14
+ ---
15
+
16
+ ## [0.33.4] - 2026-01-13
17
+
18
+ ### Refactor: Error Type Differentiation Phase 3 (PRJ-61)
19
+
20
+ **Final phase** - Complete error differentiation across all 116 remaining catch blocks in 52 files.
21
+
22
+ **Pattern Applied:**
23
+ - `isNotFoundError()` for expected ENOENT errors
24
+ - `instanceof SyntaxError` for JSON parse errors
25
+ - `_error` capture for intentional catch-all blocks (network, git, etc.)
26
+ - Unexpected errors propagate with `throw error`
27
+
28
+ **Key Files Fixed:**
29
+ - `core/storage/storage.ts` - Storage read/exists operations
30
+ - `core/commands/shipping.ts` - Ship workflow error handling
31
+ - `core/session/task-session-manager.ts` - Session management
32
+ - `core/commands/cleanup.ts` - Cleanup operations
33
+ - `core/agentic/context-builder.ts` - Context building
34
+ - Plus 47 more files
35
+
36
+ **Stats:**
37
+ - Catches fixed this phase: 116
38
+ - Total fixed (Phase 1+2+3): 185
39
+ - Remaining: 0 empty catches
40
+
41
+ ---
42
+
43
+ ## [0.33.3] - 2026-01-13
44
+
45
+ ### Refactor: Error Type Differentiation Phase 2 (PRJ-60)
46
+
47
+ **Continuation of PRJ-51** - Differentiate error types in 3 more files (14 catch blocks):
48
+
49
+ **Files Modified:**
50
+ - `core/domain/snapshot-manager.ts` - 6 catches (fs.access, fs.readFile, fs.unlink, JSON.parse)
51
+ - `core/server/routes-extended.ts` - 2 catches (readJsonFile, writeJsonFile helpers)
52
+ - `core/infrastructure/setup.ts` - 6 catches (migration, settings parsing, status line)
53
+
54
+ **Pattern Applied:**
55
+ - ENOENT errors → handled gracefully (expected for missing files)
56
+ - SyntaxError → handled gracefully (expected for malformed JSON)
57
+ - Other errors → propagated or logged (unexpected)
58
+
59
+ **Stats:**
60
+ - Catches fixed: 14
61
+ - Total fixed (Phase 1+2): 69
62
+ - Remaining: ~211 catches in 63 files
63
+
64
+ ---
65
+
66
+ ## [0.33.2] - 2026-01-13
67
+
68
+ ### Refactor: Error Type Differentiation (PRJ-51)
69
+
70
+ **Problem:** 280 catch blocks across 69 files used empty `catch {}` which swallowed all errors without differentiating between expected (ENOENT) and unexpected errors.
71
+
72
+ **Solution:** Phase 1 implementation - differentiate error types in 3 priority files (55 catch blocks):
73
+
74
+ **Pattern Applied:**
75
+ ```typescript
76
+ // Before
77
+ } catch { return null }
78
+
79
+ // After
80
+ } catch (error) {
81
+ if (isNotFoundError(error)) return null // Expected: file doesn't exist
82
+ if (error instanceof SyntaxError) return null // Expected: invalid JSON
83
+ throw error // Unexpected: propagate
84
+ }
85
+ ```
86
+
87
+ **Files Modified:**
88
+ - `core/agentic/ground-truth.ts` - 21 catches (verification functions)
89
+ - `core/domain/analyzer.ts` - 18 catches (codebase analysis)
90
+ - `core/infrastructure/command-installer.ts` - 16 catches (command management)
91
+
92
+ **Tests Added:**
93
+ - `core/__tests__/types/fs.test.ts` - 15 new tests for error utilities
94
+
95
+ **Stats:**
96
+ - Tests: 137 → 152 (+15)
97
+ - Catches fixed: 55
98
+ - Files: 4 (3 refactored + 1 test)
99
+
100
+ ---
101
+
102
+ ## [0.33.1] - 2026-01-13
103
+
104
+ ### Refactor: Code Quality Improvements
105
+
106
+ **PRJ-58: Consolidate hardcoded paths to pathManager**
107
+ - Added `getClaudeDir()`, `getClaudeCommandsDir()`, `getClaudeSettingsPath()` to pathManager
108
+ - Refactored 6 files to use centralized path methods instead of `os.homedir()` concatenation
109
+ - Files: `compaction.ts`, `generator.ts`, `routes-extended.ts`, `routes.ts`, `auth-config.ts`, `setup.ts`
110
+
111
+ **PRJ-57: Simplify logger level detection**
112
+ - Use `Set` for truthy values instead of multiple OR conditions
113
+ - Remove redundant `'prjct'` equality check (covered by `includes`)
114
+ - Use nullish coalescing (`??`) instead of ternary
115
+ - Pre-compute level name to avoid runtime lookup
116
+ - Add `createLogMethod()` factory to reduce repetition
117
+ - File: `core/utils/logger.ts`
118
+
119
+ ---
120
+
3
121
  ## [0.30.3] - 2026-01-13
4
122
 
5
123
  ### Fix: Enrichment Not Enabled by Default
package/CLAUDE.md CHANGED
@@ -91,6 +91,7 @@ User Action → Storage (JSON) → Context (MD) → Sync Events
91
91
 
92
92
  ## COMMANDS
93
93
 
94
+ ### Core Workflow
94
95
  | Trigger | Purpose |
95
96
  |---------|---------|
96
97
  | `p. init` | Initialize project with deep analysis |
@@ -102,11 +103,51 @@ User Action → Storage (JSON) → Context (MD) → Sync Events
102
103
  | `p. resume` | Resume paused task |
103
104
  | `p. bug <desc>` | Report bug with auto-priority |
104
105
 
106
+ ### Issue Tracker Integrations
107
+ | Trigger | Purpose |
108
+ |---------|---------|
109
+ | `p. linear` | Linear issues (OAuth via MCP) |
110
+ | `p. jira` | JIRA issues (OAuth/SSO via MCP) |
111
+ | `p. github` | GitHub Issues (token via MCP) |
112
+ | `p. monday` | Monday.com boards (OAuth via MCP) |
113
+ | `p. enrich <ID>` | **AI-powered ticket enrichment** |
114
+
115
+ ### Ticket Enrichment (`p. enrich`)
116
+
117
+ Transform vague PM tickets into technical PRDs:
118
+
119
+ ```
120
+ PM creates: "Add user auth"
121
+
122
+ p. enrich PRJ-59
123
+
124
+ Architect analyzes codebase:
125
+ - Similar implementations found
126
+ - 5 files affected
127
+ - OAuth2 approach recommended
128
+ - 8 story points (not PM's guess of 2)
129
+
130
+ Publishes PRD to tracker:
131
+ - Technical approach
132
+ - Acceptance criteria
133
+ - LLM-ready prompt (for any AI tool)
134
+ ```
135
+
136
+ **Subcommands:**
137
+ - `p. enrich <ID>` - Enrich specific ticket
138
+ - `p. enrich setup` - Configure team preferences (estimation, output)
139
+ - `p. enrich batch` - Enrich all assigned tickets (backlog grooming)
140
+
105
141
  ### Workflow
106
142
  ```
107
143
  p. sync → p. task "description" → [work] → p. done → p. ship
108
144
  ```
109
145
 
146
+ ### With Issue Tracker
147
+ ```
148
+ p. linear → p. enrich PRJ-59 → p. linear start PRJ-59 → [work] → p. done → p. ship
149
+ ```
150
+
110
151
  ---
111
152
 
112
153
  ## INTELLIGENT BEHAVIOR
@@ -253,7 +253,7 @@ describe('MemorySystem P3.3', () => {
253
253
  try {
254
254
  const testPath = pathManager.getGlobalProjectPath(TEST_PROJECT_ID)
255
255
  await fs.rm(testPath, { recursive: true, force: true })
256
- } catch {
256
+ } catch (_error) {
257
257
  // Ignore cleanup errors
258
258
  }
259
259
  })
@@ -261,7 +261,7 @@ describe('MemorySystem P3.3', () => {
261
261
  afterAll(async () => {
262
262
  try {
263
263
  await fs.rm(TEST_GLOBAL_BASE_DIR, { recursive: true, force: true })
264
- } catch {
264
+ } catch (_error) {
265
265
  // Ignore cleanup errors
266
266
  }
267
267
  })
@@ -0,0 +1,125 @@
1
+ /**
2
+ * FS Types Tests
3
+ * Tests for file system error utilities
4
+ */
5
+
6
+ import { describe, it, expect } from 'bun:test'
7
+ import {
8
+ isNotFoundError,
9
+ isPermissionError,
10
+ isDirNotEmptyError,
11
+ isFileExistsError,
12
+ isNodeError,
13
+ } from '../../types/fs'
14
+
15
+ describe('FS Error Utilities', () => {
16
+ describe('isNotFoundError', () => {
17
+ it('should return true for ENOENT error', () => {
18
+ const error = new Error('File not found') as NodeJS.ErrnoException
19
+ error.code = 'ENOENT'
20
+ expect(isNotFoundError(error)).toBe(true)
21
+ })
22
+
23
+ it('should return false for other error codes', () => {
24
+ const error = new Error('Permission denied') as NodeJS.ErrnoException
25
+ error.code = 'EACCES'
26
+ expect(isNotFoundError(error)).toBe(false)
27
+ })
28
+
29
+ it('should return false for errors without code', () => {
30
+ const error = new Error('Generic error')
31
+ expect(isNotFoundError(error)).toBe(false)
32
+ })
33
+
34
+ it('should return false for non-error values', () => {
35
+ expect(isNotFoundError(null)).toBe(false)
36
+ expect(isNotFoundError(undefined)).toBe(false)
37
+ expect(isNotFoundError('string')).toBe(false)
38
+ })
39
+ })
40
+
41
+ describe('isPermissionError', () => {
42
+ it('should return true for EACCES error', () => {
43
+ const error = new Error('Permission denied') as NodeJS.ErrnoException
44
+ error.code = 'EACCES'
45
+ expect(isPermissionError(error)).toBe(true)
46
+ })
47
+
48
+ it('should return true for EPERM error', () => {
49
+ const error = new Error('Operation not permitted') as NodeJS.ErrnoException
50
+ error.code = 'EPERM'
51
+ expect(isPermissionError(error)).toBe(true)
52
+ })
53
+
54
+ it('should return false for other error codes', () => {
55
+ const error = new Error('File not found') as NodeJS.ErrnoException
56
+ error.code = 'ENOENT'
57
+ expect(isPermissionError(error)).toBe(false)
58
+ })
59
+ })
60
+
61
+ describe('isDirNotEmptyError', () => {
62
+ it('should return true for ENOTEMPTY error', () => {
63
+ const error = new Error('Directory not empty') as NodeJS.ErrnoException
64
+ error.code = 'ENOTEMPTY'
65
+ expect(isDirNotEmptyError(error)).toBe(true)
66
+ })
67
+
68
+ it('should return false for other error codes', () => {
69
+ const error = new Error('File not found') as NodeJS.ErrnoException
70
+ error.code = 'ENOENT'
71
+ expect(isDirNotEmptyError(error)).toBe(false)
72
+ })
73
+ })
74
+
75
+ describe('isFileExistsError', () => {
76
+ it('should return true for EEXIST error', () => {
77
+ const error = new Error('File exists') as NodeJS.ErrnoException
78
+ error.code = 'EEXIST'
79
+ expect(isFileExistsError(error)).toBe(true)
80
+ })
81
+
82
+ it('should return false for other error codes', () => {
83
+ const error = new Error('File not found') as NodeJS.ErrnoException
84
+ error.code = 'ENOENT'
85
+ expect(isFileExistsError(error)).toBe(false)
86
+ })
87
+ })
88
+
89
+ describe('isNodeError', () => {
90
+ it('should return true for Error with code property', () => {
91
+ const error = new Error('File not found') as NodeJS.ErrnoException
92
+ error.code = 'ENOENT'
93
+ expect(isNodeError(error)).toBe(true)
94
+ })
95
+
96
+ it('should return false for Error without code property', () => {
97
+ const error = new Error('Generic error')
98
+ expect(isNodeError(error)).toBe(false)
99
+ })
100
+
101
+ it('should return false for non-Error values', () => {
102
+ expect(isNodeError(null)).toBe(false)
103
+ expect(isNodeError({ code: 'ENOENT' })).toBe(false) // Not an Error instance
104
+ })
105
+ })
106
+
107
+ describe('SyntaxError handling pattern', () => {
108
+ it('should differentiate SyntaxError from fs errors', () => {
109
+ const syntaxError = new SyntaxError('Unexpected token')
110
+ const fsError = new Error('File not found') as NodeJS.ErrnoException
111
+ fsError.code = 'ENOENT'
112
+
113
+ // Pattern used in code
114
+ const handleError = (error: unknown): string => {
115
+ if (isNotFoundError(error)) return 'not-found'
116
+ if (error instanceof SyntaxError) return 'parse-error'
117
+ return 'other'
118
+ }
119
+
120
+ expect(handleError(fsError)).toBe('not-found')
121
+ expect(handleError(syntaxError)).toBe('parse-error')
122
+ expect(handleError(new Error('Other'))).toBe('other')
123
+ })
124
+ })
125
+ })
@@ -15,6 +15,7 @@ import fs from 'fs/promises'
15
15
  import path from 'path'
16
16
  import configManager from '../infrastructure/config-manager'
17
17
  import pathManager from '../infrastructure/path-manager'
18
+ import { isNotFoundError } from '../types/fs'
18
19
  import type { Agent, AssignmentContext } from '../types'
19
20
 
20
21
  // Re-export types for convenience
@@ -57,7 +58,11 @@ class AgentRouter {
57
58
  }
58
59
 
59
60
  return agents
60
- } catch {
61
+ } catch (error) {
62
+ // Agents directory doesn't exist yet - expected for new projects
63
+ if (!isNotFoundError(error)) {
64
+ console.error(`Agent loading error: ${(error as Error).message}`)
65
+ }
61
66
  return []
62
67
  }
63
68
  }
@@ -80,7 +85,11 @@ class AgentRouter {
80
85
  const filePath = path.join(this.agentsPath, `${name}.md`)
81
86
  const content = await fs.readFile(filePath, 'utf-8')
82
87
  return { name, content }
83
- } catch {
88
+ } catch (error) {
89
+ // Agent file doesn't exist - expected
90
+ if (!isNotFoundError(error)) {
91
+ console.error(`Agent load error: ${(error as Error).message}`)
92
+ }
84
93
  return null
85
94
  }
86
95
  }
@@ -130,8 +139,11 @@ class AgentRouter {
130
139
  }) + '\n'
131
140
 
132
141
  await fs.appendFile(logPath, entry)
133
- } catch {
134
- // Silent fail for logging
142
+ } catch (error) {
143
+ // Non-critical - log unexpected errors but don't fail
144
+ if (!isNotFoundError(error)) {
145
+ console.error(`Agent usage log error: ${(error as Error).message}`)
146
+ }
135
147
  }
136
148
  }
137
149
  }
@@ -6,19 +6,11 @@
6
6
  * @version 1.0.0
7
7
  */
8
8
 
9
- interface Context {
10
- projectId?: string | null
11
- projectPath: string
12
- params: Record<string, unknown>
13
- }
9
+ import type { ProjectContext, ContextState } from '../types'
14
10
 
15
- interface State {
16
- now?: string | null
17
- next?: string | null
18
- shipped?: string | null
19
- analysis?: string | null
20
- [key: string]: unknown
21
- }
11
+ // Type aliases for compatibility with ProjectContext from contextBuilder.build()
12
+ type Context = Pick<ProjectContext, 'projectId' | 'projectPath' | 'params'>
13
+ type State = ContextState
22
14
 
23
15
  interface ReasoningStep {
24
16
  step: string
@@ -24,6 +24,7 @@ import type {
24
24
  SimpleExecutionResult,
25
25
  ExecutionToolsFn,
26
26
  ApprovalContext,
27
+ PromptContext,
27
28
  } from '../types'
28
29
 
29
30
  // =============================================================================
@@ -42,7 +43,7 @@ export function signalStart(commandName: string): void {
42
43
  fs.mkdirSync(dir, { recursive: true })
43
44
  }
44
45
  fs.writeFileSync(RUNNING_FILE, `/p:${commandName}`)
45
- } catch {
46
+ } catch (_error) {
46
47
  // Silently ignore - status line is optional
47
48
  }
48
49
  }
@@ -55,7 +56,7 @@ export function signalEnd(): void {
55
56
  if (fs.existsSync(RUNNING_FILE)) {
56
57
  fs.unlinkSync(RUNNING_FILE)
57
58
  }
58
- } catch {
59
+ } catch (_error) {
59
60
  // Silently ignore - status line is optional
60
61
  }
61
62
  }
@@ -132,7 +133,7 @@ export class CommandExecutor {
132
133
  const preState = await contextBuilder.loadStateForCommand(metadataContext, commandName)
133
134
  groundTruthResult = await groundTruth.verify(
134
135
  commandName,
135
- metadataContext as unknown as Parameters<typeof groundTruth.verify>[1],
136
+ metadataContext,
136
137
  preState
137
138
  )
138
139
 
@@ -148,8 +149,8 @@ export class CommandExecutor {
148
149
  const reasoningState = await contextBuilder.loadStateForCommand(metadataContext, commandName)
149
150
  reasoning = await chainOfThought.reason(
150
151
  commandName,
151
- metadataContext as unknown as Parameters<typeof chainOfThought.reason>[1],
152
- reasoningState as Parameters<typeof chainOfThought.reason>[2]
152
+ metadataContext,
153
+ reasoningState
153
154
  )
154
155
 
155
156
  // If reasoning shows critical issues, warn but continue
@@ -160,11 +161,9 @@ export class CommandExecutor {
160
161
  }
161
162
 
162
163
  // 3. AGENTIC: Claude decides agent assignment via templates
163
- let context: Record<string, unknown> = metadataContext as unknown as Record<string, unknown>
164
-
165
- // Provide agent info to context so Claude can delegate
166
- context = {
167
- ...context,
164
+ // Build context with agent routing info for Claude delegation
165
+ const context: PromptContext = {
166
+ ...metadataContext,
168
167
  agentsPath: path.join(os.homedir(), '.prjct-cli', 'projects', metadataContext.projectId || '', 'agents'),
169
168
  agentRoutingPath: path.join(__dirname, '..', '..', 'templates', 'agentic', 'agent-routing.md'),
170
169
  agenticDelegation: true,
@@ -205,7 +204,7 @@ export class CommandExecutor {
205
204
  // Agent is null - Claude assigns via Task tool using agent-routing.md
206
205
  const prompt = promptBuilder.build(
207
206
  template,
208
- context as Parameters<typeof promptBuilder.build>[1],
207
+ context,
209
208
  state,
210
209
  null,
211
210
  learnedPatterns,
@@ -9,6 +9,7 @@
9
9
  import fs from 'fs/promises'
10
10
  import pathManager from '../infrastructure/path-manager'
11
11
  import configManager from '../infrastructure/config-manager'
12
+ import { isNotFoundError } from '../types/fs'
12
13
  import type { ContextPaths, ProjectContext, ContextState } from '../types'
13
14
 
14
15
  // Re-export types for convenience
@@ -113,10 +114,14 @@ class ContextBuilder {
113
114
  this._cache.delete(filePath)
114
115
  this._mtimes.delete(filePath)
115
116
  }
116
- } catch {
117
- // File doesn't exist - invalidate cache
118
- this._cache.delete(filePath)
119
- this._mtimes.delete(filePath)
117
+ } catch (error) {
118
+ // File doesn't exist or access error - invalidate cache
119
+ if (isNotFoundError(error)) {
120
+ this._cache.delete(filePath)
121
+ this._mtimes.delete(filePath)
122
+ } else {
123
+ throw error
124
+ }
120
125
  }
121
126
  }
122
127
  }
@@ -137,8 +142,11 @@ class ContextBuilder {
137
142
  try {
138
143
  const [content, stat] = await Promise.all([fs.readFile(filePath, 'utf-8'), fs.stat(filePath)])
139
144
  return { key, filePath, content, mtime: stat.mtimeMs }
140
- } catch {
141
- return { key, filePath, content: null, mtime: null }
145
+ } catch (error) {
146
+ if (isNotFoundError(error)) {
147
+ return { key, filePath, content: null, mtime: null }
148
+ }
149
+ throw error
142
150
  }
143
151
  })
144
152
 
@@ -227,8 +235,11 @@ class ContextBuilder {
227
235
  try {
228
236
  const content = await fs.readFile(filePath, 'utf-8')
229
237
  return { filePath, content }
230
- } catch {
231
- return { filePath, content: null }
238
+ } catch (error) {
239
+ if (isNotFoundError(error)) {
240
+ return { filePath, content: null }
241
+ }
242
+ throw error
232
243
  }
233
244
  })
234
245
 
@@ -264,8 +275,11 @@ class ContextBuilder {
264
275
  try {
265
276
  await fs.access(filePath)
266
277
  return true
267
- } catch {
268
- return false
278
+ } catch (error) {
279
+ if (isNotFoundError(error)) {
280
+ return false
281
+ }
282
+ throw error
269
283
  }
270
284
  }
271
285