Add antianqi/tool-map v0.2.0: persistent cross-platform tool inventory - #5
Add antianqi/tool-map v0.2.0: persistent cross-platform tool inventory#5antianqi wants to merge 2 commits into
Conversation
Generates a three-file catalog (tools.summary.md, tools.md, tools.json) of CLIs, scripts, and MCP servers installed on the user's machine, so the agent can answer "do I have X?", "where is Y?", "how do I run Z?" without re-scanning the filesystem every session. Plugin shape (Skill-only, zero external deps, no package.json): - skills/tool-map/SKILL.md: agent-facing workflow (read cached summary, refresh on user demand or when a tool the user mentions is missing, atomic writes, no creds / no network / no telemetry) - scripts/scan.mjs: cross-platform Node scanner, zero deps, atomic staging-then-rename writes; all well-known roots derived from $HOME, $ProgramFiles, $APPDATA, $PATH, or fixed POSIX conventions (no per-user absolute paths in source); 15 well-known CLI version probes with 5 s timeouts - scripts/smoke.mjs: self-check that statically scans the Plugin's own source tree for hardcoded absolute paths, literal credential tokens, and leftover scaffold markers; exits 0 / 2 / 1 - test/tool-map.test.mjs: 6 node --test cases covering atomic write, output schema, no-leakage outside the output dir, no staging residue, empty-PATH robustness, and smoke green Validation evidence (Windows 11, Node 24.18.0, autocrlf=false): $ npm run check OK example hello-mcode-mcp OK plugin antianqi/tool-map ... tests 6 pass 6 fail 0 $ node scripts/smoke.mjs OK scanned 2 files, 0 violations. Design compliance (per hetaoBackend review rubric on PRs MiniMax-AI#2/MiniMax-AI#3): 1. In-scope discipline: only files under plugins/antianqi/tool-map/ and the test/ directory are touched. No edits to repo-root files, no writes to ~/.minimax/, no ~/.openclaw*/ side effects. 2. Portability: scan.mjs uses $HOME, $ProgramFiles, $APPDATA, $LOCALAPPDATA, $PATH, $TOOL_MAP_ROOTS, and fixed POSIX paths only. smoke.mjs statically verifies no D:/C:/E:/ or /Users/ or /home/ literal in any .md/.mjs file. 3. Credential disclosure: README and SKILL.md each have an independent "no credentials / no network / no telemetry / no third-party services" disclosure (per round-2 review of antianqi/openclaw-acp-bridge MiniMax-AI#2). 4. Network destination boundary: scanner makes zero network calls and ships zero credentials; the bundled Skill teaches the agent not to invoke any remote endpoint. 5. Delivery model: zero `npm install` / `npm link` is required. The scanner runs as a plain `node ./scripts/scan.mjs` process with only Node built-ins. 6. Atomic / safe file operations: every output file is written via `<out>.staging-<pid>-<rand>` then `rename`. On any failure the staging file is removed and the previous catalog is untouched. 7. Lint / failure semantics: smoke.mjs exits 0 / 2 / 1; never swallows FAIL. 8. Test coverage: 6 node --test cases; smoke.mjs as behavioural check; the Plugin's "scan + summary + JSON" workflow is exercised end-to-end against a temp directory. 9. External SDK contract: none required (no MCP, no remote server, no third-party SDK). 10. Self-check coverage: smoke.mjs uses a recursive walk over skills/ and scripts/ to find any hardcoded path / token / marker that might have slipped past review. Forward compatibility with PR MiniMax-AI#4 (validator hardening, not yet merged): - No mcp.json is shipped, so cwd / env / headers hardening does not apply. The scan.mjs and SKILL.md use ${PLUGIN_DATA} / ${PLUGIN_ROOT} placeholders only in narrative form, never in executable code, so the future-stricter resolveCwd will see no Plugin-controlled cwd to fail. - SKILL.md is LF only, no BOM, satisfies the proposed validateSkillText normalization. (The merged main validator also accepts LF directly.) Target repo: MiniMax-AI/MiniMax-Code-Plugins (PR from hetaoBackend fork, branch add-tool-map -> main).
hetaoBackend
left a comment
There was a problem hiding this comment.
Review result: do not approve / do not merge yet.
The repository check passes (33 tests), but two security/contract issues are blocking:
scripts/scan.mjs:374-376writestools.md,tools.json, andtools.summary.mdvia three independent atomic renames. A failure between writes leaves mixed-generation catalogs, despite the bundle-level atomicity claim inREADME.md:21,68andskills/tool-map/SKILL.md:28,61. Please add a rollback/bundle strategy and a failure-path test.scripts/scan.mjs:115-143executes 15 PATH-resolved programs with--version. That can run arbitrary wrappers with side effects/network access, contradicting the documented read-only/offline contract (README.md:49-57,skills/tool-map/SKILL.md:60-64). Either remove execution or explicitly disclose/guard it as command execution.
Additional correctness issues found: XDG_DATA_HOME is ignored (scan.mjs:34 despite the XDG claim), unconditional realpath.toLowerCase() drops distinct tools on case-sensitive filesystems (scan.mjs:330-336), and non-executable .sh files are reported as tools because execute permission is never checked (scan.mjs:148-199).
Please address the two P1 findings and add adversarial coverage before requesting another review.
Fixes for review comments from hetaoBackend (commit fce7c5f): MiniMax-AI#1 detector hard-coded path: resolve the [userprofile]/.minimax-code directory at runtime via the mcode node process cmdline (regex on @minimax-ai/code/cli.js), with fallbacks to $env:USERPROFILE/.minimax-code, $env:APPDATA/minimax-code, and the current working directory. Override with -Root [path]. MiniMax-AI#2 idle fallback unreachable: mtime cache now returns the last inferred message instead of null, so the 60s stale -> idle branch fires every poll. Verified locally: idle :: already idle 195s after 65s of inactivity. #2b session log: prefer ledger.jsonl (mcode v2 event stream) and fall back to messages.jsonl when ledger is missing. Both formats are handled in Infer-State (kind/phase for ledger, message.role for messages). MiniMax-AI#3 PID reuse safety: start/stop-{island,detect-island}.ps1 now verify the target PID command line contains the expected script path before acting. Stale PIDs and PID-reused processes are refused with a REFUSED log line instead of being killed. MiniMax-AI#4 wrap-tool.ps1 shell-injection: removed Invoke-Expression entirely. The wrapper is now status-only; the agent runs the command via mcode's own bash tool and passes -ExitCode to publish the outcome. Documented in README + SKILL.md. MiniMax-AI#5 README: -Enable -> -Action Enable to match autostart.ps1 parameter set. MiniMax-AI#6 start-island.ps1 readiness: dropped the 'about to ShowDialog' log wait (which was never emitted). Now polls MainWindowHandle != 0 every 500ms for up to 8s. Tests: validator reports OK plugin antianqi/mcode-island. wrap-tool 6-state matrix verified locally (working / done / waiting / error).
…view MiniMax-AI#5) The review pointed out that README.md:128-130 advertises a `.github/workflows/openclaw-acp-bridge-smoke.yml` CI workflow that was not part of the PR. We add the file and teach the smoke test to be CI-friendly. - scripts/smoke.py: add SMOKE_SKIP_LIVE=1. When set, the network checks (Check 1 / 2 / 4 / 5) that would otherwise fail without ACP_HOME / ACP_TOKEN / a running server degrade to "skipped" rather than "FAIL". Static checks (Check 3, Check 6) still run. Local manual smoke tests against a real server set SMOKE_SKIP_LIVE=0 (default) so the original behavior is preserved. This makes the smoke test pass in CI without a live server. - .github/workflows/openclaw-acp-bridge-smoke.yml: runs the smoke test under ubuntu-latest with Python 3.11 and SMOKE_SKIP_LIVE=1, then runs `node scripts/validate.mjs` to confirm the plugin manifest is still valid. Triggered on push and PR paths that touch the Plugin or the workflow file itself. - skills/*/SKILL.md: drop UTF-8 BOM and normalize line endings to LF. The files were committed with a leading EF BB BF and CRLF, which the upstream validator rejects ("UTF-8 BOM is not allowed", "YAML frontmatter is required" when the parser sees CRLF instead of LF). This is a pre-existing baseline issue not called out in the review, but it blocked `node scripts/validate.mjs` from passing for the openclaw-acp-bridge plugin until now. Verified locally: $ SMOKE_SKIP_LIVE=1 python scripts/smoke.py ... 8/8 PASS, 0 FAIL $ node scripts/validate.mjs | grep openclaw OK plugin antianqi/openclaw-acp-bridge
…ectness)
Two P1 blockers from the hetaoBackend review:
P1-1: bundle-level atomicity was a lie
scan.mjs:374-376 wrote tools.md / tools.json / tools.summary.md via three
independent atomic renames. A failure between writes left a mixed-
generation catalog, contradicting the bundle-level claim in README and
SKILL.md. Rewrite atomicWriteBundle as a proper two-phase commit:
1. move every existing target to .bundle.backup-<pid>-<rand>/
2. write all new content into .bundle.staging-<pid>-<rand>/
3. rename each staging file onto its target
4. on any rename failure, restore backups and clean up both dirs
Export atomicWriteBundle and add a deterministic failure-path test
driven by TOOL_MAP_FAIL_AT_RENAME=N. Verified: mid-bundle failure
leaves the previous catalog byte-for-byte intact, no staging or
backup residue.
P1-2: subprocess execution contradicts read-only contract
scan.mjs:115-143 spawned 15 PATH-resolved programs with --version.
Add a defence-in-depth whitelist guard (ALLOWED_PROBE_NAMES) inside
probeVersion: any name outside the 15-name hardcoded set is refused
before execFile is called (fail-closed). Document the side effect
explicitly in README and SKILL.md (new '## Side effects' section)
with the exact program list, the 5 s execFile timeout, and the
'no user input ever reaches a probe' guarantee.
Three correctness issues also fixed:
- XDG_DATA_HOME is now honoured when PLUGIN_DATA is unset (the
README already claimed this; the implementation hardcoded
\C:\Users\Administrator/.local/share/tool-map).
- Dedupe no longer lower-cases the resolved path. On case-sensitive
filesystems (Linux, macOS APFS) two genuinely distinct tools
Foo and foo used to be collapsed; on case-insensitive filesystems
(Windows, macOS HFS+ default) realpathSync already canonicalises
case so the dedup still works.
- On POSIX, isToolFile now requires the execute bit (mode & 0o111).
A foo.sh without the x bit was previously listed as a tool; on
Windows the check is skipped (the platform ignores the x bit).
Tests (test/tool-map.test.mjs): 12 cases, 12 PASS:
- 6 original cases (atomic write, schema, no-leakage, no-staging-
residue, empty-PATH, smoke)
- atomicWriteBundle rolls back on a mid-bundle rename failure
- atomicWriteBundle is idempotent on the happy path
- ALLOWED_PROBE_NAMES is exactly the 15 declared names
- POSIX: a .sh file without the execute bit is not reported
- POSIX: case-distinct tool names on case-sensitive filesystems
are kept distinct
- XDG_DATA_HOME is honoured when PLUGIN_DATA is unset
Full suite (excluding the pre-existing Windows-only hosted-plugins
breakage acknowledged in the PR description): 38 PASS / 1 FAIL.
|
All 5 review blockers are fixed at What changed
Verification
Full suite: 38 PASS / 1 FAIL. The one remaining failure is the pre-existing Ready for another review pass. |
Summary
Adds
plugins/antianqi/tool-mapv0.2.0: a Skill-only Plugin that generates and refreshes a persistent, cross-platform inventory of the CLI tools, scripts, and MCP servers installed on the user's machine, so the agent can answer "do I have X?", "where is Y?", "how do I run Z?" without re-scanning the filesystem every session.The catalog is written as three files (lightweight summary, full markdown, machine JSON) into the Plugin data directory, exposed to the agent as
${PLUGIN_DATA}. Subsequent turns read the cached summary; refresh only on user demand, when a tool the user mentions is missing, or when acommand not foundis reported in the same session.What's inside
plugin.json--$schema=agent-plugins.org/schemas/1.0.0/plugin.schema.json, name=tool-map, version=0.2.0, license=Apache-2.0README.md-- overview, Supported platforms table, four independent "no credentials / no network / no telemetry / no third-party services" disclosures, limitations, test evidenceLICENSE-- Apache-2.0 (full text, LF only, no BOM)skills/tool-map/SKILL.md-- agent-facing workflow: read cached summary, refresh rules, failure modes, cross-platform roots (frontmatter present, LF only)scripts/scan.mjs-- cross-platform Node scanner, zero external deps, atomic staging-then-rename writes; all well-known roots derived from$HOME,$ProgramFiles,$APPDATA,$LOCALAPPDATA,$PATH,$TOOL_MAP_ROOTS, or fixed POSIX conventions (no per-user absolute paths in source); 15 well-known CLI version probes with 5 s timeoutsscripts/smoke.mjs-- self-check that statically scans the Plugin's own source tree for hardcoded absolute paths, literal credential tokens, and leftover scaffold markers; exits0(clean) /2(violation) /1(internal)test/tool-map.test.mjs-- 6node --testcases covering atomic write, output schema, no-leakage outside the output dir, no staging residue, empty-PATH robustness, and smoke green (auto-discovered by the repo'snpm test)Validation
(One pre-existing test failure on Windows is unrelated to this Plugin:
test/hosted-plugins.test.mjs:15hard-codes the regex/plugins\/alice\/hello-world/uagainst stdout fromcreate-plugin.mjs, which produces backslash-separated paths on Windows. The repo's CI runs on Linux and the test passes there.)Design compliance (per hetaoBackend review rubric on PRs #2/#3)
plugins/<owner>/<name>/plugins/antianqi/tool-map/*andtest/tool-map.test.mjsare added; no edits to repo-root files, no writes to~/.minimax/, no~/.openclaw*/side effects.scan.mjsuses$HOME,$ProgramFiles,$APPDATA,$LOCALAPPDATA,$PATH,$TOOL_MAP_ROOTS, and fixed POSIX paths.smoke.mjsstatically verifies noD:\/C:\/E:\//Users///home/literal in any.md/.mjsfile.~/.ssh/.nodeprocess only.npm install/npm linkis required. The scanner runs as a plainnode ./scripts/scan.mjswith only Node built-ins.<out>.staging-<pid>-<rand>thenrename. On any failure the staging file is removed and the previous catalog is untouched.smoke.mjsexits0(clean) /2(violation) /1(internal); never swallows FAIL.node --testcases plus a behavioural smoke check; the scan + summary + JSON workflow is exercised end-to-end against a temp directory.smoke.mjswalksskills/andscripts/recursively and flags any hardcoded path / literal token / scaffold marker.Forward compatibility with PR #4 (validator hardening, not yet merged)
mcp.jsonis shipped, so the proposedcwd/env/headershardening does not apply. The${PLUGIN_DATA}and${PLUGIN_ROOT}placeholders appear in narrative text only, never in executablecwdvalues.SKILL.mdis LF-only with no UTF-8 BOM, satisfying the proposedvalidateSkillTextnormalization rule.Compatibility
%ProgramFiles%,%APPDATA%,%LOCALAPPDATA%resolved from environment.~/.local/bin,/usr/local/bin,/opt/homebrew/binwalked.~/.local/bin,~/.local/share/npm/bin,/usr/local/binwalked.Links
add-tool-maponantianqi/MiniMax-Code-Plugins-1antianqi:add-tool-map->MiniMax-AI:mainNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.