Skip to content

feat: standalone CLI (vj) with full command parity #205

Description

@pajoma

Summary

Add a standalone terminal command vj that provides full parity with the VS Code extension commands, works without VS Code running, and is bundled inside the .vsix.


Motivation

Power users want to interact with their journal from the terminal — scripts, shell aliases, agent-driven workflows — without opening VS Code. Example:

vj task --date 2025-08-09 "Do the dishes"
vj memo "Quick thought"
code $(vj entry)           # open today's entry in VS Code

Design

Approach: shared core library

The extension's business logic (conf.ts, inject.ts, writer.ts, paths.ts) is currently coupled to vscode.* APIs. The CLI cannot use those APIs. The fix is to extract platform-agnostic logic into src/core/ with zero vscode.* imports, then have both the extension and the CLI depend on it.

This is the bottom-up start of PLAN.md Phase 2.1 (DI refactor).

Core library — src/core/

File Responsibility
config.ts IJournalConfig interface + resolveConfig(raw) → JournalConfig
paths.ts resolveEntryPath(config, date), resolveNotePath(config, name), resolveWeekPath(config, date)
templates.ts renderTemplate(template, vars) → string
inject.ts buildTaskLine(), buildMemoLine(), injectIntoMarkdown(content, line, position)

No vscode.* imports allowed in src/core/. Enforced by ESLint or a dedicated tsconfig.

Extension adapters — existing src/ext/conf.ts, src/actions/inject.ts, src/actions/writer.ts, src/actions/reader.ts become thin wrappers: feed vscode.workspace.getConfiguration() output through resolveConfig(), use vscode.workspace.fs for I/O, delegate logic to core.

CLI — src/cli/

File Responsibility
index.ts Entry point; commander-based arg parsing
config-reader.ts Reads VS Code global settings.json (platform-aware path), extracts journal.* keys, applies JOURNAL_BASE env-var override
file-ops.ts Node.js fs read/write/create

Config resolution order:

  1. VS Code global settings (~/.config/Code/User/settings.json on Linux; platform-aware for macOS/Windows)
  2. JOURNAL_BASE env var overrides journal.base

Commands

vj entry [date]                                      # create entry if missing, print path to stdout
vj entry [date] [--header "text"] [--content "md"]   # inject ## header + raw markdown content at end of file
vj week [date]                                       # create weekly entry if missing, print path
vj note <name>                                       # create note if missing, print path
vj task [date] "text"                                # inject task line (uses task template)
vj memo [date] "text"                                # inject memo line (uses memo template)

Date arg: ISO YYYY-MM-DD, or today / yesterday / tomorrow / weekday names — same parser as the extension's MatchInput. Defaults to today when omitted.

entry, week, note print the resolved path to stdout on success, making them composable:

code $(vj entry)
cat $(vj entry yesterday)

--content accepts raw markdown, injected verbatim at end of file (no template processing).

Build

Two esbuild bundles:

Output Entry Externals
dist/extension.js src/extension.ts vscode
dist/vj.js src/cli/index.ts Node builtins only (self-contained)

bin/vj shell script (checked in):

#!/usr/bin/env node
require('../dist/vj.js')

package.json addition:

"bin": { "vj": "./bin/vj" }

Distribution (bundled in VSIX)

After vsce package, the VSIX extracts to ~/.vscode/extensions/pajoma.vscode-journal-<version>/. The bin/vj script and dist/vj.js must not be excluded by .vscodeignore.

Setup (once, after extension install):

ln -sf ~/.vscode/extensions/pajoma.vscode-journal-*/bin/vj /usr/local/bin/vj

Optionally: contribute a journal.installCLI VS Code command that creates the symlink programmatically.


Out of scope

  • Interactive TUI / prompts in CLI
  • Config wizard / vj init
  • Windows PATH auto-registration
  • Publishing CLI as separate npm package (future milestone)

Test plan

  • Unit tests for src/core/ functions (no VS Code host needed, plain npm test -- --grep)
  • Integration test: vj task today "test" writes expected line to temp dir entry file
  • Regression: existing extension tests still pass (core refactor must not break adapters)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions