Add Cursor agent workflow: commands, scripts, rules, and skills#30
Add Cursor agent workflow: commands, scripts, rules, and skills#30
Conversation
f1496fd to
509d503
Compare
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.
0468e22 to
0e0d9a2
Compare
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.
|
NOTE FOR AGENT: when running |
…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
samholmes
left a comment
There was a problem hiding this comment.
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.
|
Suggestion: Document how to obtain
The token comes from Asana's GitHub integration OAuth flow. To obtain it:
Could you add a setup section to This would save everyone the detective work of figuring out where the secret comes from. |
…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).
… fix-workflow-first
There was a problem hiding this comment.
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.
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.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
}| if assignee: | ||
| data['data']['assignee'] = assignee | ||
| print(json.dumps(data)) | ||
| ")") |
There was a problem hiding this comment.
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)
|
|
||
| # Output JSON summary | ||
| collected_json=$(printf '%s\n' "${collected[@]}" | jq -R . | jq -s .) | ||
| skipped_json=$(printf '%s\n' "${skipped[@]}" | jq -R . | jq -s .) |
There was a problem hiding this comment.
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.
| else | ||
| echo "Error: could not extract task GID from: $RAW_INPUT" >&2 | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
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.
| out.push(`# interval:${recommendedInterval}`) | ||
|
|
||
| console.log(out.join("\n")) | ||
| ' "$OWNER" "$REPO" "$USER" "$FORMAT" "$BUDGET" "$STATE_DIR" "$GQL_RESULT" |
There was a problem hiding this comment.
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)
| if [[ -n "$FIXUP" ]]; then | ||
| IS_FIXUP="true" | ||
| elif [[ "$MESSAGE" == fixup!* ]]; then | ||
| IS_FIXUP="true" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.



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:2. Install files into
~/.cursor/:3. Verify prerequisites:
ghCLI —gh auth loginjq—brew install jqASANA_TOKENenv var (Asana scripts only)ASANA_GITHUB_SECRETenv var (Asana PR attachment only — obtain via OAuth)Table of Contents
Architecture
Separation of concerns:
.md) — Define agent workflows: steps, rules, edge cases. Invoked explicitly via/command.SKILL.md) — Primary workflow units invoked with/skill-name(or selected by context)..sh,.js) — Handle deterministic operations: API calls, git ops, JSON processing. Skills call scripts; scripts never call skills..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
ghCLI (gh api,gh api graphql,gh pr). No rawcurl+$GITHUB_TOKEN.User-specific configuration is driven by the
GIT_BRANCH_PREFIXenv 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
/im/one-shot/asana-plan→/im→/pr-createwith default Asana attach/assign/pr-create/dep-pr/changelogPlanning and Context
/asana-plan/task-review/qReview and Landing
/pr-review/pr-address/pr-landAsana and Utility
/asana-task-update/standup/chat-audit/convention-sync~/.cursorchanges with this repo and update PR description/authorCompanion Scripts
PR Operations
pr-create.shgh pr createpr-address.shgh apiREST + GraphQLgithub-pr-review.shgh pr view+gh apiRESTgithub-pr-activity.shgh api graphqlPR Status Dashboard
pr-status-gql.shgh api graphqlpr-status.shgh apiRESTpr-watch.shPR Landing Pipeline (
/pr-land)These scripts run sequentially. Each handles one phase of the landing workflow:
pr-land-discover.sh$GIT_BRANCH_PREFIX/*PRs with approval statusgh api graphqlquerypr-land-comments.shgh api graphqlper PRpr-land-prepare.shverify-repo.shprepare/tsc/lint/testpr-land-merge.shgh apiRESTpr-land-publish.shConflict handling is fully scripted:
Chat Analysis
cursor-chat-extract.jsAsana Integration
asana-get-context.shasana-task-update.shasana-create-dep-task.shasana-whoami.shBuild & Deps
lint-commit.sh--fixbefore commit, auto-runsupdate-eslint-warningswhen availablelint-warnings.sheslint-warnings.mdcknowledge base from current lint outputinstall-deps.shupgrade-dep.shSync & Portability
convention-sync.sh~/.cursor/files with the edge-conventions repo.~/.cursor/is canonical; repo is the distribution copy. No bidirectional conflict detection.tool-sync.shport-to-opencode.sh.mdc/.mdfiles to OpenCode-compatible JSON + MD mirrorsDependency Graph
Skill → Skill
Skills with no skill dependencies:
/asana-task-update/task-review/q/pr-review/pr-address/pr-land/standup/chat-audit/changelog/convention-syncFull 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 --> edgeRepoShared Module:
edge-repo.jsedge-repo.jseliminates duplication across thepr-land-*scripts. Exports:getRepoDir(repo)~/git/,~/projects/,~/code/)getUpstreamBranch(repo)origin/developfor GUI,origin/masterfor everything elserunGit(args, cwd, opts)spawnSyncwrapper withGIT_EDITOR=trueparseConflictFiles(output)isChangelogOnly(files)runVerification(repoDir, baseRef, opts){requireChangelog: true})ghApi(endpoint, opts)gh apiwrapper with method, body, paginate, jq supportghGraphql(query, vars)gh api graphqlwrapper with typed variable injectionRules (
.mdcfiles)typescript-standards.mdc.ts/.tsxfilessimple-selectorsrule, descriptive variable names, biggystring arithmetic)review-standards.mdc/pr-reviewcommandload-standards-by-filetype.mdcfix-workflow-first.mdcanswer-questions-first.mdc?in user messages → answer before acting; loads active command context to evaluate workflow gapsno-format-lint.mdceslint-warnings.mdc.ts/.tsxfilesEditing vs. review separation:
typescript-standardscontains rules for writing code (preferuseHandler, useInteractionManager, descriptive variable names, biggystring for numeric calculations).review-standardscontains diagnostic patterns for catching bugs during review (nulltokenIdfallback, stack trace preservation, module-level cache bugs, etc.). Both are loaded together during/pr-review; onlytypescript-standardsis loaded during editing.Author Skill
author/SKILL.mdscripts-over-reasoning,gh-cli-over-curl, dependency-audit requirements before script add/update/remove, and convention-sync/CLAUDE sync post-authoring behavior.Design Principles
ghCLI overcurl— All GitHub API calls usegh api/gh api graphql. Handles auth, pagination, API versioning automatically.edge-repo.jsrather than duplicated across scripts.<goal>,<rules>,<step>) for reliable LLM instruction-following.GIT_BRANCH_PREFIXenv var, set once in.zshrc.~/.cursor/is the source of truth. The repo is the distribution copy.convention-syncdefaults touser-to-repo; use--repo-to-useronly for onboarding or pulling others' changes. No bidirectional conflict detection.eslint-warnings.mdc) instead of having the agent crawl/grep for information repeatedly. Pre-indexed knowledge reduces tool calls and context consumption.review-standards.mdc, addressed warnings updateeslint-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.mddescribing 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.