Skip to content

Add JSON CLI output and publishable ClawHub skill bundle#50

Open
wysaid wants to merge 3 commits intomainfrom
skill_support
Open

Add JSON CLI output and publishable ClawHub skill bundle#50
wysaid wants to merge 3 commits intomainfrom
skill_support

Conversation

@wysaid
Copy link
Owner

@wysaid wysaid commented Mar 21, 2026

Summary

  • add structured JSON output support to the ccap CLI for device listing, device info, and video info
  • add parser and integration tests for the new JSON CLI contract and document the new flags in English and Chinese CLI docs
  • reorganize the agent-facing guidance into a publishable ClawHub/OpenClaw skill bundle under skills/ccap
  • add repository entrypoints and publishing docs so the skill can be published from the skill folder instead of treating .github as the distribution source

Verification

  • ./ccap_cli_args_parser_test --gtest_filter=CLIArgsParserTest.ParsesJsonOutputOptions
  • ./ccap_cli_test --gtest_filter=CCAPCLITest.VideoInfoJson
  • ./ccap_cli_test --gtest_filter=CCAPCLIDeviceTest.ListDevicesJson
  • ./ccap_cli_test --gtest_filter=CCAPCLIDeviceTest.ShowDeviceInfoJson
  • bash /Users/ergozi/work/git/CameraCapture/scripts/format_all.sh

Notes

  • ClawHub/OpenClaw publishable content now lives in skills/ccap/ with a top-level SKILL.md
  • AGENTS.md remains as the repository discovery entry, while .github/skills/ is no longer treated as the publish target

Summary by CodeRabbit

  • New Features

    • Added --json and --schema-version options; CLI can emit structured JSON for device listing, device info, and video metadata.
  • Bug Fix / Behavior

    • When a video file is provided without an explicit action, the CLI now prints video metadata instead of erroring on Linux.
  • Documentation

    • Added agent/skill guidance, publishable-skill conventions, and CLI JSON usage and examples.
  • Tests

    • Added unit and integration tests for JSON option parsing and JSON responses for device/video commands.

Copilot AI review requested due to automatic review settings March 21, 2026 18:10
@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

Walkthrough

Adds agent skill documentation and a publishable ccap skill; introduces --json and --schema-version CLI options, JSON serialization for device listing/device inspection/video metadata, updates related function signatures, docs, and tests.

Changes

Cohort / File(s) Summary
Agent Skill & Publishing Docs
AGENTS.md, skills/README.md, skills/ccap/SKILL.md, skills/ccap/PUBLISHING.md, skills/ccap/references/commands.md, skills/ccap/references/install.md
Add publishable-skill guidance, ccap skill definition, install/publishing procedures, command references, and agent-focused usage guidance.
CLI Option Parsing
cli/args_parser.h, cli/args_parser.cpp
Add CLIOptions.jsonOutput and CLIOptions.schemaVersion; parse --json and --schema-version (error if value missing).
CLI Utilities & Output
cli/ccap_cli_utils.h, cli/ccap_cli_utils.cpp
Add JSON helpers and structured error envelope; implement JSON branches for listDevices, showDeviceInfo, and printVideoInfo; change printVideoInfo signature to accept CLIOptions.
CLI Integration
cli/ccap_cli.cpp
Recompute hasAction earlier; update calls to printVideoInfo to pass CLIOptions; adjust Linux video-file-without-action handling to call printVideoInfo.
Docs (CLI & READMEs)
docs/content/cli.md, docs/content/cli.zh.md, README.md, README.zh-CN.md
Document --json and --schema-version, JSON response envelope and examples; add AI Agent Skill / publish guidance linking to skill docs.
Tests
tests/test_cli_args_parser.cpp, tests/test_ccap_cli.cpp
Add unit tests for parsing --json/--schema-version (including missing value error) and integration tests asserting JSON output shapes for device-list, device-info, and video-info.

Sequence Diagram

sequenceDiagram
    actor Agent as AI Agent
    participant Parser as CLI Parser
    participant Handler as CLI Handler
    participant Device as Device/Video Layer

    Agent->>Parser: run `ccap --list-devices --json`
    Parser->>Parser: parse args -> CLIOptions{jsonOutput=true, schemaVersion}
    Parser->>Handler: invoke command with CLIOptions
    Handler->>Device: query devices / open video
    Device-->>Handler: device list / video metadata or error
    Handler->>Handler: build JSON envelope (schema_version, command, success, data/error)
    Handler->>Agent: emit JSON response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • LeeGoDamn
  • yixy-only

