agentleFS
jyjeannejyjeanne/ditacraft/CLAUDE.md

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

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
  1. Very specific:

    Create server/src/features/myfeature.ts with a handler function

    line 230
  2. Very specific:

    Add types to server/src/utils/types.ts if needed

    line 231
  3. Very specific:

    Add tests in server/test/myfeature.test.ts using createDoc() helper

    line 233
  4. Very specific:

    Add diagnostic code to server/src/utils/diagnosticCodes.ts

    line 239
  5. Very specific:

    Add message to server/src/messages/en.json and fr.json

    line 240
  6. Very specific:

    Add test in server/test/ditaRulesValidator.test.ts

    line 241
  7. Very specific:

    Create handler in src/commands/mycommand.ts

    line 247
  8. Very specific:

    Add entry to package.json under contributes.commands

    line 249
  9. Very specific:

    Add tests in src/test/suite/mycommand.test.ts

    line 250
  10. Distinctive:

    Run extension in debug mode (F5 in VS Code)

    line 256
  11. Distinctive:

    All commands run offline against the committed graph — no API keys needed:

    line 269
  12. Very specific:

    Use affected-flows before refactoring a shared service or utility to see what could break, and review-context when reviewing or summarizing …

    line 298
  13. Distinctive:

    All diagnostic messages are translatable.

    line 352
  14. Very specific:

    Add same key to both en.json and fr.json

    line 357

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

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 install

Compile (Client + Server)

npm run compile

Runs 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-types

Runs tsc --noEmit for both client and server. Faster than a full compile when you only want type errors.

Development Watch Mode (Recommended)

npm run watch

Runs 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 package

Runs vsce package to produce a .vsix file for manual installation or publishing.

Linting

npm run lint

ESLint with TypeScript support. Uses @typescript-eslint/parser and .eslintrc.json.

Knowledge Graph (graphify)

npm run graph

Generates 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 okf

Generates 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 test

Runs 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:shared

Compiles 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 test

Compiles 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 coverage

Configured 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 coverage

Enforces: 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 connection
  • server/src/server.ts — LSP server entry point, registers all feature handlers
  • Validation runs in pull-based diagnostics: client requests → server's ValidationPipeline runs 13 phases → returns Diagnostic[]

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):

  1. XML well-formedness (fast-xml-parser)
  2. DITA structure (root, DOCTYPE, required children)
  3. ID validation (duplicates, format, quote consistency)
  4. Content model validation
  5. DTD validation (TypesXML + OASIS catalog) OR RelaxNG (salve-annos)
  6. DITA rules (43 Schematron-equivalent rules, version-gated)
  7. Custom rules (user-defined regex patterns)
  8. Cross-reference validation (href/conref/keyref targets exist)
  9. Profiling validation (subject scheme controlled values)
  10. Circular reference detection (DFS for href/mapref cycles)
  11. Workspace validation (cross-file duplicate IDs, unused topics)
  12. DITAVAL validation
  13. Suppression application (comment-based rule suppression)

Key Design Decisions:

  • Severity overrides: ditacraft.validationSeverityOverrides changes 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 docs

Common Workflows

Adding a New LSP Feature

  1. Create server/src/features/myfeature.ts with a handler function
  2. Add types to server/src/utils/types.ts if needed
  3. Wire the handler directly in server/src/server.ts: standard LSP protocol methods use connection.onMyFeature(handleMyFeature); custom DitaCraft-specific requests use connection.onRequest('dita/myFeature', handleMyFeature) (see the existing dita/computeMoveEdits, dita/computeFindReplaceEdits, dita/computeBatchMetadataEdits, dita/getSubjectSchemeAttributes for the established pattern). server/src/serverHandlers.ts holds only initialization/classification helper functions server.ts's handlers call into (detectClientCapabilities, buildInitializeResult, isMapFile, classifyWatchedFileChanges, ...) — it does not itself register any connection.on* handler.
  4. Add tests in server/test/myfeature.test.ts using createDoc() helper
  5. Run: cd server && npm test -- --grep "myfeature"

Adding a New DITA Validation Rule

  1. Define rule in server/src/features/ditaRulesValidator.ts (add to DITA_RULES array)
  2. Add diagnostic code to server/src/utils/diagnosticCodes.ts
  3. Add message to server/src/messages/en.json and fr.json
  4. Add test in server/test/ditaRulesValidator.test.ts
  5. Rule runs in Phase 6 of ValidationPipeline
  6. Toggleable via ditacraft.ditaRulesEnabled and ditacraft.ditaRulesCategories

Adding a New Client Command

  1. Create handler in src/commands/mycommand.ts
  2. Register in src/extension.ts under registerCommands()
  3. Add entry to package.json under contributes.commands
  4. Add tests in src/test/suite/mycommand.test.ts
  5. Run: npm test

Debugging the LSP Server

  1. Set breakpoints in server/src/features/*.ts
  2. Run extension in debug mode (F5 in VS Code)
  3. Open Developer Tools (Help → Toggle Developer Tools)
  4. Console tab shows LSP server logs (via connection.console.log())
  5. For file logging: set ditacraft.enableFileLogging: true and ditacraft.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.json

Impact 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.json

Use 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):

SettingTypeDefaultPurpose
ditacraft.ditaOtPathstring""DITA-OT installation path
ditacraft.validationEnginestring"typesxml"built-in, typesxml, or xmllint
ditacraft.ditaRulesEnabledbooleantrueEnable DITA rules validation
ditacraft.ditaRulesCategoriesstring[]allCategories: mandatory, recommendation, authoring, accessibility
ditacraft.crossRefValidationEnabledbooleantrueCross-file reference checks
ditacraft.subjectSchemeValidationEnabledbooleantrueSubject scheme constraint checks
ditacraft.validationSeverityOverridesobject{}Per-rule severity (error/warning/info/hint/off)
ditacraft.customRulesFilestring""Path to custom regex rules JSON file
ditacraft.largeFileThresholdKBnumber500Skip heavy phases for files > threshold
ditacraft.logLevelstring"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-formedness
  • DITA-STRUCT-* — DITA structure validation
  • DITA-ID-* — ID validation and uniqueness
  • DITA-SCOPE-* — href scope consistency
  • DITA-SCH-* — Schematron-equivalent DITA rules (1-59, including DITA 2.0)
  • DITA-XREF-* — Cross-reference validation
  • DITA-CYCLE-* — Circular reference detection
  • DITA-PROF-* — Profiling/subject scheme validation
  • DITA-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:

  1. Add same key to both en.json and fr.json
  2. Reference in handler: i18n.get('DITA_MSG_KEY')
  3. 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 TextDocument
  • waitForErrors(uri, timeout) — Poll for diagnostic errors in client tests
  • defaultSettings() — 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

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.