Skip to content

Commit f7946c6

Browse files
committed
Update asana scripts, pr-review, fix-workflow-first
1 parent 75401af commit f7946c6

File tree

4 files changed

+115
-51
lines changed

4 files changed

+115
-51
lines changed

.cursor/commands/asana-attach-pr.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,12 @@ if [[ -z "$IMPLEMENTOR_GID" ]]; then
124124
fi
125125

126126
# --- Step 3: Set Implementor if override was provided ---
127+
# People fields use array format: {"field_gid": ["user_gid"]}
127128
if [[ -n "$IMPLEMENTOR_OVERRIDE" ]]; then
128129
curl -s -X PUT "https://app.asana.com/api/1.0/tasks/$TASK_GID" \
129130
-H "Authorization: Bearer $ASANA_TOKEN" \
130131
-H "Content-Type: application/json" \
131-
-d "{\"data\":{\"custom_fields\":{\"$IMPLEMENTOR_FIELD\":{\"people_value\":[\"$IMPLEMENTOR_OVERRIDE\"]}}}}" > /dev/null 2>&1 || true
132+
-d "{\"data\":{\"custom_fields\":{\"$IMPLEMENTOR_FIELD\":[\"$IMPLEMENTOR_OVERRIDE\"]}}}" > /dev/null 2>&1 || true
132133
echo ">> Implementor: set"
133134
fi
134135

.cursor/commands/asana-create-dep-task.sh

Lines changed: 83 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# TASK_URL: <url>
1717
# CREATED: true|false (false if task already existed)
1818
# ASSIGNED_TO: <user_gid>
19-
# FIELDS_SET: priority=<val>, status=<val>
19+
# FIELDS_SET: priority=<val>, status=<val>, reviewer=<name>, implementor=<name>
2020
# DEPENDENCY_SET: <new_gid> blocks <parent_gid>
2121
#
2222
# Exit codes: 0 = success, 1 = error
@@ -52,9 +52,12 @@ AUTH="Authorization: Bearer $ASANA_TOKEN"
5252

5353
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5454

55+
# Auto-resolve current user GID (used for assignee and implementor)
56+
CURRENT_USER_GID=$("$SCRIPT_DIR/asana-whoami.sh" 2>/dev/null || true)
57+
5558
# Auto-resolve assignee to current user if not provided
5659
if [[ -z "$ASSIGNEE_GID" ]]; then
57-
ASSIGNEE_GID=$("$SCRIPT_DIR/asana-whoami.sh" 2>/dev/null || true)
60+
ASSIGNEE_GID="$CURRENT_USER_GID"
5861
fi
5962

6063
# Phase 1: Check if a dependency with a matching name already exists
@@ -78,10 +81,10 @@ if [[ -n "$existing" ]]; then
7881
fi
7982

