/**
* hn notebook — Notebook management commands
*/
import { HnClient } from '../client.js'
import { table, log, error, jsonOut } from '../output.js'
export async function notebookCommand (subcommand, cmdArgs, flags) {
const client = new HnClient(flags)
switch (subcommand) {
case 'list':
case undefined:
return await listNotebooks(client, flags)
case 'create':
return await createNotebook(client, cmdArgs, flags)
case 'info':
return await infoNotebook(client, cmdArgs, flags)
case 'rename':
return await renameNotebook(client, cmdArgs, flags)
case 'delete':
return await deleteNotebook(client, cmdArgs, flags)
default:
error(`Unknown notebook subcommand: ${subcommand}\nUsage: hn notebook [list|create|info|rename|delete]`)
}
}
async function listNotebooks (client, flags) {
const notebooks = await client.get('/api/notebooks')
if (flags.json) {
jsonOut(notebooks)
return
}
if (notebooks.length === 0) {
log('No notebooks found.')
return
}
table(notebooks, [
{ key: 'slug', label: 'SLUG', width: 20 },
{ key: 'name', label: 'NAME', width: 30 },
{ key: 'noteCount', label: 'NOTES', width: 6 },
{ key: 'description', label: 'DESCRIPTION', width: 40 }
])
}
async function createNotebook (client, cmdArgs, flags) {
const slug = cmdArgs[0]
if (!slug) {
error('Usage: hn notebook create <slug> [--name "..."] [--description "..."] [--encrypted --password "..."]')
}
const body = { slug }
if (flags.name) body.name = flags.name
if (flags.description) body.description = flags.description
if (flags.encrypted) {
body.encrypted = true
if (flags.password) {
body.password = flags.password
} else {
// Prompt for password
let securePrompt
try {
securePrompt = (await import('secure-prompt')).default
} catch {
error('secure-prompt not available for encryption. Use --password flag or install secure-prompt')
}
const password = await securePrompt('Encryption password: ')
if (!password || password.length === 0) {
error('Password cannot be empty')
}
const confirm = await securePrompt('Confirm password: ')
if (password.toString() !== confirm.toString()) {
error('Passwords do not match')
}
body.password = password.toString()
}
}
const notebook = await client.post('/api/notebooks', body)
if (flags.json) {
jsonOut(notebook)
return
}
const encLabel = notebook.encrypted ? ' (encrypted)' : ''
log(`Created notebook: ${notebook.slug} (${notebook.name})${encLabel}`)
}
async function infoNotebook (client, cmdArgs, flags) {
const slug = cmdArgs[0]
if (!slug) {
error('Usage: hn notebook info <slug>')
}
const notebook = await client.get(`/api/notebooks/${encodeURIComponent(slug)}`)
if (flags.json) {
jsonOut(notebook)
return
}
log(`Slug: ${notebook.slug}`)
log(`Name: ${notebook.name}`)
log(`Description: ${notebook.description || '(none)'}`)
log(`Encrypted: ${notebook.encrypted ? 'yes' : 'no'}`)
log(`Notes: ${notebook.noteCount}`)
log(`Created: ${new Date(notebook.createdAt).toLocaleString()}`)
log(`Updated: ${new Date(notebook.updatedAt).toLocaleString()}`)
}
async function renameNotebook (client, cmdArgs, flags) {
const oldSlug = cmdArgs[0]
const newSlug = cmdArgs[1]
if (!oldSlug || !newSlug) {
error('Usage: hn notebook rename <old-slug> <new-slug>')
}
const notebook = await client.post(`/api/notebooks/${encodeURIComponent(oldSlug)}/rename`, { slug: newSlug })
if (flags.json) {
jsonOut(notebook)
return
}
log(`Renamed: ${oldSlug} → ${notebook.slug}`)
}
async function deleteNotebook (client, cmdArgs, flags) {
const slug = cmdArgs[0]
if (!slug) {
error('Usage: hn notebook delete <slug> [--force]')
}
if (!flags.force) {
log(`This will delete notebook '${slug}' and ALL its notes.`)
log('Use --force to confirm.')
return
}
const result = await client.del(`/api/notebooks/${encodeURIComponent(slug)}`)
if (flags.json) {
jsonOut(result)
return
}
log(`Deleted notebook: ${slug}`)
}
export default notebookCommand