planflow-plugin 0.1.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.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/bin/cli.js +169 -0
- package/bin/postinstall.js +87 -0
- package/commands/pfActivity/SKILL.md +725 -0
- package/commands/pfAssign/SKILL.md +623 -0
- package/commands/pfCloudLink/SKILL.md +192 -0
- package/commands/pfCloudList/SKILL.md +222 -0
- package/commands/pfCloudNew/SKILL.md +187 -0
- package/commands/pfCloudUnlink/SKILL.md +152 -0
- package/commands/pfComment/SKILL.md +227 -0
- package/commands/pfComments/SKILL.md +159 -0
- package/commands/pfConnectionStatus/SKILL.md +433 -0
- package/commands/pfDiscord/SKILL.md +740 -0
- package/commands/pfGithubBranch/SKILL.md +672 -0
- package/commands/pfGithubIssue/SKILL.md +963 -0
- package/commands/pfGithubLink/SKILL.md +859 -0
- package/commands/pfGithubPr/SKILL.md +1335 -0
- package/commands/pfGithubUnlink/SKILL.md +401 -0
- package/commands/pfLive/SKILL.md +185 -0
- package/commands/pfLogin/SKILL.md +249 -0
- package/commands/pfLogout/SKILL.md +155 -0
- package/commands/pfMyTasks/SKILL.md +198 -0
- package/commands/pfNotificationSettings/SKILL.md +619 -0
- package/commands/pfNotifications/SKILL.md +420 -0
- package/commands/pfNotificationsClear/SKILL.md +421 -0
- package/commands/pfReact/SKILL.md +232 -0
- package/commands/pfSlack/SKILL.md +659 -0
- package/commands/pfSyncPull/SKILL.md +210 -0
- package/commands/pfSyncPush/SKILL.md +299 -0
- package/commands/pfSyncStatus/SKILL.md +212 -0
- package/commands/pfTeamInvite/SKILL.md +161 -0
- package/commands/pfTeamList/SKILL.md +253 -0
- package/commands/pfTeamRemove/SKILL.md +115 -0
- package/commands/pfTeamRole/SKILL.md +160 -0
- package/commands/pfTestWebhooks/SKILL.md +722 -0
- package/commands/pfUnassign/SKILL.md +134 -0
- package/commands/pfWhoami/SKILL.md +258 -0
- package/commands/pfWorkload/SKILL.md +219 -0
- package/commands/planExportCsv/SKILL.md +106 -0
- package/commands/planExportGithub/SKILL.md +222 -0
- package/commands/planExportJson/SKILL.md +159 -0
- package/commands/planExportSummary/SKILL.md +158 -0
- package/commands/planNew/SKILL.md +641 -0
- package/commands/planNext/SKILL.md +1200 -0
- package/commands/planSettingsAutoSync/SKILL.md +199 -0
- package/commands/planSettingsLanguage/SKILL.md +201 -0
- package/commands/planSettingsReset/SKILL.md +237 -0
- package/commands/planSettingsShow/SKILL.md +482 -0
- package/commands/planSpec/SKILL.md +929 -0
- package/commands/planUpdate/SKILL.md +2518 -0
- package/commands/team/SKILL.md +740 -0
- package/locales/en.json +1499 -0
- package/locales/ka.json +1499 -0
- package/package.json +48 -0
- package/templates/PROJECT_PLAN.template.md +157 -0
- package/templates/backend-api.template.md +562 -0
- package/templates/frontend-spa.template.md +610 -0
- package/templates/fullstack.template.md +397 -0
- package/templates/ka/backend-api.template.md +562 -0
- package/templates/ka/frontend-spa.template.md +610 -0
- package/templates/ka/fullstack.template.md +397 -0
- package/templates/sections/architecture.md +21 -0
- package/templates/sections/overview.md +15 -0
- package/templates/sections/tasks.md +22 -0
- package/templates/sections/tech-stack.md +19 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pfActivity
|
|
3
|
+
description: View recent activity in the current PlanFlow project
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PlanFlow Activity Feed
|
|
7
|
+
|
|
8
|
+
View recent activity in the linked cloud project with activity feed cards.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
/pfActivity # Show recent project activity
|
|
14
|
+
/pfActivity T2.1 # Show activity for specific task
|
|
15
|
+
/pfActivity --limit 20 # Show more entries (default: 10)
|
|
16
|
+
/pfActivity --type tasks # Filter by activity type
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Process
|
|
20
|
+
|
|
21
|
+
### Step 0: Load User Language & Translations
|
|
22
|
+
|
|
23
|
+
**CRITICAL: Execute this step FIRST, before any output!**
|
|
24
|
+
|
|
25
|
+
Load user's language preference using hierarchical config (local → global → default) and translation file.
|
|
26
|
+
|
|
27
|
+
**Pseudo-code:**
|
|
28
|
+
```javascript
|
|
29
|
+
// Read config with hierarchy AND MERGE
|
|
30
|
+
function getMergedConfig() {
|
|
31
|
+
let globalConfig = {}
|
|
32
|
+
let localConfig = {}
|
|
33
|
+
|
|
34
|
+
// Read global config first (base)
|
|
35
|
+
const globalPath = expandPath("~/.config/claude/plan-plugin-config.json")
|
|
36
|
+
if (fileExists(globalPath)) {
|
|
37
|
+
try {
|
|
38
|
+
globalConfig = JSON.parse(readFile(globalPath))
|
|
39
|
+
} catch (error) {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Read local config (overrides)
|
|
43
|
+
if (fileExists("./.plan-config.json")) {
|
|
44
|
+
try {
|
|
45
|
+
localConfig = JSON.parse(readFile("./.plan-config.json"))
|
|
46
|
+
} catch (error) {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Merge configs: local overrides global, but cloud settings are merged
|
|
50
|
+
const mergedConfig = {
|
|
51
|
+
...globalConfig,
|
|
52
|
+
...localConfig,
|
|
53
|
+
cloud: {
|
|
54
|
+
...(globalConfig.cloud || {}),
|
|
55
|
+
...(localConfig.cloud || {})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return mergedConfig
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const config = getMergedConfig()
|
|
63
|
+
const language = config.language || "en"
|
|
64
|
+
|
|
65
|
+
// Cloud config - properly merged from both configs
|
|
66
|
+
const cloudConfig = config.cloud || {}
|
|
67
|
+
const isAuthenticated = !!cloudConfig.apiToken
|
|
68
|
+
const apiUrl = cloudConfig.apiUrl || "https://api.planflow.tools"
|
|
69
|
+
const projectId = cloudConfig.projectId || null
|
|
70
|
+
const projectName = cloudConfig.projectName || "Unknown Project"
|
|
71
|
+
|
|
72
|
+
// Load translations
|
|
73
|
+
const translationPath = `locales/${language}.json`
|
|
74
|
+
const t = JSON.parse(readFile(translationPath))
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Instructions for Claude:**
|
|
78
|
+
|
|
79
|
+
1. Read BOTH config files and MERGE them:
|
|
80
|
+
- First read `~/.config/claude/plan-plugin-config.json` (global, base)
|
|
81
|
+
- Then read `./.plan-config.json` (local, overrides)
|
|
82
|
+
- Merge the `cloud` sections: global values + local overrides
|
|
83
|
+
2. Use Read tool: `locales/{language}.json`
|
|
84
|
+
3. Store as `t` variable
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### Step 1: Parse Arguments
|
|
89
|
+
|
|
90
|
+
Parse the command arguments to extract task ID, limit, and type filter.
|
|
91
|
+
|
|
92
|
+
**Pseudo-code:**
|
|
93
|
+
```javascript
|
|
94
|
+
function parseArgs(args) {
|
|
95
|
+
let taskId = null
|
|
96
|
+
let limit = 10
|
|
97
|
+
let activityType = null
|
|
98
|
+
|
|
99
|
+
const parts = args.trim().split(/\s+/)
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < parts.length; i++) {
|
|
102
|
+
const part = parts[i]
|
|
103
|
+
|
|
104
|
+
if (part === '--limit' && parts[i + 1]) {
|
|
105
|
+
limit = parseInt(parts[i + 1], 10)
|
|
106
|
+
if (isNaN(limit) || limit < 1) limit = 10
|
|
107
|
+
if (limit > 50) limit = 50
|
|
108
|
+
i++ // Skip next part
|
|
109
|
+
} else if (part === '--type' && parts[i + 1]) {
|
|
110
|
+
activityType = parts[i + 1]
|
|
111
|
+
i++ // Skip next part
|
|
112
|
+
} else if (part.match(/^T\d+\.\d+$/i)) {
|
|
113
|
+
taskId = part.toUpperCase()
|
|
114
|
+
} else if (part === '--help' || part === '-h') {
|
|
115
|
+
return { showHelp: true }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { taskId, limit, activityType, showHelp: false }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const { taskId, limit, activityType, showHelp } = parseArgs(ARGUMENTS)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Activity Type Mapping:**
|
|
126
|
+
- `tasks` → action filter: `task_status_changed`
|
|
127
|
+
- `comments` → action filter: `comment_created`
|
|
128
|
+
- `assignments` → action filter: `task_assigned`
|
|
129
|
+
- `team` → entity filter: `member`
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Step 2: Show Usage Card (if help requested or no project linked)
|
|
134
|
+
|
|
135
|
+
If `--help` is provided or no project is linked, display usage information.
|
|
136
|
+
|
|
137
|
+
**Output:**
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
141
|
+
│ 📊 Activity Feed │
|
|
142
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
143
|
+
│ │
|
|
144
|
+
│ ── Usage ─────────────────────────────────────────────────────────────── │
|
|
145
|
+
│ │
|
|
146
|
+
│ /pfActivity Show recent project activity │
|
|
147
|
+
│ /pfActivity <task-id> Show activity for specific task │
|
|
148
|
+
│ /pfActivity --limit <n> Show more entries (default: 10, max: 50) │
|
|
149
|
+
│ /pfActivity --type <type> Filter by activity type │
|
|
150
|
+
│ │
|
|
151
|
+
│ ── Activity Types ───────────────────────────────────────────────────── │
|
|
152
|
+
│ │
|
|
153
|
+
│ tasks - Task status changes │
|
|
154
|
+
│ comments - Comment activity │
|
|
155
|
+
│ assignments - Task assignments │
|
|
156
|
+
│ team - Team membership changes │
|
|
157
|
+
│ │
|
|
158
|
+
│ ── Examples ──────────────────────────────────────────────────────────── │
|
|
159
|
+
│ │
|
|
160
|
+
│ /pfActivity T2.1 │
|
|
161
|
+
│ /pfActivity --type comments --limit 20 │
|
|
162
|
+
│ │
|
|
163
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### Step 3: Check Prerequisites
|
|
169
|
+
|
|
170
|
+
Verify user is authenticated and project is linked.
|
|
171
|
+
|
|
172
|
+
**Pseudo-code:**
|
|
173
|
+
```javascript
|
|
174
|
+
if (!isAuthenticated) {
|
|
175
|
+
showError(t.commands.sync.notAuthenticated)
|
|
176
|
+
// "❌ Not authenticated. Run /pfLogin first."
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!projectId) {
|
|
181
|
+
showError(t.commands.sync.notLinked)
|
|
182
|
+
// "❌ Project not linked to cloud. Run /pfCloudLink first."
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Not Authenticated Card:**
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
191
|
+
│ ❌ ERROR │
|
|
192
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
193
|
+
│ │
|
|
194
|
+
│ Not authenticated. │
|
|
195
|
+
│ │
|
|
196
|
+
│ 💡 Run /pfLogin first to authenticate. │
|
|
197
|
+
│ │
|
|
198
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Not Linked Card:**
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
205
|
+
│ ❌ ERROR │
|
|
206
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
207
|
+
│ │
|
|
208
|
+
│ Project not linked to cloud. │
|
|
209
|
+
│ │
|
|
210
|
+
│ 💡 Run /pfCloudLink first to link this project. │
|
|
211
|
+
│ │
|
|
212
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### Step 4: Fetch Activity from API
|
|
218
|
+
|
|
219
|
+
Make API request to fetch activity.
|
|
220
|
+
|
|
221
|
+
**Pseudo-code:**
|
|
222
|
+
```javascript
|
|
223
|
+
async function fetchActivity(projectId, taskId, limit, activityType, apiToken, apiUrl) {
|
|
224
|
+
let endpoint
|
|
225
|
+
const params = new URLSearchParams()
|
|
226
|
+
params.append('limit', String(limit))
|
|
227
|
+
|
|
228
|
+
if (taskId) {
|
|
229
|
+
// Task-specific activity
|
|
230
|
+
endpoint = `${apiUrl}/projects/${projectId}/tasks/${taskId}/activity`
|
|
231
|
+
} else {
|
|
232
|
+
// Project activity
|
|
233
|
+
endpoint = `${apiUrl}/projects/${projectId}/activity`
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Add type filter
|
|
237
|
+
if (activityType) {
|
|
238
|
+
switch (activityType) {
|
|
239
|
+
case 'tasks':
|
|
240
|
+
params.append('action', 'task_status_changed')
|
|
241
|
+
break
|
|
242
|
+
case 'comments':
|
|
243
|
+
params.append('action', 'comment_created')
|
|
244
|
+
break
|
|
245
|
+
case 'assignments':
|
|
246
|
+
params.append('action', 'task_assigned')
|
|
247
|
+
break
|
|
248
|
+
case 'team':
|
|
249
|
+
params.append('entityType', 'member')
|
|
250
|
+
break
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const url = `${endpoint}?${params.toString()}`
|
|
255
|
+
|
|
256
|
+
const response = await fetch(url, {
|
|
257
|
+
method: 'GET',
|
|
258
|
+
headers: {
|
|
259
|
+
'Authorization': `Bearer ${apiToken}`,
|
|
260
|
+
'Accept': 'application/json'
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
if (!response.ok) {
|
|
265
|
+
throw new Error(`HTTP ${response.status}`)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return response.json()
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Bash Implementation:**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
API_URL="https://api.planflow.tools"
|
|
276
|
+
TOKEN="$API_TOKEN"
|
|
277
|
+
PROJECT_ID="$PROJECT_ID"
|
|
278
|
+
TASK_ID="$TASK_ID" # Optional
|
|
279
|
+
LIMIT="10"
|
|
280
|
+
|
|
281
|
+
# Build URL
|
|
282
|
+
if [ -n "$TASK_ID" ]; then
|
|
283
|
+
ENDPOINT="${API_URL}/projects/${PROJECT_ID}/tasks/${TASK_ID}/activity"
|
|
284
|
+
else
|
|
285
|
+
ENDPOINT="${API_URL}/projects/${PROJECT_ID}/activity"
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
# Make request
|
|
289
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" \
|
|
290
|
+
--connect-timeout 5 \
|
|
291
|
+
--max-time 15 \
|
|
292
|
+
-X GET \
|
|
293
|
+
-H "Accept: application/json" \
|
|
294
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
295
|
+
"${ENDPOINT}?limit=${LIMIT}")
|
|
296
|
+
|
|
297
|
+
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
298
|
+
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
299
|
+
|
|
300
|
+
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
|
|
301
|
+
echo "$BODY"
|
|
302
|
+
else
|
|
303
|
+
echo "Error: HTTP $HTTP_CODE"
|
|
304
|
+
fi
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Instructions for Claude:**
|
|
308
|
+
|
|
309
|
+
1. Build the API URL based on whether taskId is provided
|
|
310
|
+
2. Add query parameters for limit and type filters
|
|
311
|
+
3. Make GET request with authorization header
|
|
312
|
+
4. Parse JSON response
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### Step 5: Format Activity Items
|
|
317
|
+
|
|
318
|
+
Format each activity item for display.
|
|
319
|
+
|
|
320
|
+
**Activity Icons:**
|
|
321
|
+
|
|
322
|
+
| Action | Icon |
|
|
323
|
+
|--------|------|
|
|
324
|
+
| task_created | ✨ |
|
|
325
|
+
| task_updated | 📝 |
|
|
326
|
+
| task_status_changed | 🔄 |
|
|
327
|
+
| task_assigned | 👤 |
|
|
328
|
+
| task_unassigned | 👤 |
|
|
329
|
+
| comment_created | 💬 |
|
|
330
|
+
| comment_updated | ✏️ |
|
|
331
|
+
| comment_deleted | 🗑️ |
|
|
332
|
+
| project_updated | 📋 |
|
|
333
|
+
| plan_updated | 📄 |
|
|
334
|
+
| member_invited | 📨 |
|
|
335
|
+
| member_joined | 🎉 |
|
|
336
|
+
| member_removed | 👋 |
|
|
337
|
+
| member_role_changed | 🔑 |
|
|
338
|
+
|
|
339
|
+
**Status Icons for task_status_changed:**
|
|
340
|
+
|
|
341
|
+
| New Status | Icon |
|
|
342
|
+
|------------|------|
|
|
343
|
+
| DONE | ✅ |
|
|
344
|
+
| IN_PROGRESS | 🔄 |
|
|
345
|
+
| BLOCKED | 🚫 |
|
|
346
|
+
| TODO | ⬜ |
|
|
347
|
+
|
|
348
|
+
**Relative Time Formatting:**
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
function formatRelativeTime(dateString) {
|
|
352
|
+
const date = new Date(dateString)
|
|
353
|
+
const now = new Date()
|
|
354
|
+
const diffMs = now.getTime() - date.getTime()
|
|
355
|
+
const diffMinutes = Math.floor(diffMs / 60000)
|
|
356
|
+
const diffHours = Math.floor(diffMinutes / 60)
|
|
357
|
+
const diffDays = Math.floor(diffHours / 24)
|
|
358
|
+
|
|
359
|
+
if (diffMinutes < 1) return 'just now'
|
|
360
|
+
if (diffMinutes < 60) return `${diffMinutes} min ago`
|
|
361
|
+
if (diffHours < 24) return `${diffHours}h ago`
|
|
362
|
+
if (diffDays < 7) return `${diffDays}d ago`
|
|
363
|
+
return date.toLocaleDateString()
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Activity Message Formatting:**
|
|
368
|
+
|
|
369
|
+
```javascript
|
|
370
|
+
function formatActivityMessage(activity) {
|
|
371
|
+
const actor = activity.actor.name || activity.actor.email.split('@')[0]
|
|
372
|
+
const taskId = activity.taskId
|
|
373
|
+
|
|
374
|
+
switch (activity.action) {
|
|
375
|
+
case 'task_status_changed':
|
|
376
|
+
const newStatus = activity.metadata?.newStatus
|
|
377
|
+
const statusIcon = getStatusIcon(newStatus)
|
|
378
|
+
if (taskId) {
|
|
379
|
+
return `${statusIcon} ${actor} marked ${taskId} as ${newStatus.toLowerCase()}`
|
|
380
|
+
}
|
|
381
|
+
return `${statusIcon} ${actor} changed task status to ${newStatus.toLowerCase()}`
|
|
382
|
+
|
|
383
|
+
case 'task_assigned':
|
|
384
|
+
const assignee = activity.metadata?.assigneeName || activity.metadata?.assigneeEmail
|
|
385
|
+
if (taskId) {
|
|
386
|
+
return `👤 ${actor} assigned ${taskId} to ${assignee}`
|
|
387
|
+
}
|
|
388
|
+
return `👤 ${actor} assigned task to ${assignee}`
|
|
389
|
+
|
|
390
|
+
case 'task_unassigned':
|
|
391
|
+
if (taskId) {
|
|
392
|
+
return `👤 ${actor} unassigned ${taskId}`
|
|
393
|
+
}
|
|
394
|
+
return `👤 ${actor} removed assignment`
|
|
395
|
+
|
|
396
|
+
case 'comment_created':
|
|
397
|
+
const preview = activity.metadata?.commentPreview
|
|
398
|
+
if (taskId && preview) {
|
|
399
|
+
return `💬 ${actor} commented on ${taskId}\n "${truncate(preview, 40)}"`
|
|
400
|
+
}
|
|
401
|
+
if (taskId) {
|
|
402
|
+
return `💬 ${actor} commented on ${taskId}`
|
|
403
|
+
}
|
|
404
|
+
return `💬 ${actor} added a comment`
|
|
405
|
+
|
|
406
|
+
case 'member_joined':
|
|
407
|
+
return `🎉 ${actor} joined the project`
|
|
408
|
+
|
|
409
|
+
case 'member_invited':
|
|
410
|
+
const invitee = activity.metadata?.inviteeEmail
|
|
411
|
+
return `📨 ${actor} invited ${invitee}`
|
|
412
|
+
|
|
413
|
+
case 'member_removed':
|
|
414
|
+
return `👋 ${actor} left the project`
|
|
415
|
+
|
|
416
|
+
case 'member_role_changed':
|
|
417
|
+
const newRole = activity.metadata?.newRole
|
|
418
|
+
return `🔑 ${actor}'s role changed to ${newRole}`
|
|
419
|
+
|
|
420
|
+
case 'plan_updated':
|
|
421
|
+
return `📄 ${actor} updated the project plan`
|
|
422
|
+
|
|
423
|
+
default:
|
|
424
|
+
return `📌 ${actor} ${activity.description || activity.action.replace(/_/g, ' ')}`
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
### Step 6: Display Activity Feed Card
|
|
432
|
+
|
|
433
|
+
Display the activity feed in a formatted card.
|
|
434
|
+
|
|
435
|
+
**Project Activity Card:**
|
|
436
|
+
|
|
437
|
+
```
|
|
438
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
439
|
+
│ 📊 Recent Activity │
|
|
440
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
441
|
+
│ │
|
|
442
|
+
│ 📁 Project: Planflow Plugin │
|
|
443
|
+
│ │
|
|
444
|
+
│ ── Activity Feed ────────────────────────────────────────────────────── │
|
|
445
|
+
│ │
|
|
446
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
447
|
+
│ │ ✅ John marked T2.1 as done 5 min ago │ │
|
|
448
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
449
|
+
│ │
|
|
450
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
451
|
+
│ │ 💬 Jane commented on T2.3 10 min ago │ │
|
|
452
|
+
│ │ "Looks good! Just one small fix..." │ │
|
|
453
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
454
|
+
│ │
|
|
455
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
456
|
+
│ │ 🔄 Bob started working on T3.1 1 hour ago │ │
|
|
457
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
458
|
+
│ │
|
|
459
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
460
|
+
│ │ 👤 Jane assigned T2.5 to John 2 hours ago │ │
|
|
461
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
462
|
+
│ │
|
|
463
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
464
|
+
│ │ 🎉 Alice joined the project 4 hours ago │ │
|
|
465
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
466
|
+
│ │
|
|
467
|
+
│ 📄 Showing 5 of 45 activities │
|
|
468
|
+
│ │
|
|
469
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
470
|
+
│ │
|
|
471
|
+
│ 💡 Commands: │
|
|
472
|
+
│ • /pfActivity --limit 20 View more activity │
|
|
473
|
+
│ • /pfActivity T2.1 View task activity │
|
|
474
|
+
│ • /pfActivity --type tasks Filter by type │
|
|
475
|
+
│ │
|
|
476
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Task-Specific Activity Card:**
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
483
|
+
│ 📊 Activity for T2.1 │
|
|
484
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
485
|
+
│ │
|
|
486
|
+
│ 📋 Task: Implement login API │
|
|
487
|
+
│ │
|
|
488
|
+
│ ── Activity Feed ────────────────────────────────────────────────────── │
|
|
489
|
+
│ │
|
|
490
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
491
|
+
│ │ ✅ John marked as done 5 min ago │ │
|
|
492
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
493
|
+
│ │
|
|
494
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
495
|
+
│ │ 💬 Jane commented 30 min ago │ │
|
|
496
|
+
│ │ "Ready for review!" │ │
|
|
497
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
498
|
+
│ │
|
|
499
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
500
|
+
│ │ 💬 John commented 1 hour ago │ │
|
|
501
|
+
│ │ "Almost done, just testing..." │ │
|
|
502
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
503
|
+
│ │
|
|
504
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
505
|
+
│ │ 🔄 John started working 3 hours ago │ │
|
|
506
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
507
|
+
│ │
|
|
508
|
+
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
|
509
|
+
│ │ 👤 Jane assigned to John 1 day ago │ │
|
|
510
|
+
│ └────────────────────────────────────────────────────────────────────────┘ │
|
|
511
|
+
│ │
|
|
512
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
513
|
+
│ │
|
|
514
|
+
│ 💡 Commands: │
|
|
515
|
+
│ • /pfComment T2.1 "Your message" Add a comment │
|
|
516
|
+
│ • /pfComments T2.1 View all comments │
|
|
517
|
+
│ │
|
|
518
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
### Step 7: No Activity Card
|
|
524
|
+
|
|
525
|
+
Display when no activity is found.
|
|
526
|
+
|
|
527
|
+
```
|
|
528
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
529
|
+
│ 📊 Recent Activity │
|
|
530
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
531
|
+
│ │
|
|
532
|
+
│ 📁 Project: Planflow Plugin │
|
|
533
|
+
│ │
|
|
534
|
+
│ ╭─────────────────────────────────────────────────────────────────────╮ │
|
|
535
|
+
│ │ │ │
|
|
536
|
+
│ │ 📭 No recent activity found. │ │
|
|
537
|
+
│ │ │ │
|
|
538
|
+
│ ╰─────────────────────────────────────────────────────────────────────╯ │
|
|
539
|
+
│ │
|
|
540
|
+
│ Activity will appear when team members: │
|
|
541
|
+
│ • Update task statuses │
|
|
542
|
+
│ • Add comments │
|
|
543
|
+
│ • Assign tasks │
|
|
544
|
+
│ • Join or leave the project │
|
|
545
|
+
│ │
|
|
546
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Error Handling
|
|
552
|
+
|
|
553
|
+
### Task Not Found (404)
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
557
|
+
│ ❌ ERROR │
|
|
558
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
559
|
+
│ │
|
|
560
|
+
│ Task not found: T2.1 │
|
|
561
|
+
│ │
|
|
562
|
+
│ Make sure the task exists. Run /pfSyncPush to sync your local tasks. │
|
|
563
|
+
│ │
|
|
564
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Invalid Activity Type
|
|
568
|
+
|
|
569
|
+
```
|
|
570
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
571
|
+
│ ❌ ERROR │
|
|
572
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
573
|
+
│ │
|
|
574
|
+
│ Invalid activity type: "{type}" │
|
|
575
|
+
│ │
|
|
576
|
+
│ Valid types: tasks, comments, assignments, team │
|
|
577
|
+
│ │
|
|
578
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Network Error
|
|
582
|
+
|
|
583
|
+
```
|
|
584
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
585
|
+
│ ❌ ERROR │
|
|
586
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
587
|
+
│ │
|
|
588
|
+
│ Failed to fetch activity. │
|
|
589
|
+
│ │
|
|
590
|
+
│ Please check your internet connection and try again. │
|
|
591
|
+
│ │
|
|
592
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Authentication Error (401)
|
|
596
|
+
|
|
597
|
+
```
|
|
598
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
599
|
+
│ ❌ ERROR │
|
|
600
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
601
|
+
│ │
|
|
602
|
+
│ Authentication failed. Your token may have expired. │
|
|
603
|
+
│ │
|
|
604
|
+
│ 💡 Run /pfLogin to re-authenticate. │
|
|
605
|
+
│ │
|
|
606
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Complete Implementation Flow
|
|
612
|
+
|
|
613
|
+
```
|
|
614
|
+
/pfActivity [args]
|
|
615
|
+
│
|
|
616
|
+
▼
|
|
617
|
+
┌────────────────────────────────────────┐
|
|
618
|
+
│ Step 0: Load config and translations │
|
|
619
|
+
└─────────────────┬──────────────────────┘
|
|
620
|
+
│
|
|
621
|
+
▼
|
|
622
|
+
┌────────────────────────────────────────┐
|
|
623
|
+
│ Step 1: Parse arguments │
|
|
624
|
+
│ - taskId (optional) │
|
|
625
|
+
│ - limit (default: 10) │
|
|
626
|
+
│ - type filter (optional) │
|
|
627
|
+
└─────────────────┬──────────────────────┘
|
|
628
|
+
│
|
|
629
|
+
▼
|
|
630
|
+
┌────────────────────────────────────────┐
|
|
631
|
+
│ Step 2: Check for --help flag │
|
|
632
|
+
│ → Show usage if needed │
|
|
633
|
+
└─────────────────┬──────────────────────┘
|
|
634
|
+
│
|
|
635
|
+
▼
|
|
636
|
+
┌────────────────────────────────────────┐
|
|
637
|
+
│ Step 3: Check prerequisites │
|
|
638
|
+
│ - Authenticated? │
|
|
639
|
+
│ - Project linked? │
|
|
640
|
+
└─────────────────┬──────────────────────┘
|
|
641
|
+
│
|
|
642
|
+
▼
|
|
643
|
+
┌────────────────────────────────────────┐
|
|
644
|
+
│ Step 4: Fetch activity from API │
|
|
645
|
+
│ GET /projects/:id/activity │
|
|
646
|
+
│ GET /projects/:id/tasks/:id/activity │
|
|
647
|
+
└─────────────────┬──────────────────────┘
|
|
648
|
+
│
|
|
649
|
+
▼
|
|
650
|
+
┌────────────────────────────────────────┐
|
|
651
|
+
│ Step 5: Format activity items │
|
|
652
|
+
│ - Apply icons │
|
|
653
|
+
│ - Format relative time │
|
|
654
|
+
│ - Build messages │
|
|
655
|
+
└─────────────────┬──────────────────────┘
|
|
656
|
+
│
|
|
657
|
+
▼
|
|
658
|
+
┌────────────────────────────────────────┐
|
|
659
|
+
│ Step 6/7: Display activity card │
|
|
660
|
+
│ - Activity feed or no activity │
|
|
661
|
+
└────────────────────────────────────────┘
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## Translation Keys
|
|
667
|
+
|
|
668
|
+
```json
|
|
669
|
+
{
|
|
670
|
+
"commands": {
|
|
671
|
+
"activity": {
|
|
672
|
+
"title": "📊 Activity Feed",
|
|
673
|
+
"recentActivity": "📊 Recent Activity",
|
|
674
|
+
"taskActivity": "📊 Activity for {taskId}",
|
|
675
|
+
"projectLabel": "📁 Project: {name}",
|
|
676
|
+
"taskLabel": "📋 Task: {name}",
|
|
677
|
+
"feedTitle": "Activity Feed",
|
|
678
|
+
"showingCount": "📄 Showing {count} of {total} activities",
|
|
679
|
+
"noActivity": "📭 No recent activity found.",
|
|
680
|
+
"noActivityHint": "Activity will appear when team members:",
|
|
681
|
+
"noActivityHint1": "Update task statuses",
|
|
682
|
+
"noActivityHint2": "Add comments",
|
|
683
|
+
"noActivityHint3": "Assign tasks",
|
|
684
|
+
"noActivityHint4": "Join or leave the project",
|
|
685
|
+
"usage": "Usage",
|
|
686
|
+
"usageMain": "/pfActivity Show recent project activity",
|
|
687
|
+
"usageTask": "/pfActivity <task-id> Show activity for specific task",
|
|
688
|
+
"usageLimit": "/pfActivity --limit <n> Show more entries (default: 10, max: 50)",
|
|
689
|
+
"usageType": "/pfActivity --type <type> Filter by activity type",
|
|
690
|
+
"types": "Activity Types",
|
|
691
|
+
"typeTasks": "tasks - Task status changes",
|
|
692
|
+
"typeComments": "comments - Comment activity",
|
|
693
|
+
"typeAssignments": "assignments - Task assignments",
|
|
694
|
+
"typeTeam": "team - Team membership changes",
|
|
695
|
+
"examples": "Examples",
|
|
696
|
+
"commands": "Commands",
|
|
697
|
+
"viewMore": "/pfActivity --limit 20 View more activity",
|
|
698
|
+
"viewTask": "/pfActivity T2.1 View task activity",
|
|
699
|
+
"filterType": "/pfActivity --type tasks Filter by type",
|
|
700
|
+
"addComment": "/pfComment {taskId} \"Your message\" Add a comment",
|
|
701
|
+
"viewComments": "/pfComments {taskId} View all comments",
|
|
702
|
+
"invalidType": "Invalid activity type: \"{type}\"",
|
|
703
|
+
"validTypes": "Valid types: tasks, comments, assignments, team",
|
|
704
|
+
"taskNotFound": "Task not found: {taskId}",
|
|
705
|
+
"taskNotFoundHint": "Make sure the task exists. Run /pfSyncPush to sync your local tasks.",
|
|
706
|
+
"fetchFailed": "Failed to fetch activity.",
|
|
707
|
+
"checkConnection": "Please check your internet connection and try again."
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
## Success Criteria
|
|
716
|
+
|
|
717
|
+
A successful /pfActivity command should:
|
|
718
|
+
- ✅ Load config and check authentication
|
|
719
|
+
- ✅ Parse task ID, limit, and type from arguments
|
|
720
|
+
- ✅ Make appropriate API call (project or task activity)
|
|
721
|
+
- ✅ Format activity items with icons and relative times
|
|
722
|
+
- ✅ Display activity in formatted card
|
|
723
|
+
- ✅ Handle empty activity gracefully
|
|
724
|
+
- ✅ Handle errors with helpful messages
|
|
725
|
+
- ✅ Show usage on --help or missing project
|