/**
* hn note — Note management commands
*/
import fs from 'bare-fs'
import { HnClient } from '../client.js'
import { table, log, error, jsonOut } from '../output.js'
export async function noteCommand (subcommand, cmdArgs, flags) {
const client = new HnClient(flags)
switch (subcommand) {
case 'list':
return await listNotes(client, cmdArgs, flags)
case 'create':
return await createNote(client, cmdArgs, flags)
case 'show':
return await showNote(client, cmdArgs, flags)
case 'edit':
return await editNote(client, cmdArgs, flags)
case 'delete':
return await deleteNote(client, cmdArgs, flags)
default:
error(`Unknown note subcommand: ${subcommand || '(none)'}\nUsage: hn note [list|create|show|edit|delete]`)
}
}
async function listNotes (client, cmdArgs, flags) {
const notebook = cmdArgs[0]
if (!notebook) {
error('Usage: hn note list <notebook>')
}
let path = `/api/notebooks/${encodeURIComponent(notebook)}/notes`
const params = []
if (flags.limit) params.push(`limit=${flags.limit}`)
if (flags.offset) params.push(`offset=${flags.offset}`)
if (params.length) path += '?' + params.join('&')
const notes = await client.get(path)
if (flags.json) {
jsonOut(notes)
return
}
if (notes.length === 0) {
log(`No notes in '${notebook}'.`)
return
}
table(notes, [
{ key: 'id', label: 'ID', width: 16 },
{ key: 'title', label: 'TITLE', width: 40 },
{ key: 'tags', label: 'TAGS', width: 20 },
{ key: 'updatedAt', label: 'UPDATED', width: 20 }
])
}
async function createNote (client, cmdArgs, flags) {
const notebook = cmdArgs[0]
if (!notebook) {
error('Usage: hn note create <notebook> --title "..." [--content "..." | --file path] [--tags "a,b"]')
}
if (!flags.title) {
error('--title is required')
}
const body = { title: flags.title }
if (flags.file) {
body.content = fs.readFileSync(flags.file, 'utf-8')
} else if (flags.content) {
body.content = flags.content
}
if (flags.tags) {
body.tags = flags.tags.split(',').map(t => t.trim()).filter(Boolean)
}
const note = await client.post(`/api/notebooks/${encodeURIComponent(notebook)}/notes`, body)
if (flags.json) {
jsonOut(note)
return
}
log(`Created note: ${note.id} — ${note.title}`)
}
async function showNote (client, cmdArgs, flags) {
const notebook = cmdArgs[0]
const noteId = cmdArgs[1]
if (!notebook || !noteId) {
error('Usage: hn note show <notebook> <id>')
}
const note = await client.get(`/api/notebooks/${encodeURIComponent(notebook)}/notes/${encodeURIComponent(noteId)}`)
if (flags.json) {
jsonOut(note)
return
}
log(`# ${note.title}`)
if (note.tags && note.tags.length) {
log(`Tags: ${note.tags.join(', ')}`)
}
log(`ID: ${note.id} | Updated: ${new Date(note.updatedAt).toLocaleString()}`)
log('')
log(note.content || '(empty)')
}
async function editNote (client, cmdArgs, flags) {
const notebook = cmdArgs[0]
const noteId = cmdArgs[1]
if (!notebook || !noteId) {
error('Usage: hn note edit <notebook> <id> [--title "..." | --content "..." | --file path | --tags "a,b"]')
}
const body = {}
if (flags.title) body.title = flags.title
if (flags.file) {
body.content = fs.readFileSync(flags.file, 'utf-8')
} else if (flags.content !== undefined) {
body.content = flags.content
}
if (flags.tags !== undefined) {
body.tags = flags.tags.split(',').map(t => t.trim()).filter(Boolean)
}
if (Object.keys(body).length === 0) {
error('No updates specified. Use --title, --content, --file, or --tags.')
}
const note = await client.patch(`/api/notebooks/${encodeURIComponent(notebook)}/notes/${encodeURIComponent(noteId)}`, body)
if (flags.json) {
jsonOut(note)
return
}
log(`Updated note: ${note.id} — ${note.title}`)
}
async function deleteNote (client, cmdArgs, flags) {
const notebook = cmdArgs[0]
const noteId = cmdArgs[1]
if (!notebook || !noteId) {
error('Usage: hn note delete <notebook> <id>')
}
const result = await client.del(`/api/notebooks/${encodeURIComponent(notebook)}/notes/${encodeURIComponent(noteId)}`)
if (flags.json) {
jsonOut(result)
return
}
log(`Deleted note: ${noteId}`)
}
export default noteCommand