javi-forge 1.37.0 → 1.38.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.md +5 -5
- package/assets/claude-hooks/javi-forge-skillguard-pre-tool-use.mjs +13 -11
- package/assets/claude-hooks/manifest.json +35 -1
- package/assets/hooks/manifest.json +6 -2
- package/assets/hooks/pre-push +206 -8
- package/ci-local/README.md +3 -6
- package/ci-local/ci-local.ps1 +36 -10
- package/ci-local/ci-local.sh +26 -9
- package/ci-local/install.ps1 +36 -14
- package/ci-local/install.sh +27 -10
- package/ci-local/lib/common.sh +3 -7
- package/ci-local/uninstall.ps1 +50 -0
- package/ci-local/uninstall.sh +38 -3
- package/dist/cli/bootstrap.d.ts +12 -0
- package/dist/cli/bootstrap.js +12 -0
- package/dist/cli/dispatch/simple-renderers.d.ts +8 -1
- package/dist/cli/dispatch/simple-renderers.js +11 -2
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +85 -0
- package/dist/commands/claude-hooks.d.ts +1 -0
- package/dist/commands/claude-hooks.js +12 -3
- package/dist/commands/codex-hooks.d.ts +1 -0
- package/dist/commands/codex-hooks.js +12 -3
- package/dist/commands/doctor.d.ts +16 -1
- package/dist/commands/doctor.js +54 -35
- package/dist/commands/init.d.ts +4 -1
- package/dist/commands/init.js +2 -2
- package/dist/index.js +4 -81
- package/dist/lib/claude-hook-manager.d.ts +11 -6
- package/dist/lib/claude-hook-manager.js +28 -21
- package/dist/lib/codex-hook-manager.d.ts +10 -4
- package/dist/lib/codex-hook-manager.js +44 -13
- package/dist/lib/platform-support.d.ts +18 -14
- package/dist/lib/platform-support.js +19 -17
- package/dist/lib/secure-fs-posix.d.ts +8 -13
- package/dist/lib/secure-fs-posix.js +11 -52
- package/dist/lib/secure-fs-transaction.d.ts +2 -3
- package/dist/types/index.d.ts +7 -2
- package/dist/ui/Doctor.d.ts +2 -0
- package/dist/ui/Doctor.js +13 -0
- package/lib/common.sh +3 -7
- package/package.json +7 -2
package/ci-local/install.ps1
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# .\install.ps1
|
|
8
8
|
#
|
|
9
9
|
# Behaviour MUST match install.sh. Diff against the bash version on every
|
|
10
|
-
# change. Test on
|
|
10
|
+
# change. Test on supported Windows and WSL hosts before tagging a release.
|
|
11
11
|
# =============================================================================
|
|
12
12
|
|
|
13
13
|
#Requires -Version 7.2
|
|
@@ -15,19 +15,36 @@
|
|
|
15
15
|
param()
|
|
16
16
|
|
|
17
17
|
function Invoke-CiLocalMain {
|
|
18
|
-
param(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
param(
|
|
19
|
+
[Parameter(Mandatory)][string]$Platform,
|
|
20
|
+
[ref]$ExitCode
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if ($Platform -notin @('Linux', 'Windows')) {
|
|
24
|
+
Write-Host 'unsupported-platform: javi-forge supports Linux and Windows only.'
|
|
25
|
+
if ($PSBoundParameters.ContainsKey('ExitCode')) {
|
|
26
|
+
$ExitCode.Value = 1
|
|
27
|
+
return
|
|
28
|
+
}
|
|
22
29
|
return 1
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
$bodyExitCode = 0
|
|
33
|
+
& ${function:Invoke-CiLocalStartupBody} -Platform $Platform -ExitCode ([ref]$bodyExitCode)
|
|
34
|
+
if ($PSBoundParameters.ContainsKey('ExitCode')) {
|
|
35
|
+
$ExitCode.Value = $bodyExitCode
|
|
36
|
+
} elseif ($bodyExitCode -ne 0) {
|
|
37
|
+
return $bodyExitCode
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
function Invoke-CiLocalStartupBody {
|
|
29
|
-
param(
|
|
42
|
+
param(
|
|
43
|
+
[string]$Platform,
|
|
44
|
+
[ref]$ExitCode
|
|
45
|
+
)
|
|
30
46
|
|
|
47
|
+
$ExitCode.Value = 0
|
|
31
48
|
# Defense-in-depth: #Requires is parsed by some hosts AFTER the script body.
|
|
32
49
|
# 7.2 is the minimum because FileSystemInfo.ResolveLinkTarget($true) is .NET 6+,
|
|
33
50
|
# which shipped with pwsh 7.2. Earlier pwsh 7.x runs on .NET 5 and the method
|
|
@@ -159,9 +176,11 @@ try {
|
|
|
159
176
|
|
|
160
177
|
$jfVersionOk = $true
|
|
161
178
|
$jfVersionOutput = ''
|
|
179
|
+
$jfVersionExitCode = 1
|
|
162
180
|
try {
|
|
163
181
|
$jfVersionOutput = (& javi-forge --version 2>&1) -join "`n"
|
|
164
|
-
|
|
182
|
+
$jfVersionExitCode = $LASTEXITCODE
|
|
183
|
+
if ($jfVersionExitCode -ne 0) { $jfVersionOk = $false }
|
|
165
184
|
} catch {
|
|
166
185
|
$jfVersionOk = $false
|
|
167
186
|
$jfVersionOutput = $_.Exception.Message
|
|
@@ -177,7 +196,8 @@ try {
|
|
|
177
196
|
Write-Host "ERROR: javi-forge found at $jfPath but '--version' failed" -ForegroundColor Red
|
|
178
197
|
Write-Host "Output: $jfVersionOutput" -ForegroundColor Yellow
|
|
179
198
|
Write-Host 'Reinstall: npm install -g javi-forge' -ForegroundColor Cyan
|
|
180
|
-
|
|
199
|
+
$ExitCode.Value = $jfVersionExitCode
|
|
200
|
+
return
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
# Reject UNC / remote paths outright — a javi-forge symlinked to
|
|
@@ -297,12 +317,13 @@ try {
|
|
|
297
317
|
& git config core.hooksPath $hooksRel
|
|
298
318
|
if ($LASTEXITCODE -ne 0) {
|
|
299
319
|
Write-Host 'ERROR: failed to set core.hooksPath. Are you inside a git repo?' -ForegroundColor Red
|
|
300
|
-
|
|
320
|
+
$ExitCode.Value = $LASTEXITCODE
|
|
321
|
+
return
|
|
301
322
|
}
|
|
302
323
|
Write-Host "hooksPath = $hooksRel" -ForegroundColor Green
|
|
303
324
|
|
|
304
325
|
# Make hooks executable. Different OS, different model:
|
|
305
|
-
# -
|
|
326
|
+
# - Linux / WSL: chmod 0755 — POSIX bits, what git expects.
|
|
306
327
|
# - Windows native NTFS: chmod is a no-op; lock the ACL via icacls so
|
|
307
328
|
# only the current user can rewrite the hooks between commits.
|
|
308
329
|
if (Test-CommandExists -Name 'chmod') {
|
|
@@ -382,9 +403,10 @@ try {
|
|
|
382
403
|
}
|
|
383
404
|
|
|
384
405
|
if ($MyInvocation.InvocationName -ne '.') {
|
|
385
|
-
$platform = if ($
|
|
386
|
-
$exitCode =
|
|
387
|
-
|
|
406
|
+
$platform = if ($IsWindows) { 'Windows' } elseif ($IsLinux) { 'Linux' } else { 'unsupported' }
|
|
407
|
+
$exitCode = 0
|
|
408
|
+
& ${function:Invoke-CiLocalMain} -Platform $platform -ExitCode ([ref]$exitCode)
|
|
409
|
+
if ($exitCode -ne 0) {
|
|
388
410
|
exit $exitCode
|
|
389
411
|
}
|
|
390
412
|
}
|
package/ci-local/install.sh
CHANGED
|
@@ -3,22 +3,40 @@
|
|
|
3
3
|
# CI-LOCAL: Installation Script
|
|
4
4
|
# =============================================================================
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
CI_LOCAL_ENTRYPOINT_SOURCE="${BASH_SOURCE[0]}"
|
|
8
|
+
if [[ "$CI_LOCAL_ENTRYPOINT_SOURCE" == /* ]]; then
|
|
9
|
+
CI_LOCAL_ENTRYPOINT_PATH="$CI_LOCAL_ENTRYPOINT_SOURCE"
|
|
10
|
+
else
|
|
11
|
+
CI_LOCAL_ENTRYPOINT_PATH="$PWD/$CI_LOCAL_ENTRYPOINT_SOURCE"
|
|
12
|
+
fi
|
|
13
|
+
CI_LOCAL_ENTRYPOINT_DIR="${CI_LOCAL_ENTRYPOINT_PATH%/*}"
|
|
14
|
+
|
|
15
|
+
function ci_local_normalize_platform() {
|
|
7
16
|
local platform="${1:?platform required}"
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
printf '%s\n' '
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
case "$platform" in
|
|
19
|
+
MINGW*_NT-*|MSYS*|CYGWIN*) printf '%s\n' 'Windows' ;;
|
|
20
|
+
*) printf '%s\n' "$platform" ;;
|
|
21
|
+
esac
|
|
22
|
+
}
|
|
13
23
|
|
|
14
|
-
|
|
24
|
+
function ci_local_main() {
|
|
25
|
+
local platform
|
|
26
|
+
platform="$(ci_local_normalize_platform "${1:?platform required}")"
|
|
27
|
+
shift
|
|
28
|
+
|
|
29
|
+
case "$platform" in
|
|
30
|
+
Linux|Windows) ci_local_startup_body "$@" ;;
|
|
31
|
+
*) printf '%s\n' 'unsupported-platform: javi-forge supports Linux and Windows only.'; return 1 ;;
|
|
32
|
+
esac
|
|
15
33
|
}
|
|
16
34
|
|
|
17
35
|
function ci_local_startup_body() {
|
|
18
36
|
set -e
|
|
19
37
|
|
|
20
|
-
SCRIPT_DIR="$
|
|
21
|
-
PROJECT_DIR="$
|
|
38
|
+
SCRIPT_DIR="$CI_LOCAL_ENTRYPOINT_DIR"
|
|
39
|
+
PROJECT_DIR="${SCRIPT_DIR%/*}"
|
|
22
40
|
|
|
23
41
|
# ─── Required tools (fail-closed) ──────────────────────────────────
|
|
24
42
|
# realpath is load-bearing for the symlink-traversal checks below. If it's
|
|
@@ -27,7 +45,6 @@ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
27
45
|
if ! command -v realpath >/dev/null 2>&1; then
|
|
28
46
|
printf 'ERROR: realpath is required for ci-local install (symlink checks).\n' >&2
|
|
29
47
|
printf ' Linux/WSL: install coreutils\n' >&2
|
|
30
|
-
printf ' macOS: brew install coreutils, then add gnubin to PATH\n' >&2
|
|
31
48
|
exit 1
|
|
32
49
|
fi
|
|
33
50
|
|
|
@@ -208,5 +225,5 @@ echo -e ""
|
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
211
|
-
ci_local_main "$(/usr/bin/uname -s)"
|
|
228
|
+
ci_local_main "$(/usr/bin/uname -s)" "$@"
|
|
212
229
|
fi
|
package/ci-local/lib/common.sh
CHANGED
|
@@ -39,16 +39,12 @@ log_info() { echo -e " ${CYAN}[INFO]${NC} $1"; }
|
|
|
39
39
|
log_step() { echo -e "${YELLOW}$1${NC}"; }
|
|
40
40
|
|
|
41
41
|
# =============================================================================
|
|
42
|
-
# sed_inplace -
|
|
42
|
+
# sed_inplace - GNU sed -i for supported Linux/WSL hosts
|
|
43
43
|
# =============================================================================
|
|
44
44
|
# Usage: sed_inplace "s/foo/bar/" file.txt
|
|
45
45
|
# =============================================================================
|
|
46
46
|
sed_inplace() {
|
|
47
|
-
|
|
48
|
-
sed -i "$@"
|
|
49
|
-
else
|
|
50
|
-
sed -i '' "$@"
|
|
51
|
-
fi
|
|
47
|
+
sed -i "$@"
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
# =============================================================================
|
|
@@ -100,7 +96,7 @@ detect_stack() {
|
|
|
100
96
|
STACK_TYPE="java-gradle"
|
|
101
97
|
BUILD_TOOL="gradle"
|
|
102
98
|
|
|
103
|
-
# Detect Java version from build files
|
|
99
|
+
# Detect Java version from build files
|
|
104
100
|
if [[ -f "$project_dir/build.gradle.kts" ]]; then
|
|
105
101
|
JAVA_VERSION=$(grep -E 'languageVersion\s*=\s*JavaLanguageVersion\.of\(' "$project_dir/build.gradle.kts" 2>/dev/null | grep -o '[0-9]\+' | head -1 || echo "21")
|
|
106
102
|
elif [[ -f "$project_dir/build.gradle" ]]; then
|
package/ci-local/uninstall.ps1
CHANGED
|
@@ -16,6 +16,42 @@ param(
|
|
|
16
16
|
[switch]$Purge
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
+
|
|
20
|
+
function Invoke-CiLocalMain {
|
|
21
|
+
param(
|
|
22
|
+
[Parameter(Mandatory)][string]$Platform,
|
|
23
|
+
[switch]$RestoreBackups,
|
|
24
|
+
[switch]$Purge,
|
|
25
|
+
[ref]$ExitCode
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if ($Platform -notin @('Linux', 'Windows')) {
|
|
29
|
+
Write-Host 'unsupported-platform: javi-forge supports Linux and Windows only.'
|
|
30
|
+
if ($PSBoundParameters.ContainsKey('ExitCode')) {
|
|
31
|
+
$ExitCode.Value = 1
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
return 1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$bodyExitCode = 0
|
|
38
|
+
& ${function:Invoke-CiLocalStartupBody} -Platform $Platform -RestoreBackups:$RestoreBackups -Purge:$Purge -ExitCode ([ref]$bodyExitCode)
|
|
39
|
+
if ($PSBoundParameters.ContainsKey('ExitCode')) {
|
|
40
|
+
$ExitCode.Value = $bodyExitCode
|
|
41
|
+
} elseif ($bodyExitCode -ne 0) {
|
|
42
|
+
return $bodyExitCode
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function Invoke-CiLocalStartupBody {
|
|
47
|
+
param(
|
|
48
|
+
[Parameter(Mandatory)][string]$Platform,
|
|
49
|
+
[switch]$RestoreBackups,
|
|
50
|
+
[switch]$Purge,
|
|
51
|
+
[ref]$ExitCode
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
$ExitCode.Value = 0
|
|
19
55
|
Set-StrictMode -Version Latest
|
|
20
56
|
$ErrorActionPreference = 'Stop'
|
|
21
57
|
|
|
@@ -32,6 +68,10 @@ try {
|
|
|
32
68
|
$current = (& git config --get core.hooksPath 2>$null) -join ''
|
|
33
69
|
if ($current) {
|
|
34
70
|
& git config --unset core.hooksPath
|
|
71
|
+
if ($LASTEXITCODE -ne 0) {
|
|
72
|
+
$ExitCode.Value = $LASTEXITCODE
|
|
73
|
+
return
|
|
74
|
+
}
|
|
35
75
|
Write-Host "Removed hooksPath = $current" -ForegroundColor Green
|
|
36
76
|
} else {
|
|
37
77
|
Write-Host 'No core.hooksPath was set; nothing to remove' -ForegroundColor Yellow
|
|
@@ -83,3 +123,13 @@ try {
|
|
|
83
123
|
} finally {
|
|
84
124
|
Pop-Location
|
|
85
125
|
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if ($MyInvocation.InvocationName -ne '.') {
|
|
129
|
+
$platform = if ($IsWindows) { 'Windows' } elseif ($IsLinux) { 'Linux' } else { 'unsupported' }
|
|
130
|
+
$exitCode = 0
|
|
131
|
+
& ${function:Invoke-CiLocalMain} -Platform $platform -RestoreBackups:$RestoreBackups -Purge:$Purge -ExitCode ([ref]$exitCode)
|
|
132
|
+
if ($exitCode -ne 0) {
|
|
133
|
+
exit $exitCode
|
|
134
|
+
}
|
|
135
|
+
}
|
package/ci-local/uninstall.sh
CHANGED
|
@@ -13,10 +13,40 @@
|
|
|
13
13
|
# ./uninstall.sh --purge # also rm -rf ci-local/ + lib/common.sh
|
|
14
14
|
# =============================================================================
|
|
15
15
|
|
|
16
|
-
set -e
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
CI_LOCAL_ENTRYPOINT_SOURCE="${BASH_SOURCE[0]}"
|
|
18
|
+
if [[ "$CI_LOCAL_ENTRYPOINT_SOURCE" == /* ]]; then
|
|
19
|
+
CI_LOCAL_ENTRYPOINT_PATH="$CI_LOCAL_ENTRYPOINT_SOURCE"
|
|
20
|
+
else
|
|
21
|
+
CI_LOCAL_ENTRYPOINT_PATH="$PWD/$CI_LOCAL_ENTRYPOINT_SOURCE"
|
|
22
|
+
fi
|
|
23
|
+
CI_LOCAL_ENTRYPOINT_DIR="${CI_LOCAL_ENTRYPOINT_PATH%/*}"
|
|
24
|
+
|
|
25
|
+
function ci_local_normalize_platform() {
|
|
26
|
+
local platform="${1:?platform required}"
|
|
27
|
+
|
|
28
|
+
case "$platform" in
|
|
29
|
+
MINGW*_NT-*|MSYS*|CYGWIN*) printf '%s\n' 'Windows' ;;
|
|
30
|
+
*) printf '%s\n' "$platform" ;;
|
|
31
|
+
esac
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ci_local_main() {
|
|
35
|
+
local platform
|
|
36
|
+
platform="$(ci_local_normalize_platform "${1:?platform required}")"
|
|
37
|
+
shift
|
|
38
|
+
|
|
39
|
+
case "$platform" in
|
|
40
|
+
Linux|Windows) ci_local_startup_body "$@" ;;
|
|
41
|
+
*) printf '%s\n' 'unsupported-platform: javi-forge supports Linux and Windows only.'; return 1 ;;
|
|
42
|
+
esac
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ci_local_startup_body() {
|
|
46
|
+
set -e
|
|
47
|
+
|
|
48
|
+
SCRIPT_DIR="$CI_LOCAL_ENTRYPOINT_DIR"
|
|
49
|
+
PROJECT_DIR="${SCRIPT_DIR%/*}"
|
|
20
50
|
|
|
21
51
|
# Source colors if available; degrade gracefully if not.
|
|
22
52
|
if [ -f "$SCRIPT_DIR/../lib/common.sh" ]; then
|
|
@@ -93,3 +123,8 @@ fi
|
|
|
93
123
|
echo -e ""
|
|
94
124
|
echo -e "${GREEN}Uninstall complete!${NC}"
|
|
95
125
|
echo -e ""
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
129
|
+
ci_local_main "$(/usr/bin/uname -s)" "$@"
|
|
130
|
+
fi
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type BootstrapResult = {
|
|
2
|
+
state: "supported";
|
|
3
|
+
exitCode: 0;
|
|
4
|
+
} | {
|
|
5
|
+
state: "unsupported-platform";
|
|
6
|
+
exitCode: 1;
|
|
7
|
+
};
|
|
8
|
+
export interface SupportedCli {
|
|
9
|
+
runCli: () => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function bootstrapCli(platform: string, loadSupportedCli: () => Promise<SupportedCli>, refuse: (message: string) => void): Promise<BootstrapResult>;
|
|
12
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { resolvePlatformSupport } from "../lib/platform-support.js";
|
|
2
|
+
export async function bootstrapCli(platform, loadSupportedCli, refuse) {
|
|
3
|
+
const refusal = resolvePlatformSupport(platform);
|
|
4
|
+
if (refusal) {
|
|
5
|
+
refuse(`${refusal.refusalCode}: ${refusal.guidance}`);
|
|
6
|
+
return { state: "unsupported-platform", exitCode: 1 };
|
|
7
|
+
}
|
|
8
|
+
const supportedCli = await loadSupportedCli();
|
|
9
|
+
await supportedCli.runCli();
|
|
10
|
+
return { state: "supported", exitCode: 0 };
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -7,8 +7,15 @@
|
|
|
7
7
|
* Ink + React are already eagerly imported at the entrypoint, so importing the
|
|
8
8
|
* concrete UI components at the top of this file does NOT worsen cold-start.
|
|
9
9
|
*/
|
|
10
|
+
import { render } from "ink";
|
|
10
11
|
import type { CLI, RendererCtx } from "./types.js";
|
|
11
|
-
export
|
|
12
|
+
export interface RendererDeps {
|
|
13
|
+
platform?: string;
|
|
14
|
+
render?: typeof render;
|
|
15
|
+
error?: (message: string) => void;
|
|
16
|
+
setExitCode?: (code: number) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare function handleDoctor(_cli: CLI, ctx: RendererCtx, deps?: RendererDeps): void;
|
|
12
19
|
export declare function handleAnalyze(cli: CLI, ctx: RendererCtx): void;
|
|
13
20
|
export declare function handleLlmsTxt(cli: CLI, ctx: RendererCtx): void;
|
|
14
21
|
export declare function handlePlugin(cli: CLI, ctx: RendererCtx): void;
|
|
@@ -17,8 +17,17 @@ import Doctor from "../../ui/Doctor.js";
|
|
|
17
17
|
import LlmsTxt from "../../ui/LlmsTxt.js";
|
|
18
18
|
import Plugin from "../../ui/Plugin.js";
|
|
19
19
|
import { VALID_CI, VALID_MEMORY, VALID_STACKS } from "../validators.js";
|
|
20
|
-
export function handleDoctor(_cli, ctx) {
|
|
21
|
-
|
|
20
|
+
export function handleDoctor(_cli, ctx, deps = {}) {
|
|
21
|
+
const platformSupport = resolvePlatformSupport(deps.platform ?? process.platform);
|
|
22
|
+
if (platformSupport) {
|
|
23
|
+
(deps.error ?? console.error)(`${platformSupport.refusalCode}: ${platformSupport.guidance}`);
|
|
24
|
+
(deps.setExitCode ??
|
|
25
|
+
((code) => {
|
|
26
|
+
process.exitCode = code;
|
|
27
|
+
}))(1);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
(deps.render ?? render)(React.createElement(CIContextProvider, { isCI: ctx.isCI },
|
|
22
31
|
React.createElement(Doctor, null)), { stdin: ctx.inkStdin });
|
|
23
32
|
}
|
|
24
33
|
export function handleAnalyze(cli, ctx) {
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import meow from "meow";
|
|
3
|
+
import { handleCi } from "./dispatch/ci.js";
|
|
4
|
+
import { handleHooks } from "./dispatch/hooks.js";
|
|
5
|
+
import { handleSecurity } from "./dispatch/security.js";
|
|
6
|
+
import { handleAnalyze, handleDoctor, handleInitDefault, handleLlmsTxt, handlePlugin, } from "./dispatch/simple-renderers.js";
|
|
7
|
+
import { handleSkillPublish } from "./dispatch/skill-publish.js";
|
|
8
|
+
import { handleSkillsCmd } from "./dispatch/skills-cmd.js";
|
|
9
|
+
import { handleTdd } from "./dispatch/tdd.js";
|
|
10
|
+
import { handleWorkflow } from "./dispatch/workflow.js";
|
|
11
|
+
import { FLAGS_SCHEMA, HELP_TEXT } from "./help.js";
|
|
12
|
+
import { createInkStdin, detectCI, setupUpdateNotifier } from "./runtime.js";
|
|
13
|
+
export async function runCli() {
|
|
14
|
+
// Check for updates in background (non-blocking, cached 24h)
|
|
15
|
+
const _require = createRequire(import.meta.url);
|
|
16
|
+
const pkg = _require("../../package.json");
|
|
17
|
+
setupUpdateNotifier(pkg);
|
|
18
|
+
const cli = meow(HELP_TEXT, {
|
|
19
|
+
importMeta: import.meta,
|
|
20
|
+
flags: FLAGS_SCHEMA,
|
|
21
|
+
// Handle --help manually so `ci --help` can show ci-specific usage instead
|
|
22
|
+
// of the global banner (meow's autoHelp would print + exit before dispatch).
|
|
23
|
+
autoHelp: false,
|
|
24
|
+
});
|
|
25
|
+
const subcommand = cli.input[0] ?? "init";
|
|
26
|
+
// Global --help: every command except `ci` and `hooks` shows the global banner
|
|
27
|
+
// here. Those two own their per-command help inside their handlers.
|
|
28
|
+
if (cli.flags.help && subcommand !== "ci" && subcommand !== "hooks") {
|
|
29
|
+
console.log(HELP_TEXT);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
const isCI = detectCI(cli.flags);
|
|
33
|
+
const inkStdin = createInkStdin();
|
|
34
|
+
switch (subcommand) {
|
|
35
|
+
case "tdd": {
|
|
36
|
+
await handleTdd(cli);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case "ci": {
|
|
40
|
+
await handleCi(cli, { inkStdin, isCI });
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case "hooks": {
|
|
44
|
+
await handleHooks(cli);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "doctor": {
|
|
48
|
+
handleDoctor(cli, { inkStdin, isCI });
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "analyze": {
|
|
52
|
+
handleAnalyze(cli, { inkStdin, isCI });
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case "workflow": {
|
|
56
|
+
await handleWorkflow(cli);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "llms-txt": {
|
|
60
|
+
handleLlmsTxt(cli, { inkStdin, isCI });
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case "plugin": {
|
|
64
|
+
handlePlugin(cli, { inkStdin, isCI });
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case "skills": {
|
|
68
|
+
await handleSkillsCmd(cli, { inkStdin, isCI });
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case "skill": {
|
|
72
|
+
await handleSkillPublish(cli);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "security": {
|
|
76
|
+
await handleSecurity(cli);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
default: {
|
|
80
|
+
handleInitDefault(cli, { inkStdin, isCI });
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -17,6 +17,7 @@ import { doctorClaudePreToolUse, installClaudePreToolUse, repairClaudePreToolUse
|
|
|
17
17
|
export type ClaudeHookSub = "install" | "doctor" | "repair";
|
|
18
18
|
/** Injectable seams for tests; each defaults to the real implementation. */
|
|
19
19
|
export interface ClaudeHookCmdDeps {
|
|
20
|
+
platform?: string;
|
|
20
21
|
install?: typeof installClaudePreToolUse;
|
|
21
22
|
doctor?: typeof doctorClaudePreToolUse;
|
|
22
23
|
repair?: typeof repairClaudePreToolUse;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* current.
|
|
15
15
|
*/
|
|
16
16
|
import { doctorClaudePreToolUse, installClaudePreToolUse, repairClaudePreToolUse, } from "../lib/claude-hook-manager.js";
|
|
17
|
+
import { resolvePlatformSupport } from "../lib/platform-support.js";
|
|
17
18
|
import { remediationForMessage } from "../lib/secure-refusal-remediation.js";
|
|
18
19
|
/**
|
|
19
20
|
* Warnings are NON-BLOCKING notices, so they go to stdout under their own
|
|
@@ -57,10 +58,13 @@ function renderMutation(verb, result, log, logError) {
|
|
|
57
58
|
renderWarnings(result.warnings, log);
|
|
58
59
|
return 1;
|
|
59
60
|
}
|
|
60
|
-
function renderDoctor(
|
|
61
|
+
function renderDoctor(result, log) {
|
|
62
|
+
if ("state" in result) {
|
|
63
|
+
log(`${result.platformSupport.refusalCode}: ${result.platformSupport.guidance}`);
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
const report = result;
|
|
61
67
|
log(`doctor claude: ${report.healthy ? "healthy" : "unhealthy"}`);
|
|
62
|
-
if (report.platformSupport)
|
|
63
|
-
log(` platform-support: ${report.platformSupport.state} — ${report.platformSupport.guidance}`);
|
|
64
68
|
log(` settings: ${report.settings.state} — ${report.settings.detail}`);
|
|
65
69
|
log(` asset: ${report.asset.state} — ${report.asset.detail}`);
|
|
66
70
|
log(` node: ${report.node.version ?? "unavailable"} (min-satisfied: ${report.node.satisfiesMinimum})`);
|
|
@@ -124,6 +128,11 @@ function renderDoctor(report, log) {
|
|
|
124
128
|
export async function runClaudeHookCommand(sub, projectDir, opts, deps = {}) {
|
|
125
129
|
const log = deps.log ?? ((m) => console.log(m));
|
|
126
130
|
const logError = deps.logError ?? ((m) => console.error(m));
|
|
131
|
+
const platformSupport = resolvePlatformSupport(deps.platform ?? process.platform);
|
|
132
|
+
if (platformSupport) {
|
|
133
|
+
logError(`${platformSupport.refusalCode}: ${platformSupport.guidance}`);
|
|
134
|
+
return 1;
|
|
135
|
+
}
|
|
127
136
|
const install = deps.install ?? installClaudePreToolUse;
|
|
128
137
|
const doctor = deps.doctor ?? doctorClaudePreToolUse;
|
|
129
138
|
const repair = deps.repair ?? repairClaudePreToolUse;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { doctorCodexPreToolUse, installCodexPreToolUse, repairCodexPreToolUse } from "../lib/codex-hook-manager.js";
|
|
14
14
|
export type CodexHookSub = "install" | "doctor" | "repair";
|
|
15
15
|
export interface CodexHookCmdDeps {
|
|
16
|
+
platform?: string;
|
|
16
17
|
install?: typeof installCodexPreToolUse;
|
|
17
18
|
doctor?: typeof doctorCodexPreToolUse;
|
|
18
19
|
repair?: typeof repairCodexPreToolUse;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* install correctly reports blocked until the user grants trust.
|
|
12
12
|
*/
|
|
13
13
|
import { doctorCodexPreToolUse, installCodexPreToolUse, repairCodexPreToolUse, } from "../lib/codex-hook-manager.js";
|
|
14
|
+
import { resolvePlatformSupport } from "../lib/platform-support.js";
|
|
14
15
|
function renderWarnings(warnings, log) {
|
|
15
16
|
if (warnings.length === 0)
|
|
16
17
|
return;
|
|
@@ -42,10 +43,13 @@ function renderMutation(verb, result, log, logError) {
|
|
|
42
43
|
renderWarnings(result.warnings, log);
|
|
43
44
|
return 1;
|
|
44
45
|
}
|
|
45
|
-
function renderDoctor(
|
|
46
|
+
function renderDoctor(result, log) {
|
|
47
|
+
if ("state" in result) {
|
|
48
|
+
log(`${result.platformSupport.refusalCode}: ${result.platformSupport.guidance}`);
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
const report = result;
|
|
46
52
|
log(`doctor codex: ${report.healthy ? "healthy" : "unhealthy"}`);
|
|
47
|
-
if (report.platformSupport)
|
|
48
|
-
log(` platform-support: ${report.platformSupport.state} — ${report.platformSupport.guidance}`);
|
|
49
53
|
log(` hooks.json: ${report.hooksJson.state}`);
|
|
50
54
|
log(` config: [features] hooks=${report.config.featuresHooks} (readable: ${report.config.readable})`);
|
|
51
55
|
log(` trust: ${report.trust.state}`);
|
|
@@ -88,6 +92,11 @@ function renderDoctor(report, log) {
|
|
|
88
92
|
export async function runCodexHookCommand(sub, _cwd, opts, deps = {}) {
|
|
89
93
|
const log = deps.log ?? ((m) => console.log(m));
|
|
90
94
|
const logError = deps.logError ?? ((m) => console.error(m));
|
|
95
|
+
const platformSupport = resolvePlatformSupport(deps.platform ?? process.platform);
|
|
96
|
+
if (platformSupport) {
|
|
97
|
+
logError(`${platformSupport.refusalCode}: ${platformSupport.guidance}`);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
91
100
|
const install = deps.install ?? installCodexPreToolUse;
|
|
92
101
|
const doctor = deps.doctor ?? doctorCodexPreToolUse;
|
|
93
102
|
const repair = deps.repair ?? repairCodexPreToolUse;
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import { detectStack } from "../lib/common.js";
|
|
3
|
+
import { refreshContextDir } from "../lib/context.js";
|
|
4
|
+
import { execFileAsync } from "../lib/exec.js";
|
|
5
|
+
import { listInstalledPlugins } from "../lib/plugin.js";
|
|
1
6
|
import type { DoctorResult } from "../types/index.js";
|
|
2
7
|
export type CheckStatus = "ok" | "fail" | "skip";
|
|
8
|
+
type DoctorFilesystem = Pick<typeof fs, "pathExists" | "readJson" | "readdir">;
|
|
9
|
+
export interface DoctorDeps {
|
|
10
|
+
platform?: string;
|
|
11
|
+
cwd?: () => string;
|
|
12
|
+
filesystem?: DoctorFilesystem;
|
|
13
|
+
exec?: typeof execFileAsync;
|
|
14
|
+
stackDetector?: typeof detectStack;
|
|
15
|
+
pluginLister?: typeof listInstalledPlugins;
|
|
16
|
+
contextRefresher?: typeof refreshContextDir;
|
|
17
|
+
}
|
|
3
18
|
interface RemoteInfo {
|
|
4
19
|
host: "github" | "gitlab" | "other";
|
|
5
20
|
owner: string;
|
|
@@ -10,6 +25,6 @@ export declare function parseRemote(url: string): RemoteInfo | null;
|
|
|
10
25
|
/**
|
|
11
26
|
* Run comprehensive health checks for the project and framework.
|
|
12
27
|
*/
|
|
13
|
-
export declare function runDoctor(projectDir?: string): Promise<DoctorResult>;
|
|
28
|
+
export declare function runDoctor(projectDir?: string, deps?: DoctorDeps): Promise<DoctorResult>;
|
|
14
29
|
export {};
|
|
15
30
|
//# sourceMappingURL=doctor.d.ts.map
|