Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bcf22de
test(examples): add v0.4.0 dogfooding artifacts (linting, source-maps…
dean0x Jul 17, 2026
ef7f72d
fix(core): bare relative filenames resolve on all subcommands (effect…
dean0x Jul 17, 2026
554fc67
fix(cli): default-exclude hidden dirs and node_modules in the shared …
dean0x Jul 17, 2026
f64c4f0
feat(core): ElseifBranch struct with per-branch offset, else_offset o…
dean0x Jul 17, 2026
8df6d70
feat(core): apply_fixes_incremental with bounded per-edit fallback + …
dean0x Jul 18, 2026
03e55d2
fix(cli): lint preview honesty, dir-mode display paths, partial-fix r…
dean0x Jul 18, 2026
9836e83
test(cli): lint preview/display-path/partial-fix integration coverage
dean0x Jul 18, 2026
7694617
fix(core): formatter gate no longer false-positives on trailing insig…
dean0x Jul 18, 2026
b3fc07b
feat(core,cli): add format_str_named and thread file name through for…
dean0x Jul 18, 2026
06fb5cf
fix(core)!: unify string-source sourceMap label on "input.mds" across…
dean0x Jul 18, 2026
30e82b0
fix(cli): relativize inline source-map paths for stdout output; allow…
dean0x Jul 18, 2026
11939fc
feat(core): span + file context on mds::type_mismatch (type_mismatch_…
dean0x Jul 18, 2026
38aa826
fix(core): lexer escape hint on unclosed interpolation brace
dean0x Jul 18, 2026
5400a0c
test(core): assert MSG_MODE_SOURCE_MAP_WARNING appears exactly once p…
dean0x Jul 18, 2026
a846746
fix(core): surface-neutral messages-mode source-map warning wording
dean0x Jul 18, 2026
f2081a7
feat(wrapper): strict unknown-option rejection + CheckOptions/Compile…
dean0x Jul 18, 2026
fede981
fix(cli): check summary says "passed", fmt --check adds unchanged cou…
dean0x Jul 18, 2026
793dee3
fix(test-infra): conftest picks freshest CLI binary by mtime; add nap…
dean0x Jul 18, 2026
01606ab
docs: v0.4.0 accuracy sweep — spec fixes, sidecar naming, fmt demo, s…
dean0x Jul 18, 2026
f55f4d0
docs(changelog): v0.4.0 remediation entries
dean0x Jul 18, 2026
b774161
style(cli): rustfmt straggler in fmt.rs summary print
dean0x Jul 18, 2026
880febe
refactor: post-implementation simplification pass (no behavior change)
dean0x Jul 18, 2026
c5a4d65
fix: scrutinizer — @extends type_mismatch span no longer mis-attribut…
dean0x Jul 18, 2026
7a61898
refactor(core): review cleanups — honest test coverage, no panic path…
dean0x Jul 18, 2026
11e82d9
fix(ci): ignore BrokenPipe in lint_stdin test helper on Linux
dean0x Jul 18, 2026
e486a80
test(python): #79 anchor SM-PY-4 warning to MSG_MODE_SOURCE_MAP_WARNI…
dean0x Jul 18, 2026
a641487
test(python): #80,#84 raise on invalid MDS_CLI_BIN; document mtime ti…
dean0x Jul 18, 2026
d9a83f6
test(python): #83 add else:pytest.fail() to all bare test_e5 try/exce…
dean0x Jul 18, 2026
7bfbf09
fix(napi-pkg): rename build script to build:native — avoids fan-out (…
dean0x Jul 18, 2026
286983b
test(napi-pkg): wire index.spec.mjs into CI (#89)
dean0x Jul 18, 2026
1815683
docs: accuracy sweep — lint API, error codes, WASM options, spec pipe…
dean0x Jul 18, 2026
d1db88c
fix(ts): restore sync-throw error contract, model CheckOptions hierar…
dean0x Jul 18, 2026
48eff05
docs(changelog): restructure [Unreleased] to Keep a Changelog conform…
dean0x Jul 18, 2026
a007bf1
fix(core/fix): Group C — FixOutcome non_exhaustive, sort asserts, rej…
dean0x Jul 18, 2026
7e90d1e
fix(core): guard UTF-8 char boundary, collapse eval dual source-of-tr…
dean0x Jul 18, 2026
32c286e
fix(wrapper): strict type-safe option validation — issues #72/#74/#18…
dean0x Jul 18, 2026
c5aa086
fix(core/cli): bare-filename gate — resolve_base_dir canonicalize, ef…
dean0x Jul 18, 2026
28dacd8
style: rustfmt pass after parallel resolve wave 1
dean0x Jul 18, 2026
678a737
test(watch/napi/python): bare-filename regression gate — avoids PF-00…
dean0x Jul 18, 2026
4e229b3
fix(cli): all-excluded dirs exit non-zero with skip count, --quiet-safe
dean0x Jul 18, 2026
15f2422
docs(cli): fix help text and README accuracy — issues #85/#42/#49/#88
dean0x Jul 18, 2026
8b00ffe
fix(cli): lint --fix contract fixes — JSON envelope, stdin --check, q…
dean0x Jul 18, 2026
6dc1b36
fix(core): pub STRING_SOURCE_MAP_LABEL + re-export from public API (i…
dean0x Jul 18, 2026
fd8f8ec
fix(core): strip tombstone comments — leave the end-state not the tra…
dean0x Jul 18, 2026
7765668
fix(cli): ESC injection guard, STRING_SOURCE_MAP_LABEL consumers, dea…
dean0x Jul 18, 2026
6aeaf56
fix(cli): sanitize ESC bytes at main() last-resort boundary and run_c…
dean0x Jul 18, 2026
aee86e3
fix(cli): guard ALL directory-mode error render sites via eprint_erro…
dean0x Jul 18, 2026
a7ef84f
fix(cli): SEC-3 guard — dotdot-escaping sources[] paths fall back to …
dean0x Jul 18, 2026
d31e967
fix(cli): route fmt through shared atomic_write_file — eliminates tru…
dean0x Jul 18, 2026
f00b1f6
feat(security): source-map path-disclosure choke-point — Steps 0–5 (A…
dean0x Jul 18, 2026
e661002
feat(cli): migrate source-map path handling to core choke-point — Ste…
dean0x Jul 18, 2026
168a58a
fix(cli): move atomic_write_file to output.rs — mode mask, sync_all, …
dean0x Jul 18, 2026
4d3ff6b
fix(source-map): close CF-SM1 — WASM filename forwarding + macOS syml…
dean0x Jul 19, 2026
9a85d5c
fix(source-map): close the root=None guard gap; make source_map_base …
dean0x Jul 19, 2026
f0da094
test: strengthen five test gaps found by alignment review
dean0x Jul 19, 2026
ce2cf69
ci(js): build mds-cli + install Python binding in JS job (avoids PF-007)
dean0x Jul 19, 2026
f2ab64b
fix(test): import mdscript from site-packages in CF-SM2 Python leg
dean0x Jul 19, 2026
c034fd1
fix(source-path): strip verbatim UNC prefix from root in path_to_unif…
dean0x Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,40 @@ jobs:
- name: Build native addon (host)
working-directory: crates/mds-napi
run: npx napi build --platform --release --no-js
# Build the unsuffixed mds-napi.node that __test__/index.spec.mjs loads
# directly via require('../mds-napi.node'). cargo is a cache hit here;
# napi just renames the already-built artifact without recompiling.
- name: Build native addon (unsuffixed, napi spec test)
run: npm run build:native -w @mdscript/mds-napi
# Build the WASM pkg so the WASM fallback (and wasm-backend tests) work.
- uses: ./.github/actions/setup-wasm
- name: Build WASM (nodejs)
run: wasm-pack build crates/mds-wasm --target nodejs --out-dir pkg
- name: Build TS packages
run: npm run build --workspaces --if-present
# Build the mds CLI binary so CF-SM2 can invoke it as the third parity
# surface. target/debug/mds is auto-discovered by findMdsCli(); no env
# var needed. The Rust toolchain + cache are already set up above, so
# this is an incremental build sharing the dep graph with the napi addon
# (avoids PF-007: CLI surface must actually run in CI, not skip silently).
- name: Build mds CLI (CF-SM2 parity producer)
run: cargo build -p mds-cli
# Install Python + the mdscript binding so CF-SM2 can compare the Python
# output as the fourth parity surface (avoids PF-007). pip uses the
# maturin PEP 517 build backend declared in crates/mds-python/pyproject.toml;
# no pre-installed maturin needed. MDS_PYTHON_BIN is set to the exact
# executable that owns the installed module so findPythonForMdscript()
# picks it up cross-platform (bin/ on Unix, Scripts/ on Windows).
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python binding (CF-SM2 parity surface)
run: python -m pip install ./crates/mds-python
- name: Export MDS_PYTHON_BIN
shell: bash
run: |
PY=$(python -c "import sys; print(sys.executable)")
echo "MDS_PYTHON_BIN=$PY" >> "$GITHUB_ENV"
- name: Test
run: npm test --workspaces --if-present

Expand Down
220 changes: 199 additions & 21 deletions CHANGELOG.md

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ mds lint [FILE|DIR] [OPTIONS] Static-analysis lint (9 rules; --fix, --format j
mds init [FILENAME] Create a starter MDS file

Global options:
-q, --quiet Suppress status messages (applies to all commands)
-q, --quiet Suppress status and diagnostic output; errors always print; exit codes unaffected

Build/Watch options:
-o, --output <PATH> Output file, or "-" for stdout (build and single-file watch only;
Expand All @@ -90,7 +90,7 @@ Build/Watch options:
--vars <FILE> JSON file with variable overrides (reloaded each rebuild)
--set KEY=VALUE Set a single variable (repeatable); value coerced to number/bool/null/array when possible
--set-string KEY=VALUE Set a single variable as a string, bypassing type coercion (repeatable)
--source-map Write a Source Map v3 sidecar (<output>.md.map); output is
--source-map Write a Source Map v3 sidecar (<output-file>.map, e.g. -o out.md → out.md.map); output is
byte-identical to a no-flag build. Ignored for messages-mode
templates (no renderable output). See ⚠ privacy note below.
--inline Embed the source map as a sourceMappingURL data-URI comment
Expand Down Expand Up @@ -124,7 +124,7 @@ Exit codes:
3 Resource limit exceeded
```

**Directory mode** (`mds build <dir>` / `mds check <dir>`): every non-partial `.mds` file under the directory is compiled. `_`-prefixed files are partials — tracked as dependencies but never emitted to their own output. Output mirrors the source subtree (e.g. `src/a/b/foo.mds` → `dist/a/b/foo.md`). Symlinks are rejected. Errors are per-file and do not abort the run; a summary is printed and the exit code is non-zero if any file fails. Stale output files (compiled outputs with no corresponding source) are cleaned up automatically. The output extension is intrinsic: `.md` for Markdown templates, `.json` for templates with `@message` blocks.
**Directory mode** (`mds build <dir>` / `mds check <dir>`): every non-partial `.mds` file under the directory is compiled, with two automatic exclusions: directories whose name starts with `.` (e.g. `.git`, `.github`, `.claude`, `.cursor`) and `node_modules` are skipped during traversal. `_`-prefixed files are partials — tracked as dependencies but never emitted to their own output. Output mirrors the source subtree (e.g. `src/a/b/foo.mds` → `dist/a/b/foo.md`). Symlinks are rejected. Errors are per-file and do not abort the run; a summary is printed and the exit code is non-zero if any file fails. If **every** `.mds` file is under a default-excluded directory, the command exits non-zero and prints a diagnostic carrying the skip count — even under `--quiet` — because this is the silent CI green-pass failure mode for prompt-template libraries stored under `.github/prompts/`, `.claude/`, or `.cursor/rules/`. A genuinely empty directory (no `.mds` files anywhere) still exits 0 with a "No .mds files found" message. Stale output files (compiled outputs with no corresponding source) are cleaned up automatically. The output extension is intrinsic: `.md` for Markdown templates, `.json` for templates with `@message` blocks.

`mds fmt <dir>` follows the same directory-mode conventions (recursive, symlinks rejected, continue-on-error, non-zero exit summary) with one deliberate difference: it formats `_`-prefixed **partials too** — formatting rewrites source, not compiled output, and a partial's source is just as much a candidate for reformatting as any other file.

Expand Down Expand Up @@ -179,7 +179,7 @@ mds fmt . # format every .mds file recursively (partials i
mds fmt --check template.mds # exit 1 if the file would change; never writes — for CI
mds fmt --diff template.mds # print a unified diff of pending changes; never writes
mds fmt --check --diff . # show diffs for every file that would change; exit 1 if any would
echo 'Hello {name}!' | mds fmt - # format from stdin, write to stdout; creates no file
printf '@if ready: \nGo\n@end\n' | mds fmt - # format from stdin, write to stdout; creates no file
```

What it normalizes:
Expand All @@ -199,7 +199,7 @@ What it deliberately leaves untouched:

Directory mode formats every `.mds` file recursively, **including `_`-prefixed partials**,
continuing past per-file errors and printing a summary
(`N formatted, M unchanged, K failed`, or `N would reformat, K failed` under `--check`). A file
(`N formatted, M unchanged, K failed`, or `N would reformat, M unchanged, K failed` under `--check`). A file
is only written (and its mtime touched) when its content actually changes. Status lines and
summaries go to stderr; `--diff` output and stdin filter-mode content go to stdout; `--quiet`
suppresses status but never errors. Reads a `fmt` section from `mds.json`
Expand All @@ -215,7 +215,7 @@ mds lint template.mds # lint a single file
mds lint . # lint all .mds files recursively (partials included)
mds lint --fix template.mds # auto-fix fixable issues in place
mds lint --format json . # machine-readable JSON output (stdout)
mds lint --quiet template.mds # suppress warnings; exit 2 on errors only
mds lint --quiet template.mds # suppress output; exits 1 on warnings, 2 on errors
```

Rules (configure via `mds.json` `lint.rules`; severities differ per rule):
Expand All @@ -232,7 +232,7 @@ Rules (configure via `mds.json` `lint.rules`; severities differ per rule):
| `duplicate-import` | **error** | Same file imported more than once (auto-fixable) |
| `duplicate-export` | **error** | Same export name defined more than once (auto-fixable) |

Exit codes: `0` = clean, `1` = warnings only, `2` = errors or analysis failure, `3` = resource limit.
Exit codes: `0` = clean, `1` = warnings only, `2` = errors or analysis failure, `3` = resource limit. With `--quiet`, output is suppressed but exit codes are unaffected.
JSON output shape: `{"files":[{"file":"…","diagnostics":[…]}],"truncated":false,"version":1}`.

## Bundler Integration
Expand Down
Loading
Loading