javi-forge 1.36.0 → 1.37.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 -0
- package/ci-local/README.md +11 -6
- package/ci-local/ci-local.ps1 +34 -0
- package/ci-local/ci-local.sh +19 -1
- package/ci-local/install.ps1 +23 -0
- package/ci-local/install.sh +18 -1
- package/dist/cli/dispatch/simple-renderers.js +7 -0
- package/dist/commands/claude-hooks.js +2 -0
- package/dist/commands/codex-hooks.js +2 -0
- package/dist/commands/init.d.ts +9 -1
- package/dist/commands/init.js +10 -6
- package/dist/lib/claude-hook-manager.d.ts +17 -1
- package/dist/lib/claude-hook-manager.js +17 -2
- package/dist/lib/codex-hook-manager.d.ts +18 -1
- package/dist/lib/codex-hook-manager.js +17 -2
- package/dist/lib/platform-support.d.ts +19 -0
- package/dist/lib/platform-support.js +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -372,6 +372,11 @@ npx javi-forge doctor
|
|
|
372
372
|
- **Project Manifest** — Reads `.javi-forge/manifest.json`
|
|
373
373
|
- **Installed Modules** — engram, obsidian-brain, memory-simple, ghagga
|
|
374
374
|
|
|
375
|
+
|
|
376
|
+
## macOS CI-Local support
|
|
377
|
+
|
|
378
|
+
macOS is deprecated and unsupported for new CI-Local install/startup. Pin a supported release or migrate your workflow. Existing installed guards are not removed in 1.x; Darwin code removal is planned separately for 2.0.
|
|
379
|
+
|
|
375
380
|
## Requirements
|
|
376
381
|
|
|
377
382
|
- **Node.js** >= 22 (required by ink 7; previous versions ran on >= 18)
|
package/ci-local/README.md
CHANGED
|
@@ -34,21 +34,26 @@ cp -r lib /path/to/new-project/
|
|
|
34
34
|
|
|
35
35
|
# En el nuevo proyecto
|
|
36
36
|
cd /path/to/new-project
|
|
37
|
-
./.ci-local/install.sh # Linux/
|
|
37
|
+
./.ci-local/install.sh # Linux/WSL
|
|
38
38
|
# o
|
|
39
39
|
.\.ci-local\install.ps1 # Windows (PowerShell 7+)
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
> **Importante:** CI-Local depende de `lib/common.sh` (Linux/
|
|
42
|
+
> **Importante:** CI-Local depende de `lib/common.sh` (Linux/WSL) y/o `lib/common.psm1` (Windows). Copiá ambos junto con el directorio `ci-local/`. macOS está deprecated y se rechaza para instalaciones o arranques nuevos.
|
|
43
43
|
|
|
44
44
|
El installer (ambas variantes) falla con mensaje claro si `javi-forge` no está en el PATH.
|
|
45
45
|
|
|
46
|
+
|
|
47
|
+
## macOS deprecation
|
|
48
|
+
|
|
49
|
+
macOS is deprecated and unsupported for new CI-Local install/startup. Pin a supported release or migrate your workflow. Existing installed guards are not removed in 1.x; Darwin code removal is planned separately for 2.0.
|
|
50
|
+
|
|
46
51
|
### Soporte cross-platform
|
|
47
52
|
|
|
48
53
|
| Plataforma | Installer | Runner | Hooks | Requisitos |
|
|
49
54
|
|---|---|---|---|---|
|
|
50
55
|
| Linux | `install.sh` | `ci-local.sh` | bash | bash, perl, docker (opcional) |
|
|
51
|
-
| macOS |
|
|
56
|
+
| macOS | Unsupported: pin a supported release or migrate | Refused before startup | Existing installed guards retained | No new install or startup |
|
|
52
57
|
| WSL | `install.sh` | `ci-local.sh` | bash | bash, perl, docker (opcional) |
|
|
53
58
|
| Windows nativo | `install.ps1` | `ci-local.ps1` | bash | PowerShell 7+, Git for Windows (MSYS2 bash), docker (opcional) |
|
|
54
59
|
|
|
@@ -117,7 +122,7 @@ Si tu modelo de amenaza requiere defensa contra adversarios motivados, usá
|
|
|
117
122
|
.\.ci-local\ci-local.ps1 shell # Shell en entorno CI
|
|
118
123
|
.\.ci-local\ci-local.ps1 detect # Ver stack detectado
|
|
119
124
|
|
|
120
|
-
# Linux/
|
|
125
|
+
# Linux/WSL
|
|
121
126
|
./.ci-local/ci-local.sh quick
|
|
122
127
|
./.ci-local/ci-local.sh full
|
|
123
128
|
./.ci-local/ci-local.sh shell
|
|
@@ -164,9 +169,9 @@ Developer workflow:
|
|
|
164
169
|
```
|
|
165
170
|
.ci-local/
|
|
166
171
|
├── ci-local.ps1 # Script principal (Windows)
|
|
167
|
-
├── ci-local.sh # Script principal (Linux/
|
|
172
|
+
├── ci-local.sh # Script principal (Linux/WSL)
|
|
168
173
|
├── install.ps1 # Instalador Windows
|
|
169
|
-
├── install.sh # Instalador Linux/
|
|
174
|
+
├── install.sh # Instalador Linux/WSL
|
|
170
175
|
├── semgrep.yml # Reglas de seguridad
|
|
171
176
|
├── README.md # Esta guía
|
|
172
177
|
├── hooks/
|
package/ci-local/ci-local.ps1
CHANGED
|
@@ -21,6 +21,30 @@ param(
|
|
|
21
21
|
[string]$Mode = 'full'
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
+
function Invoke-CiLocalMain {
|
|
25
|
+
param(
|
|
26
|
+
[Parameter(Mandatory)][string]$Platform,
|
|
27
|
+
[ref]$ExitCode
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if ($Platform -eq 'Darwin') {
|
|
31
|
+
Write-Host 'macOS is deprecated and unsupported for new CI-Local install/startup. Pin a supported release or migrate. Existing installed guards are not removed; Darwin code removal is planned separately for 2.0.'
|
|
32
|
+
if ($PSBoundParameters.ContainsKey('ExitCode')) {
|
|
33
|
+
$ExitCode.Value = 1
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
return 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Invoke-CiLocalStartupBody -Platform $Platform
|
|
40
|
+
if ($PSBoundParameters.ContainsKey('ExitCode')) {
|
|
41
|
+
$ExitCode.Value = 0
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Invoke-CiLocalStartupBody {
|
|
46
|
+
param([string]$Platform)
|
|
47
|
+
|
|
24
48
|
# See install.ps1 for the rationale behind 7.2 minimum (ResolveLinkTarget).
|
|
25
49
|
if ($PSVersionTable.PSEdition -ne 'Core' -or $PSVersionTable.PSVersion -lt [Version]'7.2') {
|
|
26
50
|
Write-Host 'ERROR: ci-local.ps1 requires PowerShell 7.2+ (pwsh).' -ForegroundColor Red
|
|
@@ -441,3 +465,13 @@ Write-Host ''
|
|
|
441
465
|
Write-Host 'CI Local completed successfully!' -ForegroundColor Green
|
|
442
466
|
Write-Host ' Safe to push - CI should pass.' -ForegroundColor Green
|
|
443
467
|
Write-Host ''
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if ($MyInvocation.InvocationName -ne '.') {
|
|
471
|
+
$platform = if ($IsMacOS) { 'Darwin' } else { 'Windows' }
|
|
472
|
+
$exitCode = 0
|
|
473
|
+
& ${function:Invoke-CiLocalMain} -Platform $platform -ExitCode ([ref]$exitCode)
|
|
474
|
+
if ($exitCode -ne 0) {
|
|
475
|
+
exit $exitCode
|
|
476
|
+
}
|
|
477
|
+
}
|
package/ci-local/ci-local.sh
CHANGED
|
@@ -11,7 +11,20 @@
|
|
|
11
11
|
# ./ci-local.sh detect # Mostrar stack detectado
|
|
12
12
|
# =============================================================================
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
function ci_local_main() {
|
|
15
|
+
local platform="${1:?platform required}"
|
|
16
|
+
shift
|
|
17
|
+
|
|
18
|
+
if [ "$platform" = "Darwin" ]; then
|
|
19
|
+
printf '%s\n' 'macOS is deprecated and unsupported for new CI-Local install/startup. Pin a supported release or migrate. Existing installed guards are not removed; Darwin code removal is planned separately for 2.0.'
|
|
20
|
+
return 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
ci_local_startup_body "$@"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ci_local_startup_body() {
|
|
27
|
+
set -e
|
|
15
28
|
|
|
16
29
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
17
30
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
@@ -390,3 +403,8 @@ esac
|
|
|
390
403
|
|
|
391
404
|
echo -e "\n${GREEN}CI Local completed successfully!${NC}"
|
|
392
405
|
echo -e "${GREEN} Safe to push - CI should pass.${NC}\n"
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
409
|
+
ci_local_main "$(/usr/bin/uname -s)" "$@"
|
|
410
|
+
fi
|
package/ci-local/install.ps1
CHANGED
|
@@ -14,6 +14,20 @@
|
|
|
14
14
|
[CmdletBinding()]
|
|
15
15
|
param()
|
|
16
16
|
|
|
17
|
+
function Invoke-CiLocalMain {
|
|
18
|
+
param([Parameter(Mandatory)][string]$Platform)
|
|
19
|
+
|
|
20
|
+
if ($Platform -eq 'Darwin') {
|
|
21
|
+
Write-Host 'macOS is deprecated and unsupported for new CI-Local install/startup. Pin a supported release or migrate. Existing installed guards are not removed; Darwin code removal is planned separately for 2.0.'
|
|
22
|
+
return 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Invoke-CiLocalStartupBody -Platform $Platform
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function Invoke-CiLocalStartupBody {
|
|
29
|
+
param([string]$Platform)
|
|
30
|
+
|
|
17
31
|
# Defense-in-depth: #Requires is parsed by some hosts AFTER the script body.
|
|
18
32
|
# 7.2 is the minimum because FileSystemInfo.ResolveLinkTarget($true) is .NET 6+,
|
|
19
33
|
# which shipped with pwsh 7.2. Earlier pwsh 7.x runs on .NET 5 and the method
|
|
@@ -365,3 +379,12 @@ try {
|
|
|
365
379
|
} finally {
|
|
366
380
|
Pop-Location
|
|
367
381
|
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if ($MyInvocation.InvocationName -ne '.') {
|
|
385
|
+
$platform = if ($IsMacOS) { 'Darwin' } else { 'Windows' }
|
|
386
|
+
$exitCode = & ${function:Invoke-CiLocalMain} -Platform $platform
|
|
387
|
+
if ($exitCode -is [int] -and $exitCode -ne 0) {
|
|
388
|
+
exit $exitCode
|
|
389
|
+
}
|
|
390
|
+
}
|
package/ci-local/install.sh
CHANGED
|
@@ -3,7 +3,19 @@
|
|
|
3
3
|
# CI-LOCAL: Installation Script
|
|
4
4
|
# =============================================================================
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
function ci_local_main() {
|
|
7
|
+
local platform="${1:?platform required}"
|
|
8
|
+
|
|
9
|
+
if [ "$platform" = "Darwin" ]; then
|
|
10
|
+
printf '%s\n' 'macOS is deprecated and unsupported for new CI-Local install/startup. Pin a supported release or migrate. Existing installed guards are not removed; Darwin code removal is planned separately for 2.0.'
|
|
11
|
+
return 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
ci_local_startup_body "$platform"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ci_local_startup_body() {
|
|
18
|
+
set -e
|
|
7
19
|
|
|
8
20
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
21
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
@@ -193,3 +205,8 @@ echo -e " - pre-commit: AI check + lint + security"
|
|
|
193
205
|
echo -e " - commit-msg: Block AI attribution"
|
|
194
206
|
echo -e " - pre-push: CI simulation in Docker"
|
|
195
207
|
echo -e ""
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
211
|
+
ci_local_main "$(/usr/bin/uname -s)"
|
|
212
|
+
fi
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { render } from "ink";
|
|
11
11
|
import React from "react";
|
|
12
|
+
import { resolvePlatformSupport } from "../../lib/platform-support.js";
|
|
12
13
|
import AnalyzeUI from "../../ui/AnalyzeUI.js";
|
|
13
14
|
import App from "../../ui/App.js";
|
|
14
15
|
import { CIProvider as CIContextProvider } from "../../ui/CIContext.js";
|
|
@@ -49,6 +50,12 @@ export function handlePlugin(cli, ctx) {
|
|
|
49
50
|
React.createElement(Plugin, { action: action, target: target, dryRun: cli.flags.dryRun, codex: cli.flags.codex, force: cli.flags.force })), { stdin: ctx.inkStdin });
|
|
50
51
|
}
|
|
51
52
|
export function handleInitDefault(cli, ctx) {
|
|
53
|
+
const platformSupport = resolvePlatformSupport(process.platform);
|
|
54
|
+
if (platformSupport) {
|
|
55
|
+
console.error(`${platformSupport.refusalCode}: ${platformSupport.guidance}`);
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
52
59
|
const presetStack = VALID_STACKS.includes(cli.flags.stack)
|
|
53
60
|
? cli.flags.stack
|
|
54
61
|
: undefined;
|
|
@@ -59,6 +59,8 @@ function renderMutation(verb, result, log, logError) {
|
|
|
59
59
|
}
|
|
60
60
|
function renderDoctor(report, log) {
|
|
61
61
|
log(`doctor claude: ${report.healthy ? "healthy" : "unhealthy"}`);
|
|
62
|
+
if (report.platformSupport)
|
|
63
|
+
log(` platform-support: ${report.platformSupport.state} — ${report.platformSupport.guidance}`);
|
|
62
64
|
log(` settings: ${report.settings.state} — ${report.settings.detail}`);
|
|
63
65
|
log(` asset: ${report.asset.state} — ${report.asset.detail}`);
|
|
64
66
|
log(` node: ${report.node.version ?? "unavailable"} (min-satisfied: ${report.node.satisfiesMinimum})`);
|
|
@@ -44,6 +44,8 @@ function renderMutation(verb, result, log, logError) {
|
|
|
44
44
|
}
|
|
45
45
|
function renderDoctor(report, log) {
|
|
46
46
|
log(`doctor codex: ${report.healthy ? "healthy" : "unhealthy"}`);
|
|
47
|
+
if (report.platformSupport)
|
|
48
|
+
log(` platform-support: ${report.platformSupport.state} — ${report.platformSupport.guidance}`);
|
|
47
49
|
log(` hooks.json: ${report.hooksJson.state}`);
|
|
48
50
|
log(` config: [features] hooks=${report.config.featuresHooks} (readable: ${report.config.readable})`);
|
|
49
51
|
log(` trust: ${report.trust.state}`);
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PlatformSupport } from "../lib/platform-support.js";
|
|
1
2
|
import type { InitOptions } from "../types/index.js";
|
|
2
3
|
import type { StepCallback } from "./init/types.js";
|
|
3
4
|
/**
|
|
@@ -6,5 +7,12 @@ import type { StepCallback } from "./init/types.js";
|
|
|
6
7
|
*
|
|
7
8
|
* Pure dispatch: every step lives in src/commands/init/steps/.
|
|
8
9
|
*/
|
|
9
|
-
export
|
|
10
|
+
export type InitProjectResult = {
|
|
11
|
+
ok: true;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
refusalCode: string;
|
|
15
|
+
platformSupport: PlatformSupport;
|
|
16
|
+
};
|
|
17
|
+
export declare function initProject(options: InitOptions, onStep: StepCallback): Promise<InitProjectResult>;
|
|
10
18
|
//# sourceMappingURL=init.d.ts.map
|
package/dist/commands/init.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensureDirExists } from "../lib/common.js";
|
|
2
|
+
import { resolvePlatformSupport, } from "../lib/platform-support.js";
|
|
2
3
|
import { stepAgentSkills } from "./init/steps/agent-skills.js";
|
|
3
4
|
import { stepAISync } from "./init/steps/ai-sync.js";
|
|
4
5
|
import { stepCITemplate, stepDependabot } from "./init/steps/ci.js";
|
|
@@ -15,13 +16,15 @@ import { stepMemory } from "./init/steps/memory.js";
|
|
|
15
16
|
import { stepMock } from "./init/steps/mock.js";
|
|
16
17
|
import { stepSDD } from "./init/steps/sdd.js";
|
|
17
18
|
import { stepSecurityHooks } from "./init/steps/security.js";
|
|
18
|
-
/**
|
|
19
|
-
* Main init orchestrator: bootstraps a project with CI, git hooks,
|
|
20
|
-
* memory module, AI config sync, SDD, ghagga, and friends.
|
|
21
|
-
*
|
|
22
|
-
* Pure dispatch: every step lives in src/commands/init/steps/.
|
|
23
|
-
*/
|
|
24
19
|
export async function initProject(options, onStep) {
|
|
20
|
+
const platformSupport = resolvePlatformSupport(process.platform);
|
|
21
|
+
if (platformSupport) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
refusalCode: platformSupport.refusalCode,
|
|
25
|
+
platformSupport,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
25
28
|
const { projectDir, dryRun } = options;
|
|
26
29
|
// Ensure project directory exists before any steps
|
|
27
30
|
if (!dryRun && projectDir) {
|
|
@@ -47,5 +50,6 @@ export async function initProject(options, onStep) {
|
|
|
47
50
|
await stepLocalAi(ctx);
|
|
48
51
|
await stepAgentSkills(ctx);
|
|
49
52
|
await stepManifest(ctx);
|
|
53
|
+
return { ok: true };
|
|
50
54
|
}
|
|
51
55
|
//# sourceMappingURL=init.js.map
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Slice-3 seams — Slice 3 GROWS this file, it does not relocate this code.
|
|
9
9
|
*/
|
|
10
10
|
import { type ClaudeHookComponentState, type SettingsClassification, type SettingsIdentityManifest } from "./claude-hook-settings.js";
|
|
11
|
+
import { type PlatformSupport } from "./platform-support.js";
|
|
11
12
|
import { type AclCapability, type SpawnFn } from "./secure-fs-posix.js";
|
|
12
13
|
import { type PlatformSecureFs } from "./secure-fs-transaction.js";
|
|
13
14
|
declare const COVERAGE: readonly ["Bash", "PowerShell", "Read", "Write", "Edit"];
|
|
@@ -29,6 +30,7 @@ export interface ClaudeHookAssetClassification {
|
|
|
29
30
|
}
|
|
30
31
|
export interface ClaudeHookDoctorReport {
|
|
31
32
|
healthy: boolean;
|
|
33
|
+
platformSupport?: PlatformSupport;
|
|
32
34
|
settings: {
|
|
33
35
|
state: ClaudeHookComponentState;
|
|
34
36
|
version?: number;
|
|
@@ -207,8 +209,9 @@ export declare function doctorClaudePreToolUse(projectDir: string, options?: {
|
|
|
207
209
|
execution?: ExecutionProbeEnv;
|
|
208
210
|
/** Injectable read-only ACL capability probe (defaults to the real one). */
|
|
209
211
|
aclProbe?: () => Promise<AclCapability>;
|
|
212
|
+
platform?: NodeJS.Platform;
|
|
210
213
|
}): Promise<ClaudeHookDoctorReport>;
|
|
211
|
-
|
|
214
|
+
interface ClaudeHookMutationResultWithReport {
|
|
212
215
|
ok: boolean;
|
|
213
216
|
changed: string[];
|
|
214
217
|
backups: string[];
|
|
@@ -220,7 +223,19 @@ export interface ClaudeHookMutationResult {
|
|
|
220
223
|
* which is strictly worse than an exec-form guard that may not resolve.
|
|
221
224
|
*/
|
|
222
225
|
warnings: string[];
|
|
226
|
+
lifecycleRefusal?: never;
|
|
227
|
+
}
|
|
228
|
+
interface ClaudeHookLifecycleRefusal {
|
|
229
|
+
ok: false;
|
|
230
|
+
changed: string[];
|
|
231
|
+
backups: string[];
|
|
232
|
+
errors: string[];
|
|
233
|
+
warnings: string[];
|
|
234
|
+
/** The lifecycle gate refused before any doctor or installed-state probe. */
|
|
235
|
+
lifecycleRefusal: PlatformSupport;
|
|
236
|
+
report?: never;
|
|
223
237
|
}
|
|
238
|
+
export type ClaudeHookMutationResult = ClaudeHookMutationResultWithReport | ClaudeHookLifecycleRefusal;
|
|
224
239
|
/** Injectable deps so tests drive `_run` with a fake `PlatformSecureFs`. */
|
|
225
240
|
export interface ClaudeHookRunDeps {
|
|
226
241
|
secureFs?: PlatformSecureFs | null;
|
|
@@ -236,6 +251,7 @@ export interface ClaudeHookRunDeps {
|
|
|
236
251
|
aclProbe?: () => Promise<AclCapability>;
|
|
237
252
|
/** Injectable node-on-PATH heuristic, shared by the warning and the report. */
|
|
238
253
|
nodeProbe?: () => Promise<NodeOnPathProbe>;
|
|
254
|
+
doctor?: typeof doctorClaudePreToolUse;
|
|
239
255
|
}
|
|
240
256
|
/** Internal deps-taking entry; tests drive it with a fake `PlatformSecureFs`. */
|
|
241
257
|
export declare function _run(projectDir: string, mode: "install" | "repair", options: {
|
|
@@ -15,6 +15,7 @@ import path from "node:path";
|
|
|
15
15
|
import { CLAUDE_HOOK_ASSETS_DIR } from "../constants.js";
|
|
16
16
|
import { ASSET_MANAGED_MARKER, ASSET_NAME, } from "./__fixtures__/claude-hook-ownership.js";
|
|
17
17
|
import { buildManagedContainer, classifySettingsEntry, isPlainObject, LEGACY_FILE_SHA256, MANAGED_AGENT_ARG, MANAGED_ASSET_ARG, MANAGED_MATCHER, MANAGED_STATUS_PREFIX, planForceReplace, planLegacyCohortExcision, planManagedClaudeHookMerge, scanExecutionFlags, } from "./claude-hook-settings.js";
|
|
18
|
+
import { resolvePlatformSupport, } from "./platform-support.js";
|
|
18
19
|
import { safeReadFile } from "./safe-read.js";
|
|
19
20
|
import { ACL_DETAIL, probeAclCapability, selectSecureFs, } from "./secure-fs-posix.js";
|
|
20
21
|
import { runTransaction, } from "./secure-fs-transaction.js";
|
|
@@ -537,7 +538,9 @@ export async function doctorClaudePreToolUse(projectDir, options) {
|
|
|
537
538
|
execution.blockers.some((blocker) => blocker.startsWith("guard:"))) {
|
|
538
539
|
remediation.add(aclRemediation);
|
|
539
540
|
}
|
|
541
|
+
const platformSupport = resolvePlatformSupport(options?.platform ?? process.platform);
|
|
540
542
|
return {
|
|
543
|
+
...(platformSupport ? { platformSupport } : {}),
|
|
541
544
|
healthy,
|
|
542
545
|
settings: {
|
|
543
546
|
state: settings.state,
|
|
@@ -712,8 +715,20 @@ function serializeSettings(container) {
|
|
|
712
715
|
}
|
|
713
716
|
/** Internal deps-taking entry; tests drive it with a fake `PlatformSecureFs`. */
|
|
714
717
|
export async function _run(projectDir, mode, options, deps) {
|
|
715
|
-
const manifest = deps.manifest ?? (await readManifest());
|
|
716
718
|
const platform = deps.platform ?? process.platform;
|
|
719
|
+
const platformSupport = resolvePlatformSupport(platform);
|
|
720
|
+
if (platformSupport) {
|
|
721
|
+
return {
|
|
722
|
+
ok: false,
|
|
723
|
+
changed: [],
|
|
724
|
+
backups: [],
|
|
725
|
+
errors: [platformSupport.refusalCode],
|
|
726
|
+
warnings: [platformSupport.guidance],
|
|
727
|
+
lifecycleRefusal: platformSupport,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const doctorFn = deps.doctor ?? doctorClaudePreToolUse;
|
|
731
|
+
const manifest = deps.manifest ?? (await readManifest());
|
|
717
732
|
const secureFs = deps.secureFs !== undefined ? deps.secureFs : selectSecureFs(platform);
|
|
718
733
|
const clock = deps.clock ?? (() => new Date());
|
|
719
734
|
const nonce = deps.nonce ?? (() => randomBytes(4).toString("hex"));
|
|
@@ -725,7 +740,7 @@ export async function _run(projectDir, mode, options, deps) {
|
|
|
725
740
|
// report, so the run never spawns `node --version` twice and the warning, the
|
|
726
741
|
// report row and the verdict can never disagree about the same PATH.
|
|
727
742
|
const nodeOnPath = await (deps.nodeProbe ?? probeNodeOnPath)();
|
|
728
|
-
const doctor = () =>
|
|
743
|
+
const doctor = () => doctorFn(projectDir, {
|
|
729
744
|
manifest,
|
|
730
745
|
aclProbe: deps.aclProbe,
|
|
731
746
|
execution: { nodeProbe: async () => nodeOnPath },
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
import { type AssetManifestEntry, type ExecutionReport, type NodeOnPathProbe } from "./claude-hook-manager.js";
|
|
32
32
|
import { type ClaudeHookComponentState } from "./claude-hook-settings.js";
|
|
33
|
+
import { type PlatformSupport } from "./platform-support.js";
|
|
33
34
|
import { type SpawnFn } from "./secure-fs-posix.js";
|
|
34
35
|
import { type PlatformSecureFs } from "./secure-fs-transaction.js";
|
|
35
36
|
export type { NodeOnPathProbe };
|
|
@@ -101,6 +102,7 @@ export declare function classifyCodexHooksJson(value: unknown, expectedCommand:
|
|
|
101
102
|
export type CodexTrustState = "trusted" | "untrusted";
|
|
102
103
|
export interface CodexHookDoctorReport {
|
|
103
104
|
healthy: boolean;
|
|
105
|
+
platformSupport?: PlatformSupport;
|
|
104
106
|
hooksJson: {
|
|
105
107
|
state: ClaudeHookComponentState;
|
|
106
108
|
detail?: string;
|
|
@@ -127,20 +129,34 @@ export interface CodexHookDoctorReport {
|
|
|
127
129
|
remediation: string[];
|
|
128
130
|
}
|
|
129
131
|
export interface CodexDoctorOptions {
|
|
132
|
+
platform?: NodeJS.Platform;
|
|
130
133
|
manifest?: Manifest;
|
|
131
134
|
assetPath?: string;
|
|
132
135
|
nodeVersion?: string;
|
|
133
136
|
nodeProbe?: () => Promise<NodeOnPathProbe>;
|
|
134
137
|
}
|
|
135
138
|
export declare function doctorCodexPreToolUse(homeDir?: string, options?: CodexDoctorOptions): Promise<CodexHookDoctorReport>;
|
|
136
|
-
|
|
139
|
+
interface CodexHookMutationResultWithReport {
|
|
137
140
|
ok: boolean;
|
|
138
141
|
changed: string[];
|
|
139
142
|
backups: string[];
|
|
143
|
+
/** Real post-mutation doctor snapshot. */
|
|
140
144
|
report: CodexHookDoctorReport;
|
|
141
145
|
errors: string[];
|
|
142
146
|
warnings: string[];
|
|
147
|
+
lifecycleRefusal?: never;
|
|
148
|
+
}
|
|
149
|
+
interface CodexHookLifecycleRefusal {
|
|
150
|
+
ok: false;
|
|
151
|
+
changed: string[];
|
|
152
|
+
backups: string[];
|
|
153
|
+
errors: string[];
|
|
154
|
+
warnings: string[];
|
|
155
|
+
/** The lifecycle gate refused before any doctor or installed-state probe. */
|
|
156
|
+
lifecycleRefusal: PlatformSupport;
|
|
157
|
+
report?: never;
|
|
143
158
|
}
|
|
159
|
+
export type CodexHookMutationResult = CodexHookMutationResultWithReport | CodexHookLifecycleRefusal;
|
|
144
160
|
export interface CodexHookRunDeps {
|
|
145
161
|
secureFs?: PlatformSecureFs | null;
|
|
146
162
|
clock?: () => Date;
|
|
@@ -150,6 +166,7 @@ export interface CodexHookRunDeps {
|
|
|
150
166
|
assetPath?: string;
|
|
151
167
|
nodeProbe?: () => Promise<NodeOnPathProbe>;
|
|
152
168
|
nodeSpawn?: SpawnFn;
|
|
169
|
+
doctor?: typeof doctorCodexPreToolUse;
|
|
153
170
|
}
|
|
154
171
|
export declare function _runCodex(homeDir: string, _mode: "install" | "repair", _options: {
|
|
155
172
|
force?: boolean;
|
|
@@ -34,6 +34,7 @@ import { CLAUDE_HOOK_ASSETS_DIR } from "../constants.js";
|
|
|
34
34
|
import { ASSET_NAME } from "./__fixtures__/claude-hook-ownership.js";
|
|
35
35
|
import { classifyAssetState, detectNode, probeNodeOnPath, } from "./claude-hook-manager.js";
|
|
36
36
|
import { isPlainObject, validateSettingsShape, } from "./claude-hook-settings.js";
|
|
37
|
+
import { resolvePlatformSupport, } from "./platform-support.js";
|
|
37
38
|
import { safeReadFile } from "./safe-read.js";
|
|
38
39
|
import { selectSecureFs } from "./secure-fs-posix.js";
|
|
39
40
|
import { runTransaction, } from "./secure-fs-transaction.js";
|
|
@@ -372,7 +373,9 @@ export async function doctorCodexPreToolUse(homeDir = os.homedir(), options = {}
|
|
|
372
373
|
}
|
|
373
374
|
if (!node.satisfiesMinimum)
|
|
374
375
|
remediation.push("install Node 22 or newer");
|
|
376
|
+
const platformSupport = resolvePlatformSupport(options.platform ?? process.platform);
|
|
375
377
|
return {
|
|
378
|
+
...(platformSupport ? { platformSupport } : {}),
|
|
376
379
|
healthy: status === "runnable",
|
|
377
380
|
hooksJson,
|
|
378
381
|
config: { featuresHooks, readable: configReadable },
|
|
@@ -396,8 +399,20 @@ function serialize(container) {
|
|
|
396
399
|
return Buffer.from(`${JSON.stringify(container, null, 2)}\n`, "utf8");
|
|
397
400
|
}
|
|
398
401
|
export async function _runCodex(homeDir, _mode, _options, deps) {
|
|
399
|
-
const manifest = deps.manifest ?? (await readManifest());
|
|
400
402
|
const platform = deps.platform ?? process.platform;
|
|
403
|
+
const platformSupport = resolvePlatformSupport(platform);
|
|
404
|
+
if (platformSupport) {
|
|
405
|
+
return {
|
|
406
|
+
ok: false,
|
|
407
|
+
changed: [],
|
|
408
|
+
backups: [],
|
|
409
|
+
errors: [platformSupport.refusalCode],
|
|
410
|
+
warnings: [platformSupport.guidance],
|
|
411
|
+
lifecycleRefusal: platformSupport,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const doctorFn = deps.doctor ?? doctorCodexPreToolUse;
|
|
415
|
+
const manifest = deps.manifest ?? (await readManifest());
|
|
401
416
|
const secureFs = deps.secureFs !== undefined ? deps.secureFs : selectSecureFs(platform);
|
|
402
417
|
const clock = deps.clock ?? (() => new Date());
|
|
403
418
|
const nonce = deps.nonce ??
|
|
@@ -406,7 +421,7 @@ export async function _runCodex(homeDir, _mode, _options, deps) {
|
|
|
406
421
|
const { codexDir, hooksFile, configFile } = codexConfigPaths(homeDir);
|
|
407
422
|
const expectedCommand = expectedCodexCommand(assetPath);
|
|
408
423
|
const nodeOnPath = await (deps.nodeProbe ?? probeNodeOnPath)();
|
|
409
|
-
const doctor = () =>
|
|
424
|
+
const doctor = () => doctorFn(homeDir, {
|
|
410
425
|
manifest,
|
|
411
426
|
assetPath,
|
|
412
427
|
nodeProbe: async () => nodeOnPath,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const PLATFORM_SUPPORT_STATE: {
|
|
2
|
+
readonly MACOS_DEPRECATED: "macos-deprecated";
|
|
3
|
+
};
|
|
4
|
+
export declare const LIFECYCLE_SUPPORT: {
|
|
5
|
+
readonly UNSUPPORTED: "unsupported";
|
|
6
|
+
};
|
|
7
|
+
export declare const PLATFORM_REFUSAL: {
|
|
8
|
+
readonly MACOS_LIFECYCLE_UNSUPPORTED: "macos-lifecycle-unsupported";
|
|
9
|
+
};
|
|
10
|
+
export interface PlatformSupport {
|
|
11
|
+
platform: "darwin";
|
|
12
|
+
state: typeof PLATFORM_SUPPORT_STATE.MACOS_DEPRECATED;
|
|
13
|
+
lifecycle: typeof LIFECYCLE_SUPPORT.UNSUPPORTED;
|
|
14
|
+
refusalCode: typeof PLATFORM_REFUSAL.MACOS_LIFECYCLE_UNSUPPORTED;
|
|
15
|
+
guidance: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const MACOS_DEPRECATION_GUIDANCE = "macOS is deprecated and unsupported for install, repair, and init; pin a supported release or migrate. Existing installed guards are not removed; Darwin removal is planned for 2.0.";
|
|
18
|
+
export declare function resolvePlatformSupport(platform: string): PlatformSupport | undefined;
|
|
19
|
+
//# sourceMappingURL=platform-support.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const PLATFORM_SUPPORT_STATE = {
|
|
2
|
+
MACOS_DEPRECATED: "macos-deprecated",
|
|
3
|
+
};
|
|
4
|
+
export const LIFECYCLE_SUPPORT = {
|
|
5
|
+
UNSUPPORTED: "unsupported",
|
|
6
|
+
};
|
|
7
|
+
export const PLATFORM_REFUSAL = {
|
|
8
|
+
MACOS_LIFECYCLE_UNSUPPORTED: "macos-lifecycle-unsupported",
|
|
9
|
+
};
|
|
10
|
+
export const MACOS_DEPRECATION_GUIDANCE = "macOS is deprecated and unsupported for install, repair, and init; pin a supported release or migrate. Existing installed guards are not removed; Darwin removal is planned for 2.0.";
|
|
11
|
+
export function resolvePlatformSupport(platform) {
|
|
12
|
+
if (platform !== "darwin")
|
|
13
|
+
return undefined;
|
|
14
|
+
return {
|
|
15
|
+
platform: "darwin",
|
|
16
|
+
state: PLATFORM_SUPPORT_STATE.MACOS_DEPRECATED,
|
|
17
|
+
lifecycle: LIFECYCLE_SUPPORT.UNSUPPORTED,
|
|
18
|
+
refusalCode: PLATFORM_REFUSAL.MACOS_LIFECYCLE_UNSUPPORTED,
|
|
19
|
+
guidance: MACOS_DEPRECATION_GUIDANCE,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=platform-support.js.map
|