// std/rules - run declarative structural rules (the harn-rules engine) from
// Harn. A rule is its TOML source, so an agent can author and run a rule
// inline without recompiling the binary.
/**
* Run a rule and return its matches.
*
* `params` is a dict:
*
* - `rule`: the rule's TOML source (id / language / `[rule]` block / `where`
* / `transform` / `fix` / `safety`).
* - `source` **or** `paths`: inline text (with `language`) or a list of file
* paths to read.
*
* On success `result == "ok"` and `matches` is a list of `{path, text,
* start_row, start_col, captures}` where `captures` maps each metavar name
* to its bound text.
*
* @effects: [host]
* @allocation: heap
* @errors: [backend]
* @api_stability: experimental
* @example: rules_search({rule: src, source: "foo();", language: "typescript"})
*/
pub fn rules_search(params) -> dict {
let payload = hostlib_rules_search(params ?? {}) ?? {}
return payload
.merge(
{ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_search"}},
)
}
/**
* Run a rule in **report-only** mode and return a data table — columnar
* findings (`rows`) plus a metrics `summary` (total / files / per-file
* counts). Never edits anything.
*
* @effects: [host]
* @allocation: heap
* @errors: [backend]
* @api_stability: experimental
* @example: rules_report({rule: src, paths: ["a.ts", "b.ts"]})
*/
pub fn rules_report(params) -> dict {
let payload = hostlib_rules_report(params ?? {}) ?? {}
return payload.merge({provenance: {module: "std/rules", helper: "rules_report"}})
}
/**
* Run a declarative rule and return its **diagnostics** — the lint surface of
* a rule that carries a `message`. On success `result == "ok"` and
* `diagnostics` is a list of `{path, rule_id, message, severity, start_row,
* start_col, end_row, end_col, applicability, fix}` (one per match; `fix` is
* `nil` unless the rule has a `fix` template).
*
* @effects: [host]
* @allocation: heap
* @errors: [backend]
* @api_stability: experimental
* @example: rules_diagnostics({rule: src, source: "foo();", language: "typescript"})
*/
pub fn rules_diagnostics(params) -> dict {
let payload = hostlib_rules_diagnostics(params ?? {}) ?? {}
return payload
.merge(
{ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_diagnostics"}},
)
}
/**
* Run a rule's matcher, then call an **imperative** visitor `on_match(node,
* ctx)` once per match — the escape hatch for logic a declarative rule cannot
* express (compute a message or fix from the captured metavars, filter on
* arbitrary conditions). `params` is `{rule, source|paths, language?,
* on_match}` where `on_match` is `fn(node, ctx)`:
*
* - `node`: `{text, captures, start_row, start_col, end_row, end_col}` —
* `captures` maps each metavar name to its bound text.
* - `ctx`: `{path, language, source, rule_id}` (read-only).
* - returns: `nil`/`false` to skip the match, `true` to flag it with the
* rule's defaults, a `{message, fix, safety, severity}` dict, or a list of
* such dicts (to report several findings on one node).
*
* Read-only — `visit` never writes; apply fixes with `rules_apply` or your own
* write. On success `result == "ok"` and `diagnostics` is a list in the same
* shape `rules_diagnostics` emits, so an imperative rule and its declarative
* equivalent produce identical diagnostics.
*
* @effects: [host]
* @allocation: heap
* @errors: [backend]
* @api_stability: experimental
* @example: rules_visit({rule: src, source: "f();", language: "typescript", on_match: fn(node, ctx) { return {message: "call"} }})
*/
pub fn rules_visit(params) -> dict {
let payload = hostlib_rules_visit(params ?? {}) ?? {}
return payload
.merge(
{ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_visit"}},
)
}
/**
* Apply a codemod rule's `fix`. **Dry-run by default** — pass `dry_run:
* false` to write. Writes only when the rule's `safety` is machine-applicable
* (`format-only` / `behavior-preserving`) or `allow_unsafe: true`. Each file
* entry reports `{path, changed, applied, idempotent, safety, preview}`.
*
* Requires the deterministic-tools gate
* (`hostlib_enable("tools:deterministic")`).
*
* @effects: [host, fs]
* @allocation: heap
* @errors: [backend]
* @api_stability: experimental
* @example: rules_apply({rule: src, paths: ["a.ts"], dry_run: false})
*/
pub fn rules_apply(params) -> dict {
let payload = hostlib_rules_apply(params ?? {}) ?? {}
return payload
.merge(
{ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_apply"}},
)
}