diff --git a/Gruntfile.js b/Gruntfile.js
index 97f4ec4c0a17ec4ee2f298eb0f433f9b0ca4ee6d..c3016a2b0069db7b3d7ac3c85f86074a0443d801 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -3,6 +3,7 @@
 module.exports = function(grunt) {
   "use strict";
 
+  RegExp.quote = require('regexp-quote')
   var btoa = require('btoa')
   // Project configuration.
   grunt.initConfig({
@@ -156,6 +157,17 @@ module.exports = function(grunt) {
         files: 'less/*.less',
         tasks: ['recess']
       }
+    },
+
+    sed: {
+      versionNumber: {
+        pattern: (function () {
+          var old = grunt.option('oldver')
+          return old ? RegExp.quote(old) : old
+        })(),
+        replacement: grunt.option('newver'),
+        recursive: true
+      }
     }
   });
 
@@ -173,6 +185,7 @@ module.exports = function(grunt) {
   grunt.loadNpmTasks('grunt-html-validation');
   grunt.loadNpmTasks('grunt-jekyll');
   grunt.loadNpmTasks('grunt-recess');
+  grunt.loadNpmTasks('grunt-sed');
 
   // Docs HTML validation task
   grunt.registerTask('validate-html', ['jekyll', 'validation']);
@@ -203,6 +216,11 @@ module.exports = function(grunt) {
   // Default task.
   grunt.registerTask('default', ['test', 'dist', 'build-customizer']);
 
+  // Version numbering task.
+  // grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
+  // This can be overzealous, so its changes should always be manually reviewed!
+  grunt.registerTask('change-version-number', ['sed']);
+
   // task for building customizer
   grunt.registerTask('build-customizer', 'Add scripts/less files to customizer.', function () {
     var fs = require('fs')
diff --git a/package.json b/package.json
index 46266fe0dad07418b74226777206b798e4226f89..ee3ce608038bb35b66a12ba10a72132a6e751961 100644
--- a/package.json
+++ b/package.json
@@ -34,5 +34,7 @@
     , "grunt-html-validation": "~0.1.6"
     , "grunt-jekyll": "~0.4.0"
     , "grunt-recess": "~0.4.0"
+    , "grunt-sed": "~0.1.1"
+    , "regexp-quote": "~0.0.0"
   }
 }