@kaitranntt/ccs 3.4.6 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +470 -146
- package/README.md +338 -151
- package/README.vi.md +484 -157
- package/VERSION +1 -1
- package/bin/auth/auth-commands.js +98 -13
- package/bin/auth/profile-detector.js +11 -6
- package/bin/ccs.js +87 -2
- package/bin/utils/error-codes.js +59 -0
- package/bin/utils/error-manager.js +38 -32
- package/bin/utils/helpers.js +65 -1
- package/bin/utils/progress-indicator.js +111 -0
- package/bin/utils/prompt.js +134 -0
- package/bin/utils/shell-completion.js +234 -0
- package/lib/ccs +541 -25
- package/lib/ccs.ps1 +381 -20
- package/lib/error-codes.ps1 +55 -0
- package/lib/error-codes.sh +63 -0
- package/lib/progress-indicator.ps1 +120 -0
- package/lib/progress-indicator.sh +117 -0
- package/lib/prompt.ps1 +109 -0
- package/lib/prompt.sh +99 -0
- package/package.json +1 -1
- package/scripts/completion/README.md +308 -0
- package/scripts/completion/ccs.bash +81 -0
- package/scripts/completion/ccs.fish +92 -0
- package/scripts/completion/ccs.ps1 +157 -0
- package/scripts/completion/ccs.zsh +130 -0
- package/scripts/postinstall.js +24 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# CCS Progress Indicator (PowerShell 5.1+ compatible)
|
|
2
|
+
# Simple spinner for long-running operations
|
|
3
|
+
# NO external dependencies - ASCII-only for cross-platform compatibility
|
|
4
|
+
|
|
5
|
+
$ErrorActionPreference = "Stop"
|
|
6
|
+
|
|
7
|
+
# Show simple spinner (synchronous)
|
|
8
|
+
function Show-Spinner {
|
|
9
|
+
param(
|
|
10
|
+
[string]$Message,
|
|
11
|
+
[scriptblock]$Task
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# TTY detection: only animate if not redirected and not in CI
|
|
15
|
+
$IsTTY = -not [Console]::IsOutputRedirected -and -not $env:CI -and -not $env:NO_COLOR
|
|
16
|
+
|
|
17
|
+
$StartTime = Get-Date
|
|
18
|
+
|
|
19
|
+
if (-not $IsTTY) {
|
|
20
|
+
# Non-TTY: just print message and run task
|
|
21
|
+
Write-Host "[i] $Message..." -ForegroundColor Gray
|
|
22
|
+
$result = & $Task
|
|
23
|
+
Write-Host "[OK] $Message" -ForegroundColor Green
|
|
24
|
+
return $result
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ASCII-only frames for cross-platform compatibility
|
|
28
|
+
$Frames = @('|', '/', '-', '\')
|
|
29
|
+
$FrameIndex = 0
|
|
30
|
+
|
|
31
|
+
# Start task in background job
|
|
32
|
+
$Job = Start-Job -ScriptBlock $Task
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
# Animate spinner while job is running
|
|
36
|
+
while ($Job.State -eq 'Running') {
|
|
37
|
+
$Frame = $Frames[$FrameIndex]
|
|
38
|
+
$Elapsed = [math]::Round(((Get-Date) - $StartTime).TotalSeconds, 1)
|
|
39
|
+
Write-Host "`r[$Frame] $Message... ($($Elapsed)s)" -NoNewline -ForegroundColor Cyan
|
|
40
|
+
$FrameIndex = ($FrameIndex + 1) % $Frames.Length
|
|
41
|
+
Start-Sleep -Milliseconds 100
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Clear spinner line
|
|
45
|
+
Write-Host "`r$(' ' * 80)`r" -NoNewline
|
|
46
|
+
|
|
47
|
+
# Check job result
|
|
48
|
+
$JobResult = Receive-Job -Job $Job -ErrorAction Stop
|
|
49
|
+
$Elapsed = [math]::Round(((Get-Date) - $StartTime).TotalSeconds, 1)
|
|
50
|
+
Write-Host "[OK] $Message ($($Elapsed)s)" -ForegroundColor Green
|
|
51
|
+
|
|
52
|
+
return $JobResult
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
# Clear spinner line
|
|
56
|
+
Write-Host "`r$(' ' * 80)`r" -NoNewline
|
|
57
|
+
Write-Host "[X] $Message" -ForegroundColor Red
|
|
58
|
+
throw
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
# Cleanup job
|
|
62
|
+
if ($Job) {
|
|
63
|
+
Remove-Job -Job $Job -Force -ErrorAction SilentlyContinue
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Show progress counter for multi-step operations
|
|
69
|
+
function Show-ProgressStep {
|
|
70
|
+
param(
|
|
71
|
+
[int]$Current,
|
|
72
|
+
[int]$Total,
|
|
73
|
+
[string]$Message
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# TTY detection
|
|
77
|
+
$IsTTY = -not [Console]::IsOutputRedirected -and -not $env:CI
|
|
78
|
+
|
|
79
|
+
if (-not $IsTTY) {
|
|
80
|
+
Write-Host "[$Current/$Total] $Message" -ForegroundColor Gray
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Show progress with carriage return (can be overwritten)
|
|
85
|
+
Write-Host "`r[$Current/$Total] $Message..." -NoNewline -ForegroundColor Cyan
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Clear progress line
|
|
89
|
+
function Clear-Progress {
|
|
90
|
+
$IsTTY = -not [Console]::IsOutputRedirected -and -not $env:CI
|
|
91
|
+
|
|
92
|
+
if ($IsTTY) {
|
|
93
|
+
Write-Host "`r$(' ' * 80)`r" -NoNewline
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Simple status message (for operations that don't need spinners)
|
|
98
|
+
function Write-Status {
|
|
99
|
+
param(
|
|
100
|
+
[string]$Message,
|
|
101
|
+
[ValidateSet('Info', 'Success', 'Warning', 'Error')]
|
|
102
|
+
[string]$Type = 'Info'
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
$Prefix = switch ($Type) {
|
|
106
|
+
'Info' { '[i]'; break }
|
|
107
|
+
'Success' { '[OK]'; break }
|
|
108
|
+
'Warning' { '[!]'; break }
|
|
109
|
+
'Error' { '[X]'; break }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
$Color = switch ($Type) {
|
|
113
|
+
'Info' { 'Gray'; break }
|
|
114
|
+
'Success' { 'Green'; break }
|
|
115
|
+
'Warning' { 'Yellow'; break }
|
|
116
|
+
'Error' { 'Red'; break }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
Write-Host "$Prefix $Message" -ForegroundColor $Color
|
|
120
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CCS Progress Indicator (bash 3.2+ compatible)
|
|
3
|
+
# Simple spinner for long-running operations
|
|
4
|
+
# NO external dependencies - ASCII-only for cross-platform compatibility
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Show spinner while a background process is running
|
|
9
|
+
# Usage: show_spinner "message" $pid
|
|
10
|
+
show_spinner() {
|
|
11
|
+
local message="$1"
|
|
12
|
+
local pid="$2"
|
|
13
|
+
|
|
14
|
+
# TTY detection: only animate if stderr is TTY and not in CI
|
|
15
|
+
if [[ ! -t 2 ]] || [[ -n "${CI:-}" ]] || [[ -n "${NO_COLOR:-}" ]]; then
|
|
16
|
+
# Non-TTY: just print message once
|
|
17
|
+
echo "[i] $message..." >&2
|
|
18
|
+
wait "$pid" 2>/dev/null || true
|
|
19
|
+
return
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# ASCII-only frames for cross-platform compatibility
|
|
23
|
+
local frames=('|' '/' '-' '\\')
|
|
24
|
+
local frame_idx=0
|
|
25
|
+
local start_time=$(date +%s)
|
|
26
|
+
|
|
27
|
+
# Animate spinner while process is running
|
|
28
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
29
|
+
local frame="${frames[$frame_idx]}"
|
|
30
|
+
local elapsed=$(($(date +%s) - start_time))
|
|
31
|
+
printf "\r[%s] %s... (%ds)" "$frame" "$message" "$elapsed" >&2
|
|
32
|
+
frame_idx=$(( (frame_idx + 1) % 4 ))
|
|
33
|
+
sleep 0.1
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
# Clear spinner line
|
|
37
|
+
printf "\r\033[K" >&2
|
|
38
|
+
|
|
39
|
+
# Wait for process to complete and capture exit code
|
|
40
|
+
wait "$pid" 2>/dev/null || true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Show spinner with success/fail result
|
|
44
|
+
# Usage: spinner_with_result "message" "command"
|
|
45
|
+
spinner_with_result() {
|
|
46
|
+
local message="$1"
|
|
47
|
+
shift
|
|
48
|
+
local command=("$@")
|
|
49
|
+
|
|
50
|
+
local start_time=$(date +%s)
|
|
51
|
+
|
|
52
|
+
# TTY detection
|
|
53
|
+
if [[ ! -t 2 ]] || [[ -n "${CI:-}" ]] || [[ -n "${NO_COLOR:-}" ]]; then
|
|
54
|
+
# Non-TTY: just print message and run command
|
|
55
|
+
echo "[i] $message..." >&2
|
|
56
|
+
if "${command[@]}"; then
|
|
57
|
+
echo "[OK] $message" >&2
|
|
58
|
+
return 0
|
|
59
|
+
else
|
|
60
|
+
echo "[X] $message" >&2
|
|
61
|
+
return 1
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Run command in background
|
|
66
|
+
"${command[@]}" &>/dev/null &
|
|
67
|
+
local pid=$!
|
|
68
|
+
|
|
69
|
+
# Show spinner
|
|
70
|
+
local frames=('|' '/' '-' '\\')
|
|
71
|
+
local frame_idx=0
|
|
72
|
+
|
|
73
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
74
|
+
local frame="${frames[$frame_idx]}"
|
|
75
|
+
local elapsed=$(($(date +%s) - start_time))
|
|
76
|
+
printf "\r[%s] %s... (%ds)" "$frame" "$message" "$elapsed" >&2
|
|
77
|
+
frame_idx=$(( (frame_idx + 1) % 4 ))
|
|
78
|
+
sleep 0.1
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
# Clear spinner line
|
|
82
|
+
printf "\r\033[K" >&2
|
|
83
|
+
|
|
84
|
+
# Check result
|
|
85
|
+
if wait "$pid" 2>/dev/null; then
|
|
86
|
+
local elapsed=$(($(date +%s) - start_time))
|
|
87
|
+
echo "[OK] $message (${elapsed}s)" >&2
|
|
88
|
+
return 0
|
|
89
|
+
else
|
|
90
|
+
echo "[X] $message" >&2
|
|
91
|
+
return 1
|
|
92
|
+
fi
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Simple progress counter (for multi-step operations)
|
|
96
|
+
# Usage: show_progress_step 3 10 "Checking configuration"
|
|
97
|
+
show_progress_step() {
|
|
98
|
+
local current="$1"
|
|
99
|
+
local total="$2"
|
|
100
|
+
local message="$3"
|
|
101
|
+
|
|
102
|
+
# TTY detection
|
|
103
|
+
if [[ ! -t 2 ]] || [[ -n "${CI:-}" ]]; then
|
|
104
|
+
echo "[${current}/${total}] $message" >&2
|
|
105
|
+
return
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Show progress with carriage return (can be overwritten)
|
|
109
|
+
printf "\r[%d/%d] %s..." "$current" "$total" "$message" >&2
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Clear progress line
|
|
113
|
+
clear_progress() {
|
|
114
|
+
if [[ -t 2 ]] && [[ -z "${CI:-}" ]]; then
|
|
115
|
+
printf "\r\033[K" >&2
|
|
116
|
+
fi
|
|
117
|
+
}
|
package/lib/prompt.ps1
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# CCS Interactive Prompt Utilities (PowerShell 5.1+ compatible)
|
|
2
|
+
# NO external dependencies
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = "Stop"
|
|
5
|
+
|
|
6
|
+
# Interactive confirmation prompt
|
|
7
|
+
function Confirm-Action {
|
|
8
|
+
param(
|
|
9
|
+
[string]$Message,
|
|
10
|
+
[ValidateSet('Yes', 'No')]
|
|
11
|
+
[string]$Default = 'No'
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Check for --yes flag (automation)
|
|
15
|
+
if ($env:CCS_YES -eq '1' -or $global:RemainingArgs -contains '--yes' -or $global:RemainingArgs -contains '-y') {
|
|
16
|
+
return $Default -eq 'Yes'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Check for --no-input flag (CI)
|
|
20
|
+
if ($env:CCS_NO_INPUT -eq '1' -or $global:RemainingArgs -contains '--no-input') {
|
|
21
|
+
Write-Host "[X] Interactive input required but --no-input specified" -ForegroundColor Red
|
|
22
|
+
exit 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Non-TTY: use default
|
|
26
|
+
if ([Console]::IsInputRedirected) {
|
|
27
|
+
return $Default -eq 'Yes'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Interactive prompt
|
|
31
|
+
$PromptText = if ($Default -eq 'Yes') {
|
|
32
|
+
"$Message [Y/n]: "
|
|
33
|
+
} else {
|
|
34
|
+
"$Message [y/N]: "
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
while ($true) {
|
|
38
|
+
Write-Host $PromptText -NoNewline -ForegroundColor Cyan
|
|
39
|
+
$Response = Read-Host
|
|
40
|
+
|
|
41
|
+
$Normalized = $Response.Trim().ToLower()
|
|
42
|
+
|
|
43
|
+
# Empty answer: use default
|
|
44
|
+
if ($Normalized -eq '' -or $Normalized -eq ' ') {
|
|
45
|
+
return $Default -eq 'Yes'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Valid answers
|
|
49
|
+
if ($Normalized -eq 'y' -or $Normalized -eq 'yes') {
|
|
50
|
+
return $true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ($Normalized -eq 'n' -or $Normalized -eq 'no') {
|
|
54
|
+
return $false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Invalid input: retry
|
|
58
|
+
Write-Host "[!] Please answer y or n" -ForegroundColor Yellow
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Interactive text input
|
|
63
|
+
function Read-Input {
|
|
64
|
+
param(
|
|
65
|
+
[string]$Message,
|
|
66
|
+
[string]$Default = '',
|
|
67
|
+
[scriptblock]$Validate = $null
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Non-TTY: use default or error
|
|
71
|
+
if ([Console]::IsInputRedirected) {
|
|
72
|
+
if ($Default) {
|
|
73
|
+
return $Default
|
|
74
|
+
}
|
|
75
|
+
throw "Interactive input required but stdin is redirected"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Interactive prompt
|
|
79
|
+
$PromptText = if ($Default) {
|
|
80
|
+
"$Message [$Default]: "
|
|
81
|
+
} else {
|
|
82
|
+
"$Message: "
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
while ($true) {
|
|
86
|
+
Write-Host $PromptText -NoNewline -ForegroundColor Cyan
|
|
87
|
+
$Response = Read-Host
|
|
88
|
+
|
|
89
|
+
$Value = if ($Response.Trim()) { $Response.Trim() } else { $Default }
|
|
90
|
+
|
|
91
|
+
# Validate input if validator provided
|
|
92
|
+
if ($Validate) {
|
|
93
|
+
$Error = & $Validate $Value
|
|
94
|
+
if ($Error) {
|
|
95
|
+
Write-Host "[!] $Error" -ForegroundColor Yellow
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return $Value
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Check if running in non-interactive mode
|
|
105
|
+
function Test-NonInteractive {
|
|
106
|
+
return [Console]::IsInputRedirected -or
|
|
107
|
+
$env:CCS_YES -eq '1' -or
|
|
108
|
+
$env:CCS_NO_INPUT -eq '1'
|
|
109
|
+
}
|
package/lib/prompt.sh
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CCS Interactive Prompt Utilities (bash 3.2+ compatible)
|
|
3
|
+
# NO external dependencies
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Interactive confirmation prompt
|
|
8
|
+
# Usage: confirm_action "message" "yes"|"no"
|
|
9
|
+
# Returns: 0 (true) or 1 (false)
|
|
10
|
+
confirm_action() {
|
|
11
|
+
local message="$1"
|
|
12
|
+
local default="${2:-no}" # Default to NO for safety
|
|
13
|
+
|
|
14
|
+
# Check for --yes flag (automation) - always returns true (0)
|
|
15
|
+
if [[ "${CCS_YES:-}" == "1" ]]; then
|
|
16
|
+
return 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Check for --no-input flag (CI)
|
|
20
|
+
if [[ "${CCS_NO_INPUT:-}" == "1" ]]; then
|
|
21
|
+
echo "[X] Interactive input required but --no-input specified" >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Non-TTY: use default
|
|
26
|
+
if [[ ! -t 0 ]]; then
|
|
27
|
+
[[ "$default" == "yes" ]] && return 0 || return 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Interactive prompt
|
|
31
|
+
local prompt
|
|
32
|
+
if [[ "$default" == "yes" ]]; then
|
|
33
|
+
prompt="$message [Y/n]: "
|
|
34
|
+
else
|
|
35
|
+
prompt="$message [y/N]: "
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
while true; do
|
|
39
|
+
read -r -p "$prompt" response >&2
|
|
40
|
+
response=$(echo "$response" | tr '[:upper:]' '[:lower:]')
|
|
41
|
+
|
|
42
|
+
case "$response" in
|
|
43
|
+
""|" ")
|
|
44
|
+
# Empty answer: use default
|
|
45
|
+
[[ "$default" == "yes" ]] && return 0 || return 1
|
|
46
|
+
;;
|
|
47
|
+
y|yes)
|
|
48
|
+
return 0
|
|
49
|
+
;;
|
|
50
|
+
n|no)
|
|
51
|
+
return 1
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
echo "[!] Please answer y or n" >&2
|
|
55
|
+
;;
|
|
56
|
+
esac
|
|
57
|
+
done
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Interactive text input
|
|
61
|
+
# Usage: prompt_input "message" "default_value"
|
|
62
|
+
# Outputs: user input to stdout
|
|
63
|
+
prompt_input() {
|
|
64
|
+
local message="$1"
|
|
65
|
+
local default="${2:-}"
|
|
66
|
+
|
|
67
|
+
# Non-TTY: use default or error
|
|
68
|
+
if [[ ! -t 0 ]]; then
|
|
69
|
+
if [[ -n "$default" ]]; then
|
|
70
|
+
echo "$default"
|
|
71
|
+
return 0
|
|
72
|
+
else
|
|
73
|
+
echo "[X] Interactive input required but stdin is not a TTY" >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Interactive prompt
|
|
79
|
+
local prompt
|
|
80
|
+
if [[ -n "$default" ]]; then
|
|
81
|
+
prompt="$message [$default]: "
|
|
82
|
+
else
|
|
83
|
+
prompt="$message: "
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
read -r -p "$prompt" response >&2
|
|
87
|
+
|
|
88
|
+
# Return user input or default
|
|
89
|
+
if [[ -z "$response" ]]; then
|
|
90
|
+
echo "$default"
|
|
91
|
+
else
|
|
92
|
+
echo "$response"
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Check if running in non-interactive mode
|
|
97
|
+
is_non_interactive() {
|
|
98
|
+
[[ ! -t 0 ]] || [[ "${CCS_YES:-}" == "1" ]] || [[ "${CCS_NO_INPUT:-}" == "1" ]]
|
|
99
|
+
}
|