Pure Dart port of DMTools (Java/GraalJS → Dart/QuickJS). Read GOAL.md first —
it is the spec. This file is the operating manual: rules, commands, layout.
- crap4dart is law. CRAP threshold 8.0 (
crap4dart.yaml), tool pinned to 0.9.5 in CI (and locally; keep both in sync). Pre-commit hook runscrap4dart check --staged; CI runscheck --all+analyzeon every push. Never commit on red, never weaken a gate to make code pass. Known exemptions live incrap4dart.yamlwith comments (Java-parity client classes → class_size warning; testrail_create_case* Java-parity @MCPParam signatures → method_size max_params override). - Signature parity with Java DMTools. Config JSON keys, tool names
(snake_case), env variables, CLI commands — identical to the Java version.
A config or
dmtools.envthat works with Java must work unchanged here. When behavior is ambiguous, the Java source is the spec; record the decision in the commit message. - No JVM, no GraalVM. Dart only. JS runtime is QuickJS via
dart:ffiwith synchronous host calls (not theflutter_jsplugin), consumed from thequickjs_runtimepackage (github.com/IstiN/quickjs_runtime, git dep inpubspec.yaml) — never vendored here. Build the native library withmake native(orbash "$QUICKJS_PKG/tool/build_quickjs.sh"); the .so is built inside the package checkout and found via.dart_tool/package_config.json(override withJSR_QUICKJS_LIB). - Thin
bin/. CI coverage is collected with--report-on lib, sobin/is outside LCOV. All logic lives inlib/;bin/only parses argv and delegates. - Tests ship with code. Coverage gate is 80% (
gates.test_coverage). Test code is held to the same bar (sources: [lib, bin, test]). - Never commit secrets.
dmtools.env/dmtools-local.envare git-ignored; keep them that way. Credentials in tests come only from the standard config resolution chain (see GOAL.md → Integration testing strategy).
| Path | What |
|---|---|
GOAL.md |
The spec: mission, constraints, audit facts, phases 0–5, test strategy |
crap4dart.yaml |
Quality gate config (threshold 8.0, sources lib/bin/test) |
.github/workflows/quality.yml |
CI: format → analyze → test+coverage → crap4dart |
bin/dmtools.dart |
CLI entry (stub; full JobRunner surface = Phase 2) |
install.sh |
One-line installer: prebuilt AOT binaries from GitHub Releases (see release-cli.yml / install-test.yml) |
lib/ |
All implementation code |
test/ |
dart test suite (L1 unit; L2 contract fixtures later) |
Java reference implementation (read-only, the spec for behavior):
https://github.com/epam/dm.ai — clone it fresh (always the latest upstream);
key files are named per phase in GOAL.md (e.g. PropertyReader.java,
JobRunner.java, JobJavaScriptBridge.java, cliagent/CliAgent.java).
Agent scripts and the JS test suite: https://github.com/IstiN/dmtools-agents.
JS runtime (jsr) extensions go to https://github.com/IstiN/flutter_js_widget_runtime
via fork + PR — never vendored here (see GOAL.md).
export PATH="$HOME/.pub-cache/bin:$PATH" # crap4dart lives here; without it the
# pre-commit hook SKIPS checks silently
dart pub get
make native # build the QuickJS .so (quickjs_runtime pkg)
dart format . # CI fails on unformatted code
dart analyze
dart test --coverage=coverage
dart pub global run coverage:format_coverage \
--lcov --in coverage --out coverage/lcov.info --report-on lib
crap4dart check --all # gates (fast, no network)
crap4dart analyze # CRAP scores, threshold 8.0If crap4dart is missing: dart pub global activate crap4dart.
- Small, focused commits; message states what and why (and the Java file that served as spec, when porting behavior).
- The hook runs only on staged files — CI runs everything, so run the full local loop above before pushing.
- CI red = stop and fix; no merging on red.
git commit --no-verifyis a code smell; if you use it, say why in the message.
All 5 phases implemented. 387 canonical MCP tools across 17 integrations
(no dmtools-agents tool call remains uncovered; the Java↔Dart gap snapshot in
test/fixtures/java_mcp_tool_gaps.txt tracks the remaining non-agent names).
QuickJS runtime via dart:ffi with sync callbacks — dmtools-agents suite passes
unmodified (751 tests green). CliAgent lifecycle fully
ported (incl. timer/error/line JS actions). dmtools run, list, doctor,
--version, --help, --list-jobs all functional.
Sync dispatch (executeToolViaJava): SyncToolDispatcher routes by
prefix to self-contained per-integration classes in lib/src/js/sync_tools/
(jira, github, gitlab, ado, confluence, bitrise, jenkins) plus per-provider
<provider>_ai_chat — HTTP via curl subprocess inside NativeCallable
callbacks, file_*/cli_* via SyncToolDispatcher.nonHttpHandler. The
JS bridge (JobJavaScriptBridge parity): CommonJS require loader
(require_loader.dart), JSRunner context (ticket/response/initiator/
inputJql/metadata), inline/URL/file jsPath, action() contract, JS-visible
host-fn validation errors (__jsError sentinel), alias tool wrappers.
Remaining work (diminishing returns, in priority order):
- InstructionProcessor
- Integration test layer (L2 contract tests, L3 live integration)
- Re-extract
test/fixtures/java_mcp_tool_names.txtwhen Java gains new @MCPTools and port what the gap snapshot then reports
Phase checkboxes live in GOAL.md — tick them as you complete items and keep the file current when a decision changes the plan.
| Layer | Location | Runs where |
|---|---|---|
| L1 unit | test/, mocked |
pre-commit + CI quality.yml |
| L2 contract | test/ with recorded Java fixtures |
CI quality.yml |
| L3 live integration | test/ @Tags(['integration']), creds via env/dmtools.env |
nightly + manual, excluded by default via dart_test.yaml |
| L4 agents suite | external agents/js/unit-tests/run_all.json |
dedicated CI job from Phase 4 |
The agents suite (dmtools run agents/js/unit-tests/run_all.json under the Dart
runtime, unmodified) is the primary acceptance gate — see GOAL.md.
dart formatdefaults; public API documented (public_docs gate enforces it).- Keep methods small (complexity ≤ 10, body ≤ 60 lines, params ≤ 6 — the gates enforce this; design around it rather than fighting it).
- Generated code (
*.g.dart,*.freezed.dart) is gate-excluded by default — don't hand-edit, regenerate.
The ai-teammate machine resumes one fa session per ticket instead of starting
cold on every run. Sessions are named deterministically per repo:GH-<n>:group —
bug/story/rework share the dev-write conversation (a rework continues the
ticket's dev thread), review has its own dev-review. The session directory
(.dmtools/fa-sessions) travels across CI runs on a dedicated per-issue branch
fa-sess/gh-<n>: restored before the run, force-pushed after it, never merged.
For live debugging, FA_LOG_FILE (fa --log-file) captures the full
untruncated agent transcript, uploaded as the fa-trace-* run artifact;
dmtools also streams the agent's output live into the step log via stderr
mirroring (gh-50). Wiring: .github/workflows/ai-teammate.yml.