Skip to content

fix(ui-plugin): consumer-safe pre-push gate — repo scoping, collision resolution, MIT license - #514

Merged
depthark merged 12 commits into
masterfrom
claude/anout-plugin-hook-safety-9b005p
Jul 24, 2026
Merged

depthark merged 12 commits into
masterfrom
claude/anout-plugin-hook-safety-9b005p

Conversation

@depthark

@depthark depthark commented Jul 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes the consumer-safety issue reported when installing the techsio-ui-kit-ai plugin in other repositories: the SessionStart hook installed (and moved aside) a real git pre-push hook in every repo a session started in, even repos without libs/ui, and the PreToolUse guard blocked git push --no-verify everywhere.

Scope the gate to the ui-kit source repo (9f552b0)

  • Both hooks first check the repo actually contains guarded paths — a libs/ui/ directory with anything besides the plugin bundle itself. Anywhere else the installer installs nothing and the --no-verify guard allows everything.
  • Self-heal: a hook left behind by an earlier plugin version in a consumer repo is removed on the next session start and the original hook it had moved aside is restored.
  • A relative core.hooksPath is now resolved against the worktree root (as git does), not the process cwd.

Resolve collisions with existing/default hooks (febf1f4, 527f550)

Collisions are resolved so existing hooks keep running — never won by force:

  • A hook tracked in the repo (committed .husky/pre-push, lefthook output, a default hooks dir committed by another tool) is never renamed or overwritten. Instead core.hooksPath (repo-local config — nothing in the worktree changes) points at a shim dir inside .git/ that forwards every git hook type to the original dir and adds the gate only to pre-push — the original hook runs first and its exit code is honoured.
  • Untracked foreign hooks are moved aside into numbered pre-push.pre-ui-kit[.N] slots; the gate runs all of them, oldest first, before its own check. This also fixes a destructive bug where a stale backup plus a new foreign hook led to the foreign hook being overwritten without backup.
  • The consumer-repo self-heal understands both mechanisms (restores core.hooksPath + removes the shim, or restores the most recently moved-aside hook).

License (28fca40)

  • The plugin is distributed via marketplace but all three manifests said UNLICENSED. Now MIT: LICENSE file added, license field switched in package.json + both plugin manifests, README notes the bundled skills are covered too.

README documents the scoping and collision-resolution rules and fixes the previously inaccurate claim that pre-existing hooks are "left alone".

Testing

  • Scripted end-to-end scenarios against throwaway repos (installer + gate + guard): consumer repo untouched / self-healed, vendored-plugin-only repo skipped, tracked husky hook → shim redirect with clean worktree, original pre-push runs first and its exit code propagates, other hook types forwarded, numbered backups all run oldest-first and a failing one still blocks the push, --no-verify guard allows in consumer repos and blocks in the ui-kit repo, idempotent re-runs.
  • bunx biome check on the touched scripts.

🤖 Generated with Claude Code

https://claude.ai/code/session_013vUG3XqkXS4qovoemSuxRD


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added MIT licensing for the plugin, including the full licence text in distributed packages.
    • Enhanced Git pre-push validation and hook compatibility by chaining existing hooks safely.
    • Restricted automatic pre-push validation and installation to the ui-kit source repository only.
  • Bug Fixes
    • Prevented the validation gate from impacting unrelated consumer repositories.
    • Improved cleanup and compatibility when hooks were previously installed.
  • Documentation
    • Updated the README with detailed scope rules, collision behaviour, cleanup logic, and licensing details.

claude added 4 commits July 20, 2026 15:40
The SessionStart installer ran in whatever repo a session started in, so a
globally installed plugin installed (and chained aside) a real pre-push hook
in every consumer repo, even ones without libs/ui. Likewise the PreToolUse
guard blocked `git push --no-verify` everywhere.

Both hooks now first check the repo actually contains guarded paths — a
libs/ui/ directory with anything besides the plugin bundle itself. In any
other repo the installer installs nothing and the guard allows everything.
The installer also self-heals repos polluted by earlier versions: it removes
its stale hook and restores any original hook it had chained aside.

