@nocville/npc 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -2
- package/dist/cjs/cli.js +584 -0
- package/dist/cjs/core/api-key.js +69 -0
- package/dist/cjs/core/base-npc.js +160 -0
- package/dist/cjs/core/config.js +108 -0
- package/dist/cjs/core/event-npc.js +14 -0
- package/dist/cjs/core/logger.js +39 -0
- package/dist/cjs/core/polling-npc.js +65 -0
- package/dist/cjs/core/screen.js +28 -0
- package/dist/cjs/core/session-filter.js +150 -0
- package/dist/cjs/core/text.js +15 -0
- package/dist/cjs/core/thresholds.js +48 -0
- package/dist/cjs/core/types.js +10 -0
- package/dist/cjs/core/user-config.js +92 -0
- package/dist/cjs/doctor.js +130 -0
- package/dist/cjs/index.js +95 -0
- package/dist/cjs/npcs/agent-common/activity.js +291 -0
- package/dist/cjs/npcs/agent-common/git-stats.js +71 -0
- package/dist/cjs/npcs/agent-common/hook-runner.js +252 -0
- package/dist/cjs/npcs/agent-common/hook-settings.js +279 -0
- package/dist/cjs/npcs/agent-common/liveness.js +112 -0
- package/dist/cjs/npcs/agent-common/lock.js +99 -0
- package/dist/cjs/npcs/agent-common/runtime.js +154 -0
- package/dist/cjs/npcs/agent-common/session.js +412 -0
- package/dist/cjs/npcs/agent-common/shim.js +96 -0
- package/dist/cjs/npcs/agent-common/watchdog.js +149 -0
- package/dist/cjs/npcs/claude-code/defaults.js +20 -0
- package/dist/cjs/npcs/claude-code/hook-handler.js +139 -0
- package/dist/cjs/npcs/claude-code/index.js +104 -0
- package/dist/cjs/npcs/claude-code/setup.js +133 -0
- package/dist/cjs/npcs/claude-code/state-machine.js +130 -0
- package/dist/cjs/npcs/codex/defaults.js +32 -0
- package/dist/cjs/npcs/codex/hook-handler.js +116 -0
- package/dist/cjs/npcs/codex/index.js +92 -0
- package/dist/cjs/npcs/codex/rollout.js +246 -0
- package/dist/cjs/npcs/codex/setup.js +104 -0
- package/dist/cjs/npcs/codex/watcher.js +361 -0
- package/dist/cjs/npcs/cursor/defaults.js +15 -0
- package/dist/cjs/npcs/cursor/hook-handler.js +65 -0
- package/dist/cjs/npcs/cursor/index.js +72 -0
- package/dist/cjs/npcs/cursor/normalize.js +54 -0
- package/dist/cjs/npcs/cursor/setup.js +98 -0
- package/dist/cjs/npcs/gemini/defaults.js +15 -0
- package/dist/cjs/npcs/gemini/hook-handler.js +93 -0
- package/dist/cjs/npcs/gemini/index.js +69 -0
- package/dist/cjs/npcs/gemini/setup.js +98 -0
- package/dist/cjs/npcs/github-watcher/defaults.js +11 -0
- package/dist/cjs/npcs/github-watcher/format.js +21 -0
- package/dist/cjs/npcs/github-watcher/github-api.js +126 -0
- package/dist/cjs/npcs/github-watcher/index.js +148 -0
- package/dist/cjs/npcs/internet-connectivity/checks.js +92 -0
- package/dist/cjs/npcs/internet-connectivity/defaults.js +22 -0
- package/dist/cjs/npcs/internet-connectivity/index.js +251 -0
- package/dist/cjs/npcs/log-watcher/buffer.js +40 -0
- package/dist/cjs/npcs/log-watcher/defaults.js +12 -0
- package/dist/cjs/npcs/log-watcher/index.js +161 -0
- package/dist/cjs/npcs/log-watcher/tail.js +81 -0
- package/dist/cjs/npcs/process-watcher/defaults.js +9 -0
- package/dist/cjs/npcs/process-watcher/index.js +140 -0
- package/dist/cjs/npcs/process-watcher/process-scanner.js +114 -0
- package/dist/cjs/npcs/receptionist/defaults.js +9 -0
- package/dist/cjs/npcs/receptionist/index.js +120 -0
- package/dist/cjs/npcs/receptionist/weather.js +129 -0
- package/dist/cjs/npcs/system-monitor/collectors.js +68 -0
- package/dist/cjs/npcs/system-monitor/defaults.js +13 -0
- package/dist/cjs/npcs/system-monitor/index.js +105 -0
- package/dist/cjs/npcs/web-page-watcher/basic-mode.js +58 -0
- package/dist/cjs/npcs/web-page-watcher/capture.js +80 -0
- package/dist/cjs/npcs/web-page-watcher/defaults.js +31 -0
- package/dist/cjs/npcs/web-page-watcher/index.js +234 -0
- package/dist/cjs/npcs/web-page-watcher/pixelate.js +181 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +549 -0
- package/dist/core/api-key.d.ts +34 -0
- package/dist/core/api-key.js +64 -0
- package/dist/core/base-npc.d.ts +41 -0
- package/dist/core/base-npc.js +156 -0
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.js +101 -0
- package/dist/core/event-npc.d.ts +8 -0
- package/dist/core/event-npc.js +10 -0
- package/dist/core/logger.d.ts +11 -0
- package/dist/core/logger.js +35 -0
- package/dist/core/polling-npc.d.ts +18 -0
- package/dist/core/polling-npc.js +61 -0
- package/dist/core/screen.d.ts +11 -0
- package/dist/core/screen.js +24 -0
- package/dist/core/session-filter.d.ts +59 -0
- package/dist/core/session-filter.js +143 -0
- package/dist/core/text.d.ts +6 -0
- package/dist/core/text.js +12 -0
- package/dist/core/thresholds.d.ts +10 -0
- package/dist/core/thresholds.js +44 -0
- package/dist/core/types.d.ts +60 -0
- package/dist/core/types.js +7 -0
- package/dist/core/user-config.d.ts +43 -0
- package/dist/core/user-config.js +85 -0
- package/dist/doctor.d.ts +8 -0
- package/dist/doctor.js +126 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +26 -0
- package/dist/npcs/agent-common/activity.d.ts +42 -0
- package/dist/npcs/agent-common/activity.js +280 -0
- package/dist/npcs/agent-common/git-stats.d.ts +11 -0
- package/dist/npcs/agent-common/git-stats.js +67 -0
- package/dist/npcs/agent-common/hook-runner.d.ts +62 -0
- package/dist/npcs/agent-common/hook-runner.js +245 -0
- package/dist/npcs/agent-common/hook-settings.d.ts +75 -0
- package/dist/npcs/agent-common/hook-settings.js +265 -0
- package/dist/npcs/agent-common/liveness.d.ts +12 -0
- package/dist/npcs/agent-common/liveness.js +105 -0
- package/dist/npcs/agent-common/lock.d.ts +6 -0
- package/dist/npcs/agent-common/lock.js +95 -0
- package/dist/npcs/agent-common/runtime.d.ts +16 -0
- package/dist/npcs/agent-common/runtime.js +149 -0
- package/dist/npcs/agent-common/session.d.ts +192 -0
- package/dist/npcs/agent-common/session.js +390 -0
- package/dist/npcs/agent-common/shim.d.ts +4 -0
- package/dist/npcs/agent-common/shim.js +90 -0
- package/dist/npcs/agent-common/watchdog.d.ts +16 -0
- package/dist/npcs/agent-common/watchdog.js +144 -0
- package/dist/npcs/claude-code/defaults.d.ts +15 -0
- package/dist/npcs/claude-code/defaults.js +17 -0
- package/dist/npcs/claude-code/hook-handler.d.ts +40 -0
- package/dist/npcs/claude-code/hook-handler.js +131 -0
- package/dist/npcs/claude-code/index.d.ts +36 -0
- package/dist/npcs/claude-code/index.js +92 -0
- package/dist/npcs/claude-code/setup.d.ts +58 -0
- package/dist/npcs/claude-code/setup.js +121 -0
- package/dist/npcs/claude-code/state-machine.d.ts +28 -0
- package/dist/npcs/claude-code/state-machine.js +125 -0
- package/dist/npcs/codex/defaults.d.ts +24 -0
- package/dist/npcs/codex/defaults.js +28 -0
- package/dist/npcs/codex/hook-handler.d.ts +24 -0
- package/dist/npcs/codex/hook-handler.js +110 -0
- package/dist/npcs/codex/index.d.ts +21 -0
- package/dist/npcs/codex/index.js +76 -0
- package/dist/npcs/codex/rollout.d.ts +39 -0
- package/dist/npcs/codex/rollout.js +242 -0
- package/dist/npcs/codex/setup.d.ts +37 -0
- package/dist/npcs/codex/setup.js +96 -0
- package/dist/npcs/codex/watcher.d.ts +65 -0
- package/dist/npcs/codex/watcher.js +356 -0
- package/dist/npcs/cursor/defaults.d.ts +9 -0
- package/dist/npcs/cursor/defaults.js +11 -0
- package/dist/npcs/cursor/hook-handler.d.ts +8 -0
- package/dist/npcs/cursor/hook-handler.js +60 -0
- package/dist/npcs/cursor/index.d.ts +18 -0
- package/dist/npcs/cursor/index.js +57 -0
- package/dist/npcs/cursor/normalize.d.ts +6 -0
- package/dist/npcs/cursor/normalize.js +49 -0
- package/dist/npcs/cursor/setup.d.ts +29 -0
- package/dist/npcs/cursor/setup.js +90 -0
- package/dist/npcs/gemini/defaults.d.ts +9 -0
- package/dist/npcs/gemini/defaults.js +11 -0
- package/dist/npcs/gemini/hook-handler.d.ts +10 -0
- package/dist/npcs/gemini/hook-handler.js +87 -0
- package/dist/npcs/gemini/index.d.ts +17 -0
- package/dist/npcs/gemini/index.js +56 -0
- package/dist/npcs/gemini/setup.d.ts +29 -0
- package/dist/npcs/gemini/setup.js +90 -0
- package/dist/npcs/github-watcher/defaults.d.ts +8 -0
- package/dist/npcs/github-watcher/defaults.js +8 -0
- package/dist/npcs/github-watcher/format.d.ts +3 -0
- package/dist/npcs/github-watcher/format.js +18 -0
- package/dist/npcs/github-watcher/github-api.d.ts +55 -0
- package/dist/npcs/github-watcher/github-api.js +120 -0
- package/dist/npcs/github-watcher/index.d.ts +35 -0
- package/dist/npcs/github-watcher/index.js +144 -0
- package/dist/npcs/internet-connectivity/checks.d.ts +24 -0
- package/dist/npcs/internet-connectivity/checks.js +84 -0
- package/dist/npcs/internet-connectivity/defaults.d.ts +20 -0
- package/dist/npcs/internet-connectivity/defaults.js +19 -0
- package/dist/npcs/internet-connectivity/index.d.ts +55 -0
- package/dist/npcs/internet-connectivity/index.js +246 -0
- package/dist/npcs/log-watcher/buffer.d.ts +18 -0
- package/dist/npcs/log-watcher/buffer.js +34 -0
- package/dist/npcs/log-watcher/defaults.d.ts +9 -0
- package/dist/npcs/log-watcher/defaults.js +9 -0
- package/dist/npcs/log-watcher/index.d.ts +45 -0
- package/dist/npcs/log-watcher/index.js +157 -0
- package/dist/npcs/log-watcher/tail.d.ts +9 -0
- package/dist/npcs/log-watcher/tail.js +78 -0
- package/dist/npcs/process-watcher/defaults.d.ts +6 -0
- package/dist/npcs/process-watcher/defaults.js +6 -0
- package/dist/npcs/process-watcher/index.d.ts +26 -0
- package/dist/npcs/process-watcher/index.js +136 -0
- package/dist/npcs/process-watcher/process-scanner.d.ts +19 -0
- package/dist/npcs/process-watcher/process-scanner.js +110 -0
- package/dist/npcs/receptionist/defaults.d.ts +6 -0
- package/dist/npcs/receptionist/defaults.js +6 -0
- package/dist/npcs/receptionist/index.d.ts +29 -0
- package/dist/npcs/receptionist/index.js +116 -0
- package/dist/npcs/receptionist/weather.d.ts +37 -0
- package/dist/npcs/receptionist/weather.js +121 -0
- package/dist/npcs/system-monitor/collectors.d.ts +45 -0
- package/dist/npcs/system-monitor/collectors.js +60 -0
- package/dist/npcs/system-monitor/defaults.d.ts +7 -0
- package/dist/npcs/system-monitor/defaults.js +10 -0
- package/dist/npcs/system-monitor/index.d.ts +27 -0
- package/dist/npcs/system-monitor/index.js +101 -0
- package/dist/npcs/web-page-watcher/basic-mode.d.ts +16 -0
- package/dist/npcs/web-page-watcher/basic-mode.js +52 -0
- package/dist/npcs/web-page-watcher/capture.d.ts +49 -0
- package/dist/npcs/web-page-watcher/capture.js +75 -0
- package/dist/npcs/web-page-watcher/defaults.d.ts +19 -0
- package/dist/npcs/web-page-watcher/defaults.js +27 -0
- package/dist/npcs/web-page-watcher/index.d.ts +42 -0
- package/dist/npcs/web-page-watcher/index.js +230 -0
- package/dist/npcs/web-page-watcher/pixelate.d.ts +36 -0
- package/dist/npcs/web-page-watcher/pixelate.js +172 -0
- package/package.json +73 -3
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { NpcConfig } from '../../core/types.js';
|
|
2
|
+
import type { AgentHookSpec, HookRunOptions } from '../agent-common/hook-runner.js';
|
|
3
|
+
import type { AgentAdapter } from '../agent-common/session.js';
|
|
4
|
+
import type { HookEvent } from './state-machine.js';
|
|
5
|
+
export declare const CLAUDE_ADAPTER: AgentAdapter;
|
|
6
|
+
/**
|
|
7
|
+
* Derive a serviceId from the session and project directory.
|
|
8
|
+
* Format: "claude-{project_name}-{session_id_prefix}" (≤100 chars).
|
|
9
|
+
*/
|
|
10
|
+
export declare function deriveServiceId(sessionId: string, cwd: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Humanize a project dir name into a banner label: split on punctuation and
|
|
13
|
+
* title-case each word ("gridbreak" -> "Gridbreak", "my-cool-app" -> "My Cool App").
|
|
14
|
+
*/
|
|
15
|
+
export declare function humanizeProjectName(cwd: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Correlate a session_id to its user-facing name via the local session store.
|
|
18
|
+
* Claude Code writes ~/.claude/sessions/<pid>.json — keyed by pid, each holding
|
|
19
|
+
* `{ sessionId, name, ... }` — so we scan for the file whose sessionId matches.
|
|
20
|
+
* `dir` is injectable for testing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function lookupSessionNameFromStore(sessionId: string, dir?: string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the user-facing session name. Checked fresh only on the cheap,
|
|
25
|
+
* low-frequency events (SessionStart carries `session_title`; UserPromptSubmit
|
|
26
|
+
* falls back to the store scan) so renames propagate; otherwise the cached
|
|
27
|
+
* value is reused. Returns null when unknown (banner falls back to the project).
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveSessionName(event: HookEvent, cached: string | null, dir?: string): string | null;
|
|
30
|
+
export declare const CLAUDE_HOOK_SPEC: AgentHookSpec;
|
|
31
|
+
/**
|
|
32
|
+
* Handle a single Claude Code hook event.
|
|
33
|
+
*
|
|
34
|
+
* Reads the JSON payload (from `rawInput` if provided, else stdin), updates the
|
|
35
|
+
* per-session state file, and pushes an NPC status update (banner
|
|
36
|
+
* "Claude · <session name or project>"). Defaults to an in-process push; the CLI
|
|
37
|
+
* passes `mode: 'detach'` so the push runs in a background process. Never throws.
|
|
38
|
+
*/
|
|
39
|
+
export declare function handleHookEvent(config: NpcConfig, rawInput?: string, opts?: HookRunOptions): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=hook-handler.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { runAgentHook, readStdin } from '../agent-common/hook-runner.js';
|
|
5
|
+
import { projectNameOf, sanitizeServiceId } from '../agent-common/session.js';
|
|
6
|
+
import { isCursorInvocation } from '../cursor/normalize.js';
|
|
7
|
+
import { handleCursorHookEvent } from '../cursor/hook-handler.js';
|
|
8
|
+
import { DEFAULTS } from './defaults.js';
|
|
9
|
+
import { claudeTransition, eventName } from './state-machine.js';
|
|
10
|
+
export const CLAUDE_ADAPTER = {
|
|
11
|
+
command: 'claude-code',
|
|
12
|
+
label: 'Claude',
|
|
13
|
+
family: 'claude',
|
|
14
|
+
stateDir: DEFAULTS.stateDir,
|
|
15
|
+
heartbeatSeconds: DEFAULTS.heartbeatSeconds,
|
|
16
|
+
processNames: ['claude'],
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Derive a serviceId from the session and project directory.
|
|
20
|
+
* Format: "claude-{project_name}-{session_id_prefix}" (≤100 chars).
|
|
21
|
+
*/
|
|
22
|
+
export function deriveServiceId(sessionId, cwd) {
|
|
23
|
+
return sanitizeServiceId(`claude-${projectNameOf(cwd)}-${sessionId.slice(0, 8)}`);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Humanize a project dir name into a banner label: split on punctuation and
|
|
27
|
+
* title-case each word ("gridbreak" -> "Gridbreak", "my-cool-app" -> "My Cool App").
|
|
28
|
+
*/
|
|
29
|
+
export function humanizeProjectName(cwd) {
|
|
30
|
+
const raw = projectNameOf(cwd);
|
|
31
|
+
const words = raw
|
|
32
|
+
.replace(/[^a-zA-Z0-9]+/g, ' ')
|
|
33
|
+
.trim()
|
|
34
|
+
.split(/\s+/)
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
if (words.length === 0)
|
|
37
|
+
return raw;
|
|
38
|
+
return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
39
|
+
}
|
|
40
|
+
/** Default location of Claude Code's per-session sidecar files. */
|
|
41
|
+
function sessionsStoreDir() {
|
|
42
|
+
return join(homedir(), '.claude', 'sessions');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Correlate a session_id to its user-facing name via the local session store.
|
|
46
|
+
* Claude Code writes ~/.claude/sessions/<pid>.json — keyed by pid, each holding
|
|
47
|
+
* `{ sessionId, name, ... }` — so we scan for the file whose sessionId matches.
|
|
48
|
+
* `dir` is injectable for testing.
|
|
49
|
+
*/
|
|
50
|
+
export function lookupSessionNameFromStore(sessionId, dir = sessionsStoreDir()) {
|
|
51
|
+
if (!sessionId)
|
|
52
|
+
return null;
|
|
53
|
+
let files;
|
|
54
|
+
try {
|
|
55
|
+
files = readdirSync(dir);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
if (!file.endsWith('.json'))
|
|
62
|
+
continue;
|
|
63
|
+
try {
|
|
64
|
+
const obj = JSON.parse(readFileSync(join(dir, file), 'utf-8'));
|
|
65
|
+
if (obj.sessionId === sessionId && typeof obj.name === 'string' && obj.name.trim()) {
|
|
66
|
+
return obj.name.trim();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Skip unreadable / partially-written session files.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the user-facing session name. Checked fresh only on the cheap,
|
|
77
|
+
* low-frequency events (SessionStart carries `session_title`; UserPromptSubmit
|
|
78
|
+
* falls back to the store scan) so renames propagate; otherwise the cached
|
|
79
|
+
* value is reused. Returns null when unknown (banner falls back to the project).
|
|
80
|
+
*/
|
|
81
|
+
export function resolveSessionName(event, cached, dir) {
|
|
82
|
+
const name = eventName(event);
|
|
83
|
+
const shouldRefresh = name === 'SessionStart' || name === 'UserPromptSubmit' || !cached;
|
|
84
|
+
if (!shouldRefresh)
|
|
85
|
+
return cached;
|
|
86
|
+
const fromPayload = typeof event.session_title === 'string' ? event.session_title.trim() : '';
|
|
87
|
+
if (fromPayload)
|
|
88
|
+
return fromPayload;
|
|
89
|
+
return lookupSessionNameFromStore(event.session_id ?? '', dir) ?? cached;
|
|
90
|
+
}
|
|
91
|
+
export const CLAUDE_HOOK_SPEC = {
|
|
92
|
+
adapter: CLAUDE_ADAPTER,
|
|
93
|
+
serviceIdFor: deriveServiceId,
|
|
94
|
+
transition: (event, prev) => claudeTransition(event, prev),
|
|
95
|
+
resolveName: (event, prev) => {
|
|
96
|
+
// Only scan the store on the low-frequency events; never on PreToolUse.
|
|
97
|
+
const name = eventName(event);
|
|
98
|
+
if (prev && name !== 'SessionStart' && name !== 'UserPromptSubmit')
|
|
99
|
+
return undefined;
|
|
100
|
+
return resolveSessionName(event, prev?.name ?? null) ?? undefined;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Handle a single Claude Code hook event.
|
|
105
|
+
*
|
|
106
|
+
* Reads the JSON payload (from `rawInput` if provided, else stdin), updates the
|
|
107
|
+
* per-session state file, and pushes an NPC status update (banner
|
|
108
|
+
* "Claude · <session name or project>"). Defaults to an in-process push; the CLI
|
|
109
|
+
* passes `mode: 'detach'` so the push runs in a background process. Never throws.
|
|
110
|
+
*/
|
|
111
|
+
export async function handleHookEvent(config, rawInput, opts = {}) {
|
|
112
|
+
const input = rawInput ?? readStdin();
|
|
113
|
+
let event;
|
|
114
|
+
try {
|
|
115
|
+
event = JSON.parse(input);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!event || typeof event !== 'object' || Array.isArray(event))
|
|
121
|
+
return;
|
|
122
|
+
if (isCursorInvocation(event)) {
|
|
123
|
+
await handleCursorHookEvent(config, event, opts, 'third-party');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await runAgentHook(CLAUDE_HOOK_SPEC, config, event, {
|
|
127
|
+
minPushGapMs: DEFAULTS.minPushGapMs,
|
|
128
|
+
...opts,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=hook-handler.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { NpcConfig } from '../../core/types.js';
|
|
2
|
+
export { DEFAULTS } from './defaults.js';
|
|
3
|
+
export { processHookEvent, claudeTransition } from './state-machine.js';
|
|
4
|
+
export type { SessionState, HookEvent, StatusOutput, SessionMachineState, } from './state-machine.js';
|
|
5
|
+
export { handleHookEvent, CLAUDE_ADAPTER } from './hook-handler.js';
|
|
6
|
+
export { setupHooks, uninstallHooks } from './setup.js';
|
|
7
|
+
export type { SetupOptions } from './setup.js';
|
|
8
|
+
export interface ClaudeCodeSubcommandOptions {
|
|
9
|
+
subcommand: string;
|
|
10
|
+
config: NpcConfig;
|
|
11
|
+
flags: Record<string, string | boolean>;
|
|
12
|
+
/** Positional args after the subcommand (e.g. the session id for `flush`). */
|
|
13
|
+
args?: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* claude-code subcommands: setup | uninstall | hook | flush | cleanup.
|
|
17
|
+
* `hook` and `flush` are invoked by Claude Code / ourselves and must stay
|
|
18
|
+
* silent on stdout and never exit non-zero.
|
|
19
|
+
*/
|
|
20
|
+
export declare function handleClaudeCodeCommand(opts: ClaudeCodeSubcommandOptions): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* ClaudeCodeNPC class for programmatic use.
|
|
23
|
+
*/
|
|
24
|
+
export declare class ClaudeCodeNPC {
|
|
25
|
+
private config;
|
|
26
|
+
constructor(config: NpcConfig);
|
|
27
|
+
/** Process a hook event from stdin (in-process push). */
|
|
28
|
+
handleHook(): Promise<void>;
|
|
29
|
+
/** Install hooks into Claude Code settings. */
|
|
30
|
+
setup(): void;
|
|
31
|
+
/** Remove all Nocville hooks from Claude Code settings. */
|
|
32
|
+
uninstall(): void;
|
|
33
|
+
/** Clean up stale session state files. */
|
|
34
|
+
cleanup(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { numberFlag, stringFlag } from '../../core/config.js';
|
|
2
|
+
import { readStdin, runFlushCommand } from '../agent-common/hook-runner.js';
|
|
3
|
+
import { runWatchdog } from '../agent-common/watchdog.js';
|
|
4
|
+
import { cleanupSessionStates } from '../agent-common/session.js';
|
|
5
|
+
import { DEFAULTS } from './defaults.js';
|
|
6
|
+
import { CLAUDE_ADAPTER, handleHookEvent } from './hook-handler.js';
|
|
7
|
+
import { setupHooks, uninstallHooks } from './setup.js';
|
|
8
|
+
export { DEFAULTS } from './defaults.js';
|
|
9
|
+
export { processHookEvent, claudeTransition } from './state-machine.js';
|
|
10
|
+
export { handleHookEvent, CLAUDE_ADAPTER } from './hook-handler.js';
|
|
11
|
+
export { setupHooks, uninstallHooks } from './setup.js';
|
|
12
|
+
/** Remove stale session state files. */
|
|
13
|
+
async function cleanupStateFiles() {
|
|
14
|
+
const removed = await cleanupSessionStates(DEFAULTS.stateDir, DEFAULTS.stateFileMaxAge);
|
|
15
|
+
console.log(`Cleanup complete: ${removed} stale file(s) removed`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* claude-code subcommands: setup | uninstall | hook | flush | cleanup.
|
|
19
|
+
* `hook` and `flush` are invoked by Claude Code / ourselves and must stay
|
|
20
|
+
* silent on stdout and never exit non-zero.
|
|
21
|
+
*/
|
|
22
|
+
export async function handleClaudeCodeCommand(opts) {
|
|
23
|
+
const { subcommand, config, flags } = opts;
|
|
24
|
+
switch (subcommand) {
|
|
25
|
+
case 'setup': {
|
|
26
|
+
if (flags['uninstall']) {
|
|
27
|
+
uninstallHooks();
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
const ok = setupHooks({
|
|
31
|
+
apiUrl: stringFlag(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
|
|
32
|
+
apiKey: stringFlag(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
|
|
33
|
+
map: stringFlag(flags, 'map'),
|
|
34
|
+
cliPath: process.argv[1],
|
|
35
|
+
debug: config.debug,
|
|
36
|
+
});
|
|
37
|
+
if (!ok)
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case 'uninstall':
|
|
42
|
+
uninstallHooks();
|
|
43
|
+
break;
|
|
44
|
+
case 'hook':
|
|
45
|
+
await handleHookEvent(config, readStdin(), { mode: 'detach' });
|
|
46
|
+
break;
|
|
47
|
+
case 'watchdog':
|
|
48
|
+
await runWatchdog(CLAUDE_ADAPTER, config, opts.args?.[0]);
|
|
49
|
+
break;
|
|
50
|
+
case 'flush':
|
|
51
|
+
await runFlushCommand(CLAUDE_ADAPTER, config, opts.args?.[0], numberFlag(flags, 'delay'), flags['stats'] === true);
|
|
52
|
+
break;
|
|
53
|
+
case 'cleanup':
|
|
54
|
+
await cleanupStateFiles();
|
|
55
|
+
break;
|
|
56
|
+
default: {
|
|
57
|
+
console.error(`Unknown claude-code subcommand: ${subcommand}`);
|
|
58
|
+
console.error('Available subcommands: setup, uninstall, hook, cleanup');
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* ClaudeCodeNPC class for programmatic use.
|
|
65
|
+
*/
|
|
66
|
+
export class ClaudeCodeNPC {
|
|
67
|
+
config;
|
|
68
|
+
constructor(config) {
|
|
69
|
+
this.config = config;
|
|
70
|
+
}
|
|
71
|
+
/** Process a hook event from stdin (in-process push). */
|
|
72
|
+
async handleHook() {
|
|
73
|
+
await handleHookEvent(this.config);
|
|
74
|
+
}
|
|
75
|
+
/** Install hooks into Claude Code settings. */
|
|
76
|
+
setup() {
|
|
77
|
+
setupHooks({
|
|
78
|
+
apiUrl: this.config.apiUrl,
|
|
79
|
+
apiKey: this.config.apiKey,
|
|
80
|
+
debug: this.config.debug,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** Remove all Nocville hooks from Claude Code settings. */
|
|
84
|
+
uninstall() {
|
|
85
|
+
uninstallHooks();
|
|
86
|
+
}
|
|
87
|
+
/** Clean up stale session state files. */
|
|
88
|
+
async cleanup() {
|
|
89
|
+
await cleanupStateFiles();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { HooksFile } from '../agent-common/hook-settings.js';
|
|
2
|
+
export { extractFlag } from '../agent-common/hook-settings.js';
|
|
3
|
+
export declare function claudeSettingsPath(): string;
|
|
4
|
+
/**
|
|
5
|
+
* Hook events to install. Verified against code.claude.com/docs/en/hooks —
|
|
6
|
+
* all exist; events without matcher support ignore the empty matcher.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CLAUDE_HOOK_EVENTS: readonly ["SessionStart", "SessionEnd", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PostToolUseFailure", "PermissionRequest", "Notification", "Stop", "SubagentStart", "SubagentStop", "PreCompact"];
|
|
9
|
+
export interface SetupOptions {
|
|
10
|
+
/** Explicit --api-url (or env). Saved to the user config. */
|
|
11
|
+
apiUrl?: string;
|
|
12
|
+
/** Explicit --api-key (or env). Saved to the user config. */
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
/** Explicit --map. Saved to the user config. */
|
|
15
|
+
map?: string;
|
|
16
|
+
/** Absolute path to the built cli.js; defaults to the running script. */
|
|
17
|
+
cliPath?: string;
|
|
18
|
+
/** Override ~/.claude/settings.json (tests). */
|
|
19
|
+
settingsPath?: string;
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
/** Suppress console output (tests). */
|
|
22
|
+
quiet?: boolean;
|
|
23
|
+
}
|
|
24
|
+
type ClaudeSettings = HooksFile;
|
|
25
|
+
/** Install the nocville group into every Claude Code event. Mutates `settings`. */
|
|
26
|
+
export declare function installHooksInto(settings: ClaudeSettings, command: string, opts?: {
|
|
27
|
+
timeout?: number;
|
|
28
|
+
platform?: NodeJS.Platform;
|
|
29
|
+
}): {
|
|
30
|
+
migrated: number;
|
|
31
|
+
installed: number;
|
|
32
|
+
};
|
|
33
|
+
/** Remove all nocville entries (nested or legacy flat). Mutates `settings`. */
|
|
34
|
+
export declare function removeHooksFrom(settings: ClaudeSettings): number;
|
|
35
|
+
export { findNocvilleCommand as findExistingNocvilleCommand } from '../agent-common/hook-settings.js';
|
|
36
|
+
/** `'<node>' '<cli.js>' claude-code hook # nocville-npc-hook` — no key on the command line. */
|
|
37
|
+
export declare function buildHookCommand(_cliPath: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Credentials embedded in a legacy hook command (`--api-url=… --api-key=…`),
|
|
40
|
+
* so upgrading setup can move them into the user config.
|
|
41
|
+
*/
|
|
42
|
+
export declare function legacyCredentials(settings: ClaudeSettings): {
|
|
43
|
+
apiUrl?: string;
|
|
44
|
+
apiKey?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Install Nocville hooks into Claude Code settings (matcher-group format,
|
|
48
|
+
* absolute node/cli paths, 10s timeout safety net). Idempotent: migrates and
|
|
49
|
+
* de-duplicates legacy entries and moves any legacy inline credentials into
|
|
50
|
+
* ~/.config/nocville/config.json. Returns false if setup could not complete.
|
|
51
|
+
*/
|
|
52
|
+
export declare function setupHooks(opts: SetupOptions): boolean;
|
|
53
|
+
/** Remove all Nocville hooks from Claude Code settings. */
|
|
54
|
+
export declare function uninstallHooks(opts?: {
|
|
55
|
+
settingsPath?: string;
|
|
56
|
+
quiet?: boolean;
|
|
57
|
+
} | boolean): number;
|
|
58
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { maskKey, persistCredentials } from '../../core/user-config.js';
|
|
5
|
+
import { buildShimHookCommand, stableNodePath, extractFlag, findNocvilleCommand, installHookGroups, readJsonFile, removeHookGroups, resolveCliPath, writeJsonFile, } from '../agent-common/hook-settings.js';
|
|
6
|
+
import { installHookShim } from '../agent-common/shim.js';
|
|
7
|
+
import { ensureStableCliPath } from '../agent-common/runtime.js';
|
|
8
|
+
import { DEFAULTS } from './defaults.js';
|
|
9
|
+
export { extractFlag } from '../agent-common/hook-settings.js';
|
|
10
|
+
export function claudeSettingsPath() {
|
|
11
|
+
return join(homedir(), '.claude', 'settings.json');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Hook events to install. Verified against code.claude.com/docs/en/hooks —
|
|
15
|
+
* all exist; events without matcher support ignore the empty matcher.
|
|
16
|
+
*/
|
|
17
|
+
export const CLAUDE_HOOK_EVENTS = [
|
|
18
|
+
'SessionStart',
|
|
19
|
+
'SessionEnd',
|
|
20
|
+
'UserPromptSubmit',
|
|
21
|
+
'PreToolUse',
|
|
22
|
+
'PostToolUse',
|
|
23
|
+
'PostToolUseFailure',
|
|
24
|
+
'PermissionRequest',
|
|
25
|
+
'Notification',
|
|
26
|
+
'Stop',
|
|
27
|
+
'SubagentStart',
|
|
28
|
+
'SubagentStop',
|
|
29
|
+
'PreCompact',
|
|
30
|
+
];
|
|
31
|
+
/** Install the nocville group into every Claude Code event. Mutates `settings`. */
|
|
32
|
+
export function installHooksInto(settings, command, opts = {}) {
|
|
33
|
+
return installHookGroups(settings, command, CLAUDE_HOOK_EVENTS, {
|
|
34
|
+
perEvent: Object.fromEntries(CLAUDE_HOOK_EVENTS.map((event) => [
|
|
35
|
+
event,
|
|
36
|
+
{
|
|
37
|
+
...((opts.platform ?? process.platform) === 'win32' ? { shell: 'powershell' } : {}),
|
|
38
|
+
...(event === 'SessionEnd'
|
|
39
|
+
? { timeout: 5 }
|
|
40
|
+
: { async: true, timeout: opts.timeout ?? 10 }),
|
|
41
|
+
},
|
|
42
|
+
])),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Remove all nocville entries (nested or legacy flat). Mutates `settings`. */
|
|
46
|
+
export function removeHooksFrom(settings) {
|
|
47
|
+
return removeHookGroups(settings);
|
|
48
|
+
}
|
|
49
|
+
export { findNocvilleCommand as findExistingNocvilleCommand } from '../agent-common/hook-settings.js';
|
|
50
|
+
/** `'<node>' '<cli.js>' claude-code hook # nocville-npc-hook` — no key on the command line. */
|
|
51
|
+
export function buildHookCommand(_cliPath) {
|
|
52
|
+
return buildShimHookCommand('claude-code');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Credentials embedded in a legacy hook command (`--api-url=… --api-key=…`),
|
|
56
|
+
* so upgrading setup can move them into the user config.
|
|
57
|
+
*/
|
|
58
|
+
export function legacyCredentials(settings) {
|
|
59
|
+
const prev = findNocvilleCommand(settings);
|
|
60
|
+
if (!prev)
|
|
61
|
+
return {};
|
|
62
|
+
return { apiUrl: extractFlag(prev, 'api-url'), apiKey: extractFlag(prev, 'api-key') };
|
|
63
|
+
}
|
|
64
|
+
function out(opts, msg) {
|
|
65
|
+
if (!opts.quiet)
|
|
66
|
+
console.log(msg);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Install Nocville hooks into Claude Code settings (matcher-group format,
|
|
70
|
+
* absolute node/cli paths, 10s timeout safety net). Idempotent: migrates and
|
|
71
|
+
* de-duplicates legacy entries and moves any legacy inline credentials into
|
|
72
|
+
* ~/.config/nocville/config.json. Returns false if setup could not complete.
|
|
73
|
+
*/
|
|
74
|
+
export function setupHooks(opts) {
|
|
75
|
+
const settingsPath = opts.settingsPath ?? claudeSettingsPath();
|
|
76
|
+
const dir = join(settingsPath, '..');
|
|
77
|
+
if (!existsSync(dir)) {
|
|
78
|
+
console.error(`${dir} does not exist — is Claude Code installed? Skipping Claude Code setup.`);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const settings = readJsonFile(settingsPath);
|
|
82
|
+
if (settings === null) {
|
|
83
|
+
console.error(`Could not parse ${settingsPath}; fix it (or move it aside) and re-run setup.`);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const creds = persistCredentials(opts, legacyCredentials(settings));
|
|
87
|
+
const resolved = resolveCliPath(opts.cliPath);
|
|
88
|
+
const cliPath = ensureStableCliPath(resolved);
|
|
89
|
+
installHookShim(stableNodePath(), cliPath);
|
|
90
|
+
const command = buildHookCommand(cliPath);
|
|
91
|
+
const { migrated, installed } = installHooksInto(settings, command, {
|
|
92
|
+
timeout: DEFAULTS.hookTimeoutSeconds,
|
|
93
|
+
});
|
|
94
|
+
writeJsonFile(settingsPath, settings);
|
|
95
|
+
out(opts, `✓ Claude Code: installed Nocville hooks for ${installed} events in ${settingsPath}`);
|
|
96
|
+
if (migrated > 0)
|
|
97
|
+
out(opts, ` (migrated ${migrated} legacy entr${migrated === 1 ? 'y' : 'ies'})`);
|
|
98
|
+
out(opts, ` Hook runs: ${process.execPath} ${cliPath}`);
|
|
99
|
+
out(opts, ` API: ${creds.apiUrl ?? 'https://nocville.com'} key: ${maskKey(creds.apiKey)}`);
|
|
100
|
+
if (!creds.apiKey) {
|
|
101
|
+
out(opts, ' ⚠ No API key saved yet — run: nocville-npc init --api-key=<your team key>');
|
|
102
|
+
}
|
|
103
|
+
out(opts, ' New Claude Code sessions will appear in your office. Restart running sessions.');
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
/** Remove all Nocville hooks from Claude Code settings. */
|
|
107
|
+
export function uninstallHooks(opts = {}) {
|
|
108
|
+
const o = typeof opts === 'boolean' ? {} : opts;
|
|
109
|
+
const settingsPath = o.settingsPath ?? claudeSettingsPath();
|
|
110
|
+
const settings = readJsonFile(settingsPath);
|
|
111
|
+
if (!settings || !existsSync(settingsPath)) {
|
|
112
|
+
out(o, 'Claude Code: no settings.json — nothing to remove.');
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
const removed = removeHooksFrom(settings);
|
|
116
|
+
if (removed > 0)
|
|
117
|
+
writeJsonFile(settingsPath, settings);
|
|
118
|
+
out(o, `✓ Claude Code: removed ${removed} Nocville hook(s) from ${settingsPath}`);
|
|
119
|
+
return removed;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AgentHookEvent } from '../agent-common/hook-runner.js';
|
|
2
|
+
import type { AgentPhase, AgentSessionState, AgentTransition, StatusOutput } from '../agent-common/session.js';
|
|
3
|
+
export type SessionMachineState = AgentPhase;
|
|
4
|
+
export type SessionState = AgentSessionState;
|
|
5
|
+
export type { StatusOutput };
|
|
6
|
+
export interface HookEvent extends AgentHookEvent {
|
|
7
|
+
/** User-facing session name — present in the SessionStart payload only. */
|
|
8
|
+
session_title?: string;
|
|
9
|
+
/** Notification events: permission_prompt | idle_prompt | … */
|
|
10
|
+
notification_type?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve the event name from either the real or legacy field. */
|
|
13
|
+
export declare function eventName(event: HookEvent): string;
|
|
14
|
+
/**
|
|
15
|
+
* Map one Claude Code hook event to a phase + activity line.
|
|
16
|
+
* Event names / payload fields per code.claude.com/docs/en/hooks.
|
|
17
|
+
* Prompt text is never read (privacy); tool inputs are summarized, not echoed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function claudeTransition(event: HookEvent, prev: AgentSessionState | null): AgentTransition;
|
|
20
|
+
/**
|
|
21
|
+
* Derive the NPC status output from a hook event (state + presentation).
|
|
22
|
+
* Kept for library consumers; the hook runner uses `claudeTransition` directly.
|
|
23
|
+
*/
|
|
24
|
+
export declare function processHookEvent(event: HookEvent, currentState: SessionState | null): {
|
|
25
|
+
state: SessionMachineState;
|
|
26
|
+
output: StatusOutput;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=state-machine.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { THINKING, describeToolUse, failureText, isQuestionTool, } from '../agent-common/activity.js';
|
|
2
|
+
import { hookEventName } from '../agent-common/hook-runner.js';
|
|
3
|
+
import { phasePresentation } from '../agent-common/session.js';
|
|
4
|
+
/** Resolve the event name from either the real or legacy field. */
|
|
5
|
+
export function eventName(event) {
|
|
6
|
+
return hookEventName(event);
|
|
7
|
+
}
|
|
8
|
+
const working = (activity, warn = false) => ({
|
|
9
|
+
state: 'WORKING',
|
|
10
|
+
activity,
|
|
11
|
+
status: warn ? 'warning' : 'healthy',
|
|
12
|
+
mood: warn ? 'concerned' : 'happy',
|
|
13
|
+
});
|
|
14
|
+
const waiting = (activity) => ({
|
|
15
|
+
state: 'WAITING',
|
|
16
|
+
activity,
|
|
17
|
+
status: 'warning',
|
|
18
|
+
mood: 'concerned',
|
|
19
|
+
});
|
|
20
|
+
const idle = (activity) => ({
|
|
21
|
+
state: 'IDLE',
|
|
22
|
+
activity,
|
|
23
|
+
status: 'healthy',
|
|
24
|
+
mood: 'idle',
|
|
25
|
+
});
|
|
26
|
+
/** Notification types that mean "the human needs to act". */
|
|
27
|
+
const WAITING_NOTIFICATIONS = new Set([
|
|
28
|
+
'permission_prompt',
|
|
29
|
+
'elicitation_dialog',
|
|
30
|
+
'elicitation_url_dialog',
|
|
31
|
+
'agent_needs_input',
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Map one Claude Code hook event to a phase + activity line.
|
|
35
|
+
* Event names / payload fields per code.claude.com/docs/en/hooks.
|
|
36
|
+
* Prompt text is never read (privacy); tool inputs are summarized, not echoed.
|
|
37
|
+
*/
|
|
38
|
+
export function claudeTransition(event, prev) {
|
|
39
|
+
const name = eventName(event);
|
|
40
|
+
const cwd = (typeof event.cwd === 'string' && event.cwd) || prev?.projectDir;
|
|
41
|
+
const keep = prev?.state ?? 'IDLE';
|
|
42
|
+
switch (name) {
|
|
43
|
+
case 'SessionStart':
|
|
44
|
+
return idle('Ready for a task');
|
|
45
|
+
case 'UserPromptSubmit':
|
|
46
|
+
return working(THINKING);
|
|
47
|
+
case 'PreToolUse': {
|
|
48
|
+
const activity = describeToolUse(event.tool_name, event.tool_input, cwd);
|
|
49
|
+
if (isQuestionTool(event.tool_name))
|
|
50
|
+
return waiting(activity);
|
|
51
|
+
return working(activity);
|
|
52
|
+
}
|
|
53
|
+
case 'PostToolUse':
|
|
54
|
+
case 'PostToolBatch':
|
|
55
|
+
// Tool finished — still working; keep showing the same activity.
|
|
56
|
+
return { ...working(undefined), quiet: prev?.state === 'WORKING' };
|
|
57
|
+
case 'PostToolUseFailure':
|
|
58
|
+
return working(failureText(event.tool_name), true);
|
|
59
|
+
case 'PermissionRequest': {
|
|
60
|
+
const what = event.tool_name ? describeToolUse(event.tool_name, event.tool_input, cwd) : '';
|
|
61
|
+
return waiting(what ? `Needs your approval: ${what}` : 'Needs your approval');
|
|
62
|
+
}
|
|
63
|
+
case 'Notification': {
|
|
64
|
+
const type = typeof event.notification_type === 'string' ? event.notification_type : '';
|
|
65
|
+
if (type === 'idle_prompt')
|
|
66
|
+
return idle('Done — waiting for next task');
|
|
67
|
+
if (type === 'permission_prompt')
|
|
68
|
+
return waiting('Needs your approval');
|
|
69
|
+
if (WAITING_NOTIFICATIONS.has(type) || !type)
|
|
70
|
+
return waiting('Waiting for you');
|
|
71
|
+
// auth_success, agent_completed, quota_* … → informational only.
|
|
72
|
+
return {
|
|
73
|
+
state: keep,
|
|
74
|
+
status: prev?.status ?? 'healthy',
|
|
75
|
+
mood: prev?.mood ?? 'idle',
|
|
76
|
+
quiet: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
case 'Stop':
|
|
80
|
+
return idle('Done — waiting for next task');
|
|
81
|
+
case 'StopFailure':
|
|
82
|
+
return { ...idle('Stopped with an error'), status: 'warning', mood: 'concerned' };
|
|
83
|
+
case 'SubagentStart':
|
|
84
|
+
return working('Delegating to a subagent');
|
|
85
|
+
case 'SubagentStop':
|
|
86
|
+
return working('Subagent finished');
|
|
87
|
+
case 'PreCompact':
|
|
88
|
+
return working('Compacting context');
|
|
89
|
+
case 'SessionEnd':
|
|
90
|
+
return {
|
|
91
|
+
state: 'IDLE',
|
|
92
|
+
activity: 'Session ended',
|
|
93
|
+
status: 'unknown',
|
|
94
|
+
mood: 'idle',
|
|
95
|
+
offline: true,
|
|
96
|
+
};
|
|
97
|
+
default:
|
|
98
|
+
return {
|
|
99
|
+
state: keep,
|
|
100
|
+
status: prev?.status ?? 'healthy',
|
|
101
|
+
mood: prev?.mood ?? 'idle',
|
|
102
|
+
quiet: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Derive the NPC status output from a hook event (state + presentation).
|
|
108
|
+
* Kept for library consumers; the hook runner uses `claudeTransition` directly.
|
|
109
|
+
*/
|
|
110
|
+
export function processHookEvent(event, currentState) {
|
|
111
|
+
const t = claudeTransition(event, currentState);
|
|
112
|
+
const { subStatus, behavior } = phasePresentation(t.state, t.offline);
|
|
113
|
+
return {
|
|
114
|
+
state: t.state,
|
|
115
|
+
output: {
|
|
116
|
+
status: t.status,
|
|
117
|
+
mood: t.mood,
|
|
118
|
+
message: t.activity ?? currentState?.activity?.[0] ?? '',
|
|
119
|
+
subStatus,
|
|
120
|
+
behavior,
|
|
121
|
+
offline: t.offline,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=state-machine.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const CODEX_DEFAULTS: {
|
|
2
|
+
/** Hook-mode per-session state (shared with the watcher to avoid double pushes). */
|
|
3
|
+
stateDir: string;
|
|
4
|
+
stateFileMaxAge: number;
|
|
5
|
+
/** Watchdog-backed sessions become unknown after three minutes without a heartbeat. */
|
|
6
|
+
heartbeatSeconds: number;
|
|
7
|
+
minPushGapMs: number;
|
|
8
|
+
hookTimeoutSeconds: number;
|
|
9
|
+
/** Watcher: Codex session rollout logs. */
|
|
10
|
+
sessionsDir: string;
|
|
11
|
+
/** Watcher: poll interval for new sessions / appended lines. */
|
|
12
|
+
pollMs: number;
|
|
13
|
+
/**
|
|
14
|
+
* Watcher: a session whose rollout file hasn't grown for this long is treated
|
|
15
|
+
* as ended (offline push). Matches Codex's own 30-minute idle SessionEnd.
|
|
16
|
+
*/
|
|
17
|
+
idleTimeoutMs: number;
|
|
18
|
+
/** Watcher: bytes read from the end of an already-active rollout on discovery. */
|
|
19
|
+
initialTailBytes: number;
|
|
20
|
+
/** Watcher: max bytes read per tick per session before skipping ahead to the tail. */
|
|
21
|
+
maxReadBytes: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function codexHome(): string;
|
|
24
|
+
//# sourceMappingURL=defaults.d.ts.map
|