denote-acme/EXTENDING.md

3.2 KiB

Extending acme-denote

Architecture

acme-denote is extended by building programs upon its 9P API.

denote/
├── ctl          (write-only)  Control commands
├── dir          (read-only)   Current denote directory path
├── index        (read-only)   List of all notes (respects active filter)
├── new          (write-only)  Create new notes
└── n/
    └── <identifier>/
        ├── backlinks    (read-only)   Notes linking to this note
        ├── ctl          (write-only)  Per-note control commands
        ├── keywords     (read-write)  Comma-separated tags
        ├── path         (read-write)  Filesystem path
        └── title        (read-write)  Note title

Below is a minimal example of an extension that counts notes by a given tag.

#!/usr/local/plan9/bin/rc

# Dcount - Count notes by tag
if(~ $#* 0) {
    echo 'usage: Dcount <tag>' >[1=2]
    exit 1
}

tag=$1
echo 'filter tag:'$tag | 9p write denote/ctl || {
    echo 'failed to filter' >[1=2]
    exit 1
}

count=`{9p read denote/index | wc -l}
echo 'filter' | 9p write denote/ctl
echo 'Notes with tag' $tag':' $count
exit 0

Note: The example above is written with the Plan 9 rc shell syntax. An extension can be written in any language or shell syntax. The key requirement is the ability to perform 9P client operations (read, write, stat, ls, etc.)

Common Patterns

Patterns exhibited by existing extensions illustrated below (also rc syntax).

Read-Only Query (e.g., Dbkp)

denotedir=`{9p read denote/dir}
# Operate on $denotedir
tar czf backup.tar.gz $denotedir

Filter and Process (e.g., hypothetical Dqry)

echo 'filter tag:project' | 9p write denote/ctl
results=`{9p read denote/index}
echo 'filter' | 9p write denote/ctl  # Reset filter
# Process $results

Filter syntax:

  • tag:tagname - Filter by tag
  • title:pattern - Filter by title (use quotes for spaces: title:"my note")
  • date:YYYYMMDD - Filter by date
  • !tag:tagname - Exclude tag
  • Multiple filters space-separated: tag:work !tag:draft

Create and Open (e.g., Djournal)

# Create note
identifier=`{echo '''journal entry'' journal' | 9p write denote/new}

# Get the path
notepath=`{9p read denote/n/$identifier/path}

# Open in acme or plumb
plumb $notepath

Metadata Manipulation (e.g., Dmerge)

Dmerge performs metadata manipulation, and it also illustrates an important architectural concept: the acme-denote program manages metadata, not content (the latter is a function of the appropriate editor). Therefore, content manipulation is performed directly on the filesystem.

# Read source content
sourcepath=`{9p read denote/n/$sourceid/path}
content=`{cat $sourcepath}

# Update destination
destpath=`{9p read denote/n/$destid/path}
echo $content >> $destpath

# Delete source via ctl
echo 'd' | 9p write denote/n/$sourceid/ctl

Switching Directories (e.g., Dsilo)

Write to denote/ctl:

echo 'cd /path/to/new/silo' | 9p write denote/ctl
9p read denote/n/20251127T120000/backlinks

Returns notes that link to this identifier (same format as index).