Also resolve a relative core.hooksPath against the worktree root (as git
does) instead of the process cwd, and fix the README claim that pre-existing
hooks are "left alone" — they are chained and run first.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vUG3XqkXS4qovoemSuxRD
The plugin is distributed via marketplace but all three manifests said
UNLICENSED, formally forbidding the redistribution the marketplace does.
Add the MIT license text, switch the license field in package.json and
both plugin manifests, ship LICENSE in the npm files list, and note in
the README that the bundled skills are covered too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vUG3XqkXS4qovoemSuxRD
Two collision paths remained even after scoping the installer to the
ui-kit source repo:

- A hook tracked in the repo (committed .husky/pre-push, lefthook output)
  was renamed aside, dirtying the worktree and inviting an agent to commit
  our copy. Tracked hooks are now never touched — the installer warns and
  skips instead.
- When a stale pre-push.pre-ui-kit backup existed and a NEW foreign hook
  had appeared since, the installer silently overwrote the foreign hook
  without any backup. Now it leaves both files untouched and warns.

Only an untracked foreign hook is chained, as before. README documents the
collision rules (agent-level hooks.json hooks are additive by design and
cannot collide with Codex/Claude defaults or other plugins).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vUG3XqkXS4qovoemSuxRD
Previously the installer refused to install the gate when acting safely
was impossible (tracked hook, stale backup + new foreign hook). Now both
collisions are resolved so existing hooks keep running:

- Tracked hooks dir (committed .husky/pre-push, lefthook, a default hooks
  dir committed by another tool): core.hooksPath (repo-local config,
  nothing in the worktree) is pointed at a shim inside .git/ that forwards
  every git hook type to the original dir and adds the gate only to
  pre-push — the original hook runs first and its exit code is honoured.
- Multiple foreign hooks over time: moved-aside hooks accumulate in
  numbered pre-push.pre-ui-kit[.N] slots and the gate runs all of them,
  oldest first, before its own check.

The consumer-repo self-heal understands both: it restores the original
core.hooksPath and removes the shim, or restores the most recently
moved-aside hook.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vUG3XqkXS4qovoemSuxRD
@semanticdiff-com

semanticdiff-com Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  libs/ui/agent-plugin/.claude-plugin/plugin.json  17% smaller
  libs/ui/agent-plugin/.codex-plugin/plugin.json  17% smaller
  libs/ui/agent-plugin/scripts/install-git-hook.mjs  14% smaller
  libs/ui/agent-plugin/package.json  6% smaller
  libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs  2% smaller
  libs/ui/agent-plugin/LICENSE Unsupported file format
  libs/ui/agent-plugin/README.md Unsupported file format
  libs/ui/agent-plugin/hooks/pre-push Unsupported file format
  libs/ui/agent-plugin/scripts/lib/is-ui-kit-source-repo.mjs  0% smaller

@vercel

vercel Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
new-engine-ui-storybook Ready Ready Preview, Comment Jul 24, 2026 8:08am

@greptile-apps

greptile-apps Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR makes the ui-kit Git push gate safe for consumer repositories. The main changes are:

  • Scope installation and command guarding to the @techsio/ui-kit source repository.
  • Preserve existing Git hooks through backups and redirect shims.
  • Resolve Git target selectors before blocking --no-verify pushes.
  • Add MIT license metadata and the bundled license file.

Confidence Score: 5/5

The updated hook and target-resolution paths look safe to merge.

  • The source-repository marker prevents activation in consumer repositories with unrelated UI directories.
  • Hook redirects and backup chaining preserve existing hook execution paths.
  • Git directory selectors and unresolvable targets are handled conservatively for hook-skipping pushes.

Important Files Changed

Filename Overview
libs/ui/agent-plugin/scripts/install-git-hook.mjs Scopes installation to the ui-kit source repository and preserves existing hooks through backups or a redirect shim.
libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs Resolves Git target selectors and aliases before applying the --no-verify guard.
libs/ui/agent-plugin/hooks/pre-push Runs redirected and backed-up pre-push hooks before the ui-kit validation gate.
libs/ui/agent-plugin/scripts/lib/is-ui-kit-source-repo.mjs Adds a shared package-marker check for identifying the ui-kit source repository.

Reviews (9): Last reviewed commit: "fix(ui-plugin): resolve GIT_DIR env assi..." | Re-trigger Greptile

