From 2d02ad543d5e90c5276473660f8b282b8ca8face Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Fri, 7 Aug 2026 20:08:10 +0530 Subject: [PATCH 1/3] chore: remove dead code and stale daemon residue --- CHANGELOG.md | 3 ++ .../actions/update-scheduled-audit.test.ts | 2 +- __tests__/audit/cache.test.ts | 2 +- __tests__/components/button.test.tsx | 2 +- .../components/date-picker-input.test.tsx | 2 +- .../e2e/hooks/codex-integration.e2e.test.ts | 1 - __tests__/hooks/cloud-enrollment-cli.test.ts | 1 - __tests__/hooks/collector-config.test.ts | 2 +- __tests__/hooks/configure-wizard.test.ts | 3 +- __tests__/hooks/daemon-client.test.ts | 37 +++++++++---------- __tests__/hooks/fp-home.test.ts | 2 +- __tests__/hooks/new-telemetry.test.ts | 4 +- __tests__/lib/codex-sessions.test.ts | 2 +- __tests__/lib/pi-sessions.test.ts | 2 +- __tests__/lib/telemetry-id.test.ts | 1 - __tests__/lib/telemetry.test.ts | 1 - .../scripts/translate-docs/cache.test.ts | 2 +- app/actions/get-hooks-config.ts | 1 - app/audit/_components/audit-dashboard.tsx | 4 +- .../_components/audit-progress-strip.tsx | 2 +- .../_components/come-back-better-section.tsx | 2 +- app/audit/_components/empty-state.tsx | 1 - .../_components/how-to-improve-section.tsx | 2 +- app/audit/_components/quirks-section.tsx | 1 - app/audit/_components/report-footer.tsx | 2 - app/audit/_components/run-progress.tsx | 2 +- app/audit/_components/strengths-section.tsx | 1 - app/components/pause-notices.tsx | 2 +- app/components/raw-log-viewer.tsx | 2 +- app/components/toast.tsx | 2 +- app/policies/hooks-client.tsx | 5 ++- app/settings/settings-client.tsx | 3 +- bin/failproofaid-shim.mjs | 8 ++-- bun.lock | 11 ------ crates/PROTOCOL.md | 24 ++++++------ crates/failproofaid/Cargo.toml | 2 +- crates/failproofaid/src/worker.rs | 4 +- crates/failproofaid/tests/daemon_e2e.rs | 4 +- crates/fpai-ipc/src/envelope.rs | 6 +-- crates/fpai-ipc/src/peer.rs | 7 ++-- lib/auth/auth-store.ts | 1 - package.json | 1 - scripts/translate-docs/cache.ts | 2 +- scripts/translate-docs/cli.ts | 4 +- scripts/translate-docs/mintlify-nav.ts | 2 +- src/audit/cache.ts | 1 - src/audit/dashboard-cache.ts | 2 - src/audit/report.ts | 1 - src/hooks/builtin-policies.ts | 2 +- src/hooks/cloud-enrollment-cli.ts | 2 - src/hooks/cloud-enrollment.ts | 3 +- src/hooks/cloud-managed-policies.ts | 1 - src/hooks/collector-config.ts | 24 ++---------- src/hooks/configure-wizard.ts | 4 -- src/hooks/custom-hooks-loader.ts | 1 - src/hooks/daemon-client.ts | 32 ++-------------- src/hooks/daemon-download.ts | 1 - src/hooks/daemon-service.ts | 28 ++++++-------- src/hooks/hook-logger.ts | 1 - src/hooks/integrations.ts | 1 - src/hooks/loader-utils.ts | 2 +- src/hooks/manager.ts | 2 +- src/hooks/onboarding-lock.ts | 3 +- src/hooks/session-pause.ts | 1 - tsconfig.json | 1 + 65 files changed, 99 insertions(+), 191 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0108aee1..54751b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Fixes - Make `failproofai config` refuse setup on an unsupported platform (Windows, today) instead of completing it unenforced. The wizard used to skip the daemon requirement and finish anyway, leaving the machine reading as configured while enforcing in-process with no fail-closed guarantee — now it prints why and exits 1 before drawing a single prompt, writing nothing. (#664) +### Chores +- Remove compiler-proven dead code left around the `failproofaid` integration, including the obsolete daemon-hook wrapper and unused `html2canvas` dependency. Enable `noUnusedLocals` to prevent that residue returning, and correct stale daemon documentation and package metadata. + ## 1.0.0-beta.12 — 2026-08-07 ### Fixes diff --git a/__tests__/actions/update-scheduled-audit.test.ts b/__tests__/actions/update-scheduled-audit.test.ts index 3a30cf8f..67aca50d 100644 --- a/__tests__/actions/update-scheduled-audit.test.ts +++ b/__tests__/actions/update-scheduled-audit.test.ts @@ -15,7 +15,7 @@ * parity is real: both write through the same `updateConfig`. */ import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { mkdtempSync, readFileSync, rmSync, writeFileSync, mkdirSync } from "node:fs"; +import { mkdtempSync, readFileSync, rmSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; import { configFile } from "../../src/hooks/fp-home"; diff --git a/__tests__/audit/cache.test.ts b/__tests__/audit/cache.test.ts index 59802c07..04eabc6e 100644 --- a/__tests__/audit/cache.test.ts +++ b/__tests__/audit/cache.test.ts @@ -1,7 +1,7 @@ // @vitest-environment node import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; -import { tmpdir, homedir } from "node:os"; +import { tmpdir } from "node:os"; import { join } from "node:path"; import { createHash } from "node:crypto"; import { diff --git a/__tests__/components/button.test.tsx b/__tests__/components/button.test.tsx index af379745..41407279 100644 --- a/__tests__/components/button.test.tsx +++ b/__tests__/components/button.test.tsx @@ -1,4 +1,4 @@ -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect } from "vitest"; import { render, screen } from "@testing-library/react"; import { createRef } from "react"; import { Button } from "@/components/ui/button"; diff --git a/__tests__/components/date-picker-input.test.tsx b/__tests__/components/date-picker-input.test.tsx index 01ae11b4..0d12c293 100644 --- a/__tests__/components/date-picker-input.test.tsx +++ b/__tests__/components/date-picker-input.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import { render, screen } from "@testing-library/react"; +import { render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import DatePickerInput from "@/app/components/date-picker-input"; diff --git a/__tests__/e2e/hooks/codex-integration.e2e.test.ts b/__tests__/e2e/hooks/codex-integration.e2e.test.ts index 33311a69..234092c9 100644 --- a/__tests__/e2e/hooks/codex-integration.e2e.test.ts +++ b/__tests__/e2e/hooks/codex-integration.e2e.test.ts @@ -16,7 +16,6 @@ import { assertAllow, assertPreToolUseDeny, assertPostToolUseDeny, - assertStopInstruct, assertPermissionRequestDeny, } from "../helpers/hook-runner"; import { CodexPayloads } from "../helpers/payloads"; diff --git a/__tests__/hooks/cloud-enrollment-cli.test.ts b/__tests__/hooks/cloud-enrollment-cli.test.ts index 49beb672..0cc9bc62 100644 --- a/__tests__/hooks/cloud-enrollment-cli.test.ts +++ b/__tests__/hooks/cloud-enrollment-cli.test.ts @@ -6,7 +6,6 @@ import { resolve } from "node:path"; import { runConnectCommand, runDisconnectCommand, connectionStatusLines } from "../../src/hooks/cloud-enrollment-cli"; import { cloudCredentialPath, readCloudCredentials, writeCloudCredentials } from "../../src/hooks/cloud-enrollment"; import { readIngestCredential } from "../../src/hooks/collector-config"; -import { readHooksConfig } from "../../src/hooks/hooks-config"; import { readConfig } from "../../src/hooks/fp-config"; let dir: string; diff --git a/__tests__/hooks/collector-config.test.ts b/__tests__/hooks/collector-config.test.ts index 7e4b20ec..21f9c2c4 100644 --- a/__tests__/hooks/collector-config.test.ts +++ b/__tests__/hooks/collector-config.test.ts @@ -4,7 +4,7 @@ // `policies-config.json` is 0664 inside a 0775 `~/.failproofai` on a normal // machine, which is exactly why the key lives in its own file at 0600. import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { mkdtempSync, rmSync, statSync, readFileSync, writeFileSync, chmodSync, mkdirSync } from "node:fs"; +import { mkdtempSync, rmSync, statSync, writeFileSync, chmodSync, mkdirSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; diff --git a/__tests__/hooks/configure-wizard.test.ts b/__tests__/hooks/configure-wizard.test.ts index cf34d52a..b3a56874 100644 --- a/__tests__/hooks/configure-wizard.test.ts +++ b/__tests__/hooks/configure-wizard.test.ts @@ -144,14 +144,13 @@ import { maybeFirstRunConfigure, hasSeenLauncher, markLauncherSeen, - classifyDaemonInstallFailure, } from "../../src/hooks/configure-wizard"; import { resolvePreset, resolveEverything } from "../../src/hooks/policy-presets"; import { INTEGRATION_TYPES, type IntegrationType } from "../../src/hooks/types"; import { getIntegration } from "../../src/hooks/integrations"; import { runPostSetupAudit } from "../../src/audit/cli"; import { trackHookEvent } from "../../src/hooks/hook-telemetry"; -import { globalPolicyConfigFile, configFile as fpConfigFile, launcherMarker } from "../../src/hooks/fp-home"; +import { configFile as fpConfigFile, launcherMarker } from "../../src/hooks/fp-home"; import { readConfig as readFpConfig } from "../../src/hooks/fp-config"; const mkTtyStdin = (): TTYIn => ({ isTTY: true }) as unknown as TTYIn; diff --git a/__tests__/hooks/daemon-client.test.ts b/__tests__/hooks/daemon-client.test.ts index c3c6da49..4a4c5fe1 100644 --- a/__tests__/hooks/daemon-client.test.ts +++ b/__tests__/hooks/daemon-client.test.ts @@ -11,6 +11,7 @@ import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { writeConfig, DEFAULT_CONFIG } from "../../src/hooks/fp-config"; +import type { DaemonHookRequest, DaemonHookResponse } from "../../src/hooks/daemon-client"; vi.mock("../../src/hooks/hook-logger", () => ({ hookLogInfo: vi.fn(), @@ -74,6 +75,12 @@ describe("hooks/daemon-client", () => { await new Promise((resolvePromise) => server!.listen(socketPath, resolvePromise)); } + async function daemonResult(req: DaemonHookRequest): Promise { + const { attemptDaemonHook } = await import("../../src/hooks/daemon-client"); + const attempt = await attemptDaemonHook(req); + return attempt.ok ? attempt.response : null; + } + it("returns the parsed result on a real hookResult response", async () => { await startServer(async (socket) => { const req = await readFrame(socket); @@ -92,8 +99,7 @@ describe("hooks/daemon-client", () => { ); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); - const result = await tryDaemonHook({ + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}", @@ -116,8 +122,7 @@ describe("hooks/daemon-client", () => { ); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); expect(result).toEqual({ exitCode: 2, stdout: "", stderr: "blocked: sudo is not allowed" }); }); @@ -127,8 +132,7 @@ describe("hooks/daemon-client", () => { socket.end(encodeFrame({ type: "error", protocolVersion: 1, message: "daemon unreachable" })); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); - const result = await tryDaemonHook({ hookEvent: "Stop", cli: "codex", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "Stop", cli: "codex", stdin: "{}" }); expect(result).toBeNull(); }); @@ -140,8 +144,7 @@ describe("hooks/daemon-client", () => { ); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); expect(result).toBeNull(); }); @@ -204,16 +207,14 @@ describe("hooks/daemon-client", () => { socket.end(encodeFrame({ type: "hookResult", protocolVersion: 1, stdout: "", stderr: "" })); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); expect(result).toBeNull(); }); it("returns null immediately when no socket file exists at all", async () => { // No server started — socketPath was never bound. - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); const start = Date.now(); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); const elapsedMs = Date.now() - start; expect(result).toBeNull(); // ENOENT/ECONNREFUSED on a nonexistent socket is a kernel-level rejection, @@ -237,9 +238,8 @@ describe("hooks/daemon-client", () => { }, 600); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); const start = Date.now(); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); expect(result).toEqual({ exitCode: 0, stdout: "ok", stderr: "" }); expect(Date.now() - start).toBeGreaterThanOrEqual(500); }); @@ -252,9 +252,8 @@ describe("hooks/daemon-client", () => { // Deliberately never write a response. }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); let settled = false; - const pending = tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }).then((r) => { + const pending = daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }).then((r) => { settled = true; return r; }); @@ -277,8 +276,7 @@ describe("hooks/daemon-client", () => { socket.end(Buffer.concat([header, body])); }); - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); expect(result).toBeNull(); }); @@ -286,9 +284,8 @@ describe("hooks/daemon-client", () => { const originalPlatform = process.platform; Object.defineProperty(process, "platform", { value: "win32" }); try { - const { tryDaemonHook } = await import("../../src/hooks/daemon-client"); const start = Date.now(); - const result = await tryDaemonHook({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); + const result = await daemonResult({ hookEvent: "PreToolUse", cli: "claude", stdin: "{}" }); const elapsedMs = Date.now() - start; expect(result).toBeNull(); expect(elapsedMs).toBeLessThan(20); diff --git a/__tests__/hooks/fp-home.test.ts b/__tests__/hooks/fp-home.test.ts index 38e26821..5d962e0f 100644 --- a/__tests__/hooks/fp-home.test.ts +++ b/__tests__/hooks/fp-home.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs"; +import { mkdtempSync, rmSync, mkdirSync, writeFileSync, readFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; import * as H from "../../src/hooks/fp-home"; diff --git a/__tests__/hooks/new-telemetry.test.ts b/__tests__/hooks/new-telemetry.test.ts index eb998320..20c2f01a 100644 --- a/__tests__/hooks/new-telemetry.test.ts +++ b/__tests__/hooks/new-telemetry.test.ts @@ -5,10 +5,8 @@ * fire at the trigger site. Keep one focused case per event. */ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { readFileSync, existsSync } from "node:fs"; import { execSync } from "node:child_process"; -import { resolve } from "node:path"; -import { homedir } from "node:os"; vi.mock("node:fs", () => ({ readFileSync: vi.fn(), diff --git a/__tests__/lib/codex-sessions.test.ts b/__tests__/lib/codex-sessions.test.ts index cbe20942..e995a517 100644 --- a/__tests__/lib/codex-sessions.test.ts +++ b/__tests__/lib/codex-sessions.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; -import { tmpdir, homedir } from "node:os"; +import { tmpdir } from "node:os"; const line = (obj: Record): string => JSON.stringify(obj); diff --git a/__tests__/lib/pi-sessions.test.ts b/__tests__/lib/pi-sessions.test.ts index 9fb2ad07..73cb6c4f 100644 --- a/__tests__/lib/pi-sessions.test.ts +++ b/__tests__/lib/pi-sessions.test.ts @@ -1,6 +1,6 @@ // @vitest-environment node import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { mkdtempSync, mkdirSync, rmSync, writeFileSync, unlinkSync } from "node:fs"; +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import type { AssistantEntry, ContentBlock, ToolUseBlock } from "@/lib/log-entries"; diff --git a/__tests__/lib/telemetry-id.test.ts b/__tests__/lib/telemetry-id.test.ts index 128a1df6..11aebc1b 100644 --- a/__tests__/lib/telemetry-id.test.ts +++ b/__tests__/lib/telemetry-id.test.ts @@ -39,7 +39,6 @@ vi.mock("node:crypto", async () => { const mockedFs = vi.mocked(fs); const mockedOs = vi.mocked(os); -const mockedCrypto = vi.mocked(crypto); const mockedExecSync = vi.mocked(execSync); describe("lib/telemetry-id", () => { diff --git a/__tests__/lib/telemetry.test.ts b/__tests__/lib/telemetry.test.ts index 25769313..879fdd05 100644 --- a/__tests__/lib/telemetry.test.ts +++ b/__tests__/lib/telemetry.test.ts @@ -29,7 +29,6 @@ import { isTelemetryEnabled, initTelemetry, trackEvent, - flushTelemetry, shutdownTelemetry, } from "@/lib/telemetry"; diff --git a/__tests__/scripts/translate-docs/cache.test.ts b/__tests__/scripts/translate-docs/cache.test.ts index f0403880..5466e139 100644 --- a/__tests__/scripts/translate-docs/cache.test.ts +++ b/__tests__/scripts/translate-docs/cache.test.ts @@ -1,5 +1,5 @@ // @vitest-environment node -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { describe, it, expect } from "vitest"; import { contentHash, getCacheKey, diff --git a/app/actions/get-hooks-config.ts b/app/actions/get-hooks-config.ts index 80a72632..fb083a08 100644 --- a/app/actions/get-hooks-config.ts +++ b/app/actions/get-hooks-config.ts @@ -11,7 +11,6 @@ import { customPolicyId, conventionPolicyId, discoverPolicyFiles } from "@/src/h import { findProjectConfigDir } from "@/src/hooks/hooks-config"; import { readFile } from "node:fs/promises"; import { existsSync } from "node:fs"; -import { homedir } from "node:os"; import { basename, resolve } from "node:path"; import { customPoliciesDir } from "@/src/hooks/fp-home"; diff --git a/app/audit/_components/audit-dashboard.tsx b/app/audit/_components/audit-dashboard.tsx index 1b3bc5d0..ce64dfc3 100644 --- a/app/audit/_components/audit-dashboard.tsx +++ b/app/audit/_components/audit-dashboard.tsx @@ -14,7 +14,7 @@ * * Empty / running states fall back to EmptyState and RunProgress. */ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { getAuditResultAction } from "@/app/actions/get-audit-result"; import type { AuditResult, RunAuditOptions } from "@/src/audit/types"; import { classifyAgent } from "@/src/audit/archetypes"; @@ -227,8 +227,6 @@ export function AuditDashboard({ initial, projectFromUrl, totalCatalogSize }: Pr const result = cache.status === "cached" ? cache.result : null; if (!result) return null; const cachedAt = cache.status === "cached" ? cache.cachedAt : null; - const params = cache.status === "cached" ? cache.params : undefined; - /* ---- scanned but zero sessions --------------------------------- */ if (result.transcripts.scanned === 0) { return ( diff --git a/app/audit/_components/audit-progress-strip.tsx b/app/audit/_components/audit-progress-strip.tsx index b26d12be..2973931b 100644 --- a/app/audit/_components/audit-progress-strip.tsx +++ b/app/audit/_components/audit-progress-strip.tsx @@ -15,7 +15,7 @@ * Reuses the existing `RerunError.kind` discrimination to render a red * error strip with kind-specific copy when a run dies. */ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import type { RerunError } from "./rerun-button"; export type RerunStatus = diff --git a/app/audit/_components/come-back-better-section.tsx b/app/audit/_components/come-back-better-section.tsx index 9a3c12b2..68efcbf5 100644 --- a/app/audit/_components/come-back-better-section.tsx +++ b/app/audit/_components/come-back-better-section.tsx @@ -18,7 +18,7 @@ * link sits under the reminder card so the affordance survives without * dominating the layout. */ -import React, { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { usePostHog } from "@/contexts/PostHogContext"; import { isAbortError } from "@/lib/fetch-with-timeout"; import { AuthDialog, type AuthedUser } from "./auth-dialog"; diff --git a/app/audit/_components/empty-state.tsx b/app/audit/_components/empty-state.tsx index 4e652810..2e29a79c 100644 --- a/app/audit/_components/empty-state.tsx +++ b/app/audit/_components/empty-state.tsx @@ -12,7 +12,6 @@ * sharp `.btn-press` action button. Sized so it occupies the same vertical * space as the loaded dashboard does on its hero — no more cramped popover. */ -import React from "react"; import { triggerRun } from "./rerun-button"; import { usePostHog } from "@/contexts/PostHogContext"; diff --git a/app/audit/_components/how-to-improve-section.tsx b/app/audit/_components/how-to-improve-section.tsx index 188b93e8..878c5f01 100644 --- a/app/audit/_components/how-to-improve-section.tsx +++ b/app/audit/_components/how-to-improve-section.tsx @@ -10,7 +10,7 @@ * A single "install all" button at the section header copies the * combined install command for every prescribed policy. */ -import React, { useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import type { AuditResult } from "@/src/audit/types"; import { type Grade, tierName } from "@/src/audit/scoring"; import { usePostHog } from "@/contexts/PostHogContext"; diff --git a/app/audit/_components/quirks-section.tsx b/app/audit/_components/quirks-section.tsx index a074a74d..bfbc6cd3 100644 --- a/app/audit/_components/quirks-section.tsx +++ b/app/audit/_components/quirks-section.tsx @@ -8,7 +8,6 @@ * detector. No per-finding card chrome, no 4-quad body, no corner * crosshairs — evidence and fix live in section 04 (How to improve). */ -import React from "react"; import type { FindingCard } from "@/src/audit/findings"; interface Props { diff --git a/app/audit/_components/report-footer.tsx b/app/audit/_components/report-footer.tsx index 62675e48..d439d991 100644 --- a/app/audit/_components/report-footer.tsx +++ b/app/audit/_components/report-footer.tsx @@ -1,7 +1,5 @@ "use client"; -import React from "react"; - interface Props { cachedAt: string | null; fixed?: boolean; diff --git a/app/audit/_components/run-progress.tsx b/app/audit/_components/run-progress.tsx index 751f1dff..c9e73d7f 100644 --- a/app/audit/_components/run-progress.tsx +++ b/app/audit/_components/run-progress.tsx @@ -16,7 +16,7 @@ * pink "▮▮" / dim "○" markers, and a marquee progress bar at the bottom * filling pink-on-dark as the run advances. */ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; const STAGES = [ { label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" }, diff --git a/app/audit/_components/strengths-section.tsx b/app/audit/_components/strengths-section.tsx index 81124576..06ecef0d 100644 --- a/app/audit/_components/strengths-section.tsx +++ b/app/audit/_components/strengths-section.tsx @@ -5,7 +5,6 @@ * ✓ glyph · headline + sub · right-aligned metric. No card chrome, * no hover backgrounds, no checkmark backdrop. */ -import React from "react"; import type { Strength } from "@/src/audit/strengths"; interface Props { diff --git a/app/components/pause-notices.tsx b/app/components/pause-notices.tsx index 840862f1..b15fa200 100644 --- a/app/components/pause-notices.tsx +++ b/app/components/pause-notices.tsx @@ -8,7 +8,7 @@ * where every policy ran and allowed, and without saying so the log asserts a * clean window over exactly the window that was not enforced. */ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { ShieldAlert, TriangleAlert } from "lucide-react"; import type { ActivePause } from "@/src/hooks/session-pause"; diff --git a/app/components/raw-log-viewer.tsx b/app/components/raw-log-viewer.tsx index 7305b3fd..66a5fe76 100644 --- a/app/components/raw-log-viewer.tsx +++ b/app/components/raw-log-viewer.tsx @@ -5,7 +5,7 @@ */ "use client"; -import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { useWindowVirtualizer } from "@tanstack/react-virtual"; import { ChevronDown, Wrench } from "lucide-react"; import type { LogEntry, ToolUseBlock } from "@/lib/log-entries"; diff --git a/app/components/toast.tsx b/app/components/toast.tsx index ad563c59..dc543206 100644 --- a/app/components/toast.tsx +++ b/app/components/toast.tsx @@ -1,6 +1,6 @@ "use client"; -import { useSyncExternalStore, useCallback, useEffect, useState } from "react"; +import { useSyncExternalStore, useEffect, useState } from "react"; interface Toast { id: number; diff --git a/app/policies/hooks-client.tsx b/app/policies/hooks-client.tsx index e6d2e0ea..cc0ed6c6 100644 --- a/app/policies/hooks-client.tsx +++ b/app/policies/hooks-client.tsx @@ -1,6 +1,7 @@ "use client"; -import React, { useState, useEffect, useCallback, useMemo, useRef, useTransition } from "react"; +import { useState, useEffect, useCallback, useMemo, useRef, useTransition } from "react"; +import * as React from "react"; import { createPortal } from "react-dom"; import Link from "next/link"; import { Check, ChevronDown, Code, Copy, Settings, Shield, ShieldAlert, ShieldCheck, ShieldX, TriangleAlert, X } from "lucide-react"; @@ -11,7 +12,7 @@ import { getActivePausesAction } from "@/app/actions/get-active-pauses"; import type { ActivePause } from "@/src/hooks/session-pause"; import { PausedBanner, PausedNote, PausedPill } from "@/app/components/pause-notices"; import { getHooksConfigAction } from "@/app/actions/get-hooks-config"; -import type { HooksConfigPayload, PolicyInfo, CustomPolicyInfo } from "@/app/actions/get-hooks-config"; +import type { HooksConfigPayload, PolicyInfo } from "@/app/actions/get-hooks-config"; import type { IntegrationType } from "@/src/hooks/types"; import { toggleCustomPolicyAction, togglePolicyAction } from "@/app/actions/update-hooks-config"; import { installHooksWebAction, removeHooksWebAction } from "@/app/actions/install-hooks-web"; diff --git a/app/settings/settings-client.tsx b/app/settings/settings-client.tsx index 4639f980..48ea6f6e 100644 --- a/app/settings/settings-client.tsx +++ b/app/settings/settings-client.tsx @@ -17,11 +17,10 @@ * documented on each action module. */ -import React, { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { getScheduledAuditAction, type ScheduledAuditView } from "@/app/actions/get-scheduled-audit"; import { setAutoAuditAction, setAuditIntervalAction } from "@/app/actions/update-scheduled-audit"; import { triggerRun, RerunError } from "@/app/audit/_components/rerun-button"; -import { AuthDialog } from "@/app/audit/_components/auth-dialog"; import { toast } from "@/app/components/toast"; import { fetchWithTimeout } from "@/lib/fetch-with-timeout"; import { formatRelativeTime } from "@/lib/format-duration"; diff --git a/bin/failproofaid-shim.mjs b/bin/failproofaid-shim.mjs index ba38e10b..b5ad1cc3 100644 --- a/bin/failproofaid-shim.mjs +++ b/bin/failproofaid-shim.mjs @@ -11,10 +11,10 @@ * keep alive itself. This shim exists only for a user (or script) * invoking `failproofaid` by hand. * - * The npm package deliberately ships no binary: one tarball serves every - * platform, and the four cross-compiled binaries live on the GitHub - * Release for this version (see `src/hooks/daemon-download.ts`). - * `failproofai config` is what fetches one. So "not installed" here is a + * The root npm tarball contains no native binary. A matching optional platform + * package normally supplies it; the GitHub Release for this version is the + * verified fallback (see `src/hooks/daemon-download.ts`). `failproofai config` + * installs either source into `~/.failproofai/bin`. So "not installed" here is a * normal state rather than a broken install, and it degrades with a * one-line message and a non-zero exit — never a stack trace — including * on a platform that has no binary at all (Windows). The daemon-connect diff --git a/bun.lock b/bun.lock index f2943e93..8ae1be8e 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,6 @@ "name": "failproofai", "dependencies": { "html-to-image": "^1.11.13", - "html2canvas": "^1.4.1", "posthog-node": "^5.47.7", "smol-toml": "^1.7.1", "sql.js": "^1.14.1", @@ -486,8 +485,6 @@ "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "base64-arraybuffer": ["base64-arraybuffer@1.0.2", "", {}, "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg=="], "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], @@ -530,8 +527,6 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "css-line-break": ["css-line-break@2.1.0", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w=="], - "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], @@ -738,8 +733,6 @@ "html-to-image": ["html-to-image@1.11.13", "", {}, "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg=="], - "html2canvas": ["html2canvas@1.4.1", "", { "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" } }, "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA=="], - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], @@ -1178,8 +1171,6 @@ "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], - "text-segmentation": ["text-segmentation@1.0.3", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], "tinyexec": ["tinyexec@1.2.2", "", {}, "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g=="], @@ -1250,8 +1241,6 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "utrie": ["utrie@1.0.2", "", { "dependencies": { "base64-arraybuffer": "^1.0.2" } }, "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw=="], - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], diff --git a/crates/PROTOCOL.md b/crates/PROTOCOL.md index 26613275..f7de1337 100644 --- a/crates/PROTOCOL.md +++ b/crates/PROTOCOL.md @@ -8,9 +8,8 @@ invocation already has today — see `src/hooks/handler.ts`'s relays it over a socket instead of a fresh process's argv/stdin/stdout. Implemented in `crates/fpai-ipc` (framing + envelope + peer verification) and -`crates/failproofaid` (the socket server itself). As of Stage 2, the daemon -answers `ping` and rejects `hook` with a stub "not implemented" error — Stage 3 -wires `hook` up to a real warm Node/Bun worker. +`crates/failproofaid` (the socket server and worker supervisor). The daemon +answers `ping` directly and relays `hook` requests to its warm Node/Bun worker. ## Transport @@ -25,9 +24,9 @@ this at a directory failproofaid doesn't own itself; see `crates/failproofaid/src/paths.rs`'s `ensure_run_dir`, which refuses to modify permissions on a pre-existing directory it didn't create). -Permissions: the run directory is `0700` and the socket file `0600` — this is -the actual access-control boundary (user-scope only, no elevation, same OS -user only). `crates/fpai-ipc/src/peer.rs`'s `SO_PEERCRED` (Linux) / +Permissions: the run directory is `0700` and the socket file `0600`. Although +the service definition is installed system-wide, the daemon process runs as +the configured OS user. `crates/fpai-ipc/src/peer.rs`'s `SO_PEERCRED` (Linux) / `getpeereid` (macOS) check is defense-in-depth on top of that, not a stronger boundary — same-user access can always reach this daemon regardless. @@ -86,8 +85,8 @@ hazard" the TS-side plan calls out explicitly). } // The daemon accepted the connection and parsed the request, but could not -// produce a verdict (worker down/hung, or — in Stage 2 — hook evaluation -// simply isn't wired up yet). Distinct from hookResult so the client can +// produce a verdict (for example, the worker is down or hung). Distinct from +// hookResult so the client can // tell "ran and decided" apart from "daemon couldn't evaluate at all" — the // latter is what drives the client's fail-closed path. { "type": "error", "protocolVersion": 1, "message": "..." } @@ -96,11 +95,10 @@ hazard" the TS-side plan calls out explicitly). ## Protocol versioning `protocolVersion` is carried on every message in both directions. A mismatch -gets an explicit `error` response from the daemon — the client treats *any* -failure mode (missing socket, refused connection, timeout, malformed -response, or an explicit version mismatch) identically: fall through to -whatever the client's fail-closed/in-process policy dictates. There is no -negotiation, only agree-or-fall-back. +gets an explicit `error` response from the daemon. A daemon-configured client +fails closed on every missing or unusable verdict; it preserves the mismatch +category only to print the correct repair instructions. There is no protocol +negotiation: the CLI and daemon must agree exactly. ## Peer verification diff --git a/crates/failproofaid/Cargo.toml b/crates/failproofaid/Cargo.toml index c930a7bc..58d53af5 100644 --- a/crates/failproofaid/Cargo.toml +++ b/crates/failproofaid/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true license-file.workspace = true repository.workspace = true -description = "Thin Rust supervisor: owns the failproofai IPC socket, service lifecycle, and warm worker process supervision. Carries no policy logic." +description = "FailproofAI background service for hook evaluation, policy synchronization, collection, scheduled audits, and telemetry." publish = false [[bin]] diff --git a/crates/failproofaid/src/worker.rs b/crates/failproofaid/src/worker.rs index 0f3f789f..05edfe65 100644 --- a/crates/failproofaid/src/worker.rs +++ b/crates/failproofaid/src/worker.rs @@ -70,8 +70,8 @@ impl WorkerCommand { if let Ok(cmd) = std::env::var("FAILPROOFAI_WORKER_CMD") { return WorkerCommand::shell(cmd); } - // Packaging (Stage 5) lands dist/worker.mjs; until then this is only - // reachable via the explicit override above in dev/test. + // Published packages place dist/worker.mjs at this relative path; the + // installed service normally supplies an absolute command via env. WorkerCommand::Node { script: PathBuf::from("dist/worker.mjs"), } diff --git a/crates/failproofaid/tests/daemon_e2e.rs b/crates/failproofaid/tests/daemon_e2e.rs index b423905c..9a8ab051 100644 --- a/crates/failproofaid/tests/daemon_e2e.rs +++ b/crates/failproofaid/tests/daemon_e2e.rs @@ -1,6 +1,6 @@ //! Black-box tests that spawn the real compiled `failproofaid` binary as a -//! subprocess and talk to it over a real Unix socket — closer to how a -//! systemd unit / launchd agent will actually invoke it (Stage 4) than the +//! subprocess and talk to it over a real Unix socket — closer to how the +//! installed systemd unit / launchd daemon invokes it than the //! in-process unit tests in `src/server.rs` are. use fpai_ipc::{ClientMessage, PROTOCOL_VERSION, ServerMessage, read_message, write_message}; diff --git a/crates/fpai-ipc/src/envelope.rs b/crates/fpai-ipc/src/envelope.rs index 55f86727..2d80a7f6 100644 --- a/crates/fpai-ipc/src/envelope.rs +++ b/crates/fpai-ipc/src/envelope.rs @@ -9,9 +9,9 @@ use serde::{Deserialize, Serialize}; /// Bumped whenever a wire-incompatible change is made to either message -/// enum below. The client treats any mismatch identically to "daemon -/// unreachable" (see `docs/configuration.mdx` and the failproofaid plan) — -/// there is no negotiation, only agree-or-fall-back. +/// enum below. A daemon-configured client fails closed on a mismatch and uses +/// the distinct failure category only to explain how to repair the skew. +/// There is no protocol negotiation. pub const PROTOCOL_VERSION: u32 = 1; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/crates/fpai-ipc/src/peer.rs b/crates/fpai-ipc/src/peer.rs index 1ff61791..1c8859d4 100644 --- a/crates/fpai-ipc/src/peer.rs +++ b/crates/fpai-ipc/src/peer.rs @@ -1,10 +1,9 @@ //! Verifies that a Unix socket peer is running as the same OS user as this //! process. //! -//! failproofaid is user-scope only (see the plan: "no elevation, the daemon -//! spawns the worker because the daemon already runs as the user"): the -//! socket directory is `0700` and the socket file `0600`, which is the -//! actual access-control boundary. This check is a second, defense-in-depth +//! failproofaid's service definition is installed system-wide but runs the +//! process as the configured user. The socket directory is `0700` and the +//! socket file `0600`. This check is a second, defense-in-depth //! layer against the narrow window between a socket file existing and its //! permissions having been fully applied, and against misconfigured //! filesystems (e.g. a shared mount with unexpectedly loose permissions). diff --git a/lib/auth/auth-store.ts b/lib/auth/auth-store.ts index 3cfcae5d..a3ec0c1c 100644 --- a/lib/auth/auth-store.ts +++ b/lib/auth/auth-store.ts @@ -7,7 +7,6 @@ */ import { existsSync, readFileSync, rmSync } from "node:fs"; -import { homedir } from "node:os"; import { join } from "node:path"; import { writeJsonAtomically } from "../atomic-write"; diff --git a/package.json b/package.json index dd2476a2..fe1cb17b 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,6 @@ }, "dependencies": { "html-to-image": "^1.11.13", - "html2canvas": "^1.4.1", "posthog-node": "^5.47.7", "smol-toml": "^1.7.1", "sql.js": "^1.14.1", diff --git a/scripts/translate-docs/cache.ts b/scripts/translate-docs/cache.ts index 94bfd7b6..eca8cea4 100644 --- a/scripts/translate-docs/cache.ts +++ b/scripts/translate-docs/cache.ts @@ -2,7 +2,7 @@ import { createHash } from "node:crypto"; import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import type { CacheEntry, TranslationCache } from "./types"; +import type { TranslationCache } from "./types"; const __dirname = dirname(fileURLToPath(import.meta.url)); const CACHE_FILE = join(__dirname, ".translation-cache.json"); diff --git a/scripts/translate-docs/cli.ts b/scripts/translate-docs/cli.ts index a708c788..1f23f68e 100644 --- a/scripts/translate-docs/cli.ts +++ b/scripts/translate-docs/cli.ts @@ -1,6 +1,6 @@ #!/usr/bin/env bun import { parseArgs } from "node:util"; -import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { existsSync, readFileSync } from "node:fs"; import { dirname, join, relative } from "node:path"; import { fileURLToPath } from "node:url"; import { @@ -297,7 +297,7 @@ async function main() { // Translate uncached pages with concurrency limit if (uncachedTasks.length > 0) { - const taskResults = await runWithConcurrency( + await runWithConcurrency( uncachedTasks.map(({ page, relPath, lang }) => async () => { try { const result = await translateMdxPage(page, lang, { diff --git a/scripts/translate-docs/mintlify-nav.ts b/scripts/translate-docs/mintlify-nav.ts index e003c6bf..24b6f5f3 100644 --- a/scripts/translate-docs/mintlify-nav.ts +++ b/scripts/translate-docs/mintlify-nav.ts @@ -1,7 +1,7 @@ import { readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { getLanguageByCode, NAV_TRANSLATIONS, LANGUAGES } from "./config"; +import { getLanguageByCode, NAV_TRANSLATIONS } from "./config"; const __dirname = dirname(fileURLToPath(import.meta.url)); const DOCS_JSON_PATH = join(__dirname, "..", "..", "docs", "docs.json"); diff --git a/src/audit/cache.ts b/src/audit/cache.ts index 323bc53c..29f60ca6 100644 --- a/src/audit/cache.ts +++ b/src/audit/cache.ts @@ -12,7 +12,6 @@ import { createHash } from "node:crypto"; import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "node:fs"; import { join } from "node:path"; -import { homedir } from "node:os"; import { BUILTIN_POLICIES } from "../hooks/builtin-policies"; import { AUDIT_DETECTORS } from "./detectors"; import type { TranscriptAuditResult } from "./types"; diff --git a/src/audit/dashboard-cache.ts b/src/audit/dashboard-cache.ts index abb38135..bc2370b3 100644 --- a/src/audit/dashboard-cache.ts +++ b/src/audit/dashboard-cache.ts @@ -11,8 +11,6 @@ * makes navigating back to /audit instant without re-running. */ import { existsSync, readFileSync } from "node:fs"; -import { join } from "node:path"; -import { homedir } from "node:os"; import { writeJsonAtomically } from "../../lib/atomic-write"; import type { AuditResult, RunAuditOptions } from "./types"; import { auditDashboardFile } from "../hooks/fp-home"; diff --git a/src/audit/report.ts b/src/audit/report.ts index aec9f666..31ede51b 100644 --- a/src/audit/report.ts +++ b/src/audit/report.ts @@ -31,7 +31,6 @@ function noColorEnabled(): boolean { } function stripAnsi(s: string): string { - // eslint-disable-next-line no-control-regex return s.replace(/\x1B\[[0-9;]*m/g, ""); } diff --git a/src/hooks/builtin-policies.ts b/src/hooks/builtin-policies.ts index 2d7bc132..44220b2e 100644 --- a/src/hooks/builtin-policies.ts +++ b/src/hooks/builtin-policies.ts @@ -3,7 +3,7 @@ */ import { resolve, join } from "node:path"; import { statSync } from "node:fs"; -import { readFile, writeFile, stat, open } from "node:fs/promises"; +import { readFile, writeFile } from "node:fs/promises"; import { execSync, execFileSync } from "node:child_process"; import { homedir } from "node:os"; import type { BuiltinPolicyDefinition, PolicyContext, PolicyResult, PolicyParamsSchema } from "./policy-types"; diff --git a/src/hooks/cloud-enrollment-cli.ts b/src/hooks/cloud-enrollment-cli.ts index 61e941eb..01761397 100644 --- a/src/hooks/cloud-enrollment-cli.ts +++ b/src/hooks/cloud-enrollment-cli.ts @@ -10,7 +10,6 @@ import { resolveMachineLabel, validateCloudUrl, verifyCloudCredentials, - writeCloudCredentials, cloudCredentialPath, } from "./cloud-enrollment"; import { daemonRestartCommand, daemonServiceStatus, daemonVersionSkew } from "./daemon-service"; @@ -23,7 +22,6 @@ import { configuredPaths, connectToCloud, describeOutcome, - ingestUrlFor, } from "./cloud-connection"; import { clearIngestCredential, diff --git a/src/hooks/cloud-enrollment.ts b/src/hooks/cloud-enrollment.ts index 649a7641..e803be51 100644 --- a/src/hooks/cloud-enrollment.ts +++ b/src/hooks/cloud-enrollment.ts @@ -18,8 +18,7 @@ */ import { randomUUID } from "node:crypto"; import { existsSync, readFileSync, rmSync } from "node:fs"; -import { homedir, hostname } from "node:os"; -import { join } from "node:path"; +import { hostname } from "node:os"; import { writeJsonAtomically } from "../../lib/atomic-write"; import { fetchWithTimeout, isAbortError } from "../../lib/fetch-with-timeout"; import { credentialsFile } from "./fp-home"; diff --git a/src/hooks/cloud-managed-policies.ts b/src/hooks/cloud-managed-policies.ts index 40ccfdfd..f09167e7 100644 --- a/src/hooks/cloud-managed-policies.ts +++ b/src/hooks/cloud-managed-policies.ts @@ -6,7 +6,6 @@ */ import { createHash } from "node:crypto"; import { existsSync, readFileSync, realpathSync, rmSync } from "node:fs"; -import { homedir } from "node:os"; import { isAbsolute, relative, resolve } from "node:path"; import { cloudPoliciesDir } from "./fp-home"; diff --git a/src/hooks/collector-config.ts b/src/hooks/collector-config.ts index 21d409bc..e8811c91 100644 --- a/src/hooks/collector-config.ts +++ b/src/hooks/collector-config.ts @@ -3,31 +3,15 @@ * * Two files, deliberately: * - * ~/.failproofai/ingest.json mode 0600, the credential ONLY - * ~/.failproofai/policies-config.json the non-secret settings + * ~/.failproofai/credentials.toml mode 0600, including the ingest credential + * ~/.failproofai/config.toml non-secret collector settings * - * The split is not tidiness. `policies-config.json` is written with a bare - * `writeFileSync`, so it inherits the umask and lands at 0664 on a normal - * machine — inside a `~/.failproofai/` that is itself 0775. An API key there - * would be readable by every local user on the box. `~/.agenteye/cli.json` - * already stores its session token at 0600, so the correct precedent existed. + * The split keeps bearer credentials out of the human-readable configuration + * file and gives every token the same owner-only storage boundary. * * The Rust daemon reads both (see `crates/fpai-collect/src/config.rs`); this is * the only thing that writes them. */ -import { - writeFileSync, - mkdirSync, - existsSync, - chmodSync, - statSync, - readFileSync, - rmSync, -} from "node:fs"; -import { join } from "node:path"; -import { homedir } from "node:os"; - -import { readHooksConfig, writeHooksConfig } from "./hooks-config"; import { credentialsFile, failproofaiHome as layoutHome } from "./fp-home"; import { readCredentials, writeCredentials, updateConfig } from "./fp-config"; diff --git a/src/hooks/configure-wizard.ts b/src/hooks/configure-wizard.ts index eeae2357..08325aaf 100644 --- a/src/hooks/configure-wizard.ts +++ b/src/hooks/configure-wizard.ts @@ -44,8 +44,6 @@ import { import { DEFAULT_INGEST_URL, validateIngestKey, - writeIngestCredential, - writeCollectorSettings, } from "./collector-config"; import { detectInstalledClis, @@ -82,8 +80,6 @@ import { resolveMachineId, resolveMachineLabel, validateCloudUrl, - verifyCloudCredentials, - writeCloudCredentials, } from "./cloud-enrollment"; import { cloudBaseFor, diff --git a/src/hooks/custom-hooks-loader.ts b/src/hooks/custom-hooks-loader.ts index 2dd79baf..8a627c96 100644 --- a/src/hooks/custom-hooks-loader.ts +++ b/src/hooks/custom-hooks-loader.ts @@ -15,7 +15,6 @@ import { randomUUID } from "crypto"; import { existsSync, readdirSync } from "node:fs"; import { readFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; -import { homedir } from "node:os"; import { createHash } from "node:crypto"; import { hookLogWarn, hookLogError, hookLogInfo } from "./hook-logger"; import { customPolicies, getCustomHooks, clearCustomHooks } from "./custom-hooks-registry"; diff --git a/src/hooks/daemon-client.ts b/src/hooks/daemon-client.ts index b44304b0..394d518c 100644 --- a/src/hooks/daemon-client.ts +++ b/src/hooks/daemon-client.ts @@ -6,17 +6,12 @@ * request, a 4-byte big-endian u32 length prefix followed by that many * bytes of UTF-8 JSON, camelCase fields, `"type"` as the tag. * - * This module makes NO decision about what to do when the daemon can't be - * reached — `tryDaemonHook` just returns `null` on any failure. The caller - * (`bin/failproofai.mjs`) decides: fall back to full in-process evaluation - * on a machine that was never daemon-configured, or fail closed on one that - * was. See `isDaemonConfigured`. + * This module reports whether a daemon request succeeded, was unreachable, or + * used an incompatible protocol. The caller (`bin/failproofai.mjs`) owns the + * fail-closed response for daemon-configured machines. */ import { createConnection } from "node:net"; -import { resolve } from "node:path"; -import { homedir } from "node:os"; import type { IntegrationType } from "./types"; -import { readHooksConfig } from "./hooks-config"; import { existsSync } from "node:fs"; import { daemonSocket as daemonSocketPath } from "./fp-home"; import { readConfig } from "./fp-config"; @@ -181,26 +176,7 @@ function encodeFrame(value: unknown): Buffer { return Buffer.concat([header, body]); } -/** - * Attempts one hook evaluation via the daemon. Returns `null` on **any** - * failure — no socket, connection refused, timeout, malformed response, - * protocol-version mismatch, or an explicit `error` message from the - * daemon. The caller never needs to distinguish failure modes; it just - * falls through to whatever its own fallback policy is. - */ -export async function tryDaemonHook(req: DaemonHookRequest): Promise { - const attempt = await attemptDaemonHook(req); - return attempt.ok ? attempt.response : null; -} - -/** - * The same attempt, but reporting WHY it failed. - * - * `tryDaemonHook` above collapses every failure into `null`, which loses the - * one thing the caller still needs: both failures deny (see `DaemonFailure`), - * but they call for different remedies — reinstall the daemon versus start it — - * and a `null` cannot say which to print. - */ +/** Attempts a daemon evaluation while preserving the failure category. */ export async function attemptDaemonHook( req: DaemonHookRequest, opts?: { diff --git a/src/hooks/daemon-download.ts b/src/hooks/daemon-download.ts index 219a080e..62831172 100644 --- a/src/hooks/daemon-download.ts +++ b/src/hooks/daemon-download.ts @@ -45,7 +45,6 @@ import { writeFileSync, } from "node:fs"; import { createRequire } from "node:module"; -import { homedir } from "node:os"; import { dirname, resolve } from "node:path"; import { gunzipSync } from "node:zlib"; import { version } from "../../package.json"; diff --git a/src/hooks/daemon-service.ts b/src/hooks/daemon-service.ts index ddf198cf..12a371ae 100644 --- a/src/hooks/daemon-service.ts +++ b/src/hooks/daemon-service.ts @@ -1,8 +1,7 @@ /** - * Installs/uninstalls/checks failproofaid as a real OS-level user service - * (systemd `--user` on Linux, launchd `LaunchAgent` on macOS) so it's - * "constant" — starts at login, restarts on crash — without ever needing - * elevation. User-scope only, matching the daemon itself. + * Installs, upgrades, checks, and removes failproofaid as a system-managed + * service. The definition is root-owned and starts at boot, but the daemon + * process itself runs as the user who configured failproofai. * * No public `failproofai daemon install`-style subcommand exists — * `configure-wizard.ts` calls the functions here directly, the same @@ -18,18 +17,17 @@ import { rmSync, } from "node:fs"; import { homedir, tmpdir, userInfo } from "node:os"; -import { resolve, dirname } from "node:path"; +import { resolve } from "node:path"; import { execFileSync } from "node:child_process"; import { hookLogWarn } from "./hook-logger"; -import { getConfigPathForScope } from "./hooks-config"; import { downloadFailproofaidBinary, installFromNpmPackage, installedBinaryPath } from "./daemon-download"; import { logsDir } from "./fp-home"; import { version } from "../../package.json"; import { readVersionFile, updateConfig, writeVersionFile } from "./fp-config"; /** - * Every `systemctl --user` / `launchctl` call is bounded. Both talk to a - * per-user session bus or to launchd, and a wedged session makes an + * Every `systemctl` / `launchctl` call is bounded. A wedged service manager + * makes an * unbounded `execFileSync` block forever — inside the interactive wizard * that reads as a hang with no output at all (`stdio: "ignore"`), right * after the user pressed "apply". A timeout throws instead, which the @@ -39,7 +37,7 @@ const SERVICE_CMD_TIMEOUT_MS = 10_000; /** * How long to wait for the service manager to actually get the daemon into - * a running state after `enable --now` / `load -w`. Both commands return as + * a running state after restart/load. Those commands return as * soon as the job is accepted, which is well before the process has proven * it can stay up. */ @@ -47,7 +45,7 @@ const SERVICE_START_TIMEOUT_MS = 5_000; const SERVICE_START_POLL_MS = 100; /** * How long a unit has to still be running after it first reports running. - * `systemctl --user is-active` calls a `Type=simple` unit active the moment + * `systemctl is-active` calls a `Type=simple` unit active the moment * it forks, so a daemon that dies immediately still reports active once — * a single check would wave through exactly the crash-at-startup case this * is here to catch. Comfortably longer than the unit's `RestartSec=2` @@ -688,9 +686,8 @@ ${conditionLines} Type=simple User=${user} # Set explicitly rather than relying on systemd deriving it from User=: -# failproofaid is user-scope by construction and refuses to start without -# HOME ("HOME is not set; failproofaid is user-scope only"), so the one -# variable it cannot do without is not left to a version-dependent default. +# failproofaid stores per-user state and refuses to start without HOME, so this +# required variable is not left to a version-dependent default. Environment="HOME=${homedir()}" ${envLines}ExecStart=${binaryPath} Restart=on-failure @@ -810,9 +807,8 @@ export async function installDaemonService(): Promise { return { installed: false, reason: `failproofaid is not supported on ${process.platform} yet` }; } - // May reach the network: the npm package carries no binary, so this is - // where a machine opting into the daemon fetches the one built for its - // platform from this version's release. + // May reach the network when the matching optional platform package is not + // installed. The GitHub release asset is the verified fallback channel. const { path: binaryPath, reason: binaryReason } = await ensureFailproofaidBinary(); if (!binaryPath) { return { installed: false, reason: binaryReason ?? "failproofaid binary not found for this platform" }; diff --git a/src/hooks/hook-logger.ts b/src/hooks/hook-logger.ts index d197e645..c800cae3 100644 --- a/src/hooks/hook-logger.ts +++ b/src/hooks/hook-logger.ts @@ -21,7 +21,6 @@ import { statSync, } from "node:fs"; import { join } from "node:path"; -import { homedir } from "node:os"; import { logsDir } from "./fp-home"; export type LogLevel = "info" | "warn" | "error"; diff --git a/src/hooks/integrations.ts b/src/hooks/integrations.ts index 4a403d93..f809f61f 100644 --- a/src/hooks/integrations.ts +++ b/src/hooks/integrations.ts @@ -14,7 +14,6 @@ import { homedir } from "node:os"; import { parseDocument, type Document } from "yaml"; import { listHermesProfiles, hermesRoot } from "../../lib/hermes-profiles"; import { - HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES, HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, diff --git a/src/hooks/loader-utils.ts b/src/hooks/loader-utils.ts index 7c788143..1b17244d 100644 --- a/src/hooks/loader-utils.ts +++ b/src/hooks/loader-utils.ts @@ -184,7 +184,7 @@ async function writeShim( } export async function createEsmShim( - distIndex: string, + _distIndex: string, distUrl: string, tmpSuffix = TMP_SUFFIX, ): Promise<{ shimPath: string; shimUrl: string }> { diff --git a/src/hooks/manager.ts b/src/hooks/manager.ts index 1202f6ee..8d99cd7f 100644 --- a/src/hooks/manager.ts +++ b/src/hooks/manager.ts @@ -8,7 +8,7 @@ import { execSync } from "node:child_process"; import { existsSync } from "node:fs"; import { resolve, basename } from "node:path"; -import { homedir, platform, arch, release, hostname } from "node:os"; +import { platform, arch, release, hostname } from "node:os"; import { HOOK_SCOPES, type HookScope, diff --git a/src/hooks/onboarding-lock.ts b/src/hooks/onboarding-lock.ts index 3b78e073..9b7e00e1 100644 --- a/src/hooks/onboarding-lock.ts +++ b/src/hooks/onboarding-lock.ts @@ -25,9 +25,8 @@ * user one printed hint, where wrongly proceeding costs a duplicated install. * The command the user typed always runs either way. */ -import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; -import { resolve } from "node:path"; import { onboardingLockFile, stateDir } from "./fp-home"; interface LockBody { diff --git a/src/hooks/session-pause.ts b/src/hooks/session-pause.ts index ace34ddc..c5da82cc 100644 --- a/src/hooks/session-pause.ts +++ b/src/hooks/session-pause.ts @@ -22,7 +22,6 @@ * never ended". */ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "node:fs"; -import { homedir } from "node:os"; import { createHash } from "node:crypto"; import { join, resolve } from "node:path"; import { writeJsonAtomically } from "../../lib/atomic-write"; diff --git a/tsconfig.json b/tsconfig.json index 7d5e9855..bb2ab23d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "allowJs": true, "skipLibCheck": true, "strict": true, + "noUnusedLocals": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", From 67b55ddd29b02133a172b24256dce2fa5764d41c Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Fri, 7 Aug 2026 20:13:01 +0530 Subject: [PATCH 2/3] chore: remove local agent hook configs --- .claude/settings.json | 304 ------------------------------------------ .codex/hooks.json | 76 ----------- 2 files changed, 380 deletions(-) delete mode 100644 .claude/settings.json delete mode 100644 .codex/hooks.json diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 959cd2c5..00000000 --- a/.claude/settings.json +++ /dev/null @@ -1,304 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SessionStart", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SessionEnd", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook UserPromptSubmit", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PreToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PreToolUse", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PermissionRequest": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PermissionRequest", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PermissionDenied": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PermissionDenied", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PostToolUse", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PostToolUseFailure": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PostToolUseFailure", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "Notification": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook Notification", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "SubagentStart": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SubagentStart", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "SubagentStop": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SubagentStop", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "TaskCreated": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook TaskCreated", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "TaskCompleted": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook TaskCompleted", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook Stop", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "StopFailure": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook StopFailure", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "TeammateIdle": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook TeammateIdle", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "InstructionsLoaded": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook InstructionsLoaded", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "ConfigChange": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook ConfigChange", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "CwdChanged": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook CwdChanged", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "FileChanged": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook FileChanged", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "WorktreeRemove": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook WorktreeRemove", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PreCompact": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PreCompact", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PostCompact": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PostCompact", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "Elicitation": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook Elicitation", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "ElicitationResult": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook ElicitationResult", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ] - } -} diff --git a/.codex/hooks.json b/.codex/hooks.json deleted file mode 100644 index 9afb9e63..00000000 --- a/.codex/hooks.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook SessionStart --cli codex", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PreToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook PreToolUse --cli codex", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PermissionRequest": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook PermissionRequest --cli codex", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "PostToolUse": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook PostToolUse --cli codex", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook UserPromptSubmit --cli codex", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node scripts/dev-hook.mjs --hook Stop --cli codex", - "timeout": 60, - "__failproofai_hook__": true - } - ] - } - ] - } -} From 2961565ee56ac65e8141938a8148217a727250fc Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Fri, 7 Aug 2026 20:18:34 +0530 Subject: [PATCH 3/3] Revert "chore: remove local agent hook configs" This reverts commit 67b55ddd29b02133a172b24256dce2fa5764d41c. --- .claude/settings.json | 304 ++++++++++++++++++++++++++++++++++++++++++ .codex/hooks.json | 76 +++++++++++ 2 files changed, 380 insertions(+) create mode 100644 .claude/settings.json create mode 100644 .codex/hooks.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..959cd2c5 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,304 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SessionStart", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SessionEnd", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook UserPromptSubmit", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PreToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PreToolUse", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PermissionRequest": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PermissionRequest", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PermissionDenied": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PermissionDenied", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PostToolUse", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PostToolUseFailure": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PostToolUseFailure", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "Notification": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook Notification", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SubagentStart", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook SubagentStop", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "TaskCreated": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook TaskCreated", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "TaskCompleted": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook TaskCompleted", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook Stop", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "StopFailure": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook StopFailure", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "TeammateIdle": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook TeammateIdle", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "InstructionsLoaded": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook InstructionsLoaded", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "ConfigChange": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook ConfigChange", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "CwdChanged": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook CwdChanged", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "FileChanged": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook FileChanged", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "WorktreeRemove": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook WorktreeRemove", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PreCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PreCompact", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PostCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook PostCompact", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "Elicitation": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook Elicitation", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "ElicitationResult": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node \"$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs\" --hook ElicitationResult", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ] + } +} diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 00000000..9afb9e63 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,76 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook SessionStart --cli codex", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PreToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook PreToolUse --cli codex", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PermissionRequest": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook PermissionRequest --cli codex", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "PostToolUse": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook PostToolUse --cli codex", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 2; }; node scripts/dev-hook.mjs --hook UserPromptSubmit --cli codex", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "command -v node >/dev/null 2>&1 || { echo '[failproofai] node not found; install Node >=20.9' >&2; exit 1; }; node scripts/dev-hook.mjs --hook Stop --cli codex", + "timeout": 60, + "__failproofai_hook__": true + } + ] + } + ] + } +}