8083
# Phase 2: Get parent task's project and custom fields to copy
81-
parent_info=$(curl -s "$API/tasks/$PARENT_GID?opt_fields=workspace.gid,memberships.project.gid,memberships.project.name,custom_fields.gid,custom_fields.enum_value.gid,custom_fields.enum_value.name" \
84+
parent_info=$(curl -s "$API/tasks/$PARENT_GID?opt_fields=workspace.gid,memberships.project.gid,memberships.project.name,custom_fields.gid,custom_fields.enum_value.gid,custom_fields.enum_value.name,custom_fields.people_value.gid,custom_fields.people_value.name" \
8285
-H "$AUTH")
8386

84-
read -r WORKSPACE_GID PROJECT_GIDS PRIORITY_GID PRIORITY_VAL STATUS_GID STATUS_VAL < <(echo "$parent_info" | python3 -c "
87+
read -r WORKSPACE_GID PROJECT_GIDS PRIORITY_INFO STATUS_INFO REVIEWER_INFO < <(echo "$parent_info" | python3 -c "
8588
import sys, json, re
8689
data = json.load(sys.stdin)['data']
8790
ws = data.get('workspace', {}).get('gid', '')
@@ -96,51 +99,70 @@ if not projects and data.get('memberships'):
9699
projects.append(data['memberships'][0]['project']['gid'])
97100
proj_str = ','.join(projects)
98101
99-
# Custom fields to copy
100-
FIELD_MAP = {
102+
# Field GIDs
103+
ENUM_FIELDS = {
101104
'795866930204488': 'priority',
102105
'1190660107346181': 'status',
103106
}
104-
fields = {}
107+
PEOPLE_FIELDS = {
108+
'1203334388004673': 'reviewer',
109+
}
110+
111+
enum_results = {}
112+
people_results = {}
113+
105114
for f in data.get('custom_fields', []):
106-
label = FIELD_MAP.get(f['gid'])
107-
if label and f.get('enum_value'):
108-
fields[label + '_gid'] = f['gid']
109-
fields[label + '_val'] = f['enum_value']['gid']
110-
fields[label + '_name'] = f['enum_value']['name']
111-
112-
pri_gid = fields.get('priority_gid', '')
113-
pri_val = fields.get('priority_val', '')
114-
sta_gid = fields.get('status_gid', '')
115-
sta_val = fields.get('status_val', '')
116-
117-
print(f'{ws} {proj_str} {pri_gid}:{pri_val}:{fields.get(\"priority_name\",\"\")} {sta_gid}:{sta_val}:{fields.get(\"status_name\",\"\")}')
115+
fgid = f['gid']
116+
if fgid in ENUM_FIELDS and f.get('enum_value'):
117+
label = ENUM_FIELDS[fgid]
118+
enum_results[label] = (fgid, f['enum_value']['gid'], f['enum_value'].get('name', ''))
119+
if fgid in PEOPLE_FIELDS:
120+
label = PEOPLE_FIELDS[fgid]
121+
pv = f.get('people_value', [])
122+
if pv:
123+
people_results[label] = (fgid, pv[0]['gid'], pv[0].get('name', ''))
124+
125+
def fmt_enum(key):
126+
if key in enum_results:
127+
return ':'.join(enum_results[key])
128+
return '::'
129+
130+
def fmt_people(key):
131+
if key in people_results:
132+
return ':'.join(people_results[key])
133+
return '::'
134+
135+
print(f\"{ws} {proj_str} {fmt_enum('priority')} {fmt_enum('status')} {fmt_people('reviewer')}\")
118136
")
119137

120-
PRIORITY_FIELD=$(echo "$PRIORITY_VAL" | cut -d: -f1)
121-
PRIORITY_ENUM=$(echo "$PRIORITY_VAL" | cut -d: -f2)
122-
PRIORITY_NAME=$(echo "$PRIORITY_VAL" | cut -d: -f3)
123-
STATUS_FIELD=$(echo "$STATUS_VAL" | cut -d: -f1)
124-
STATUS_ENUM=$(echo "$STATUS_VAL" | cut -d: -f2)
125-
STATUS_NAME=$(echo "$STATUS_VAL" | cut -d: -f3)
138+
PRIORITY_FIELD=$(echo "$PRIORITY_INFO" | cut -d: -f1)
139+
PRIORITY_ENUM=$(echo "$PRIORITY_INFO" | cut -d: -f2)
140+
PRIORITY_NAME=$(echo "$PRIORITY_INFO" | cut -d: -f3)
141+
STATUS_FIELD=$(echo "$STATUS_INFO" | cut -d: -f1)
142+
STATUS_ENUM=$(echo "$STATUS_INFO" | cut -d: -f2)
143+
STATUS_NAME=$(echo "$STATUS_INFO" | cut -d: -f3)
144+
REVIEWER_FIELD=$(echo "$REVIEWER_INFO" | cut -d: -f1)
145+
REVIEWER_GID=$(echo "$REVIEWER_INFO" | cut -d: -f2)
146+
REVIEWER_NAME=$(echo "$REVIEWER_INFO" | cut -d: -f3)
147+
148+
# Auto-resolve implementor to current user
149+
IMPLEMENTOR_FIELD="1203334386796983"
150+
IMPLEMENTOR_GID="$CURRENT_USER_GID"
151+
IMPLEMENTOR_NAME="current user"
126152

127153
# Phase 3: Create the task
128154
NOTES_JSON=$(python3 -c "import json; print(json.dumps('''$TASK_NOTES'''))")
129155

130-
custom_fields_json="{}"
131-
if [[ -n "$PRIORITY_FIELD" && -n "$PRIORITY_ENUM" ]]; then
132-
custom_fields_json=$(python3 -c "
156+
# Build enum custom fields for task creation (people fields set separately after)
157+
custom_fields_json=$(python3 -c "
133158
import json
134159
cf = {}
135-
pf = '$PRIORITY_FIELD'
136-
pe = '$PRIORITY_ENUM'
137-
sf = '$STATUS_FIELD'
138-
se = '$STATUS_ENUM'
160+
pf, pe = '$PRIORITY_FIELD', '$PRIORITY_ENUM'
161+
sf, se = '$STATUS_FIELD', '$STATUS_ENUM'
139162
if pf and pe: cf[pf] = pe
140163
if sf and se: cf[sf] = se
141164
print(json.dumps(cf))
142165
")
143-
fi
144166

145167
# Build projects list from comma-separated GIDs
146168
IFS=',' read -ra PROJECT_ARR <<< "$PROJECT_GIDS"
@@ -180,6 +202,28 @@ if [[ -z "$NEW_GID" || "$NEW_GID" == "ERROR"* ]]; then
180202
exit 1
181203
fi
182204

205+
# Phase 3b: Set people fields (reviewer, implementor) via separate PUT
206+
# People fields use array format: {"field_gid": ["user_gid"]}
207+
people_fields_json=$(python3 -c "
208+
import json
209+
cf = {}
210+
rf, rg = '$REVIEWER_FIELD', '$REVIEWER_GID'
211+
imf, img = '$IMPLEMENTOR_FIELD', '$IMPLEMENTOR_GID'
212+
if rf and rg: cf[rf] = [rg]
213+
if imf and img: cf[imf] = [img]
214+
if cf:
215+
print(json.dumps({'data': {'custom_fields': cf}}))
216+
else:
217+
print('')
218+
")
219+
220+
if [[ -n "$people_fields_json" ]]; then
221+
curl -s -X PUT "$API/tasks/$NEW_GID" \
222+
-H "$AUTH" \
223+
-H "Content-Type: application/json" \
224+
-d "$people_fields_json" > /dev/null 2>&1 || true
225+
fi
226+
183227
FIRST_PROJECT=$(echo "$PROJECT_GIDS" | cut -d, -f1)
184228
echo "TASK_GID: $NEW_GID"
185229
echo "TASK_URL: https://app.asana.com/0/$FIRST_PROJECT/$NEW_GID"
@@ -194,9 +238,9 @@ curl -s -X POST "$API/tasks/$PARENT_GID/addDependencies" \
194238

195239
echo "DEPENDENCY_SET: $NEW_GID blocks $PARENT_GID"
196240

197-
if [[ -n "$PRIORITY_NAME" || -n "$STATUS_NAME" ]]; then
198-
fields_msg=""
199-
[[ -n "$PRIORITY_NAME" ]] && fields_msg="priority=$PRIORITY_NAME"
200-
[[ -n "$STATUS_NAME" ]] && fields_msg="${fields_msg:+$fields_msg, }status=$STATUS_NAME"
201-
echo "FIELDS_SET: $fields_msg"
202-
fi
241+
fields_msg=""
242+
[[ -n "$PRIORITY_NAME" ]] && fields_msg="priority=$PRIORITY_NAME"
243+
[[ -n "$STATUS_NAME" ]] && fields_msg="${fields_msg:+$fields_msg, }status=$STATUS_NAME"
244+
[[ -n "$REVIEWER_NAME" ]] && fields_msg="${fields_msg:+$fields_msg, }reviewer=$REVIEWER_NAME"
245+
[[ -n "$IMPLEMENTOR_GID" ]] && fields_msg="${fields_msg:+$fields_msg, }implementor=$IMPLEMENTOR_NAME"
246+
[[ -n "$fields_msg" ]] && echo "FIELDS_SET: $fields_msg"

.cursor/commands/pr-review.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,26 @@ If the script exits code 2 with `PROMPT_GH_AUTH`, prompt: "`gh` CLI is not authe
2323
Save the output JSON — it contains `number`, `title`, `url`, `headRef`, `baseRef`, `headSha`, `reviews[]`, and `files[]` (with patches).
2424
</step>
2525

26-
<step id="2" name="Load review standards">
26+
<step id="2" name="Checkout PR branch">
27+
Checkout the PR branch to ensure file reads reflect the PR's code, not the current local branch:
28+
29+
```bash
30+
git fetch origin <headRef> && git checkout <headRef>
31+
```
32+
33+
Replace `<headRef>` with the branch name from the context output (e.g., `william/fix-eth-sync`).
34+
35+
If checkout fails due to uncommitted changes, prompt the user to stash or commit before proceeding.
36+
</step>
37+
38+
<step id="3" name="Load review standards">
2739
Read these files in parallel (skip any already present in `cursor_rules_context`):
2840

2941
- `~/.cursor/rules/review-standards.mdc`
3042
- `~/.cursor/rules/typescript-standards.mdc`
3143
</step>
3244

33-
<step id="3" name="Review changed files">
45+
<step id="4" name="Review changed files">
3446
For each changed file in the context output:
3547

3648
1. Read the full file to understand surrounding context (batch reads in parallel)
@@ -50,7 +62,7 @@ Categorize findings as:
5062
Cross-reference findings against `reviews[]` from the context output. Omit any findings already raised by another reviewer.
5163
</step>
5264

53-
<step id="4" name="Submit review">
65+
<step id="5" name="Submit review">
5466
If there are findings to report, prepare a review JSON and submit via the companion script:
5567

5668
```bash
@@ -87,7 +99,7 @@ Use `"REQUEST_CHANGES"` for critical issues, `"COMMENT"` for suggestions only, `
8799
</sub-step>
88100
</step>
89101

90-
<step id="5" name="Summarize">
102+
<step id="6" name="Summarize">
91103
After submitting (or if no findings), provide a summary in the chat response:
92104
- Number of files reviewed
93105
- Findings by category (critical, warning, suggestion)

.cursor/rules/fix-workflow-first.mdc

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ description: When a workflow issue is identified in a command or skill, fix the
33
alwaysApply: true
44
---
55

6-
# Fix Workflow Definitions First
6+
<goal>Ensure workflow definitions are fixed at the source before any workarounds are applied.</goal>
77

8-
When a misbehavior is traced to a command (`.cursor/commands/*.md`) or skill (`.cursor/skills/*/SKILL.md`):
8+
<rules description="Non-negotiable constraints.">
9+
<rule id="absolute-priority">Fixing the command/skill takes **absolute priority** over all other actions — including workarounds, continuing the original task, or applying temporary fixes.</rule>
10+
<rule id="no-workarounds-first">Do NOT apply workarounds or manual fixes before proposing the command/skill update. The correct sequence is: identify gap → propose fix → get approval → apply fix → then resume original task.</rule>
11+
<rule id="use-author">Always use `/author` to fix commands and skills. It contains current best practices and the mandatory revision checklist.</rule>
12+
</rules>
913

10-
1. **Stop** fixing the downstream symptom.
11-
2. **Identify the root cause** in the command/skill definition.
12-
3. **Fix the command/skill first** using `/author` (`~/.cursor/skills/author/SKILL.md`), which contains the current best practices and mandatory revision checklist for command/skill authoring.
13-
4. **Then** fix the immediate misbehavior if still needed.
14+
<sequence name="When a workflow gap is discovered">
15+
1. **Stop immediately** — do not continue the current task or apply any workaround.
16+
2. **Identify the root cause** in the command (`.cursor/commands/*.md`) or skill (`.cursor/skills/*/SKILL.md`) definition.
17+
3. **Propose the fix** to the user and wait for approval before proceeding.
18+
4. **Fix the command/skill** using `/author` after approval.
19+
5. **Resume the original task** only after the command/skill is updated.
20+
</sequence>
1421

15-
This applies to all workflow issues — missed steps, incorrect output, wrong tool usage, formatting problems, etc. The command/skill is the source of truth; patching around it creates drift.
22+
<scope>This applies to all workflow issues — missed steps, incorrect output, wrong tool usage, formatting problems, etc. The command/skill is the source of truth; patching around it creates drift.</scope>

0 commit comments

Comments
 (0)