Comment thread libs/ui/agent-plugin/scripts/install-git-hook.mjs Outdated
Comment thread libs/ui/agent-plugin/scripts/install-git-hook.mjs
@coderabbitai

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Luko248, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d1d817ff-85e4-49a9-96b0-0fc88de569d8

📥 Commits

Reviewing files that changed from the base of the PR and between bd00cae and 7cf58fe.

📒 Files selected for processing (1)
  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs

Walkthrough

The plugin now uses MIT licensing, bundles the licence text, and limits its pre-push gate to the ui-kit source repository. Installation preserves existing hooks through redirects or backups, while chained hooks receive the same push input and retain exit-status handling.

Changes

Plugin licensing and hook lifecycle

Layer / File(s) Summary
MIT licensing and package metadata
libs/ui/agent-plugin/.claude-plugin/plugin.json, libs/ui/agent-plugin/.codex-plugin/plugin.json, libs/ui/agent-plugin/LICENSE, libs/ui/agent-plugin/package.json, libs/ui/agent-plugin/README.md
Plugin manifests and package metadata now declare MIT licensing, include LICENSE in published files, and document the bundled licence.
Repository-scoped hook installation
libs/ui/agent-plugin/scripts/lib/is-ui-kit-source-repo.mjs, libs/ui/agent-plugin/scripts/install-git-hook.mjs, libs/ui/agent-plugin/README.md
The installer recognises the ui-kit source repository, preserves tracked or foreign hooks, manages core.hooksPath shims, and cleans up stale installations elsewhere.
Pre-push hook chaining and validation scope
libs/ui/agent-plugin/hooks/pre-push, libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
The hook discovers original and moved-aside hooks, replays buffered input sequentially, skips unrunnable hooks, stops on non-zero statuses, and scopes validation to the effective Git repository.လ

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Installer as install-git-hook.mjs
  participant Repository as ui-kit source repository
  participant GitHooks as Git hooks configuration
  participant ChainedHook as hooks/pre-push
  participant ValidationGate as pre-push-validate-gate.mjs
  Installer->>Repository: Detect repository identity
  Installer->>GitHooks: Install gate, shim hooks, or backup foreign pre-push
  GitHooks->>ChainedHook: Invoke pre-push with push input
  ChainedHook->>ChainedHook: Replay input to original and moved-aside hooks
  ChainedHook->>ValidationGate: Run scoped validation gate
  ValidationGate-->>ChainedHook: Return validation status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main changes: consumer-safe pre-push gating, hook collision handling, and the MIT licence update.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/anout-plugin-hook-safety-9b005p
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/anout-plugin-hook-safety-9b005p

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.

@coderabbitai coderabbitai Bot left a comment

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.

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 `@libs/ui/agent-plugin/scripts/install-git-hook.mjs`:
- Around line 62-68: Extract the canonical isUiKitSourceRepo implementation from
libs/ui/agent-plugin/scripts/install-git-hook.mjs lines 62-68 into a shared
helper under libs/ui/agent-plugin/scripts/lib, preserving its current detection
and fallback behavior. Update
libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs lines 33-53 to remove
its duplicate isUiKitSourceRepo(cwd) logic and import the shared helper, passing
cwd through as needed; the install-git-hook site should use the shared helper
after extraction.
- Around line 190-213: Update removeRedirect() so rmSync(shimDir, { recursive:
true, force: true }) runs only after successfully restoring or unsetting
core.hooksPath. Track whether the git configuration operation failed in the
existing catch, preserve the note about the error, and return without deleting
the shim when restoration fails.
🪄 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

Run ID: 36669fcc-2e59-495c-b147-42deda01e290

📥 Commits

Reviewing files that changed from the base of the PR and between fb2ed4f and 527f550.

📒 Files selected for processing (8)
  • libs/ui/agent-plugin/.claude-plugin/plugin.json
  • libs/ui/agent-plugin/.codex-plugin/plugin.json
  • libs/ui/agent-plugin/LICENSE
  • libs/ui/agent-plugin/README.md
  • libs/ui/agent-plugin/hooks/pre-push
  • libs/ui/agent-plugin/package.json
  • libs/ui/agent-plugin/scripts/install-git-hook.mjs
  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
📜 Review details
⚠️ CI failures not shown inline (1)

GitHub Check: Kilo Code Review: Kilo Code Review failed

Conclusion: failure

View job details

Review failed: The message could not be delivered
🧰 Additional context used
📓 Path-based instructions (2)
libs/**

📄 CodeRabbit inference engine (AGENTS.md)

Use RSLib for building libraries in the monorepo

Files:

  • libs/ui/agent-plugin/LICENSE
  • libs/ui/agent-plugin/package.json
  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
  • libs/ui/agent-plugin/scripts/install-git-hook.mjs
  • libs/ui/agent-plugin/hooks/pre-push
  • libs/ui/agent-plugin/README.md
**/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Use pnpm CLI to add dependencies; never edit package.json directly

Files:

  • libs/ui/agent-plugin/package.json
🧠 Learnings (3)
📚 Learning: 2026-02-05T14:43:17.404Z
Learnt from: KaiUweCZE
Repo: NMIT-WR/new-engine PR: 324
File: apps/medusa-be/package.json:0-0
Timestamp: 2026-02-05T14:43:17.404Z
Learning: Validate and enforce React 19 compatibility across monorepo workspaces. Since Medusa UI supports React 19 via root package.json overrides and Medusa Cloud prerequisites show React 19 overrides for npm workspaces, ensure workspace root and all relevant package.json files align with React 19 (18+ requirement is satisfied). When reviewing, verify that overrides exist in the root package.json and that dependent packages in apps or packages directories declare React 19 (or compatible) in their peerDependencies or dependencies as appropriate for workspace usage.

Applied to files:

  • libs/ui/agent-plugin/package.json
📚 Learning: 2026-05-07T19:05:58.339Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 390
File: apps/medusa-be/package.json:78-81
Timestamp: 2026-05-07T19:05:58.339Z
Learning: When reviewing changes to `package.json`, do not automatically flag dependency additions/removals as "manually edited" or as "bypassing the pnpm lockfile" just because the `package.json` diff shows only that file changed. First verify whether `pnpm-lock.yaml` is missing the corresponding entries. Since `pnpm add` updates both `package.json` and `pnpm-lock.yaml` together, legitimate changes can appear in the `package.json` diff while still being properly tracked in the lockfile.

Applied to files:

  • libs/ui/agent-plugin/package.json
📚 Learning: 2026-07-13T10:43:17.745Z
Learnt from: Luko248
Repo: TechsioCZ/new-engine PR: 510
File: libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs:0-0
Timestamp: 2026-07-13T10:43:17.745Z
Learning: In Node scripts, treat command strings built with `child_process.execSync(...)` as a potential shell-injection risk when any part of the command comes from untrusted or agent/LLM/remote input (e.g., ref/branch names like `tool_input.command`). Do not interpolate untrusted values into an `execSync` shell command string (and avoid relying on `shellQuote` workarounds). Instead, execute the program directly with argv arrays (e.g., `execFileSync('git', ['rev-parse', ref, ...], { stdio: ... })` or `spawn/execFile` with `args`), passing each argument as a separate array element at every call site so no shell parsing is involved.

Applied to files:

  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
  • libs/ui/agent-plugin/scripts/install-git-hook.mjs
🪛 LanguageTool
libs/ui/agent-plugin/README.md

[style] ~121-~121: Consider placing the discourse marker ‘first’ at the beginning of the sentence for more clarity.
Context: ..., so sessions start in arbitrary repos. Both hooks first check whether the repo actually contain...

(SENT_START_FIRST_PREMIUM)


[uncategorized] ~122-~122: Possible missing comma found.
Context: ... the plugin bundle itself. In any other repo the installer installs nothing and the ...

(AI_HYDRA_LEO_MISSING_COMMA)


[misspelling] ~153-~153: This word is normally spelled as one.
Context: ...orce: - an untracked foreign hook (hand-written, .git/hooks) is moved aside to a `p...

(EN_COMPOUNDS_HAND_WRITTEN)


[uncategorized] ~157-~157: It seems likely that a singular genitive (’s) apostrophe is missing.
Context: ...pre-push`, lefthook output, a default hooks dir committed by another tool) is never...

