Security advisory triage
Each week Jimothy runs your audit command in a fresh worktree. Claude Code reads every advisory and checks whether your code reaches the vulnerable function, so the report separates real exposure from noise. The report goes to Slack either way; when there’s something reachable and fixable, it upgrades those packages, your tests run, and you approve the PR.
- Run audit Shell
- Check reachability Claude Code · Opus
- Post report Shell
- Anything to fix? Condition
- Upgrade packages Claude Code · Sonnet
- Run tests Shell
- Approve upgrade You
- Open pull request Shell
What each step does
Run audit
ShellRuns the audit command. Audits exit with an error when they find something, so the step always succeeds.
Check reachability
Claude Code · OpusChecks each advisory against your code and decides whether it matters and how to fix it.
Post report
ShellPosts the triage report to Slack.
Anything to fix?
ConditionContinues only when a reachable advisory can be fixed with a patch or minor upgrade.
Upgrade packages
Claude Code · SonnetUpgrades only the reachable, fixable packages and adjusts code if the upgrade needs it.
Run tests
ShellRuns your tests and sends failures back to the upgrade step.
Approve upgrade
YouShows you the report before the upgrade is pushed.
Open pull request
ShellPushes the branch and opens a pull request with the triage report.
From copy to first run
- Copy the pipeline JSON with the button above.
- In Jimothy, open Pipelines → New pipeline, paste it under “Or import a pipeline JSON” and click Import.
- Under Workspace, set Local repository to your clone.
- Fill in the variables below under Pipeline settings → Variables.
- Triggers → Add trigger → Schedule, cron 0 6 * * 2 (Tuesdays at 06:00).
What to fill in
The prompts and commands read these as {{vars.<name>}}. The examples are placeholders: replace them with your own.
| Variable | What to put in it | Example |
|---|---|---|
auditCommand | Your dependency audit command, e.g. npm audit --json, pip-audit -f json or cargo audit --json. | npm audit --json |
testCommand | The command that runs your test suite. | npm test |
slackWebhookUrl | A Slack incoming webhook for the security or engineering channel. | — |
Show the pipeline JSON
{
"jimothyPipeline": 1,
"name": "Security advisory triage",
"description": "Audits dependencies, checks which advisories your code actually reaches, and opens a PR fixing them.",
"icon": "bug",
"color": "#c2410c",
"repo": {
"mode": "worktree",
"localPath": "",
"baseBranch": "main",
"branchTemplate": "jimothy/security-{{run.number}}"
},
"concurrency": 1,
"variables": {
"auditCommand": "npm audit --json",
"testCommand": "npm test",
"slackWebhookUrl": ""
},
"steps": [
{
"id": "audit",
"name": "Run audit",
"type": "shell",
"description": "Runs the audit command. Audits exit with an error when they find something, so the step always succeeds.",
"timeoutMinutes": 10,
"command": "sh -c {{vars.auditCommand}} 2>&1 | head -c 60000; true"
},
{
"id": "triage",
"name": "Check reachability",
"type": "agent",
"description": "Checks each advisory against your code and decides whether it matters and how to fix it.",
"harnessId": "claude-code",
"model": "opus",
"dependsOn": [
"audit"
],
"timeoutMinutes": 40,
"prompt": "Dependency audit output:\n{{steps.audit.output}}\n\nFor each advisory, read the advisory and check this repository: is the vulnerable package used at runtime or only in development, and does our code call the vulnerable function or path? Do not modify any files.\n\nWrite a Slack report (*bold*, - bullets) with three groups: *Reachable* (with the file and line that reaches it), *Not reachable* (one line why), *Dev only*. For each reachable one, give the fixed version and whether upgrading is a patch, minor or major change.\n\nEnd with exactly one line: FIXABLE: yes if at least one reachable advisory can be fixed by a patch or minor upgrade, otherwise FIXABLE: no."
},
{
"id": "report",
"name": "Post report",
"type": "shell",
"description": "Posts the triage report to Slack.",
"dependsOn": [
"triage"
],
"timeoutMinutes": 2,
"command": "printf '{\"text\":%s}' {{steps.triage.output | json}} | curl -fsS -X POST -H 'Content-Type: application/json' --data @- {{vars.slackWebhookUrl}}"
},
{
"id": "fixable",
"name": "Anything to fix?",
"type": "condition",
"description": "Continues only when a reachable advisory can be fixed with a patch or minor upgrade.",
"dependsOn": [
"triage"
],
"condition": {
"match": "all",
"rules": [
{
"value": "{{steps.triage.output}}",
"op": "matches",
"compare": "FIXABLE:\\s*yes"
}
]
}
},
{
"id": "fix",
"name": "Upgrade packages",
"type": "agent",
"description": "Upgrades only the reachable, fixable packages and adjusts code if the upgrade needs it.",
"harnessId": "claude-code",
"model": "sonnet",
"dependsOn": [
"fixable"
],
"timeoutMinutes": 40,
"prompt": "Triage report:\n{{steps.triage.output}}\n\nUpgrade only the packages listed as reachable and fixable by a patch or minor upgrade, to the lowest fixed version. Update the lockfile, adjust code if the upgrade requires it, and commit with a message listing each advisory fixed.{{#if steps.test.output}}\n\nThe tests failed after your last attempt:\n{{steps.test.output | truncate:6000}}{{/if}}"
},
{
"id": "test",
"name": "Run tests",
"type": "shell",
"description": "Runs your tests and sends failures back to the upgrade step.",
"dependsOn": [
"fix"
],
"timeoutMinutes": 30,
"loopBackTo": "fix",
"maxLoops": 2,
"command": "{{vars.testCommand | raw}}"
},
{
"id": "approve",
"name": "Approve upgrade",
"type": "approval",
"description": "Shows you the report before the upgrade is pushed.",
"dependsOn": [
"test"
],
"approvalMessage": "Open a PR with these security upgrades?\n\n{{steps.triage.output}}"
},
{
"id": "pr",
"name": "Open pull request",
"type": "shell",
"description": "Pushes the branch and opens a pull request with the triage report.",
"dependsOn": [
"approve"
],
"timeoutMinutes": 5,
"command": "git push -u origin HEAD && gh pr create --base {{run.baseBranch}} --title \"Fix reachable security advisories\" --body {{steps.triage.output}}"
}
]
}More for engineering, and beyond
Incident postmortem draft
Turns an incident timeline into a blameless postmortem, traced to the commits that caused it, and opens it as a pull request.
EngineeringFlaky test hunter
Runs your test suite several times overnight. If some runs fail and others pass, an agent finds the cause, fixes it properly and opens a PR.
EngineeringRelease notes from a tag
When you push a release tag, writes release notes from the commits since the last one and publishes them to the GitHub release.