Skip to content

Add Cursor agent workflow: commands, scripts, rules, and skills#30

Open
j0ntz wants to merge 27 commits intomasterfrom
jon/agents
Open

Add Cursor agent workflow: commands, scripts, rules, and skills#30
j0ntz wants to merge 27 commits intomasterfrom
jon/agents

Conversation

@j0ntz
Copy link

@j0ntz j0ntz commented Feb 18, 2026

Complete agent-assisted development workflow for Edge repositories — slash skills with companion scripts, coding standards, review standards, and the author skill.

Installation

1. Set the required env var in your ~/.zshrc:

export GIT_BRANCH_PREFIX=yourname   # e.g. jon, paul, sam — used for branch naming and PR discovery

2. Install files into ~/.cursor/:

curl -sL https://github.com/EdgeApp/edge-conventions/archive/refs/heads/jon/agents.tar.gz | \
  tar -xz --strip-components=2 -C ~/.cursor 'edge-conventions-jon-agents/.cursor' && \
  find ~/.cursor -type f -name "*.sh" -exec chmod +x {} + && \
  echo "✓ Installed into ~/.cursor/"

3. Verify prerequisites:

  • gh CLI — gh auth login
  • jqbrew install jq
  • ASANA_TOKEN env var (Asana scripts only)
  • ASANA_GITHUB_SECRET env var (Asana PR attachment only — obtain via OAuth)

Table of Contents


Architecture

.cursor/
├── skills/            # Primary slash skills (*/SKILL.md) + skill scripts
├── scripts/           # Shared utility scripts (status dashboard, portability)
├── commands/          # Minimal legacy command wrappers (if present)
└── rules/             # Coding/review standards (.mdc)

Separation of concerns:

  • Commands (.md) — Define agent workflows: steps, rules, edge cases. Invoked explicitly via /command.
  • Skills (SKILL.md) — Primary workflow units invoked with /skill-name (or selected by context).
  • Companion scripts (.sh, .js) — Handle deterministic operations: API calls, git ops, JSON processing. Skills call scripts; scripts never call skills.
  • Rules (.mdc) — Persistent coding standards loaded on-demand by file type or command step. Two classes: editing standards (loaded when writing code) and review standards (loaded during PR review).

All GitHub API operations use gh CLI (gh api, gh api graphql, gh pr). No raw curl + $GITHUB_TOKEN.

User-specific configuration is driven by the GIT_BRANCH_PREFIX env var — set once in .zshrc, used by scripts for branch naming ($GIT_BRANCH_PREFIX/feature-name) and PR discovery. No hardcoded usernames.


Skills (Slash Skills)

Core Implementation

Skill Description
/im Implement an Asana task or ad-hoc feature/fix with clean, structured commits
/one-shot Legacy-style one-command flow: /asana-plan/im/pr-create with default Asana attach/assign
/pr-create Create a PR from the current branch; optional Asana attach/assign flags
/dep-pr Create dependent Asana tasks and run downstream PR workflow
/changelog Update CHANGELOG.md following existing patterns

Planning and Context

Skill Description
/asana-plan Build implementation plans from Asana tasks or text/file requirements
/task-review Fetch + analyze Asana task context
/q Answer questions before taking action

Review and Landing

Skill Description
/pr-review Review a PR against coding and review standards
/pr-address Address PR feedback with fixup commits and replies
/pr-land Land approved PRs: prepare, merge, publish, and Asana updates

Asana and Utility

Skill Description
/asana-task-update Generic Asana mutations (attach PR, assign, status/field updates)
/standup Generate daily standup from Asana + GitHub activity
/chat-audit Audit chat sessions for workflow/rule issues
/convention-sync Sync ~/.cursor changes with this repo and update PR description
/author Create/update/debug skills and related scripts/rules

Companion Scripts

PR Operations

Script What it does API
pr-create.sh Create PR for current branch with auto-generated title/body gh pr create
pr-address.sh Fetch unresolved feedback, post replies, resolve threads, mark addressed gh api REST + GraphQL
github-pr-review.sh Fetch PR context (metadata + patches) and submit reviews gh pr view + gh api REST
github-pr-activity.sh List PRs by activity (recent reviews, comments, CI status) gh api graphql

PR Status Dashboard

