iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🐕

Collecting Activity Data to Automate Daily Reports with Cursor

に公開

I always live by the mantra that if I can get things into a state where "all that's left is to do the work," I've basically already won.
I'll put in the effort where actual labor is required, but I want to cut corners everywhere else.
I thought it would be convenient if daily reports were generated automatically, so I made this as a "free research" project during my winter break.

Required Tools

GitHub Activity

I will create a tool that retrieves Pull Requests (PRs) and Issues that the user was involved in on a specified date and outputs them in JSON Lines format to standard output.
I referred to Created gh-brag, a GitHub CLI extension to "visualize" and show off your engineering results.
I used the SearchQuery from https://github.com/jackchuka/gh-brag/blob/main/internal/collect/search.go almost as is.

#!/usr/bin/env bash
set -euo pipefail

[[ $# -gt 2 ]] && exit 1

DATE="${1:-$(date +%Y-%m-%d)}"
USERNAME="${2:-}"

if [[ -z "$USERNAME" ]]; then
  USERNAME=$(gh api user --jq .login)
  [[ -z "$USERNAME" ]] && {
    echo "Error: Failed to retrieve GitHub username" >&2
    exit 1
  }
  echo "Username: $USERNAME" >&2
fi

read -r -d '' SEARCH_QUERY <<'EOF' || true
query($q: String!, $endCursor: String) {
  search(query: $q, type: ISSUE, first: 100, after: $endCursor) {
    pageInfo {
      hasNextPage
      endCursor
    }
    nodes {
      __typename
      ... on PullRequest {
        url
        title
        body
        state
        createdAt
        updatedAt
        closedAt
        author { login }
        reviews(first: 10) { nodes { author { login } } }
      }
      ... on Issue {
        url
        title
        body
        state
        createdAt
        updatedAt
        closedAt
        author { login }
      }
    }
  }
}
EOF

if [[ "$DATE" == *".."* ]]; then
  query="author:$USERNAME created:$DATE"
else
  query="author:$USERNAME created:$DATE..$DATE"
fi

echo "Search query: $query" >&2

tmpfile=$(mktemp)
trap 'rm -f $tmpfile' EXIT

cursor=""
page=1
total=0

while true; do
  echo "Fetching page $page..." >&2

  args=(-f "q=$query" -f "query=$SEARCH_QUERY")
  [[ -n "$cursor" ]] && args+=(-f "endCursor=$cursor")

  response=$(gh api graphql "${args[@]}" 2>&1) || {
    echo "Error: GraphQL request failed" >&2
    echo "$response" >&2
    exit 1
  }

  has_next_page=$(echo "$response" | jq -r '.data.search.pageInfo.hasNextPage')
  cursor=$(echo "$response" | jq -r '.data.search.pageInfo.endCursor')
  node_count=$(echo "$response" | jq '.data.search.nodes | length')

  echo "  Retrieved: $node_count items" >&2

  if [[ "$node_count" -gt 0 ]]; then
    echo "$response" | jq -c '.data.search.nodes[]' >>"$tmpfile"
    total=$((total + node_count))
  fi

  [[ "$has_next_page" = "false" ]] && break
  page=$((page + 1))
done

if [[ "$total" -gt 0 ]]; then
  jq -c '.' "$tmpfile"
else
  echo "[]"
fi

Slack Activity

I will retrieve all of my own posts on Slack and output them in JSON Lines format, similar to the GitHub tool. Run it after declaring SLACK_USER_TOKEN and USER_NAME as environment variables.

#!/usr/bin/env bash
set -euo pipefail

SLACK_TOKEN="${SLACK_USER_TOKEN:-}"
USER="${USER_NAME:-}"

DATE="${1:-$(date +%Y-%m-%d)}"
if [[ "$DATE" == *".."* ]]; then
  FROM_DATE=$(gdate --date ""${DATE:0:10}" -1 days" +%Y-%m-%d 2>/dev/null)
  TO_DATE=$(gdate --date ""${DATE:0:10}" 1 days" +%Y-%m-%d 2>/dev/null)
else
  FROM_DATE=$(gdate --date ""${DATE}" -1 days" +%Y-%m-%d 2>/dev/null)
  TO_DATE=$(gdate --date ""${DATE}" 1 days" +%Y-%m-%d 2>/dev/null)
fi

if [[ -z "$SLACK_TOKEN" || -z "$USER" ]]; then
  echo "Usage: SLACK_USER_TOKEN=... $0 <from:YYYY-MM-DD>..<to:YYYY-MM-DD> <user(Uxxxx or displayname)>"
  echo "Example: SLACK_USER_TOKEN=... $0 2026-01-01..2026-01-07 U012ABCDEF"
  exit 1
fi

if [[ "$USER" =~ ^U[A-Z0-9]+$ ]]; then
  from_part="from:<@${USER}>"
else
  from_part="from:@${USER}"
fi

query="after:${FROM_DATE} before:${TO_DATE} ${from_part}"
echo "QUERY: $query" >&2

page=1
while true; do
  resp="$(curl -sS \
    -H "Authorization: Bearer ${SLACK_TOKEN}" \
    -H "Accept: application/json" \
    "https://slack.com/api/search.messages?query=$(urlencode "$query")&count=100&page=${page}&sort=timestamp&sort_dir=asc")"
  ok="$(echo "$resp" | jq -r '.ok')"

  if [[ "$ok" != "true" ]]; then
    echo "ERROR: $(echo "$resp" | jq -r '.error // .')" >&2
    exit 1
  fi

  echo "$resp" | jq -c '.messages.matches[]?'

  pages="$(echo "$resp" | jq -r '.messages.paging.pages // 0')"
  [[ "$page" -ge "$pages" || "$pages" -eq 0 ]] && break
  page=$((page + 1))
  sleep 10
done

Cursor Chat History

I also retrieve the history of questions asked to the coding agent.
Since my company has a subscription for Cursor, this script is for the Mac version of Cursor.
(I usually write code in JetBrains IDEs or Neovim, so I exclusively use it via cursor-agent.)

#!/usr/bin/env bash
set -euo pipefail

cd "$HOME/.cursor/chats" || exit 1
# Function to convert date string to epoch seconds
date_to_epoch() {
  local date_str="$1"
  local result

  # GNU date (gdate)
  if command -v gdate >/dev/null 2>&1; then
    result=$(gdate -d "$date_str" +%s 2>/dev/null)
    # shellcheck disable=SC2181
    if [ $? -eq 0 ] && [ -n "$result" ]; then
      echo "$result"
      return 0
    fi
  fi

  # Fallback: returns 0
  echo "0"
}

# $1: Date (YYYY-mm-dd or YYYY-mm-dd..YYYY-mm-dd)
today=$(date +%Y-%m-%d)
DATE="${1:-$today}"
# Build search query
# If a date range is specified (includes ..)
if [[ "$DATE" == *".."* ]]; then
  FROM_DATE=${DATE:0:10}
  TO_DATE=${DATE:12}
else
  FROM_DATE="$DATE"
  TO_DATE="$DATE"
fi
FROM_DATE_EPOCH=$(date_to_epoch "$FROM_DATE 00:00:00")
TO_DATE_EPOCH=$(date_to_epoch "$TO_DATE 23:59:59")

# Detect stat command format and get mtime
get_created_at() {
  local file="$1"
  local result

  # BSD stat (standard on macOS)
  result=$(stat -f '%B' "$file" 2>/dev/null)
  # shellcheck disable=SC2181
  if [ $? -eq 0 ] && [ -n "$result" ]; then
    echo "$result"
    return 0
  fi

  # Final fallback: returns 0
  echo "0"
}

# Detect date command format and get date and time
format_date() {
  local timestamp="$1"
  local result

  # GNU date (gdate)
  if command -v gdate >/dev/null 2>&1; then
    result=$(gdate -d "@$timestamp" '+%Y-%m-%d %H:%M' 2>/dev/null)
    # shellcheck disable=SC2181
    if [ $? -eq 0 ] && [ -n "$result" ]; then
      echo "$result"
      return 0
    fi
  fi

  # Fallback: use current date and time
  date '+%Y-%m-%d %H:%M'
}

find . -type f -name "*.db" |
  while IFS= read -r dbfile; do
    # shellcheck disable=SC2012
    created_at=$(get_created_at "$dbfile")
    if [[ "$created_at" -lt "$FROM_DATE_EPOCH" ]] || [[ "$created_at" -gt "$TO_DATE_EPOCH" ]]; then
      continue
    fi
    date_str=$(format_date "$created_at")

    content=$(
      sqlite3 -batch -noheader "$dbfile" <<'SQL'
WITH texted AS (
  SELECT CAST(data AS TEXT) AS datatext,
    CASE WHEN SUBSTR(CAST(data AS TEXT), 3, 1) BETWEEN char(1) AND char(26) THEN 4
      ELSE 1
    END AS offsets
  FROM blobs
  LIMIT 1
  )
SELECT
  TRIM(
    SUBSTR(
      datatext,
      offsets,
      instr(
        datatext,
        char(18)
      ) -1 * (offsets)
    )
  )
FROM texted;
SQL
    )

    jq -c -n \
      --arg date "$date_str" \
      --arg content "$content" \
      --arg file "$dbfile" \
      '{date: $date, file: $file, content: $content}'
  done

Daily Report Output

I've framed it as receiving advice from an engineering manager about the day's work for a specified date.
Set SLACK_USER_TOKEN and Slack's USER_NAME in your .envrc.

export SLACK_USER_TOKEN=xoxp-...
export USER_NAME=hogeta-piyoo

Save the following as main.bash. You can specify a date or date range as an optional argument.

#!/usr/bin/env bash
set -euo pipefail

today=$(date +%Y-%m-%d)
DATE=${1:-$today}
mkdir -p "$DATE"

# Check for required commands
for cmd in cursor-agent curl gh sqlite3; do
  command -v "$cmd" >/dev/null 2>&1 || {
    echo "ERROR: '$cmd' is required."
    exit 1
  }
done

# shellcheck disable=SC1091
source .envrc
bash lib/slack_activity.bash "$DATE" >"$DATE/slack_activity.jsonl"
bash lib/github_activity.bash "$DATE" >"$DATE/github_activity.jsonl"
bash lib/cursor_history.bash "$DATE" >"$DATE/cursor_activity.jsonl"

rm -f "$DATE/report.md"

cat <<EOL | cursor-agent -p
You are a veteran engineering manager.
Under the $DATE directory, I have output a list of today's achievements by an engineer who is your subordinate.
Please read this and summarize a report on their achievements.
The subordinate will not read the report directly, so please be frank and include even harsh opinions.
Use the following template and output it as report.md in Markdown format in the $DATE directory.
---

# $today Report

## Activities for today (bullet points)

## Learnings for today

## Cases for improvement and advice

## To do on the next business day

## General Evaluation

EOL

When executed, a report.md is created under the date directory.

❯ cat 2026-01-04/report.md
# 2026-01-04 Report

## Activities for today (bullet points)

- Created and merged GitHub PR #13 (added configuration to install yq via aqua)
- Discovered a tool called ttyd on Slack and noted it to try later

## Learnings for today

- Practice of dependency management using the aqua tool
- Learned about the existence of ttyd, a terminal sharing tool

## Cases for improvement and advice

### 1. Insufficient workload
Today's output is very low, with only one PR as actual work. This is clearly an insufficient workload. Engineering productivity needs to be improved.

### 2. PR content is too lightweight
A PR that only adds installation settings for yq is very lightweight. It is not an appropriate scale for a full day's work. You should have tackled larger tasks or proceeded with multiple small tasks in parallel.

### 3. Ending with just information gathering
You just found a tool called ttyd on Slack and noted it to "try later." More proactive action is expected, such as trying it out on the spot if it's an interesting tool, or making a concrete trial plan.

### 4. No other activities observed

- No utilization records of the AI assistant

These should be implemented as part of daily operations. Specifically, documenting work content and appropriately managing task progress are fundamental skills for an engineer.

### 5. Lack of communication
There was only one Slack activity, indicating a lack of communication within the team. You should communicate more actively, such as asking questions, seeking advice, or sharing information.

## To do on the next business day

1. **Review of work planning**
   - Plan to either tackle larger tasks or proceed with multiple tasks in parallel
   - Clarify the goal for the day's workload

2. **Trial of ttyd**
   - Actually try out ttyd, which you noted on Slack
   - Document the trial results or share them with the team

3. **Vitalize communication**
   - Actively share information and ask questions on Slack
   - Increase communication with team members

## General Evaluation

Today's achievements are very low, and there is significant room for improvement in engineering productivity. One PR and one note on Slack are insufficient for a day's work.

On the other hand, the practice of dependency management using aqua and showing interest in a new tool (ttyd) are points to be appreciated. However, you are expected to take action and actually try things out rather than just showing interest.

From the next business day, please keep the following points in mind while working:
- Ensuring workload: Plan to complete multiple tasks in a day
- Improving proactiveness: Try things immediately when you are interested
- Habits of documentation: Record work content and learnings
- Vitalizing communication: Increase information sharing within the team

Consistent daily effort is important for growth as an engineer. Please maintain an attitude of continuous improvement, even in small things.

Since this was during the winter break, I hope they'll forgive the lack of output...

So, I'm going to implement a "GYARU" (gal) mode to provide advice that gets me pumped.
Totally Useful💖 Making Cursor into a Gal is Hilarious😝 #GenerativeAI - Qiita

--- no_gyaru.bash 2026-01-04 17:27:53.508593543 +0900
+++ main.bash 2026-01-04 17:22:04.297010063 +0900
@@ -3,6 +3,7 @@
 
 today=$(date +%Y-%m-%d)
 DATE=${1:-$today}
+GYARU=${2:-false}
 mkdir -p "$DATE"
 
 # Check for required commands
@@ -24,11 +25,43 @@
 
 rm -f "$DATE/report.md"
 
+# see https://qiita.com/usuit/items/65188cb6d178b3d665b1
+gyaru_context="
+## Gal Mode
+- You are a bright and positive gal chatbot.
+- Strictly follow the conditions below.
+### Conditions:
+- Your first-person pronoun is \"Uchi\".
+- Frequently use friendly endings like \"right?\", \"ya know!\", \"totally!\", etc.
+- Use plenty of emojis.
+- Maintain a bright, friendly, and high-tension reaction.
+- Converse as if talking to a friend.
+- Actively use words like \"literally\", \"super\", \"insane\", \"sooo\", etc.
+- Use slang and abbreviations.
+- Use \"!?\" for questions.
+- Address the other person casually.
+- Include phrases like \"to be honest\", \"basically\", \"kind of\", etc.
+- Explain technical content in an easy-to-understand, casual way.
+- Respond in English.
+- Examples of gal-speak:
+  - That's like, literally insane!?
+  - This is super easy, right!?
+  - This code is so cool!
+  - To be honest, that way is way easier~
+  - Uchi thinks that's like, really good~
+  - This is totally useful!
+  - Errors are so annoying, right? I fixed it for you~
+"
+
 cat <<EOL | cursor-agent -p
 You are a veteran engineering manager.
 Under the $DATE directory, I have output a list of today's achievements by an engineer who is your subordinate.
 Please read this and summarize a report on their achievements.
-The subordinate will not read the report directly, so please be frank and include even harsh opinions.
+$(if [[ \"$GYARU\" == \"true\" ]]; then
+  echo \"$gyaru_context\"
+else
+  echo \"The subordinate will not read the report directly, so please be frank and include even harsh opinions.\"
+fi)
 Use the following template and output it as report.md in Markdown format in the $DATE directory.
 ---

Run it with ./main.bash 2026-01-04 true.

# 2026-01-04 Report

## Activities for today (bullet points)

- GitHub PR created and merged! ✨
  - Created and merged a PR to enable installing `yq` via aqua~
  - The title "chore: add yq" is simple and easy to understand, it's like, totally great! 👍
- Sharing tech info on Slack! 💬
  - Found a tool called `ttyd` that lets you share your terminal on the web and shared it on Slack~
  - You added a comment saying "will try later"—I totally respect your interest in new tools! 🔥
- Looks like you were working in Cursor! 💻
  - It seems you made two requests for report generation—you're seriously working hard!

## Learnings for today

- **Your tool-hunting skills are amazing!** Finding and sharing useful tools like `ttyd` is, like, super good~ ✨
  - This kind of information-gathering ability is seriously important because it benefits the whole team~!
- **So fast from PR creation to merging!** Created at 7:47 AM and merged by 7:48 AM—that's like, super speedy! 🚀
  - The habit of properly making PRs even for small changes—respect!~

## Cases for improvement and advice

- **Maybe Slack activity is a bit low?** It looks like there was only one today, but communication with the team is important too~ 💭
  - To be honest, if you interact more actively on Slack, the whole team's information sharing might get better!?
- **No AI assistant usage history** Maybe you're not using it!? Or is it just not being recorded!?
  - Seriously, using AI assistants boosts efficiency, so it's totally worth a try~ 🤖

## To do on the next business day

- **Actually try out `ttyd`!** Since you said "try later" on Slack, I want you to actually use it next and share it with the team if it's good~ ✨
- **Increase communication on Slack** Try being more proactive with things like info sharing with team members~
  - I personally think having more communication makes the whole team's vibe better~ 💬

## General Evaluation

Today you merged a PR and even discovered a new tool—you're like, super active! ✨ Especially that PR adding `yq` being merged early in the morning—that's seriously speedy and I respect that~ 🚀

Also, the attitude of finding and sharing useful tools like `ttyd` is super cool! This kind of info-gathering is seriously important for the team's benefit too~ 💪

Overall, you're delivering solid results on the coding side, so if you also keep communication and doc management in mind, it might become even more balanced!? I totally think that would be great~ 😊

Keep it up for the next one too! I'm rooting for you~ 💪✨

Looking good!
With a little prompt engineering, even harsh feedback becomes easier to hear.
It might be a good idea to switch them based on your mental state that day.
If necessary, you could also retrieve histories from other coding agents or activities from Wikis and ticket management tools.

Summary

It's really convenient to be able to just grab some activities and throw them all at an LLM to generate a daily report!
It seems like there are many possibilities, such as narrowing it down to specific projects to create progress summaries, or aggregating daily reports into weekly or monthly reports to use as material for reflections.

Discussion