Installation
MarkSpec ships as a single self-contained binary. There are three ways to get started, ordered by the experience they give you:
VS Code extension (recommended)
The MarkSpec extension (driftsys.markspec-ide) bundles the language server
directly. Install it and you get real-time diagnostics, completions, and
go-to-definition with zero extra configuration.
Install from the marketplace:
- Open VS Code.
- Open the Extensions panel (
Ctrl+Shift+X/Cmd+Shift+X). - Search for MarkSpec.
- Click Install on the
driftsys.markspec-ideextension.
The extension prompts you to install or locate the markspec binary the first
time it activates. Click Download to let it fetch the platform binary
automatically, or point it at an existing install.
See VS Code extension for the full feature list, settings reference, and troubleshooting.
Binary install
A self-contained binary without any runtime requirement.
macOS / Linux (install script)
curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
The script:
- Detects your platform and architecture.
- Downloads the release binary from GitHub Releases.
- Verifies the SHA256 checksum.
- Places the binary in
~/.local/bin.
Add ~/.local/bin to your PATH if it is not already there:
# bash / zsh
export PATH="$HOME/.local/bin:$PATH"
Manual download
Pre-built binaries are attached to every GitHub Release. Download the archive
for your platform, extract the markspec binary, and place it anywhere on your
PATH.
| Platform | File |
|---|---|
| macOS (Apple) | markspec-macos-aarch64.tar.gz |
| macOS (Intel) | markspec-macos-x86_64.tar.gz |
| Linux (x86_64) | markspec-linux-x86_64.tar.gz |
| Linux (aarch64) | markspec-linux-aarch64.tar.gz |
| Windows (x86_64) | markspec-windows-x86_64.zip |
Deno (from source)
If you already use Deno and prefer running from source:
deno install -g jsr:@driftsys/markspec
Binary is placed in ~/.deno/bin. Run without installing:
deno run jsr:@driftsys/markspec --help
Verify
markspec --version
# markspec 0.5.0 (core-schema 1)
AI assistant skillset (upskill)
MarkSpec ships a Claude Code skillset that teaches AI assistants the MarkSpec authoring conventions — entry block syntax, EARS patterns, the agent write loop, and traceability review.
Install it with upskill:
upskill add markspec:markspec-core.bundle.md
This registers the following skills in your project’s .claude/plugins/:
| Skill | Purpose |
|---|---|
markspec-entry-authoring | Entry block syntax, shapes, attributes |
markspec-core-rules | Validation rules and diagnostic codes |
markspec-write-loop | The insert → format → validate agent loop |
markspec-gherkin | GWT / Gherkin pattern for test entries |
markspec-traceability-review | Cross-file link review agent |
markspec-profile-bundle-authoring | Writing and publishing profile manifests |
See AI agents and skillset for MCP server setup and how to use the skills from Claude Code or Claude Desktop.
Quickstart
Get MarkSpec running in 15 minutes. This chapter walks a single linear path from install to a rendered HTML book with entry blocks and a traceability link.
Step 1 — Install (2 min)
Pre-compiled binary (recommended)
Downloads a self-contained binary — no Deno or Node.js required at runtime:
curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
The installer detects your platform (macOS or Linux), downloads the release
binary, verifies the SHA256 checksum, and places it in ~/.local/bin. Add that
directory to your PATH if it is not already there.
Deno runtime (for Deno developers)
If you already have Deno installed and want to run MarkSpec from source:
deno install -g jsr:@driftsys/markspec
If markspec is not found after install, add ~/.deno/bin to your PATH.
Run without installing:
deno run jsr:@driftsys/markspec --help
Verify the install
markspec --version
Step 2 — Create a project (1 min)
Coming soon:
markspec initA future release will scaffold a project with a single command. For now, create the files by hand — it takes 30 seconds.
Create a project directory and two configuration files:
mkdir my-project && cd my-project
Create project.yaml:
name: my-project
version: "1.0.0"
Create .markspec.yaml (activates the default profile):
profiles: []
That is enough to start. Both files are picked up automatically when you run any
MarkSpec command from inside my-project/.
Step 3 — Author three entries (5 min)
Create requirements.md with a stakeholder requirement, a software requirement,
and a traceability link connecting them:
# Requirements
- [STK_PRJ_0001] System availability
The system shall be available 99.9% of the time.
- [SRS_PRJ_0001] Health check endpoint
The service shall expose a `/health` endpoint returning `200 OK`.
Satisfies: STK_PRJ_0001
An entry is a Markdown list item where:
- The display ID in
[…]identifies the entry (STK_PRJ_0001). - The rest of the list-item text is the title (
System availability). - The indented paragraph underneath is the body.
- An indented code block at the end holds attributes (
Satisfies:).
Step 4 — Format: stamp ULIDs (2 min)
Run markspec format to assign a permanent ULID to each entry:
markspec format requirements.md
info: requirements.md:3 assigned Id: 01KPVVC9J2B1ZA64QZEMHF02PW to STK_PRJ_0001
info: requirements.md:9 assigned Id: 01KPVVC9J2B1ZA64QZEMHF02PX to SRS_PRJ_0001
2 file(s) formatted, 0 unchanged (2 total)
Your file now has Id: attributes:
- [STK_PRJ_0001] System availability
The system shall be available 99.9% of the time.
Id: 01KPVVC9J2B1ZA64QZEMHF02PW
- [SRS_PRJ_0001] Health check endpoint
The service shall expose a `/health` endpoint returning `200 OK`.
Id: 01KPVVC9J2B1ZA64QZEMHF02PX
Satisfies: STK_PRJ_0001
The ULID is a universally unique, immutable identifier. Once assigned it never changes — even if the display ID or title is renamed.
Step 5 — Validate: check traceability (2 min)
Run markspec validate to confirm the link is intact:
markspec validate requirements.md
No output means validation passed (exit code 0).
Now break a reference to see a failure:
# temporarily change "Satisfies: STK_PRJ_0001" → "Satisfies: STK_PRJ_9999"
markspec validate requirements.md
error[MSL-R080]: requirements.md:13 unresolved reference: STK_PRJ_9999
Restore the correct reference before continuing.
Step 6 — Build: render an HTML book (2 min)
Create a minimal SUMMARY.md:
# Summary
- [Requirements](requirements.md)
Build the site:
markspec book build
wrote _site/requirements.html
wrote _site/index.html
Step 7 — View the output (1 min)
Open _site/index.html in a browser. The entry blocks render with type-based
styling, display IDs, and clickable traceability links.
Editor integration
The MarkSpec VS Code extension (editors/vscode/ in the repository) provides
real-time diagnostics, entry block completion, and go-to-definition — all backed
by the same binary you just installed. See
CLI guide — Editor and LSP integration for
setup instructions covering VS Code, Neovim, and other editors.
Next steps
| I want to… | Go to |
|---|---|
| Understand entries, shapes, and types | Concepts |
| See all CLI flags and subcommands | CLI guide |
| Set up a compliance profile | Profile guide |
| Browse answers to common questions | FAQ |
Concepts
Content coming in a future release.
This chapter will cover the MarkSpec mental model: entries, shapes (Authored and Reference), types, traceability links, profiles, and listing directives. It is the skimmable reference all three audiences (architect, developer, compliance lead) read before diving into their audience-specific chapter.
This chapter will explain:
- Entries — the fundamental unit: a display ID, a title, a body, and a trailer block of attributes.
- Shapes — Authored entries (identified by a ULID stamped by
markspec format) versus Reference entries (citations to external artefacts such as standards or upstream requirements). - Types — the vocabulary layer declared by profiles; each type carries a display-ID pattern, allowed attributes, and traceability rules.
- Traceability links — directed edges in the requirement graph, created by
trace attributes (
Satisfies:,Derived-from:,Verified-by:, etc.). - Profiles — layered manifests that declare type vocabulary, attribute rules, and display-ID patterns for a project or compliance standard.
- Listing directives —
glossary,components, andreferencesdirectives that generate structured listing documents from the entry graph.
Until this chapter ships, the canonical source is docs/spec/internal/markspec-core-data-model.md (§1 — the two-layer model and type system).
Authoring guide
Content coming in a future release.
This chapter is the primary resource for architects who structure a project’s requirements and architecture documentation. Its content depends on listing-directive support and profile-schema implementation, which are not yet shipped on
main.
This chapter will cover:
- Project layout — recommended directory structure for multi-domain requirements documents.
- Entry authoring workflow — write, format, validate, review; the canonical
insert → format → validateloop for AI agents and human authors. - Traceability graph design — how to structure
Satisfies:chains across stakeholder requirements, software requirements, and test entries. - Listing documents — authoring
glossary.md,components.md, andreferences.mdusing the listing directives. - Profile-driven type vocabulary — declaring and using project-specific entry types.
- In-code specs — embedding MarkSpec entry blocks in Rust
///and Kotlin/**doc comments; the V-model colocated test convention.
Until this chapter ships, refer to AGENTS.md (§Test conventions — V-model) for the in-code spec pattern, and the CLI guide for the authoring commands.
Profile guide
Full profile guide coming in a future release.
This chapter is the primary resource for compliance leads who stack profiles to map a project to ASPICE, ISO 26262, or other standards. Its detailed content depends on the profile-schema implementation, which is in progress.
This chapter will cover profile stacking, ASPICE mapping, and ISO 26262 alignment. The current section documents how to bind a profile to a project — the configuration that is already shipped.
.markspec.yaml
The .markspec.yaml file binds a project to one or more profile manifests. It
sits next to project.yaml in the project root.
Example
profiles:
- ./profiles/markspec.yaml
Profile specifiers
Three formats are supported:
Local path — relative to .markspec.yaml:
profiles:
- ./profiles/markspec.yaml
- ../shared/markspec.yaml
Git HTTPS — cloned and cached locally:
profiles:
- git+https://github.com/acme/compliance-profiles.git#v1.0.0
Git HTTPS with subpath — for monorepos:
profiles:
- git+https://github.com/acme/profiles.git/aspice#v2.0.0
Git file — for local development:
profiles:
- git+file:///home/user/profiles.git#main
Only one profile is supported in the current implementation. If multiple profiles are listed, a
PROFILE-LOAD-006warning is emitted and only the first is used.
Profile manifests
A profile manifest (markspec.yaml) declares the vocabulary for a project:
entry types, attributes, traceability rules, and display-ID patterns.
Profiles can extend other profiles via extends:, forming a chain. Child
profiles can add types and attributes, or tighten constraints (e.g., narrowing
cardinality, adding required: true) — but cannot relax them.
Use markspec profile show to inspect the active profile chain and
markspec doctor for a project health check.
Scaffold a new profile with:
markspec profile new my-profile
This creates a my-profile/ directory with a markspec.yaml manifest and a
README.md.
Coming in a future release
- ASPICE / ISO 26262 mapping — how profile types correspond to process work products and safety integrity levels.
- Profile stacking — composing a project profile on top of a compliance base profile.
- Type vocabulary reference — all built-in types, their allowed attributes, and their traceability constraints.
- Publishing profiles — validating a profile for distribution with
markspec profile publish.
CLI guide
Reference for project.yaml, every subcommand, and editor / LSP integration.
MarkSpec follows the Command Line Interface Guidelines.
Every command supports --help. Commands that produce structured output support
--format json for machine-readable output to stdout (diagnostics always go to
stderr).
Exit codes: 0 success, 1 error, 2 warnings only (validate).
Global options (available on every command):
| Flag | Description |
|---|---|
-h, --help | Show help |
-V, --version | Show version |
-q, --quiet | Suppress non-error output |
Project configuration
project.yaml
Every MarkSpec project requires a project.yaml in the project root. MarkSpec
discovers it by walking up from the current directory.
Minimal example
name: my-project
version: "1.0.0"
Complete example
name: io.acme.braking-system
version: "2.3.0"
labels:
- ASIL-A
- ASIL-B
- ASIL-C
- ASIL-D
parents:
- https://acme.com/refhub
parent-fallback: https://driftsys.github.io/refhub
Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Project name. Reverse-DNS convention recommended. |
version | string | yes | "0.0.0" | Project version. Quote in YAML to avoid number coercion. |
labels | string[] | no | [] | Allowed label vocabulary. Empty means no constraint. |
parents | string[] | no | [] | Upstream parent registry URLs, searched in order. |
parent-fallback | string | no | https://driftsys.github.io/refhub | Fallback registry when parents don’t resolve a reference. |
Directory conventions
MarkSpec does not enforce a directory layout. By convention:
docs/— Markdown files containing requirements and design documentationsrc/— source code with doc-comment entries (Rust///, Kotlin/**)project.yaml— project root marker
The compile and report commands accept explicit paths or globs:
markspec compile "docs/**/*.md"
markspec compile docs/requirements.md src/main.rs
Profile configuration (.markspec.yaml and profile manifests) is covered in the
Profile guide.
Commands
Authoring
format
Stamp ULIDs, fix indentation, normalize attributes.
markspec format <file...>
markspec format --check <file...>
| Flag | Type | Default | Description |
|---|---|---|---|
--check | bool | false | Report changes without writing. Exit 1 if changes needed |
Examples:
# Format a single file (writes changes in place)
markspec format docs/requirements.md
# Format multiple files
markspec format docs/*.md
# Check mode for CI — reports but doesn't modify
markspec format --check docs/*.md
validate
Check broken refs, missing Ids, malformed entries, duplicates.
markspec validate <file...>
| Flag | Type | Default | Description |
|---|---|---|---|
--strict | bool | false | Promote warnings to errors |
--format | string | text | Output format: json, text |
Examples:
# Validate a file
markspec validate docs/requirements.md
# Strict mode — warnings become errors (useful for CI)
markspec validate --strict docs/requirements.md
# JSON output for tool integration
markspec validate --format json docs/*.md
Querying
show
Show details of a single entry by display ID or ULID.
markspec show <id> <paths...>
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
Examples:
markspec show STK_PRJ_0001 "docs/**/*.md"
markspec show --format json STK_PRJ_0001 docs/requirements.md
context
Walk the Satisfies chain upward from an entry to see what it ultimately satisfies.
markspec context <id> <paths...>
| Flag | Type | Default | Description |
|---|---|---|---|
--depth | number | 10 | Maximum depth to walk |
--format | string | text | Output format: json, text |
Examples:
markspec context SRS_PRJ_0001 "docs/**/*.md"
markspec context --depth 3 SRS_PRJ_0001 docs/requirements.md
dependents
List all entries that depend on (satisfy) a given entry.
markspec dependents <id> <paths...>
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
Examples:
markspec dependents STK_PRJ_0001 "docs/**/*.md"
Building
compile
Parse files, build traceability graph, output compiled result.
markspec compile <paths...>
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
Examples:
# Summary output
markspec compile "docs/**/*.md"
# Full JSON output for downstream tools
markspec compile --format json "docs/**/*.md" > compiled.json
report
Generate a traceability matrix or coverage report.
markspec report <kind> <paths...>
kind is one of: traceability, coverage.
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | md | Output format: md, json, csv |
--scope | string | — | Filter by domain abbreviation |
--label | string | — | Filter by label value |
--output | string | — | Write to file instead of stdout |
Examples:
# Traceability matrix in Markdown
markspec report traceability "docs/**/*.md"
# Coverage report as CSV
markspec report coverage --format csv "docs/**/*.md"
# Write to file
markspec report traceability --output matrix.md "docs/**/*.md"
# Filter by label
markspec report traceability --label ASIL-B "docs/**/*.md"
export
Emit the compiled traceability graph in a portable format.
markspec export <format> <paths...>
Supported formats: json, yaml, csv.
Examples:
# JSON export
markspec export json "docs/**/*.md" > compiled.json
# CSV — one row per entry with display ID, title, type, shape, id, file, line
markspec export csv "docs/**/*.md" > entries.csv
# YAML
markspec export yaml "docs/**/*.md"
insert
Append a scaffolded entry block to a file. This is the agent write path — use it from scripts or AI agents rather than hand-authoring new blocks.
markspec insert <type> <file>
| Flag | Type | Default | Description |
|---|---|---|---|
--print | bool | false | Echo the inserted block to stdout as well |
The command:
- Finds the highest existing display ID for
<type>in<file>. - Computes the next sequential display ID.
- Generates a fresh ULID.
- Appends the block with a blank separator.
Example:
markspec insert requirement docs/requirements.md --print
# → appends SRS_PRJ_0003, prints block to stdout
Follow with markspec format to normalize indentation and markspec validate
to confirm no broken references.
create
Print a scaffolded entry block without writing it to any file.
markspec create <type> <paths...>
Example:
markspec create requirement "docs/**/*.md"
next-id
Print the next available display ID for a profile-declared type without creating an entry.
markspec next-id <type> <paths...>
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
Example:
markspec next-id requirement "docs/**/*.md"
# → SRS_PRJ_0004
markspec next-id requirement "docs/**/*.md" --format json
# → {"type":"requirement","displayId":"SRS_PRJ_0004"}
lint
Run prose-quality analysis on entries (INCOSE lexicon, modal keywords,
structural checks). Returns MSL-Q and MSL-M codes.
markspec lint <paths...>
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
--strict | bool | false | Promote warnings to errors |
Lint does not run as part of validate or the pre-commit hook. It is a
review-time quality gate.
Examples:
markspec lint docs/requirements.md
markspec lint --strict "docs/**/*.md"
markspec lint --format json "docs/**/*.md"
hook
Run format --check and validate on a list of files. Designed as a pre-commit
hook entry point.
markspec hook [files...]
Exits 0 when no files are given (nothing staged). Exits 1 if any file needs
formatting or has validation errors.
See Pre-commit hook for setup instructions.
Documents
doc build
Generate a single-document PDF via Typst.
markspec doc build <file>
| Flag | Type | Default | Description |
|---|---|---|---|
-o, --output | string | <file>.pdf | Output file path |
Examples:
markspec doc build docs/spec.md
markspec doc build -o output/spec.pdf docs/spec.md
Books
book build
Generate a multi-chapter static HTML site from a SUMMARY.md.
markspec book build
| Flag | Type | Default | Description |
|---|---|---|---|
-o, --output | string | _site | Output directory |
-s, --summary | string | SUMMARY.md | SUMMARY.md path |
Examples:
markspec book build
markspec book build -o dist -s docs/SUMMARY.md
Profile and diagnostics
profile show
Show the active profile chain and effective configuration.
markspec profile show
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
profile new
Scaffold a new profile directory with a starter manifest.
markspec profile new <id>
<id> must be lowercase alphanumeric with hyphens, optionally scoped
(@org/name).
markspec profile new my-profile
markspec profile new @acme/iso26262
Creates <id>/markspec.yaml and <id>/README.md.
profile add
Add a profile to the project’s .markspec.yaml.
markspec profile add <spec>
<spec> is a local path or a scoped package name.
markspec profile add ./profiles/my-profile
markspec profile add @driftsys/iso26262
profile publish
Validate a profile manifest for publishability and print any issues.
markspec profile publish [--dir <dir>] [--format json|text]
Checks required fields (id, version), warns about missing description and
license, and reports any manifest validation errors. Exits 0 when the
manifest is publish-ready.
profile describe
Show full details for a profile element (type, attribute, relation, label, or convention).
markspec profile describe <kind> <name>
kind is one of: type, attribute, relation, label, convention.
markspec profile describe type requirement
markspec profile describe attribute ASIL
markspec profile describe relation Satisfies
doctor
Project health check: verifies project.yaml, profile configuration, and
project structure.
markspec doctor
| Flag | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: json, text |
AI agent integration
mcp
Start the MarkSpec MCP server. Communicates over stdio JSON-RPC. Exposes the active project as MCP resources and tools to any MCP-capable AI client (Claude Code, Claude Desktop, GitHub Copilot in VS Code, OpenCode).
markspec mcp
Resources
markspec://profile— distilled profile manifest (types, attributes, link kinds, labels).markspec://entries— index of all project entries, grouped by type.markspec://entry/{displayId}— one entry per resource, with attributes, body, outgoing/incoming links.
Tools
entry_search { query, limit? }— rank-search entries by display ID and title.entry_context { id, depth? }— walk thesatisfieschain upward.validate { files? }— run the validator, return a Markdown diagnostics report.markspec_refresh— force-invalidate the compile cache (call after agent edits to guarantee freshness).
Claude Desktop config
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"markspec": {
"command": "markspec",
"args": ["mcp"],
"cwd": "/path/to/your/markspec-project"
}
}
}
Restart Claude Desktop. The MarkSpec resources and tools appear in the attach menu.
Claude Code
claude mcp add markspec --command markspec --args mcp --cwd /path/to/project
VS Code (Copilot)
The markspec-ide extension auto-registers the MarkSpec MCP server with VS Code
1.101+ — install the extension and the server appears in Copilot’s MCP picker.
See Editor and LSP integration — VS Code below.
For users who don’t run the extension, the manual recipe still works. Add a
.vscode/mcp.json in your project:
{
"servers": {
"markspec": {
"command": "markspec",
"args": ["mcp"]
}
}
}
Copilot does not support MCP resource subscriptions today, but the markspec://
resources still work — the server runs a fresh staleness check on every read, so
a re-read after an edit returns up-to-date content.
mcp install
Print MCP server configuration for a client.
markspec mcp install --client <client>
Supported clients: claude-desktop, cursor, vscode.
markspec mcp install --client claude-desktop
markspec mcp install --client cursor
markspec mcp install --client vscode
lsp install
Print LSP server configuration for an editor.
markspec lsp install --editor <editor>
Supported editors: vscode, neovim, zed.
markspec lsp install --editor neovim
markspec lsp install --editor zed
Not yet implemented
These commands are registered but print an error and exit:
| Command | Intended purpose |
|---|---|
book dev | Live preview with hot reload |
deck build | Slides → PDF via Touying/Typst |
deck dev | Live slide preview |
Editor and LSP integration
MarkSpec ships a built-in Language Server Protocol (LSP) server. Run it with:
markspec lsp
The server communicates over stdio JSON-RPC — the standard transport that every LSP-capable editor supports.
Features
Diagnostics — broken references, missing IDs, duplicate display IDs, and malformed entries appear as inline errors and warnings as you type. File-local checks run immediately; cross-file validation runs on save.
Entry block completion — type - [ at the start of a line to get a
pre-filled entry block scaffold with the next available display ID for each type
defined in your profile.
ID reference completion — after a trace attribute keyword (Satisfies:,
Derived-from:, Verified-by:, etc.) the server suggests all known display IDs
in the project.
Source file context guard — in source files (Rust, Kotlin, Java, C, C++), completions only activate near entry markers or trace keywords. The server won’t interfere with your language’s native LSP (rust-analyzer, kotlin-lsp, etc.).
VS Code
Install the MarkSpec extension from the editors/vscode/ directory in this
repository. The extension requires VS Code 1.101 or newer (the version that
introduced the stable MCP extension API).
The extension provides two integrations in one install:
- LSP — diagnostics, completions, and entry-block scaffolding in the editor.
- MCP — registers a
markspecMCP server with VS Code so Copilot (and any other MCP-aware client) can use the project’s resources and tools without a separate.vscode/mcp.json.
Both point at the same markspec binary, resolved from markspec.server.path.
From source
cd editors/vscode
npm install
npm run compile
Then in VS Code: Extensions → ⋯ → Install from VSIX or press Ctrl+Shift+P →
Extensions: Install from VSIX… and select the .vsix file, or use the
development host:
code --extensionDevelopmentPath=editors/vscode
Configuration
| Setting | Default | Description |
|---|---|---|
markspec.server.path | "markspec" | Path to the markspec binary (used by both LSP and MCP). |
markspec.server.args | ["lsp"] | Arguments passed to start the LSP server. |
markspec.mcp.enabled | true | Register the MarkSpec MCP server with VS Code. |
markspec.mcp.args | ["mcp"] | Arguments passed to start the MCP server. |
markspec.trace.server | "off" | Trace level: off, messages, or verbose. |
If markspec is not on your PATH, set the full path:
{
"markspec.server.path": "/home/you/.local/bin/markspec"
}
MCP server
Once the extension is installed, the MarkSpec MCP server appears in
Copilot’s MCP picker — no .vscode/mcp.json required. To disable the
registration:
{
"markspec.mcp.enabled": false
}
The manual .vscode/mcp.json recipe in
mcp — VS Code (Copilot) remains supported for editors that
don’t run the extension.
Neovim
Neovim’s built-in LSP client works out of the box. Add this to your init.lua:
vim.api.nvim_create_autocmd("FileType", {
pattern = { "markdown" },
callback = function()
vim.lsp.start({
name = "markspec",
cmd = { "markspec", "lsp" },
root_dir = vim.fs.root(0, { "project.yaml", ".git" }),
})
end,
})
For source files (Rust, Kotlin, etc.) where MarkSpec entry blocks appear in doc
comments, add the relevant file types to the pattern list:
pattern = { "markdown", "rust", "kotlin", "java", "c", "cpp" },
The server’s context guard ensures it only activates near MarkSpec entry markers, so it won’t conflict with rust-analyzer or other language servers running on the same buffer.
With nvim-lspconfig
If you use nvim-lspconfig, add a custom server definition:
local lspconfig = require("lspconfig")
local configs = require("lspconfig.configs")
if not configs.markspec then
configs.markspec = {
default_config = {
cmd = { "markspec", "lsp" },
filetypes = { "markdown", "rust", "kotlin", "java", "c", "cpp" },
root_dir = lspconfig.util.root_pattern("project.yaml", ".git"),
},
}
end
lspconfig.markspec.setup({})
Other editors
Any editor with LSP support can use markspec lsp. The server expects:
- Transport: stdio (stdin/stdout JSON-RPC)
- Trigger characters:
[(block scaffold) and:(ID reference) - Document sync: full text on each change
Point your editor’s LSP client at markspec lsp and it should work. If your
editor needs a specific configuration example, please open an issue.
VS Code extension
The MarkSpec extension (driftsys.markspec-ide) provides first-class editor
support for MarkSpec documents and source-file doc comments. It speaks the
Language Server Protocol (LSP) and delegates to the same markspec binary you
use on the command line.
Features
| Feature | Description |
|---|---|
| Real-time diagnostics | Validation errors and warnings inline as you type |
| Entry block completions | - [ → full block scaffold with display ID and attribute skeleton |
| ID reference completions | Satisfies: → pick from all display IDs in the workspace |
| Type completions | Type: → core types + profile-declared types |
| Hover | Hover any display ID to preview the entry’s title, type, and body |
| Go-to-definition | F12 on a display ID jumps to the entry’s source location |
| Find all references | Shift+F12 lists every file that references a display ID |
| Workspace rename | F2 renames a display ID across the entire workspace |
| Document outline | Outline view lists every entry in the file |
| Workspace symbol search | Ctrl+T fuzzy-searches entries by display ID or title |
| Folding | Each entry block is collapsible |
| Document highlights | Cursor on a display ID highlights every occurrence in the file |
| Quick fixes | One-click fixes for MSL-M060 (uppercase modal), MSL-A030 (generated attr), and more |
Install
- Open Extensions (
Ctrl+Shift+X/Cmd+Shift+X). - Search for MarkSpec.
- Install
driftsys.markspec-ide.
The extension activates on any workspace that contains .md files.
Configuration
All settings live under the markspec. prefix in VS Code settings.
| Setting | Default | Description |
|---|---|---|
markspec.server.path | "markspec" | Path to the markspec binary. Override if not on PATH. |
markspec.server.args | ["lsp"] | Arguments passed to the binary to start the LSP server. |
markspec.trace.server | "off" | LSP protocol trace level: off, messages, or verbose. |
Example — binary in a project-local path:
{
"markspec.server.path": "${workspaceFolder}/.bin/markspec"
}
MCP server
The extension also registers MarkSpec as an MCP server so Claude and other AI agents can query your entry graph directly from inside the editor.
No extra configuration is required — the extension reads markspec.server.path
and registers the MCP server automatically. In VS Code with Copilot or Claude
extension enabled, the server appears as MarkSpec in the agent tool list.
Neovim / other LSP clients
Any editor that supports LSP can use markspec lsp. Example Neovim (lazy.nvim)
configuration:
require("lspconfig").markspec.setup({
cmd = { "markspec", "lsp" },
filetypes = { "markdown" },
root_dir = require("lspconfig.util").root_pattern("project.yaml"),
})
Generate the full configuration snippet for your editor:
markspec lsp install --editor neovim
markspec lsp install --editor zed
markspec lsp install --editor vscode # prints JSON config block
Troubleshooting
Extension activates but shows no diagnostics
- Confirm the binary is on
PATH:markspec --versionin a terminal. - Check the MarkSpec output panel (View → Output → MarkSpec) for LSP errors.
- Verify the project has a
project.yaml—markspec validaterequires one.
“markspec: command not found”
Set markspec.server.path to the absolute binary path, e.g.
/home/user/.local/bin/markspec.
Completions not appearing
- Completions for block scaffold require the line to start with
- [. - Trace-attribute completions require the workspace to be indexed — check the output panel for “Indexed N files”.
AI agents and skillset
MarkSpec integrates with AI assistants at two levels:
- MCP server — exposes the compiled entry graph to any MCP-capable agent so it can query entries, search by display ID, and read traceability context.
- Skillset — teaches the agent MarkSpec’s authoring conventions so it can write and review entries correctly without constant guidance.
MCP server
The MCP server runs as a subcommand of the same markspec binary:
markspec mcp
It speaks the Model Context Protocol over stdio JSON-RPC and exposes the following tools:
| Tool | Description |
|---|---|
entry_search | Fuzzy search entries by display ID or title |
entry_context | Walk the Satisfies chain upward from an entry |
markspec_refresh | Re-index the workspace after file changes |
validate | Run validation and return structured diagnostics |
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"markspec": {
"command": "markspec",
"args": ["mcp"]
}
}
}
Generate the snippet automatically:
markspec mcp install --client claude-desktop
Cursor
markspec mcp install --client cursor
VS Code (Copilot / Claude)
The VS Code extension registers the MCP server automatically — no extra configuration needed. See VS Code extension.
Skillset
The skillset is a bundle of Claude Code skills that teach AI assistants MarkSpec-specific authoring conventions. Install it once per project; the skills activate automatically when Claude Code opens the directory.
Install with upskill
Install into the current project (writes per-client output under .claude/,
.github/, .opencode/, and .agents/ and records the install in
.upskill-lock.json):
upskill add driftsys/markspec:skills/markspec-core.bundle.md
Install globally into $HOME instead, so the skills are available in every
project for the current user:
upskill add --global driftsys/markspec:skills/markspec-core.bundle.md
The bundle includes:
| Skill | Activates on | What it does |
|---|---|---|
markspec-entry-authoring | Any entry authoring request | Guides the agent through correct block syntax and attributes |
markspec-core-rules | Diagnostic triage | Maps MSL- codes to their fix, suppression, and rationale |
markspec-write-loop | File modification tasks | Enforces the insert → format → validate agent write loop |
markspec-gherkin | Test entry authoring | Applies GWT / Gherkin structure to test entries |
markspec-traceability-review | PR reviews, traceability audits | Walks the graph, checks coverage, flags orphaned entries |
markspec-profile-bundle-authoring | Profile manifest writing | Validates manifest fields, extends chains, display-ID patterns |
Manual install (without upskill)
If your team does not use upskill, download the bundle from the repository and
place it in .claude/plugins/:
curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/skills/markspec-core.bundle.md \
-o .claude/plugins/markspec-core.bundle.md
Invoking skills from Claude Code
Skills activate automatically when the task matches. You can also invoke them explicitly:
/markspec-write-loop
/markspec-traceability-review
Agent write loop
The canonical pattern for AI-assisted requirement authoring is:
markspec insert <type> <file> # scaffold a new entry
markspec format <file> # assign ULID, normalize indentation
markspec validate <file> # confirm no broken references
Each step produces structured output the agent can parse:
markspec insert requirement docs/requirements.md --print
markspec format docs/requirements.md
markspec validate docs/requirements.md --format json
The markspec-write-loop skill enforces this sequence so agents don’t skip the
format or validate steps.
Pre-commit hook
markspec hook is a pre-commit hook that runs format-check and validation on
the files staged for commit. Commits that contain malformed entries or broken
references are rejected before they reach the repository.
What the hook checks
- Format — every staged
.mdfile is checked against the canonical format (markspec format --check). If any file needs reformatting the commit is rejected with a message:needs formatting (run 'markspec format'). - Validation — cross-file rules run on all staged entries. Missing
Id:attributes, brokenSatisfies:references, and duplicate display IDs all block the commit.
The hook does not run markspec lint (prose analysis). That is a
review-time quality gate, not a commit blocker.
Setup
pre-commit framework (recommended)
Add to .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: markspec
name: markspec
entry: markspec hook
language: system
types: [markdown]
pass_filenames: true
Install the hook:
pre-commit install
Plain Git hook
Create .git/hooks/pre-commit:
#!/usr/bin/env sh
set -e
# Get staged .md files
STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.md$' || true)
if [ -z "$STAGED" ]; then
exit 0
fi
markspec hook $STAGED
Make it executable:
chmod +x .git/hooks/pre-commit
Sharing the hook with the team
Git hooks are not committed to the repository. Use one of these approaches to share them:
-
pre-commit framework (recommended) — the
.pre-commit-config.yamlfile is committed; developers runpre-commit installafter cloning. -
Bootstrap script — add a
bootstrapscript that installs the hook:#!/usr/bin/env sh cp scripts/pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit
Bypass (emergency)
git commit --no-verify -m "emergency: skip markspec hook"
Use sparingly. Bypassed commits accumulate formatting debt that must be paid before the next regular commit.
CI traceability gate
Run MarkSpec in CI to enforce traceability and format hygiene across the entire repository on every push and pull request.
Recommended pipeline
A minimal CI gate runs three jobs in sequence:
format-check → validate → (optional) lint
All three jobs consume no build artifacts — they operate on the committed source files only.
GitHub Actions
name: MarkSpec
on: [push, pull_request]
jobs:
format-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install markspec
run: curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
- name: Format check
run: markspec format --check docs/**/*.md
validate:
runs-on: ubuntu-latest
needs: format-check
steps:
- uses: actions/checkout@v4
- name: Install markspec
run: curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
- name: Validate
run: markspec validate docs/**/*.md
lint:
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- name: Install markspec
run: curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
- name: Prose lint
run: markspec lint docs/**/*.md
GitLab CI
stages:
- quality
markspec-format:
stage: quality
script:
- curl -fsSL https://raw.githubusercontent.com/driftsys/markspec/main/install.sh | sh
- markspec format --check docs/**/*.md
markspec-validate:
stage: quality
script:
- markspec validate docs/**/*.md
needs: [markspec-format]
markspec-lint:
stage: quality
allow_failure: true # lint is informational; remove to make it blocking
script:
- markspec lint docs/**/*.md
needs: [markspec-validate]
Exit codes
| Code | Meaning |
|---|---|
0 | Clean — no errors, no warnings |
1 | Errors present — commit should be blocked |
2 | Warnings only — informational; gate at your discretion |
The validate command exits 2 when only warnings are present. Use --strict
to promote warnings to errors and make the gate fully binary:
markspec validate --strict docs/**/*.md
Traceability report as CI artifact
Generate a coverage or traceability matrix and upload it as an artifact:
- name: Traceability report
run: markspec report traceability docs/**/*.md --output traceability.md
- uses: actions/upload-artifact@v4
with:
name: traceability
path: traceability.md
Caching the binary
Cache ~/.local/bin/markspec between runs to avoid downloading on every job:
- uses: actions/cache@v4
with:
path: ~/.local/bin/markspec
key: markspec-${{ runner.os }}-0.5.0
ISO 26262 / ASPICE workflow
A reference setup for automotive functional-safety and process-compliance projects. This recipe covers the entry type vocabulary, V-model traceability structure, and required label vocabulary.
Profile
Activate the bundled compliance profile in .markspec.yaml:
profiles:
- "@driftsys/iso26262"
Or declare a local profile that extends it:
profiles:
- "./profiles/my-project"
with profiles/my-project/markspec.yaml:
id: "my-project"
version: 0.1.0
extends: "@driftsys/iso26262"
Entry type vocabulary
The ISO 26262 profile declares the following entry types:
| Display-ID prefix | Type | Extends | Level |
|---|---|---|---|
STK_ | stakeholder | Requirement | Acceptance |
SYS_ | system-requirement | Requirement | System |
SRS_ | software-requirement | Requirement | Software |
ARC_ | architecture | Contract | Software |
ICD_ | interface | SoftwareInterface | Software |
TST_ | validation-test | Test | Acceptance |
SIT_ | integration-test | Test | System |
SWT_ | unit-test | Test | Software |
HZD_ | hazard | Risk | System |
V-model traceability structure
STK (stakeholder)
└─ Satisfies ─→ SYS (system requirement)
└─ Satisfies ─→ SRS (software requirement)
└─ Satisfies ─→ ARC / ICD
TST (validation) ←─ Verifies ─ STK
SIT (integration) ←─ Verifies ─ SYS
SWT (unit test) ←─ Verifies ─ SRS
The compiler generates inverse edges automatically — authors only write the
forward Satisfies: and Verifies: attributes.
ASIL labels
Declare allowed ASIL labels in project.yaml to enable MSL-L010 enforcement:
name: io.acme.braking
version: "1.0.0"
labels:
- ASIL-QM
- ASIL-A
- ASIL-B
- ASIL-C
- ASIL-D
Entries carry the label in the trailer block:
- [SRS_BRK_0107] Sensor debouncing
The sensor driver shall debounce raw inputs to eliminate transient noise
spikes of duration less than 10 ms.
Id: 01HGW2Q8MNP3RSTVWXYZABCDEF
Satisfies: SYS_BRK_0042
Labels: ASIL-B
In-code entries (V-model colocated)
Software requirements and unit tests at the SRS/SWT level are colocated in source files using doc comments:
#![allow(unused)]
fn main() {
/// [SRS_BRK_0107] Sensor debouncing
///
/// The sensor driver shall debounce raw inputs to eliminate
/// transient noise spikes of duration less than 10 ms.
///
/// Id: 01HGW2Q8MNP3RSTVWXYZABCDEF
/// Satisfies: SYS_BRK_0042
/// Labels: ASIL-B
#[cfg(test)]
fn swt_brk_0107_debounce_rejects_short_pulse() {
let result = debounce(5); // 5 ms pulse, below 10 ms threshold
assert!(!result.passed);
}
}
Coverage report
markspec compile "docs/**/*.md" "src/**/*.rs"
markspec report coverage "docs/**/*.md" "src/**/*.rs"
The coverage report shows which requirements have at least one linked test and which are uncovered — the key metric for ISO 26262 traceability audits.
CI gate
Add to your pipeline (see CI traceability gate):
markspec validate --strict "docs/**/*.md" "src/**/*.rs"
markspec report coverage "docs/**/*.md" "src/**/*.rs" --output coverage.md
Flag the coverage report as a required CI artifact for the functional safety manager’s review.
FAQ
Short answers to the questions a team actually asks when trialing MarkSpec.
What is a display ID?
A display ID is the human-readable identifier in the […] bracket on an entry
title line, for example STK_PRJ_0001. It encodes the entry type prefix
(STK), an optional domain segment (PRJ), and a zero-padded sequence number
(0001). Display IDs are mutable — you can rename STK_PRJ_0001 to
STK_AEB_0001 — and MarkSpec rewrites all references in the workspace when you
use the LSP rename command.
What is a ULID and why does MarkSpec assign one?
A ULID (Universally Unique Lexicographically Sortable Identifier) is a
26-character string like 01KPVVC9J2B1ZA64QZEMHF02PW. MarkSpec stamps one onto
each entry during markspec format and stores it as the Id: attribute.
The ULID is immutable. Once assigned it never changes, even if the display ID or title is renamed. Traceability links between projects and external tools (JIRA, DOORS) reference the ULID, not the display ID, so renaming an entry never breaks cross-tool links.
How does MarkSpec discover project.yaml?
markspec walks up the directory tree from the current working directory until
it finds a project.yaml file. The directory containing that file becomes the
project root. If no project.yaml is found, commands that require project
context (compile, show, context, dependents, report, next-id,
doc build, book build) exit with an error. format and validate work
without a project.yaml.
Can I use MarkSpec without a profile?
Yes. markspec format and markspec validate work without a profile — they
apply built-in formatting rules and lint checks. Commands that need type
vocabulary (compile, create, next-id) require a profile to know the
display-ID patterns for each type.
To activate a profile, create a .markspec.yaml in the project root:
profiles:
- ./profiles/markspec.yaml
See the Profile guide for the full configuration reference.
What do exit codes 0, 1, and 2 mean?
| Code | Meaning |
|---|---|
0 | Success — no errors, no warnings |
1 | Error — validation failed or command error |
2 | Warnings only — validate found warnings but no errors |
In CI, treat exit code 2 as a pass or a fail depending on your policy. Pass
--strict to markspec validate to promote warnings to errors (exit code 1).
Where should I put my Markdown files?
Anywhere you like — MarkSpec does not enforce a directory layout. The convention used in this project is:
docs/— Markdown files containing requirements and architecture descriptionssrc/— source code with doc-comment entries (Rust///, Kotlin/**)
Pass explicit paths or globs to commands that need them:
markspec validate docs/requirements.md
markspec compile "docs/**/*.md" src/main.rs
What is the difference between format and validate?
| Command | What it does | Writes files? |
|---|---|---|
markspec format | Stamps ULIDs, normalizes indentation and attribute order | Yes (in place) |
markspec validate | Checks broken references, missing IDs, duplicates, lint rules | No |
Run format first (or as a pre-commit hook via markspec hook) to ensure every
entry has a ULID before committing. Run validate in CI to catch broken
traceability links.
How do I set up the LSP in my editor?
- VS Code — install the
markspec-ideextension fromeditors/vscode/in the repository. See CLI guide — VS Code. - Neovim — add a
vim.lsp.startautocmd. See CLI guide — Neovim. - Other editors — point your LSP client at
markspec lsp. Transport: stdio. See CLI guide — Other editors.
Can I run multiple profiles at once?
The current implementation supports one active profile per project. If multiple
profiles are listed in .markspec.yaml, a PROFILE-LOAD-006 warning is emitted
and only the first is used. Profile stacking (extends chains) is the recommended
way to compose vocabulary. See the Profile guide.
Migration guide
Stage 2 — content deferred.
This chapter will document how to migrate existing requirements documents, DOORS exports, and ReqIF files into MarkSpec format. Its content is deferred to Stage 2 of the MarkSpec documentation roadmap, after the toolchain distribution and profile-schema implementation are complete.
Per project decision: no migration tooling or backward-compatibility shims are provided until version 1.0. All migration paths will be documented here when they ship.
Stage 2 will cover:
- DOORS XML → MarkSpec — automated export and ULID assignment.
- ReqIF → MarkSpec — type mapping and attribute preservation.
- Display-ID renaming — workspace-wide rename via the LSP or CLI.
- Version upgrade notes — breaking changes between MarkSpec releases.