Skip to content

Adapt CLI templates for framework plugins (Vite/Bun) and add agent skill#948

Merged
yamcodes merged 13 commits into
mainfrom
arkenv-cli-install-plugins
May 12, 2026
Merged

Adapt CLI templates for framework plugins (Vite/Bun) and add agent skill#948
yamcodes merged 13 commits into
mainfrom
arkenv-cli-install-plugins

Conversation

@yamcodes

@yamcodes yamcodes commented May 12, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • CLI now auto-bootstraps framework plugins for Vite and Bun during scaffolding, with graceful fallback to manual instructions when safe mutation isn't possible
    • Default environment templates now include a PORT field with validation
  • Documentation

    • Added design, spec, tasks and proposal docs describing plugin bootstrapping and scaffolding behavior
  • Tests

    • Added tests covering config discovery, mutation, idempotency and fallback behavior
  • Chores

    • Added a dependency used for safe config mutation

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds spec/design/docs and a magicast-based Vite config mutation library plus Bun instructions, Vitest coverage, wiring into InitCommand post-install to auto-bootstrap plugins, template PORT validation updates, a logger tweak, and a changeset for the CLI patch release.

Changes

Framework Plugin Auto-Configuration

Layer / File(s) Summary
Specification & Design Documents
openspec/changes/cli-bootstrap-plugins/...
Design, proposal, spec, tasks, and metadata specifying AST-first config mutation with safe fallback and scaffolding CLI requirements.
Configuration Mutation Library & Dependencies
packages/cli/package.json, packages/cli/src/lib/config-mutation.ts
Adds magicast dependency; implements findViteConfig/findBunConfig, bootstrapViteConfig (AST-first injection and formatting-preserving edits), and bootstrapBunConfig (instructions output).
Configuration Mutation Tests
packages/cli/src/lib/config-mutation.test.ts
Vitest suite validating config detection, Vite plugin injection across multiple config shapes, idempotency, formatting preservation, and failure semantics for complex configs.
CLI Init Integration
packages/cli/src/commands/init.ts, packages/cli/src/lib/logger.ts
Integrates discovery and bootstrap helpers into InitCommand post-install flow; logs success/warn/manual instructions; install-step errors include installCmd; final prompt capitalization; Logger.step() marker styling adjusted.
Template Schema Updates
packages/cli/src/templates/arktype.ts, packages/cli/src/templates/valibot.ts, packages/cli/src/templates/zod.ts, packages/cli/src/env-template.test.ts
Adds a PORT field (coerced/integer constrained to 1..65535) to default env schemas, reformats Vite/Bun template branches, and adds a test asserting v.integer().
Release Metadata
.changeset/metal-moles-stop.md
Changeset documenting the @arkenv/cli patch release for automatic framework plugin installation during scaffolding.

Sequence Diagram(s)

sequenceDiagram
  participant User as CLI User
  participant Init as InitCommand
  participant Installer as dependency installer
  participant Finder as findViteConfig / findBunConfig
  participant Bootstrap as bootstrapViteConfig / bootstrapBunConfig
  User->>Init: run init (framework option)
  Init->>Installer: run installCmd
  Installer-->>Init: exit code
  alt install succeeded
    Init->>Finder: probe for framework config
    Finder-->>Init: config path or null
    alt Vite config found
      Init->>Bootstrap: bootstrapViteConfig(path, envImportPath?)
      Bootstrap-->>Init: { success, error? }
      Init->>User: log success or manual instructions/warning
    else Bun selected
      Init->>Bootstrap: bootstrapBunConfig(path?)
      Bootstrap-->>Init: { success, instructions?, error? }
      Init->>User: print instructions or error
    else No config
      Init->>User: print manual integration guidance
    end
  else install failed
    Installer-->>Init: non-zero -> throw Error including installCmd
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • yamcodes/arkenv#943: Overlaps on template and Vite/Bun workflow updates touching similar template files.
  • yamcodes/arkenv#904: Related CLI scaffolding and template generation changes that overlap with this work.

