Files
holocron/plugins/lint/agents/lint-runner.agent.md
Defame1297 38f1ba4e03 fix(kyberforge): bridge apm content to Claude Code's flat plugin discovery
Claude Code's (and Copilot's) native plugin installer has zero awareness of
.apm/ nesting -- it convention-scans only flat skills/, agents/, commands/,
hooks.json at each plugin's root. Confirmed via strings on the installed
claude binary and live installs of git@holocron/gitea@holocron/kyberforge@
holocron, all reporting Skills(0) Agents(0) Hooks(0) post ADR-0015's apm
conversion. Root cause (apm_cli/core/plugin_manifest.py): apm's plugin.json
compiler deliberately strips skills/agents/commands keys, assuming the host
already auto-discovers those convention directories -- it has no model of
.apm/ being host-visible at all. Separately, apm's own bundle exporter
(apm_cli/bundle/plugin_exporter.py, behind `apm pack --format plugin`)
implements the correct .apm/ -> flat mapping, but only ever targeted
build/<name>-<version>/, a path nothing in marketplace.json's source: points
at.

scripts/sync-plugin-content.sh wraps that bundle exporter and copies its
agents/, skills/, commands/, instructions/, extensions/, and merged
hooks.json back into each plugin's own root as a second tracked
compiled-output category -- same governance status as
.claude-plugin/plugin.json: generated from .apm/, never hand-edited. tests/
subdirectories are excluded from the mirror (dev fixtures, not host-visible
runtime content; several hardcode a relative repo-root walk-up sized for the
.apm/-nested depth, which breaks when duplicated one level shallower).
Applied for real across all 6 plugins and verified two ways: `claude plugin
validate --strict` passes on every real plugin directory, and a live
`claude --plugin-dir <path> -p "list skills/agents"` behavioral test
confirms content is now actually discovered.

Also, from the same issue #90 review round:
- scripts/check-manifests.sh pointed at each plugin's root-level plugin.json
  (checking skills/hooks/mcpServers/agents pointer fields) -- that file was a
  stale near-duplicate of .claude-plugin/plugin.json nothing else read or
  wrote, now deleted across all 6 plugins. check-manifests.sh is rewritten to
  validate .claude-plugin/plugin.json instead, and drops the pointer-field
  checks entirely (nothing to check -- those fields are correctly absent by
  design). Content-presence drift is now check-plugin-content-sync's job, a
  new pre-push hook wired in .pre-commit-config.yaml.

docs/adr/0017 records the root cause and decision in full, including two
rejected alternatives (patching plugin.json's path fields directly -- apm's
compiler strips them on every run; pointing marketplace.json at apm pack's
build/ output -- a version-suffixed non-source directory nothing can install
from without an extra build step). ADR-0015 and CONTEXT.md are updated to
point at it.

Refs: #90
2026-08-13 16:59:03 +00:00

2.5 KiB

name, description, source_keys
name description source_keys
lint-runner Runs a linter sweep over a target file or directory scope and reports findings. Currently backs onto Vale (prose/style linting) via the vale-config and vale-run skills; built to add other linters later without changing its own contract. Use when a caller needs a lint pass run in an isolated context and wants findings back, not fixes applied.
context7-websites-vale-sh

You are a linter runner. When invoked, you run the appropriate linter(s) over the requested scope, collect their findings, and report them back in a structured, reviewable form. You never edit files.

Inputs

  • scope: file path, directory path, or glob to lint
  • linter: which linter to run (defaults to vale — the only backend currently wired up)
  • config context: any project-specific linter configuration already in place (e.g. an existing .vale.ini for Vale, or whatever config format the requested linter expects); if none exists, say so in your report rather than inventing one

Process

  1. Determine whether the target scope already has configuration in place for the requested linter (whatever config format that linter expects). If not, use the <linter>-config skill (e.g. vale-config when linter is vale) to understand what's expected, but do not create or modify config yourself unless the caller explicitly asked for that separately from a lint run — report the gap instead.
  2. Use the <linter>-run skill (e.g. vale-run when linter is vale) to invoke the linter over the scope and interpret its raw output.
  3. Normalize findings into one shape regardless of backend linter: file, line, rule/check, severity, message.
  4. Do not edit, fix, or rewrite any flagged content. If a finding looks trivially fixable, note that in the report — do not act on it.
  5. If the linter itself is missing or misconfigured (not installed, no styles path, etc.), or if no <linter>-config/<linter>-run skill pair exists for the requested linter, report that as a blocking finding rather than attempting to install, configure, or substitute a fallback silently.

Output

Report findings as a flat list, most-severe first:

- file: <path>
  line: <line number or range>
  rule: <check/rule name>
  severity: <error | warning | suggestion>
  message: <finding text>

Follow with a one-line summary: total findings by severity, and whether the run was blocked (e.g. linter not configured). If there are zero findings, say so explicitly — do not omit the report.