prjct-cli 0.55.2 β†’ 0.55.3

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 CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.55.3] - 2026-02-05
4
+
5
+ ### Bug Fixes
6
+
7
+ - ensure test isolation in IndexStorage tests
8
+ - remove MCP integrations, keep only Context7 (#87)
9
+
10
+
11
+ ## [0.56.0] - 2026-02-04
12
+
13
+ ### Breaking Changes
14
+
15
+ - **Removed MCP-based integrations** (Monday.com, GitHub Issues)
16
+ - Only Context7 remains as the sole MCP server (for library docs)
17
+ - Linear and JIRA use SDK/REST API directly (4x faster)
18
+
19
+ ### Features
20
+
21
+ - **Context7 auto-install**: Now automatically configured in `~/.claude/mcp.json` on `prjct init`
22
+
23
+ ### Fixed
24
+
25
+ - Templates no longer reference non-existent CLI commands (`prjct context task`, etc.)
26
+ - All workflow templates (task, done, bug, pause, resume, next, dash) now use Read/Write directly
27
+
28
+ ### Removed
29
+
30
+ - `templates/commands/monday.md` - MCP integration
31
+ - `templates/commands/github.md` - MCP integration
32
+ - `templates/_bases/tracker-base.md` - MCP base template
33
+ - `core/integrations/jira/mcp-adapter.ts` - Unused MCP adapter
34
+
3
35
  ## [0.55.2] - 2026-02-04
4
36
 
5
37
  ### Bug Fixes
@@ -184,9 +184,11 @@ describe('FileScorer', () => {
184
184
  })
185
185
 
186
186
  describe('IndexStorage', () => {
187
- const testProjectId = `test-project-${Date.now()}`
187
+ let testProjectId: string
188
188
 
189
189
  beforeEach(async () => {
190
+ // Generate unique project ID for each test to ensure isolation
191
+ testProjectId = `test-project-${Date.now()}-${Math.random().toString(36).slice(2)}`
190
192
  // Set up test directory
191
193
  const testDir = path.join(os.tmpdir(), `prjct-test-${Date.now()}`)
192
194
  pathManager.setGlobalBaseDir(testDir)
@@ -169,6 +169,9 @@ export async function run(): Promise<SetupResults> {
169
169
 
170
170
  // Install status line (Claude only)
171
171
  await installStatusLine()
172
+
173
+ // Install Context7 MCP (only MCP server prjct uses)
174
+ await installContext7MCP()
172
175
  }
173
176
  } else if (providerName === 'gemini') {
174
177
  // Gemini provider - install router and global config
@@ -875,6 +878,58 @@ echo "prjct"
875
878
  }
876
879
  }
877
880
 
881
+ /**
882
+ * Install Context7 MCP server configuration
883
+ *
884
+ * Context7 is the ONLY MCP server prjct uses - for library documentation lookup.
885
+ * All issue tracker integrations (Linear, JIRA) use SDK/REST API directly.
886
+ */
887
+ async function installContext7MCP(): Promise<void> {
888
+ try {
889
+ const claudeDir = path.join(os.homedir(), '.claude')
890
+ const mcpConfigPath = path.join(claudeDir, 'mcp.json')
891
+
892
+ // Ensure ~/.claude directory exists
893
+ if (!fs.existsSync(claudeDir)) {
894
+ fs.mkdirSync(claudeDir, { recursive: true })
895
+ }
896
+
897
+ // Context7 MCP configuration
898
+ const context7Config = {
899
+ mcpServers: {
900
+ context7: {
901
+ command: 'npx',
902
+ args: ['-y', '@upstash/context7-mcp@latest'],
903
+ },
904
+ },
905
+ }
906
+
907
+ // Check if mcp.json exists
908
+ if (fs.existsSync(mcpConfigPath)) {
909
+ // Read existing config
910
+ const existingContent = fs.readFileSync(mcpConfigPath, 'utf-8')
911
+ const existingConfig = JSON.parse(existingContent)
912
+
913
+ // Check if context7 is already configured
914
+ if (existingConfig.mcpServers?.context7) {
915
+ // Already configured, skip
916
+ return
917
+ }
918
+
919
+ // Add context7 to existing config
920
+ existingConfig.mcpServers = existingConfig.mcpServers || {}
921
+ existingConfig.mcpServers.context7 = context7Config.mcpServers.context7
922
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(existingConfig, null, 2), 'utf-8')
923
+ } else {
924
+ // Create new mcp.json with context7
925
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(context7Config, null, 2), 'utf-8')
926
+ }
927
+ } catch (error) {
928
+ // Non-fatal error, just log
929
+ console.error(`Context7 MCP setup warning: ${(error as Error).message}`)
930
+ }
931
+ }
932
+
878
933
  /**
879
934
  * Install statusline modules (lib/ or components/)
880
935
  * Copies .sh files from source to destination, always overwriting for updates
@@ -19,20 +19,5 @@ export {
19
19
  } from './cache'
20
20
  // REST API client
21
21
  export { type JiraAuthMode, JiraProvider, jiraProvider } from './client'
22
- // MCP adapter (deprecated - will be removed)
23
- export {
24
- createCreateIssueInstruction,
25
- createGetIssueInstruction,
26
- // MCP instruction generators
27
- createSearchInstruction,
28
- createTransitionInstruction,
29
- createUpdateInstruction,
30
- getMCPSetupInstructions,
31
- // Utilities
32
- isMCPAvailable,
33
- JiraMCPAdapter,
34
- jiraMCPAdapter,
35
- type MCPInstruction,
36
- } from './mcp-adapter'
37
22
  // Service layer with caching (preferred API)
38
23
  export { JiraService, jiraService } from './service'
@@ -26,6 +26,7 @@ import {
26
26
  type ProjectContext,
27
27
  resolveToolIds,
28
28
  } from '../ai-tools'
29
+ import commandInstaller from '../infrastructure/command-installer'
29
30
  import configManager from '../infrastructure/config-manager'
30
31
  import pathManager from '../infrastructure/path-manager'
31
32
  import { metricsStorage } from '../storage/metrics-storage'
@@ -232,6 +233,11 @@ class SyncService {
232
233
  const duration = Date.now() - startTime
233
234
  const syncMetrics = await this.recordSyncMetrics(stats, contextFiles, agents, duration)
234
235
 
236
+ // 10. Update global config and commands (CLI does EVERYTHING)
237
+ // This ensures `prjct sync` from terminal updates global CLAUDE.md and commands
238
+ await commandInstaller.installGlobalConfig()
239
+ await commandInstaller.syncCommands()
240
+
235
241
  return {
236
242
  success: true,
237
243
  projectId: this.projectId,
@@ -19105,6 +19105,7 @@ var init_sync_service = __esm({
19105
19105
  "core/services/sync-service.ts"() {
19106
19106
  "use strict";
19107
19107
  init_ai_tools();
19108
+ init_command_installer();
19108
19109
  init_config_manager();
19109
19110
  init_path_manager();
19110
19111
  init_metrics_storage();
@@ -19203,6 +19204,8 @@ var init_sync_service = __esm({
19203
19204
  ]);
19204
19205
  const duration = Date.now() - startTime;
19205
19206
  const syncMetrics = await this.recordSyncMetrics(stats, contextFiles, agents, duration);
19207
+ await command_installer_default.installGlobalConfig();
19208
+ await command_installer_default.syncCommands();
19206
19209
  return {
19207
19210
  success: true,
19208
19211
  projectId: this.projectId,
@@ -21231,6 +21234,7 @@ async function run() {
21231
21234
  }
21232
21235
  await command_installer_default.installDocs();
21233
21236
  await installStatusLine();
21237
+ await installContext7MCP();
21234
21238
  }
21235
21239
  } else if (providerName === "gemini") {
21236
21240
  const geminiInstalled = await installGeminiRouter();
@@ -21681,6 +21685,37 @@ echo "prjct"
21681
21685
  }
21682
21686
  }
21683
21687
  }
21688
+ async function installContext7MCP() {
21689
+ try {
21690
+ const claudeDir = path44.join(os13.homedir(), ".claude");
21691
+ const mcpConfigPath = path44.join(claudeDir, "mcp.json");
21692
+ if (!fs41.existsSync(claudeDir)) {
21693
+ fs41.mkdirSync(claudeDir, { recursive: true });
21694
+ }
21695
+ const context7Config = {
21696
+ mcpServers: {
21697
+ context7: {
21698
+ command: "npx",
21699
+ args: ["-y", "@upstash/context7-mcp@latest"]
21700
+ }
21701
+ }
21702
+ };
21703
+ if (fs41.existsSync(mcpConfigPath)) {
21704
+ const existingContent = fs41.readFileSync(mcpConfigPath, "utf-8");
21705
+ const existingConfig = JSON.parse(existingContent);
21706
+ if (existingConfig.mcpServers?.context7) {
21707
+ return;
21708
+ }
21709
+ existingConfig.mcpServers = existingConfig.mcpServers || {};
21710
+ existingConfig.mcpServers.context7 = context7Config.mcpServers.context7;
21711
+ fs41.writeFileSync(mcpConfigPath, JSON.stringify(existingConfig, null, 2), "utf-8");
21712
+ } else {
21713
+ fs41.writeFileSync(mcpConfigPath, JSON.stringify(context7Config, null, 2), "utf-8");
21714
+ }
21715
+ } catch (error) {
21716
+ console.error(`Context7 MCP setup warning: ${error.message}`);
21717
+ }
21718
+ }
21684
21719
  function installStatusLineModules(sourceDir, destDir) {
21685
21720
  if (!fs41.existsSync(sourceDir)) {
21686
21721
  return;
@@ -21777,6 +21812,7 @@ var init_setup = __esm({
21777
21812
  __name(migrateProjectsCliVersion, "migrateProjectsCliVersion");
21778
21813
  __name(ensureStatusLineSettings, "ensureStatusLineSettings");
21779
21814
  __name(installStatusLine, "installStatusLine");
21815
+ __name(installContext7MCP, "installContext7MCP");
21780
21816
  __name(installStatusLineModules, "installStatusLineModules");
21781
21817
  __name(ensureStatusLineSymlink, "ensureStatusLineSymlink");
21782
21818
  __name(showResults, "showResults");
@@ -25161,7 +25197,7 @@ var require_package = __commonJS({
25161
25197
  "package.json"(exports, module) {
25162
25198
  module.exports = {
25163
25199
  name: "prjct-cli",
25164
- version: "0.55.2",
25200
+ version: "0.55.3",
25165
25201
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
25166
25202
  main: "core/index.ts",
25167
25203
  bin: {
@@ -1168,6 +1168,7 @@ async function run() {
1168
1168
  }
1169
1169
  await command_installer_default.installDocs();
1170
1170
  await installStatusLine();
1171
+ await installContext7MCP();
1171
1172
  }
1172
1173
  } else if (providerName === "gemini") {
1173
1174
  const geminiInstalled = await installGeminiRouter();
@@ -1635,6 +1636,38 @@ echo "prjct"
1635
1636
  }
1636
1637
  }
1637
1638
  __name(installStatusLine, "installStatusLine");
1639
+ async function installContext7MCP() {
1640
+ try {
1641
+ const claudeDir = import_node_path5.default.join(import_node_os4.default.homedir(), ".claude");
1642
+ const mcpConfigPath = import_node_path5.default.join(claudeDir, "mcp.json");
1643
+ if (!import_node_fs3.default.existsSync(claudeDir)) {
1644
+ import_node_fs3.default.mkdirSync(claudeDir, { recursive: true });
1645
+ }
1646
+ const context7Config = {
1647
+ mcpServers: {
1648
+ context7: {
1649
+ command: "npx",
1650
+ args: ["-y", "@upstash/context7-mcp@latest"]
1651
+ }
1652
+ }
1653
+ };
1654
+ if (import_node_fs3.default.existsSync(mcpConfigPath)) {
1655
+ const existingContent = import_node_fs3.default.readFileSync(mcpConfigPath, "utf-8");
1656
+ const existingConfig = JSON.parse(existingContent);
1657
+ if (existingConfig.mcpServers?.context7) {
1658
+ return;
1659
+ }
1660
+ existingConfig.mcpServers = existingConfig.mcpServers || {};
1661
+ existingConfig.mcpServers.context7 = context7Config.mcpServers.context7;
1662
+ import_node_fs3.default.writeFileSync(mcpConfigPath, JSON.stringify(existingConfig, null, 2), "utf-8");
1663
+ } else {
1664
+ import_node_fs3.default.writeFileSync(mcpConfigPath, JSON.stringify(context7Config, null, 2), "utf-8");
1665
+ }
1666
+ } catch (error) {
1667
+ console.error(`Context7 MCP setup warning: ${error.message}`);
1668
+ }
1669
+ }
1670
+ __name(installContext7MCP, "installContext7MCP");
1638
1671
  function installStatusLineModules(sourceDir, destDir) {
1639
1672
  if (!import_node_fs3.default.existsSync(sourceDir)) {
1640
1673
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.55.2",
3
+ "version": "0.55.3",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {
@@ -4,35 +4,160 @@ allowed-tools: [Read, Write, Bash, Task, AskUserQuestion]
4
4
 
5
5
  # p. bug "$ARGUMENTS"
6
6
 
7
+ ## Step 1: Validate Arguments
8
+
9
+ ```
10
+ IF $ARGUMENTS is empty:
11
+ ASK: "What bug do you want to report?"
12
+ WAIT for response
13
+ DO NOT proceed with empty description
14
+ ```
15
+
16
+ ## Step 2: Resolve Project Paths
17
+
18
+ ```bash
19
+ # Get projectId from local config
20
+ cat .prjct/prjct.config.json | grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4
21
+ ```
22
+
23
+ Set `globalPath = ~/.prjct-cli/projects/{projectId}`
24
+
25
+ ## Step 3: Parse Severity from Keywords
26
+
27
+ Analyze `$ARGUMENTS` for severity indicators:
28
+ - `crash`, `down`, `broken`, `production`, `critical` β†’ **critical**
29
+ - `error`, `fail`, `exception`, `cannot` β†’ **high**
30
+ - `bug`, `incorrect`, `wrong`, `issue` β†’ **medium** (default)
31
+ - `minor`, `typo`, `cosmetic`, `ui` β†’ **low**
32
+
33
+ ## Step 4: Explore Codebase
34
+
35
+ ```
36
+ USE Task(Explore) β†’ find affected files, recent commits related to the bug
37
+ ```
38
+
39
+ ## Step 5: Check for Active Task
40
+
41
+ READ `{globalPath}/storage/state.json`
42
+
43
+ ```
44
+ IF currentTask exists AND currentTask.status == "active":
45
+ AskUserQuestion:
46
+ question: "You have an active task. How should we handle this bug?"
47
+ header: "Bug"
48
+ options:
49
+ - label: "Pause current and fix bug (Recommended)"
50
+ description: "Save current task, start bug fix"
51
+ - label: "Queue bug for later"
52
+ description: "Add to queue, continue current task"
53
+
54
+ IF "Queue bug for later":
55
+ # Add to queue and stop
56
+ READ {globalPath}/storage/queue.json (or create empty array)
57
+ APPEND bug to queue:
58
+ {
59
+ "id": "{uuid}",
60
+ "type": "bug",
61
+ "description": "$ARGUMENTS",
62
+ "severity": "{severity}",
63
+ "createdAt": "{timestamp}",
64
+ "status": "queued"
65
+ }
66
+ WRITE {globalPath}/storage/queue.json
67
+
68
+ OUTPUT:
69
+ """
70
+ πŸ› Queued: $ARGUMENTS [{severity}]
71
+
72
+ Continue with: {currentTask.description}
73
+
74
+ Later: `p. task` to work on queued bug
75
+ """
76
+ STOP
77
+
78
+ IF "Pause current and fix bug":
79
+ # Move current task to pausedTasks
80
+ # Will be handled in Step 6
81
+ ```
82
+
83
+ ## Step 6: Create Bug Branch
84
+
85
+ ```bash
86
+ git branch --show-current
87
+ ```
88
+
89
+ ```
90
+ IF current branch == "main" OR "master":
91
+ # Create bug fix branch
92
+ slug = sanitize($ARGUMENTS) # lowercase, hyphens, max 50 chars
93
+
94
+ git checkout -b bug/{slug}
95
+
96
+ IF git command fails:
97
+ OUTPUT: "Failed to create branch. Check git status."
98
+ STOP
99
+ ```
100
+
101
+ ## Step 7: Write State
102
+
103
+ Generate UUID and timestamp:
7
104
  ```bash
8
- prjct context bug $ARGUMENTS
105
+ node -e "console.log(require('crypto').randomUUID())"
106
+ node -e "console.log(new Date().toISOString())"
9
107
  ```
10
108
 
11
- Parse severity from keywords:
12
- - crash/down/broken/production β†’ critical
13
- - error/fail β†’ high
14
- - bug/incorrect β†’ medium (default)
15
- - minor/typo β†’ low
109
+ READ current state, then update:
16
110
 
17
- USE Task(Explore) β†’ find affected files, recent commits
111
+ ```
112
+ # If there was an active task, move it to pausedTasks
113
+ IF state.currentTask exists:
114
+ interruptedTask = state.currentTask
115
+ interruptedTask.status = "interrupted"
116
+ interruptedTask.interruptedAt = "{timestamp}"
117
+ interruptedTask.interruptedBy = "{new bug task id}"
18
118
 
19
- IF `currentTask` active:
20
- Ask: "Pause current and fix?" or "Queue for later"
21
- IF pause β†’ save as `interruptedTask`
119
+ state.pausedTasks = state.pausedTasks || []
120
+ state.pausedTasks.push(interruptedTask)
121
+ ```
22
122
 
23
- IF on main β†’ `git checkout -b bug/{slug}`
123
+ WRITE `{globalPath}/storage/state.json`:
124
+ ```json
125
+ {
126
+ "currentTask": {
127
+ "id": "{uuid}",
128
+ "description": "$ARGUMENTS",
129
+ "type": "bug",
130
+ "severity": "{severity}",
131
+ "status": "active",
132
+ "startedAt": "{timestamp}",
133
+ "branch": "bug/{slug}",
134
+ "affectedFiles": ["{files from exploration}"]
135
+ },
136
+ "pausedTasks": [{interruptedTask if any}]
137
+ }
138
+ ```
24
139
 
25
- UPDATE `{globalPath}/storage/state.json` with new bug task
26
- ADD to `{globalPath}/storage/queue.json`
140
+ ## Step 8: Log Event
141
+
142
+ APPEND to `{globalPath}/memory/events.jsonl`:
143
+ ```json
144
+ {"type":"bug_reported","taskId":"{uuid}","description":"$ARGUMENTS","severity":"{severity}","timestamp":"{timestamp}","branch":"bug/{slug}"}
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Output
27
150
 
28
- **Output**:
29
151
  ```
30
152
  πŸ› [{severity}] $ARGUMENTS
31
153
 
32
- Affected: {files}
33
- Branch: {branch}
154
+ Affected: {files from exploration}
155
+ Branch: bug/{slug}
156
+
157
+ {IF interruptedTask: "Paused: {interruptedTask.description}"}
34
158
 
35
159
  Next:
36
- - Fix the bug β†’ `p. done`
37
- - Queue only β†’ add `--later` flag
160
+ - Fix the bug β†’ work on code
161
+ - When fixed β†’ `p. done`
162
+ - Resume previous β†’ `p. resume`
38
163
  ```
@@ -1,32 +1,61 @@
1
1
  ---
2
- allowed-tools: [Read]
2
+ allowed-tools: [Read, Bash]
3
3
  ---
4
4
 
5
5
  # p. dash
6
6
 
7
+ ## Step 1: Resolve Project Paths
8
+
7
9
  ```bash
8
- prjct context dash
10
+ # Get projectId from local config
11
+ cat .prjct/prjct.config.json | grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4
9
12
  ```
10
13
 
14
+ Set `globalPath = ~/.prjct-cli/projects/{projectId}`
15
+
16
+ ## Step 2: Read All Storage Files
17
+
11
18
  READ all storage files:
12
- - `{globalPath}/storage/state.json` β†’ current/paused task
13
- - `{globalPath}/storage/queue.json` β†’ queue
14
- - `{globalPath}/storage/shipped.json` β†’ ships
15
- - `{globalPath}/storage/ideas.json` β†’ ideas
19
+ - `{globalPath}/storage/state.json` β†’ current/paused tasks
20
+ - `{globalPath}/storage/queue.json` β†’ queue (or empty array)
21
+ - `{globalPath}/storage/shipped.json` β†’ shipped features (or empty array)
22
+ - `{globalPath}/storage/ideas.json` β†’ ideas (or empty array)
23
+
24
+ ## Step 3: Calculate Metrics
25
+
26
+ ```
27
+ currentTask = state.currentTask
28
+ pausedTasks = state.pausedTasks || []
29
+ queueCount = queue.length
30
+ shippedCount = shipped.length
31
+ ideasCount = ideas.length
32
+
33
+ IF currentTask:
34
+ elapsed = time since currentTask.startedAt (or resumedAt)
35
+
36
+ IF shipped.length > 0:
37
+ lastShip = shipped[0]
38
+ daysSinceLastShip = days since lastShip.shippedAt
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Output (default)
16
44
 
17
- **Output (default)**:
18
45
  ```
19
46
  πŸ“Š DASHBOARD
20
47
 
21
- 🎯 Current: {task} ({elapsed})
22
- ⏸️ Paused: {paused_task or "None"}
48
+ 🎯 Current: {currentTask.parentDescription or currentTask.description} ({elapsed})
49
+ Subtask: {current subtask if exists}
50
+ ⏸️ Paused: {pausedTasks[0].description or "None"}
23
51
 
24
- πŸ“‹ Queue ({count})
25
- β€’ {task_1}
26
- β€’ {task_2}
52
+ πŸ“‹ Queue ({queueCount})
53
+ β€’ {queue[0].description}
54
+ β€’ {queue[1].description}
55
+ {... up to 5 items}
27
56
 
28
- πŸš€ Recent: {last_ship} ({days}d ago)
29
- πŸ’‘ Ideas: {count}
57
+ πŸš€ Recent: {lastShip.description} ({daysSinceLastShip}d ago)
58
+ πŸ’‘ Ideas: {ideasCount}
30
59
 
31
60
  Next:
32
61
  - Finish β†’ `p. done`
@@ -34,10 +63,37 @@ Next:
34
63
  - Queue β†’ `p. next`
35
64
  ```
36
65
 
37
- **Compact** (`p. dash compact`):
66
+ ---
67
+
68
+ ## Compact View (`p. dash compact`)
69
+
70
+ ```
71
+ 🎯 {currentTask.description} | πŸ“‹ {queueCount} | πŸš€ {daysSinceLastShip}d ago
38
72
  ```
39
- 🎯 {task} | πŸ“‹ {queue} | πŸš€ {days}d ago
73
+
74
+ ---
75
+
76
+ ## Week View (`p. dash week`)
77
+
78
+ Calculate from events.jsonl:
79
+ - Tasks completed this week
80
+ - Time spent (sum of task durations)
81
+ - Velocity (tasks/day)
82
+
40
83
  ```
84
+ πŸ“Š This Week
85
+
86
+ Completed: {count} tasks
87
+ Time: {hours}h focused
88
+ Velocity: {tasks_per_day}/day
89
+
90
+ Top areas:
91
+ - {area_1}: {count} tasks
92
+ - {area_2}: {count} tasks
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Month View (`p. dash month`)
41
98
 
42
- **Week/Month** (`p. dash week`):
43
- Show completed tasks, velocity, focus time
99
+ Same as week, but for last 30 days. Show weekly trends.