Automatically estimate JIRA story points with AI using the --estimate flag

Story Points Estimation

Use the --estimate flag to have your AI agent analyze JIRA tasks and automatically assign story point estimates. This is useful for backlog grooming, sprint planning, or keeping estimates up to date as tasks evolve.

What It Does

  • Analyzes task description, comments, linked resources, and related issues
  • Produces a Fibonacci-scale estimate (1, 2, 3, 5, 8, 13, 21)
  • Provides confidence level (high / medium / low), reasoning, risks, and unclear areas
  • Sets the story points field directly in JIRA
  • Posts a rich estimation comment with full context

Usage

Single Task

devintern PROJ-123 --estimate

Batch via JQL

# Estimate all tasks in the backlog
devintern --estimate --jql "project = PROJ AND status = 'To Do'"

# Estimate unestimated tasks in the current sprint
devintern --estimate --jql "project = PROJ AND sprint in openSprints() AND 'Story Points' is EMPTY"

# Estimate recently updated tasks
devintern --estimate --jql "project = PROJ AND updated >= -7d"

Smart Behavior

Skip Recently Created Tasks

Tasks created less than 24 hours ago are automatically skipped. This gives the team time to refine the description before estimation.

Smart Re-Estimation

If a task already has an estimation comment:

  • If the task hasn’t been updated since the last estimate → skipped
  • If the task was updated after the last estimate → re-estimated in place (existing comment is updated, not duplicated)

This keeps estimates fresh without creating comment clutter.

Story Points Scale

PointsMeaning
1Trivial change, config tweak, typo fix
2Small, well-defined task, single file change
3Moderate task, a few files, clear requirements
5Significant feature, multiple files, some complexity
8Large feature, cross-cutting concerns, integration work
13Very large, multiple subsystems, high complexity
21Epic-sized, major architectural change, high uncertainty

Configuration

Story Points Field

The tool auto-discovers the story points field in JIRA by searching for common names like:

  • Story Points
  • Story Point Estimate
  • Story point estimate

If your JIRA instance uses a custom field name, you can override it in .devintern-code/settings.json:

{
  "projects": {
    "PROJ": {
      "storyPointsField": "customfield_10016"
    }
  }
}

Automated Estimation

Run estimation on a schedule via systemd timers (recommended on modern Linux servers) or cron (any Unix-like system without systemd).

systemd timers

A systemd job is a one-shot .service plus a .timer that triggers it. Below is the daily morning run; the weekly grooming and sprint-planning helpers follow the same shape with different OnCalendar expressions and JQL.

Daily estimation (weekday mornings)

Run every weekday at 9 AM to estimate new tasks.

/etc/systemd/system/devintern-estimate-daily.service:

[Unit]
Description=Daily estimation of NeedsEstimate tasks
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=devintern
WorkingDirectory=/path/to/your/project
ExecStart=/usr/local/bin/devintern --estimate \
  --jql 'project = PROJ AND status = "To Do" AND labels IN (NeedsEstimate)'
StandardOutput=journal
StandardError=journal

/etc/systemd/system/devintern-estimate-daily.timer:

[Unit]
Description=Run daily estimation at 9 AM on weekdays

[Timer]
OnCalendar=Mon..Fri *-*-* 09:00:00
Persistent=true

[Install]
WantedBy=timers.target

Enable and inspect:

sudo systemctl daemon-reload
sudo systemctl enable --now devintern-estimate-daily.timer
systemctl list-timers devintern-estimate-daily.timer
journalctl -u devintern-estimate-daily.service -f

Weekly backlog grooming

OnCalendar for Monday at 8 AM, JQL covers the last 7 days:

# devintern-estimate-weekly.timer
[Timer]
OnCalendar=Mon *-*-* 08:00:00
Persistent=true

[Install]
WantedBy=timers.target
# devintern-estimate-weekly.service — ExecStart
ExecStart=/usr/local/bin/devintern --estimate \
  --jql 'project = PROJ AND status = "Backlog" AND updated >= -7d'

Sprint planning helper

Wednesday at 10 AM, fills in any remaining gaps before planning:

# devintern-estimate-sprint.timer
[Timer]
OnCalendar=Wed *-*-* 10:00:00
Persistent=true

[Install]
WantedBy=timers.target
# devintern-estimate-sprint.service — ExecStart
ExecStart=/usr/local/bin/devintern --estimate \
  --jql 'project = PROJ AND sprint in openSprints() AND "Story Points" is EMPTY'

Cron

If you’re not on systemd, the same three schedules work as crontab entries:

# In crontab (run `crontab -e`)

# Daily at 9 AM, weekdays — estimate new tasks
0 9 * * 1-5 cd /path/to/your/project && devintern --estimate --jql 'project = PROJ AND status = "To Do" AND labels IN (NeedsEstimate)' >> /tmp/devintern-estimate.log 2>&1

# Weekly on Monday at 8 AM — re-estimate recently updated backlog
0 8 * * 1 cd /path/to/your/project && devintern --estimate --jql 'project = PROJ AND status = "Backlog" AND updated >= -7d' >> /tmp/devintern-estimate.log 2>&1

# Weekly on Wednesday at 10 AM — sprint planning helper
0 10 * * 3 cd /path/to/your/project && devintern --estimate --jql 'project = PROJ AND sprint in openSprints() AND "Story Points" is EMPTY' >> /tmp/devintern-estimate.log 2>&1

Important Notes

  • Set WorkingDirectory (systemd) or cd (cron) to your project directory so the correct .devintern-code/.env is loaded
  • Use absolute paths to devintern and your agent binary, or pin PATH explicitly in the unit file
  • For systemd, journalctl -u <unit> gives you logs; for cron, redirect stdout/stderr to a log file
  • Test your JQL query manually before scheduling
  • Tasks created less than 24 hours ago are automatically skipped, so frequent runs are safe

Example Output

📊 Running in estimation mode...

============================================================
📊 Estimating: PROJ-456

🔄 Re-estimating PROJ-456 — task updated since last estimate
✅ Estimated PROJ-456: 5 story points (high confidence)

============================================================
📊 Estimation Summary:
   Estimated: 3
   Skipped (< 24h old): 1
   Skipped (not updated): 2
   Failed: 0

Troubleshooting

“Story points field not found”

  • Check your JIRA instance’s custom field name for story points
  • Set storyPointsField in .devintern-code/settings.json

“Failed to parse estimation response”

  • The AI agent may have returned non-JSON output
  • Try running with --verbose to see the raw response
  • Check that your agent CLI is working correctly

Low confidence estimates

  • The estimation comment will flag low confidence and ask for more details
  • Consider refining the task description before re-estimating