From e5acff04ff90b382ff4c68c9cc4b07fc230fe47c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 17 Sep 2026 02:42:53 +0800 Subject: [PATCH 1/2] feat: support updating note titles --- README.md | 11 +++++++++-- src/commands/notes/update.ts | 8 +++++--- src/commands/team-notes/update.ts | 8 +++++--- src/note-update.ts | 4 +++- test/note-update.test.ts | 13 +++++++++++++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index df0c672..04aa67e 100644 --- a/README.md +++ b/README.md @@ -523,7 +523,7 @@ Update note ``` USAGE $ hackmd-cli notes update [--content ] [-h] [--noteId ] [--parentFolderId ] [--permalink - ] [--readPermission ] [--tags ] [--writePermission ] + ] [--readPermission ] [--tags ] [--title ] [--writePermission ] FLAGS -h, --help Show CLI help. @@ -533,12 +533,15 @@ FLAGS --permalink= note permalink --readPermission= set note permission: owner, signed_in, guest --tags= set note tags, comma-separated (e.g. tag1,tag2) + --title= new note title --writePermission= set note permission: owner, signed_in, guest DESCRIPTION Update note EXAMPLES + $ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --title='A new title' + $ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --content='# A new title' $ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --parentFolderId=fc7a3d48-4a07-4cbf-bf4f-e65dd896e01c --content='# A new title' @@ -804,7 +807,8 @@ Update team note ``` USAGE $ hackmd-cli team-notes update [--content ] [-h] [--noteId ] [--parentFolderId ] [--permalink - ] [--readPermission ] [--tags ] [--teamPath ] [--writePermission ] + ] [--readPermission ] [--tags ] [--teamPath ] [--title ] [--writePermission + ] FLAGS -h, --help Show CLI help. @@ -815,12 +819,15 @@ FLAGS --readPermission= set note permission: owner, signed_in, guest --tags= set note tags, comma-separated (e.g. tag1,tag2) --teamPath= HackMD team path + --title= new note title --writePermission= set note permission: owner, signed_in, guest DESCRIPTION Update team note EXAMPLES + $ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --title='A new title' + $ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --content='# A new title' $ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --parentFolderId=fc7a3d48-4a07-4cbf-bf4f-e65dd896e01c --content='# A new title' diff --git a/src/commands/notes/update.ts b/src/commands/notes/update.ts index 21ae05d..b0aae8b 100644 --- a/src/commands/notes/update.ts +++ b/src/commands/notes/update.ts @@ -4,7 +4,7 @@ import {Flags} from '@oclif/core' import HackMDCommand from '../../command' import { - noteContent, noteId, notePermission, noteTags, parentFolderId, permalink, + noteContent, noteId, notePermission, noteTags, noteTitle, parentFolderId, permalink, } from '../../flags' import {buildNoteUpdatePayload} from '../../note-update' import {safeStdinRead} from '../../utils' @@ -12,6 +12,7 @@ import {safeStdinRead} from '../../utils' export default class Update extends HackMDCommand { static description = 'Update note' static examples = [ + "$ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --title='A new title'", "$ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --content='# A new title'", "$ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --parentFolderId=fc7a3d48-4a07-4cbf-bf4f-e65dd896e01c --content='# A new title'", '$ hackmd-cli notes update --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --readPermission=owner --writePermission=owner', @@ -26,12 +27,13 @@ export default class Update extends HackMDCommand { permalink, readPermission: notePermission(), tags: noteTags, + title: noteTitle, writePermission: notePermission(), } async run() { const {flags} = await this.parse(Update) - const {content, noteId, parentFolderId, permalink, readPermission, tags, writePermission} = flags + const {content, noteId, parentFolderId, permalink, readPermission, tags, title, writePermission} = flags if (!noteId) { this.error('Flag noteId could not be empty') @@ -42,7 +44,7 @@ export default class Update extends HackMDCommand { try { payload = buildNoteUpdatePayload( { - content, parentFolderId, permalink, readPermission, tags, writePermission, + content, parentFolderId, permalink, readPermission, tags, title, writePermission, }, stdinContent, ) diff --git a/src/commands/team-notes/update.ts b/src/commands/team-notes/update.ts index 5b92981..5f22e92 100644 --- a/src/commands/team-notes/update.ts +++ b/src/commands/team-notes/update.ts @@ -4,7 +4,7 @@ import {Flags} from '@oclif/core' import HackMDCommand from '../../command' import { - noteContent, noteId, notePermission, noteTags, parentFolderId, permalink, teamPath, + noteContent, noteId, notePermission, noteTags, noteTitle, parentFolderId, permalink, teamPath, } from '../../flags' import {buildNoteUpdatePayload} from '../../note-update' import {safeStdinRead} from '../../utils' @@ -12,6 +12,7 @@ import {safeStdinRead} from '../../utils' export default class Update extends HackMDCommand { static description = 'Update team note' static examples = [ + "$ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --title='A new title'", "$ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --content='# A new title'", "$ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --parentFolderId=fc7a3d48-4a07-4cbf-bf4f-e65dd896e01c --content='# A new title'", '$ hackmd-cli team-notes update --teamPath=CLI-test --noteId=WNkLM6gkS0Cg2cQ8rv7bYA --readPermission=owner --writePermission=owner', @@ -27,12 +28,13 @@ export default class Update extends HackMDCommand { readPermission: notePermission(), tags: noteTags, teamPath, + title: noteTitle, writePermission: notePermission(), } async run() { const {flags} = await this.parse(Update) - const {content, noteId, parentFolderId, permalink, readPermission, tags, teamPath, writePermission} = flags + const {content, noteId, parentFolderId, permalink, readPermission, tags, teamPath, title, writePermission} = flags if (!teamPath) { this.error('Flag teamPath could not be empty') @@ -47,7 +49,7 @@ export default class Update extends HackMDCommand { try { payload = buildNoteUpdatePayload( { - content, parentFolderId, permalink, readPermission, tags, writePermission, + content, parentFolderId, permalink, readPermission, tags, title, writePermission, }, stdinContent, ) diff --git a/src/note-update.ts b/src/note-update.ts index 7d80736..ceaef4a 100644 --- a/src/note-update.ts +++ b/src/note-update.ts @@ -6,11 +6,12 @@ export type NoteUpdateFlags = { permalink?: string readPermission?: string tags?: string + title?: string writePermission?: string } export function buildNoteUpdatePayload( - {content, parentFolderId, permalink, readPermission, tags, writePermission}: NoteUpdateFlags, + {content, parentFolderId, permalink, readPermission, tags, title, writePermission}: NoteUpdateFlags, stdinContent?: string, ): UpdateNoteOptions { if (stdinContent && content !== undefined) { @@ -26,6 +27,7 @@ export function buildNoteUpdatePayload( if (writePermission !== undefined) payload.writePermission = writePermission as NotePermissionRole if (permalink !== undefined) payload.permalink = permalink if (tags !== undefined) payload.tags = tags.split(',').map(tag => tag.trim()).filter(Boolean) + if (title !== undefined) payload.title = title if (Object.keys(payload).length === 0) { throw new Error('Nothing to update') diff --git a/test/note-update.test.ts b/test/note-update.test.ts index be3b218..039bab1 100644 --- a/test/note-update.test.ts +++ b/test/note-update.test.ts @@ -1,8 +1,15 @@ import {expect} from 'chai' +import NotesUpdate from '../src/commands/notes/update' +import TeamNotesUpdate from '../src/commands/team-notes/update' import {buildNoteUpdatePayload} from '../src/note-update' describe('Note update payload', () => { + it('exposes --title for personal and team note updates', () => { + expect(NotesUpdate.flags).to.have.property('title') + expect(TeamNotesUpdate.flags).to.have.property('title') + }) + it('uses piped stdin content without changing it', () => { const content = '# Piped content\n\nBody with trailing newline.\n' @@ -30,12 +37,14 @@ describe('Note update payload', () => { permalink: 'new-permalink', readPermission: 'guest', tags: ' tag1, tag2, ,', + title: 'New title', writePermission: 'owner', })).to.deep.equal({ parentFolderId: 'folder-id', permalink: 'new-permalink', readPermission: 'guest', tags: ['tag1', 'tag2'], + title: 'New title', writePermission: 'owner', }) }) @@ -43,4 +52,8 @@ describe('Note update payload', () => { it('allows an explicit empty --content value', () => { expect(buildNoteUpdatePayload({content: ''}, '')).to.deep.equal({content: ''}) }) + + it('allows an explicit empty --title value', () => { + expect(buildNoteUpdatePayload({title: ''})).to.deep.equal({title: ''}) + }) }) From 10c9bffe93804008b631be6f0f84760751319b3a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 17 Sep 2026 02:55:03 +0800 Subject: [PATCH 2/2] docs: update skill for note title updates --- hackmd-cli.skill | Bin 2435 -> 2452 bytes hackmd-cli/SKILL.md | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hackmd-cli.skill b/hackmd-cli.skill index c3147ea5886f54403b7cc477f4f7e838b211d355..55ff35e5f5d49e76bd6a63555bb824c52553bcd4 100644 GIT binary patch delta 2315 zcmV+m3H0`Z6O@3IG5A005OB<&g~`E?Q?vYFlSXYIS%E009K`0RR956aWAK zP)h>@6aWAK2mqoMFeXH006y_Tpxds<*I6u<*I6Rcnbgl1oZ&`00a~O006yN zZExc?68`RAF-U;(E?_zBq3wsDE-rBF+uUtlut~Nk7K@mX=$N~*=p!jRzNYuvZ-%5~ zN|x*--8+A=MUnIR%y4ELMNubXbAAjn7Jr>5QJjiS!gZ{~!U&li!#i9*KZRJ%=PZ9q zqEuuYL}s{}vY5kED!_I2AhHKoaHVC&(j&v!95QLR20VkC(ga`i;Z}1nGoe9$6=@2q z8P6cs_zG4mGhigdiZjD|fL}=u@kDF;TCyTOoVTDq?qp+L&1>$jQ}NAK4$mH8=T!ejUPDrVUHe zPG>Ti+%rAvWQ#d^$}U3m0Dp5HJ_;O-JKfo#%%5!2Z9Jk|*up~c%ip54OIiwOz z25&T5qE`_X|2gV=>D}4>7c|)0&PEk?f;@7mtg(2u6iQ}u^foM+66`+ZodPrGr+28& z-N|q`y1u@9r3Me9YLcy7k3}}Jy;A$q>T-5o_2dunZ`ygqZ

APC-L+MNVi^H9l;4}tzquSjqO=+n=%$A&W@dw*?eYSDhA@9GDYOtM!PG>j1|uSM zD&uYw`CYJm0lQ~II+VpjY@e#7Ut4&$V$jh~y5EFuJ2pdGwgB|iT%QHreFiLHyq{%5 zes|29GrX^ELcYaPJ!bO`T^Yjpez&+j#NMJks{ z=iD&#Cwl>ppg4aYbgz{B!eax8e<={YgYU;befOP(aw-)9`G@fjKaGA2L#AD^5?&RX zmO}WDfYM+dLhS@g@N7AW+{P*R$M<4*^r?W6Axdco6Q z4?h3Vt!3d|7!AwttpM3zuq_`~KpfC|A*mfGe-6=o`PYAWx5%zdvGBj(^4y3Cc(;em zpOxfW#MN_*v@e104!o+1!UVpD?Y<1MIFhjbcli^>DSLkiF0q|ub@@T`YGb+YTjxXofIHMM8U!ON`BsaKDCyl9+kS8h2_m=b&Iz2zX5yE!cktZx)I#c$Ppn>e`tp-NpEfE__=MA!eQE zv4>{jkC+A%1XoFr#vqtGCnq@nwMEvC0FA78Jt7_Af2+*MGLmww(CmVspv zv~oGP9HH0|Ery|(uI(HJD}|jyl(GdpIA7BRe$eq#S2cG#C?BIy6i2x8C83w*i*0|y zE$@OF$xS7~9}(F^t_zNVzB{NFoXWR+%S==8JM?|Mtp> zS$({>EorqKe1Tk7mT&hNJBKroCVL9vQZPFH&Uf+-WAI3^G?}vYt7aDexD4t)z{bEmJfNiMS8qxn*rQJP33D374 zc+Iu@t$G(d!IytQxBuxg{_2Y)5PU2!qO91MPAs^060KwsHw`Wm=SV60FG!Z8RyLnV zIW7Bt+r*#h00000l_2F@00000005KO2q-LCXGv;xcnbgl1oZ&`00a~O002-+0RkQa6aWAK l2mqoMFeXH006y{EeS0HkmahAT?rrt)Cd3o006;OND}}6 delta 2315 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 O1Wp;Y=D!vL0{sJK=0Vi} diff --git a/hackmd-cli/SKILL.md b/hackmd-cli/SKILL.md index b56962f..32b75df 100644 --- a/hackmd-cli/SKILL.md +++ b/hackmd-cli/SKILL.md @@ -67,6 +67,7 @@ hackmd-cli notes create -e # Update note hackmd-cli notes update --noteId= --content='# New Content' +hackmd-cli notes update --noteId= --title='New title' # Move note into a folder hackmd-cli notes update --noteId= --parentFolderId= @@ -89,6 +90,7 @@ hackmd-cli team-notes create --teamPath= --parentFolderId= # Update team note hackmd-cli team-notes update --teamPath= --noteId= --content='# Updated' +hackmd-cli team-notes update --teamPath= --noteId= --title='New title' # Move team note into a folder hackmd-cli team-notes update --teamPath= --noteId= --parentFolderId= @@ -216,7 +218,7 @@ cat doc.md | hackmd-cli notes create --title="My Doc" hackmd-cli notes --filter=title="My Doc" # Update existing note from file and verify -cat doc.md | hackmd-cli notes update --noteId= +cat doc.md | hackmd-cli notes update --noteId= --title="My Doc" hackmd-cli export --noteId= | head -5 ```