Is there a script or other way to selectively archive tasks

Let me raise again the 2nd part of my recent question on archiving @done tasks. The “archive @done items” menu command works globally archiving all done tasks and adding project name/stripping @done tag depending on the preferences settings.

I would like to selectively archive @done tasks (e.g. current selected task). In general for a given task with subtasks I like to keep track of which subtasks I’ve done (with notes) to follow progress until the whole task is done. In short I don’t want to archive the subtasks until the whole task is completed. The current global command doesn’t allow me to do that.

There used to be scripts in TP2 that allowed this. I miss this in TP3 and am hoping that the functionality can be provided either in the programme or as script.


1 Like

This is a modification of another script, and may serve as a stop-gap.

(It aims to archive any @done lines in the current selection)

If there are any tags which you want to remove from the archived version, you can list them in the option at the bottom of the script.

If not, just edit the default list from:

tagsToStrip: ['na', 'next', 'priority', 'waiting']


tagsToStrip: []

SCRIPT (Set the language selector in Script Editor to JavaScript to test this)


// Ver 0.3 prunes surplus indent of archived items

(function (dctOptions) { // Options at end of script
    'use strict';


    function fn(editor, options) {

        // Task with some of its tags removed
        function tagsPruned(oTask, lstNoiseTags) {
            return lstNoiseTags.reduce(function (t, strTag) {
                var strAttrib = 'data-' + strTag;

                if (t.hasAttribute(strAttrib)) {
                return t;
            }, oTask);

        // Task with a @project(strProjName) tag added (if not found)
        function projectTagged(oTask) {
            if (!oTask.hasAttribute('data-project')) {
                if (oTask.getAttribute('data-type') !== 'project') {
                    var project = taskProject(oTask),
                        strProj = project ? (function () {
                            var strText = project.bodyString;

                            return strText.slice(0, strText.indexOf(':'));
                        })() : '';

                    if (strProj) oTask.setAttribute('data-project', strProj);
            return oTask;

        // Immediately enclosing project of the task
        function taskProject(oTask) {
            var p = oTask,
                idRoot =,
                blnFound = false;

            while (!(blnFound = (p.getAttribute('data-type') === 'project'))) {
                if ('Birch' !== p = p.parent;
                else break;

            return blnFound ? p : undefined;

        // Capture any value from the edit callback
        // ( otherwise discarded by .groupUndoAndChanges )
        // (a -> b) -> a -> IO b
        function editResult(f, v) {
            var result;

            outline.groupUndoAndChanges(function (v) {
                result = f(v);

            return result;

        // ARCHIVE AND LOG

        var outline = editor.outline,

            as = editor.outline.evaluateItemPath(
                '/(Archive and @type=project)[-1]'
            archive = as.length > 0 ? as[0] : editResult(
                function () {
                    var newArchive = outline.createItem('Archive:');

                    return newArchive;

        var oFirstChild = archive.hasChildren ? archive.firstChild :
            editResult(function (x) {
                var oChild = outline.createItem();
                return oChild;

        var lstDone = editor.selection.selectedItems
            .filter(function (x) {
                return x.hasAttribute('data-done');

        if (lstDone.length > 0) {

            // If we have a non-circular destination, make the move ...
            if (oFirstChild && (lstDone.indexOf(oFirstChild) === -1)) {
                var lstNoise = options.tagsToStrip,

                    strProjectPrefix = options.projectPrefix;

                // project-tagged and noise-tag-pruned
                outline.groupUndoAndChanges(function () {
               (x) {
                            return projectTagged(
                                tagsPruned(x, lstNoise)
                    lstDone.forEach(function (x) {
                        if (x.indent > 1) x.indent = 1;

                // readable result string
                return (t) {
                        return (
                            ) === 'project' ? strProjectPrefix : ''
                        ) + t.bodyString;
            } else return [];
        } else return '';


    var ds = Application("com.hogbaysoftware.TaskPaper3")
        d = ds.length ? ds[0] : undefined;

    return d ? d.evaluate({
        script: fn.toString(),
        withOptions: {
            tagsToStrip: dctOptions.tagsToStrip,
    }) : '';

    // OPTIONS
})({ // Remove any tags from the archived copy of the line ?
    tagsToStrip: ['na', 'next', 'priority', 'waiting']

Many thanks - will give it a try asap.


Just tried out the script and it works just like you said i.e. I can selectively archive @done items (the highlighted ones) leaving the other ones in place Wonderful :slight_smile:

One small tweak I would like to see. The “archive all @done tasks” of TP3 removes the indentation of the archived task - your script leaves the original indentation which looks a little funny at time and is inconsistent with the TP3 archive behaviour.

I’ve look through the script but cannot figure out how to do it myself ? Can someone provide the final magical tweak to the script of ComplexPoint ?

Many thanks


I have made an edit to version 0.2 above, so that it prunes any excess indentation beyond the level of the Archive: project.

lstDone.forEach(function (x) {
    if (x.indent > 1) x.indent = 1;

Does that seem to fix it ?

( Like the TaskPaper 3 menu Tags > Archive @done items, it does still preserve any indentation relationships amongst clusters or sub-trees of items which are all @done, and are all archived together in the same script run )

1 Like

Works like a charm and I learn more about scripting possibilities to boot. Well done and many thanks.



Many scripts are not working since version 3.5. Almost all.

The 3.5 preview contains one or two small changes in API, I think. Once they are documented and there is a release version, we can adjust the scripts, which are currently written for the non-preview version.

In the latest release (todays) I think there’s a problem setting selection (just in the app code) so that might be causing other problems for scripts.

Hi all, I was heavily relying upon this script for my productivity, and now that I’ve finally got around to updating to TaskPaper 3.5 this script is broken.

Any ideas how to fix it easily? If not, I’ll try to comb through the code over the holidays.

Can you describe what’s broken? I just tried running in latest taskPaper preview release and it seems to work for me there. Note you might have to change:

var ds = Application("com.hogbaysoftware.TaskPaper3")

That hardcodes the script to the Mac App Store version of TaskPaper. Instead I use:

var ds = Application("TaskPaper")

Which works with all the different versions, but then has the problem that it will also try to work with TaskPaper 2 if you still have that installed.

Oh man, well that’s embarrassing. That was indeed the issue. Though I don’t know why it worked before since I’ve always used the TP downloaded from your site.

Anyway, thanks for your help.

1 Like