@hasna/hooks 0.2.19 → 0.3.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.
Files changed (61) hide show
  1. package/README.md +45 -5
  2. package/bin/index.js +1262 -9569
  3. package/dist/db/index.d.ts +11 -0
  4. package/dist/db/legacy-import.d.ts +11 -0
  5. package/dist/db/migrations/001_initial.d.ts +6 -0
  6. package/dist/db/migrations/002_session_events.d.ts +10 -0
  7. package/dist/db/migrations/003_user_prompt_submit_event.d.ts +10 -0
  8. package/dist/db/migrations/index.d.ts +7 -0
  9. package/dist/db/pg-migrations.d.ts +7 -0
  10. package/dist/db/remote-storage.d.ts +9 -0
  11. package/dist/db/retention.d.ts +8 -0
  12. package/dist/db/schema.d.ts +22 -0
  13. package/dist/db/storage-sync.d.ts +57 -0
  14. package/dist/index.d.ts +72 -0
  15. package/dist/index.js +1042 -58
  16. package/dist/lib/installer.d.ts +56 -0
  17. package/dist/lib/profiles.d.ts +33 -0
  18. package/dist/lib/registry.d.ts +21 -0
  19. package/dist/storage.d.ts +4 -0
  20. package/dist/storage.js +696 -0
  21. package/hooks/codewith-native-common.test.ts +61 -0
  22. package/hooks/codewith-native-common.ts +478 -0
  23. package/hooks/hook-agent-rules-version-check/LICENSE +191 -0
  24. package/hooks/hook-agent-rules-version-check/README.md +50 -0
  25. package/hooks/hook-agent-rules-version-check/package.json +53 -0
  26. package/hooks/hook-agent-rules-version-check/src/hook.test.ts +104 -0
  27. package/hooks/hook-agent-rules-version-check/src/hook.ts +215 -0
  28. package/hooks/hook-agent-rules-version-check/tsconfig.json +25 -0
  29. package/hooks/hook-announce-start/README.md +7 -5
  30. package/hooks/hook-announce-start/package.json +2 -2
  31. package/hooks/hook-announce-start/src/hook.ts +33 -14
  32. package/hooks/hook-fleet-blockers-gate/LICENSE +191 -0
  33. package/hooks/hook-fleet-blockers-gate/README.md +39 -0
  34. package/hooks/hook-fleet-blockers-gate/package.json +52 -0
  35. package/hooks/hook-fleet-blockers-gate/src/hook.test.ts +106 -0
  36. package/hooks/hook-fleet-blockers-gate/src/hook.ts +242 -0
  37. package/hooks/hook-fleet-blockers-gate/tsconfig.json +25 -0
  38. package/hooks/hook-fleet-catchup/LICENSE +191 -0
  39. package/hooks/hook-fleet-catchup/README.md +40 -0
  40. package/hooks/hook-fleet-catchup/package.json +52 -0
  41. package/hooks/hook-fleet-catchup/src/hook.test.ts +156 -0
  42. package/hooks/hook-fleet-catchup/src/hook.ts +283 -0
  43. package/hooks/hook-fleet-catchup/tsconfig.json +25 -0
  44. package/hooks/pre-bash/README.md +17 -0
  45. package/hooks/pre-bash/package.json +12 -0
  46. package/hooks/pre-bash/src/hook.ts +103 -0
  47. package/hooks/prompt-guard/README.md +17 -0
  48. package/hooks/prompt-guard/package.json +12 -0
  49. package/hooks/prompt-guard/src/hook.ts +76 -0
  50. package/hooks/session-start/README.md +17 -0
  51. package/hooks/session-start/package.json +12 -0
  52. package/hooks/session-start/src/hook.ts +150 -0
  53. package/hooks/stop-sync/README.md +17 -0
  54. package/hooks/stop-sync/package.json +12 -0
  55. package/hooks/stop-sync/src/hook.ts +69 -0
  56. package/hooks/worktree-guard/README.md +17 -0
  57. package/hooks/worktree-guard/package.json +12 -0
  58. package/hooks/worktree-guard/src/hook.ts +88 -0
  59. package/package.json +14 -7
  60. package/hooks/hook-affected-tests/dist/hook.js +0 -106
  61. package/hooks/hook-announce-start/dist/hook.js +0 -98
@@ -0,0 +1,11 @@
1
+ /**
2
+ * SQLite DB module for hooks — persistent storage at ~/.hasna/hooks/hooks.db
3
+ *
4
+ * Uses bun:sqlite with WAL mode for concurrent reads.
5
+ * Supports HASNA_HOOKS_DATA_DIR / HOOKS_DATA_DIR and HASNA_HOOKS_DB_PATH / HOOKS_DB_PATH env overrides.
6
+ */
7
+ import { Database } from "bun:sqlite";
8
+ export declare function getDbPath(): string;
9
+ export declare function getDb(): Database;
10
+ export declare function closeDb(): void;
11
+ export declare function createTestDb(): Database;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Legacy flat-file import — runs once on first DB creation.
3
+ *
4
+ * Scans for old .claude/session-log-*.jsonl and .claude/errors.log files
5
+ * in the user's home projects directory and imports their entries into hook_events.
6
+ *
7
+ * Non-blocking: any failure is logged to stderr and skipped.
8
+ * Tracks completion via a `_meta` table row keyed "legacy_import_done".
9
+ */
10
+ import type { Database } from "bun:sqlite";
11
+ export declare function runLegacyImport(db: Database): void;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Migration 001 — initial schema
3
+ * Creates hook_events table and indexes.
4
+ */
5
+ import type { Database } from "bun:sqlite";
6
+ export declare function up(db: Database): void;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Migration 002 — session events
3
+ * Rebuilds hook_events so the event_type CHECK constraint accepts
4
+ * 'SessionStart' and 'SessionEnd'. SQLite cannot ALTER a CHECK
5
+ * constraint, so the table is recreated and rows are copied over.
6
+ * Skips the rebuild when the constraint already includes SessionStart
7
+ * (fresh databases created from the updated schema.ts).
8
+ */
9
+ import type { Database } from "bun:sqlite";
10
+ export declare function up(db: Database): void;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Migration 003 — UserPromptSubmit event.
3
+ *
4
+ * Rebuilds hook_events so the event_type CHECK constraint accepts Codewith's
5
+ * UserPromptSubmit. SQLite cannot ALTER a CHECK constraint, so the table is
6
+ * recreated and rows are copied over. Skips the rebuild when the constraint is
7
+ * already current (fresh databases or databases rebuilt by a newer 002).
8
+ */
9
+ import type { Database } from "bun:sqlite";
10
+ export declare function up(db: Database): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Migration runner — applies pending migrations in order.
3
+ * Tracks applied migrations in a `schema_migrations` table.
4
+ * Migrations are additive-only, never destructive.
5
+ */
6
+ import type { Database } from "bun:sqlite";
7
+ export declare function runMigrations(db: Database): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * PostgreSQL migrations for open-hooks remote storage and sync.
3
+ *
4
+ * Equivalent to the SQLite schema in schema.ts, migrations/, and index.ts,
5
+ * translated for PostgreSQL.
6
+ */
7
+ export declare const PG_MIGRATIONS: string[];
@@ -0,0 +1,9 @@
1
+ export declare class PgAdapterAsync {
2
+ private readonly pool;
3
+ constructor(connectionString: string);
4
+ run(sql: string, ...params: unknown[]): Promise<{
5
+ changes: number;
6
+ }>;
7
+ all(sql: string, ...params: unknown[]): Promise<unknown[]>;
8
+ close(): Promise<void>;
9
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Retention/cleanup — auto-prune hook_events older than N days.
3
+ *
4
+ * Configurable via HOOKS_RETENTION_DAYS env var (default: 30).
5
+ * Called on DB open after migrations.
6
+ */
7
+ import type { Database } from "bun:sqlite";
8
+ export declare function runRetention(db: Database, days?: number): number;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * SQLite schema for @hasna/hooks local data.
3
+ * Owned by this package (no shared runtime dependency).
4
+ */
5
+ import type { Database } from "bun:sqlite";
6
+ export declare const CREATE_HOOK_EVENTS_TABLE = "\n CREATE TABLE IF NOT EXISTS hook_events (\n id TEXT PRIMARY KEY,\n timestamp TEXT NOT NULL,\n session_id TEXT NOT NULL,\n hook_name TEXT NOT NULL,\n event_type TEXT NOT NULL CHECK (event_type IN ('PreToolUse', 'PostToolUse', 'Stop', 'Notification', 'SessionStart', 'SessionEnd', 'UserPromptSubmit')),\n tool_name TEXT,\n tool_input TEXT,\n result TEXT CHECK (result IN ('continue', 'block', NULL)),\n error TEXT,\n duration_ms INTEGER,\n project_dir TEXT,\n metadata TEXT\n )\n";
7
+ export declare const CREATE_INDEXES: string[];
8
+ export interface HookEventRow {
9
+ id: string;
10
+ timestamp: string;
11
+ session_id: string;
12
+ hook_name: string;
13
+ event_type: "PreToolUse" | "PostToolUse" | "Stop" | "Notification" | "SessionStart" | "SessionEnd" | "UserPromptSubmit";
14
+ tool_name: string | null;
15
+ tool_input: string | null;
16
+ result: "continue" | "block" | null;
17
+ error: string | null;
18
+ duration_ms: number | null;
19
+ project_dir: string | null;
20
+ metadata: string | null;
21
+ }
22
+ export declare function applySchema(db: Database): void;
@@ -0,0 +1,57 @@
1
+ import { PgAdapterAsync } from "./remote-storage.js";
2
+ export declare const STORAGE_TABLES: readonly ["hook_events", "schema_migrations", "_meta", "feedback"];
3
+ export declare const HOOKS_STORAGE_TABLES: readonly ["hook_events", "schema_migrations", "_meta", "feedback"];
4
+ type StorageTable = (typeof STORAGE_TABLES)[number];
5
+ export type StorageMode = "local" | "hybrid" | "remote";
6
+ export interface StorageEnv {
7
+ name: string;
8
+ }
9
+ export interface SyncResult {
10
+ table: string;
11
+ rowsRead: number;
12
+ rowsWritten: number;
13
+ errors: string[];
14
+ }
15
+ export interface SyncMeta {
16
+ table_name: string;
17
+ last_synced_at: string | null;
18
+ direction: "push" | "pull";
19
+ }
20
+ export declare const HOOKS_STORAGE_ENV = "HASNA_HOOKS_DATABASE_URL";
21
+ export declare const HOOKS_STORAGE_FALLBACK_ENV = "HOOKS_DATABASE_URL";
22
+ export declare const HOOKS_STORAGE_MODE_ENV = "HASNA_HOOKS_STORAGE_MODE";
23
+ export declare const HOOKS_STORAGE_MODE_FALLBACK_ENV = "HOOKS_STORAGE_MODE";
24
+ export declare const STORAGE_DATABASE_ENV: readonly ["HASNA_HOOKS_DATABASE_URL", "HOOKS_DATABASE_URL"];
25
+ export declare const STORAGE_MODE_ENV: readonly ["HASNA_HOOKS_STORAGE_MODE", "HOOKS_STORAGE_MODE"];
26
+ export interface StorageStatus {
27
+ configured: boolean;
28
+ mode: StorageMode;
29
+ env: typeof STORAGE_DATABASE_ENV;
30
+ activeEnv: string | null;
31
+ service: "hooks";
32
+ tables: typeof STORAGE_TABLES;
33
+ sync: SyncMeta[];
34
+ }
35
+ export declare function getStorageDatabaseEnvName(): (typeof STORAGE_DATABASE_ENV)[number] | null;
36
+ export declare function getStorageDatabaseEnv(): StorageEnv | null;
37
+ export declare function getStorageDatabaseUrl(): string | null;
38
+ export declare function getStorageMode(): StorageMode;
39
+ export declare function getStoragePg(): Promise<PgAdapterAsync>;
40
+ export declare function runStorageMigrations(remote: PgAdapterAsync): Promise<void>;
41
+ export declare function storagePush(options?: {
42
+ tables?: string[];
43
+ }): Promise<SyncResult[]>;
44
+ export declare function storagePull(options?: {
45
+ tables?: string[];
46
+ }): Promise<SyncResult[]>;
47
+ export declare function storageSync(options?: {
48
+ tables?: string[];
49
+ }): Promise<{
50
+ pull: SyncResult[];
51
+ push: SyncResult[];
52
+ }>;
53
+ export declare function getSyncMetaAll(): SyncMeta[];
54
+ export declare function getStorageStatus(): StorageStatus;
55
+ export declare function resolveTables(tables?: string[]): StorageTable[];
56
+ export declare function parseStorageTables(value?: string | string[] | null): StorageTable[] | undefined;
57
+ export {};
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @hasna/hooks - Open source Claude Code hooks library
3
+ *
4
+ * Install hooks with a single command:
5
+ * npx @hasna/hooks install gitguard branchprotect
6
+ *
7
+ * Or use the interactive CLI:
8
+ * npx @hasna/hooks
9
+ */
10
+ export { HOOKS, HOOK_EVENTS, CATEGORIES, getHook, getHooksByCategory, searchHooks, type HookMeta, type HookEvent, type Category, } from "./lib/registry.js";
11
+ import type { HookEvent as HookEventType } from "./lib/registry.js";
12
+ export { installHook, installHooks, getInstalledHooks, getRegisteredHooks, getRegisteredHooksForTarget, removeHook, hookExists, buildCodewithTomlFragment, getHookPath, getSettingsPath, isEventSupported, type InstallResult, type InstallOptions, type Scope, type Target, type CodewithInstallMode, } from "./lib/installer.js";
13
+ export interface HookAgentInfo {
14
+ agent_id: string;
15
+ agent_type: "claude" | "gemini" | "codewith" | "custom";
16
+ name?: string;
17
+ preferences?: Record<string, unknown>;
18
+ }
19
+ /** The JSON object passed to a hook via stdin */
20
+ export interface HookInput {
21
+ session_id?: string;
22
+ cwd?: string;
23
+ hook_event_name?: HookEventType | string;
24
+ prompt?: string;
25
+ tool_name?: string;
26
+ tool_input?: Record<string, unknown>;
27
+ agent?: HookAgentInfo;
28
+ [key: string]: unknown;
29
+ }
30
+ /** The JSON object a PreToolUse hook returns via stdout */
31
+ export interface HookOutput {
32
+ decision?: "approve" | "block";
33
+ reason?: string;
34
+ continue?: boolean;
35
+ hookSpecificOutput?: {
36
+ hookEventName: "SessionStart" | "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "Stop" | "Notification" | "SessionEnd";
37
+ additionalContext?: string;
38
+ permissionDecision?: "allow" | "deny" | "ask";
39
+ permissionDecisionReason?: string;
40
+ updatedInput?: unknown;
41
+ };
42
+ [key: string]: unknown;
43
+ }
44
+ import type { InstallOptions, InstallResult } from "./lib/installer.js";
45
+ /** Install a hook scoped to the current project (.claude/settings.json) */
46
+ export declare function installHookForProject(name: string, options?: Omit<InstallOptions, "scope">): InstallResult;
47
+ /** Install multiple hooks scoped to the current project */
48
+ export declare function installHooksForProject(names: string[], options?: Omit<InstallOptions, "scope">): InstallResult[];
49
+ /** List all hooks registered for the current project */
50
+ export declare function listProjectHooks(): string[];
51
+ /** Remove a hook from the current project */
52
+ export declare function removeProjectHook(name: string): boolean;
53
+ export interface RunHookOptions {
54
+ /** Agent profile ID to inject into hook input */
55
+ profile?: string;
56
+ /** Timeout in milliseconds (default: 10000) */
57
+ timeout?: number;
58
+ }
59
+ export interface RunHookResult {
60
+ output: HookOutput;
61
+ stderr: string;
62
+ exitCode: number;
63
+ }
64
+ /**
65
+ * Programmatically execute a hook with the given input.
66
+ * Spawns the hook's src/hook.ts via bun, passes input as stdin JSON,
67
+ * and returns the parsed stdout JSON.
68
+ */
69
+ export declare function runHook(name: string, input: HookInput, options?: RunHookOptions): Promise<RunHookResult>;
70
+ export { createProfile, getProfile, listProfiles, updateProfile, deleteProfile, touchProfile, getProfilesDir, exportProfiles, importProfiles, type AgentProfile, type CreateProfileInput, } from "./lib/profiles.js";
71
+ export { HOOKS_STORAGE_ENV, HOOKS_STORAGE_FALLBACK_ENV, HOOKS_STORAGE_MODE_ENV, HOOKS_STORAGE_MODE_FALLBACK_ENV, HOOKS_STORAGE_TABLES, STORAGE_DATABASE_ENV, STORAGE_MODE_ENV, STORAGE_TABLES, getStorageDatabaseEnv, getStorageDatabaseEnvName, getStorageDatabaseUrl, getStorageMode, getStoragePg, getStorageStatus, getSyncMetaAll, parseStorageTables, resolveTables, runStorageMigrations, storagePull, storagePush, storageSync, } from "./storage.js";
72
+ export type { StorageEnv, StorageMode, StorageStatus, SyncMeta, SyncResult } from "./storage.js";