Skip to content

#70: Add thin Beislið CLI and shared user-install foundation#74

Merged
sandsower merged 3 commits intomainfrom
phase-1-thin-cli
May 10, 2026
Merged

#70: Add thin Beislið CLI and shared user-install foundation#74
sandsower merged 3 commits intomainfrom
phase-1-thin-cli

Conversation

@sandsower
Copy link
Copy Markdown
Owner

@sandsower sandsower commented May 10, 2026

Summary

  • add bin/beislid as a thin Bash command dispatcher
  • extract shared user install/status/update behavior into scripts/install_lib.sh
  • install/repair the global CLI link during user install
  • record bin_dir and cli_path in the user install manifest
  • expand installer tests for CLI install, safety rules, manifest fields, and CLI dispatch
  • configure this repo's PR review source/update path to use gh for heard-chef
  • document the Phase 1 CLI foundation

Notes

Project-level install remains deferred. beislid install project is not implemented in this slice.

Verification

  • python3 scripts/validate_skills.py
  • bash scripts/test_install.sh (35 passed, 0 failed)
  • bash -n install.sh bin/beislid scripts/install_lib.sh scripts/test_install.sh
  • git diff --check
  • review + fresh-eyes passes completed; blocking findings fixed
  • python3 tests/agent-smoke/run.py gate ship-it --hosts claude,codex --timeout 900 --changed-only

Summary by CodeRabbit

  • New Features

    • Phased CLI added (install user, status, update, help); safer CLI symlink handling, manifest writing, and installer/runtime flags (project install not yet available).
  • Documentation

    • Expanded install/CLI docs: bin/state paths, PATH guidance, CLI commands/flags, rerun/update/symlink behavior, and installer notes.
  • Tests

    • New CLI-focused tests covering symlink safety, manifest fields, status/update behavior, legacy flags, and help.
  • Other

    • Added workflow section for PR review integration.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b147fb12-7183-47c4-831e-0685a9e15f3b

📥 Commits

Reviewing files that changed from the base of the PR and between 2c8e2ec and 0f5d122.

📒 Files selected for processing (1)
  • scripts/test_install.sh

📝 Walkthrough

Walkthrough

This PR extracts installer implementation from install.sh into a reusable scripts/install_lib.sh module and introduces a new bin/beislid CLI that dispatches user-facing commands. Both entrypoints source the shared library, reducing duplication and enabling independent access to installer functions.

Changes

CLI and Installer Module Extraction

Layer / File(s) Summary
CLI Interface & Dispatcher
bin/beislid
New CLI entrypoint that resolves its location, exports BEISLID_HOME, sources install_lib.sh, and dispatches install user, update, status, and help subcommands with flag parsing; rejects legacy top-level flags and install project with guidance.
Installer Implementation Module
scripts/install_lib.sh
New shared module providing symlink management (_link, repair, repoint), skill/hook linking across Claude/Agents/Codex, legacy cleanup, optional pi install, CLI linking with PATH validation, manifest generation/preservation, status verification, and public functions: beislid_install_user, beislid_update_repo, and beislid_status.
Install Script Refactoring
install.sh
Simplified to set SCRIPT_DIR, export BEISLID_HOME, source install_lib.sh, update usage text (BEISLID_BIN_DIR/BEISLID_STATE_DIR), and dispatch to beislid_status, beislid_update_repo, or beislid_install_user; prior inlined installer logic removed.
Tests & Documentation
scripts/test_install.sh, README.md, docs/configuration.md, docs/how-to-use.md, .beislid/workflow.md
Test harness extended with JSON helpers and CLI runners; comprehensive CLI integration tests added (symlink creation, PATH warnings, safety/repair, manifest fields). Documentation updated to document beislid CLI, ${BEISLID_BIN_DIR} and ${BEISLID_STATE_DIR}, CLI commands, and rerun/repair behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Poem

🐰 A lean CLI hops out with flair,

Library roots tidy up the lair,
Symlinks mended, manifests penned,
install.sh rests — the refactor’s friend,
Tests hop forward, carrots in the air 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding a thin Beislið CLI dispatcher and extracting shared installer foundation, which aligns with the primary changes across bin/beislid, scripts/install_lib.sh, and related files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 phase-1-thin-cli

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

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
scripts/test_install.sh (1)

727-751: ⚡ Quick win

Add an assertion that update preserves the CLI target too.

_preserve_manifest_for_update() now restores bin_dir, but this test only checks skill/hook targets and opt-ins. A regression here would silently move the CLI back to the default bin dir after update without failing the suite.