Poem

🐰 I hopped through docs and CLI light,

JSON berries shining bright.
Skills packed neat for agents' cheer,
Cameras speak so clear and near.
🥕📸

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add JSON CLI output and publishable ClawHub skill bundle' accurately summarizes the two main changes: JSON output support for the CLI and the new publishable skill bundle structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch skill_support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds structured JSON output to the ccap CLI (device listing, device info, and video info), expands test coverage for the new JSON contract, and introduces a publishable ClawHub/OpenClaw skill bundle under skills/ccap with repository entrypoints and publishing guidance.

Changes:

  • Add --json + --schema-version parsing and JSON emitters for --list-devices, --device-info, and video info.
  • Add/extend CLI parser + integration tests to exercise JSON output paths.
  • Add skills/ccap bundle (SKILL.md + references + publishing docs) and link it from README + AGENTS.md.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/test_cli_args_parser.cpp Adds a unit test for --json / --schema-version parsing.
tests/test_ccap_cli.cpp Adds integration tests asserting JSON output envelope for devices and video info.
skills/ccap/references/install.md Adds install decision tree notes for the publishable skill.
skills/ccap/references/commands.md Adds agent-oriented command reference emphasizing --json.
skills/ccap/SKILL.md Introduces publishable skill entrypoint and operating rules.
skills/ccap/PUBLISHING.md Documents how to publish the skill folder to ClawHub.
skills/README.md Declares skills/ as the publishable-skill root and sets structural rules.
docs/content/cli.zh.md Documents --json / --schema-version and adds JSON examples (Chinese).
docs/content/cli.md Documents --json / --schema-version and adds JSON examples (English).
cli/ccap_cli_utils.h Updates printVideoInfo signature to accept CLIOptions.
cli/ccap_cli_utils.cpp Implements JSON serialization helpers and JSON output for list/device/video info.
cli/ccap_cli.cpp Wires CLIOptions into printVideoInfo call site.
cli/args_parser.h Adds jsonOutput + schemaVersion to CLIOptions.
cli/args_parser.cpp Adds parsing + usage strings for --json / --schema-version.
README.zh-CN.md Adds entrypoints and publishing guidance for the skill bundle (Chinese).
README.md Adds entrypoints and publishing guidance for the skill bundle (English).
AGENTS.md Adds a repository agent entry directing to skills/ccap.

ccap -i /path/to/video.mp4 --json
```

JSON output currently covers device enumeration, device info, and video metadata. The payload uses a stable top-level envelope with `schema_version`, `command`, `success`, and either `data` or `error`.
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section documents the JSON envelope as containing schema_version, command, success, and either data or error, but the implementation currently adds an extra top-level exit_code on errors. Please update this description to match the actual JSON contract (or adjust the implementation to match the doc).

Suggested change
JSON output currently covers device enumeration, device info, and video metadata. The payload uses a stable top-level envelope with `schema_version`, `command`, `success`, and either `data` or `error`.
JSON output currently covers device enumeration, device info, and video metadata. The payload uses a stable top-level envelope with `schema_version`, `command`, `success`, and either a `data` object on success or an `error` object plus an `exit_code` on failure.

Copilot uses AI. Check for mistakes.
ccap -i /path/to/video.mp4 --json
```

当前 JSON 输出先覆盖设备枚举、设备信息和视频元数据。返回体采用稳定的顶层结构,包含 `schema_version`、`command`、`success`,以及 `data` 或 `error`。
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此处描述 JSON 顶层结构仅包含 schema_versioncommandsuccess,以及 dataerror;但当前实现的错误返回还包含额外的顶层字段 exit_code。请同步更新文档以匹配实际 JSON 合约(或调整实现以匹配文档)。

Suggested change
当前 JSON 输出先覆盖设备枚举、设备信息和视频元数据。返回体采用稳定的顶层结构,包含 `schema_version``command``success`,以及 `data` `error`
当前 JSON 输出先覆盖设备枚举、设备信息和视频元数据。返回体采用稳定的顶层结构,始终包含 `schema_version``command``success` 字段;成功时携带 `data` 字段,错误时携带 `error` 字段,并额外包含顶层字段 `exit_code`

Copilot uses AI. Check for mistakes.
Comment on lines 148 to +150
// If video file specified without action, print video info
if (!opts.videoFilePath.empty() && !hasAction) {
return ccap_cli::printVideoInfo(opts.videoFilePath);
return ccap_cli::printVideoInfo(opts, opts.videoFilePath);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Linux, the early videoFilePath check returns a plain-text error to stderr and exits before calling printVideoInfo. When --json is set, this breaks the new JSON CLI contract for video-info (and any video-file usage). Consider emitting a JSON error when opts.jsonOutput is true, or moving this platform gate into printVideoInfo so JSON output remains consistent.

Copilot uses AI. Check for mistakes.
Comment on lines 41 to +45
* @brief Print video file information
* @param videoPath Path to video file
* @return Exit code (0 on success, 1 on error)
*/
int printVideoInfo(const std::string& videoPath);
int printVideoInfo(const CLIOptions& opts, const std::string& videoPath);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function signature now takes const CLIOptions& opts, but the doc comment still only documents videoPath. Please add an @param opts entry (and clarify which options affect output, e.g., jsonOutput/schemaVersion) so the header stays accurate for callers.

Copilot uses AI. Check for mistakes.
Comment on lines +350 to +352
if (i + 1 < argc) {
opts.schemaVersion = argv[++i];
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--schema-version silently does nothing when provided without a following value. Since the usage string documents a required argument, this should be treated as an error (print message + usage and exit non-zero), similar to how --camera-backend enforces its required value.

Suggested change
if (i + 1 < argc) {
opts.schemaVersion = argv[++i];
}
if (i + 1 >= argc) {
printWindowsBackendOptionErrorAndExit(argv[0], "--schema-version requires a value.");
}
opts.schemaVersion = argv[++i];

Copilot uses AI. Check for mistakes.
Comment on lines +143 to +147
os << ",\"success\":false,\"exit_code\":" << exitCode << ",\"error\":{\"code\":";
writeJsonEscapedString(os, code);
os << ",\"message\":";
writeJsonEscapedString(os, message);
os << "}}";
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON error envelope includes an exit_code top-level field, but the docs describe the stable envelope as only schema_version, command, success, and data/error. Either update the documented contract (and keep it consistent across success/error), or remove/relocate exit_code to avoid surprising strict JSON consumers.

Suggested change
os << ",\"success\":false,\"exit_code\":" << exitCode << ",\"error\":{\"code\":";
writeJsonEscapedString(os, code);
os << ",\"message\":";
writeJsonEscapedString(os, message);
os << "}}";
os << ",\"success\":false,\"error\":{\"code\":";
writeJsonEscapedString(os, code);
os << ",\"message\":";
writeJsonEscapedString(os, message);
os << ",\"exit_code\":" << exitCode << "}}";

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
tests/test_ccap_cli.cpp (1)

388-417: Consider extracting a shared JSON envelope assertion helper.

schema_version / command / success checks are repeated across new tests; a helper would reduce drift and simplify future schema updates.

Also applies to: 753-768

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_ccap_cli.cpp` around lines 388 - 417, Extract a small reusable
helper function (e.g., AssertJsonEnvelope or ExpectCliJsonEnvelope) and call it
from TEST_F(CCAPCLIDeviceTest, ListDevicesJson) and TEST_F(CCAPCLIDeviceTest,
ShowDeviceInfoJson) to centralize the repeated assertions for
"\"schema_version\":\"1.0\"", "\"command\":\"...\"", and "\"success\":..."; the
helper should accept the CLI output string and expected command name (or allow
command to be optional) and perform the common EXPECT_THAT checks, then replace
the duplicated checks in those tests (and the similar block around lines
753-768) with a single call to the new helper to reduce duplication and make
future schema changes easier to update.
cli/ccap_cli_utils.cpp (2)

474-490: Consider guarding against invalid floating-point values in JSON output.

The << operator for double can produce nan, inf, or -inf for special floating-point values, which are not valid JSON numbers per RFC 8259. While unlikely in normal operation, corrupted video files could potentially yield such values.

🛡️ Optional: Add validation for floating-point values
+    auto safeDouble = [](double v) -> double {
+        return std::isfinite(v) ? v : 0.0;
+    };
+
     if (opts.jsonOutput) {
         std::ostringstream os;
         os << "{\"schema_version\":";
         writeJsonEscapedString(os, opts.schemaVersion);
         os << ",\"command\":";
         writeJsonEscapedString(os, "video-info");
         os << ",\"success\":true,\"data\":{\"video_path\":";
         writeJsonEscapedString(os, videoPath);
         os << ",\"width\":" << width
            << ",\"height\":" << height
-           << ",\"frame_rate\":" << frameRate
-           << ",\"duration_seconds\":" << duration
+           << ",\"frame_rate\":" << safeDouble(frameRate)
+           << ",\"duration_seconds\":" << safeDouble(duration)
            << ",\"total_frames\":" << static_cast<int>(frameCount)
            << "}}";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/ccap_cli_utils.cpp` around lines 474 - 490, The JSON builder currently
streams floating values (frameRate, duration) directly which can produce
NaN/Inf; update the serialization to validate floats with std::isfinite (from
<cmath>) before writing: for each floating variable (frameRate, duration) check
std::isfinite(value) and if false emit the JSON literal null (or a quoted
fallback) instead of the raw value, otherwise emit the numeric value as before;
implement a small helper used when building the os stream (referencing
opts.jsonOutput logic and the existing writeJsonEscapedString context) so the
same validation can be reused and frameCount/width/height handling remains
unchanged.

501-509: Redundant (void)opts; cast.

The opts parameter is already used on lines 501-503 (opts.jsonOutput and opts.schemaVersion), making the (void)opts; suppression on line 507 unnecessary.

🧹 Remove redundant cast
     std::cerr << "Video file playback is not supported. Rebuild with CCAP_ENABLE_FILE_PLAYBACK=ON" << std::endl;
-    (void)opts;
     (void)videoPath;
     return 1;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/ccap_cli_utils.cpp` around lines 501 - 509, Remove the redundant
suppression for opts: delete the line "(void)opts;" since opts is already
referenced (opts.jsonOutput and opts.schemaVersion) in the surrounding block;
keep the unused-videoPath suppression "(void)videoPath;" if needed and leave the
calls to printJsonError and the return statements unchanged (references: opts,
videoPath, printJsonError).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/args_parser.cpp`:
- Around line 349-352: The CLI parser currently accepts "--schema-version"
without a following value and silently continues; update the argument handling
in the parse loop (the block handling arg == "--schema-version" that assigns
opts.schemaVersion = argv[++i]) to validate presence of a value: if i+1 >= argc,
emit a clear error message about the missing schema version and abort with a
non-zero exit (or return an error/throw an exception consistent with existing
parse error handling), otherwise consume argv[++i] into opts.schemaVersion as
before.

In `@docs/content/cli.md`:
- Line 233: The doc's JSON envelope summary omits the top-level exit_code field
produced on error responses; update the envelope description that lists
schema_version, command, success, data/error to explicitly include exit_code (an
integer present on error responses), describe its type and meaning, and show an
example error envelope including "exit_code" alongside "error" so the documented
schema matches the implementation.

In `@docs/content/cli.zh.md`:
- Around line 88-89: The changed lines in docs/content/cli.zh.md introduce
Chinese text for CLI flags (`--json` and `--schema-version VERSION`) and other
sections (lines 194–210) which violate the English-only docs rule; update these
lines to English equivalents (e.g., "`--json` | Output structured JSON for
supported commands" and "`--schema-version VERSION` | Set the schema_version
field in JSON output (default: `1.0`)") or move the Chinese content to a
localized docs directory and keep docs/content/cli.md strictly English; ensure
references to the flags and any headings use the same identifiers (`--json`,
`--schema-version VERSION`) so links/search remain valid.

In `@skills/ccap/references/install.md`:
- Around line 31-35: The docs currently suggest running
./scripts/build_and_install.sh but that script may not enable the CCAP CLI
build; update the install.md text to warn readers that the shortcut may not
produce the ccap binary and add an explicit alternative: instruct users to run a
CMake configure with CCAP_BUILD_CLI=ON and CCAP_INSTALL=ON, then build and
install the project and verify the ccap --version output; reference the
build/install flow and the CCAP_BUILD_CLI and CCAP_INSTALL options and the ccap
executable so readers know exactly which flags and artifact are required.

---

Nitpick comments:
In `@cli/ccap_cli_utils.cpp`:
- Around line 474-490: The JSON builder currently streams floating values
(frameRate, duration) directly which can produce NaN/Inf; update the
serialization to validate floats with std::isfinite (from <cmath>) before
writing: for each floating variable (frameRate, duration) check
std::isfinite(value) and if false emit the JSON literal null (or a quoted
fallback) instead of the raw value, otherwise emit the numeric value as before;
implement a small helper used when building the os stream (referencing
opts.jsonOutput logic and the existing writeJsonEscapedString context) so the
same validation can be reused and frameCount/width/height handling remains
unchanged.
- Around line 501-509: Remove the redundant suppression for opts: delete the
line "(void)opts;" since opts is already referenced (opts.jsonOutput and
opts.schemaVersion) in the surrounding block; keep the unused-videoPath
suppression "(void)videoPath;" if needed and leave the calls to printJsonError
and the return statements unchanged (references: opts, videoPath,
printJsonError).

In `@tests/test_ccap_cli.cpp`:
- Around line 388-417: Extract a small reusable helper function (e.g.,
AssertJsonEnvelope or ExpectCliJsonEnvelope) and call it from
TEST_F(CCAPCLIDeviceTest, ListDevicesJson) and TEST_F(CCAPCLIDeviceTest,
ShowDeviceInfoJson) to centralize the repeated assertions for
"\"schema_version\":\"1.0\"", "\"command\":\"...\"", and "\"success\":..."; the
helper should accept the CLI output string and expected command name (or allow
command to be optional) and perform the common EXPECT_THAT checks, then replace
the duplicated checks in those tests (and the similar block around lines
753-768) with a single call to the new helper to reduce duplication and make
future schema changes easier to update.
🪄 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: CHILL

Plan: Pro

Run ID: b79b6b27-3776-4f3b-9388-d78b95e2cc76

📥 Commits

Reviewing files that changed from the base of the PR and between 6f21ec7 and ee451dd.

📒 Files selected for processing (17)
  • AGENTS.md
  • README.md
  • README.zh-CN.md
  • cli/args_parser.cpp
  • cli/args_parser.h
  • cli/ccap_cli.cpp
  • cli/ccap_cli_utils.cpp
  • cli/ccap_cli_utils.h
  • docs/content/cli.md
  • docs/content/cli.zh.md
  • skills/README.md
  • skills/ccap/PUBLISHING.md
  • skills/ccap/SKILL.md
  • skills/ccap/references/commands.md
  • skills/ccap/references/install.md
  • tests/test_ccap_cli.cpp
  • tests/test_cli_args_parser.cpp

Comment on lines +88 to +89
| `--json` | 对已支持的命令输出结构化 JSON |
| `--schema-version VERSION` | 设置 JSON 输出中的 schema_version 字段(默认:`1.0`) |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

docs/ language policy violation in newly changed lines.

The updates at Line 88–Line 89 and Line 194–Line 210 are in Chinese, but this path is governed as English-only. Please move localized content out of docs/ or convert these updates to English in this location.

As per coding guidelines, .md files in docs/ must be English and require review before publishing.

Also applies to: 194-210

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/cli.zh.md` around lines 88 - 89, The changed lines in
docs/content/cli.zh.md introduce Chinese text for CLI flags (`--json` and
`--schema-version VERSION`) and other sections (lines 194–210) which violate the
English-only docs rule; update these lines to English equivalents (e.g.,
"`--json` | Output structured JSON for supported commands" and
"`--schema-version VERSION` | Set the schema_version field in JSON output
(default: `1.0`)") or move the Chinese content to a localized docs directory and
keep docs/content/cli.md strictly English; ensure references to the flags and
any headings use the same identifiers (`--json`, `--schema-version VERSION`) so
links/search remain valid.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/ccap_cli_utils.cpp`:
- Around line 491-498: The total_frames field currently does a
static_cast<int>(frameCount) which is undefined if frameCount is NaN/Inf; change
the output to use the same finite-number helper as frame_rate/duration_seconds
(call writeJsonFiniteNumberOrNull with frameCount) instead of casting, so
non-finite values become null; update the surrounding lines in
cli/ccap_cli_utils.cpp (the block that writes ",\"frame_rate\":",
writeJsonFiniteNumberOrNull, ",\"duration_seconds\":",
writeJsonFiniteNumberOrNull, and ",\"total_frames\":") to call
writeJsonFiniteNumberOrNull for frameCount (or conditionally cast only after
checking std::isfinite) and fix the inconsistent indentation on the lines that
write frame_rate/duration_seconds/total_frames to match the surrounding
formatting.
🪄 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: CHILL

Plan: Pro

Run ID: 3b8a574f-6f3d-45fd-ad11-ad6cd85c5b8d

📥 Commits

Reviewing files that changed from the base of the PR and between ee451dd and 46451b2.

📒 Files selected for processing (8)
  • cli/args_parser.cpp
  • cli/ccap_cli.cpp
  • cli/ccap_cli_utils.cpp
  • cli/ccap_cli_utils.h
  • docs/content/cli.md
  • docs/content/cli.zh.md
  • skills/ccap/references/install.md
  • tests/test_cli_args_parser.cpp
✅ Files skipped from review due to trivial changes (3)
  • tests/test_cli_args_parser.cpp
  • skills/ccap/references/install.md
  • docs/content/cli.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • cli/ccap_cli_utils.h
  • cli/ccap_cli.cpp
  • docs/content/cli.zh.md

@wysaid
Copy link
Owner Author

wysaid commented Mar 21, 2026

@Auggie review

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Comment on lines +96 to +111
void writeJsonFiniteNumberOrNull(std::ostream& os, double value) {
if (std::isfinite(value)) {
os << value;
} else {
os << "null";
}
}

void writeJsonFiniteIntOrNull(std::ostream& os, double value) {
if (std::isfinite(value) && value >= static_cast<double>(std::numeric_limits<int>::min())
&& value <= static_cast<double>(std::numeric_limits<int>::max())) {
os << static_cast<int>(value);
} else {
os << "null";
}
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON number formatting currently relies on the stream’s active locale and default precision (e.g., some locales use ',' as decimal separator), which can generate invalid JSON and/or lose precision for automation. Consider imbuing a classic/C locale on the JSON stream and using an explicit precision (e.g., max_digits10) when writing floating-point values.

Copilot uses AI. Check for mistakes.
Comment on lines 475 to 492
@@ -279,15 +490,49 @@ int printVideoInfo(const std::string& videoPath) {
int width = static_cast<int>(provider.get(ccap::PropertyName::Width));
int height = static_cast<int>(provider.get(ccap::PropertyName::Height));

Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printVideoInfo assumes that provider.open(videoPath, false) opens a video file when CCAP_ENABLE_FILE_PLAYBACK is defined, but on platforms without file-mode support (notably Linux) this call routes to the camera open path and will fail (or could theoretically open a device whose name matches the path). After opening, verify provider.isFileMode() before reading Duration/FrameCount/etc, and if it’s false return a clear “file playback unsupported” JSON/text error (instead of “Failed to open video file”).

Copilot uses AI. Check for mistakes.
Comment on lines +268 to +274
ccap::Provider devProvider(deviceNames[index]);
std::optional<ccap::DeviceInfo> info;
if (devProvider.isOpened()) {
info = devProvider.getDeviceInfo();
}

writeJsonDevice(os, index, deviceNames[index], info);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In --json mode, listDevices opens every camera (ccap::Provider devProvider(deviceName) opens the device in its constructor) to fetch capabilities. This can be slow and can trigger camera permission prompts just for enumeration, which is risky for automation. Consider emitting only index/name in --list-devices --json (or making capability probing opt-in via a separate flag) to keep the JSON listing lightweight and reliable.

Suggested change
ccap::Provider devProvider(deviceNames[index]);
std::optional<ccap::DeviceInfo> info;
if (devProvider.isOpened()) {
info = devProvider.getDeviceInfo();
}
writeJsonDevice(os, index, deviceNames[index], info);
// In JSON listing mode, avoid opening each device to probe capabilities.
// Opening devices here can be slow and may trigger permission prompts,
// which is undesirable for automation. We therefore emit only index/name
// and pass no capability info.
writeJsonDevice(os, index, deviceNames[index], std::nullopt);

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/ccap_cli_utils.cpp`:
- Around line 104-111: The helper writeJsonFiniteIntOrNull currently downcasts
to 32-bit int and returns "null" for values outside INT range or with fractional
parts; change it to accept and preserve full 64-bit frame counts by using
std::int64_t (or checking against
std::numeric_limits<std::int64_t>::min()/max()) and emitting the integer value
as a 64-bit integer when finite and within that range (or otherwise emit the
finite value directly as a number), and ensure the same logic is applied to the
text-mode "Total frames" branch so it no longer truncates or collapses large
frame counts to null/unknown; update references to static_cast<int>(...) to use
static_cast<std::int64_t>(...) or direct emission of the double as appropriate
in writeJsonFiniteIntOrNull and the Total frames code path.

In `@tests/test_ccap_cli.cpp`:
- Around line 388-395: The test TEST_F CCAPCLIDeviceTest::ListDevicesJson
currently asserts substrings in result.output; instead parse result.output as
JSON using the new JSON parser (the same parser added in this PR), then assert
the actual fields/types: check root is an object, "schema_version" equals "1.0",
"command" equals "list-devices", "success" is boolean true, and "devices" is an
array (optionally assert array elements have expected object fields). Update the
test to call runCLI("--list-devices --json"), parse result.output into a JSON
value, and replace EXPECT_THAT substring checks with precise assertions on the
parsed JSON; apply the same pattern to the other tests mentioned (lines 404-417,
753-769).
- Around line 404-417: The ShowDeviceInfoJson test currently accepts both
success and failure envelopes; update TEST_F(CCAPCLIDeviceTest,
ShowDeviceInfoJson) to require a true success path by asserting result.exitCode
== 0 (or failing the test if not) and only then check for "\"success\":true",
"\"device_count\":1", "\"supported_resolutions\":", and
"\"supported_pixel_formats\"" using runCLI("--device-info 0 --json"); then add a
new test (e.g., TEST_F(CCAPCLIDeviceTest, ShowDeviceInfoJson_ErrorPath)) that
calls runCLI with an invalid index/name (for example "--device-info 999 --json"
or an obviously invalid name), asserts a non-zero exitCode, and checks the JSON
error envelope contains "\"success\":false", "\"error\":", and "\"code\":".
Ensure tests reference the same runCLI helper and keep assertions strict for
each path.
🪄 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: CHILL

Plan: Pro

Run ID: baf2bd69-e7c3-49ea-90e2-30f8488ffde2

📥 Commits

Reviewing files that changed from the base of the PR and between 46451b2 and 47736ed.

📒 Files selected for processing (2)
  • cli/ccap_cli_utils.cpp
  • tests/test_ccap_cli.cpp

Comment on lines +104 to +111
void writeJsonFiniteIntOrNull(std::ostream& os, double value) {
if (std::isfinite(value) && value >= static_cast<double>(std::numeric_limits<int>::min())
&& value <= static_cast<double>(std::numeric_limits<int>::max())) {
os << static_cast<int>(value);
} else {
os << "null";
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Don't downcast valid frame counts to 32-bit int.

Both branches turn any finite value above INT_MAX into null/unknown, and they still silently truncate fractional values. That will misreport legitimate long-form recordings. Use a 64-bit integer check here, or emit the finite number directly.

🛠️ Possible fix
 void writeJsonFiniteIntOrNull(std::ostream& os, double value) {
-    if (std::isfinite(value) && value >= static_cast<double>(std::numeric_limits<int>::min())
-        && value <= static_cast<double>(std::numeric_limits<int>::max())) {
-        os << static_cast<int>(value);
+    if (std::isfinite(value) && std::trunc(value) == value
+        && value >= static_cast<double>(std::numeric_limits<long long>::min())
+        && value <= static_cast<double>(std::numeric_limits<long long>::max())) {
+        os << static_cast<long long>(value);
     } else {
         os << "null";
     }
 }

And mirror the same wider/integral check in the text-mode Total frames branch.

Also applies to: 521-526

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/ccap_cli_utils.cpp` around lines 104 - 111, The helper
writeJsonFiniteIntOrNull currently downcasts to 32-bit int and returns "null"
for values outside INT range or with fractional parts; change it to accept and
preserve full 64-bit frame counts by using std::int64_t (or checking against
std::numeric_limits<std::int64_t>::min()/max()) and emitting the integer value
as a 64-bit integer when finite and within that range (or otherwise emit the
finite value directly as a number), and ensure the same logic is applied to the
text-mode "Total frames" branch so it no longer truncates or collapses large
frame counts to null/unknown; update references to static_cast<int>(...) to use
static_cast<std::int64_t>(...) or direct emission of the double as appropriate
in writeJsonFiniteIntOrNull and the Total frames code path.

Comment on lines +388 to +395
TEST_F(CCAPCLIDeviceTest, ListDevicesJson) {
auto result = runCLI("--list-devices --json");
EXPECT_EQ(result.exitCode, 0);
EXPECT_THAT(result.output, ::testing::HasSubstr("\"schema_version\":\"1.0\""));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"command\":\"list-devices\""));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"success\":true"));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"devices\":"));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate a JSON document, not just substrings.

Because Line 73 redirects stderr into result.output, these checks still pass if the CLI prints log noise or malformed JSON around the payload. Parse the full response with the parser added in this PR and assert field values/types so the JSON contract is actually enforced.

Also applies to: 404-417, 753-769

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_ccap_cli.cpp` around lines 388 - 395, The test TEST_F
CCAPCLIDeviceTest::ListDevicesJson currently asserts substrings in
result.output; instead parse result.output as JSON using the new JSON parser
(the same parser added in this PR), then assert the actual fields/types: check
root is an object, "schema_version" equals "1.0", "command" equals
"list-devices", "success" is boolean true, and "devices" is an array (optionally
assert array elements have expected object fields). Update the test to call
runCLI("--list-devices --json"), parse result.output into a JSON value, and
replace EXPECT_THAT substring checks with precise assertions on the parsed JSON;
apply the same pattern to the other tests mentioned (lines 404-417, 753-769).

Comment on lines +404 to +417
TEST_F(CCAPCLIDeviceTest, ShowDeviceInfoJson) {
auto result = runCLI("--device-info 0 --json");
EXPECT_THAT(result.output, ::testing::HasSubstr("\"command\":\"device-info\""));
if (result.exitCode == 0) {
EXPECT_THAT(result.output, ::testing::HasSubstr("\"success\":true"));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"device_count\":1"));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"supported_resolutions\":"));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"supported_pixel_formats\":"));
} else {
EXPECT_THAT(result.output, ::testing::HasSubstr("\"success\":false"));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"error\":"));
EXPECT_THAT(result.output, ::testing::HasSubstr("\"code\":"));
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the success-path test fail on unexpected error envelopes.

This test goes green for both success:true and success:false, so a regression where --device-info 0 --json always fails would be missed. Keep this case strict on exitCode == 0, and cover the error path with a separate invalid-index/name test.

🧪 Tighten this test
 TEST_F(CCAPCLIDeviceTest, ShowDeviceInfoJson) {
     auto result = runCLI("--device-info 0 --json");
+    ASSERT_EQ(result.exitCode, 0) << result.output;
     EXPECT_THAT(result.output, ::testing::HasSubstr("\"command\":\"device-info\""));
-    if (result.exitCode == 0) {
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"success\":true"));
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"device_count\":1"));
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"supported_resolutions\":"));
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"supported_pixel_formats\":"));
-    } else {
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"success\":false"));
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"error\":"));
-        EXPECT_THAT(result.output, ::testing::HasSubstr("\"code\":"));
-    }
+    EXPECT_THAT(result.output, ::testing::HasSubstr("\"success\":true"));
+    EXPECT_THAT(result.output, ::testing::HasSubstr("\"device_count\":1"));
+    EXPECT_THAT(result.output, ::testing::HasSubstr("\"supported_resolutions\":"));
+    EXPECT_THAT(result.output, ::testing::HasSubstr("\"supported_pixel_formats\":"));
 }

Then add a separate JSON error-path test that intentionally uses an invalid device index or name.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_ccap_cli.cpp` around lines 404 - 417, The ShowDeviceInfoJson test
currently accepts both success and failure envelopes; update
TEST_F(CCAPCLIDeviceTest, ShowDeviceInfoJson) to require a true success path by
asserting result.exitCode == 0 (or failing the test if not) and only then check
for "\"success\":true", "\"device_count\":1", "\"supported_resolutions\":", and
"\"supported_pixel_formats\"" using runCLI("--device-info 0 --json"); then add a
new test (e.g., TEST_F(CCAPCLIDeviceTest, ShowDeviceInfoJson_ErrorPath)) that
calls runCLI with an invalid index/name (for example "--device-info 999 --json"
or an obviously invalid name), asserts a non-zero exitCode, and checks the JSON
error envelope contains "\"success\":false", "\"error\":", and "\"code\":".
Ensure tests reference the same runCLI helper and keep assertions strict for
each path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants