prjct-cli 0.30.3 → 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.
- package/CHANGELOG.md +118 -0
- package/CLAUDE.md +41 -0
- package/assets/statusline/components/jira.sh +108 -0
- package/assets/statusline/default-config.json +8 -1
- package/assets/statusline/lib/config.sh +21 -5
- package/core/__tests__/agentic/memory-system.test.ts +2 -2
- package/core/__tests__/types/fs.test.ts +125 -0
- package/core/agentic/agent-router.ts +16 -4
- package/core/agentic/chain-of-thought.ts +4 -12
- package/core/agentic/command-executor.ts +10 -11
- package/core/agentic/context-builder.ts +24 -10
- package/core/agentic/ground-truth.ts +139 -55
- package/core/agentic/prompt-builder.ts +20 -7
- package/core/agentic/smart-context.ts +1 -1
- package/core/agentic/template-loader.ts +1 -1
- package/core/agentic/tool-registry.ts +4 -2
- package/core/bus/bus.ts +1 -1
- package/core/commands/cleanup.ts +24 -8
- package/core/commands/planning.ts +4 -2
- package/core/commands/setup.ts +4 -4
- package/core/commands/shipping.ts +34 -8
- package/core/commands/snapshots.ts +27 -13
- package/core/context/generator.ts +9 -5
- package/core/domain/agent-generator.ts +1 -1
- package/core/domain/agent-loader.ts +1 -1
- package/core/domain/analyzer.ts +76 -31
- package/core/domain/context-estimator.ts +1 -1
- package/core/domain/snapshot-manager.ts +55 -21
- package/core/domain/task-stack.ts +16 -7
- package/core/infrastructure/author-detector.ts +1 -1
- package/core/infrastructure/claude-agent.ts +12 -8
- package/core/infrastructure/command-installer.ts +42 -21
- package/core/infrastructure/editors-config.ts +1 -1
- package/core/infrastructure/path-manager.ts +27 -2
- package/core/infrastructure/permission-manager.ts +1 -1
- package/core/infrastructure/setup.ts +31 -13
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/manager.ts +2 -1
- package/core/integrations/jira/client.ts +753 -0
- package/core/integrations/jira/index.ts +36 -0
- package/core/integrations/jira/mcp-adapter.ts +451 -0
- package/core/integrations/linear/client.ts +23 -3
- package/core/plugin/loader.ts +16 -6
- package/core/plugin/registry.ts +16 -6
- package/core/server/routes-extended.ts +13 -6
- package/core/server/routes.ts +15 -5
- package/core/server/sse.ts +4 -3
- package/core/services/agent-service.ts +4 -2
- package/core/services/memory-service.ts +16 -5
- package/core/services/project-service.ts +11 -2
- package/core/services/skill-service.ts +4 -3
- package/core/session/compaction.ts +4 -5
- package/core/session/metrics.ts +11 -4
- package/core/session/task-session-manager.ts +27 -9
- package/core/storage/storage-manager.ts +12 -5
- package/core/storage/storage.ts +26 -10
- package/core/sync/auth-config.ts +2 -2
- package/core/sync/oauth-handler.ts +1 -1
- package/core/sync/sync-client.ts +4 -2
- package/core/sync/sync-manager.ts +1 -1
- package/core/types/agentic.ts +8 -18
- package/core/types/config.ts +1 -1
- package/core/types/index.ts +3 -2
- package/core/types/integrations.ts +4 -48
- package/core/types/storage.ts +0 -8
- package/core/types/task.ts +0 -4
- package/core/utils/file-helper.ts +10 -4
- package/core/utils/jsonl-helper.ts +4 -4
- package/core/utils/keychain.ts +130 -0
- package/core/utils/logger.ts +27 -25
- package/core/utils/runtime.ts +1 -1
- package/core/utils/session-helper.ts +4 -4
- package/core/utils/version.ts +1 -1
- package/dist/bin/prjct.mjs +1 -1
- package/package.json +1 -1
- package/packages/shared/src/utils.ts +1 -1
- package/templates/commands/enrich.md +601 -195
- package/templates/commands/github.md +231 -0
- package/templates/commands/init.md +45 -26
- package/templates/commands/jira.md +276 -0
- package/templates/commands/linear.md +159 -177
- package/templates/commands/monday.md +196 -0
- package/templates/commands/setup.md +4 -1
- package/templates/mcp-config.json +42 -39
- package/core/integrations/notion/client.ts +0 -413
- package/core/integrations/notion/index.ts +0 -46
- package/core/integrations/notion/setup.ts +0 -235
- package/core/integrations/notion/sync.ts +0 -818
- package/core/integrations/notion/templates.ts +0 -246
- package/core/plugin/builtin/notion.ts +0 -178
- package/templates/skills/notion-push.md +0 -116
- package/templates/skills/notion-setup.md +0 -199
- 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
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# prjct statusline - JIRA integration component
|
|
3
|
+
# Displays the linked JIRA issue key and priority
|
|
4
|
+
|
|
5
|
+
component_jira() {
|
|
6
|
+
component_enabled "jira" || return
|
|
7
|
+
[[ "${CONFIG_JIRA_ENABLED}" != "true" ]] && return
|
|
8
|
+
|
|
9
|
+
local cache_file="${CACHE_DIR}/jira.cache"
|
|
10
|
+
local issue_data=""
|
|
11
|
+
|
|
12
|
+
# Check cache first
|
|
13
|
+
if cache_valid "$cache_file" "$CONFIG_CACHE_TTL_JIRA"; then
|
|
14
|
+
issue_data=$(cat "$cache_file")
|
|
15
|
+
else
|
|
16
|
+
# Get project ID
|
|
17
|
+
local project_id=$(get_project_id)
|
|
18
|
+
[[ -z "$project_id" ]] && return
|
|
19
|
+
|
|
20
|
+
local global_path="${HOME}/.prjct-cli/projects/${project_id}"
|
|
21
|
+
local state_file="${global_path}/storage/state.json"
|
|
22
|
+
local issues_file="${global_path}/storage/issues.json"
|
|
23
|
+
|
|
24
|
+
# Check if state file exists
|
|
25
|
+
[[ ! -f "$state_file" ]] && return
|
|
26
|
+
|
|
27
|
+
# Get linked issue from current task
|
|
28
|
+
local linked_issue=""
|
|
29
|
+
|
|
30
|
+
# First check if current task has a linked JIRA issue
|
|
31
|
+
local linked_provider=$(jq -r '.currentTask.linkedIssue.provider // ""' "$state_file" 2>/dev/null)
|
|
32
|
+
if [[ "$linked_provider" == "jira" ]]; then
|
|
33
|
+
linked_issue=$(jq -r '.currentTask.linkedIssue.id // ""' "$state_file" 2>/dev/null)
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# If no linked issue, try to get from issues.json (last worked)
|
|
37
|
+
if [[ -z "$linked_issue" ]] && [[ -f "$issues_file" ]]; then
|
|
38
|
+
# Check if issues.json is for JIRA
|
|
39
|
+
local provider=$(jq -r '.provider // ""' "$issues_file" 2>/dev/null)
|
|
40
|
+
if [[ "$provider" == "jira" ]]; then
|
|
41
|
+
# Get most recently updated issue that's in_progress
|
|
42
|
+
linked_issue=$(jq -r '
|
|
43
|
+
.issues // {} | to_entries
|
|
44
|
+
| map(select(.value.status == "in_progress"))
|
|
45
|
+
| sort_by(.value.updatedAt) | last
|
|
46
|
+
| .key // ""
|
|
47
|
+
' "$issues_file" 2>/dev/null)
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
[[ -z "$linked_issue" ]] && return
|
|
52
|
+
|
|
53
|
+
# Get issue details from issues cache
|
|
54
|
+
if [[ -f "$issues_file" ]]; then
|
|
55
|
+
issue_data=$(jq -r --arg id "$linked_issue" '
|
|
56
|
+
.issues[$id] // {} |
|
|
57
|
+
"\(.externalId // "")|\(.priority // "none")|\(.status // "")"
|
|
58
|
+
' "$issues_file" 2>/dev/null)
|
|
59
|
+
|
|
60
|
+
# Cache the result
|
|
61
|
+
write_cache "$cache_file" "$issue_data"
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Return empty if no data
|
|
66
|
+
[[ -z "$issue_data" || "$issue_data" == "||" ]] && return
|
|
67
|
+
|
|
68
|
+
# Parse issue data
|
|
69
|
+
local issue_key=$(echo "$issue_data" | cut -d'|' -f1)
|
|
70
|
+
local priority=$(echo "$issue_data" | cut -d'|' -f2)
|
|
71
|
+
local status=$(echo "$issue_data" | cut -d'|' -f3)
|
|
72
|
+
|
|
73
|
+
[[ -z "$issue_key" ]] && return
|
|
74
|
+
|
|
75
|
+
# Format output with JIRA-style coloring
|
|
76
|
+
local output=""
|
|
77
|
+
|
|
78
|
+
# Add status indicator if enabled
|
|
79
|
+
if [[ "${CONFIG_JIRA_SHOW_STATUS}" == "true" ]] && [[ -n "$status" ]]; then
|
|
80
|
+
local status_icon=$(get_jira_status_icon "$status")
|
|
81
|
+
[[ -n "$status_icon" ]] && output+="${status_icon} "
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Issue key
|
|
85
|
+
output+="${ACCENT}${issue_key}${NC}"
|
|
86
|
+
|
|
87
|
+
# Add priority icon if enabled and priority is significant
|
|
88
|
+
if [[ "${CONFIG_JIRA_SHOW_PRIORITY}" == "true" ]]; then
|
|
89
|
+
local priority_icon=$(get_priority_icon "$priority")
|
|
90
|
+
[[ -n "$priority_icon" ]] && output+=" ${priority_icon}"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo -e "$output"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Get JIRA-specific status icon
|
|
97
|
+
get_jira_status_icon() {
|
|
98
|
+
local status="$1"
|
|
99
|
+
case "$status" in
|
|
100
|
+
backlog) echo "📋" ;;
|
|
101
|
+
todo) echo "📝" ;;
|
|
102
|
+
in_progress) echo "🔄" ;;
|
|
103
|
+
in_review) echo "👀" ;;
|
|
104
|
+
done) echo "✅" ;;
|
|
105
|
+
cancelled) echo "❌" ;;
|
|
106
|
+
*) echo "" ;;
|
|
107
|
+
esac
|
|
108
|
+
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
"cacheTTL": {
|
|
5
5
|
"prjct": 30,
|
|
6
6
|
"git": 5,
|
|
7
|
-
"linear": 60
|
|
7
|
+
"linear": 60,
|
|
8
|
+
"jira": 60
|
|
8
9
|
},
|
|
9
10
|
"components": {
|
|
10
11
|
"prjct_icon": {
|
|
@@ -21,6 +22,12 @@
|
|
|
21
22
|
"position": 2,
|
|
22
23
|
"showPriority": true
|
|
23
24
|
},
|
|
25
|
+
"jira": {
|
|
26
|
+
"enabled": false,
|
|
27
|
+
"position": 2,
|
|
28
|
+
"showPriority": true,
|
|
29
|
+
"showStatus": false
|
|
30
|
+
},
|
|
24
31
|
"dir": {
|
|
25
32
|
"enabled": true,
|
|
26
33
|
"position": 3
|
|
@@ -10,6 +10,7 @@ DEFAULT_THEME="default"
|
|
|
10
10
|
DEFAULT_CACHE_TTL_PRJCT=30
|
|
11
11
|
DEFAULT_CACHE_TTL_GIT=5
|
|
12
12
|
DEFAULT_CACHE_TTL_LINEAR=60
|
|
13
|
+
DEFAULT_CACHE_TTL_JIRA=60
|
|
13
14
|
DEFAULT_TASK_MAX_LENGTH=25
|
|
14
15
|
DEFAULT_CONTEXT_MIN_PERCENT=30
|
|
15
16
|
DEFAULT_ENRICHMENT_ENABLED="true"
|
|
@@ -26,16 +27,21 @@ load_config() {
|
|
|
26
27
|
CONFIG_CACHE_TTL_PRJCT="$DEFAULT_CACHE_TTL_PRJCT"
|
|
27
28
|
CONFIG_CACHE_TTL_GIT="$DEFAULT_CACHE_TTL_GIT"
|
|
28
29
|
CONFIG_CACHE_TTL_LINEAR="$DEFAULT_CACHE_TTL_LINEAR"
|
|
30
|
+
CONFIG_CACHE_TTL_JIRA="$DEFAULT_CACHE_TTL_JIRA"
|
|
29
31
|
CONFIG_TASK_MAX_LENGTH="$DEFAULT_TASK_MAX_LENGTH"
|
|
30
32
|
CONFIG_CONTEXT_MIN_PERCENT="$DEFAULT_CONTEXT_MIN_PERCENT"
|
|
31
33
|
CONFIG_LINEAR_ENABLED="true"
|
|
32
34
|
CONFIG_LINEAR_SHOW_PRIORITY="true"
|
|
35
|
+
CONFIG_JIRA_ENABLED="false"
|
|
36
|
+
CONFIG_JIRA_SHOW_PRIORITY="true"
|
|
37
|
+
CONFIG_JIRA_SHOW_STATUS="false"
|
|
33
38
|
CONFIG_ENRICHMENT_ENABLED="$DEFAULT_ENRICHMENT_ENABLED"
|
|
34
39
|
|
|
35
40
|
# Default component configuration
|
|
36
41
|
COMPONENT_ENABLED["prjct_icon"]="true"
|
|
37
42
|
COMPONENT_ENABLED["task"]="true"
|
|
38
43
|
COMPONENT_ENABLED["linear"]="true"
|
|
44
|
+
COMPONENT_ENABLED["jira"]="false"
|
|
39
45
|
COMPONENT_ENABLED["dir"]="true"
|
|
40
46
|
COMPONENT_ENABLED["git"]="true"
|
|
41
47
|
COMPONENT_ENABLED["changes"]="true"
|
|
@@ -45,6 +51,7 @@ load_config() {
|
|
|
45
51
|
COMPONENT_POSITION["prjct_icon"]=0
|
|
46
52
|
COMPONENT_POSITION["task"]=1
|
|
47
53
|
COMPONENT_POSITION["linear"]=2
|
|
54
|
+
COMPONENT_POSITION["jira"]=2
|
|
48
55
|
COMPONENT_POSITION["dir"]=3
|
|
49
56
|
COMPONENT_POSITION["git"]=4
|
|
50
57
|
COMPONENT_POSITION["changes"]=5
|
|
@@ -63,12 +70,16 @@ load_config() {
|
|
|
63
70
|
(.cacheTTL.prjct // 30),
|
|
64
71
|
(.cacheTTL.git // 5),
|
|
65
72
|
(.cacheTTL.linear // 60),
|
|
73
|
+
(.cacheTTL.jira // 60),
|
|
66
74
|
(.components.task.maxLength // 25),
|
|
67
75
|
(.components.context.minPercent // 30),
|
|
68
76
|
(if .components.linear.showPriority == null then true else .components.linear.showPriority end),
|
|
77
|
+
(if .components.jira.showPriority == null then true else .components.jira.showPriority end),
|
|
78
|
+
(if .components.jira.showStatus == null then false else .components.jira.showStatus end),
|
|
69
79
|
(if .components.prjct_icon.enabled == null then true else .components.prjct_icon.enabled end),
|
|
70
80
|
(if .components.task.enabled == null then true else .components.task.enabled end),
|
|
71
81
|
(if .components.linear.enabled == null then true else .components.linear.enabled end),
|
|
82
|
+
(if .components.jira.enabled == null then false else .components.jira.enabled end),
|
|
72
83
|
(if .components.dir.enabled == null then true else .components.dir.enabled end),
|
|
73
84
|
(if .components.git.enabled == null then true else .components.git.enabled end),
|
|
74
85
|
(if .components.changes.enabled == null then true else .components.changes.enabled end),
|
|
@@ -78,6 +89,7 @@ load_config() {
|
|
|
78
89
|
(.components.prjct_icon.position // 0),
|
|
79
90
|
(.components.task.position // 1),
|
|
80
91
|
(.components.linear.position // 2),
|
|
92
|
+
(.components.jira.position // 2),
|
|
81
93
|
(.components.dir.position // 3),
|
|
82
94
|
(.components.git.position // 4),
|
|
83
95
|
(.components.changes.position // 5),
|
|
@@ -92,10 +104,11 @@ load_config() {
|
|
|
92
104
|
local old_ifs="$IFS"
|
|
93
105
|
IFS=$'\t' read -r \
|
|
94
106
|
CONFIG_THEME \
|
|
95
|
-
CONFIG_CACHE_TTL_PRJCT CONFIG_CACHE_TTL_GIT CONFIG_CACHE_TTL_LINEAR \
|
|
96
|
-
CONFIG_TASK_MAX_LENGTH CONFIG_CONTEXT_MIN_PERCENT
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
CONFIG_CACHE_TTL_PRJCT CONFIG_CACHE_TTL_GIT CONFIG_CACHE_TTL_LINEAR CONFIG_CACHE_TTL_JIRA \
|
|
108
|
+
CONFIG_TASK_MAX_LENGTH CONFIG_CONTEXT_MIN_PERCENT \
|
|
109
|
+
CONFIG_LINEAR_SHOW_PRIORITY CONFIG_JIRA_SHOW_PRIORITY CONFIG_JIRA_SHOW_STATUS \
|
|
110
|
+
E_PRJCT_ICON E_TASK E_LINEAR E_JIRA E_DIR E_GIT E_CHANGES E_CONTEXT E_MODEL E_ENRICHMENT \
|
|
111
|
+
P_PRJCT_ICON P_TASK P_LINEAR P_JIRA P_DIR P_GIT P_CHANGES P_CONTEXT P_MODEL \
|
|
99
112
|
<<< "$config_data"
|
|
100
113
|
IFS="$old_ifs"
|
|
101
114
|
|
|
@@ -103,6 +116,7 @@ load_config() {
|
|
|
103
116
|
COMPONENT_ENABLED["prjct_icon"]="$E_PRJCT_ICON"
|
|
104
117
|
COMPONENT_ENABLED["task"]="$E_TASK"
|
|
105
118
|
COMPONENT_ENABLED["linear"]="$E_LINEAR"
|
|
119
|
+
COMPONENT_ENABLED["jira"]="$E_JIRA"
|
|
106
120
|
COMPONENT_ENABLED["dir"]="$E_DIR"
|
|
107
121
|
COMPONENT_ENABLED["git"]="$E_GIT"
|
|
108
122
|
COMPONENT_ENABLED["changes"]="$E_CHANGES"
|
|
@@ -113,14 +127,16 @@ load_config() {
|
|
|
113
127
|
COMPONENT_POSITION["prjct_icon"]="$P_PRJCT_ICON"
|
|
114
128
|
COMPONENT_POSITION["task"]="$P_TASK"
|
|
115
129
|
COMPONENT_POSITION["linear"]="$P_LINEAR"
|
|
130
|
+
COMPONENT_POSITION["jira"]="$P_JIRA"
|
|
116
131
|
COMPONENT_POSITION["dir"]="$P_DIR"
|
|
117
132
|
COMPONENT_POSITION["git"]="$P_GIT"
|
|
118
133
|
COMPONENT_POSITION["changes"]="$P_CHANGES"
|
|
119
134
|
COMPONENT_POSITION["context"]="$P_CONTEXT"
|
|
120
135
|
COMPONENT_POSITION["model"]="$P_MODEL"
|
|
121
136
|
|
|
122
|
-
# Update linear enabled based on component config
|
|
137
|
+
# Update linear/jira enabled based on component config
|
|
123
138
|
CONFIG_LINEAR_ENABLED="${COMPONENT_ENABLED["linear"]}"
|
|
139
|
+
CONFIG_JIRA_ENABLED="${COMPONENT_ENABLED["jira"]}"
|
|
124
140
|
|
|
125
141
|
# Update enrichment enabled from config
|
|
126
142
|
[[ -n "$E_ENRICHMENT" ]] && CONFIG_ENRICHMENT_ENABLED="$E_ENRICHMENT"
|
|
@@ -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
|
-
//
|
|
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
|
-
|
|
10
|
-
projectId?: string | null
|
|
11
|
-
projectPath: string
|
|
12
|
-
params: Record<string, unknown>
|
|
13
|
-
}
|
|
9
|
+
import type { ProjectContext, ContextState } from '../types'
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|