-
Notifications
You must be signed in to change notification settings - Fork 4
fix(users): confirm before creating a user, and unstick the skill audit #400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
manovotny
merged 2 commits into
main
from
manovotny/users-create-confirmation-and-audit-path
Jul 30, 2026
+110
−14
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "clerk": patch | ||
| --- | ||
|
|
||
| Make `clerk users create` confirm before writing. The command registers `--yes` as "Skip confirmation prompt" and its examples recommend passing it, but no prompt existed, so the flag did nothing and the user was created immediately. Human mode now previews the redacted request body and asks before the POST. Agent mode is unchanged: it never prompts, so `--dry-run` remains the safety net there. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import { test, expect, describe, beforeEach, afterEach, mock, spyOn } from "bun:test"; | ||
| import { useCaptureLog } from "../../test/lib/stubs.ts"; | ||
| import { BapiError, CliError, ERROR_CODE, EXIT_CODE } from "../../lib/errors.ts"; | ||
| import { BapiError, CliError, ERROR_CODE, EXIT_CODE, UserAbortError } from "../../lib/errors.ts"; | ||
|
|
||
| const mockResolveBapiSecretKey = mock(); | ||
| const mockHandleBapiError = mock((_error: unknown) => false); | ||
|
|
@@ -27,6 +27,11 @@ mock.module("./create-wizard.ts", () => ({ | |
| runCreateWizard: (...args: unknown[]) => mockRunCreateWizard(...args), | ||
| })); | ||
|
|
||
| const mockConfirm = mock(); | ||
| mock.module("../../lib/prompts.ts", () => ({ | ||
| confirm: (...args: unknown[]) => mockConfirm(...args), | ||
| })); | ||
|
|
||
| mock.module("../../lib/spinner.ts", () => ({ | ||
| intro: () => {}, | ||
| outro: () => {}, | ||
|
|
@@ -46,6 +51,7 @@ describe("users create", () => { | |
| mockIsAgent.mockReturnValue(false); | ||
| mockResolveBapiSecretKey.mockResolvedValue("sk_test_123"); | ||
| mockRunCreateWizard.mockResolvedValue({ fields: {}, targeting: {} }); | ||
| mockConfirm.mockResolvedValue(true); | ||
| mockBapiRequest.mockResolvedValue({ | ||
| status: 200, | ||
| headers: new Headers(), | ||
|
|
@@ -64,6 +70,7 @@ describe("users create", () => { | |
| mockBapiRequest.mockReset(); | ||
| mockIsAgent.mockReset(); | ||
| mockRunCreateWizard.mockReset(); | ||
| mockConfirm.mockReset(); | ||
| logSpy.mockRestore(); | ||
| errorSpy.mockRestore(); | ||
| }); | ||
|
|
@@ -260,4 +267,64 @@ describe("users create", () => { | |
| expect(mockResolveBapiSecretKey).not.toHaveBeenCalled(); | ||
| expect(mockBapiRequest).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test("confirms before creating in human mode and redacts sensitive preview fields", async () => { | ||
| await runCreate({ | ||
| app: "app_123", | ||
| email: "alice@example.com", | ||
| password: "Password123!", | ||
| }); | ||
|
|
||
| expect(mockConfirm).toHaveBeenCalledWith({ message: "Proceed?" }); | ||
| expect(captured.err).toContain("About to POST /v1/users"); | ||
| expect(captured.err).toContain("[REDACTED]"); | ||
| expect(captured.err).not.toContain("Password123!"); | ||
| expect(mockBapiRequest).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test("aborts without calling BAPI when the confirmation is declined", async () => { | ||
| mockConfirm.mockResolvedValue(false); | ||
|
|
||
| const error = await runCreate({ | ||
| app: "app_123", | ||
| email: "alice@example.com", | ||
| }).catch((caught) => caught); | ||
|
|
||
| expect(error).toBeInstanceOf(UserAbortError); | ||
| expect(mockBapiRequest).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test("skips the confirmation when --yes is passed", async () => { | ||
| await runCreate({ | ||
| app: "app_123", | ||
| email: "alice@example.com", | ||
| yes: true, | ||
| }); | ||
|
|
||
| expect(mockConfirm).not.toHaveBeenCalled(); | ||
| expect(mockBapiRequest).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test("never confirms in agent mode, with or without --yes", async () => { | ||
| mockIsAgent.mockReturnValue(true); | ||
|
|
||
| await runCreate({ | ||
| app: "app_123", | ||
| email: "alice@example.com", | ||
| }); | ||
|
|
||
| expect(mockConfirm).not.toHaveBeenCalled(); | ||
| expect(mockBapiRequest).toHaveBeenCalled(); | ||
| }); | ||
|
Comment on lines
+308
to
+318
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Exercise the The test title promises coverage “with or without 🤖 Prompt for AI Agents |
||
|
|
||
| test("does not confirm on --dry-run", async () => { | ||
| await runCreate({ | ||
| app: "app_123", | ||
| email: "alice@example.com", | ||
| dryRun: true, | ||
| }); | ||
|
|
||
| expect(mockConfirm).not.toHaveBeenCalled(); | ||
| expect(mockBapiRequest).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Make the missing skill checkout fail closed.
ls ... || echo ...prints an error but returns success, so the audit can continue despite the instruction to stop when$SKILL_ROOT/SKILL.mdis missing. Use a non-zero exit path instead.Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents