clickup-agent-cli 0.5.0 → 0.5.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/dist/clickup.js +1 -1
- package/package.json +1 -1
- package/skills/clickup/SKILL.md +2 -0
- package/skills/clickup-blocker-report/SKILL.md +3 -1
- package/skills/clickup-blocker-report/assets/report-template.md +16 -0
- package/skills/clickup-capacity-check/SKILL.md +3 -1
- package/skills/clickup-capacity-check/assets/report-template.md +14 -0
- package/skills/clickup-goal-progress/SKILL.md +3 -1
- package/skills/clickup-goal-progress/assets/report-template.md +13 -0
- package/skills/clickup-my-day/SKILL.md +2 -14
- package/skills/clickup-my-day/assets/report-template.md +12 -0
- package/skills/clickup-release-notes/SKILL.md +2 -13
- package/skills/clickup-release-notes/assets/report-template.md +12 -0
- package/skills/clickup-standup/SKILL.md +2 -14
- package/skills/clickup-standup/assets/report-template.md +9 -0
- package/skills/clickup-team-report/SKILL.md +3 -1
- package/skills/clickup-team-report/assets/report-template.md +24 -0
- package/skills/clickup-time-audit/SKILL.md +5 -1
- package/skills/clickup-time-audit/assets/report-template.md +19 -0
- package/skills/clickup-timesheet-export/SKILL.md +18 -19
- package/skills/clickup-timesheet-export/assets/report-template.md +9 -0
- package/skills/clickup-timesheet-export/scripts/aggregate.mjs +73 -0
- package/skills/clickup-weekly-review/SKILL.md +3 -1
- package/skills/clickup-weekly-review/assets/report-template.md +26 -0
- package/skills/clickup-workspace-audit/SKILL.md +12 -20
- package/skills/clickup-workspace-audit/assets/report-template.md +21 -0
- package/skills/clickup-workspace-audit/scripts/classify.mjs +72 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"name": "clickup",
|
|
12
12
|
"source": "./",
|
|
13
13
|
"description": "ClickUp CLI with 31 agent skills covering the full API -- token-efficient alternative to MCP with chat, time tracking, docs, and project management workflows",
|
|
14
|
-
"version": "0.5.
|
|
14
|
+
"version": "0.5.1",
|
|
15
15
|
"homepage": "https://github.com/henryreith/clickup-cli",
|
|
16
16
|
"keywords": ["clickup", "project-management", "tasks", "time-tracking"],
|
|
17
17
|
"category": "productivity"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clickup",
|
|
3
3
|
"description": "ClickUp CLI with 31 agent skills covering the full API -- token-efficient alternative to MCP with chat, time tracking, docs, and project management workflows",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Henry Reith"
|
|
7
7
|
},
|
package/dist/clickup.js
CHANGED
|
@@ -4629,7 +4629,7 @@ function registerChatCommands(program, getClient) {
|
|
|
4629
4629
|
}
|
|
4630
4630
|
|
|
4631
4631
|
// src/cli.ts
|
|
4632
|
-
var VERSION = "0.5.
|
|
4632
|
+
var VERSION = "0.5.1";
|
|
4633
4633
|
function createProgram() {
|
|
4634
4634
|
const program = new Command();
|
|
4635
4635
|
program.name("clickup").description("ClickUp CLI - Manage ClickUp workspaces from the terminal").version(VERSION).option("--token <token>", "API token").option("--token-file <path>", "Read API token from this file path").option("--profile <name>", "Profile to use (key, workspace name, or nickname)").option("--workspace-id <id>", "Workspace ID").addOption(
|
package/package.json
CHANGED
package/skills/clickup/SKILL.md
CHANGED
|
@@ -37,6 +37,8 @@ clickup skill show <name> # Print a skill's full contents (e.g. cli
|
|
|
37
37
|
|
|
38
38
|
Known pitfalls (rate limits, error codes, exit codes, destructive-command rules) live in `references/gotchas.md` next to this file; read it before debugging a failing command (`clickup skill path clickup` prints this skill's directory).
|
|
39
39
|
|
|
40
|
+
Skills bundle supporting files per the Agent Skills standard: `references/` (deep detail), `assets/` (output templates to copy), and `scripts/` (deterministic helpers to run with node). `clickup skill path <name>` prints any skill's directory so these files can be read or executed directly.
|
|
41
|
+
|
|
40
42
|
## Sub-Skills (load when needed)
|
|
41
43
|
|
|
42
44
|
| Skill | What it covers |
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[workspace-id]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Blocker Report
|
|
@@ -71,6 +71,8 @@ clickup task bulk-time-in-status --task-id <id1> --task-id <id2> --format json
|
|
|
71
71
|
|
|
72
72
|
### Step 6: Compile the report
|
|
73
73
|
|
|
74
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-blocker-report` prints it). Fill every placeholder; drop sections with no content.
|
|
75
|
+
|
|
74
76
|
- **Blocked tasks**: What, who, and what is blocking them
|
|
75
77
|
- **Stale tasks**: In progress but not updated recently
|
|
76
78
|
- **Overdue + unassigned**: Nobody owns these
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## Blocker Report: <scope> (<date>)
|
|
2
|
+
|
|
3
|
+
### Blocked tasks
|
|
4
|
+
- <task> - <assignee>, blocked <n> days, waiting on: <blocker>
|
|
5
|
+
|
|
6
|
+
### Dependency chains
|
|
7
|
+
- <task A> blocks <task B> blocks <task C> (chain head is <status>)
|
|
8
|
+
|
|
9
|
+
### Stale (in progress, no updates)
|
|
10
|
+
- <task> - <assignee>, last touched <date>
|
|
11
|
+
|
|
12
|
+
### Overdue and unassigned (nobody owns these)
|
|
13
|
+
- <task> - due <date> (<list>)
|
|
14
|
+
|
|
15
|
+
### Escalations needed
|
|
16
|
+
1. <task>: blocked <n> days - <suggested action>
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[workspace-id]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Capacity Check
|
|
@@ -57,6 +57,8 @@ clickup task search --workspace-id <id> --assignee <user-id> \
|
|
|
57
57
|
|
|
58
58
|
### Step 5: Compile capacity report
|
|
59
59
|
|
|
60
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-capacity-check` prints it). Fill every placeholder; drop sections with no content.
|
|
61
|
+
|
|
60
62
|
For each team member:
|
|
61
63
|
- **Active tasks**: Count and total estimated hours
|
|
62
64
|
- **Time logged this week**: Hours
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Capacity Check: <team/scope> (<date>)
|
|
2
|
+
|
|
3
|
+
| Person | Active tasks | Est. hours | Logged this week | Overdue | Status |
|
|
4
|
+
|--------|--------------|-----------|------------------|---------|--------|
|
|
5
|
+
| <name> | <n> | <n>h | <n>h | <n> | Under / At / Over |
|
|
6
|
+
|
|
7
|
+
### Overloaded
|
|
8
|
+
- <name>: <what to move and to whom>
|
|
9
|
+
|
|
10
|
+
### Available bandwidth
|
|
11
|
+
- <name>: <n> open tasks, can take on more
|
|
12
|
+
|
|
13
|
+
### Suggested rebalance
|
|
14
|
+
1. Move "<task>" from <overloaded> to <available>
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[workspace-id]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Goal Progress Report
|
|
@@ -56,6 +56,8 @@ clickup task list --list-id <linked-list-id> --include-closed --format json
|
|
|
56
56
|
|
|
57
57
|
### Step 5: Compile the report
|
|
58
58
|
|
|
59
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-goal-progress` prints it). Fill every placeholder; drop sections with no content.
|
|
60
|
+
|
|
59
61
|
For each goal:
|
|
60
62
|
- **Goal name** and due date
|
|
61
63
|
- **Key results**: Current vs. target, % complete
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Goal Progress: <scope> (<date>)
|
|
2
|
+
|
|
3
|
+
| Goal | Due | Progress | Expected | Status |
|
|
4
|
+
|------|-----|----------|----------|--------|
|
|
5
|
+
| <goal> | <date> | <current>/<target> (<x>%) | <y>% | On track / At risk / Behind |
|
|
6
|
+
|
|
7
|
+
### <goal name>
|
|
8
|
+
- <key result>: <current> of <target> (<x>%)
|
|
9
|
+
- Latest: <most recent note or update>
|
|
10
|
+
- <status rationale in one line>
|
|
11
|
+
|
|
12
|
+
### Stale goals (no recent key result updates)
|
|
13
|
+
- <goal> - last update <date>
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[optional: a different person, or 'this week' for a wider window]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# My Day
|
|
@@ -45,19 +45,7 @@ Priority (urgent=1 to low=4) breaks ties inside each bucket.
|
|
|
45
45
|
|
|
46
46
|
### Step 4: Present the agenda
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
## Your day - <date>
|
|
50
|
-
|
|
51
|
-
Now: <running timer task, if any>
|
|
52
|
-
|
|
53
|
-
1. [OVERDUE] <task> - was due <date> (<list>)
|
|
54
|
-
2. [TODAY] <task> - <priority>
|
|
55
|
-
3. ...
|
|
56
|
-
|
|
57
|
-
Blocked (not actionable): <task> - waiting on <dependency>
|
|
58
|
-
|
|
59
|
-
Suggestion: <one sentence - e.g. "Clear the two overdue items before starting new work.">
|
|
60
|
-
```
|
|
48
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-my-day` prints it). Fill every placeholder; drop sections with no content.
|
|
61
49
|
|
|
62
50
|
Keep it under ~10 items; summarize the rest as "and N more in the backlog".
|
|
63
51
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## Your day - <date>
|
|
2
|
+
|
|
3
|
+
Now: <running timer task, if any>
|
|
4
|
+
|
|
5
|
+
1. [OVERDUE] <task> - was due <date> (<list>)
|
|
6
|
+
2. [TODAY] <task> - <priority>
|
|
7
|
+
3. [IN PROGRESS] <task> - untouched <n> days
|
|
8
|
+
4. [UP NEXT] <task> - <priority>
|
|
9
|
+
|
|
10
|
+
Blocked (not actionable): <task> - waiting on <dependency>
|
|
11
|
+
|
|
12
|
+
Suggestion: <one sentence on what to tackle first>
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[period and scope - e.g. 'last sprint for the mobile space', 'June, customer-facing']"
|
|
9
|
-
allowed-tools: Bash(clickup *), Write
|
|
9
|
+
allowed-tools: Bash(clickup *), Write, Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Release Notes
|
|
@@ -39,18 +39,7 @@ Group by tags and task names into: **Features**, **Improvements**, **Fixes**, an
|
|
|
39
39
|
|
|
40
40
|
Rewrite task names for the audience - "Fix login redirect on Safari" becomes "Fixed an issue where Safari users could be redirected to the wrong page after logging in." Never paste raw task IDs into customer-facing notes.
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
# Release Notes - <period>
|
|
44
|
-
|
|
45
|
-
## New
|
|
46
|
-
- ...
|
|
47
|
-
|
|
48
|
-
## Improved
|
|
49
|
-
- ...
|
|
50
|
-
|
|
51
|
-
## Fixed
|
|
52
|
-
- ...
|
|
53
|
-
```
|
|
42
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-release-notes` prints it). Fill every placeholder; drop sections with no content.
|
|
54
43
|
|
|
55
44
|
For internal notes append task IDs and assignees for traceability.
|
|
56
45
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Release Notes - <period>
|
|
2
|
+
|
|
3
|
+
## New
|
|
4
|
+
- <benefit-focused line for each shipped feature>
|
|
5
|
+
|
|
6
|
+
## Improved
|
|
7
|
+
- <what got better and for whom>
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
- <user-visible bug fixes, written as outcomes>
|
|
11
|
+
|
|
12
|
+
<!-- Internal variant: append task IDs and assignees per line -->
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[scope - e.g. 'my tasks', 'engineering team', user-id]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Daily Standup
|
|
@@ -74,19 +74,7 @@ clickup time running --workspace-id <id> --assignee <user-id> --format json
|
|
|
74
74
|
|
|
75
75
|
### Step 6: Compile standup
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
**Yesterday:**
|
|
81
|
-
- Completed: [list of finished tasks with names]
|
|
82
|
-
|
|
83
|
-
**Today:**
|
|
84
|
-
- Working on: [list of in-progress tasks]
|
|
85
|
-
- Due today: [list of tasks due today]
|
|
86
|
-
|
|
87
|
-
**Blockers:**
|
|
88
|
-
- [list of blocked tasks, if any]
|
|
89
|
-
```
|
|
77
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-standup` prints it). Fill every placeholder; drop sections with no content.
|
|
90
78
|
|
|
91
79
|
## Tips
|
|
92
80
|
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[team or department name, e.g. 'marketing', 'engineering', 'operations']"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Team / Department Report
|
|
@@ -102,6 +102,8 @@ Filter time entries to those related to the team's tasks.
|
|
|
102
102
|
|
|
103
103
|
### Step 7: Compile the report
|
|
104
104
|
|
|
105
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-team-report` prints it). Fill every placeholder; drop sections with no content.
|
|
106
|
+
|
|
105
107
|
Structure the report as:
|
|
106
108
|
|
|
107
109
|
- **Team/Department**: Name and scope of what is covered
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# <Team> Status Report (<date>)
|
|
2
|
+
|
|
3
|
+
**Scope:** <spaces/folders/lists covered>
|
|
4
|
+
**Summary:** <2-3 sentences on where things stand>
|
|
5
|
+
|
|
6
|
+
## By the numbers
|
|
7
|
+
| To do | In progress | Done this week | Overdue |
|
|
8
|
+
|-------|-------------|----------------|---------|
|
|
9
|
+
| <n> | <n> | <n> | <n> |
|
|
10
|
+
|
|
11
|
+
## Key accomplishments
|
|
12
|
+
- <completed task worth naming> (<assignee>)
|
|
13
|
+
|
|
14
|
+
## Currently working on
|
|
15
|
+
- <task> - <assignee>, due <date>
|
|
16
|
+
|
|
17
|
+
## Upcoming deadlines (next 7 days)
|
|
18
|
+
- <task> - due <date>, <assignee>
|
|
19
|
+
|
|
20
|
+
## Risks and blockers
|
|
21
|
+
- <overdue or blocked item> - <what unblocks it>
|
|
22
|
+
|
|
23
|
+
## Time invested
|
|
24
|
+
<n> hours logged this period (<top contributors>)
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[workspace-id] [start-date] [end-date]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Time Audit
|
|
@@ -81,6 +81,10 @@ Look for:
|
|
|
81
81
|
clickup time running --workspace-id <id> --format json
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
### Compile the report
|
|
85
|
+
|
|
86
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-time-audit` prints it). Fill every placeholder; drop sections with no content.
|
|
87
|
+
|
|
84
88
|
## Tips
|
|
85
89
|
|
|
86
90
|
- Duration is in milliseconds. Divide by 3600000 for hours.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Time Audit: <scope> (<period>)
|
|
2
|
+
|
|
3
|
+
Total logged: <n>h (<m>h billable / <k>h non-billable)
|
|
4
|
+
|
|
5
|
+
### By person
|
|
6
|
+
| Person | Hours | Billable | Entries |
|
|
7
|
+
|--------|-------|----------|---------|
|
|
8
|
+
| <name> | <n>h | <n>h | <n> |
|
|
9
|
+
|
|
10
|
+
### Estimate accuracy (actual / estimated)
|
|
11
|
+
- <task>: <actual>h vs <estimated>h (<ratio>)
|
|
12
|
+
|
|
13
|
+
### Anomalies
|
|
14
|
+
- <entry over 8h - forgot to stop timer?>
|
|
15
|
+
- <entries with no description>
|
|
16
|
+
- <running timers active for days>
|
|
17
|
+
|
|
18
|
+
### Missing time
|
|
19
|
+
- <tasks that changed status with zero time logged>
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[period and scope - e.g. 'last week', 'June for Sarah', 'this month billable only']"
|
|
9
|
-
allowed-tools: Bash(clickup *), Write
|
|
9
|
+
allowed-tools: Bash(clickup *), Bash(node *), Write, Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Timesheet Export
|
|
@@ -29,38 +29,37 @@ clickup time list --workspace-id <id> \
|
|
|
29
29
|
|
|
30
30
|
`--start`/`--end` accept ISO dates (`2026-06-01`), relative forms (`-30d`), or Unix timestamps. Entry durations are milliseconds.
|
|
31
31
|
|
|
32
|
-
### Step 2: Aggregate
|
|
32
|
+
### Step 2: Aggregate deterministically
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Never sum hours yourself. Pipe the entries through the bundled script; the math comes out exact:
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
```bash
|
|
37
|
+
SKILL_DIR=$(clickup skill path clickup-timesheet-export)
|
|
38
|
+
|
|
39
|
+
# JSON summary: totals, byPerson, byTask, flagged entries
|
|
40
|
+
clickup time list --workspace-id <id> --start <start> --end <end> --format json \
|
|
41
|
+
| node "$SKILL_DIR/scripts/aggregate.mjs"
|
|
42
|
+
|
|
43
|
+
# CSV rows for the export file
|
|
44
|
+
clickup time list --workspace-id <id> --start <start> --end <end> --format json \
|
|
45
|
+
| node "$SKILL_DIR/scripts/aggregate.mjs" --csv
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The summary's `flags` field lists running timers (excluded from totals), entries with no task, and entries over 12h.
|
|
40
49
|
|
|
41
50
|
### Step 3: Write the export
|
|
42
51
|
|
|
43
52
|
CSV for spreadsheets (default when the user says export/invoice):
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
date,person,task,description,hours,billable
|
|
47
|
-
2026-06-30,Sarah,Login bug fix,Safari redirect,2.50,true
|
|
48
|
-
```
|
|
54
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-timesheet-export` prints it). Fill every placeholder; drop sections with no content.
|
|
49
55
|
|
|
50
56
|
Save with the Write tool as `timesheet-<start>-<end>.csv` and tell the user the path. For a chat answer, use a markdown table plus totals instead.
|
|
51
57
|
|
|
52
58
|
### Step 4: Summarize
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
Timesheet <start> to <end>
|
|
56
|
-
- Total: N hours (M billable / K non-billable)
|
|
57
|
-
- By person: Sarah 32.5h, Tom 28.0h, ...
|
|
58
|
-
- Top tasks: <task> 12.5h, ...
|
|
59
|
-
Flagged: entries with no task attached, running timers still open
|
|
60
|
-
```
|
|
60
|
+
Use the summary block from the same template.
|
|
61
61
|
|
|
62
62
|
## Tips
|
|
63
63
|
|
|
64
|
-
- Entries with a null `end` are running timers; exclude them from totals and flag them.
|
|
65
64
|
- Cross-check suspicious days (over 12h per person) rather than silently exporting them.
|
|
66
65
|
- For a recurring client invoice, filter to the client's space via the task IDs' locations.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
date,person,task,description,hours,billable
|
|
2
|
+
2026-06-30,Sarah,Login bug fix,Safari redirect,2.50,true
|
|
3
|
+
|
|
4
|
+
<!-- Summary block to accompany the CSV: -->
|
|
5
|
+
Timesheet <start> to <end>
|
|
6
|
+
- Total: <n> hours (<m> billable / <k> non-billable)
|
|
7
|
+
- By person: <name> <n>h, ...
|
|
8
|
+
- Top tasks: <task> <n>h, ...
|
|
9
|
+
Flagged: <entries with no task, running timers still open>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deterministic timesheet aggregation. Pipe `clickup time list ... --format json`
|
|
3
|
+
// into this script; totals come out exact so no arithmetic is done by the agent.
|
|
4
|
+
//
|
|
5
|
+
// clickup time list --workspace-id X --start -7d --end today --format json \
|
|
6
|
+
// | node scripts/aggregate.mjs [--csv]
|
|
7
|
+
//
|
|
8
|
+
// Default output: JSON summary (totals, byPerson, byTask, flags).
|
|
9
|
+
// --csv: timesheet rows (date,person,task,description,hours,billable) on stdout.
|
|
10
|
+
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
12
|
+
|
|
13
|
+
const wantCsv = process.argv.includes('--csv')
|
|
14
|
+
const raw = readFileSync(0, 'utf-8')
|
|
15
|
+
let entries = JSON.parse(raw)
|
|
16
|
+
if (!Array.isArray(entries)) entries = entries.data ?? [entries]
|
|
17
|
+
|
|
18
|
+
const MS_PER_HOUR = 3_600_000
|
|
19
|
+
const hours = (ms) => Math.round((ms / MS_PER_HOUR) * 100) / 100
|
|
20
|
+
const rows = []
|
|
21
|
+
const flags = { runningTimers: [], noTask: [], over12h: [] }
|
|
22
|
+
const byPerson = {}
|
|
23
|
+
const byTask = {}
|
|
24
|
+
let totalMs = 0
|
|
25
|
+
let billableMs = 0
|
|
26
|
+
|
|
27
|
+
for (const e of entries) {
|
|
28
|
+
const duration = Number(e.duration)
|
|
29
|
+
const person = e.user_name || e.user?.username || 'unknown'
|
|
30
|
+
const task = e.task?.name || e.task_id || e.task?.id || ''
|
|
31
|
+
const desc = e.description || ''
|
|
32
|
+
const billable = e.billable === true || e.billable === 'true'
|
|
33
|
+
|
|
34
|
+
if (!e.end || duration < 0) {
|
|
35
|
+
flags.runningTimers.push({ id: e.id, person, task })
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
if (!task) flags.noTask.push({ id: e.id, person, hours: hours(duration) })
|
|
39
|
+
if (duration > 12 * MS_PER_HOUR) flags.over12h.push({ id: e.id, person, task, hours: hours(duration) })
|
|
40
|
+
|
|
41
|
+
totalMs += duration
|
|
42
|
+
if (billable) billableMs += duration
|
|
43
|
+
byPerson[person] = (byPerson[person] ?? 0) + duration
|
|
44
|
+
byTask[task || '(no task)'] = (byTask[task || '(no task)'] ?? 0) + duration
|
|
45
|
+
|
|
46
|
+
const date = new Date(Number(e.start)).toISOString().slice(0, 10)
|
|
47
|
+
rows.push({ date, person, task, desc, hours: hours(duration), billable })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (wantCsv) {
|
|
51
|
+
const esc = (v) => (/[",\n]/.test(String(v)) ? `"${String(v).replace(/"/g, '""')}"` : String(v))
|
|
52
|
+
process.stdout.write('date,person,task,description,hours,billable\n')
|
|
53
|
+
for (const r of rows.sort((a, b) => a.date.localeCompare(b.date) || a.person.localeCompare(b.person))) {
|
|
54
|
+
process.stdout.write([r.date, r.person, r.task, r.desc, r.hours.toFixed(2), r.billable].map(esc).join(',') + '\n')
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
const toHours = (o) => Object.fromEntries(Object.entries(o).sort((a, b) => b[1] - a[1]).map(([k, v]) => [k, hours(v)]))
|
|
58
|
+
process.stdout.write(
|
|
59
|
+
JSON.stringify(
|
|
60
|
+
{
|
|
61
|
+
entries: rows.length,
|
|
62
|
+
totalHours: hours(totalMs),
|
|
63
|
+
billableHours: hours(billableMs),
|
|
64
|
+
nonBillableHours: hours(totalMs - billableMs),
|
|
65
|
+
byPerson: toHours(byPerson),
|
|
66
|
+
byTask: toHours(byTask),
|
|
67
|
+
flags,
|
|
68
|
+
},
|
|
69
|
+
null,
|
|
70
|
+
2,
|
|
71
|
+
) + '\n',
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[scope - e.g. 'marketing team', space-id, or workspace-id]"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Weekly Review
|
|
@@ -82,6 +82,8 @@ clickup goal list --workspace-id <id> --format json
|
|
|
82
82
|
|
|
83
83
|
### Step 7: Compile the report
|
|
84
84
|
|
|
85
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-weekly-review` prints it). Fill every placeholder; drop sections with no content.
|
|
86
|
+
|
|
85
87
|
Summarize the data into a clear report with sections:
|
|
86
88
|
- **Completed this week**: Count and highlights of finished tasks
|
|
87
89
|
- **In progress**: Tasks actively being worked on
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Weekly Review: <scope> (<week-start> to <week-end>)
|
|
2
|
+
|
|
3
|
+
**Summary:** <2-3 sentences: overall trajectory, the one thing to know>
|
|
4
|
+
|
|
5
|
+
## By the numbers
|
|
6
|
+
| Metric | This week |
|
|
7
|
+
|--------|-----------|
|
|
8
|
+
| Completed | <n> |
|
|
9
|
+
| In progress | <n> |
|
|
10
|
+
| Overdue | <n> |
|
|
11
|
+
| Hours logged | <n> |
|
|
12
|
+
|
|
13
|
+
## Completed this week
|
|
14
|
+
- <task name> (<assignee>)
|
|
15
|
+
|
|
16
|
+
## In progress
|
|
17
|
+
- <task name> - <assignee>, due <date>
|
|
18
|
+
|
|
19
|
+
## Overdue / Blocked
|
|
20
|
+
- <task name> - due <date>, <why it matters>
|
|
21
|
+
|
|
22
|
+
## Goal progress
|
|
23
|
+
- <goal>: <current>/<target> (<status>)
|
|
24
|
+
|
|
25
|
+
## Next week
|
|
26
|
+
- <what is due or planned>
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
context: fork
|
|
7
7
|
agent: general-purpose
|
|
8
8
|
argument-hint: "[scope - workspace, space name, or list name; optionally 'and fix']"
|
|
9
|
-
allowed-tools: Bash(clickup *)
|
|
9
|
+
allowed-tools: Bash(clickup *), Bash(node *), Read
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Workspace Audit
|
|
@@ -27,9 +27,17 @@ clickup task search --workspace-id <id> --format json
|
|
|
27
27
|
|
|
28
28
|
Server-side filters cannot express "no assignee" or "no due date", so fetch active tasks and inspect the JSON client-side. For large workspaces sweep one space at a time (`--space-id`).
|
|
29
29
|
|
|
30
|
-
### Step 2:
|
|
30
|
+
### Step 2: Classify deterministically
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Pipe the tasks through the bundled script so every task is bucketed by rule, not judgment:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
SKILL_DIR=$(clickup skill path clickup-workspace-audit)
|
|
36
|
+
clickup task search --workspace-id <id> --format json \
|
|
37
|
+
| node "$SKILL_DIR/scripts/classify.mjs" [--stale-days 14]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The output contains `activeTasks`, `flaggedTasks`, per-bucket lists, and per-person `load`. The buckets:
|
|
33
41
|
|
|
34
42
|
| Check | Condition |
|
|
35
43
|
|-------|-----------|
|
|
@@ -45,23 +53,7 @@ Tasks with `blocked` or `waiting` status whose blockers are themselves overdue d
|
|
|
45
53
|
|
|
46
54
|
### Step 4: Report
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
## Workspace Audit: <scope> (<date>)
|
|
50
|
-
|
|
51
|
-
Overall: N active tasks, M flagged (X%)
|
|
52
|
-
|
|
53
|
-
### Overdue (worst first)
|
|
54
|
-
- <task> - due <date>, assignee <name> (<id>)
|
|
55
|
-
|
|
56
|
-
### Unassigned / No due date / Stale
|
|
57
|
-
- ...
|
|
58
|
-
|
|
59
|
-
### Load
|
|
60
|
-
- <name>: N open tasks (team median: M)
|
|
61
|
-
|
|
62
|
-
### Recommended fixes
|
|
63
|
-
1. ...
|
|
64
|
-
```
|
|
56
|
+
Copy the exact structure from `assets/report-template.md` in this skill's directory (`clickup skill path clickup-workspace-audit` prints it). Fill every placeholder; drop sections with no content.
|
|
65
57
|
|
|
66
58
|
### Step 5: Apply fixes (only when asked)
|
|
67
59
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## Workspace Audit: <scope> (<date>)
|
|
2
|
+
|
|
3
|
+
Overall: <n> active tasks, <m> flagged (<x>%)
|
|
4
|
+
|
|
5
|
+
### Overdue (worst first)
|
|
6
|
+
- <task> - due <date>, assignee <name> (<id>)
|
|
7
|
+
|
|
8
|
+
### Unassigned
|
|
9
|
+
- <task> (<list>)
|
|
10
|
+
|
|
11
|
+
### No due date
|
|
12
|
+
- <task> (<list>)
|
|
13
|
+
|
|
14
|
+
### Stale (in progress, untouched 14+ days)
|
|
15
|
+
- <task> - last touched <date>
|
|
16
|
+
|
|
17
|
+
### Load
|
|
18
|
+
- <name>: <n> open tasks (team median: <m>)
|
|
19
|
+
|
|
20
|
+
### Recommended fixes
|
|
21
|
+
1. <specific action with the command to run>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deterministic hygiene classification. Pipe `clickup task search ... --format json`
|
|
3
|
+
// into this script; every task is bucketed by rule, not judgment.
|
|
4
|
+
//
|
|
5
|
+
// clickup task search --workspace-id X --format json | node scripts/classify.mjs
|
|
6
|
+
//
|
|
7
|
+
// Output: JSON buckets (overdue, unassigned, noDueDate, stale, load) plus counts.
|
|
8
|
+
// --stale-days <n> overrides the 14-day staleness window.
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from 'node:fs'
|
|
11
|
+
|
|
12
|
+
const staleIdx = process.argv.indexOf('--stale-days')
|
|
13
|
+
const STALE_DAYS = staleIdx !== -1 ? Number(process.argv[staleIdx + 1]) : 14
|
|
14
|
+
const now = Date.now()
|
|
15
|
+
const staleCutoff = now - STALE_DAYS * 24 * 3_600_000
|
|
16
|
+
|
|
17
|
+
const raw = readFileSync(0, 'utf-8')
|
|
18
|
+
let tasks = JSON.parse(raw)
|
|
19
|
+
if (!Array.isArray(tasks)) tasks = tasks.tasks ?? [tasks]
|
|
20
|
+
|
|
21
|
+
const summarize = (t) => ({
|
|
22
|
+
id: t.id,
|
|
23
|
+
name: t.name,
|
|
24
|
+
status: typeof t.status === 'object' ? t.status?.status : t.status,
|
|
25
|
+
assignees: (t.assignees ?? []).map((a) => a.username ?? a.id),
|
|
26
|
+
due_date: t.due_date ? new Date(Number(t.due_date)).toISOString().slice(0, 10) : null,
|
|
27
|
+
list: t.list?.name ?? null,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const buckets = { overdue: [], unassigned: [], noDueDate: [], stale: [] }
|
|
31
|
+
const load = {}
|
|
32
|
+
const isClosed = (t) => {
|
|
33
|
+
const type = typeof t.status === 'object' ? t.status?.type : undefined
|
|
34
|
+
const s = (typeof t.status === 'object' ? t.status?.status : t.status ?? '').toLowerCase()
|
|
35
|
+
return type === 'closed' || type === 'done' || ['closed', 'complete', 'done'].includes(s)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let active = 0
|
|
39
|
+
for (const t of tasks) {
|
|
40
|
+
if (isClosed(t)) continue
|
|
41
|
+
active++
|
|
42
|
+
const s = summarize(t)
|
|
43
|
+
for (const a of s.assignees) load[a] = (load[a] ?? 0) + 1
|
|
44
|
+
|
|
45
|
+
if (s.assignees.length === 0) buckets.unassigned.push(s)
|
|
46
|
+
if (!t.due_date) buckets.noDueDate.push(s)
|
|
47
|
+
else if (Number(t.due_date) < now) buckets.overdue.push(s)
|
|
48
|
+
|
|
49
|
+
const statusType = typeof t.status === 'object' ? t.status?.type : undefined
|
|
50
|
+
const updated = Number(t.date_updated)
|
|
51
|
+
if (statusType === 'custom' || /progress|review|doing/.test((s.status ?? '').toLowerCase())) {
|
|
52
|
+
if (updated && updated < staleCutoff) buckets.stale.push({ ...s, last_updated: new Date(updated).toISOString().slice(0, 10) })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
buckets.overdue.sort((a, b) => (a.due_date ?? '').localeCompare(b.due_date ?? ''))
|
|
57
|
+
const flagged = new Set([...buckets.overdue, ...buckets.unassigned, ...buckets.noDueDate, ...buckets.stale].map((t) => t.id))
|
|
58
|
+
|
|
59
|
+
process.stdout.write(
|
|
60
|
+
JSON.stringify(
|
|
61
|
+
{
|
|
62
|
+
activeTasks: active,
|
|
63
|
+
flaggedTasks: flagged.size,
|
|
64
|
+
staleDays: STALE_DAYS,
|
|
65
|
+
counts: Object.fromEntries(Object.entries(buckets).map(([k, v]) => [k, v.length])),
|
|
66
|
+
load: Object.fromEntries(Object.entries(load).sort((a, b) => b[1] - a[1])),
|
|
67
|
+
...buckets,
|
|
68
|
+
},
|
|
69
|
+
null,
|
|
70
|
+
2,
|
|
71
|
+
) + '\n',
|
|
72
|
+
)
|