Suggested assertions
   assert_json_field "$STATE/install.json" skill_dirs.codex "$CODEX_SKILLS"
   assert_json_field "$STATE/install.json" hooks_dir "$HOOKS"
+  assert_json_field "$STATE/install.json" bin_dir "$BIN_DIR"
+  assert_json_field "$STATE/install.json" cli_path "$BIN_DIR/beislid"
   assert_symlink_to "$CLAUDE_SKILLS/verify" "$REPO_DIR/skills/verify"
   assert_symlink_to "$AGENTS_SKILLS/verify" "$REPO_DIR/skills/verify"
   assert_symlink_to "$CODEX_SKILLS/verify" "$REPO_DIR/skills/verify"
   assert_symlink_to "$HOOKS/credential_guard.py" "$REPO_DIR/hooks/credential_guard.py"
+  assert_symlink_to "$BIN_DIR/beislid" "$REPO_DIR/bin/beislid"
   assert_file_contents "$TMP/pi-update.log" "install $REPO_DIR"
🤖 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 `@scripts/test_install.sh` around lines 727 - 751, The test
test_update_preserves_manifest_opt_ins_and_targets needs to also assert the CLI
target/bin dir is preserved by _preserve_manifest_for_update: add an
assert_json_field check on the install manifest's "bin_dir" to match the
expected BIN_DIR and add an assert_symlink_to that verifies the repository's
bin/<cli_executable> points to that BIN_DIR target (use the same helpers
assert_json_field and assert_symlink_to used elsewhere in the test).
🤖 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 `@scripts/install_lib.sh`:
- Around line 362-365: The error text printed when uncommitted changes are
detected is entrypoint-specific; update the two echo messages that currently
reference "install.sh --update" to be entrypoint-neutral or mention both options
(for example: "re-run the installer with --update (e.g. ./install.sh --update or
beislid update)"). Locate the block using SCRIPT_DIR and the git status check
and replace the existing second echo (and any similar guidance) so users see
either both "install.sh --update" and "beislid update" or a neutral phrasing
like "re-run the installer with --update" to cover both entrypoints.
- Around line 271-286: The status check currently only validates skill symlinks
(loop over "$CLAUDE_SKILLS" "$AGENTS_SKILLS" "$CODEX_SKILLS") but does not
verify the new CLI or credential-hook links (e.g., the beislid CLI link or
enabled hook symlinks), so extend the same symlink/target verification pattern:
after the skills loop in the status function add checks that verify the beislid
CLI symlink (check that a "beislid" entry is a symlink and readlink matches the
expected target in "$SCRIPT_DIR"/bin or similar) and iterate enabled hook
entries (e.g., "$SCRIPT_DIR/hooks" or the variable that holds hook paths) to
ensure each is a symlink and points to the expected hook file; print
success/failure messages analogous to "✓ $skill"/"✗ $skill" and set failed=1 on
any mismatch so status returns nonzero when CLI or hook links are broken.
- Around line 247-266: The current inline python invocation (using python3 to
json.load the file referenced by MANIFEST and running subprocess.check_output)
can cause beislid status to fail if python3 is missing or the JSON is malformed;
update the block so it first verifies python3 is available and that MANIFEST is
readable, and make the Python snippet itself resilient by wrapping json.load and
subprocess.check_output in try/except and on any error printing safe fallback
values (e.g., manifest: unknown, installed_at: unknown, version: unknown,
installed_commit: unknown, current_commit: unknown) and exiting with success so
the script continues to the symlink checks; refer to MANIFEST, SCRIPT_DIR, the
python3 invocation, json.load, and subprocess.check_output to locate and harden
the code.

---

Nitpick comments:
In `@scripts/test_install.sh`:
- Around line 727-751: The test
test_update_preserves_manifest_opt_ins_and_targets needs to also assert the CLI
target/bin dir is preserved by _preserve_manifest_for_update: add an
assert_json_field check on the install manifest's "bin_dir" to match the
expected BIN_DIR and add an assert_symlink_to that verifies the repository's
bin/<cli_executable> points to that BIN_DIR target (use the same helpers
assert_json_field and assert_symlink_to used elsewhere in the test).
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ddfb195-c8c0-44bf-82d4-266b4a6ef0e4

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4ff22 and ffe60f4.

📒 Files selected for processing (7)
  • README.md
  • bin/beislid
  • docs/configuration.md
  • docs/how-to-use.md
  • install.sh
  • scripts/install_lib.sh
  • scripts/test_install.sh

Comment thread scripts/install_lib.sh
Comment thread scripts/install_lib.sh
Comment thread scripts/install_lib.sh
Copy link
Copy Markdown
Contributor