Suggested labels

skills, @arkenv/vite-plugin, @arkenv/bun-plugin, enhancement

Poem

🐰 I hopped through configs, nibbling lines of code,
I tucked a plugin in Vite's humble abode,
Bun got a note instead of a shove,
PORT stands guarded — integer-bound love,
Scaffolding now hums a little more bold.

🚥 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 accurately describes the primary changes: adapting CLI templates for framework plugins (Vite/Bun) and adding an agent skill, which is reflected throughout the changeset.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch arkenv-cli-install-plugins

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.

@changeset-bot

changeset-bot Bot commented May 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1c2dc29

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@arkenv/cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added the @arkenv/cli Issues or Pull Requests involving the ArkEnv CLI label May 12, 2026
@pkg-pr-new

pkg-pr-new Bot commented May 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

arkenv

npm i https://pkg.pr.new/arkenv@948

@arkenv/bun-plugin

npm i https://pkg.pr.new/@arkenv/bun-plugin@948

@arkenv/cli

npm i https://pkg.pr.new/@arkenv/cli@948

@arkenv/fumadocs-ui

npm i https://pkg.pr.new/@arkenv/fumadocs-ui@948

@arkenv/vite-plugin

npm i https://pkg.pr.new/@arkenv/vite-plugin@948

commit: 1c2dc29

@arkenv-bot

arkenv-bot Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

No results found

All size limits passed!

@github-actions github-actions Bot added the docs Adds or changes documentation, or acts as documentation in and of itself label May 12, 2026
@yamcodes

Copy link
Copy Markdown
Owner Author

This is very much a draft, still need to add the actual framework plugin logic

@yamcodes

yamcodes commented May 12, 2026

Copy link
Copy Markdown
Owner Author

Plan looks good

@yamcodes
yamcodes marked this pull request as ready for review May 12, 2026 13:59
@github-actions github-actions Bot added the tests This issue or PR is about adding, removing or changing tests label May 12, 2026
@arkenv-bot

arkenv-bot Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

🤖 Hi @yamcodes, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@arkenv-bot

arkenv-bot Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

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

Warning

Rate limit exceeded

@autofix-ci[bot] has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 41 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered by pushing new commits to this PR or by re-running the workflow.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

We enforce hourly and daily rate limits to ensure stability and fair usage of resources.

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli/src/commands/init.ts (1)

95-102: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include attempted install command in failure error.

Install failures currently report only exit code. Include installCmd in the thrown error to make recovery actionable.

Suggested patch
 					child.on("close", (code: number | null) => {
 						if (code === 0) resolve();
-						else reject(new Error(`Installation failed with code ${code}`));
+						else
+							reject(
+								new Error(
+									`Installation failed with code ${code}. Command: ${installCmd}`,
+								),
+							);
 					});
🤖 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/commands/init.ts` around lines 95 - 102, Update the error
path in the child process close handler so the thrown error includes the
attempted command string; specifically in the spawn call and its close callback
(the child.on("close", ...) block) include installCmd in the reject message
instead of only the exit code so the new Error contains both installCmd and code
(e.g., reject(new Error(`Installation of ${installCmd} failed with code
${code}`))). Ensure this change touches the spawn/child.on("close", ...) logic
that currently calls resolve() or reject(new Error(`Installation failed with
code ${code}`)).
🧹 Nitpick comments (5)
packages/cli/src/templates/valibot.ts (1)

14-14: ⚡ Quick win

Consider adding upper bound validation for PORT.

The current validation enforces a minimum value of 1 but does not restrict the maximum. Valid network ports range from 1 to 65535. Consider adding v.maxValue(65535) to the pipe for completeness.

🔧 Suggested enhancement
-		PORT: v.pipe(v.string(), v.transform(Number), v.number(), v.minValue(1)),`;
+		PORT: v.pipe(v.string(), v.transform(Number), v.number(), v.minValue(1), v.maxValue(65535)),`;
🤖 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/templates/valibot.ts` at line 14, The PORT validation pipe
currently allows values >=1 but no upper bound; update the v.pipe for PORT to
include a maxValue check (add v.maxValue(65535)) after v.minValue(1) in the PORT
validation chain so the pipe becomes v.pipe(v.string(), v.transform(Number),
v.number(), v.minValue(1), v.maxValue(65535)); this ensures PORT validation
restricts values to the valid 1–65535 range.
packages/cli/src/templates/zod.ts (1)

14-14: ⚡ Quick win

Consider adding upper bound validation for PORT.

The current validation enforces positive values but does not restrict the maximum. Valid network ports range from 1 to 65535. Consider adding .max(65535) for completeness.

🔧 Suggested enhancement
-		PORT: z.coerce.number().positive(),`;
+		PORT: z.coerce.number().int().min(1).max(65535),`;

Note: Also added .int() and .min(1) for explicit port semantics (.positive() allows >0 but not exactly 1, and doesn't enforce integers).

🤖 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/templates/zod.ts` at line 14, The PORT schema currently uses
z.coerce.number().positive() which allows non-integers and values >65535; update
the PORT entry in the template (the "PORT" schema key in
packages/cli/src/templates/zod.ts) to enforce integer and valid TCP/UDP port
range by replacing .positive() with .int().min(1).max(65535) (i.e., coerce to
number, require integer, and bound between 1 and 65535).
openspec/changes/cli-bootstrap-plugins/specs/plugin-bootstrapping/spec.md (1)

12-15: ⚡ Quick win

Clarify Bun scenario behavior.

Line 15 states the CLI "SHALL inject the required setup code or instructions." The word "inject" suggests file modification, but the current implementation (bootstrapBunConfig) only returns user-facing instructions without modifying files. Consider rephrasing for clarity:

-THEN** the CLI SHALL inject the required setup code or instructions
+THEN** the CLI SHALL provide explicit setup instructions to the user

This aligns with the implementation and the graceful-fallback philosophy.

🤖 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 `@openspec/changes/cli-bootstrap-plugins/specs/plugin-bootstrapping/spec.md`
around lines 12 - 15, Update the spec wording for the "Bun Configuration Update"
scenario to match the implementation: replace the term "SHALL inject the
required setup code or instructions" with language stating the CLI "SHALL
provide or output the required setup code or user-facing instructions (but SHALL
NOT modify files automatically)" so behavior aligns with the bootstrapBunConfig
implementation; ensure the spec references that bootstrapBunConfig returns
instructions for the user rather than performing file edits.
packages/cli/src/lib/config-mutation.ts (2)

84-92: ⚡ Quick win

Simplify plugin detection logic.

The current check combines multiple conditions and falls back to checking the generated code. While functional, this makes the logic hard to follow and maintain.

Consider simplifying by primarily relying on the generated code check (line 92), which is more reliable than trying to inspect the AST structure:

const hasPlugin = mod.generate().code.includes("arkenvPlugin");

The AST inspection (lines 84-91) adds complexity without clear benefit, since the final code check catches all cases anyway.

🤖 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/lib/config-mutation.ts` around lines 84 - 92, Replace the
convoluted AST-inspection plugin check with a single, reliable generated-code
check: remove the config.plugins.some(...) logic and use the output of
mod.generate().code to detect the plugin by checking
mod.generate().code.includes("arkenvPlugin"); update the variable (e.g.,
hasPlugin) where used so it relies solely on
mod.generate().code.includes("arkenvPlugin") rather than inspecting
config.plugins or JSON-stringifying entries.

145-145: ⚡ Quick win

Replace any with unknown for error type.

Prefer unknown over any for caught errors to maintain type safety:

-	} catch (e: any) {
+	} catch (e: unknown) {
+		const message = e instanceof Error ? e.message : String(e);
-		return { success: false, error: e.message };
+		return { success: false, error: message };
🤖 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/lib/config-mutation.ts` at line 145, Replace the catch
annotation "e: any" with "e: unknown" in the catch block in
packages/cli/src/lib/config-mutation.ts (the catch clause declaring variable
"e"), and then narrow/check "e" with a safe type guard (e.g., instanceof Error
or typeof checks) before using its properties or passing it to logging; update
any subsequent uses of "e" inside the catch to handle the unknown type (convert
to string or access message only after the guard).
🤖 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 `@openspec/changes/cli-bootstrap-plugins/tasks.md`:
- Around line 3-21: The tasks.md checklist is pre-marked completed; change the
initial checkboxes from checked (- [x]) to unchecked (- [ ]) so reviewers can
track progress as items are implemented, and ensure new branch-specific files
under openspec/changes/<branch>/tasks.md always start with unchecked items and
are updated to - [x] only when feature flags or implementation code (e.g.,
Vite/Bun bootstrap tasks listed) actually land.

In `@packages/cli/src/commands/init.ts`:
- Around line 107-121: The branch handling options.framework === "vite" silently
does nothing when findViteConfig() returns no path; update the logic around
findViteConfig/ bootstrapViteConfig to log an explicit manual fallback message
when viteConfigPath is falsy (e.g., use logger.info or logger.warn) so users see
a success-or-fallback outcome; reference the existing symbols options.framework,
findViteConfig(), viteConfigPath, bootstrapViteConfig(), and logger
(logger.step/logger.info/logger.warn) to insert a clear instruction like "No
Vite config found — please add '@arkenv/vite-plugin' to your Vite config
manually" in that branch.
- Around line 123-126: The Bun bootstrap currently only logs result.instructions
and can silently ignore failures; update the handling around
bootstrapBunConfig(bunConfigPath) (the result variable) to check result.success
(or result.error) and when success === false or result.error is present call
logger.error(...) with the error/details (e.g., logger.error(result.error ||
'Bun bootstrap failed')) so failures are surfaced to the user instead of being
ignored.

In `@packages/cli/src/lib/config-mutation.test.ts`:
- Around line 107-125: The test "returns failure for invalid/too complex config"
is nondeterministic; update it so it asserts that bootstrapViteConfig returns a
failure for this invalid config: call bootstrapViteConfig(configPath) and assert
expect(result.success).toBe(false) and expect(result.error).toBeDefined() (or
replace initialContent with a deliberately unparseable config if you prefer to
test unavoidable failure), referencing the existing bootstrapViteConfig,
configPath and initialContent variables to locate and change the assertions.

In `@packages/cli/src/lib/config-mutation.ts`:
- Around line 44-118: The runtime error comes from calling mod.generate().code
(magicast exposes mod.generateCode()); replace both occurrences of
mod.generate().code with mod.generateCode().code, and update the
plugin-detection logic in bootstrapViteConfig (the
Array.isArray(config.plugins()) check that inspects each p) to avoid relying on
the undocumented $callee property: when p is an object, first check p.$type ===
"function-call" and extract the callee name (falling back to p.$name if
present), otherwise stringify the node; use that name to test for
"arkenvPlugin". Ensure all references to mod.generate() are replaced and the
detection uses $type === "function-call" + $name/fallback rather than assuming
$callee.
- Around line 133-136: The example shows an incorrect Bun plugin usage: remove
the unnecessary import of Bun and replace the standalone Bun.plugin(arkenv())
call with the correct Bun.build plugins usage; specifically, stop importing
"Bun" and stop calling Bun.plugin, and instead register arkenv directly in the
Bun.build call via plugins: [arkenv] or plugins: [arkenv(Env)] (or plugins:
[arkenv()] for zero-config), referencing the arkenv symbol and the
Bun.build/plugins configuration in the snippet.

---

Outside diff comments:
In `@packages/cli/src/commands/init.ts`:
- Around line 95-102: Update the error path in the child process close handler
so the thrown error includes the attempted command string; specifically in the
spawn call and its close callback (the child.on("close", ...) block) include
installCmd in the reject message instead of only the exit code so the new Error
contains both installCmd and code (e.g., reject(new Error(`Installation of
${installCmd} failed with code ${code}`))). Ensure this change touches the
spawn/child.on("close", ...) logic that currently calls resolve() or reject(new
Error(`Installation failed with code ${code}`)).

---

Nitpick comments:
In `@openspec/changes/cli-bootstrap-plugins/specs/plugin-bootstrapping/spec.md`:
- Around line 12-15: Update the spec wording for the "Bun Configuration Update"
scenario to match the implementation: replace the term "SHALL inject the
required setup code or instructions" with language stating the CLI "SHALL
provide or output the required setup code or user-facing instructions (but SHALL
NOT modify files automatically)" so behavior aligns with the bootstrapBunConfig
implementation; ensure the spec references that bootstrapBunConfig returns
instructions for the user rather than performing file edits.

In `@packages/cli/src/lib/config-mutation.ts`:
- Around line 84-92: Replace the convoluted AST-inspection plugin check with a
single, reliable generated-code check: remove the config.plugins.some(...) logic
and use the output of mod.generate().code to detect the plugin by checking
mod.generate().code.includes("arkenvPlugin"); update the variable (e.g.,
hasPlugin) where used so it relies solely on
mod.generate().code.includes("arkenvPlugin") rather than inspecting
config.plugins or JSON-stringifying entries.
- Line 145: Replace the catch annotation "e: any" with "e: unknown" in the catch
block in packages/cli/src/lib/config-mutation.ts (the catch clause declaring
variable "e"), and then narrow/check "e" with a safe type guard (e.g.,
instanceof Error or typeof checks) before using its properties or passing it to
logging; update any subsequent uses of "e" inside the catch to handle the
unknown type (convert to string or access message only after the guard).

In `@packages/cli/src/templates/valibot.ts`:
- Line 14: The PORT validation pipe currently allows values >=1 but no upper
bound; update the v.pipe for PORT to include a maxValue check (add
v.maxValue(65535)) after v.minValue(1) in the PORT validation chain so the pipe
becomes v.pipe(v.string(), v.transform(Number), v.number(), v.minValue(1),
v.maxValue(65535)); this ensures PORT validation restricts values to the valid
1–65535 range.

In `@packages/cli/src/templates/zod.ts`:
- Line 14: The PORT schema currently uses z.coerce.number().positive() which
allows non-integers and values >65535; update the PORT entry in the template
(the "PORT" schema key in packages/cli/src/templates/zod.ts) to enforce integer
and valid TCP/UDP port range by replacing .positive() with
.int().min(1).max(65535) (i.e., coerce to number, require integer, and bound
between 1 and 65535).
🪄 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: 0f6663a0-0299-4329-8202-8afb8c7ee3ce

📥 Commits

Reviewing files that changed from the base of the PR and between f736591 and 2772b8b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • .changeset/metal-moles-stop.md
  • openspec/changes/cli-bootstrap-plugins/.openspec.yaml
  • openspec/changes/cli-bootstrap-plugins/design.md
  • openspec/changes/cli-bootstrap-plugins/proposal.md
  • openspec/changes/cli-bootstrap-plugins/specs/plugin-bootstrapping/spec.md
  • openspec/changes/cli-bootstrap-plugins/specs/scaffolding-cli/spec.md
  • openspec/changes/cli-bootstrap-plugins/tasks.md
  • packages/cli/package.json
  • packages/cli/src/commands/init.ts
  • packages/cli/src/lib/config-mutation.test.ts
  • packages/cli/src/lib/config-mutation.ts
  • packages/cli/src/templates/arktype.ts
  • packages/cli/src/templates/valibot.ts
  • packages/cli/src/templates/zod.ts

Comment thread openspec/changes/cli-bootstrap-plugins/tasks.md Outdated
Comment thread packages/cli/src/commands/init.ts Outdated
Comment thread packages/cli/src/commands/init.ts Outdated
Comment thread packages/cli/src/lib/config-mutation.test.ts
Comment thread packages/cli/src/lib/config-mutation.ts
Comment thread packages/cli/src/lib/config-mutation.ts 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: 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 `@packages/cli/src/commands/init.ts`:
- Around line 111-138: The framework-specific bootstrapping (checking
options.framework and calling findViteConfig/bootstrapViteConfig or
findBunConfig/bootstrapBunConfig) must be moved out of the install-only branch
so it always runs regardless of SKIP_INSTALL or missing installCmd; update the
init flow to invoke the existing Vite logic (findViteConfig ->
bootstrapViteConfig -> logger.info/warn messages) and Bun logic (findBunConfig
-> bootstrapBunConfig -> logger.info/error) unconditionally after config
detection, and ensure you still handle null config paths (log instructions when
no Vite config found and handle bootstrapBunConfig returning false/error) so
plugin config mutation is attempted for Vite/Bun projects even when installs are
skipped.

In `@packages/cli/src/templates/valibot.ts`:
- Line 14: The PORT schema (the v.pipe chain that currently contains v.string(),
v.transform(Number), v.number(), v.minValue(1), v.maxValue(65535)) accepts
decimals; update that chain to enforce integers by inserting v.integer() after
v.number() (i.e., v.number(), v.integer(), v.minValue(...), v.maxValue(...)) so
PORT only allows whole-number ports.
🪄 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: a70a1924-b614-4ac0-b610-a3a7ae8b2b62

📥 Commits

Reviewing files that changed from the base of the PR and between 2772b8b and 846a7fa.

📒 Files selected for processing (7)
  • openspec/changes/cli-bootstrap-plugins/specs/plugin-bootstrapping/spec.md
  • openspec/changes/cli-bootstrap-plugins/tasks.md
  • packages/cli/src/commands/init.ts
  • packages/cli/src/lib/config-mutation.test.ts
  • packages/cli/src/lib/config-mutation.ts
  • packages/cli/src/templates/valibot.ts
  • packages/cli/src/templates/zod.ts
✅ Files skipped from review due to trivial changes (2)
  • openspec/changes/cli-bootstrap-plugins/specs/plugin-bootstrapping/spec.md
  • openspec/changes/cli-bootstrap-plugins/tasks.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/cli/src/lib/config-mutation.ts
  • packages/cli/src/lib/config-mutation.test.ts

Comment thread packages/cli/src/commands/init.ts Outdated
Comment thread packages/cli/src/templates/valibot.ts 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.

🧹 Nitpick comments (2)
packages/cli/src/lib/config-mutation.ts (2)

7-42: ⚡ Quick win

Extract the shared "find first accessible file" helper.

findViteConfig and findBunConfig are structurally identical except for the candidate filenames. Consolidating them now keeps additions for future frameworks from copy/pasting the loop.

♻️ Proposed refactor
-export async function findViteConfig(): Promise<string | null> {
-	const filenames = [
-		"vite.config.ts",
-		"vite.config.js",
-		"vite.config.mts",
-		"vite.config.mjs",
-	];
-	const currentDir = process.cwd();
-
-	for (const file of filenames) {
-		const fullPath = path.join(currentDir, file);
-		try {
-			await fsp.access(fullPath);
-			return fullPath;
-		} catch {
-			// ignore missing file
-		}
-	}
-	return null;
-}
-
-export async function findBunConfig(): Promise<string | null> {
-	const filenames = ["bunfig.toml", "bun.setup.ts", "bun.setup.js"];
-	const currentDir = process.cwd();
-
-	for (const file of filenames) {
-		const fullPath = path.join(currentDir, file);
-		try {
-			await fsp.access(fullPath);
-			return fullPath;
-		} catch {
-			// ignore missing file
-		}
-	}
-	return null;
-}
+async function findFirstAccessible(filenames: string[]): Promise<string | null> {
+	const currentDir = process.cwd();
+	for (const file of filenames) {
+		const fullPath = path.join(currentDir, file);
+		try {
+			await fsp.access(fullPath);
+			return fullPath;
+		} catch {
+			// ignore missing file
+		}
+	}
+	return null;
+}
+
+export function findViteConfig(): Promise<string | null> {
+	return findFirstAccessible([
+		"vite.config.ts",
+		"vite.config.js",
+		"vite.config.mts",
+		"vite.config.mjs",
+	]);
+}
+
+export function findBunConfig(): Promise<string | null> {
+	return findFirstAccessible(["bunfig.toml", "bun.setup.ts", "bun.setup.js"]);
+}
🤖 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/lib/config-mutation.ts` around lines 7 - 42, Extract the
duplicated loop into a shared helper like findFirstAccessibleFile(filenames:
string[]): Promise<string | null> that uses process.cwd(), path.join and
fsp.access to test each candidate and return the first accessible full path or
null; then simplify findViteConfig and findBunConfig to just call this helper
with their respective filename arrays, preserving their names and return types.

132-163: ⚡ Quick win

Adapt Bun instructions to the discovered config path, and consider keeping ANSI styling at the call site.

A couple of small concerns worth addressing together:

  1. findBunConfig already locates the user's actual file (bunfig.toml, bun.setup.ts, or bun.setup.js), but bootstrapBunConfig ignores _configPath and always emits the same "create one" message. When the discovered path is already a setup file, the second half of the instructions is redundant; when it is bunfig.toml, the user only needs the [preload] snippet. Branching on _configPath would make the guidance more useful.
  2. ANSI escape codes from picocolors are baked into the returned instructions string. That works for the current logger but couples this helper to terminal rendering — if the string is ever written to a file, snapshot, or non-TTY stream, the escapes will leak. Consider returning plain instructions and applying coloring at the call site (or accepting a styler), so the helper stays presentation-agnostic.

Also, the surrounding try/catch is effectively dead since the body only builds a static dedent template — you can drop it.

🤖 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/lib/config-mutation.ts` around lines 132 - 163,
bootstrapBunConfig currently ignores _configPath and returns ANSI-colored text
inside a try/catch; update it to (1) remove the unnecessary try/catch, (2)
branch on _configPath (the value discovered by findBunConfig) so that when
_configPath ends with "bunfig.toml" return only the plain [preload] snippet,
when it ends with "bun.setup.ts" or "bun.setup.js" return the plain
Bun.build(...) plugin instructions targeted to that setup file, and when
null/undefined return the combined guidance; and (3) strip out picocolors usage
so the function returns plain text instructions (no ANSI) and let the caller
apply coloring/styling, referencing bootstrapBunConfig, _configPath, and the
snippets currently built with dedent.
🤖 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.

Nitpick comments:
In `@packages/cli/src/lib/config-mutation.ts`:
- Around line 7-42: Extract the duplicated loop into a shared helper like
findFirstAccessibleFile(filenames: string[]): Promise<string | null> that uses
process.cwd(), path.join and fsp.access to test each candidate and return the
first accessible full path or null; then simplify findViteConfig and
findBunConfig to just call this helper with their respective filename arrays,
preserving their names and return types.
- Around line 132-163: bootstrapBunConfig currently ignores _configPath and
returns ANSI-colored text inside a try/catch; update it to (1) remove the
unnecessary try/catch, (2) branch on _configPath (the value discovered by
findBunConfig) so that when _configPath ends with "bunfig.toml" return only the
plain [preload] snippet, when it ends with "bun.setup.ts" or "bun.setup.js"
return the plain Bun.build(...) plugin instructions targeted to that setup file,
and when null/undefined return the combined guidance; and (3) strip out
picocolors usage so the function returns plain text instructions (no ANSI) and
let the caller apply coloring/styling, referencing bootstrapBunConfig,
_configPath, and the snippets currently built with dedent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 707d4f7b-8c00-4940-8c9c-55bc66d17a0f

📥 Commits

Reviewing files that changed from the base of the PR and between 846a7fa and 28a3c56.

📒 Files selected for processing (6)
  • packages/cli/src/commands/init.ts
  • packages/cli/src/env-template.test.ts
  • packages/cli/src/lib/config-mutation.test.ts
  • packages/cli/src/lib/config-mutation.ts
  • packages/cli/src/lib/logger.ts
  • packages/cli/src/templates/valibot.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/cli/src/env-template.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/cli/src/templates/valibot.ts
  • packages/cli/src/commands/init.ts

@yamcodes

Copy link
Copy Markdown
Owner Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Reviews paused.

# Conflicts:
#	packages/cli/src/commands/init.ts
@yamcodes
yamcodes merged commit 1dca74c into main May 12, 2026
14 of 18 checks passed
@yamcodes
yamcodes deleted the arkenv-cli-install-plugins branch May 12, 2026 16:50
@arkenv-bot arkenv-bot Bot mentioned this pull request May 12, 2026
yamcodes pushed a commit that referenced this pull request May 12, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @arkenv/cli@0.0.5

### Patch Changes

- #### Add agent skill support
_[`#945`](#945)
[`63d6237`](63d6237)
[@yamcodes](https://github.com/yamcodes)_

The CLI now has a new `--agent` flag that lets the ArkEnv agent skill
interact with it in a token-sensitive way. The skill has been updated to
support this new mode.

Also, the CLI will now suggest to install the agent skill for you when
in non-`--agent` mode.

    Read more in the [ArkEnv CLI docs](https://arkenv.js.org/docs/cli).

- #### Improve "done" message text
_[`#954`](#954)
[`fe10ef4`](fe10ef4)
[@yamcodes](https://github.com/yamcodes)_

The output now clearly distinguishes between the local scaffolding and
the AI-powered refinement:

            1 │
2 ◇ Next steps
─────────────────────────────────────────────────────────────────╮
3 │ │
4 │ 1. Check ./src/env.ts and refine your environment schema. │
5 │ 2. Import and use: import { env } from "./src/env" │
6 │ 3. (Recommended) Install the AI skill: pnpm dlx skills add
yamcodes/arkenv │
7 │ Then run /arkenv inside your AI assistant to finish. │
8 │ │
9
├──────────────────────────────────────────────────────────────────────────────╯
           10 │
           11 └  ⛯ ArkEnv scaffolding complete. Happy coding!

- #### Install framework plugins
_[`#948`](#948)
[`1dca74c`](1dca74c)
[@yamcodes](https://github.com/yamcodes)_

The ArkEnv CLI now installs framework plugins as part of the scaffolding
process if a relevant runtime/framework is selected.

- #### Passthrough `--yes` and `--quiet` to underlying process
_[`#952`](#952)
[`10cbb7d`](10cbb7d)
[@yamcodes](https://github.com/yamcodes)_

The ArkEnv CLI will now pass the flags `--yes` and `--quiet` to
underlying processes.

    This means that if you run:

    ```sh
    pnx @arkenv/cli init --yes
    ```

It would now use the recommended settings and avoid prompts even in
sub-processes like the Vercel Skills process to add the ArkEnv skill.

    Similarly, if you run:

    ```sh
    pnx @arkenv/cli init --quiet
    ```

You will not be exposed to the underlying Vercel Skills output, except
for errors which are buffered in memory. (Resolves on exit code 0,
discarding buffered output on success)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

@arkenv/cli Issues or Pull Requests involving the ArkEnv CLI docs Adds or changes documentation, or acts as documentation in and of itself tests This issue or PR is about adding, removing or changing tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant