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:
- VS Code global settings (
~/.config/Code/User/settings.json on Linux; platform-aware for macOS/Windows)
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)
Summary
Add a standalone terminal command
vjthat 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:
Design
Approach: shared core library
The extension's business logic (
conf.ts,inject.ts,writer.ts,paths.ts) is currently coupled tovscode.*APIs. The CLI cannot use those APIs. The fix is to extract platform-agnostic logic intosrc/core/with zerovscode.*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/config.tsIJournalConfiginterface +resolveConfig(raw) → JournalConfigpaths.tsresolveEntryPath(config, date),resolveNotePath(config, name),resolveWeekPath(config, date)templates.tsrenderTemplate(template, vars) → stringinject.tsbuildTaskLine(),buildMemoLine(),injectIntoMarkdown(content, line, position)No
vscode.*imports allowed insrc/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.tsbecome thin wrappers: feedvscode.workspace.getConfiguration()output throughresolveConfig(), usevscode.workspace.fsfor I/O, delegate logic to core.CLI —
src/cli/index.tscommander-based arg parsingconfig-reader.tssettings.json(platform-aware path), extractsjournal.*keys, appliesJOURNAL_BASEenv-var overridefile-ops.tsfsread/write/createConfig resolution order:
~/.config/Code/User/settings.jsonon Linux; platform-aware for macOS/Windows)JOURNAL_BASEenv var overridesjournal.baseCommands
Date arg: ISO
YYYY-MM-DD, ortoday/yesterday/tomorrow/ weekday names — same parser as the extension'sMatchInput. Defaults to today when omitted.entry,week,noteprint the resolved path to stdout on success, making them composable:--contentaccepts raw markdown, injected verbatim at end of file (no template processing).Build
Two esbuild bundles:
dist/extension.jssrc/extension.tsvscodedist/vj.jssrc/cli/index.tsbin/vjshell script (checked in):package.jsonaddition:Distribution (bundled in VSIX)
After
vsce package, the VSIX extracts to~/.vscode/extensions/pajoma.vscode-journal-<version>/. Thebin/vjscript anddist/vj.jsmust not be excluded by.vscodeignore.Setup (once, after extension install):
Optionally: contribute a
journal.installCLIVS Code command that creates the symlink programmatically.Out of scope
vj initTest plan
src/core/functions (no VS Code host needed, plainnpm test -- --grep)vj task today "test"writes expected line to temp dir entry file