nichlink-cli 0.1.3

NichLink command-line interface: project scaffolding, Studio, and the MCP bridge
Documentation

nichlink-cli

nichlink-cli is the single entry point for NichLink tooling.

cargo install --git https://github.com/Nichtigott/nichlink nichlink-cli
nichlink new my-app
cd my-app && nichlink studio

Commands

Command Action
nichlink new <name> [--lib] [--path <workspace> | --git <url>] Scaffold a NichLink host project in ./<name>
nichlink check [path] [--json] Run the registration discovery and validation pass without compiling
nichlink build [path] [cargo options] Validate the registration tree, then run cargo build
nichlink explain <node-id | logical/path> [--path <dir>] [--json] Report one node's identity, build scope, pruning state, and the declared cuts that name it
nichlink explain --overlay [--path <dir>] [--json] Render the static overlay projection of every slot and plan (not a live tree)
nichlink grafts [path] [--json] List .nichlink/external-grafts/*/graft.plan, their targets, and whether the host entry declares the slot
nichlink snippets [path] [--editor vscode | nvim | blink | auto] [--stdout] Inject the face-field editor snippets into a project or an editor config
nichlink studio Launch the Studio TUI for the current project
nichlink mcp Run the read-only MCP stdio bridge

Dependency source is detected automatically: a CLI running from a NichLink checkout writes path dependencies; an installed CLI writes Git dependencies (with a version floor, so Cargo resolves crates.io once published). Override with --path or --git.

The cargo-nichlink binary in the same package registers the plugin form: cargo nichlink studio is equivalent to nichlink studio.

The library target (nichlink_cli) holds the command dispatch so other binaries can reuse it.

nichlink check --json

nichlink check owns this contract (cli/src/commands/check.rs). With --json:

  • Success writes exactly one JSON document to stdout and exits 0.
  • Failure writes that same document, carrying every diagnostic, to stdout; the command then returns Err, so the process still exits non-zero (nichlink prints registration check failed (N diagnostic(s); JSON on stdout) to stderr and exits 1).

The document shape comes from BuildDiagnostics::to_json (core/src/registry_core/diagnostic/build.rs):

{"schema":"nichlink.build-diagnostics/1","count":N,"diagnostics":[...]}

Every diagnostic object always carries all eleven keys, in this order: phase, branch, node, source, line, function, field, expected, actual, provider, message. A reader never has to tell "absent" apart from "empty": line is 0 when the diagnostic has no line. The human renderer (render_build_item, same file) omits empty fields instead, so an empty JSON field means "not applicable to this diagnostic", not "missing key". (branch is the exception: the renderer prints branch=<unknown> when it is empty.)

diagnostics follows BuildDiagnostics::iter, the same order the human text uses, and byte-identical duplicates are collapsed, so count is the deduplicated count. phase is a stable machine string. The human renderer maps requirements to requirements / 注册需求, contract to contract / 注册合同, stable-identity to stable identity / 稳定标识, and static-plan to static plan / 静态计划, and passes any other value through unchanged.

Fields by phase

phase Constructed by branch node source line function field expected actual provider message
requirements core requirements::missing always always always always always always always never only when an ancestor provides the capability under a different kind always
contract build_method contracts::check_parent_rule never always always always always always always only for the output-contract mismatch never always
stable-identity build_method validation::collect_stable_names never never always always never always always always never always
parent-macro build_method validation::collect_parent_macro_errors never never always always never always always always never always
static-plan build_method static_plan::collect_static_faces, graft_plan_check::undeclared_plan_errors; core topology::validate_face_topology never never always always 0 never topology checks only missing-parent and no-registry only topology checks only never always
face-cfg build_method static_plan::collect_static_faces never never always always never never never never never always
out-dir build_method check_for never never never always 0 never never never never never always
face-layout build_method validation::unplaced_face_errors (the phase is chosen by discovery::record_unplaced) never never always 0 when the file has no position never never never never never always
face-syntax build_method validation::collect_face_syntax_errors never never always 0 when the parse error has no position never never never never never always
entry build_method entry::application_entry_source, entry::resolve_host_entry_reporting, entry::rejected_entry never never always 0 when the failure is about the package rather than a line never never never never never always
scope build_method scope::from_raw never never never always 0 never never never never never always
graft-entry build_method scope::auto_from_entry_reporting never never never always 0 never never never never never always

One static-plan diagnostic comes from the graft-plan cross-check (build_method/src/graft_plan_check.rs): it points source at the offending .nichlink/external-grafts/<selector>/graft.plan and carries the paste-ready static_graft_plan! clause in message.

The three static-plan topology checks are parent node is missing, parent does not own a registry, and parent cycle detected; all three set field=parent and actual, the first two also set expected, and the cycle check does not. The fourth static-plan diagnostic, parent declaration cannot be resolved (static_plan::collect_static_faces), sets only phase, source, line=0, and message.

简体中文见 README.zh-CN.md。