@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: 2

🤖 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 @.beislid/workflow.md:
- Line 31: The current reply_command uses the POST
/repos/{owner}/{repo}/pulls/{number}/comments endpoint which creates new review
comments; either rename reply_command to something like create_comment_command
if you intend to keep creating top-level comments, or change reply_command to
target the replies endpoint POST
/repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies and include a
{comment_id} placeholder in the command string and JSON payload so existing
comments are replied to (update the reply_command value accordingly and ensure
any code that writes the JSON temp file supplies comment_id).

In `@scripts/test_install.sh`:
- Around line 126-146: The assert_json_missing helper currently treats any
Python error as “field missing”; update assert_json_missing so the embedded
Python code explicitly catches json.JSONDecodeError (print error to stderr and
exit with a distinct code, e.g. 2) and other exceptions (exit code 3), and
change the shell wrapper around the python3 call to check for those exit codes
and call note_fail with a clear message like "invalid JSON at $path" or "error
reading $path" instead of treating them as a successful missing-field check;
reference the assert_json_missing function and the embedded python3 here when
making the change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eb9eb269-e759-4085-bdea-128cc0e4f3e6

📥 Commits

Reviewing files that changed from the base of the PR and between ffe60f4 and 2c8e2ec.

📒 Files selected for processing (3)
  • .beislid/workflow.md
  • scripts/install_lib.sh
  • scripts/test_install.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/install_lib.sh

Comment thread .beislid/workflow.md

```beislid:pr_review_update
type: cli
reply_command: 'gh api repos/{owner}/{repo}/pulls/{number}/comments --method POST --input {json_file}'
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 10, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the correct GitHub REST API endpoint to reply to an existing pull request review comment?

💡 Result:

To reply to an existing pull request review comment using the GitHub REST API, use: POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies Where comment_id is the ID of the top-level review comment you’re replying to (replies to replies aren’t supported). [1][2] Request body: include a JSON field body with the reply text. [1][2]

Citations:


🏁 Script executed:

cat -n .beislid/workflow.md | head -40

Repository: sandsower/beislid

Length of output: 1971


Check GitHub API endpoint for reply_command—endpoint creates new comments, not replies.

Line 31 uses POST /repos/{owner}/{repo}/pulls/{number}/comments, which creates a new review comment on a pull request. However, the command name and line 21 context ("Reply bodies are written through JSON temp files") indicate the intent is to reply to existing review comments.

To reply to existing comments, the endpoint must be:

POST /repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies

Clarify whether the config should:

  • Create new review comments (current endpoint is correct, but rename from reply_command for clarity)
  • Reply to existing comments (update endpoint to include {comment_id}/replies)
🤖 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 @.beislid/workflow.md at line 31, The current reply_command uses the POST
/repos/{owner}/{repo}/pulls/{number}/comments endpoint which creates new review
comments; either rename reply_command to something like create_comment_command
if you intend to keep creating top-level comments, or change reply_command to
target the replies endpoint POST
/repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies and include a
{comment_id} placeholder in the command string and JSON payload so existing
comments are replied to (update the reply_command value accordingly and ensure
any code that writes the JSON temp file supplies comment_id).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I’m going to keep the existing command here. This workflow schema intentionally uses reply_command with the pull comments endpoint plus an in_reply_to field in the JSON payload; that is what the heard-chef protocol documents, and it worked on this PR (the previous replies were created with in_reply_to_id set). Changing the endpoint would require changing the shared workflow format and runtime payload contract, which is outside this Phase 1 PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@sandsower, understood — that's a valid approach. The POST /repos/{owner}/{repo}/pulls/{number}/comments endpoint does accept an in_reply_to field in the request body to thread a reply onto an existing comment, so the current reply_command is correct as-is. I'll note this for future reviews.


✏️ Learnings added
Learnt from: sandsower
Repo: sandsower/beislid PR: 74
File: .beislid/workflow.md:31-31
Timestamp: 2026-05-10T08:37:47.431Z
Learning: In `.beislid/workflow.md` (sandsower/beislid), the `pr_review_update` block's `reply_command` intentionally uses `POST /repos/{owner}/{repo}/pulls/{number}/comments` (not the `/replies` sub-endpoint). Replies to existing review comments are threaded by including an `in_reply_to` (or `in_reply_to_id`) field in the JSON payload written to `{json_file}`. This is per the "heard-chef" shared workflow protocol and has been verified to work in practice. Do not flag this endpoint as incorrect.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread scripts/test_install.sh
@sandsower sandsower merged commit 4429765 into main May 10, 2026
7 checks passed
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.

1 participant