mirror of
https://github.com/alirezarezvani/ClaudeForge.git
synced 2026-07-04 10:53:16 -04:00
7d22a50c37
Cross-tool adoption. Codex, Gemini Code Assist, and any AI tool honouring
the AGENTS.md convention can now read the same instructions as Claude,
without the user maintaining two files by hand.
hooks/claude-to-agents.py (new, standalone, idempotent):
- argparse: --mode={symlink,copy,inline-chain} (default symlink),
--source (default CLAUDE.md), --output (default AGENTS.md), --force.
- Symlink mode: ln -s CLAUDE.md AGENTS.md. Windows falls back to
--copy with a stderr notice.
- Copy mode: byte-for-byte snapshot via shutil.copyfile.
- Inline-chain mode: depth-first walk of @path imports, recursive,
cycle-safe (each file read at most once). Output flattens every
chained sub-file under <!-- inlined from <rel> --> markers and
strips two flavours of Claude-only scaffolding:
• The @path import lines themselves (other tools don't resolve
them).
• Backlink quote-blocks ("> Parent context: ..." /
"> Chained import: `@../CLAUDE.md`") that we emit on every
sub-CLAUDE.md.
- Safety: existing AGENTS.md (file or symlink) is renamed to
AGENTS.md.backup.<UTC-ts> before overwrite. --force skips the
backup. Microsecond timestamp precision so back-to-back writes
don't collide.
- Exit codes: 0 success, 1 user error (missing source / unknown
mode), 2 filesystem error (symlink failure).
command/claude-to-agents.md (new slash command):
- allowed-tools: Read, Write, Glob, Bash(python3:*), Bash(ls:*),
Bash(test:*), Bash(readlink:*).
- disallowedTools: WebFetch, WebSearch (no exfiltration vector).
- argument-hint and when_to_use surface the three modes.
- Body specifies a heuristic: default to --symlink for single-file
projects, recommend --inline-chain when find . -name CLAUDE.md
returns more than one. Documents per-mode verification commands.
.claude-plugin/plugin.json:
Registers ./command/claude-to-agents.md alongside the existing two.
install.sh + install.ps1:
Banner and uninstall sections list the new command. The command
install loop already iterates command/*.md so the file itself is
copied automatically.
README.md:
/claude-to-agents added to "What's Included" under Slash commands
with a one-paragraph description covering all three modes.
Quick Stats: "2 slash commands" -> "3 slash commands".
CHANGELOG.md:
New "wave 5" entry under [Unreleased].
Verified (7/7 integration checks):
- Plugin manifest registers the command; all 9 referenced paths
resolve on disk.
- Slash command frontmatter has all required fields including
WebFetch in disallowedTools.
- Script has executable bit set.
- Both install scripts list claude-to-agents.md.
- install.sh passes bash -n.
- End-to-end against a synthetic chained CLAUDE.md tree with a
deliberate back-import cycle: symlink mode creates valid symlink;
copy mode produces bytewise-identical AGENTS.md and backs up the
prior symlink (1 backup); inline-chain mode inlines both
sub-files, strips backlinks AND chain-import lines, handles the
cycle (each file read once), backs up the prior file (2 backups
total).
- Missing source returns rc=1 with stderr message.
430 lines
17 KiB
PowerShell
430 lines
17 KiB
PowerShell
# ClaudeForge Installer for Windows
|
||
# PowerShell installation script
|
||
# Version: 2.0.0
|
||
|
||
#Requires -Version 5.1
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
# Colors for output
|
||
function Write-Info {
|
||
param([string]$Message)
|
||
Write-Host "ℹ $Message" -ForegroundColor Blue
|
||
}
|
||
|
||
function Write-Success {
|
||
param([string]$Message)
|
||
Write-Host "✓ $Message" -ForegroundColor Green
|
||
}
|
||
|
||
function Write-Warning {
|
||
param([string]$Message)
|
||
Write-Host "⚠ $Message" -ForegroundColor Yellow
|
||
}
|
||
|
||
function Write-Error-Custom {
|
||
param([string]$Message)
|
||
Write-Host "✗ $Message" -ForegroundColor Red
|
||
}
|
||
|
||
# Banner
|
||
Write-Host ""
|
||
Write-Host "╔════════════════════════════════════════╗" -ForegroundColor Blue
|
||
Write-Host "║ ║" -ForegroundColor Blue
|
||
Write-Host "║ " -NoNewline -ForegroundColor Blue
|
||
Write-Host "ClaudeForge Installer" -NoNewline -ForegroundColor Green
|
||
Write-Host " ║" -ForegroundColor Blue
|
||
Write-Host "║ ║" -ForegroundColor Blue
|
||
Write-Host "║ Automated CLAUDE.md Management Tool ║" -ForegroundColor Blue
|
||
Write-Host "║ Version 2.0.0 ║" -ForegroundColor Blue
|
||
Write-Host "║ ║" -ForegroundColor Blue
|
||
Write-Host "╚════════════════════════════════════════╝" -ForegroundColor Blue
|
||
Write-Host ""
|
||
|
||
# Check if running from correct directory or need to download
|
||
$RemoteInstall = $false
|
||
$OriginalDir = Get-Location
|
||
|
||
if (-not (Test-Path "skill") -or -not (Test-Path "command") -or -not (Test-Path "agent")) {
|
||
Write-Info "Installing from GitHub..."
|
||
$RemoteInstall = $true
|
||
|
||
# Create temporary directory
|
||
$TempDir = New-Item -ItemType Directory -Path ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.Guid]::NewGuid().ToString())) -Force
|
||
Set-Location $TempDir
|
||
|
||
Write-Info "Downloading ClaudeForge v2.0.0..."
|
||
|
||
# Download archive
|
||
$archiveUrl = "https://github.com/alirezarezvani/ClaudeForge/archive/refs/heads/main.zip"
|
||
$archivePath = Join-Path $TempDir "claudeforge.zip"
|
||
|
||
try {
|
||
Invoke-WebRequest -Uri $archiveUrl -OutFile $archivePath -UseBasicParsing
|
||
} catch {
|
||
Write-Error-Custom "Failed to download ClaudeForge. Please check your internet connection."
|
||
exit 1
|
||
}
|
||
|
||
Write-Info "Extracting files..."
|
||
Expand-Archive -Path $archivePath -DestinationPath $TempDir -Force
|
||
Set-Location (Join-Path $TempDir "ClaudeForge-main")
|
||
|
||
Write-Success "Downloaded ClaudeForge successfully"
|
||
}
|
||
|
||
# Check for Claude Code installation
|
||
Write-Info "Checking for Claude Code installation..."
|
||
|
||
$claudeDir = "$env:USERPROFILE\.claude"
|
||
if (-not (Test-Path $claudeDir)) {
|
||
Write-Warning "Claude Code user directory (~/.claude) not found."
|
||
Write-Info "Creating $claudeDir directory structure..."
|
||
New-Item -ItemType Directory -Path "$claudeDir\skills" -Force | Out-Null
|
||
New-Item -ItemType Directory -Path "$claudeDir\commands" -Force | Out-Null
|
||
New-Item -ItemType Directory -Path "$claudeDir\agents" -Force | Out-Null
|
||
Write-Success "Directory structure created"
|
||
}
|
||
|
||
# Check Claude Code version
|
||
function Check-ClaudeCodeVersion {
|
||
$version = $null
|
||
|
||
try {
|
||
$output = & claude --version 2>&1
|
||
if ($output -match '\d+\.\d+\.\d+') {
|
||
$version = $matches[0]
|
||
}
|
||
} catch {
|
||
Write-Warning "Could not detect Claude Code version"
|
||
Write-Info "ClaudeForge v2.0 requires Claude Code 2.1.0 or later"
|
||
Write-Info "Continuing with installation (compatibility not guaranteed)"
|
||
return $true
|
||
}
|
||
|
||
if ([string]::IsNullOrEmpty($version)) {
|
||
Write-Warning "Could not detect Claude Code version"
|
||
Write-Info "Continuing with installation (compatibility not guaranteed)"
|
||
return $true
|
||
}
|
||
|
||
$parts = $version.Split('.')
|
||
$major = [int]$parts[0]
|
||
$minor = [int]$parts[1]
|
||
|
||
if ($major -lt 2) {
|
||
Write-Error-Custom "Claude Code version $version is not supported"
|
||
Write-Error-Custom "Please upgrade to Claude Code 2.1.0 or later"
|
||
return $false
|
||
} elseif ($major -eq 2 -and $minor -lt 1) {
|
||
Write-Warning "Claude Code version $version may have limited features"
|
||
Write-Info "Recommended: Claude Code 2.1.4 or later for full hook support"
|
||
}
|
||
|
||
Write-Success "Claude Code version $version detected"
|
||
return $true
|
||
}
|
||
|
||
Write-Info "Checking Claude Code version..."
|
||
if (-not (Check-ClaudeCodeVersion)) {
|
||
exit 1
|
||
}
|
||
|
||
# Ask for installation scope
|
||
Write-Host ""
|
||
Write-Info "Where would you like to install ClaudeForge?"
|
||
Write-Host ""
|
||
Write-Host " " -NoNewline
|
||
Write-Host "1)" -ForegroundColor Green -NoNewline
|
||
Write-Host " User-level (~/.claude/) - Available in all Claude Code projects"
|
||
Write-Host " " -NoNewline
|
||
Write-Host "2)" -ForegroundColor Green -NoNewline
|
||
Write-Host " Project-level (./.claude/) - Available only in current project"
|
||
Write-Host ""
|
||
|
||
$validChoice = $false
|
||
while (-not $validChoice) {
|
||
$choice = Read-Host "Enter choice [1/2]"
|
||
switch ($choice) {
|
||
"1" {
|
||
$skillsDir = "$env:USERPROFILE\.claude\skills"
|
||
$commandsDir = "$env:USERPROFILE\.claude\commands"
|
||
$agentsDir = "$env:USERPROFILE\.claude\agents"
|
||
$scope = "user-level"
|
||
Write-Success "Installing at user-level (all projects)"
|
||
$validChoice = $true
|
||
}
|
||
"2" {
|
||
if ($RemoteInstall) {
|
||
$skillsDir = Join-Path $OriginalDir ".claude\skills"
|
||
$commandsDir = Join-Path $OriginalDir ".claude\commands"
|
||
$agentsDir = Join-Path $OriginalDir ".claude\agents"
|
||
} else {
|
||
$skillsDir = ".\.claude\skills"
|
||
$commandsDir = ".\.claude\commands"
|
||
$agentsDir = ".\.claude\agents"
|
||
}
|
||
$scope = "project-level"
|
||
Write-Success "Installing at project-level (current project only)"
|
||
$validChoice = $true
|
||
}
|
||
default {
|
||
Write-Error-Custom "Invalid choice. Please enter 1 or 2."
|
||
}
|
||
}
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Info "Installation will create:"
|
||
Write-Host " • Skill: $skillsDir\claudeforge-skill\"
|
||
Write-Host " • Skill: $skillsDir\karpathy-guidelines\"
|
||
Write-Host " • Skill: $skillsDir\claude-md-drift-audit\"
|
||
Write-Host " • Skill: $skillsDir\claude-md-link-check\"
|
||
Write-Host " • Skill: $skillsDir\claude-md-dependency-rescan\"
|
||
Write-Host " • Command: $commandsDir\enhance-claude-md.md"
|
||
Write-Host " • Command: $commandsDir\sync-claude-md.md"
|
||
Write-Host " • Command: $commandsDir\claude-to-agents.md"
|
||
Write-Host " • Agent: $agentsDir\claude-md-guardian.md"
|
||
Write-Host ""
|
||
|
||
# Confirm installation
|
||
$confirm = Read-Host "Proceed with installation? [Y/n]"
|
||
if ([string]::IsNullOrEmpty($confirm)) { $confirm = "Y" }
|
||
|
||
if ($confirm -notmatch "^[Yy]$") {
|
||
Write-Warning "Installation cancelled."
|
||
exit 0
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Info "Starting installation..."
|
||
Write-Host ""
|
||
|
||
# Create directories if they don't exist
|
||
New-Item -ItemType Directory -Path $skillsDir -Force | Out-Null
|
||
New-Item -ItemType Directory -Path $commandsDir -Force | Out-Null
|
||
New-Item -ItemType Directory -Path $agentsDir -Force | Out-Null
|
||
|
||
# Install skill
|
||
Write-Info "Installing ClaudeForge skill..."
|
||
$skillPath = "$skillsDir\claudeforge-skill"
|
||
if (Test-Path $skillPath) {
|
||
Write-Warning "Existing skill found. Creating backup..."
|
||
$backupName = "claudeforge-skill.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||
Move-Item -Path $skillPath -Destination "$skillsDir\$backupName" -Force
|
||
Write-Success "Backup created"
|
||
}
|
||
Copy-Item -Path "skill" -Destination $skillPath -Recurse -Force
|
||
Write-Success "Skill installed → $skillPath\"
|
||
|
||
# Install karpathy-guidelines as a separate top-level skill so it is
|
||
# discoverable as its own skill and applies to every project (not only
|
||
# during /enhance-claude-md runs).
|
||
Write-Info "Installing karpathy-guidelines skill..."
|
||
$karpathyPath = "$skillsDir\karpathy-guidelines"
|
||
if (Test-Path $karpathyPath) {
|
||
Write-Warning "Existing karpathy-guidelines skill found. Creating backup..."
|
||
$backupName = "karpathy-guidelines.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||
Move-Item -Path $karpathyPath -Destination "$skillsDir\$backupName" -Force
|
||
Write-Success "Backup created"
|
||
}
|
||
Copy-Item -Path "skill\karpathy-guidelines" -Destination $karpathyPath -Recurse -Force
|
||
# Remove the nested duplicate inside claudeforge-skill so the skill exists once.
|
||
$nestedKarpathy = "$skillPath\karpathy-guidelines"
|
||
if (Test-Path $nestedKarpathy) {
|
||
Remove-Item -Path $nestedKarpathy -Recurse -Force
|
||
}
|
||
Write-Success "Karpathy guidelines installed → $karpathyPath\"
|
||
|
||
# Install the forked task-style audit skills as separate top-level skills
|
||
# so each is invocable standalone and discoverable by /sync-claude-md --weekly.
|
||
$auditSkills = @("claude-md-drift-audit", "claude-md-link-check", "claude-md-dependency-rescan")
|
||
foreach ($auditSkill in $auditSkills) {
|
||
Write-Info "Installing $auditSkill skill..."
|
||
$auditTarget = "$skillsDir\$auditSkill"
|
||
if (Test-Path $auditTarget) {
|
||
Write-Warning "Existing $auditSkill skill found. Creating backup..."
|
||
$backupName = "$auditSkill.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||
Move-Item -Path $auditTarget -Destination "$skillsDir\$backupName" -Force
|
||
}
|
||
Copy-Item -Path "skill\$auditSkill" -Destination $auditTarget -Recurse -Force
|
||
$nestedAudit = "$skillPath\$auditSkill"
|
||
if (Test-Path $nestedAudit) {
|
||
Remove-Item -Path $nestedAudit -Recurse -Force
|
||
}
|
||
Write-Success "$auditSkill installed → $auditTarget\"
|
||
}
|
||
|
||
# Install slash commands. Each .md file in command/ becomes its own
|
||
# top-level command file so it registers as /<name>. README.md is skipped.
|
||
Write-Info "Installing slash commands..."
|
||
|
||
# Migrate legacy bundle directory if present.
|
||
$legacyBundle = "$commandsDir\enhance-claude-md"
|
||
if (Test-Path $legacyBundle) {
|
||
Write-Warning "Legacy command bundle found. Creating backup..."
|
||
$backupName = "enhance-claude-md.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||
Move-Item -Path $legacyBundle -Destination "$commandsDir\$backupName" -Force
|
||
Write-Success "Backup created"
|
||
}
|
||
|
||
Get-ChildItem -Path "command" -Filter "*.md" -File | Where-Object { $_.Name -ne "README.md" } | ForEach-Object {
|
||
$cmdTarget = Join-Path $commandsDir $_.Name
|
||
if (Test-Path $cmdTarget) {
|
||
Write-Warning "Existing $($_.Name) found. Creating backup..."
|
||
$backupName = "$($_.Name).backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||
Move-Item -Path $cmdTarget -Destination (Join-Path $commandsDir $backupName) -Force
|
||
}
|
||
Copy-Item -Path $_.FullName -Destination $cmdTarget -Force
|
||
Write-Success "Command installed → $cmdTarget"
|
||
}
|
||
|
||
# Install guardian agent
|
||
Write-Info "Installing claude-md-guardian agent..."
|
||
$agentPath = "$agentsDir\claude-md-guardian.md"
|
||
if (Test-Path $agentPath) {
|
||
Write-Warning "Existing agent found. Creating backup..."
|
||
$backupName = "claude-md-guardian.md.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||
Move-Item -Path $agentPath -Destination "$agentsDir\$backupName" -Force
|
||
Write-Success "Backup created"
|
||
}
|
||
Copy-Item -Path "agent\claude-md-guardian.md" -Destination $agentPath -Force
|
||
Write-Success "Agent installed → $agentPath"
|
||
|
||
# Optional: Install quality hooks
|
||
Write-Host ""
|
||
$installHooks = Read-Host "Would you like to install quality hooks (pre-commit validation)? [y/N]"
|
||
if ([string]::IsNullOrEmpty($installHooks)) { $installHooks = "N" }
|
||
|
||
if ($installHooks -match "^[Yy]$") {
|
||
if ($scope -eq "project-level") {
|
||
Write-Info "Installing quality hooks..."
|
||
if ($RemoteInstall) {
|
||
$hooksDir = Join-Path $OriginalDir ".claude\hooks"
|
||
} else {
|
||
$hooksDir = ".claude\hooks"
|
||
}
|
||
New-Item -ItemType Directory -Path $hooksDir -Force | Out-Null
|
||
Copy-Item -Path "hooks\pre-commit.sh" -Destination "$hooksDir\" -Force
|
||
Write-Success "Quality hooks installed → $hooksDir\"
|
||
} else {
|
||
Write-Warning "Quality hooks can only be installed at project-level"
|
||
Write-Info "Run installer with option 2 in your project directory"
|
||
}
|
||
}
|
||
|
||
# Validate v2.1.4 compatibility
|
||
function Validate-V214Compatibility {
|
||
param(
|
||
[string]$skillsDir,
|
||
[string]$agentsDir
|
||
)
|
||
|
||
Write-Info "Validating v2.1.4 compatibility..."
|
||
|
||
$skillFile = Join-Path $skillsDir "claudeforge-skill\SKILL.md"
|
||
$agentFile = Join-Path $agentsDir "claude-md-guardian.md"
|
||
|
||
# Verify new syntax is present
|
||
if (-not (Select-String -Path $skillFile -Pattern "permissions:" -Quiet)) {
|
||
Write-Error-Custom "Skill missing v2.1.4 permissions syntax"
|
||
return $false
|
||
}
|
||
|
||
if (-not (Select-String -Path $agentFile -Pattern "permissions:" -Quiet)) {
|
||
Write-Error-Custom "Agent missing v2.1.4 permissions syntax"
|
||
return $false
|
||
}
|
||
|
||
# Check for hooks
|
||
if (Select-String -Path $agentFile -Pattern "hooks:" -Quiet) {
|
||
Write-Success "Guardian agent hooks configured"
|
||
} else {
|
||
Write-Warning "Guardian agent has no hooks (optional)"
|
||
}
|
||
|
||
Write-Success "v2.1.4 compatibility validated"
|
||
return $true
|
||
}
|
||
|
||
Write-Host ""
|
||
if (-not (Validate-V214Compatibility -skillsDir $skillsDir -agentsDir $agentsDir)) {
|
||
Write-Error-Custom "Installation validation failed"
|
||
exit 1
|
||
}
|
||
|
||
# Installation complete
|
||
Write-Host ""
|
||
Write-Host "╔════════════════════════════════════════╗" -ForegroundColor Green
|
||
Write-Host "║ ║" -ForegroundColor Green
|
||
Write-Host "║ Installation completed successfully!║" -ForegroundColor Green
|
||
Write-Host "║ ║" -ForegroundColor Green
|
||
Write-Host "╚════════════════════════════════════════╝" -ForegroundColor Green
|
||
Write-Host ""
|
||
|
||
# Next steps
|
||
Write-Info "Next steps:"
|
||
Write-Host ""
|
||
Write-Host " " -NoNewline
|
||
Write-Host "1." -ForegroundColor Green -NoNewline
|
||
Write-Host " Restart Claude Code (important!)"
|
||
Write-Host " " -NoNewline
|
||
Write-Host "2." -ForegroundColor Green -NoNewline
|
||
Write-Host " Navigate to your project directory"
|
||
Write-Host " " -NoNewline
|
||
Write-Host "3." -ForegroundColor Green -NoNewline
|
||
Write-Host " Run the command:"
|
||
Write-Host ""
|
||
Write-Host " /enhance-claude-md" -ForegroundColor Blue
|
||
Write-Host ""
|
||
Write-Host " " -NoNewline
|
||
Write-Host "4." -ForegroundColor Green -NoNewline
|
||
Write-Host " Follow the interactive prompts"
|
||
Write-Host ""
|
||
|
||
# Additional information
|
||
Write-Info "Documentation:"
|
||
Write-Host ""
|
||
Write-Host " • Quick Start: docs\QUICK_START.md"
|
||
Write-Host " • Installation: docs\INSTALLATION.md"
|
||
Write-Host " • Architecture: docs\ARCHITECTURE.md"
|
||
Write-Host " • Troubleshooting: docs\TROUBLESHOOTING.md"
|
||
Write-Host ""
|
||
Write-Host " • GitHub: https://github.com/alirezarezvani/ClaudeForge" -ForegroundColor Blue
|
||
Write-Host ""
|
||
|
||
# Uninstall instructions
|
||
Write-Info "To uninstall, run:"
|
||
Write-Host ""
|
||
if ($scope -eq "user-level") {
|
||
Write-Host " Remove-Item -Recurse -Force ~\.claude\skills\claudeforge-skill"
|
||
Write-Host " Remove-Item -Recurse -Force ~\.claude\skills\karpathy-guidelines"
|
||
Write-Host " Remove-Item -Recurse -Force ~\.claude\skills\claude-md-drift-audit"
|
||
Write-Host " Remove-Item -Recurse -Force ~\.claude\skills\claude-md-link-check"
|
||
Write-Host " Remove-Item -Recurse -Force ~\.claude\skills\claude-md-dependency-rescan"
|
||
Write-Host " Remove-Item -Force ~\.claude\commands\enhance-claude-md.md"
|
||
Write-Host " Remove-Item -Force ~\.claude\commands\sync-claude-md.md"
|
||
Write-Host " Remove-Item -Force ~\.claude\commands\claude-to-agents.md"
|
||
Write-Host " Remove-Item -Force ~\.claude\agents\claude-md-guardian.md"
|
||
} else {
|
||
Write-Host " Remove-Item -Recurse -Force .\.claude\skills\claudeforge-skill"
|
||
Write-Host " Remove-Item -Recurse -Force .\.claude\skills\karpathy-guidelines"
|
||
Write-Host " Remove-Item -Recurse -Force .\.claude\skills\claude-md-drift-audit"
|
||
Write-Host " Remove-Item -Recurse -Force .\.claude\skills\claude-md-link-check"
|
||
Write-Host " Remove-Item -Recurse -Force .\.claude\skills\claude-md-dependency-rescan"
|
||
Write-Host " Remove-Item -Force .\.claude\commands\enhance-claude-md.md"
|
||
Write-Host " Remove-Item -Force .\.claude\commands\sync-claude-md.md"
|
||
Write-Host " Remove-Item -Force .\.claude\commands\claude-to-agents.md"
|
||
Write-Host " Remove-Item -Force .\.claude\agents\claude-md-guardian.md"
|
||
}
|
||
Write-Host ""
|
||
|
||
Write-Success "Thank you for installing ClaudeForge!"
|
||
Write-Host ""
|
||
|
||
# Cleanup temporary directory if remote install
|
||
if ($RemoteInstall) {
|
||
Set-Location $env:USERPROFILE
|
||
Remove-Item -Recurse -Force $TempDir -ErrorAction SilentlyContinue
|
||
}
|