Uh oh!
There was an error while loading. Please reload this page.
feat: add CLI tools backend - #27
Conversation
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis PR introduces a new ChangesCLI Tools Backend Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@caplets/repo-cli/CAPLET.md`:
- Around line 32-34: The CAPLET entry marks package_test as read-only via the
annotations.readOnlyHint flag, which is misleading because "pnpm run test" can
perform writes; remove the readOnlyHint annotation (or set
annotations.readOnlyHint to false) from the package_test/caplet definition so
the test caplet no longer claims read-only behavior and accurately reflects
potential side effects (look for the keys timeoutMs, annotations, readOnlyHint,
and package_test in the CAPLET.md diff).
In `@docs/benchmarks/coding-agent.md`:
- Around line 17-24: The benchmark doc's deterministic section uses 8400 and
2100 while the README uses canonical values 8358 and 2090; update every
occurrence in the "Serialized payload bytes", "Approx. tokens", "Candidate set
before discovery" and the "Deterministic Results" paragraph to use 8358
(serialized top-level bytes) and 2090 (tokens) and adjust any dependent phrasing
(e.g., "from 32090 bytes to 8400 bytes" → "from 32090 bytes to 8358 bytes" and
the token line) so the numbers and any derived percentages match the README's
canonical set.
In `@src/cli-tools.ts`:
- Around line 379-380: The condition that decides whether to treat command as a
path only checks for "/" so Windows-style separators are missed; update the
check in the command resolution branch (the line using isAbsolute(command) ||
command.includes("/")) to also consider backslashes (e.g., isAbsolute(command)
|| command.includes("/") || command.includes("\\") or use a regex like /[\/\\]/)
so that assertExecutable(command) is invoked for Windows-style relative paths as
well.
- Around line 31-40: The check currently evaluates templates with empty input
and fails actions that use runtime $input values; modify the loop over
actionsFor(config) so you detect if cwd or action.command contains "$input" (or
the specific "$input." token) and skip the existence check and
resolveCommandPath for those templated values; keep the existing behavior for
non-templated strings (use interpolateString + existsSync and call
resolveCommandPath), and continue to throw CapletsError("CONFIG_INVALID", ...)
only when a non-templated cwd is missing or when resolveCommandPath fails for a
non-templated command.
In `@src/cli/author.ts`:
- Around line 35-36: The CLI currently always sets include =
parseInclude(options.include) which applies the default group(s) so supplying
--command still yields additive templates; change the handler so that if
options.command is present AND options.include is undefined/empty, override
include to a single-group list representing the requested command template
(e.g., set include = [options.commandTemplateName] or call parseInclude with
that single value) before building the actions record; update the logic around
where include is used (the parseInclude call and the code that builds actions,
referenced by the include variable and options.command/options.include) so
--command produces only the single template group when --include was not
explicitly passed.
- Around line 161-162: The current logic sets annotations.readOnlyHint based on
script !== "build", which incorrectly marks most package scripts as read-only;
change this so readOnlyHint is false by default and only true for explicit,
known read-only scripts (or remove the hint entirely). Update the annotations
assignment that references readOnlyHint and script in the author CLI code to use
a whitelist (e.g., readOnlyScripts = new Set([...]) and set readOnlyHint:
readOnlyScripts.has(script)) or simply set readOnlyHint: false, ensuring the
change touches the annotations object where readOnlyHint is currently computed.
In `@test/cli-tools.test.ts`:
- Around line 41-52: Add a new assertion to the test that verifies the command
is run without a shell by passing a payload containing shell metacharacters
(e.g., characters like ; | && $() or >) into manager.callTool(caplet,
"echo_json", ...) and assert the returned structuredContent.json contains that
exact string unchanged; locate the test case around the existing it("spawns
commands without a shell and returns parsed JSON output") and extend it to call
manager.callTool again with a metacharacter-laden message and expect
result.structuredContent.json.message to strictly equal the input string,
ensuring callTool (and the CliToolsManager invocation) does not perform shell
interpretation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: defc6725-9c4e-412f-87a6-283a88ed3099
📒 Files selected for processing (25)
.changeset/curated-cli-tools.mdREADME.mdcaplets/github-cli/CAPLET.mdcaplets/repo-cli/CAPLET.mddocs/benchmarks/coding-agent.mddocs/plans/2026-05-14-cli-tools-backend.mdschemas/caplet.schema.jsonschemas/caplets-config.schema.jsonsrc/capability-description.mjssrc/caplet-files.tssrc/cli-tools.tssrc/cli.tssrc/cli/author.tssrc/cli/inspection.tssrc/config.tssrc/generated-tool-input-schema.mjssrc/registry.tssrc/runtime.tssrc/tools.tstest/author-cli.test.tstest/cli-tools.test.tstest/config.test.tstest/openapi.test.tstest/registry.test.tstest/runtime.test.ts
Uh oh!
There was an error while loading. Please reload this page.
| - Serialized payload bytes: direct flat MCP 32090, Caplets top-level 8400, 73.8% fewer. | ||
| - Approx. tokens: direct flat MCP 8023, Caplets top-level 2100, 5923 fewer. | ||
| - Candidate set before discovery: direct flat MCP 106, Caplets top-level 3, 103 fewer. | ||
| ## Deterministic Results | ||
| Caplets reduces the initial serialized MCP tool payload by 74.0%, from 32090 bytes to 8358 bytes. It reduces initially visible tools by 97.2%, from 106 direct flat tools to 3 Caplets capability tools, while preserving access to downstream tools through scoped discovery and `call_tool`. | ||
| Caplets reduces the initial serialized MCP tool payload by 73.8%, from 32090 bytes to 8400 bytes. It reduces initially visible tools by 97.2%, from 106 direct flat tools to 3 Caplets capability tools, while preserving access to downstream tools through scoped discovery and `call_tool`. | ||
There was a problem hiding this comment.
Align benchmark figures with README to avoid conflicting product claims.
This file now reports 8400/2100, while README still reports 8358/2090 for the same deterministic benchmark. Please keep one canonical precision/number set across both docs.
Also applies to: 41-41
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/benchmarks/coding-agent.md` around lines 17 - 24, The benchmark doc's
deterministic section uses 8400 and 2100 while the README uses canonical values
8358 and 2090; update every occurrence in the "Serialized payload bytes",
"Approx. tokens", "Candidate set before discovery" and the "Deterministic
Results" paragraph to use 8358 (serialized top-level bytes) and 2090 (tokens)
and adjust any dependent phrasing (e.g., "from 32090 bytes to 8400 bytes" →
"from 32090 bytes to 8358 bytes" and the token line) so the numbers and any
derived percentages match the README's canonical set.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
| Filename | Overview |
|---|---|
| src/cli-tools.ts | Core CLI tools backend: shell-free process spawning with input interpolation, JSON output parsing, timeout/byte-limit guards, and clean abort handling. Previous review findings all correctly addressed. |
| src/cli/author.ts | CLI authoring helper that generates cliTools Caplet manifests. package.json is read and parsed without a try/catch, so a malformed package.json produces an uncontextualized SyntaxError. |
| src/config.ts | Adds full cliTools schema, Zod validation, duplicate-ID detection, path normalization, and project-config rejection. Well-integrated with the existing backend pattern. |
| src/caplet-files.ts | Extends Caplet file parsing to support the cliTools backend with schema, relative cwd normalization, and correct backend dispatch. |
| src/runtime.ts | Integrates CliToolsManager into CapletsRuntime: construction, registry updates, invalidation, and caplet lookup consistent with existing manager wiring. |
| src/tools.ts | Wires CliToolsManager into handleServerTool dispatch, including field selection support for CLI tool results. |
| test/cli-tools.test.ts | Comprehensive tests covering JSON output, non-zero exits with invalid JSON, input validation, byte limits, timeouts, field selection, and duplicate-ID rejection. |
Sequence Diagram
sequenceDiagram
participant Agent as MCP Agent
participant RT as CapletsRuntime
participant TH as handleServerTool
participant CLM as CliToolsManager
participant SC as spawnCommand
Agent->>RT: "call_tool caplet=repo-cli tool=git_status"
RT->>TH: handleTool(serverId, request)
TH->>CLM: callTool(config, git_status, args)
CLM->>CLM: validateInput(action, args)
CLM->>CLM: resolveExecution interpolate $input resolve cwd/env
CLM->>SC: spawnCommand(execution, signal, elapsed)
SC->>SC: "spawn(command, args, shell=false)"
Note over SC: AbortController timeout + maxOutputBytes per chunk
SC-->>CLM: SpawnResult exitCode stdout stderr
CLM->>CLM: "parseStructuredResult parse JSON if output.type=json"
CLM-->>TH: CompatibilityCallToolResult
TH-->>Agent: content structuredContent isError
Reviews (2): Last reviewed commit: "fix: address cli tools review feedback" | Re-trigger Greptile
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
cliToolsas a typed, shell-free Caplets backend for curated CLI actionscaplets author clito generate reviewable CLI Caplet manifests from repo workflows and git/gh/package templatesValidation
lint-stagedpassedpnpm verifypassed via temporary local pnpm shim becausepnpmis not installed on PATH in this shellformat:checklinttypecheckschema:checktest(18 files, 233 tests)benchmark:checkbuildSummary by CodeRabbit
New Features
Documentation
Tests