Script What it does API
pr-status-gql.sh PR status with review state, CI checks, new comments (primary) gh api graphql
pr-status.sh Same as above, REST fallback gh api REST
pr-watch.sh TUI wrapper — auto-refresh dashboard with rate limit awareness Delegates to above

PR Landing Pipeline (/pr-land)

These scripts run sequentially. Each handles one phase of the landing workflow:

Script Phase What it does API
pr-land-discover.sh 1: Discovery Find all $GIT_BRANCH_PREFIX/* PRs with approval status Single gh api graphql query
pr-land-comments.sh 2: Comment check Detect unaddressed feedback (inline threads, review bodies, top-level comments) gh api graphql per PR
pr-land-prepare.sh 3: Prepare Autosquash → rebase → conflict detection → verification Git only
verify-repo.sh 3b: Verify CHANGELOG validation + prepare/tsc/lint/test Git + yarn
pr-land-merge.sh 5: Merge Sequential merge with auto-rebase, mandatory verification gh api REST
pr-land-publish.sh 6: Publish Version bump, changelog update, commit + tag (no push) Git + npm

Conflict handling is fully scripted:

  • Code conflicts → skip PR, continue with remaining
  • CHANGELOG-only (including staging) → agent resolves semantically, re-runs

Chat Analysis

Script What it does
cursor-chat-extract.js Parse Cursor chat export JSON into compact structured summary (messages, tool calls, stats)

Asana Integration

Script What it does API
asana-get-context.sh Fetch task details, attachments, subtasks, custom fields Asana REST
asana-task-update.sh Generic task updates (attach PR, assign, status, fields) Asana REST
asana-create-dep-task.sh Create dependent task in another repo's project Asana REST
asana-whoami.sh Get current Asana user info Asana REST

Build & Deps

Script What it does
lint-commit.sh ESLint --fix before commit, auto-runs update-eslint-warnings when available
lint-warnings.sh Update eslint-warnings.mdc knowledge base from current lint output
install-deps.sh Install dependencies and run prepare script
upgrade-dep.sh Upgrade a dependency in the GUI repo

Sync & Portability

Script What it does
convention-sync.sh Diff and sync ~/.cursor/ files with the edge-conventions repo. ~/.cursor/ is canonical; repo is the distribution copy. No bidirectional conflict detection.
tool-sync.sh Sync Cursor rules, skills, and scripts to OpenCode and Claude Code formats
port-to-opencode.sh Convert Cursor .mdc/.md files to OpenCode-compatible JSON + MD mirrors

Dependency Graph

Skill → Skill

graph LR
  asanaPlan["/asana-plan"]
  taskReview["/task-review"]
  im["/im"]
  oneShot["/one-shot"]
  depPr["/dep-pr"]
  prCreate["/pr-create"]
  asanaTaskUpdate["/asana-task-update"]
  author["/author"]
  conventionSync["/convention-sync"]

  oneShot --> asanaPlan
  oneShot --> im
  oneShot --> prCreate
  asanaPlan --> taskReview
  im --> asanaPlan
  depPr --> prCreate
  prCreate --> asanaTaskUpdate
  author --> conventionSync
Loading

Skills with no skill dependencies:

  • /asana-task-update
  • /task-review
  • /q
  • /pr-review
  • /pr-address
  • /pr-land
  • /standup
  • /chat-audit
  • /changelog
  • /convention-sync

Full Skill/Script Dependency Graph

Top-to-bottom organization: skill layer, skill-specific scripts, shared scripts.

graph TD
  subgraph skillLayer [Skills]
    im["/im"]
    oneShot["/one-shot"]
    asanaPlan["/asana-plan"]
    taskReview["/task-review"]
    depPr["/dep-pr"]
    prCreate["/pr-create"]
    asanaTaskUpdate["/asana-task-update"]
    prLand["/pr-land"]
    prReview["/pr-review"]
    prAddress["/pr-address"]
    standup["/standup"]
    chatAudit["/chat-audit"]
    conventionSync["/convention-sync"]
    author["/author"]
    q["/q"]
    changelog["/changelog"]
  end

  subgraph skillScripts [Skill Scripts]
    prCreateSh["pr-create.sh"]
    prAddressSh["pr-address.sh"]
    prReviewSh["github-pr-review.sh"]
    depTaskSh["asana-create-dep-task.sh"]
    asanaTaskUpdateSh["asana-task-update.sh"]
    prLandDisc["pr-land-discover.sh"]
    prLandCmts["pr-land-comments.sh"]
    prLandPrep["pr-land-prepare.sh"]
    prLandMerge["pr-land-merge.sh"]
    prLandPublish["pr-land-publish.sh"]
    prLandExtract["pr-land-extract-asana-task.sh"]
    standupAsana["asana-standup.sh"]
    standupGh["github-pr-activity.sh"]
    chatExtract["cursor-chat-extract.js"]
    conventionSyncSh["convention-sync.sh"]
    generateClaude["generate-claude-md.sh"]
    lintWarn["lint-warnings.sh"]
  end

  subgraph sharedScripts [Shared Scripts]
    lintCommit["lint-commit.sh"]
    verifyRepo["verify-repo.sh"]
    asanaGetContext["asana-get-context.sh"]
    asanaWhoAmI["asana-whoami.sh"]
    installDeps["install-deps.sh"]
    edgeRepo["edge-repo.js"]
  end

  oneShot --> asanaPlan
  oneShot --> im
  oneShot --> prCreate
  asanaPlan --> taskReview
  im --> asanaPlan
  depPr --> prCreate
  prCreate --> asanaTaskUpdate
  author --> conventionSync

  prCreate --> prCreateSh
  prCreate --> verifyRepo
  prCreate --> asanaTaskUpdateSh
  im --> lintWarn
  im --> lintCommit
  im --> verifyRepo
  im --> installDeps
  depPr --> depTaskSh
  depPr --> asanaGetContext
  asanaTaskUpdate --> asanaTaskUpdateSh
  taskReview --> asanaGetContext
  prAddress --> prAddressSh
  prAddress --> lintCommit
  prReview --> prReviewSh
  prLand --> prLandDisc
  prLand --> prLandCmts
  prLand --> prLandPrep
  prLand --> prLandMerge
  prLand --> prLandPublish
  prLand --> prLandExtract
  prLand --> asanaTaskUpdateSh
  prLand --> verifyRepo
  standup --> standupAsana
  standup --> standupGh
  chatAudit --> chatExtract
  conventionSync --> conventionSyncSh
  conventionSync --> generateClaude

  depTaskSh --> asanaWhoAmI
  depTaskSh --> asanaTaskUpdateSh
  asanaTaskUpdateSh --> asanaWhoAmI
  standupAsana --> asanaWhoAmI
  prLandPrep --> edgeRepo
  prLandMerge --> edgeRepo
  prLandPublish --> edgeRepo
Loading

Shared Module: edge-repo.js

edge-repo.js eliminates duplication across the pr-land-* scripts. Exports:

Function Purpose
getRepoDir(repo) Resolve local checkout path (~/git/, ~/projects/, ~/code/)
getUpstreamBranch(repo) origin/develop for GUI, origin/master for everything else
runGit(args, cwd, opts) Safe spawnSync wrapper with GIT_EDITOR=true
parseConflictFiles(output) Extract conflicting file paths from rebase output
isChangelogOnly(files) Check if all conflicts are in CHANGELOG.md
runVerification(repoDir, baseRef, opts) Run the full verify script with scoped lint (supports {requireChangelog: true})
ghApi(endpoint, opts) gh api wrapper with method, body, paginate, jq support
ghGraphql(query, vars) gh api graphql wrapper with typed variable injection

Rules (.mdc files)

Rule Activation Purpose
typescript-standards.mdc Loaded before editing .ts/.tsx files TypeScript + React coding standards for editing (includes simple-selectors rule, descriptive variable names, biggystring arithmetic)
review-standards.mdc Loaded by /pr-review command ~50 review-specific diagnostic rules extracted from PR history
load-standards-by-filetype.mdc Always applied Auto-loads language-specific standards before editing
fix-workflow-first.mdc Always applied Fix command/skill definitions before patching downstream symptoms
answer-questions-first.mdc Always applied Detect ? in user messages → answer before acting; loads active command context to evaluate workflow gaps
no-format-lint.mdc Always applied Don't manually fix formatting — auto-format on agent finish handles it
eslint-warnings.mdc .ts/.tsx files ESLint warning handling patterns

Editing vs. review separation: typescript-standards contains rules for writing code (prefer useHandler, use InteractionManager, descriptive variable names, biggystring for numeric calculations). review-standards contains diagnostic patterns for catching bugs during review (null tokenId fallback, stack trace preservation, module-level cache bugs, etc.). Both are loaded together during /pr-review; only typescript-standards is loaded during editing.


Author Skill

Skill Purpose
author/SKILL.md Meta-skill for creating/maintaining skills, scripts, and rules. Enforces XML format, scripts-over-reasoning, gh-cli-over-curl, dependency-audit requirements before script add/update/remove, and convention-sync/CLAUDE sync post-authoring behavior.

Design Principles

  1. Scripts over reasoning — Deterministic operations (API calls, git, JSON) go in companion scripts, not inline in commands.
  2. gh CLI over curl — All GitHub API calls use gh api / gh api graphql. Handles auth, pagination, API versioning automatically.
  3. GraphQL over REST — Fetch only required fields in a single request where possible. Fall back to REST only when GraphQL doesn't expose the needed data (e.g., file patches).
  4. DRY shared modules — Common utilities extracted into edge-repo.js rather than duplicated across scripts.
  5. XML format — Skills use XML structure (<goal>, <rules>, <step>) for reliable LLM instruction-following.
  6. Standards-first — Load coding standards before writing or reviewing any code.
  7. Fix workflow first — When behavior is wrong, fix the command/skill definition, not the downstream symptom.
  8. No hardcoded usernames — All user-specific values come from GIT_BRANCH_PREFIX env var, set once in .zshrc.
  9. Local-canonical sync~/.cursor/ is the source of truth. The repo is the distribution copy. convention-sync defaults to user-to-repo; use --repo-to-user only for onboarding or pulling others' changes. No bidirectional conflict detection.
  10. Minimize context — Script output must be compact and structured. Never return raw API responses. Every token costs context.
  11. Small-model conventions — High-frequency skills that run on faster/cheaper models use verbatim bash, file-over-args, inline guardrails, and explicit parallel instructions for reliability.
  12. Knowledge base over crawling — Maintain curated knowledge files (e.g., eslint-warnings.mdc) instead of having the agent crawl/grep for information repeatedly. Pre-indexed knowledge reduces tool calls and context consumption.
  13. Continuous improvement — Workflows feed back into their own knowledge. PR review feedback updates review-standards.mdc, addressed warnings update eslint-warnings.mdc, and chat audits surface rule gaps. Each cycle reduces repetitive context gathering by the agent and repetitive review by humans.

Note

Medium Risk
Large addition of local automation scripts that perform git operations and call external APIs (GitHub/Asana) with tokens; main risk is incorrect/misused scripts causing unintended local repo changes or API mutations.

Overview
Introduces a new .cursor/ convention bundle that standardizes agent-driven development workflows: slash skills (planning/implementation/review/landing), always-on rules and TypeScript/review standards, plus a top-level .cursor/README.md describing installation and design principles.

Adds multiple companion scripts to automate deterministic operations: Asana context fetching and task mutation (including PR attachment), lint/commit and dependency install helpers, PR status dashboards (pr-status*.sh + pr-watch.sh), and portability/sync tooling (convention-sync.sh, tool-sync.sh, port-to-opencode.sh) to mirror rules/skills into other tool formats.

Written by Cursor Bugbot for commit 8726edd. This will update automatically on new commits. Configure here.

@j0ntz j0ntz force-pushed the jon/agents branch 5 times, most recently from f1496fd to 509d503 Compare February 18, 2026 22:32
Complete agent-assisted development workflow for Edge repositories.
Includes slash commands with companion scripts, coding standards rules,
review standards extracted from PR history, and the author skill for
creating/maintaining commands and skills.
pr-create.sh: Replace --body with --body-file to avoid shell escaping
issues with multi-line PR descriptions. Remove broken --json flag and
parse PR URL from stdout instead. Auto-generates body when no file given.

pr-create.md: Instruct agent to write body to /tmp/pr-body.md via Write
tool, then pass --body-file. Fix stale changelog-required rule to match
im.md ("last feature commit" not "dedicated final commit").
New <small-model-conventions> section with 8 patterns for authoring
commands that run reliably on smaller/faster models: verbatim bash,
file-over-args, exact output templates, explicit parallel instructions,
priority-ordered decisions, inline guardrails, no implicit steps, and
single-tool-per-step. Added revision checklist item #9.
im.md: Drop --quiet from eslint (warnings must be visible), clarify
lint-before-change applies to files discovered mid-implementation,
batch multiple files in a single eslint call.

lint-commit.sh: Auto-run update-eslint-warnings after staging when
the repo has the script, graduating files from the override list.

pr-review.md: Include PR link in review summary output.

typescript-standards.mdc: Add simple-selectors rule — keep useSelector
callbacks to pure state access, move derivation to useMemo.
convention-sync.sh: Diffs ~/.cursor/{commands,rules,skills} against the
repo, outputs structured JSON summary, and optionally copies + stages +
commits in one pass. Supports both user-to-repo and repo-to-user.

convention-sync.md: Dry-run first, confirm, commit+push workflow.
Replaces the manual diff/copy/stage/commit/push cycle.
@j0ntz j0ntz force-pushed the jon/agents branch 4 times, most recently from 0468e22 to 0e0d9a2 Compare February 19, 2026 20:39
Cursor over-allocates max_tokens for models with 1M context windows
(e.g. claude-opus-4-6), causing Venice to reject requests. This adds
a LiteLLM proxy config that clamps output tokens to safe limits.
@j0ntz
Copy link
Author

j0ntz commented Feb 20, 2026

NOTE FOR AGENT: when running /pr-land on this branch: Remove references to "To test before merge, use --branch jon/agents" from the README before merging.

Jon-edge and others added 8 commits February 20, 2026 15:59
…c, pr-address, lint-commit, im, typescript-standards
…step, author behavioral dependency checks; add lint-warnings.sh to README
Replace raw https API calls with gh api via child_process.execFile,
removing the GITHUB_TOKEN dependency. Drop ETag caching (gh handles
internally), add concurrency limiter for parallel PR fetches, and
fetch rate limit info via /rate_limit endpoint.
…ntion-sync to include scripts dir, update author skill with node-over-python principle
Copy link
Contributor

@samholmes samholmes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make all the commands into Skills since they are accompanied with a shells script. Also why ever use commands when we can just make skill repos? Commands could make sense for quick prompts, so if we want we can keep commands, but skills are shareable by convention, not commands, so why ever use commands except for personal reasons.

@bobbythelobster
Copy link

Suggestion: Document how to obtain ASANA_GITHUB_SECRET

asana-attach-pr.sh requires ASANA_GITHUB_SECRET but there's no documentation on how to get it. New team members (or agents) hitting this for the first time will be stuck.

The token comes from Asana's GitHub integration OAuth flow. To obtain it:

  1. Visit: https://github.integrations.asana.plus/auth?domainId=ghactions
  2. Authenticate with your GitHub account when prompted
  3. Authorize the Asana GitHub integration to access your repos
  4. After completing the OAuth flow, you'll receive a token — this is your ASANA_GITHUB_SECRET

Could you add a setup section to asana-attach-pr.sh (or a shared doc like a README) covering this? Something like:

# Setup: ASANA_GITHUB_SECRET
# This token authenticates against the Asana GitHub integration widget API.
# Obtain it by completing the OAuth flow at:
#   https://github.integrations.asana.plus/auth?domainId=ghactions
# Store it securely (e.g. in your shell profile or secrets manager).

This would save everyone the detective work of figuring out where the secret comes from.

j0ntz added 10 commits February 26, 2026 13:09
…e, update pr-address, pr-create, fix-workflow-first, answer-questions-first
Add command→script and script→script dependency graph to README.
Update pr-status.sh description (now uses gh api, not GITHUB_TOKEN).
Add install-deps.sh, tool-sync.sh, port-to-opencode.sh, convention-sync.sh
to the companion scripts tables.
…ommand links

Reorganize into three sections: shared resources (scripts/commands used by
3+ consumers), command→command dependencies, and full command→script graph
with SHARED markers for easy scanning.
Shared scripts appear as single nodes with bold gold borders.
Two diagrams: command→command relationships, and full dependency
graph (commands → scripts → shared modules).
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 4 issues found in the latest run.

  • ✅ Fixed: Hardcoded server name and path in push-env-key.sh
    • Replaced hardcoded SERVER and REMOTE_REPO with ENV_SERVER and ENV_REMOTE_REPO environment variables with validation.
  • ✅ Fixed: Status update passes enum GID to name-based resolver
    • Updated status_to_gid() to explicitly accept both human-readable names and raw GIDs, with error handling for unknown values.
  • ✅ Fixed: Grep uses Perl regex unsupported on macOS
    • Replaced grep -oP with portable sed command for parsing interval from script output.
  • ✅ Fixed: Corrupted image references in typescript-standards rule
    • Removed corrupted inline image references from the precompute-outside-loops code example.

Create PR

Or push these changes by commenting:

@cursor push 7713f85641
Preview (7713f85641)
diff --git a/.cursor/rules/typescript-standards.mdc b/.cursor/rules/typescript-standards.mdc
--- a/.cursor/rules/typescript-standards.mdc
+++ b/.cursor/rules/typescript-standards.mdc
@@ -73,7 +73,7 @@
 <standard id="extract-helpers">Extract reusable helpers for common boilerplate patterns (e.g., "run at most once in parallel").</standard>
 
 <standard id="precompute-outside-loops">Avoid calling expensive transformation functions (like `normalizeForSearch`, `toLowerCase`) inside loops when the input doesn't change per iteration. Pre-compute outside the loop.
-❌ `items.filter(item => searchTerms.every(term => normalize(item![1770928879881](image/typescript-standards/1770928879881.png)![1770928886532](image/typescript-standards/1770928886532.png).name).includes(term)))`
+❌ `items.filter(item => searchTerms.every(term => normalize(item.name).includes(term)))`
 ✅ `items.filter(item => { const n = normalize(item.name); return searchTerms.every(term => n.includes(term)) })`
 </standard>
 

diff --git a/.cursor/scripts/pr-watch.sh b/.cursor/scripts/pr-watch.sh
--- a/.cursor/scripts/pr-watch.sh
+++ b/.cursor/scripts/pr-watch.sh
@@ -64,7 +64,7 @@
   NOW=$(date '+%H:%M:%S')
 
   # Parse recommended interval from script output
-  RECOMMENDED=$(echo "$OUTPUT" | grep -oP '(?<=^# interval:)\d+' || echo "")
+  RECOMMENDED=$(echo "$OUTPUT" | sed -n 's/^# interval:\([0-9]\+\)/\1/p' | head -n 1)
 
   # Determine actual sleep interval
   if [[ -n "$INTERVAL" ]]; then

diff --git a/.cursor/scripts/push-env-key.sh b/.cursor/scripts/push-env-key.sh
--- a/.cursor/scripts/push-env-key.sh
+++ b/.cursor/scripts/push-env-key.sh
@@ -6,12 +6,26 @@
 # Examples:
 #   push-env-key.sh EDGE_API_KEY abc123
 #   push-env-key.sh EDGE_API_KEY abc123 -m "Rotate Edge API key"
+#
+# Requires env vars:
+#   ENV_SERVER: SSH hostname for the remote server
+#   ENV_REMOTE_REPO: Path to the remote repository
 
 set -euo pipefail
 
-SERVER="jack"
-REMOTE_REPO="/home/jon/jenkins-files/master"
+if [[ -z "${ENV_SERVER:-}" ]]; then
+  echo "Error: ENV_SERVER not set" >&2
+  exit 1
+fi
 
+if [[ -z "${ENV_REMOTE_REPO:-}" ]]; then
+  echo "Error: ENV_REMOTE_REPO not set" >&2
+  exit 1
+fi
+
+SERVER="$ENV_SERVER"
+REMOTE_REPO="$ENV_REMOTE_REPO"
+
 KEY=""
 VALUE=""
 COMMIT_MSG=""

diff --git a/.cursor/skills/asana-task-update/scripts/asana-task-update.sh b/.cursor/skills/asana-task-update/scripts/asana-task-update.sh
--- a/.cursor/skills/asana-task-update/scripts/asana-task-update.sh
+++ b/.cursor/skills/asana-task-update/scripts/asana-task-update.sh
@@ -90,7 +90,8 @@
     "Review Needed") echo "$REVIEW_NEEDED_OPTION" ;;
     "Publish Needed") echo "$PUBLISH_NEEDED_OPTION" ;;
     "Verification Needed") echo "$VERIFICATION_NEEDED_OPTION" ;;
-    *) echo "$1" ;;
+    "$REVIEW_NEEDED_OPTION"|"$PUBLISH_NEEDED_OPTION"|"$VERIFICATION_NEEDED_OPTION") echo "$1" ;;
+    *) echo "Error: Unknown status '$1' (expected name or GID)" >&2; exit 1 ;;
   esac
 }
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

if assignee:
data['data']['assignee'] = assignee
print(json.dumps(data))
")")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python triple-quote injection in shell-interpolated variables

High Severity

Shell variables like $TASK_NAME and $TASK_NOTES are interpolated directly into Python triple-quoted strings ('''$TASK_NAME'''). If any variable contains three consecutive single quotes ('''), the Python string terminates early, causing a syntax error or arbitrary code execution. Since these values originate from Asana task data (user-controlled input), this is a code injection vector. Safe alternatives include passing values via environment variables and reading them with os.environ inside Python.

Additional Locations (1)

Fix in Cursor Fix in Web


# Output JSON summary
collected_json=$(printf '%s\n' "${collected[@]}" | jq -R . | jq -s .)
skipped_json=$(printf '%s\n' "${skipped[@]}" | jq -R . | jq -s .)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty array produces [""] instead of [] in JSON

Medium Severity

When the skipped array is empty, printf '%s\n' "${skipped[@]}" outputs a single newline (printf always processes the format string once, using an empty string for %s). This causes jq -R . | jq -s . to produce [""] instead of []. The same issue affects collected_json in theory, though it's guarded by an early exit. Downstream consumers parsing this JSON would see a spurious empty-string entry.

Fix in Cursor Fix in Web

else
echo "Error: could not extract task GID from: $RAW_INPUT" >&2
exit 1
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL parser extracts subtask GID instead of task GID

Medium Severity

The URL regex /([0-9]+)(/f)?([?#].*)?$ matches the last numeric path segment. For subtask URLs like https://app.asana.com/0/project/task/subtask/99999, it extracts 99999 (the subtask GID) instead of the actual task GID. The comment says it "strips trailing path segments (/f, /subtask/…)" but the regex only handles the /f suffix — not /subtask/<id> or other trailing segments.

Fix in Cursor Fix in Web

out.push(`# interval:${recommendedInterval}`)

console.log(out.join("\n"))
' "$OWNER" "$REPO" "$USER" "$FORMAT" "$BUDGET" "$STATE_DIR" "$GQL_RESULT"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GraphQL result passed as argv may exceed ARG_MAX

Medium Severity

The entire GraphQL API response is captured into $GQL_RESULT and passed as a positional argument to node -e. For users with many open PRs (each including up to 100 comments and 100 review threads), this JSON payload can exceed the OS ARG_MAX limit (~256KB on some systems), causing an "Argument list too long" error. The REST fallback in pr-status.sh avoids this by running Node inline and fetching data internally.

Additional Locations (1)

Fix in Cursor Fix in Web

if [[ -n "$FIXUP" ]]; then
IS_FIXUP="true"
elif [[ "$MESSAGE" == fixup!* ]]; then
IS_FIXUP="true"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixup message detection missing required space character

Low Severity

The check [[ "$MESSAGE" == fixup!* ]] matches any message starting with fixup! without requiring the space that git's --autosquash convention mandates (fixup! ). A commit message like fixup!ed a typo would be misidentified as a fixup commit, triggering an unnecessary interactive rebase that could fail or reorder commits unexpectedly.

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

[[ -z "$f" ]] && continue
rm -f "$USER_DIR/$f"
done <<< "$all_del"
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repo-to-user sync copies/deletes wrong file sets

High Severity

In --repo-to-user mode, the script reuses all_copy (built from new_json + mod_json) and all_del (from del_json), but these categories are computed from the user→repo perspective. new_json contains files in ~/.cursor/ that are absent from the repo, so cp "$REPO_CURSOR/$f" "$USER_DIR/$f" fails because those files don't exist in the repo. Conversely, del_json contains files in the repo absent from the user dir, so rm -f "$USER_DIR/$f" is a no-op. For repo-to-user, the copy set needs del_json + mod_json and the delete set needs new_json — the semantics are inverted.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants