[DX-264] Rename channel-rules to apps rules and alias legacy command paths#164
[DX-264] Rename channel-rules to apps rules and alias legacy command paths#164umair-ably merged 4 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughRefactors channel-rules into a new apps:rules namespace: full-featured Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as "User CLI"
participant Alias as "Alias Command\n(e.g. apps:channel-rules)"
participant Rules as "apps:rules Command"
participant Control as "Control API"
CLI->>Alias: invoke alias (e.g. apps:channel-rules create ...)
Alias->>Rules: delegate (new Rules(...).run())
Rules->>Control: POST/GET/PATCH/DELETE /v1/apps/{appId}/namespaces
Control-->>Rules: HTTP response (200/201/204 or error)
Rules-->>CLI: formatted output (JSON or human-readable) or this.fail(error)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 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)
📝 Coding Plan for PR comments
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 |
c87e4dd to
f8ecbdc
Compare
There was a problem hiding this comment.
Pull request overview
This PR renames the primary “channel rules” command surface area from apps channel-rules to apps rules, while keeping legacy command paths working via hidden aliases. It also aligns channel rule detail output formatting with the shared formatLabel() helper and updates docs/tests accordingly.
Changes:
- Introduce new primary commands under
src/commands/apps/rules/*(create/list/update/delete + topic). - Convert legacy commands (
apps/channel-rules/*andchannel-rule/*) into hidden alias commands that forward toapps:rules:*. - Update channel rule detail formatting and regenerate/update documentation and unit tests.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/commands/apps/rules/create.ts |
Adds the new apps:rules:create command implementation. |
src/commands/apps/rules/list.ts |
Adds the new apps:rules:list command implementation (including JSON output). |
src/commands/apps/rules/update.ts |
Adds the new apps:rules:update command implementation with validation + JSON/human output. |
src/commands/apps/rules/delete.ts |
Adds the new apps:rules:delete command implementation with confirmation flow + JSON/human output. |
src/commands/apps/rules/index.ts |
Adds the apps:rules topic command. |
src/commands/apps/channel-rules/{index,create,list,update,delete}.ts |
Converts legacy apps:channel-rules:* commands into hidden forwarders to apps:rules:*. |
src/commands/channel-rule/{index,create,list,update,delete}.ts |
Converts legacy channel-rule:* commands into hidden forwarders to apps:rules:*. |
src/utils/channel-rule-display.ts |
Switches detail line formatting to use formatLabel() consistently. |
test/unit/commands/apps/rules/*.test.ts |
Adds new unit tests for the apps:rules:* commands. |
test/unit/commands/apps/channel-rules/*.test.ts |
Updates legacy-path tests to validate forwarding behavior. |
test/unit/commands/channel-rule/*.test.ts |
Updates legacy-path tests to validate forwarding behavior. |
docs/Project-Structure.md |
Documents apps/rules as the primary path and legacy paths as hidden aliases. |
README.md |
Updates generated CLI docs to reflect apps rules as the primary command group. |
.claude/skills/ably-new-command/SKILL.md |
Clarifies ANSI/formatResource() usage constraints in fail() messages. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
f8ecbdc to
ed96936
Compare
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (1)
test/unit/commands/apps/rules/list.test.ts (1)
71-85: Test doesn't verifymutableMessagesdisplay.The test description says "should display mutableMessages in rule details" but the mock doesn't set
mutableMessages: trueand the assertion only checks for the rule id, not the "Mutable Messages" label. Based onsrc/utils/channel-rule-display.ts, the field is only displayed whenrule.mutableMessages !== undefined.💡 Suggested fix to actually test mutableMessages display
it("should display mutableMessages in rule details", async () => { const appId = getMockConfigManager().getCurrentAppId()!; nockControl() .get(`/v1/apps/${appId}/namespaces`) .reply(200, [ mockNamespace({ id: "mutable-chat", persisted: true, + mutableMessages: true, }), ]); const { stdout } = await runCommand(["apps:rules:list"], import.meta.url); expect(stdout).toContain("mutable-chat"); + expect(stdout).toContain("Mutable Messages:"); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/apps/rules/list.test.ts` around lines 71 - 85, The test "should display mutableMessages in rule details" currently only creates a mock namespace via mockNamespace({ id: "mutable-chat", persisted: true }) and asserts the id appears; update the mock to include mutableMessages: true (e.g., mockNamespace({... mutableMessages: true })) so the code path that renders the "Mutable Messages" label (checked via rule.mutableMessages in src/utils/channel-rule-display.ts) is exercised, then change the assertion to check stdout contains the visible label/text for mutable messages (e.g., "Mutable Messages" or whatever string the display code emits) after running runCommand(["apps:rules:list"], import.meta.url).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/skills/ably-new-command/SKILL.md:
- Line 214: Update the checklist entry about quoting resource names to include
the new exception for this.fail(): keep the rule that resource names are
normally unquoted and use formatResource(name) for display, but explicitly add
that inside this.fail() messages you must not use formatResource() or any
ANSI-producing helper and must use plain quoted strings instead; reference
formatResource(name) and this.fail() in the checklist text so future scaffolds
follow the exception.
In `@README.md`:
- Around line 420-442: The fenced code blocks showing CLI help (blocks starting
with "USAGE", "FLAGS", "DESCRIPTION", "EXAMPLES") are missing a language tag and
trigger markdownlint MD040; update each triple-backtick fence around those help
snippets to use a language tag (use "text") so they become ```text ... ```;
ensure you apply this change to all similar help blocks (the blocks around the
usages/examples referenced in the comment) and, if these blocks are generated,
make the change in the generator/template that emits them rather than only
editing the README.md instance.
In `@src/commands/apps/rules/delete.ts`:
- Around line 49-62: The not-found error for channel rules is invoking
this.fail() inside the try block (after calling this.createControlApi and
controlApi.listNamespaces) and then being handled again in the catch, causing
duplicate error output; fix it by moving the namespace-not-found guard out of
the try/catch (or avoid calling this.fail() inside the try and instead throw a
plain Error so the outer catch can call this.fail() once). Locate the logic
using createControlApi, controlApi.listNamespaces(appId) and the namespace check
(namespace === undefined) in the delete command and either perform the namespace
existence check before entering the try/catch or replace the internal
this.fail() with a thrown Error so only the outer catch calls this.fail().
In `@src/commands/apps/rules/update.ts`:
- Around line 117-149: The try block around control API calls and subsequent
validations is catching errors thrown by this.fail() (used in the not-found,
invalid persistence, and no-op update guards), causing duplicate JSON/fatal
logging; to fix, perform the namespace lookup and all preflight validations (the
namespace existence check using listNamespaces/find with args.nameOrId, the
persistence vs mutable-messages validation that reads namespace.mutableMessages,
and the no-op update check) outside the try that wraps network calls (or rethrow
Command/Exit errors immediately), so keep createControlApi and listNamespaces
inside a minimal try but move the this.fail() guards (and any logic referencing
namespace or flags like flags.persisted and flags["mutable-messages"]) before
the catchable block to avoid swallowing and re-emitting the same failure.
In `@src/utils/channel-rule-display.ts`:
- Line 4: The import of formatLabel() changed visible labels by adding a
trailing ":" and dimming; restore the previous label text in this file by either
using the original literal labels (e.g., "Persisted", "Created", etc.) instead
of formatLabel(), or by calling formatLabel() then removing the appended
colon/dimming so the output matches the prior shape; update all call sites in
this module (the functions that render/display channel rule details such as the
label calls around "Persisted"/"Created" and the other label lines between lines
42–124) and adjust any tests/expectations that depended on the old output.
In `@test/unit/commands/apps/channel-rules/create.test.ts`:
- Around line 16-24: Prettier flagged formatting in the nock setup: reformat the
nockControl() chain and the .reply(201, { ... }) object so it matches project
Prettier rules; specifically locate the nockControl() call with
.post(`/v1/apps/${appId}/namespaces`) and the subsequent .reply(201, { id:
"chat", persisted: false, pushEnabled: false, created: Date.now(), modified:
Date.now() }) and run prettier --write on the file (or adjust spacing/line
breaks to match Prettier) to resolve the CI formatting error.
In `@test/unit/commands/apps/channel-rules/delete.test.ts`:
- Around line 9-14: The test file currently uses a single
describe("apps:channel-rules:delete alias") block; split it into the required
five describe blocks named exactly "help", "argument validation",
"functionality", "flags", and "error handling", then move the existing
forwarding assertion (the it(...) that checks forwarding to apps:rules:delete)
into describe("functionality", ...) while leaving the other four describe blocks
present (they can be empty or contain appropriate placeholder tests/afterEach
controlApiCleanup as needed); ensure you keep the existing afterEach cleanup and
the test's assertions intact and that the describe names match the specification
exactly.
- Around line 28-30: The nock chain is split across lines causing formatter
failures; collapse the chain so the call is on one line by combining
nockControl(), .delete(`/v1/apps/${appId}/namespaces/chat`), and .reply(204)
into a single statement (e.g., nockControl().delete(...).reply(204)) so that the
formatter accepts the line; update the usage of nockControl(), .delete(...) and
.reply(204) accordingly.
In `@test/unit/commands/apps/channel-rules/list.test.ts`:
- Around line 9-14: The test file currently uses a single
describe("apps:channel-rules:list alias") block but must be refactored to
contain exactly five top-level describe blocks with the exact names "help",
"argument validation", "functionality", "flags", and "error handling"; move the
existing afterEach and the it("should forward to apps:rules:list...") test into
a new describe("functionality", ...) block (preserving controlApiCleanup and the
test body), and add empty or placeholder describe blocks for "help", "argument
validation", "flags", and "error handling" so the file conforms to the required
five-block structure without changing the test logic in the moved it block.
In `@test/unit/commands/apps/channel-rules/update.test.ts`:
- Around line 28-31: The nock mock chain starting with nockControl() and the
chained .patch(`/v1/apps/${appId}/namespaces/chat`) call has inconsistent
formatting and reply body indentation; reformat the chain to match the rest of
the test suite (align chained methods on the same indentation level and indent
the reply body object consistently), e.g. ensure nockControl() is followed by a
properly indented .patch(...) line and the .reply(200, { id: "chat", ... })
object keys are indented to match other mocks in the file, updating the block
around the .patch and .reply calls to conform to the project's formatter rules.
- Around line 9-14: The test file currently only has
describe("apps:channel-rules:update alias"); restore the required five top-level
describe blocks named exactly "help", "argument validation", "functionality",
"flags", and "error handling", and move the existing forwarding test into
describe("functionality", ...). Ensure each describe block exists (even if some
are empty) and keep the forwarding assertion inside the test that lives in the
"functionality" describe; verify no additional top-level describe names are
present and follow the existing test patterns used elsewhere in the repo.
In `@test/unit/commands/apps/rules/create.test.ts`:
- Around line 16-33: The top-level describe must use the repo-standard suite
names instead of a custom title; remove or replace describe("apps:rules:create
command") and reorganize the test file into exactly five describe blocks named
'help', 'argument validation', 'functionality', 'flags', and 'error handling'.
Move the existing calls like standardHelpTests("apps:rules:create",
import.meta.url), standardArgValidationTests(...), standardFlagTests(...) and
any controlApiCleanup/afterEach into their appropriate describe blocks (e.g.,
put standardHelpTests in the 'help' block, standardArgValidationTests in
'argument validation', standardFlagTests in 'flags', and actual behavior tests
under 'functionality' with errors under 'error handling'), ensuring the file
contains exactly those five describe blocks and no other top-level describe
titles.
- Around line 125-147: The test never verifies that mutableMessages is
serialized because the mocked response (mockNamespace) does not include
mutableMessages and the assertions only check persisted; update the nock/post
body check and mocked reply to include mutableMessages and add an assertion on
the parsed JSON result to verify rule.mutableMessages is true: adjust the
nockControl().post callback to expect body.mutableMessages === true, have
mockNamespace(...) include mutableMessages: true in its returned object, and add
expect(result.rule).toHaveProperty("mutableMessages", true) after parsing stdout
from runCommand.
---
Nitpick comments:
In `@test/unit/commands/apps/rules/list.test.ts`:
- Around line 71-85: The test "should display mutableMessages in rule details"
currently only creates a mock namespace via mockNamespace({ id: "mutable-chat",
persisted: true }) and asserts the id appears; update the mock to include
mutableMessages: true (e.g., mockNamespace({... mutableMessages: true })) so the
code path that renders the "Mutable Messages" label (checked via
rule.mutableMessages in src/utils/channel-rule-display.ts) is exercised, then
change the assertion to check stdout contains the visible label/text for mutable
messages (e.g., "Mutable Messages" or whatever string the display code emits)
after running runCommand(["apps:rules:list"], import.meta.url).
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e7318ee3-60f1-44a5-a06b-4b3f385a36fb
📒 Files selected for processing (33)
.claude/skills/ably-new-command/SKILL.mdREADME.mddocs/Project-Structure.mdsrc/commands/apps/channel-rules/create.tssrc/commands/apps/channel-rules/delete.tssrc/commands/apps/channel-rules/index.tssrc/commands/apps/channel-rules/list.tssrc/commands/apps/channel-rules/update.tssrc/commands/apps/index.tssrc/commands/apps/rules/create.tssrc/commands/apps/rules/delete.tssrc/commands/apps/rules/index.tssrc/commands/apps/rules/list.tssrc/commands/apps/rules/update.tssrc/commands/channel-rule/create.tssrc/commands/channel-rule/delete.tssrc/commands/channel-rule/index.tssrc/commands/channel-rule/list.tssrc/commands/channel-rule/update.tssrc/utils/channel-rule-display.tstest/unit/commands/apps/channel-rules/create.test.tstest/unit/commands/apps/channel-rules/delete.test.tstest/unit/commands/apps/channel-rules/list.test.tstest/unit/commands/apps/channel-rules/update.test.tstest/unit/commands/apps/rules/create.test.tstest/unit/commands/apps/rules/delete.test.tstest/unit/commands/apps/rules/index.test.tstest/unit/commands/apps/rules/list.test.tstest/unit/commands/apps/rules/update.test.tstest/unit/commands/channel-rule/create.test.tstest/unit/commands/channel-rule/delete.test.tstest/unit/commands/channel-rule/list.test.tstest/unit/commands/channel-rule/update.test.ts
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
test/unit/commands/apps/rules/list.test.ts (2)
71-85: Test name doesn't match assertion coverage.The test is titled "should display mutableMessages in rule details" but only asserts that
stdoutcontains the rule name "mutable-chat". It doesn't verify thatmutableMessagesis actually displayed in the output. Either rename the test to reflect what it actually verifies, or add an assertion for the mutableMessages display (e.g.,expect(stdout).toContain("Mutable Messages")).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/apps/rules/list.test.ts` around lines 71 - 85, The test "should display mutableMessages in rule details" only asserts that stdout contains the namespace name "mutable-chat" (via mockNamespace and runCommand(["apps:rules:list"])), so either rename the test to reflect that it verifies namespace display or add an assertion that checks for the mutableMessages output (for example add expect(stdout).toContain("Mutable Messages") or the exact label/string your CLI prints for mutableMessages) so the title matches coverage; update the test name or add the extra expect referencing runCommand, mockNamespace, and stdout accordingly.
104-121: Strengthen JSON output assertion for mutableMessages.The test "should include mutableMessages in JSON output" verifies
rulesarray length but doesn't assert thatmutableMessagesis present or has the expected value. Consider adding explicit assertions:🔧 Proposed fix
const result = JSON.parse(stdout); expect(result).toHaveProperty("success", true); expect(result.rules).toHaveLength(2); + expect(result.rules[0]).toHaveProperty("mutableMessages"); + expect(result.rules[1]).toHaveProperty("mutableMessages"); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/commands/apps/rules/list.test.ts` around lines 104 - 121, Update the "should include mutableMessages in JSON output" test to explicitly assert that the JSON result includes the mutableMessages field and its expected value for the mutable-chat namespace: after parsing stdout into result, locate the rule object for the namespace created via mockNamespace({ id: "mutable-chat", persisted: true }) and add expects that rule.mutableMessages is present and true (and optionally assert mutableMessages is false or absent for the regular-chat rule); reference the test name "should include mutableMessages in JSON output", the runCommand call, and the mockNamespace usages to find where to add these assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/skills/ably-review/SKILL.md:
- Line 92: Scope the lint rule to only flag catch blocks that use the manual
oclif re-throw guard when the command actually inherits the shared fail()
implementation: perform an LSP/AST resolution to confirm that the catch's
this.fail resolves to the base class method (e.g., BaseCommand.fail) before
reporting; only then treat patterns like `if (error instanceof Error && "oclif"
in error) throw error` as redundant and suggest removal. Ensure the resolver
checks class inheritance/override status so commands that override fail() are
excluded from the rule.
In `@src/commands/apps/channel-rules/list.ts`:
- Around line 12-14: The current run() in this command directly constructs
RulesList and calls run(), bypassing oclif lifecycle hooks; change it to
delegate to oclif via this.config.runCommand(...) instead of new RulesList(...)
so init()/finally() on the shared base command run correctly—invoke
this.config.runCommand with the canonical command identifier for "ably apps
rules list" (or the RulesList command class) and pass the existing argv/flags so
the dispatcher handles lifecycle and execution.
---
Nitpick comments:
In `@test/unit/commands/apps/rules/list.test.ts`:
- Around line 71-85: The test "should display mutableMessages in rule details"
only asserts that stdout contains the namespace name "mutable-chat" (via
mockNamespace and runCommand(["apps:rules:list"])), so either rename the test to
reflect that it verifies namespace display or add an assertion that checks for
the mutableMessages output (for example add expect(stdout).toContain("Mutable
Messages") or the exact label/string your CLI prints for mutableMessages) so the
title matches coverage; update the test name or add the extra expect referencing
runCommand, mockNamespace, and stdout accordingly.
- Around line 104-121: Update the "should include mutableMessages in JSON
output" test to explicitly assert that the JSON result includes the
mutableMessages field and its expected value for the mutable-chat namespace:
after parsing stdout into result, locate the rule object for the namespace
created via mockNamespace({ id: "mutable-chat", persisted: true }) and add
expects that rule.mutableMessages is present and true (and optionally assert
mutableMessages is false or absent for the regular-chat rule); reference the
test name "should include mutableMessages in JSON output", the runCommand call,
and the mockNamespace usages to find where to add these assertions.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 2cc0db5b-8083-4362-8aaa-c098f8034d44
📒 Files selected for processing (37)
.claude/skills/ably-codebase-review/SKILL.md.claude/skills/ably-new-command/SKILL.md.claude/skills/ably-review/SKILL.mdREADME.mddocs/Project-Structure.mdsrc/base-command.tssrc/commands/apps/channel-rules/create.tssrc/commands/apps/channel-rules/delete.tssrc/commands/apps/channel-rules/index.tssrc/commands/apps/channel-rules/list.tssrc/commands/apps/channel-rules/update.tssrc/commands/apps/index.tssrc/commands/apps/rules/create.tssrc/commands/apps/rules/delete.tssrc/commands/apps/rules/index.tssrc/commands/apps/rules/list.tssrc/commands/apps/rules/update.tssrc/commands/channel-rule/create.tssrc/commands/channel-rule/delete.tssrc/commands/channel-rule/index.tssrc/commands/channel-rule/list.tssrc/commands/channel-rule/update.tssrc/utils/channel-rule-display.tstest/fixtures/control-api.tstest/unit/commands/apps/channel-rules/create.test.tstest/unit/commands/apps/channel-rules/delete.test.tstest/unit/commands/apps/channel-rules/list.test.tstest/unit/commands/apps/channel-rules/update.test.tstest/unit/commands/apps/rules/create.test.tstest/unit/commands/apps/rules/delete.test.tstest/unit/commands/apps/rules/index.test.tstest/unit/commands/apps/rules/list.test.tstest/unit/commands/apps/rules/update.test.tstest/unit/commands/channel-rule/create.test.tstest/unit/commands/channel-rule/delete.test.tstest/unit/commands/channel-rule/list.test.tstest/unit/commands/channel-rule/update.test.ts
✅ Files skipped from review due to trivial changes (1)
- docs/Project-Structure.md
🚧 Files skipped from review as they are similar to previous changes (12)
- src/commands/apps/index.ts
- src/commands/channel-rule/delete.ts
- src/utils/channel-rule-display.ts
- test/unit/commands/apps/rules/create.test.ts
- test/unit/commands/channel-rule/create.test.ts
- src/commands/apps/rules/create.ts
- test/unit/commands/apps/rules/delete.test.ts
- test/unit/commands/apps/rules/update.test.ts
- src/commands/apps/channel-rules/delete.ts
- src/commands/channel-rule/update.ts
- test/unit/commands/channel-rule/update.test.ts
- src/commands/channel-rule/create.ts
88e1a67 to
5f14348
Compare
sacOO7
left a comment
There was a problem hiding this comment.
This seems to be json error-handling related issue.
I was testing following command for existing mutable-messages rule on chat namespace
pnpm cli apps rules create --name "chat" --mutable-messages --json
The command produced the following output
Control API Request Error: {
message: 'API request failed with status 400: Bad Request',
method: 'POST',
response: {
message: 'Unable to modify existing channel namespace id with POST',
code: 40300,
statusCode: 400,
href: 'https://help.ably.io/error/40300',
details: null
},
statusCode: 400,
url: 'https://control.ably.net/v1/apps/jy3uew/namespaces'
}
{"type":"error","command":"apps:rules:create","success":false,"error":"API request failed (400 Bad Request): Unable to modify existing channel namespace id with POST","appId":"jy3uew"}
A few issues I noticed:
- This does not appear to be valid JSON error output for
--json. Same goes for--pretty-json, maybe fix across codebase needed. - errorCode
40300and helpUrlhttps://help.ably.io/error/40300is not included in actual json error - Not sure if we should include
urlfieldhttps://control.ably.net/v1/apps/jy3uew/namespacesin the json error message.
|
Since, legacy command paths ( |
cdb4eae to
377a61c
Compare
Summary
apps channel-rulestoapps rulesas the canonical command path for managing channel rules (namespaces)apps channel-rulesandchannel-rulecommands into hidden alias wrappers that delegate toapps rulesformatResource()exception guidance to the ably-new-command skill (don't use ANSI helpers insidethis.fail())Review strategy
src/commands/apps/rules/*.tsapps/channel-rules/implementations to confirm nothing was lost.src/commands/channel-rule/create.ts)test/unit/commands/apps/rules/*.test.tsapps/channel-rules/*.test.ts,channel-rule/*.test.ts)src/utils/channel-rule-display.tsdocs/Project-Structure.md,.claude/skills/ably-new-command/SKILL.mdformatResource()insidethis.fail().Test plan
pnpm preparesucceedspnpm exec eslint .shows 0 errorspnpm test:unitpasses — specificallytest/unit/commands/apps/rules/and alias forwarding testsably apps rules listworks end-to-endably apps channel-rules liststill works (alias)ably channel-rule liststill works (alias)ably apps rules --helpshows correct subcommandsably --helporably apps --helpSummary by CodeRabbit
New Features
Documentation
Deprecation
Bug Fixes