Skip to content

fix(cli): sanitize init target directory#152

Merged
1weiho merged 2 commits into
1weiho:mainfrom
itskylechung:onboarding-with-hyphen
May 24, 2026
Merged

fix(cli): sanitize init target directory#152
1weiho merged 2 commits into
1weiho:mainfrom
itskylechung:onboarding-with-hyphen

Conversation

@itskylechung
Copy link
Copy Markdown
Contributor

@itskylechung itskylechung commented May 21, 2026

Summary

  • npx @open-slide/cli init previously accepted directory names with spaces (e.g. future of open slide), then printed a cd ... line in "Next steps" that the shell rejected (cd: too many arguments).
  • Sanitize the entered name during init — spaces → hyphens, shell-unsafe characters stripped, leading/trailing hyphens trimmed, hyphens around path separators collapsed.
  • In TTY mode, warn (showing the suggested safe name) and re-prompt with the safe suggestion prefilled so the user can accept or edit. In non-TTY mode, error out with a clear suggestion instead of silently rewriting.

Test plan

  • pnpm test — 218/218 pass, includes 11 new tests in packages/cli/src/init.test.ts covering safe passthrough, ./.., whitespace, shell metacharacters, fallback when nothing usable remains, path separators, idempotence, trailing slash, and hyphens collapsing around separators.
  • pnpm exec biome check packages/cli/src/... — clean.
  • Manual non-TTY: init "future of open slide..." → errors with suggested safe name and exits 1.
  • Manual TTY: init and enter a name with spaces → warning shows Suggested: "..." and re-prompts with the safe default.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • The init command now sanitizes directory names with spaces or shell-unfriendly characters. Spaces become hyphens and other unsafe characters are replaced; interactive runs show a suggested safe name, non-interactive runs fail with an error.
  • Tests
    • Added comprehensive tests for sanitization behavior (edge cases, path segments, idempotency, Windows-style separators).
  • Chores
    • Added a changeset documenting the behavior.

Review Change Stack

* fix(cli): sanitize init target directory to avoid shell-breaking names

Names with spaces (e.g. "future of open slide") used to produce a
"Next steps" `cd ...` line the shell rejected. Sanitize the entered
name (spaces → hyphens, shell-unsafe chars stripped) and re-prompt with
the safe suggestion; in non-TTY mode, error out with the suggestion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): show suggested safe name in init warning

The warning previously only described the problem and relied on the
re-prompt's prefilled default to communicate the fix. Print the
suggested name (e.g. `future-of-open-slide`) on the warning line itself
so the user sees a concrete example before answering the prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

@itskylechung is attempting to deploy a commit to the Yiwei Ho Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Walkthrough

This PR adds directory-name sanitization to the CLI init command to ensure generated cd commands are valid for shell environments. A new utility function normalizes names by replacing whitespace and shell-unfriendly characters, and the CLI integration applies it with context-aware error handling for non-interactive environments or user prompting for terminals.

Changes

Directory name sanitization in init

Layer / File(s) Summary
Directory name sanitization function
packages/cli/src/init.ts, packages/cli/src/init.test.ts
sanitizeDirName utility normalizes directory names by trimming, replacing whitespace and shell-unfriendly characters, collapsing separators, and falling back to 'my-slides' when empty. Test suite covers safe-name passthrough, special cases (., ..), character replacement, path preservation, idempotency, and edge cases.
CLI init integration with TTY-aware prompting
packages/cli/src/index.ts
Import refactoring adds sanitizeDirName to init exports. runInit sanitizes the user-provided directory name early: non-TTY throws an error if sanitization changes the input; TTY emits a warning and prompts for confirmation of the sanitized name before continuing.
Release notes
.changeset/sanitize-init-target-dir.md
Changeset entry documents patch-level behavior change: directory names are sanitized so printed cd commands remain valid shell syntax, with examples of safe equivalents for problematic names.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 Shell-safe names hop and bound,
No spaces or dashes to slow us down,
Your init commands now run with care,
cd my-slides—clean and fair!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(cli): sanitize init target directory' directly and clearly describes the main change: sanitizing the directory name during CLI init to prevent shell-breaking names.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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 and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01be4495b1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/init.ts Outdated
if (trimmed === '.' || trimmed === '..') return trimmed;
const cleaned = trimmed
.replace(/\s+/g, '-')
.replace(/[^A-Za-z0-9_./-]/g, '-')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve backslash separators when sanitizing target paths

sanitizeDirName treats \ as an invalid character and rewrites it to -, so on Windows an input like slides\q2 is transformed into slides-q2 (or rejected in non-TTY mode) instead of keeping the intended nested directory. This is a regression from prior behavior where Windows-native path separators worked, and it breaks valid init usage for Windows users passing normal paths.

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/init.ts Outdated
Comment on lines +47 to +48
.replace(/(^-|-$)/g, '')
.replace(/-*\/-*/g, '/');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent sanitization from producing absolute root-like targets

The current cleanup can convert relative, invalid input into absolute-looking paths (for example !!!/!!! becomes /) because hyphen trimming and slash normalization remove segment content. In TTY mode this is suggested as the default and can lead users to scaffold into / (or another absolute path) after confirming overwrite, which is a dangerous change in target scope introduced by this sanitizer.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli/src/init.ts (1)

45-45: ⚡ Quick win

Allow Unicode letters/numbers in sanitizeDirName instead of forcing ASCII-only

The current allowlist on packages/cli/src/init.ts (line 45) replaces all non-ASCII characters with -, flattening localized names (e.g., my-slides). Since the repo targets Node >=18, Unicode property escapes are available for a safer UX-focused allowlist.

Suggested update
-    .replace(/[^A-Za-z0-9_./-]/g, '-')
+    .replace(/[^\p{L}\p{N}_./-]/gu, '-')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/init.ts` at line 45, Replace the ASCII-only character class
in sanitizeDirName that currently uses /[^A-Za-z0-9_./-]/g with a Unicode-aware
regex like /[^\p{L}\p{N}_./-]/gu so letters and numbers from all scripts are
preserved; update the call site in sanitizeDirName to use the 'u' flag (and keep
'g') when calling replace. Ensure the regex is the one used inside
sanitizeDirName and run tests to confirm localized names (e.g., accented or
non-Latin) are no longer replaced with hyphens.
🤖 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 @.changeset/sanitize-init-target-dir.md:
- Line 5: Replace the verbose explanatory changeset line in the
sanitize-init-target-dir changeset with a single present-tense, user-facing
one-liner; e.g. "Sanitize target directory names during init so suggested cd
commands are shell-safe." Ensure it's short, direct, present-tense, and contains
no rationale or implementation details.

---

Nitpick comments:
In `@packages/cli/src/init.ts`:
- Line 45: Replace the ASCII-only character class in sanitizeDirName that
currently uses /[^A-Za-z0-9_./-]/g with a Unicode-aware regex like
/[^\p{L}\p{N}_./-]/gu so letters and numbers from all scripts are preserved;
update the call site in sanitizeDirName to use the 'u' flag (and keep 'g') when
calling replace. Ensure the regex is the one used inside sanitizeDirName and run
tests to confirm localized names (e.g., accented or non-Latin) are no longer
replaced with hyphens.
🪄 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: CHILL

Plan: Pro

Run ID: 9311287c-b8ea-4d80-bb95-846eea8e4a69

📥 Commits

Reviewing files that changed from the base of the PR and between a8c702b and 01be449.

📒 Files selected for processing (4)
  • .changeset/sanitize-init-target-dir.md
  • packages/cli/src/index.ts
  • packages/cli/src/init.test.ts
  • packages/cli/src/init.ts

Comment thread .changeset/sanitize-init-target-dir.md Outdated
- Preserve non-ASCII letters and digits by switching to Unicode property
  escapes (\p{L}\p{N}) — `投影片`, `スライド`, `café` now survive
  sanitization instead of being flattened to hyphens.
- Allow Windows backslash separators so `slides\q2` stays `slides\q2`
  instead of becoming `slides-q2`. The hyphen-around-separator collapse
  now applies symmetrically to `/` and `\`.
- Fall back to `my-slides` when sanitization would yield a root-like
  path (e.g. `!!!/!!!` previously suggested `/`, which could scaffold
  into filesystem root if confirmed).
- Tighten changeset wording to one direct user-facing line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 `@packages/cli/src/init.ts`:
- Around line 45-49: The current sanitization treats backslash as a safe
separator on all OSes, leaving shell-fragile names like "slides\q2"; update the
sanitization in init.ts so backslash is only preserved on Windows. Concretely,
when building `cleaned` adjust the character-class and the separator-normalizing
replace (the two regexes that include `\\` and the final replace that matches
`([/\\])`) to conditionally allow backslash only when `process.platform ===
'win32'` (or use `path.sep`), otherwise treat `\` as an unsafe character to be
replaced—ensure `cleaned` normalization and the early-empty check still work the
same.
🪄 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: CHILL

Plan: Pro

Run ID: fcec4be5-9746-4812-8217-ed30b43d2f4f

📥 Commits

Reviewing files that changed from the base of the PR and between 01be449 and 7b4063d.

📒 Files selected for processing (3)
  • .changeset/sanitize-init-target-dir.md
  • packages/cli/src/init.test.ts
  • packages/cli/src/init.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/sanitize-init-target-dir.md

Comment thread packages/cli/src/init.ts
Comment on lines +45 to +49
.replace(/[^\\\p{L}\p{N}_./-]/gu, '-')
.replace(/-+/g, '-')
.replace(/(^-|-$)/g, '')
.replace(/-*([/\\])-*/g, '$1');
if (cleaned === '' || /^[/\\]+$/.test(cleaned)) return 'my-slides';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Backslash should not be preserved on non-Windows platforms.

On Line 45 and Line 48, \ is treated as a safe path separator for every OS. In POSIX shells, backslash is an escape character, so names like slides\q2 remain shell-fragile and can still break unquoted cd guidance.

Suggested fix
 export function sanitizeDirName(value: string): string {
   const trimmed = value.trim();
   if (trimmed === '.' || trimmed === '..') return trimmed;
+  const unsafeChars = IS_WINDOWS ? /[^\\\p{L}\p{N}_./-]/gu : /[^\p{L}\p{N}_./-]/gu;
+  const separatorWithHyphens = IS_WINDOWS ? /-*([/\\])-*/g : /-*(\/)-*/g;
+  const rootLikeOnly = IS_WINDOWS ? /^[/\\]+$/ : /^\/+$/;
   const cleaned = trimmed
     .replace(/\s+/g, '-')
-    .replace(/[^\\\p{L}\p{N}_./-]/gu, '-')
+    .replace(unsafeChars, '-')
     .replace(/-+/g, '-')
     .replace(/(^-|-$)/g, '')
-    .replace(/-*([/\\])-*/g, '$1');
-  if (cleaned === '' || /^[/\\]+$/.test(cleaned)) return 'my-slides';
+    .replace(separatorWithHyphens, '$1');
+  if (cleaned === '' || rootLikeOnly.test(cleaned)) return 'my-slides';
   return cleaned;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.replace(/[^\\\p{L}\p{N}_./-]/gu, '-')
.replace(/-+/g, '-')
.replace(/(^-|-$)/g, '')
.replace(/-*([/\\])-*/g, '$1');
if (cleaned === '' || /^[/\\]+$/.test(cleaned)) return 'my-slides';
export function sanitizeDirName(value: string): string {
const trimmed = value.trim();
if (trimmed === '.' || trimmed === '..') return trimmed;
const unsafeChars = IS_WINDOWS ? /[^\\\p{L}\p{N}_./-]/gu : /[^\p{L}\p{N}_./-]/gu;
const separatorWithHyphens = IS_WINDOWS ? /-*([/\\])-*/g : /-*(\/)-*/g;
const rootLikeOnly = IS_WINDOWS ? /^[/\\]+$/ : /^\/+$/;
const cleaned = trimmed
.replace(/\s+/g, '-')
.replace(unsafeChars, '-')
.replace(/-+/g, '-')
.replace(/(^-|-$)/g, '')
.replace(separatorWithHyphens, '$1');
if (cleaned === '' || rootLikeOnly.test(cleaned)) return 'my-slides';
return cleaned;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/init.ts` around lines 45 - 49, The current sanitization
treats backslash as a safe separator on all OSes, leaving shell-fragile names
like "slides\q2"; update the sanitization in init.ts so backslash is only
preserved on Windows. Concretely, when building `cleaned` adjust the
character-class and the separator-normalizing replace (the two regexes that
include `\\` and the final replace that matches `([/\\])`) to conditionally
allow backslash only when `process.platform === 'win32'` (or use `path.sep`),
otherwise treat `\` as an unsafe character to be replaced—ensure `cleaned`
normalization and the early-empty check still work the same.

@1weiho
Copy link
Copy Markdown
Owner

1weiho commented May 24, 2026

@itskylechung hey Kyle!
LGTM, thanks for the contribution!

@1weiho 1weiho merged commit 30cec7f into 1weiho:main May 24, 2026
5 of 7 checks passed
@github-actions github-actions Bot mentioned this pull request May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants