@versionstory/vs-cli
v0.1.7
Published
Agent-first CLI wrapper for Version Story API
Readme
Version Story Command Line
Agent-first CLI for the Version Story API (/api/v1).
This tool wraps Repository, document, version/artifact, review, and notification endpoints so an AI agent can run deterministic workflows without UI dependencies.
Design Goals (for AI agents)
- Non-interactive only: every command is flag-driven.
- API endpoint wrapper commands output JSON.
- Local workspace commands (
vs init,vs status,vs read,vs commit,vs pull,vs edit,vs open) output human-readable text (legacy-style). - JSON errors for API endpoint wrapper commands, human-readable errors for local workspace commands.
- Workspace format support is split:
vs init,vs pull,vs commit, andvs opensupport.doc,.docx,.pdf,.md, and.markdownvs readandvs editare.docx-only
- No hidden prompts or wizard flows.
- One command = one API operation (or one explicit multi-step operation, like upload).
Agent Skill
For agents that need to operate this CLI consistently, use:
skills/vs/SKILL.md
The skill is adapted from the legacy CLI skill but aligned to this repo's commands and behavior (vs auth ..., local .versionstory/state.json, human-readable local workflow commands, and endpoint-wrapper JSON commands).
Install
npm install -g @versionstory/vs-cliThen use:
vs --helpFor local development from this repo:
npm install
npm run build
npm linkThen use:
vs --helpPublish
Build and inspect the package:
npm run build:full
npm pack --dry-runnpm run build:full now:
- builds native
edit-docxbinaries intobin/ - auto-bumps the package version if that version is already published on npm
- prepares platform-specific npm packages in
packages/native/ - builds the CLI into
dist/ - runs
npm packfor the main npm package
Publish the native packages first, then publish the main CLI package.
Publish to npm:
npm login
bash scripts/publish-packages.sh --otp <code>Publish through GitHub Actions:
- Add repository secret
NPM_TOKEN - Run the
Publish CLIworkflow from GitHub Actions
The workflow uses npm run build:full and then publishes the native packages plus the main CLI package with the configured token.
Install the published package:
npm install -g @versionstory/vs-cliConfiguration
Set environment and authenticate once:
vs config set-environment prod
vs auth loginEnvironment targets:
- Local:
vs config set-environment local- Dev:
vs config set-environment dev- Production:
vs config set-environment prodRuntime precedence is strict and explicit:
- CLI flags (
vs --environment <local|dev|prod> <command>) - Local config (
~/.version-story-cli/config.json) - Default environment (when none is set):
prod
vs --environment by itself is invalid. The global flag must be followed by a command, for example:
vs --environment dev repositories list
vs --environment local init --name "Acme"Data Path Rule
CLI environment selects the API host only.
Database selection is controlled by that API's DATABASE_URL, not CLI config.
Quick Preflight
Before debugging auth/data issues:
- Run
vs config showand confirm environment - Confirm web
VITE_API_URLif testing both web and CLI - Confirm
DATABASE_URLin the API process being targeted
Core Commands
Local Workspace (Git-style)
These commands use a project-local .versionstory/state.json file and working files in your current directory.
# Initialize local state and perform first sync
vs init --repository-id <repository-id> --branch-id <branch-id>
# Create a new Repository and initialize local state
vs init --name "VS CLI test"
# Reinitialize and overwrite local state
vs init --repository-id <repository-id> --branch-id <branch-id> --force
# Use local API mode (legacy-style command flag)
vs init --repository-id <repository-id> --branch-id <branch-id> --local
# After state exists, pull reuses stored repository/branch unless you override selectors
vs pull
# Compare working tree, index, and local commit queue
vs status
# Stage changes
vs add SPA.docx
# Create a local commit from staged changes
vs commit -m "Update SPA"
# Upload local commits to the current branch
vs push
# Show local and remote branch history
vs log
# Read tracked document from cached preprocessed working state (.docx only)
vs read NDA.docx
# Show only changed lines with one line of context
vs read NDA.docx --changes --context 1
# Search document text and return matching paragraph IDs/snippets (.docx only)
vs read NDA.docx --search "Acme, Inc."
# Return only matching paragraph IDs
vs read NDA.docx --search "Acme, Inc." --return-ids
# Print document structure IDs for tables, rows, cells, and paragraphs
vs read NDA.docx --return-ids
# Note: redline mode is not supported in preprocessed working-state reads
# vs read NDA.docx --redline
# Create and list branches
vs branch "NDA updates"
vs branch
# Switch local workspace state to another branch
vs switch "NDA updates"
# Apply edits (with target paragraph IDs), then create a local commit automatically
vs edit NDA.docx --edits '[...]' --chat-context "request context" -m "Update clause"
# Validate target IDs without changing files
vs edit NDA.docx --edits '[...]' --chat-context "request context" -m "Update clause" --validate
# Open the current repository workspace in browser
vs open
# Open using explicit local web mode
vs open --local
# Register the browser-to-CLI protocol on macOS
vs protocol register
# Print a versionstory:// URL for a tracked DOCX
vs protocol print-open-word-url --relative-path NDA.docx
# Open a tracked DOCX locally and start a short-lived watch session
vs protocol open-word --relative-path NDA.docx
# Check whether the watched DOCX changed on disk
vs protocol status --relative-path NDA.docxvs status reports staged changes, unstaged changes, untracked files, and unpushed local commits.
Legacy-style output examples:
$ vs status
On repository <repository-name> (branch <branch-name>)
Changes to be committed:
(use "vs commit -m <message>" to create a local commit)
modify: NDA.docx
1 staged, 0 unstaged, 0 untracked, 0 unpushed$ vs commit -m "Update NDA"
✓ Created local commit 1234abcd Update NDA$ vs push
Pushing 1234abcd Update NDA
✓ NDA.docx
✓ Pushed 1 local commit$ vs pull
Pulling latest versions...
✓ NDA.docx
✓ SPA.docx
✓ Pulled 2 documents$ vs edit NDA.docx --edits '[...]' --chat-context "..." -m "Update NDA"
Applying edits...
Accepting track changes for clean commit version...
Creating local commit...
✓ Created local commit 1234abcd
Pushing commit...
✓ Edit complete. Edit set ID: <uuid>
✓ Pushed commit 1234abcd (#12)$ vs read NDA.docx --changes --context 1
--- Change 1 of 2 [2b48b62c3ef64df090e10b0741b91ffe] ---
...
... {.deletion}
... {.insertion} <- CHANGE
...$ vs open --local
Opening: http://localhost:5173/#section=repositories&screen=workspace&view=documents&repository=<repository-id>&branch=<branch-id>$ vs protocol open-word --relative-path NDA.docx
Opened NDA.docx and started local watch session.
Status: vs protocol status --workspace "/path/to/repo" --relative-path "NDA.docx"Environment convention:
vs --environment local <command>selects the local API environment for commands that use the global runtime context--localonvs init,vs pull,vs push,vs log,vs branch,vs switch,vs editis a legacy shortcut that also targets the local API athttp://localhost:3001--localonvs openuses local web athttp://localhost:5173vs init,vs pull, andvs switchlink the current workspace directory to the current repository branch in local CLI config.- The web app uses that local link to invoke
versionstory://open-word?...for tracked.docand.docxfiles without requiringvs open. - After Word saves are detected, the web app can pull the changed local file through the localhost bridge, upload it as a new version, and refresh the redline viewer.
- Without
--local,vs openmaps:https://api.development.versionstory.com->https://web.development.versionstory.comhttps://api.versionstory.com->https://web.versionstory.com- local API hosts (
localhost,127.0.0.1,api-local.versionstory.com) ->http://localhost:5173
Local browser-to-CLI integration:
vs protocol registerinstalls a macOSversionstory://handler backed by this CLI package.vs protocol open-word --relative-path <path>opens a tracked.docor.docxfile in the default local app and starts a short-lived localhost watch session on127.0.0.1:32107.vs protocol status --relative-path <path>reports whether the watched file has changed on disk since it was opened.vs protocol print-open-word-url --relative-path <path>prints theversionstory://open-word?...URL that the web app can invoke.- The bridge exits after an idle timeout; it is not intended to be a permanent daemon.
vs edit runs a local native binary in this repo:
bin/osx-arm64/edit-docxbin/osx-x64/edit-docxbin/linux-x64/edit-docxbin/win-x64/edit-docx.exe
When installed from npm, vs edit resolves the matching binary from the installed platform package at runtime.
If those binaries are missing, vs edit falls back to:
dotnet run --project apps/edit-docx/edit-docx.csproj -- ...
Auth
vs auth login
vs auth me
vs auth switch-organization --organization-id <org-id>vs auth login opens the Version Story web app sign-in page, where you can use email/password or any configured OAuth provider, and stores the returned token in local CLI config.
Repository and Tree
vs repositories list
vs repositories create --name "Our Repository"
vs repositories branches --repository-id <repository-id>
vs repositories create-branch --repository-id <repository-id> --name "NDA updates"
vs repositories merge-branch --repository-id <repository-id> --branch-id <branch-id>
vs repositories close-branch --repository-id <repository-id> --branch-id <branch-id>
vs repositories delete-branch --repository-id <repository-id> --branch-id <branch-id>
vs repositories tree --repository-id <repository-id> --branch-id <branch-id>
vs repositories create-document --repository-id <repository-id> --name "NDA.docx" --path "NDAs"Document Versions (Upversion)
Upload flow is fully wrapped:
uploads/start- signed
PUTto S3 uploads/complete
vs versions upload --document-id <doc-id> --branch-id <branch-id> --file ./NDA.pdf
vs versions upversion --document-id <doc-id> --branch-id <branch-id> --file ./NDA.pdfArtifact / Redline Download
Get signed URL only:
vs versions download --version-id <version-id> --variant redline --format htmlDownload to file:
vs versions download \
--version-id <version-id> \
--variant redline \
--format changed-pages-pdf \
--output ./nda-redline-cpor.pdfSupported options:
--variant clean|redline--format original|pdf|docx|html|txt|xml|preprocessed-docx|changed-pages-pdf
Notes:
changed-pages-pdfis redline-only.- Clean
docxis not a separate derived endpoint; use--format original.
Reviews
vs reviews list --repository-id <repository-id>
vs reviews create \
--repository-id <repository-id> \
--branch-id <branch-id> \
--title "Review NDA updates" \
--description "Please check section 2" \
--reviewer-user-id <user-1> \
--reviewer-user-id <user-2>
vs reviews comments --repository-id <repository-id> --review-id <review-id>
vs reviews comment-create \
--repository-id <repository-id> \
--review-id <review-id> \
--document-version-id <version-id> \
--paragraph-anchor-id <anchor-id> \
--paragraph-text "selected text" \
--body "needs update" \
--comment-type issueGeneric Request Escape Hatch
vs request GET /api/v1/repositories --query organizationId=<id>
vs request POST /api/v1/repositories/<repository-id>/reviews --body-file ./review-create.jsonLocal State System
The CLI stores local sync metadata at:
.versionstory/state.json
It stores only tracked working-file state (not full version history caches).
State shape:
{
"repositoryId": "uuid",
"repositoryName": "VS CLI test",
"branchId": "uuid",
"branchName": "main",
"files": [
{
"documentId": "uuid",
"documentName": "NDA.docx",
"relativePath": "NDAs/NDA.docx",
"versionId": "uuid",
"sha256": "hex",
"preprocessedRelativePath": ".versionstory/preprocessed/NDAs/NDA.docx",
"preprocessedSourceSha256": "hex",
"downloadedAt": "2026-03-02T14:00:00.000Z",
"lastCommittedAt": "2026-03-02T14:15:00.000Z"
}
],
"initializedAt": "2026-03-02T14:00:00.000Z",
"lastPulledAt": "2026-03-02T14:00:00.000Z"
}Behavior:
vs init:- with
--repository-*: creates.versionstory/state.jsonfor a selected repository/branch and performs initial pull.
- with
- with
--name: creates a new Repository, uploads local.doc/.docx/.pdf/.md/.markdownworking files as new documents/versions directly tomain, then initializes.versionstory/state.json. vs pull: downloads latest versions for documents in the selected branch, overwrites tracked working files, refreshes.versionstory/preprocessed/...cache copies for DOCX-derived files, removes stale previously-tracked files, and rewritesstate.json.vs status: reports the current branch plus staged changes, unstaged changes, untracked files, and unpushed local commits.vs read:.docxonly. Always reads from cached preprocessed working state. If the tracked working file changed locally, it preprocesses the working file first and refreshes the cached preprocessed state before reading.vs read --search:.docxonly. Searches paragraph text in cached preprocessed working state and returns matching paragraph IDs/snippets.vs read --return-ids:.docxonly. Without--search, prints document structure IDs for tables, table rows, table cells, and paragraphs from cached preprocessed working state.vs add: stages modified tracked files, tracked deletions, and untracked supported files into the local index.vs commit: creates a local commit from the staged index and updates the local workspace head instate.jsonwithout network I/O.vs push: uploads unpushed local commits in order. Normal tracked-file commits use the repository-level commit endpoints.vs editcommits with a redline artifact use the single-document finalize path during push.vs log: shows local unpushed commits first, then remote commits for the current branch.vs branch: lists branches or creates a new branch.vs switch: switches the workspace to another branch and performs a pull, but only when the workspace is clean.vs edit:.docxonly. If the tracked working file changed locally, refreshes preprocessed cache and exits with instructions to re-runvs readfor fresh UUIDs. Otherwise it validates target IDs, applies tracked edits with the legacyedit-docxtool, accepts revisions into a clean file, stages the result plus redline artifact, creates one local commit automatically, and pushes that commit immediately.
Agent Workflow Example
# 1) Login
vs auth login
# 2) Find Repository and branch
vs repositories list
vs repositories branches --repository-id <repository-id>
# 3) Upload a new version
vs versions upload --document-id <doc-id> --branch-id <branch-id> --file ./contract-v2.pdf
# 4) Poll conversion operations
vs versions operations --version-id <new-version-id>
# 5) Download redline HTML
vs versions download --version-id <new-version-id> --variant redline --format html --output ./redline.htmlExit Codes
0success1any API/client error
Development
npm run dev -- --help
npm run build
npm run build:native
npm run build:all
npm run build:full
bash scripts/build.sh --nativeLegacy-style distribution packaging:
scripts/build.sh --nativenow:
- builds native
edit-docxbinaries for all target platforms - prepares platform-specific npm packages in
packages/native/<platform>/ - builds the CLI TypeScript bundle
- runs
npm packto produce the npm package
Native edit-docx Build
Source is now in this repo at:
apps/edit-docx/Program.csapps/edit-docx/edit-docx.csproj
Native build script:
scripts/build-native.sh
What it does:
- Runs
dotnet publishforosx-arm64,osx-x64,linux-x64,win-x64 - Uses self-contained single-file publish output
- Writes executables to
bin/<platform>/edit-docx(or.exeon Windows) scripts/package-native-packages.shprepares platform-specific npm packages for distribution
Compatibility note:
apps/edit-docx/edit-docx.csprojresolvesOpenXmlPowerTools.csprojfromVSOXT_PATH.- Set
VSOXT_PATHto your localversion-story-open-xml-toolscheckout before runningnpm run build:native.
