ditacraft
CLAUDE.md19 starsChanged 32 days agoMITView on GitHub
DitaCraft is a comprehensive VS Code extension for editing and publishing DITA (Darwin Information Typing Architecture) XML files. It provides syntax highlighting, real-time validation, smart navigation, LSP-based IntelliSense, live HTML preview, and one-click DITA-OT publishing. The project uses a client-server architecture: the client extension handles UI commands and a separate LSP server handles all language intelligence. Key Stats: - 2,900+ tests (1,371 client + 1,167 server + 307 visual preview/editor core + 88 MCP) - 50,000+ lines of TypeScript code…
- Installs packages
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- writing-commit-messagesSkill
The file
Show its rules and text
We split this CLAUDE.md into the rules it gives an agent
14 rules. About 0 are common, 3 are distinctive, and 11 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
11
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Very specific: line 230
Create server/src/features/myfeature.ts with a handler function
- Very specific: line 231
Add types to server/src/utils/types.ts if needed
- Very specific: line 233
Add tests in server/test/myfeature.test.ts using createDoc() helper
- Very specific: line 239
Add diagnostic code to server/src/utils/diagnosticCodes.ts
- Very specific: line 240
Add message to server/src/messages/en.json and fr.json
- Very specific: line 241
Add test in server/test/ditaRulesValidator.test.ts
- Very specific: line 247
Create handler in src/commands/mycommand.ts
- Very specific: line 249
Add entry to package.json under contributes.commands
- Very specific: line 250
Add tests in src/test/suite/mycommand.test.ts
- Distinctive: line 256
Run extension in debug mode (F5 in VS Code)
- Distinctive: line 269
All commands run offline against the committed graph — no API keys needed:
- Very specific: line 298
Use affected-flows before refactoring a shared service or utility to see what could break, and review-context when reviewing or summarizing …
- Distinctive: line 352
All diagnostic messages are translatable.
- Very specific: line 357
Add same key to both en.json and fr.json
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
DitaCraft is a comprehensive VS Code extension for editing and publishing DITA (Darwin Information Typing Architecture) XML files. It provides syntax highlighting, real-time validation, smart navigation, LSP-based IntelliSense, live HTML preview, and one-click DITA-OT publishing. The project uses a client-server architecture: the client extension handles UI commands and a separate LSP server handles all language intelligence.
Key Stats:
- 2,900+ tests (1,371 client + 1,167 server + 307 visual preview/editor core + 88 MCP)
- 50,000+ lines of TypeScript code
- Client: ES2020, CommonJS, esbuild bundled
- Server: 13-phase validation pipeline, LSP 3.17+
- Validation: TypesXML DTD (OASIS catalog), optional RelaxNG, 43 DITA rules, custom regex rules
Building & Development
Install Dependencies
npm installCompile (Client + Server)
npm run compileRuns type checking and esbuild for both client and server via esbuild.js. Produces out/extension.js and server/out/server.js.
Type-Check Only (No Emit)
npm run check-typesRuns tsc --noEmit for both client and server. Faster than a full compile when you only want type errors.
Development Watch Mode (Recommended)
npm run watchRuns esbuild and TypeScript in watch mode. Press F5 in VS Code to launch in debug mode. Reload with Ctrl+R to test changes.
Package Extension
npm run packageRuns vsce package to produce a .vsix file for manual installation or publishing.
Linting
npm run lintESLint with TypeScript support. Uses @typescript-eslint/parser and .eslintrc.json.
Knowledge Graph (graphify)
npm run graphGenerates a queryable knowledge graph of the codebase into docs/graph/ using Engram, formerly graphify (@sentropic/engram; @sentropic/graphify is now a deprecated forwarding shim; local tree-sitter AST extraction — no LLM/API keys). Artifacts: graph.json, GRAPH_REPORT.md, graph.svg, and an interactive studio/studio.html viewer. npm run watch includes watch:graph, so the graph auto-regenerates on code changes during development; the knowledge-graph GitHub Actions workflow refreshes it on every push to main. Extraction scope is controlled by .graphifyignore (Engram still reads it); working state in .engram/ is git-ignored. Query with npx graphify query "..." --graph docs/graph/graph.json (Engram installs both the engram and graphify commands).
OKF Knowledge Base (okf-rs)
npm run okfGenerates a conformant Open Knowledge Format bundle of the codebase into docs/okf-knowledge/ using okf-rs — a standalone Rust CLI (cargo install --git https://github.com/jyjeanne/okf-rs okf-cli, or a prebuilt release binary; not an npm devDependency, so this is opt-in and NOT part of npm install/npm run watch, unlike graphify above). Unlike graph.json's single-file, database-like artifact, an OKF bundle is one Markdown+YAML-frontmatter file per concept (package/module/class/interface/function/method), cross-linked by ordinary markdown links — git diff-able per concept, and readable without any tooling at all. Also provides change-impact analysis (okf-rs impact <ref-a> <ref-b>), a PR-review report generator (okf-rs review), and call-cycle/community detection that complement graphify's own querying and visualization strengths. docs/graph/studio/assets/ (graphify's vendored, minified viewer bundle) is excluded from the scan via .gitignore — it isn't source code and previously flooded the bundle with garbage concepts extracted from minified JS. Working cache .okf-cache.json is git-ignored; see docs/okf-knowledge/README.md for querying examples. okf.toml at the repo root records output = "docs/okf-knowledge", so okf-rs search/explore/graph/validate/coverage all resolve the bundle without needing --output repeated on every call — e.g. okf-rs explore KeySpaceService.
Querying it as an MCP tool (Claude Code): okf-mcp (built from the same jyjeanne/okf-rs source — cargo build --release -p okf-mcp, or use the prebuilt binary alongside okf-rs) exposes docs/okf-knowledge's search/explore/coverage/graph queries as MCP tools over stdio, so a Claude Code session can query the codebase's structure and call graph directly instead of grep-and-read — cheaper (one structured answer vs. reading whole files) and more precise (a resolved call edge vs. a name match). Register it project-wide (shared via git with every contributor's Claude Code instance) with:
claude mcp add okf-rs -s project -- /path/to/okf-mcp .Prefer explore <concept> when you need several facts about the same concept (signature, callers, callees, blast radius) in one round trip; graph (pass relation: callers|callees|path|cycles|communities|...) for everything else — see the MCP server section of the okf-rs README for the full tool list and --benchmark for a local, offline token-cost report specific to this bundle.
Testing
Client Tests (VS Code Integration)
npm testRuns integration tests using @vscode/test-electron. Timeout: 10 seconds per test.
To run a single test: Modify src/test/suite/index.ts to filter tests by name.
Visual Preview Core Tests (Standalone Mocha, No VS Code)
npm run test:sharedCompiles the client tests and runs out/test/shared/**/*.test.js with plain Mocha: the CST round-trip gate over every DITA file in the repo (add a corpus with DITACRAFT_CORPUS=<folder>), the grammar compiler over every bundled DTD shell (including ProseMirror schema instantiation and the catalog PUBLIC-id audit), the renderer, the preview's resolver/DITAVAL/grammar-selection modules, and the visual editor core (lossless round trip, edit and command fuzzing over the corpus — DITACRAFT_FUZZ_SEED/_RUNS/_STEPS run it harder). See docs/VISUAL_PREVIEW.md and docs/VISUAL_EDITOR.md.
The visual editor's invariant: an edit on the page reaches the document as one minimal text edit, and every node unchanged since load is written back byte for byte (src/shared/editor/toSource.ts). Any change to the editor core must keep the corpus fuzz suites green.
Server Tests (Standalone Mocha, No VS Code)
cd server
npm testCompiles TypeScript and runs Mocha: mocha out/test/**/*.test.js --ui tdd --timeout 10000
To run a single server test:
cd server
npm test -- --grep "test suite name"Example: npm test -- --grep "validateDITADocument"
The integration suites of server/test/docsDataset.test.ts run against the DITA-OT documentation sources, and only when asked: set DITACRAFT_DOCS_DATASET to the folder holding userguide.ditamap (a docs-develop checkout, or the docsrc folder of a DITA-OT distribution). Otherwise they are skipped.
Code Coverage
Client:
npm run coverageConfigured in .c8rc.json (a c8 key in package.json was not applied): 61% lines and statements, 63% functions, 73% branches, checked by npm run coverage:check. The bundled language server (server/**), which the tests start as a child process, is excluded: the server has its own gate.
Server:
cd server && npm run coverageEnforces: 90% lines/statements/functions, 80% branches.
Project Architecture
High-Level Structure
Client-Server Architecture:
- Client (
src/) — VS Code extension: commands, UI views, file operations, configuration - LSP Server (
server/src/) — Separate Node.js process via IPC: validation, IntelliSense, navigation, formatting
The client starts the server as a child process. They communicate via LSP 3.17+ (JSON-RPC over stdio).
Key Integration Points:
src/languageClient.ts— Initializes LSP client, manages connectionserver/src/server.ts— LSP server entry point, registers all feature handlers- Validation runs in pull-based diagnostics: client requests → server's
ValidationPipelineruns 13 phases → returnsDiagnostic[]
Validation Pipeline (13 Phases)
The ValidationPipeline in server/src/services/validationPipeline.ts orchestrates all validation with error isolation (if phase 6 crashes, phases 7-13 still run):
- XML well-formedness (fast-xml-parser)
- DITA structure (root, DOCTYPE, required children)
- ID validation (duplicates, format, quote consistency)
- Content model validation
- DTD validation (TypesXML + OASIS catalog) OR RelaxNG (salve-annos)
- DITA rules (43 Schematron-equivalent rules, version-gated)
- Custom rules (user-defined regex patterns)
- Cross-reference validation (href/conref/keyref targets exist)
- Profiling validation (subject scheme controlled values)
- Circular reference detection (DFS for href/mapref cycles)
- Workspace validation (cross-file duplicate IDs, unused topics)
- DITAVAL validation
- Suppression application (comment-based rule suppression)
Key Design Decisions:
- Severity overrides:
ditacraft.validationSeverityOverrideschanges any diagnostic code's severity - Comment suppression:
<!-- ditacraft-disable CODE -->/<!-- ditacraft-enable CODE -->ranges suppress rules - Large file optimization: Files > 500 KB skip phases 6-12
- Smart debouncing: Topics 300ms, Maps 1000ms, per-document cancellation
Key Services
ValidationPipeline — Orchestrates 13 phases, applies severity overrides and comment suppression
SuppressionEngine — Parses inline suppression comments and filters diagnostics by range or file; extracted from the pipeline for testability
KeySpaceService — Parses DITA maps, builds key space (BFS traversal), handles @keyscope nesting, 5-min cache TTL
SubjectSchemeService — Parses subject scheme maps, extracts controlled value constraints, caches schemes
CatalogValidationService — TypesXML DTD validation with OASIS Catalog, parser pool (3 instances), DITA 1.0-2.0
RngValidationService — Optional RelaxNG validation (salve-annos + saxes), grammar caching (max 20 schemas)
Directory Reference
src/ # Client extension
extension.ts # Activation, command registration, lifecycle
languageClient.ts # LSP client initialization
commands/ # Command handlers
providers/ # VS Code API (validators, views, webviews)
utils/ # DITA-OT wrapper, key space, config, logger
shared/ # Visual preview core, environment-neutral (no vscode import):
cst/ # format-preserving XML tree, element ids/offsets, CALS grid
grammar/ # DTD → grammar JSON compiler (typesxml), ProseMirror schema spec
render/ # CST → HTML renderer (DITA-OT class contract), labels, entities
editor/ # visual editor core: grammar → ProseMirror schema, source ⇄ editor doc
# (lossless writer toSource.ts), DITA commands, edit sync, map rows (maps.ts), their editing (mapCommands.ts), relationship tables (relTables.ts)
preview/ # Visual preview host: panel, grammar selection, resolver, DITAVAL (also the text editor's condition highlighting marks: conditionMarks.ts), map context of topics (mapContext.ts, mapContexts.ts)
editor/ # Visual editor host: CustomTextEditorProvider (ditacraft.visualEditor), protocol,
# Replace with copy (reuseCopy.ts: DITA conref resolution of the copy, references rebased),
# image files (imageFiles.ts: hrefs, pasted images), link picker
# (linkPicker.ts QuickPick + Open link target; linkTargets.ts items, no vscode)
properties/ # Properties view (side bar): follows the visual or text editor, edits attributes
test/ # 1,371 client tests; test/shared/ = plain-Mocha suites for shared/ + preview/ (307)
webview/preview/ # Visual preview page script + stylesheet (bundled to out/webview/)
webview/editor/ # Visual editor page (ProseMirror; map rows: mapView.ts) + stylesheet (bundled to out/webview/)
webview/properties/ # Properties view page + stylesheet (bundled to out/webview/)
scripts/compile-grammars.js # Builds out/grammars/ from dtds/ (run by esbuild.js; cached)
scripts/screenshots/ # README/user-guide screenshots of the visual editor in a real VS Code window (shoot.js, demo project)
scripts/cover/ # User guide cover image: cover.html → docs/user-guide/front_page_picture.png (node scripts/cover/render.js);
# trace-logo.js traces resources/ditacraft-logo.png into resources/ditacraft-logo.svg (needs `npm install --no-save potrace@2 pngjs@7`)
server/
src/
server.ts # LSP entry point, handler wiring
serverHandlers.ts # Handler registration and capabilities
settings.ts # Per-document config caching
features/ # LSP handlers (validation, completion, hover, etc.)
services/ # Domain services (ValidationPipeline, KeySpaceService, etc.)
utils/ # Shared utilities (referenceParser, workspaceScanner, textUtils)
messages/ # Localization (en.json, fr.json)
data/ # Static schema data (ditaSchema.ts, ditaSpecialization.ts)
test/ # 1,167 server tests (standalone Mocha)
dtds/ # DITA 1.2, 1.3, 2.0 DTDs + OASIS catalogs
docs/ # Architecture docsCommon Workflows
Adding a New LSP Feature
- Create
server/src/features/myfeature.tswith a handler function - Add types to
server/src/utils/types.tsif needed - Wire the handler directly in
server/src/server.ts: standard LSP protocol methods useconnection.onMyFeature(handleMyFeature); custom DitaCraft-specific requests useconnection.onRequest('dita/myFeature', handleMyFeature)(see the existingdita/computeMoveEdits,dita/computeFindReplaceEdits,dita/computeBatchMetadataEdits,dita/getSubjectSchemeAttributesfor the established pattern).server/src/serverHandlers.tsholds only initialization/classification helper functions server.ts's handlers call into (detectClientCapabilities,buildInitializeResult,isMapFile,classifyWatchedFileChanges, ...) — it does not itself register anyconnection.on*handler. - Add tests in
server/test/myfeature.test.tsusingcreateDoc()helper - Run:
cd server && npm test -- --grep "myfeature"
Adding a New DITA Validation Rule
- Define rule in
server/src/features/ditaRulesValidator.ts(add toDITA_RULESarray) - Add diagnostic code to
server/src/utils/diagnosticCodes.ts - Add message to
server/src/messages/en.jsonandfr.json - Add test in
server/test/ditaRulesValidator.test.ts - Rule runs in Phase 6 of ValidationPipeline
- Toggleable via
ditacraft.ditaRulesEnabledandditacraft.ditaRulesCategories
Adding a New Client Command
- Create handler in
src/commands/mycommand.ts - Register in
src/extension.tsunderregisterCommands() - Add entry to
package.jsonundercontributes.commands - Add tests in
src/test/suite/mycommand.test.ts - Run:
npm test
Debugging the LSP Server
- Set breakpoints in
server/src/features/*.ts - Run extension in debug mode (F5 in VS Code)
- Open Developer Tools (Help → Toggle Developer Tools)
- Console tab shows LSP server logs (via
connection.console.log()) - For file logging: set
ditacraft.enableFileLogging: trueandditacraft.logLevel: debug
Using the Knowledge Graph
A pre-built, always-up-to-date knowledge graph of the codebase lives in docs/graph/graph.json (~1,500 nodes: classes, functions, files, and their imports/calls/contains relationships, clustered into communities). Query it before grepping when you need architectural understanding — it answers "what connects to what" questions in one command instead of many file reads.
Query Commands
All commands run offline against the committed graph — no API keys needed:
# Orientation: top hubs, key communities, graph stats (start here)
npx graphify summary docs/graph/graph.json
# Ask a question — BFS traversal returns the relevant subgraph
npx graphify query "how does key space resolution work" --graph docs/graph/graph.json
# Everything about one symbol: role, edges, neighbors, source file
npx graphify explain "ValidationPipeline" --graph docs/graph/graph.json
# How are two components connected?
npx graphify path "KeySpaceService" "ValidationPipeline" --graph docs/graph/graph.json
# Dependency tree from one node
npx graphify tree "SubjectSchemeService" --graph docs/graph/graph.jsonImpact Analysis Before/After Changes
# Which execution flows does a change to these files affect? (blast radius)
npx graphify affected-flows server/src/services/keySpaceService.ts --graph docs/graph/graph.json --flows docs/graph/flows.json
# Focused review context for a set of changed files
npx graphify review-context src/extension.ts server/src/server.ts --graph docs/graph/graph.jsonUse affected-flows before refactoring a shared service or utility to see what could break, and review-context when reviewing or summarizing a diff.
When to Use Graph vs. Grep
- Graph: "what depends on X", "how do A and B connect", "what's the blast radius of this change", "give me an overview of subsystem Y"
- Grep/Glob: exact string lookups, finding a specific diagnostic code, locating a config key
Keeping It Fresh
The graph regenerates automatically (watch:graph inside npm run watch locally; the knowledge-graph workflow on pushes to main). If you've made large local changes without watch running, refresh manually with npm run graph. See Knowledge Graph (graphify) for the generation pipeline; the interactive viewer is docs/graph/studio/studio.html.
Configuration & Settings
Core settings (in package.json contributes.configuration):
| Setting | Type | Default | Purpose |
ditacraft.ditaOtPath | string | "" | DITA-OT installation path |
ditacraft.validationEngine | string | "typesxml" | built-in, typesxml, or xmllint |
ditacraft.ditaRulesEnabled | boolean | true | Enable DITA rules validation |
ditacraft.ditaRulesCategories | string[] | all | Categories: mandatory, recommendation, authoring, accessibility |
ditacraft.crossRefValidationEnabled | boolean | true | Cross-file reference checks |
ditacraft.subjectSchemeValidationEnabled | boolean | true | Subject scheme constraint checks |
ditacraft.validationSeverityOverrides | object | {} | Per-rule severity (error/warning/info/hint/off) |
ditacraft.customRulesFile | string | "" | Path to custom regex rules JSON file |
ditacraft.largeFileThresholdKB | number | 500 | Skip heavy phases for files > threshold |
ditacraft.logLevel | string | "info" | debug, info, warn, error |
Settings cached per-document in server/src/settings.ts, broadcast via workspace/didChangeConfiguration.
Diagnostic Codes
Codes follow pattern DITA-XXX-NNN (e.g., DITA-XML-001, DITA-SCH-023). Central registry in server/src/utils/diagnosticCodes.ts (78+ codes):
DITA-XML-*— XML well-formednessDITA-STRUCT-*— DITA structure validationDITA-ID-*— ID validation and uniquenessDITA-SCOPE-*— href scope consistencyDITA-SCH-*— Schematron-equivalent DITA rules (1-59, including DITA 2.0)DITA-XREF-*— Cross-reference validationDITA-CYCLE-*— Circular reference detectionDITA-PROF-*— Profiling/subject scheme validationDITA-ORD-*— Orphan/unused topic detection
Each code maps to a message in server/src/messages/en.json (and fr.json for French).
Localization (i18n)
All diagnostic messages are translatable. Files:
server/src/messages/en.json— English (80+ messages)server/src/messages/fr.json— French (80+ messages)
To add a message:
- Add same key to both
en.jsonandfr.json - Reference in handler:
i18n.get('DITA_MSG_KEY') - Server auto-detects client locale and applies correct bundle
Testing Best Practices
Client Test Pattern
suite('My Test Suite', () => {
suiteSetup(async function() {
this.timeout(30000);
const api = extension.exports as DitaCraftAPI;
serverReady = await api?.waitForLanguageClientReady(20000);
});
test('should do something', async () => {
const diagnostics = await waitForErrors(uri, 3000);
assert.ok(diagnostics.length > 0);
});
});Server Test Pattern
suite('My Feature', () => {
test('should validate correctly', () => {
const doc = createDoc('<dita>...</dita>', 'file:///test.dita');
const diags = myValidator(doc);
assert.strictEqual(diags[0].code, 'DITA-CODE-001');
});
});Key Helpers:
createDoc(content, uri)— Mock TextDocumentwaitForErrors(uri, timeout)— Poll for diagnostic errors in client testsdefaultSettings()— Standard DitaCraftSettings
Key Files to Know
Client Entry: src/extension.ts, src/languageClient.ts
Server Entry: server/src/server.ts, server/src/serverHandlers.ts
Core Validation: server/src/services/validationPipeline.ts, server/src/features/validation.ts, server/src/features/ditaRulesValidator.ts
Key Services: server/src/services/keySpaceService.ts, server/src/services/catalogValidationService.ts, server/src/services/subjectSchemeService.ts, server/src/services/suppressionEngine.ts
Service Interfaces: server/src/services/interfaces.ts — contracts for IKeySpaceService, ISubjectSchemeService, ICatalogValidationService; use these for mocking in tests
Config & Utils: src/utils/configurationManager.ts, server/src/utils/textUtils.ts, server/src/utils/diagnosticCodes.ts
Notes for Future Work
- Large files: > 500 KB skip phases 6-12 by design; threshold configurable
- Key space: 5-minute cache TTL; refresh with DITA: Refresh Key Space command
- DITA versions: Auto-detected from @DITAArchVersion or DOCTYPE; override with ditacraft.ditaVersion
- Custom rules: Load from JSON file (ditacraft.customRulesFile); support fileType filtering
- Comment suppression: Three directives —
<!-- ditacraft-disable CODE -->/<!-- ditacraft-enable CODE -->for range-based suppression;<!-- ditacraft-disable-file CODE -->suppresses a rule for the entire file - Performance: Smart debouncing (300ms topics, 1000ms maps) with per-document cancellation
- Security: XXE protection, path traversal validation, command injection prevention, quote-aware entity pre-checks
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

