(argv: string[])
| 53 | const NO_VAULT_COMMANDS = new Set(['help', '--help', '-h', '--version', 'mcp', 'open']) |
| 54 | |
| 55 | async function main(argv: string[]): Promise<number> { |
| 56 | if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h' || argv[0] === 'help') { |
| 57 | process.stdout.write(renderHelp()) |
| 58 | return 0 |
| 59 | } |
| 60 | if (argv[0] === '--version') { |
| 61 | process.stdout.write(renderVersion()) |
| 62 | return 0 |
| 63 | } |
| 64 | |
| 65 | const [command, ...rest] = argv |
| 66 | |
| 67 | // Some commands have a second-level subcommand (`zn folder list`, |
| 68 | // `zn task toggle`, `zn tag find`, `zn search-title`). We resolve |
| 69 | // the subcommand before parsing flags so positionals don't include it. |
| 70 | const { subcommand, parsed } = peelSubcommand(command, rest) |
| 71 | |
| 72 | if (command === 'mcp') { |
| 73 | await cmdMcp() |
| 74 | return 0 |
| 75 | } |
| 76 | |
| 77 | const key = subcommand ? `${command} ${subcommand}` : command |
| 78 | |
| 79 | // Every other command needs the vault root. Resolving here lets us |
| 80 | // emit a single, clean error if the user hasn't configured one. |
| 81 | // `--vault <name|path>` selects among the app's known vaults and |
| 82 | // always wins (an invalid selector errors loudly, never falls back). |
| 83 | // `open` is special: it works without a vault (arbitrary markdown |
| 84 | // files), but uses one when available so the vault-relative paths |
| 85 | // `zn list` prints open from any directory. `vault list` enumerates |
| 86 | // vaults, so it must work before any vault is configured. |
| 87 | const vaultSelector = getString(parsed, 'vault') |
| 88 | let vault = '' |
| 89 | if (vaultSelector) { |
| 90 | vault = await resolveVaultRoot(vaultSelector) |
| 91 | } else if (command === 'open') { |
| 92 | vault = await resolveVaultRoot().catch(() => '') |
| 93 | } else if (!NO_VAULT_COMMANDS.has(command) && key !== 'vault list') { |
| 94 | vault = await resolveVaultRoot() |
| 95 | } |
| 96 | const dispatch: Record<string, (v: string, args: ParsedArgs) => Promise<void>> = { |
| 97 | list: cmdList, |
| 98 | read: cmdRead, |
| 99 | create: cmdCreate, |
| 100 | write: cmdWrite, |
| 101 | append: cmdAppend, |
| 102 | prepend: cmdPrepend, |
| 103 | rename: cmdRename, |
| 104 | move: cmdMove, |
| 105 | archive: cmdArchive, |
| 106 | unarchive: cmdUnarchive, |
| 107 | trash: cmdTrash, |
| 108 | restore: cmdRestore, |
| 109 | delete: cmdDelete, |
| 110 | duplicate: cmdDuplicate, |
| 111 | search: cmdSearch, |
| 112 | 'search-title': cmdSearchTitle, |
no test coverage detected