feat(security): wire SecretRedactHook.addPattern() and SkillSynthesizer ApprovalQueue - #165
Conversation
|
CodeAnt AI is reviewing your PR. Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR implements security and approval wiring at daemon startup by: (1) injecting an initialized ApprovalQueue into SkillSynthesizer, (2) refactoring SecretRedactHook to a singleton with dynamic pattern registration, (3) registering stored secret names into the hook during orchestrator boot, and (4) adding integration tests validating the wiring. Changes
Sequence Diagram(s)sequenceDiagram
participant Daemon as Daemon Process
participant Orchestrator
participant SkillSynthesizer
participant SecretsManager
participant SecretRedactHook
participant ApprovalQueue
Daemon->>Orchestrator: boot()
activate Orchestrator
Orchestrator->>SecretsManager: listSecrets()
activate SecretsManager
SecretsManager-->>Orchestrator: secret names []
deactivate SecretsManager
loop For each secret name
Orchestrator->>SecretRedactHook: addPattern(^name$, i)
activate SecretRedactHook
SecretRedactHook->>SecretRedactHook: store pattern
deactivate SecretRedactHook
end
Orchestrator-->>Daemon: boot complete
deactivate Orchestrator
Daemon->>SkillSynthesizer: setApprovalQueue(queue)
activate SkillSynthesizer
SkillSynthesizer->>SkillSynthesizer: _approvalQueue = queue
deactivate SkillSynthesizer
note over Daemon,ApprovalQueue: Runtime skill confirmation (non-TTY context)
Daemon->>SkillSynthesizer: _confirmWithUser(skill)
activate SkillSynthesizer
alt process.stdin.isTTY is false & approvalQueue enabled
SkillSynthesizer->>ApprovalQueue: request(skill)
activate ApprovalQueue
ApprovalQueue-->>SkillSynthesizer: { allow: true/false }
deactivate ApprovalQueue
else no queue or queue disabled
SkillSynthesizer->>SkillSynthesizer: fail closed
end
deactivate SkillSynthesizer
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances security by enabling dynamic redaction of sensitive information and improves the reliability of skill generation in daemon mode. It addresses previous limitations where stored secrets were not always redacted and auto-generated skills were silently discarded in non-interactive environments, ensuring better control and transparency. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
Sequence DiagramThis PR wires two previously partial security paths: stored secret names are now registered for runtime argument redaction, and daemon skill generation now uses the approval queue instead of silently dropping skills. The flow ensures both safer tool inputs and preserved human approval in non interactive runs. sequenceDiagram
participant Daemon
participant Orchestrator
participant SecretsManager
participant SecretRedactHook
participant ToolRunner
participant SkillSynthesizer
participant ApprovalQueue
Daemon->>Orchestrator: Boot system
Orchestrator->>SecretsManager: Initialize and list secret names
SecretsManager-->>Orchestrator: Return stored secret names
Orchestrator->>SecretRedactHook: Register secret name patterns
ToolRunner->>SecretRedactHook: Run before tool execution
SecretRedactHook-->>ToolRunner: Return redacted arguments
Daemon->>SkillSynthesizer: Set approval queue via orchestrator
SkillSynthesizer->>ApprovalQueue: Request skill save approval in daemon mode
ApprovalQueue-->>SkillSynthesizer: Approve or deny
SkillSynthesizer->>SkillSynthesizer: Save skill only if approved
Generated by CodeAnt AI |
Nitpicks 🔍
|
There was a problem hiding this comment.
Code Review
This pull request introduces two security-related features: dynamic secret redaction and a human-in-the-loop approval flow for skill synthesis in daemon mode. The changes are well-structured, but I've found a critical issue in orchestrator.ts that will cause a runtime error and prevent the secret redaction from working as intended. Specifically, an incorrect method name is called, and secret names are not properly escaped when constructing regular expressions, leading to a Regex injection vulnerability. I've provided a code suggestion to fix both issues. I also have a suggestion to improve code conciseness in secret-redact.ts.
| const secretNames = await this._secretsManager.listSecrets(); | ||
| for (const name of secretNames) { | ||
| // Match as a key name (exact, case-insensitive) so any arg key matching | ||
| // the stored secret name is redacted. | ||
| SecretRedactHook.addPattern(new RegExp(`^${name}$`, 'i')); | ||
| } |
There was a problem hiding this comment.
There are two issues in this block that will prevent secret redaction from working correctly:
- Incorrect method name: The method to retrieve secret names from
SecretsManagerislistSecretNames(), butlistSecrets()is being called. This will result in a runtime error. - Regex injection vulnerability: The secret
nameis used directly to construct a regular expression. If a secret name contains characters with special meaning in regex (e.g.,.,*,+), it can lead to incorrect matching. The secret name must be escaped before being used in theRegExpconstructor.
These issues are critical as they break the intended security feature.
const secretNames = await this._secretsManager.listSecretNames();
for (const name of secretNames) {
// Match as a key name (exact, case-insensitive) so any arg key matching
// the stored secret name is redacted.
const escapedName = name.replace(/[.*+?^${}()|[\]]/g, '\\$&');
SecretRedactHook.addPattern(new RegExp(`^${escapedName}$`, 'i'));
}| private _shouldRedact(key: string, value: string): boolean { | ||
| if (STATIC_KEY_PATTERN.test(key) || STATIC_VALUE_PATTERN.test(value)) return true; | ||
| if (this._extraKeyPatterns.some(p => p.test(key))) return true; | ||
| if (this._extraValuePatterns.some(p => p.test(value))) return true; | ||
| return false; | ||
| } |
There was a problem hiding this comment.
The _shouldRedact method can be simplified by combining the conditions into a single return statement. This improves readability and makes the logic more concise.
private _shouldRedact(key: string, value: string): boolean {
return (
STATIC_KEY_PATTERN.test(key) ||
STATIC_VALUE_PATTERN.test(value) ||
this._extraKeyPatterns.some(p => p.test(key)) ||
this._extraValuePatterns.some(p => p.test(value))
);
}| if (typeof value === 'string') { | ||
| return this._shouldRedact(key, value) ? '[REDACTED]' : value; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| return value.map((item, i) => this._redactDeep(String(i), item)); |
There was a problem hiding this comment.
Suggestion: Redaction is only applied when the value is a string, and array recursion replaces the original key with numeric indexes. This leaks secrets when a sensitive key (including dynamically registered secret names) holds non-string or array/object values, because nested values are checked against 0, 1, etc. instead of the sensitive parent key. Redact immediately when the key matches a sensitive pattern and preserve parent-key context for arrays. [security]
Severity Level: Critical 🚨
- ❌ Secret array values can bypass redaction.
- ⚠️ Tool-call logs may retain sensitive argument data.
- ⚠️ Affects all tools using structured argument payloads.| if (typeof value === 'string') { | |
| return this._shouldRedact(key, value) ? '[REDACTED]' : value; | |
| } | |
| if (Array.isArray(value)) { | |
| return value.map((item, i) => this._redactDeep(String(i), item)); | |
| const keyIsSensitive = | |
| STATIC_KEY_PATTERN.test(key) || this._extraKeyPatterns.some(p => p.test(key)); | |
| if (keyIsSensitive) { | |
| return '[REDACTED]'; | |
| } | |
| if (typeof value === 'string') { | |
| return this._shouldRedact(key, value) ? '[REDACTED]' : value; | |
| } | |
| if (Array.isArray(value)) { | |
| return value.map(item => this._redactDeep(key, item)); |
Steps of Reproduction ✅
1. Start normal boot path where secrets are loaded in
`src/orchestrator/orchestrator.ts:271-280` (`Orchestrator.boot`), which calls
`SecretRedactHook.addPattern(new RegExp(\`^\${name}$\`, 'i'))` for each stored secret
name.
2. Trigger any tool call event with nested args like `{ prodcred: ["plainsecret"] }`;
tool-call processing enters `src/orchestrator/orchestrator.ts:1017-1021` and executes
`ToolHookRunner.runBefore(...)`.
3. In `src/hooks/built-in/secret-redact.ts:65-67`, `SecretRedactHook.run` calls
`_redactObj`, which invokes `_redactDeep("prodcred", value)` for each argument.
4. In `_redactDeep` at `src/hooks/built-in/secret-redact.ts:48-50`, array handling
replaces the key with indices via `this._redactDeep(String(i), item)`, so `"prodcred"`
context is lost.
5. For each string item, `_shouldRedact("0", "plainsecret")` at
`src/hooks/built-in/secret-redact.ts:37-41` returns false unless value matches static
token regex, so value remains unredacted; then orchestrator logs hooked args at
`src/orchestrator/orchestrator.ts:1028-1040`.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/hooks/built-in/secret-redact.ts
**Line:** 45:49
**Comment:**
*Security: Redaction is only applied when the value is a string, and array recursion replaces the original key with numeric indexes. This leaks secrets when a sensitive key (including dynamically registered secret names) holds non-string or array/object values, because nested values are checked against `0`, `1`, etc. instead of the sensitive parent key. Redact immediately when the key matches a sensitive pattern and preserve parent-key context for arrays.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.| // method is added to the hook's interface (SecretRedactHook has no addPattern yet) | ||
| // Wire stored secret names into SecretRedactHook so their values are | ||
| // redacted from tool arguments even if they don't match static patterns. | ||
| const secretNames = await this._secretsManager.listSecrets(); |
There was a problem hiding this comment.
Suggestion: The secrets API call uses listSecrets(), but SecretsManager exposes listSecretNames(). This will break the boot path when secrets are enabled, so switch to the correct method to preserve the existing manager contract. [logic error]
Severity Level: Critical 🚨
- ❌ Orchestrator boot crashes when secrets are enabled.
- ❌ CLI ask/daemon startup fails before handling tasks.
- ⚠️ Type-check/lint fails on invalid method call.| const secretNames = await this._secretsManager.listSecrets(); | |
| const secretNames = await this._secretsManager.listSecretNames(); |
Steps of Reproduction ✅
1. Set `ZORA_MASTER_PASSWORD` and run any entrypoint that boots the orchestrator, e.g.
`ask` command at `src/cli/index.ts:178-180` or daemon at `src/cli/daemon.ts:190-191`.
2. `Orchestrator.boot()` enters the secrets branch at
`src/orchestrator/orchestrator.ts:268-275` and executes
`this._secretsManager.listSecrets()`.
3. `this._secretsManager` is `SecretsManager`, whose implemented list API is
`listSecretNames()` at `src/security/secrets-manager.ts:136-139`; no `listSecrets()`
exists in that class.
4. Runtime throws `TypeError: this._secretsManager.listSecrets is not a function`, so boot
aborts before task execution.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/orchestrator/orchestrator.ts
**Line:** 275:275
**Comment:**
*Logic Error: The secrets API call uses `listSecrets()`, but `SecretsManager` exposes `listSecretNames()`. This will break the boot path when secrets are enabled, so switch to the correct method to preserve the existing manager contract.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.| for (const name of secretNames) { | ||
| // Match as a key name (exact, case-insensitive) so any arg key matching | ||
| // the stored secret name is redacted. | ||
| SecretRedactHook.addPattern(new RegExp(`^${name}$`, 'i')); |
There was a problem hiding this comment.
Suggestion: Secret names are interpolated directly into RegExp, so names containing regex metacharacters can throw at runtime or create unintended broad matches. Escape the secret name before building the regex to ensure safe, exact-key matching. [security]
Severity Level: Major ⚠️
- ❌ Boot can crash on malformed secret names.
- ⚠️ Secret redaction can match unintended argument keys.
- ⚠️ Tool argument logging may be over-redacted unexpectedly.| SecretRedactHook.addPattern(new RegExp(`^${name}$`, 'i')); | |
| const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | |
| SecretRedactHook.addPattern(new RegExp(`^${escapedName}$`, 'i')); |
Steps of Reproduction ✅
1. Store a secret name containing regex metacharacters via CLI `secret set <name> <value>`
at `src/cli/secret-commands.ts:39-45` (name is passed directly; no validation in
`SecretsManager.storeSecret()` at `src/security/secrets-manager.ts:66-93`).
2. Start a flow that calls `Orchestrator.boot()` (`src/cli/index.ts:178-180` or
`src/cli/daemon.ts:190-191`) with `ZORA_MASTER_PASSWORD` set.
3. Boot loads names and builds a regex from raw name at
`src/orchestrator/orchestrator.ts:275-279` using `new RegExp(\`^${name}$\`, 'i')`.
4. If name is invalid regex text (e.g. `[`), `RegExp` throws and boot fails; if name is
valid-but-special (e.g. `a.*`), pattern matches unintended keys, causing incorrect
redaction scope.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/orchestrator/orchestrator.ts
**Line:** 279:279
**Comment:**
*Security: Secret names are interpolated directly into `RegExp`, so names containing regex metacharacters can throw at runtime or create unintended broad matches. Escape the secret name before building the regex to ensure safe, exact-key matching.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.|
CodeAnt AI finished reviewing your PR. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
tests/integration/security-wiring.test.ts (1)
63-67: Exercise the real orchestrator wiring in at least one test.These cases reimplement the production wiring with
listSecretNames()+hook.addPattern(...), so they won’t catch regressions at the actual call site insrc/orchestrator/orchestrator.ts—the currentlistSecrets()typo slipped through for exactly that reason. One boot-path test that goes throughOrchestrator.boot()would make this coverage much more meaningful.Also applies to: 84-87, 106-109, 127-130
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/integration/security-wiring.test.ts` around lines 63 - 67, The tests currently mirror orchestration wiring by calling sm.listSecretNames() and hook.addPattern(...) instead of exercising the real code path, so add at least one integration test that creates an Orchestrator instance and calls Orchestrator.boot() (or the exported boot helper) to exercise the actual wiring in src/orchestrator/orchestrator.ts and catch issues like the listSecrets/listSecretNames typo; update the tests that currently call sm.listSecretNames() + hook.addPattern (locations referenced around the current blocks) to instead initialize the real Orchestrator, invoke boot(), and assert the expected hook patterns/registrations so the production call site is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli/daemon.ts`:
- Around line 193-195: The approval queue is being set after Orchestrator.boot()
starts background work, causing SkillSynthesizer._approvalQueue to be unset
during startup; move the call to
orchestrator.skillSynthesizer.setApprovalQueue(approvalQueue) so it runs before
orchestrator.boot() is invoked (or else inject approvalQueue into Orchestrator
at construction/boot time), ensuring SkillSynthesizer sees a non-null
_approvalQueue during boot and preventing synthesized skills from being dropped.
In `@src/orchestrator/orchestrator.ts`:
- Line 275: Replace the incorrect call to this._secretsManager.listSecrets()
with the existing API this._secretsManager.listSecretNames(); update the call in
the orchestrator (where secretNames is assigned) so it awaits listSecretNames()
(preserving the secretNames variable) and ensure any downstream code consuming
secretNames matches the returned type from listSecretNames().
- Around line 276-280: The loop that builds redact patterns uses raw secret
names to construct RegExp objects (see secretNames iteration and
SecretRedactHook.addPattern(new RegExp(`^${name}$`, 'i'))), which breaks when
names contain regex metacharacters; fix by escaping regex-special characters in
each name before interpolation (use a function to replace characters like
-/\\^$*+?.()|[]{} with escaped versions) and then create the RegExp from the
escaped string with the same anchors and 'i' flag so secrets are matched
literally and safely.
---
Nitpick comments:
In `@tests/integration/security-wiring.test.ts`:
- Around line 63-67: The tests currently mirror orchestration wiring by calling
sm.listSecretNames() and hook.addPattern(...) instead of exercising the real
code path, so add at least one integration test that creates an Orchestrator
instance and calls Orchestrator.boot() (or the exported boot helper) to exercise
the actual wiring in src/orchestrator/orchestrator.ts and catch issues like the
listSecrets/listSecretNames typo; update the tests that currently call
sm.listSecretNames() + hook.addPattern (locations referenced around the current
blocks) to instead initialize the real Orchestrator, invoke boot(), and assert
the expected hook patterns/registrations so the production call site is covered.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ce83a63c-7808-48e6-a107-b62210f87843
📒 Files selected for processing (5)
src/cli/daemon.tssrc/hooks/built-in/secret-redact.tssrc/orchestrator/orchestrator.tssrc/skills/SkillSynthesizer.tstests/integration/security-wiring.test.ts
… redaction, setApprovalQueue race, lastIndex drift, error handling - Escape secret names before constructing RegExp to prevent regex injection (BLOCKER 1) - Redact entire array when parent key is sensitive, preventing bypass via numeric indices (BLOCKER 2) - Move setApprovalQueue() before orchestrator.boot() to eliminate race window (BLOCKER 3) - Strip g/y flags from patterns in addPattern() to prevent lastIndex drift on global/sticky regexes (MUST-FIX 4) - Wrap ApprovalQueue.request() in try/catch, fail closed on transport error (MUST-FIX 5) Co-Authored-By: Claude <noreply@anthropic.com>
…er ApprovalQueue Two Winchester audit items resolved: ## SecretRedactHook dynamic patterns (🟡-4) SecretRedactHook was a plain const with static regex patterns and no way to register new ones at runtime. The orchestrator.ts TODO at line 273-274 noted addPattern() didn't exist yet. - Converted SecretRedactHook to a class (SecretRedactHookImpl) implementing ToolHook; exports singleton instance - addPattern(keyPattern, valuePattern?) registers extra regex arrays - After SecretsManager.init(), orchestrator iterates listSecrets() and calls addPattern() for each stored secret name (exact case-insensitive match) - Stored secrets now guaranteed to be redacted even if their names don't match the static pattern (key|token|secret|password|auth|bearer|credential) ## SkillSynthesizer daemon approval (🟡-3) _confirmWithUser() silently dropped skills in daemon mode (stdin not a TTY) with a TODO to wire ApprovalQueue. - Added approvalQueue? option to SkillSynthesizerOptions - Added setApprovalQueue(queue) method called by daemon after boot - When stdin is not TTY and queue is enabled: routes to queue.request() with score=50 (medium risk) instead of failing closed - Falls back to fail-closed if queue not configured (preserves safety) - daemon.ts calls orchestrator.skillSynthesizer.setApprovalQueue(approvalQueue) immediately after orchestrator.boot() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sizer approval queue wiring Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wiring tests caught a TypeError: SecretsManager exposes listSecretNames() but orchestrator.ts called the non-existent listSecrets(). Would have thrown at runtime whenever ZORA_MASTER_PASSWORD was set, silently preventing any stored secret names from reaching SecretRedactHook. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… redaction, setApprovalQueue race, lastIndex drift, error handling - Escape secret names before constructing RegExp to prevent regex injection (BLOCKER 1) - Redact entire array when parent key is sensitive, preventing bypass via numeric indices (BLOCKER 2) - Move setApprovalQueue() before orchestrator.boot() to eliminate race window (BLOCKER 3) - Strip g/y flags from patterns in addPattern() to prevent lastIndex drift on global/sticky regexes (MUST-FIX 4) - Wrap ApprovalQueue.request() in try/catch, fail closed on transport error (MUST-FIX 5) Co-Authored-By: Claude <noreply@anthropic.com>
e4a32c3 to
cc76ab8
Compare
User description
Summary
Winchester Mystery House Audit — Stream F: Security Wiring
Resolves two 🟡 partially-wired audit items.
1. SecretRedactHook dynamic patterns (audit 🟡-4)
SecretRedactHookhad static regex patterns (API keys, tokens, etc.) butaddPattern()didn't exist — the orchestrator.ts comment at line 273 explicitly said so. Stored secrets fromSecretsManagerwere never registered with the redact hook, meaning a secret namedmy_db_passwordstored viazora secret set my_db_passwordwould not be redacted from tool arguments.Changes:
SecretRedactHookfrom plainconstto a singleton class (SecretRedactHookImpl)addPattern(keyPattern: RegExp, valuePattern?: RegExp)registers extra match arrays checked inrun()orchestrator.boot()now callssecretsManager.listSecrets()after init and registers each name as a case-insensitive key patternSecretRedactHookstill exported as singleton — zero API change at call sites2. SkillSynthesizer daemon approval (audit 🟡-3)
_confirmWithUser()silently dropped auto-generated skills in daemon mode (stdin not a TTY) with a TODO to wireApprovalQueue. Skills generated during daemon runs were silently lost.Changes:
approvalQueue?: ApprovalQueuetoSkillSynthesizerOptionssetApprovalQueue(queue)methodqueue.request()withscore=50if queue is enableddaemon.tscallsorchestrator.skillSynthesizer.setApprovalQueue(approvalQueue)after bootTest plan
npm test— 0 failures (better than baseline of 18 failed tests / 3 files)zora secret set my_api_key abc123→ verifymy_api_keyarg in any tool call is redacted to[REDACTED]approval.enabled: true→ auto-generated skill routes through queueapproval.enabled: false→ skill generation fails closed with warning log🤖 Generated with Claude Code
CodeAnt-AI Description
Keep stored secrets redacted and preserve skill approvals in daemon mode
What Changed
Impact
✅ Fewer secret leaks in tool logs✅ Safer daemon skill approvals✅ Fewer lost skills in background runs💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.
Summary by CodeRabbit
New Features
Tests