feat(cli): Add warden skill installation to warden init#182
feat(cli): Add warden skill installation to warden init#182
warden init#182Conversation
Install the built-in warden skill during init with an interactive prompt (default yes). Copies skill files from the package into .agents/skills/warden/ and creates a .claude/skills/warden symlink when .claude/ exists. Restructures init output into sectioned layout (CONFIG, WORKFLOW, SKILL) matching the analysis flow visual style. Co-Authored-By: Claude <noreply@anthropic.com>
Use Node's cpSync({ recursive: true }) instead of a hand-rolled
directory copy helper. Matches the existing pattern in evals/runner.ts.
Co-Authored-By: Claude <noreply@anthropic.com>
Without force: true, cpSync silently skips existing files, which means --force wouldn't actually refresh stale skill content. Co-Authored-By: Claude <noreply@anthropic.com>
| process.stderr.write(key === '\r' || key === '\n' ? 'y' : key); | ||
| process.stderr.write('\n'); | ||
|
|
||
| if (key !== 'n') { |
There was a problem hiding this comment.
Uppercase 'N' input not treated as decline
Medium Severity
The interactive skill install prompt checks key !== 'n' (lowercase only), so typing 'N' is not recognized as a decline and will proceed with installation. The [Y/n] prompt convention implies both cases of n mean "no." The check needs to be case-insensitive, e.g. comparing against key.toLowerCase().
There was a problem hiding this comment.
False positive — readSingleKey() in src/cli/input.ts:24 already calls key.toLowerCase() before returning, so uppercase N is converted to n before the check.
There was a problem hiding this comment.
False positive. readSingleKey() in src/cli/input.ts:24 already lowercases all input via resolve(key.toLowerCase()) before returning. The comparison against "n" is guaranteed to be case-insensitive.
existsSync follows symlinks and returns false for broken symlinks, but symlinkSync throws EEXIST if the symlink file itself exists. Use lstatSync to detect any symlink (broken or valid) before attempting to create one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of just detecting broken symlinks to avoid EEXIST, actively remove any existing symlink (broken or valid) and recreate it. Since installWardenSkill is only called when the user wants installation (--force or interactive confirm), a fresh symlink is always correct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
unlinkSync throws EISDIR for directories, causing the catch to swallow the error and symlinkSync to crash with EEXIST. Use rmSync with recursive+force to handle files, symlinks, and directories uniformly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace lstatSync+rmSync guard with a single rmSync call. force:true already handles missing paths silently, making the lstatSync check unnecessary and avoiding behavior differences across Node.js versions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| reporter.text(` 1. Add a skill: ${chalk.cyan('warden add <skill-name>')}`); | ||
| reporter.text(` 2. Set ${chalk.cyan('WARDEN_ANTHROPIC_API_KEY')} in .env.local`); | ||
| reporter.text(` 3. Add ${chalk.cyan('WARDEN_ANTHROPIC_API_KEY')} to organization or repository secrets`); | ||
| reporter.text(` 1. Set ${chalk.cyan('WARDEN_ANTHROPIC_API_KEY')} in .env.local`); |
There was a problem hiding this comment.
Misleading tip when skill skipped in non-interactive mode
Low Severity
When the skill doesn't exist but is skipped due to non-interactive mode (or user declining), filesCreated stays at 0 without the skill being installed. The filesCreated === 0 check then shows "All configuration files already exist" — which is factually wrong since the skill was never installed. This is a likely upgrade scenario: users with pre-existing config/workflow but no skill directory running warden init in CI get told everything is set up when it isn't.
Additional Locations (1)
Use non-recursive rmSync first to handle files and symlinks (including broken ones), then fall back to recursive rmSync for directories. Fixes potential behavior difference with recursive rmSync on broken symlinks across Node.js versions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rmSync with force:true may silently skip broken symlinks because it internally uses stat (which follows symlinks) to check existence. Instead, attempt symlinkSync first and handle EEXIST by removing the existing entry (unlinkSync for files/symlinks, rmSync for dirs) then retrying. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>


warden initnow installs the built-in warden skill alongside the configand workflow scaffolding. In interactive mode it prompts with a default-yes
[Y/n]; with--forceit installs unconditionally; in CI it skips silently.When
.claude/exists in the repo, a.claude/skills/wardensymlink iscreated pointing to
.agents/skills/wardenfor Claude Code compatibility.Also restructures the init output into a sectioned layout (CONFIG, WORKFLOW,
SKILL) matching the visual style of the analysis flow, instead of flat
Created/Skipped lines.