(AI_HYDRA_LEO_APOSTROPHE_S_XS)


[uncategorized] ~157-~157: A comma may be missing after the conjunctive/linking adverb ‘Instead’.
Context: ... tool) is never renamed or overwritten. Instead core.hooksPath (repo-local config —...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[locale-violation] ~181-~181: License must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...idate-gate.mjs, sync-skills.mjs ``` ## License MIT — including the bundl...

(LICENCE_LICENSE_NOUN_SINGULAR)


[locale-violation] ~183-~183: LICENSE must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...s, sync-skills.mjs ``` ## License MIT — including the bundled skills.

(LICENCE_LICENSE_NOUN_SINGULAR)

🔇 Additional comments (8)
libs/ui/agent-plugin/.claude-plugin/plugin.json (1)

12-12: LGTM!

libs/ui/agent-plugin/.codex-plugin/plugin.json (1)

12-12: LGTM!

libs/ui/agent-plugin/LICENSE (1)

1-22: LGTM!

libs/ui/agent-plugin/package.json (1)

15-15: LGTM!

Also applies to: 25-32

libs/ui/agent-plugin/README.md (1)

23-23: LGTM!

Also applies to: 120-126, 144-162, 180-183

libs/ui/agent-plugin/scripts/install-git-hook.mjs (1)

8-61: LGTM!

Also applies to: 70-144, 166-188, 215-265, 275-275

libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs (1)

20-21: LGTM!

Also applies to: 131-136

libs/ui/agent-plugin/hooks/pre-push (1)

23-24: LGTM!

Also applies to: 36-42, 43-72, 74-84, 86-91

Comment thread libs/ui/agent-plugin/scripts/install-git-hook.mjs Outdated
Comment thread libs/ui/agent-plugin/scripts/install-git-hook.mjs
…files, safe shim removal

Applies the four review findings on PR #514, all scoped to the plugin's hook scripts:

- Greptile P1 (install-git-hook.mjs): a consumer repo with its own libs/ui package
  tripped the "libs/ui has something besides agent-plugin" scope check, so the gate
  wrongly activated. Identify THE ui-kit source repo by a specific tracked marker —
  libs/ui is the @techsio/ui-kit package — instead of any extra entry under libs/ui.
- Greptile P1 (install-git-hook.mjs): isTrackedInRepo passed an absolute path to
  `git ls-files`, which matches worktree-relative pathspecs; a committed .husky/pre-push
  could be misread as untracked and renamed instead of shimmed. Pass a repo-relative path.
- CodeRabbit Major (install-git-hook.mjs): removeRedirect() deleted the shim even when
  restoring core.hooksPath threw, leaving config pointing at a missing dir and silencing
  every hook. Return early on restore failure, leaving the shim in place for a retry.
- CodeRabbit (both scripts): the source-repo detection was duplicated. Extract it to
  scripts/lib/is-ui-kit-source-repo.mjs so the installer and the guard can't diverge.

Verified: consumer-scoping unit cases (own libs/ui → not gated) and an end-to-end
tracked-hook run (committed .husky/pre-push left untouched, worktree clean, core.hooksPath
routed through the shim). Plugin keeps its own formatting; no unrelated style churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs Outdated
…e check

Greptile P1 on the scoping added earlier in this PR: the guard tested the shell tool's cwd,
so `git -C /path/to/ui-kit push --no-verify` issued from a consumer cwd resolved to
"not the ui-kit repo", the guard allowed it, and `--no-verify` then skipped the ui-kit
worktree's installed pre-push hook.

Resolve the command's effective directory — walking repeatable, cumulative `-C <dir>` before
the subcommand — and run both the source-repo scope check and the alias lookup against it.

Verified: `git -C <uikit> push --no-verify` from a consumer cwd (and its relative `-C ../uikit`
form) is now blocked, while a genuine consumer `git push --no-verify` is still allowed and a
normal ui-kit push is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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 `@libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs`:
- Around line 43-44: Update command tokenization near `tokens` to preserve
quoted arguments, including paths containing spaces, while still splitting
unquoted whitespace. Update the `gitIdx` executable match to recognize Unix and
Windows paths and both `git` and `git.exe` names, including backslash-separated
paths, so `effectiveGitCwd` receives the correct command context.
🪄 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

Run ID: d3543159-218e-4acf-809e-9059a4020098

📥 Commits

Reviewing files that changed from the base of the PR and between 3306961 and bd00cae.

📒 Files selected for processing (1)
  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
📜 Review details
⚠️ CI failures not shown inline (1)

GitHub Check: Kilo Code Review: Kilo Code Review failed

Conclusion: failure

View job details

Review failed: The message could not be delivered
🧰 Additional context used
📓 Path-based instructions (1)
libs/**

📄 CodeRabbit inference engine (AGENTS.md)

Use RSLib for building libraries in the monorepo

Files:

  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
🧠 Learnings (2)
📚 Learning: 2026-07-13T10:43:17.745Z
Learnt from: Luko248
Repo: TechsioCZ/new-engine PR: 510
File: libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs:0-0
Timestamp: 2026-07-13T10:43:17.745Z
Learning: In Node scripts, treat command strings built with `child_process.execSync(...)` as a potential shell-injection risk when any part of the command comes from untrusted or agent/LLM/remote input (e.g., ref/branch names like `tool_input.command`). Do not interpolate untrusted values into an `execSync` shell command string (and avoid relying on `shellQuote` workarounds). Instead, execute the program directly with argv arrays (e.g., `execFileSync('git', ['rev-parse', ref, ...], { stdio: ... })` or `spawn/execFile` with `args`), passing each argument as a separate array element at every call site so no shell parsing is involved.

Applied to files:

  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
📚 Learning: 2026-07-20T17:19:36.961Z
Learnt from: Luko248
Repo: TechsioCZ/new-engine PR: 514
File: libs/ui/agent-plugin/scripts/install-git-hook.mjs:0-0
Timestamp: 2026-07-20T17:19:36.961Z
Learning: In `libs/ui/agent-plugin/scripts`, keep ui-kit source-repository detection centralized in `scripts/lib/is-ui-kit-source-repo.mjs`. Ensure any git-hook/validation scripts (e.g., `install-git-hook.mjs`, `pre-push-validate-gate.mjs`) reuse this shared detector rather than duplicating logic. The shared detection must include the `techsio/ui-kit` marker (or equivalent required marker) so the code can reliably distinguish the real ui-kit source repository from consumer repositories.

Applied to files:

  • libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
🔇 Additional comments (1)
libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs (1)

20-21: LGTM!

Also applies to: 140-148

Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
…re shell expansion

Greptile P1 (security) follow-up to the -C handling: the hook receives the command verbatim,
before the shell expands it, so `git -C "$UI_KIT_DIR" push --no-verify` from a consumer cwd
made resolve() point at a literal `$UI_KIT_DIR` dir that does not exist, the source-repo check
returned "not ui-kit", and the guard allowed it — then the shell expanded the variable, git
targeted the ui-kit worktree, and --no-verify skipped its pre-push gate.

effectiveGitCwd now reports trusted=false when a -C value contains shell-expansion syntax
($VAR, $(...), ~, globs). The scope check only exits early when it can PROVE the target is a
non-ui-kit repo (trusted && resolvable); otherwise it falls through and fails closed — the guard
blocks the command iff it is push + --no-verify. Normal pushes and non-push commands with an
unresolvable -C are still allowed, so consumers are not over-blocked.

Verified: $VAR / ~ / $(...) -C targets with --no-verify now blocked; the same with a normal push
allowed; plain consumer push --no-verify allowed; trusted literal -C cases unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs Outdated
…vable `-C`

Greptile P1 follow-up: when `-C` is unresolvable (shell expansion), the guard expanded aliases
from the CALLER's config, not the target repo's. So `git -C "$UI_KIT_DIR" publish` — where the
ui-kit repo defines `alias.publish = push --no-verify` — showed neither "push" nor "--no-verify"
at the call site, the caller's config had no such alias, and the command was allowed; the shell
then expanded the path, git ran in ui-kit, resolved the alias, and skipped the pre-push gate.

The target repo's aliases are unknowable when its path isn't resolved yet, so on the unresolvable
`-C` path we now fail closed: block a literal `push --no-verify`, and block any NON-built-in
subcommand that could be an alias expanding to it. Git forbids an alias from shadowing a built-in,
so every built-in subcommand is provably safe — a bare `push` (its pre-push hook still runs),
`status`, `commit`, `fetch`, etc. stay allowed; only shell-variable `-C` + a custom alias
over-blocks, which is rare and never a bypass.

Verified: `git -C "$UI_KIT_DIR" publish|deploy` and `-C "$D" push --no-verify` blocked; bare push,
status, commit, fetch via a shell-variable `-C` allowed; all trusted/literal paths unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
Comment thread libs/ui/agent-plugin/hooks/pre-push
…verify guard

The guard resolved only `git -C <dir>` when deciding whether a command targets
the ui-kit repo. `--git-dir`/`--work-tree` were skipped as value-taking flags but
never changed the scoped directory, so from a consumer checkout
`git --git-dir /path/to/ui-kit/.git push --no-verify` passed the non-ui-kit scope
check while git pushed the ui-kit worktree and `--no-verify` skipped its gate.

`effectiveGitCwd` now also extracts `--git-dir` (the only other selector that
retargets a push; `--work-tree` merely relocates the tree and is intentionally not
treated as a selector, to avoid over-blocking a legit consumer `--work-tree` push).
`resolveTargetWorktree` derives the real worktree from the git dir via
`rev-parse --absolute-git-dir` — cwd can't be trusted here, since git reports the
cwd as top level when only `--git-dir` is set. Shell-expansion values and
un-derivable worktrees (bare/linked) fail closed through the existing path.

Verified end-to-end against throwaway repos: `--git-dir`/`-C` at ui-kit (incl.
alias.publish and shell-var forms) blocked; `--work-tree`-alone, consumer own-repo,
bare push, and non-push subcommands allowed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
…kups

Two follow-ups to the collision/scope work, from review:

- Guard: the `--git-dir` scope resolution only matched the space form
  (`--git-dir <dir>`); the attached form `--git-dir=/path/to/ui-kit/.git` fell
  through as a generic flag, leaving the target at the consumer cwd so
  `git --git-dir=<ui-kit>/.git push --no-verify` slipped past the scope check.
  effectiveGitCwd now handles both forms (short `-C` stays space-only, per git).

- pre-push shim: chainedHooks() only scanned its own directory. A foreign hook
  backed up as `.git/hooks/pre-push.pre-ui-kit` during an earlier untracked-path
  install is then invisible once the repo grows a tracked hooks dir and the
  installer switches to the core.hooksPath shim — its checks and non-zero exit
  were silently dropped on every push. It now also scans git's default
  `.git/hooks` when running as the shim (guarded so a normal install never
  double-runs a backup).

Verified end-to-end: `--git-dir=` (incl. alias + shell-var forms) blocked for
ui-kit and allowed for consumers; the default-dir foreign backup now runs and its
failure propagates, while a direct install runs each backup exactly once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs
`effectiveGitCwd` only inspected tokens after the git executable, so a leading
environment assignment — `GIT_DIR=/path/to/ui-kit/.git git push --no-verify` (also
via `env`, or after other `NAME=value` prefixes) — left the target at the consumer
cwd and slipped past the scope check while git pushed the ui-kit repo with its
pre-push hook skipped.

The scan now also reads a leading `GIT_DIR=` assignment and feeds it through the
same resolution as `--git-dir` (GIT_WORK_TREE is intentionally ignored — like
--work-tree it only relocates the tree, not the repo). A command-line --git-dir
still overrides the env var, matching git's own precedence.

Verified: `GIT_DIR=<uikit>/.git git push --no-verify` (and its `env …`, alias, and
`$VAR` forms) blocked; GIT_DIR at a consumer repo, GIT_WORK_TREE-only, bare push,
non-push subcommands, and a consumer `--git-dir` overriding a ui-kit GIT_DIR all
allowed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@depthark
depthark merged commit f6b8d36 into master Jul 24, 2026
8 of 9 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.27.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

This branch was successfully deployed

1 active deployment
Preview — 601dbc63 Deployed Jul 24, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants