@s_s/agent-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +317 -0
- package/build/create-kit.d.ts +16 -0
- package/build/create-kit.js +54 -0
- package/build/create-kit.js.map +1 -0
- package/build/detect.d.ts +13 -0
- package/build/detect.js +34 -0
- package/build/detect.js.map +1 -0
- package/build/hook-capabilities.d.ts +87 -0
- package/build/hook-capabilities.js +191 -0
- package/build/hook-capabilities.js.map +1 -0
- package/build/hook-registry.d.ts +158 -0
- package/build/hook-registry.js +223 -0
- package/build/hook-registry.js.map +1 -0
- package/build/hook-translators/claude-code.d.ts +26 -0
- package/build/hook-translators/claude-code.js +351 -0
- package/build/hook-translators/claude-code.js.map +1 -0
- package/build/hook-translators/index.d.ts +4 -0
- package/build/hook-translators/index.js +4 -0
- package/build/hook-translators/index.js.map +1 -0
- package/build/hook-translators/openclaw.d.ts +25 -0
- package/build/hook-translators/openclaw.js +272 -0
- package/build/hook-translators/openclaw.js.map +1 -0
- package/build/hook-translators/opencode.d.ts +23 -0
- package/build/hook-translators/opencode.js +254 -0
- package/build/hook-translators/opencode.js.map +1 -0
- package/build/hook-translators/types.d.ts +59 -0
- package/build/hook-translators/types.js +2 -0
- package/build/hook-translators/types.js.map +1 -0
- package/build/hook-types.d.ts +155 -0
- package/build/hook-types.js +2 -0
- package/build/hook-types.js.map +1 -0
- package/build/hooks.d.ts +30 -0
- package/build/hooks.js +333 -0
- package/build/hooks.js.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.js +11 -0
- package/build/index.js.map +1 -0
- package/build/platform.d.ts +17 -0
- package/build/platform.js +79 -0
- package/build/platform.js.map +1 -0
- package/build/prompt.d.ts +21 -0
- package/build/prompt.js +87 -0
- package/build/prompt.js.map +1 -0
- package/build/register.d.ts +18 -0
- package/build/register.js +34 -0
- package/build/register.js.map +1 -0
- package/build/types.d.ts +111 -0
- package/build/types.js +53 -0
- package/build/types.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { AgentType } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Content injection intent.
|
|
4
|
+
* Injects text into agent context at various lifecycle points.
|
|
5
|
+
*/
|
|
6
|
+
export interface InjectIntent {
|
|
7
|
+
type: 'inject';
|
|
8
|
+
/** Reminder injected on every user message turn. */
|
|
9
|
+
perTurn: string;
|
|
10
|
+
/** Reminder injected at session start. */
|
|
11
|
+
sessionStart?: string;
|
|
12
|
+
/** Reminder injected before context compaction. */
|
|
13
|
+
compaction?: string;
|
|
14
|
+
/** Reminder injected at session end. */
|
|
15
|
+
sessionEnd?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Tool call interception result.
|
|
19
|
+
*/
|
|
20
|
+
export interface ToolCallInterceptResult {
|
|
21
|
+
/** Block the tool call entirely. */
|
|
22
|
+
block?: boolean;
|
|
23
|
+
/** Reason for blocking (shown to agent). */
|
|
24
|
+
reason?: string;
|
|
25
|
+
/** Modified arguments to pass to the tool (if not blocking). */
|
|
26
|
+
args?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Tool call context passed to handler.
|
|
30
|
+
*/
|
|
31
|
+
export interface ToolCallContext {
|
|
32
|
+
toolName: string;
|
|
33
|
+
args: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Pre-tool-call interception intent.
|
|
37
|
+
* Intercept tool calls before execution — can block or modify arguments.
|
|
38
|
+
*/
|
|
39
|
+
export interface BeforeToolCallIntent {
|
|
40
|
+
type: 'beforeToolCall';
|
|
41
|
+
/** Regex pattern to match tool names. Omit or '*' to match all. */
|
|
42
|
+
match?: RegExp | string;
|
|
43
|
+
/** Handler that decides whether to block or modify the tool call. */
|
|
44
|
+
handler: (ctx: ToolCallContext) => ToolCallInterceptResult | void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Tool call observation context.
|
|
48
|
+
*/
|
|
49
|
+
export interface ToolCallObserveContext {
|
|
50
|
+
toolName: string;
|
|
51
|
+
args: Record<string, unknown>;
|
|
52
|
+
result: string;
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Post-tool-call observation intent.
|
|
57
|
+
* Observe tool call results — cannot modify, only react.
|
|
58
|
+
*/
|
|
59
|
+
export interface AfterToolCallIntent {
|
|
60
|
+
type: 'afterToolCall';
|
|
61
|
+
/** Regex pattern to match tool names. Omit or '*' to match all. */
|
|
62
|
+
match?: RegExp | string;
|
|
63
|
+
/** Handler that reacts to tool call completion. */
|
|
64
|
+
handler: (ctx: ToolCallObserveContext) => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Session lifecycle context.
|
|
68
|
+
*/
|
|
69
|
+
export interface SessionContext {
|
|
70
|
+
sessionId?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Session lifecycle intent.
|
|
74
|
+
* React to session start and/or end.
|
|
75
|
+
*/
|
|
76
|
+
export interface OnSessionIntent {
|
|
77
|
+
type: 'onSession';
|
|
78
|
+
/** Handler called when session starts. */
|
|
79
|
+
start?: (ctx: SessionContext) => void;
|
|
80
|
+
/** Handler called when session ends. */
|
|
81
|
+
end?: (ctx: SessionContext) => void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Permission decision.
|
|
85
|
+
*/
|
|
86
|
+
export type PermissionDecision = 'allow' | 'deny' | 'ask';
|
|
87
|
+
/**
|
|
88
|
+
* Permission request context.
|
|
89
|
+
*/
|
|
90
|
+
export interface PermissionContext {
|
|
91
|
+
toolName: string;
|
|
92
|
+
args: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Permission decision intent.
|
|
96
|
+
* Intercept permission requests and decide allow/deny/ask.
|
|
97
|
+
*/
|
|
98
|
+
export interface OnPermissionIntent {
|
|
99
|
+
type: 'onPermission';
|
|
100
|
+
/** Regex pattern to match tool names. Omit or '*' to match all. */
|
|
101
|
+
match?: RegExp | string;
|
|
102
|
+
/** Handler that decides the permission outcome. */
|
|
103
|
+
handler: (ctx: PermissionContext) => PermissionDecision;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Union of all intent types.
|
|
107
|
+
*/
|
|
108
|
+
export type HookIntent = InjectIntent | BeforeToolCallIntent | AfterToolCallIntent | OnSessionIntent | OnPermissionIntent;
|
|
109
|
+
/**
|
|
110
|
+
* Discriminant values for intent types.
|
|
111
|
+
*/
|
|
112
|
+
export type IntentType = HookIntent['type'];
|
|
113
|
+
/** Claude Code / Codex native hook event names. */
|
|
114
|
+
export type ClaudeCodeHookName = 'SessionStart' | 'InstructionsLoaded' | 'UserPromptSubmit' | 'PreToolUse' | 'PermissionRequest' | 'PostToolUse' | 'PostToolUseFailure' | 'Notification' | 'SubagentStart' | 'SubagentStop' | 'Stop' | 'TeammateIdle' | 'TaskCompleted' | 'ConfigChange' | 'WorktreeCreate' | 'WorktreeRemove' | 'PreCompact' | 'PostCompact' | 'Elicitation' | 'ElicitationResult' | 'SessionEnd';
|
|
115
|
+
/** OpenCode native hook names. */
|
|
116
|
+
export type OpenCodeHookName = 'event' | 'config' | 'tool' | 'auth' | 'chat.message' | 'chat.params' | 'chat.headers' | 'permission.ask' | 'command.execute.before' | 'tool.execute.before' | 'shell.env' | 'tool.execute.after' | 'experimental.chat.messages.transform' | 'experimental.chat.system.transform' | 'experimental.session.compacting' | 'experimental.text.complete' | 'tool.definition';
|
|
117
|
+
/** OpenClaw plugin hook names (24). */
|
|
118
|
+
export type OpenClawPluginHookName = 'before_model_resolve' | 'before_prompt_build' | 'before_agent_start' | 'llm_input' | 'llm_output' | 'agent_end' | 'before_compaction' | 'after_compaction' | 'before_reset' | 'message_received' | 'message_sending' | 'message_sent' | 'before_tool_call' | 'after_tool_call' | 'tool_result_persist' | 'before_message_write' | 'session_start' | 'session_end' | 'subagent_spawning' | 'subagent_delivery_target' | 'subagent_spawned' | 'subagent_ended' | 'gateway_start' | 'gateway_stop';
|
|
119
|
+
/** OpenClaw internal hook names (type:action). */
|
|
120
|
+
export type OpenClawInternalHookName = 'command:new' | 'command:reset' | 'command:stop' | 'session:compact:before' | 'session:compact:after' | 'agent:bootstrap' | 'gateway:startup' | 'message:received' | 'message:sent' | 'message:transcribed' | 'message:preprocessed';
|
|
121
|
+
/** All OpenClaw hook names (plugin + internal). */
|
|
122
|
+
export type OpenClawHookName = OpenClawPluginHookName | OpenClawInternalHookName;
|
|
123
|
+
/** Map from agent type to its native hook name union. */
|
|
124
|
+
export type NativeHookNameMap = {
|
|
125
|
+
'claude-code': ClaudeCodeHookName;
|
|
126
|
+
codex: ClaudeCodeHookName;
|
|
127
|
+
opencode: OpenCodeHookName;
|
|
128
|
+
openclaw: OpenClawHookName;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Raw hook registration.
|
|
132
|
+
* Provides the handler code (as string) for a specific agent's native hook.
|
|
133
|
+
* When a raw hook targets the same native hook as an intent, raw wins.
|
|
134
|
+
*/
|
|
135
|
+
export interface RawHookRegistration<A extends AgentType = AgentType> {
|
|
136
|
+
agent: A;
|
|
137
|
+
hookName: A extends keyof NativeHookNameMap ? NativeHookNameMap[A] : string;
|
|
138
|
+
/** Handler code as string — will be written directly into the generated file. */
|
|
139
|
+
handler: string;
|
|
140
|
+
/** Optional matcher (regex string). Only used by agents that support it. */
|
|
141
|
+
matcher?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Extend hook registration.
|
|
145
|
+
* Runs after the intent-generated handler for the same native hook.
|
|
146
|
+
* Cannot replace, only augment.
|
|
147
|
+
*/
|
|
148
|
+
export interface ExtendHookRegistration<A extends AgentType = AgentType> {
|
|
149
|
+
agent: A;
|
|
150
|
+
hookName: A extends keyof NativeHookNameMap ? NativeHookNameMap[A] : string;
|
|
151
|
+
/** Extension code as string — appended to the intent-generated handler. */
|
|
152
|
+
handler: string;
|
|
153
|
+
/** Optional matcher (regex string). Only used by agents that support it. */
|
|
154
|
+
matcher?: string;
|
|
155
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-types.js","sourceRoot":"","sources":["../src/hook-types.ts"],"names":[],"mappings":""}
|
package/build/hooks.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AgentType, HookInstallResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Install hooks for the given agent type.
|
|
4
|
+
*
|
|
5
|
+
* Reads all registered intents, raw hooks, and extend hooks from the hook
|
|
6
|
+
* registry. Translates them into native hook files using the agent-specific
|
|
7
|
+
* translator. Runs degradation checks and conflict detection.
|
|
8
|
+
*
|
|
9
|
+
* @internal — called by the Kit object returned from createKit().
|
|
10
|
+
*/
|
|
11
|
+
export declare function installHooks(name: string, agent: AgentType): Promise<HookInstallResult>;
|
|
12
|
+
/**
|
|
13
|
+
* Uninstall hooks for the given agent type.
|
|
14
|
+
*
|
|
15
|
+
* Removes hook files from the hook directory and cleans up settings.json
|
|
16
|
+
* entries for agents that use them (Claude Code, Codex).
|
|
17
|
+
*
|
|
18
|
+
* @internal — called by the Kit object returned from createKit().
|
|
19
|
+
*/
|
|
20
|
+
export declare function uninstallHooks(name: string, agent: AgentType): Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
removed: string[];
|
|
23
|
+
error?: string;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Check if hooks are already installed for the given agent type.
|
|
27
|
+
*
|
|
28
|
+
* @internal — called by the Kit object returned from createKit().
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasHooksInstalled(name: string, agent: AgentType): Promise<boolean>;
|
package/build/hooks.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { AGENT_REGISTRY } from './types.js';
|
|
7
|
+
import { getIntents, getRawHooks, getExtendHooks } from './hook-registry.js';
|
|
8
|
+
import { checkAllDegradation } from './hook-capabilities.js';
|
|
9
|
+
import { ClaudeCodeTranslator } from './hook-translators/claude-code.js';
|
|
10
|
+
import { OpenCodeTranslator } from './hook-translators/opencode.js';
|
|
11
|
+
import { OpenClawTranslator } from './hook-translators/openclaw.js';
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Translator factory
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function getTranslator(agent) {
|
|
17
|
+
switch (agent) {
|
|
18
|
+
case 'claude-code':
|
|
19
|
+
return new ClaudeCodeTranslator('claude-code');
|
|
20
|
+
case 'codex':
|
|
21
|
+
return new ClaudeCodeTranslator('codex');
|
|
22
|
+
case 'opencode':
|
|
23
|
+
return new OpenCodeTranslator();
|
|
24
|
+
case 'openclaw':
|
|
25
|
+
return new OpenClawTranslator();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// installHooks — main entry point
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Install hooks for the given agent type.
|
|
33
|
+
*
|
|
34
|
+
* Reads all registered intents, raw hooks, and extend hooks from the hook
|
|
35
|
+
* registry. Translates them into native hook files using the agent-specific
|
|
36
|
+
* translator. Runs degradation checks and conflict detection.
|
|
37
|
+
*
|
|
38
|
+
* @internal — called by the Kit object returned from createKit().
|
|
39
|
+
*/
|
|
40
|
+
export async function installHooks(name, agent) {
|
|
41
|
+
const home = os.homedir();
|
|
42
|
+
const entry = AGENT_REGISTRY[agent];
|
|
43
|
+
const hookDir = entry.getHookDir(home, name);
|
|
44
|
+
const result = {
|
|
45
|
+
success: false,
|
|
46
|
+
hookDir,
|
|
47
|
+
filesWritten: [],
|
|
48
|
+
settingsUpdated: false,
|
|
49
|
+
notes: [],
|
|
50
|
+
warnings: [],
|
|
51
|
+
skipped: [],
|
|
52
|
+
};
|
|
53
|
+
const intents = getIntents();
|
|
54
|
+
const rawHooks = getRawHooks();
|
|
55
|
+
const extendHooks = getExtendHooks();
|
|
56
|
+
// Check if there's anything to install
|
|
57
|
+
if (intents.length === 0 && rawHooks.size === 0 && extendHooks.size === 0) {
|
|
58
|
+
result.error =
|
|
59
|
+
'No hooks registered. Use hooks.inject(), hooks.beforeToolCall(), etc. to declare hook behavior.';
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
// Step 1: Run degradation checks
|
|
64
|
+
const intentTypes = [...new Set(intents.map((i) => i.type))];
|
|
65
|
+
const degradations = checkAllDegradation(agent, intentTypes);
|
|
66
|
+
for (const d of degradations) {
|
|
67
|
+
if (d.level === 'unsupported') {
|
|
68
|
+
result.warnings.push(d.message);
|
|
69
|
+
}
|
|
70
|
+
else if (d.level === 'partial') {
|
|
71
|
+
result.warnings.push(d.message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Step 2: Filter raw/extend hooks for this agent
|
|
75
|
+
const agentRawHooks = new Map();
|
|
76
|
+
for (const [key, reg] of rawHooks) {
|
|
77
|
+
if (key.startsWith(`${agent}::`)) {
|
|
78
|
+
agentRawHooks.set(key, reg);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const agentExtendHooks = new Map();
|
|
82
|
+
for (const [key, regs] of extendHooks) {
|
|
83
|
+
if (key.startsWith(`${agent}::`)) {
|
|
84
|
+
agentExtendHooks.set(key, regs);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Step 3: Translate intents → native files
|
|
88
|
+
const translator = getTranslator(agent);
|
|
89
|
+
const translation = translator.translate(intents, agentRawHooks, agentExtendHooks, name);
|
|
90
|
+
// Merge translation warnings and skipped
|
|
91
|
+
result.warnings.push(...translation.warnings);
|
|
92
|
+
result.skipped.push(...translation.skipped);
|
|
93
|
+
// Step 4: Write hook files
|
|
94
|
+
if (Object.keys(translation.files).length === 0) {
|
|
95
|
+
result.notes.push('No hook files generated for this agent.');
|
|
96
|
+
result.success = true;
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
await fs.mkdir(hookDir, { recursive: true });
|
|
100
|
+
for (const [fileName, content] of Object.entries(translation.files)) {
|
|
101
|
+
const filePath = path.join(hookDir, fileName);
|
|
102
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
103
|
+
if (fileName.endsWith('.sh')) {
|
|
104
|
+
await fs.chmod(filePath, 0o755);
|
|
105
|
+
}
|
|
106
|
+
result.filesWritten.push(filePath);
|
|
107
|
+
}
|
|
108
|
+
// Step 5: Merge settings.json for agents that need it (Claude Code / Codex)
|
|
109
|
+
if (entry.getSettingsPath) {
|
|
110
|
+
const settingsPath = entry.getSettingsPath(home);
|
|
111
|
+
const shellFiles = Object.keys(translation.files).filter((f) => f.endsWith('.sh'));
|
|
112
|
+
if (shellFiles.length > 0) {
|
|
113
|
+
await mergeHookSettings(settingsPath, hookDir, shellFiles, name);
|
|
114
|
+
result.settingsUpdated = true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Step 6: Agent-specific post-install
|
|
118
|
+
if (agent === 'openclaw') {
|
|
119
|
+
try {
|
|
120
|
+
await execFileAsync('openclaw', ['hooks', 'enable', name]);
|
|
121
|
+
result.notes.push(`Hook activated via \`openclaw hooks enable ${name}\`.`);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
result.notes.push(`Run \`openclaw hooks enable ${name}\` to activate the hook.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
result.success = true;
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// uninstallHooks
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
/**
|
|
138
|
+
* Uninstall hooks for the given agent type.
|
|
139
|
+
*
|
|
140
|
+
* Removes hook files from the hook directory and cleans up settings.json
|
|
141
|
+
* entries for agents that use them (Claude Code, Codex).
|
|
142
|
+
*
|
|
143
|
+
* @internal — called by the Kit object returned from createKit().
|
|
144
|
+
*/
|
|
145
|
+
export async function uninstallHooks(name, agent) {
|
|
146
|
+
const home = os.homedir();
|
|
147
|
+
const entry = AGENT_REGISTRY[agent];
|
|
148
|
+
const hookDir = entry.getHookDir(home, name);
|
|
149
|
+
const removed = [];
|
|
150
|
+
try {
|
|
151
|
+
// Remove hook directory
|
|
152
|
+
try {
|
|
153
|
+
const files = await fs.readdir(hookDir);
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
const filePath = path.join(hookDir, file);
|
|
156
|
+
await fs.unlink(filePath);
|
|
157
|
+
removed.push(filePath);
|
|
158
|
+
}
|
|
159
|
+
await fs.rmdir(hookDir);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Directory may not exist — that's fine
|
|
163
|
+
}
|
|
164
|
+
// Clean settings.json for Claude Code / Codex
|
|
165
|
+
if (entry.getSettingsPath) {
|
|
166
|
+
const settingsPath = entry.getSettingsPath(home);
|
|
167
|
+
await cleanHookSettings(settingsPath, name);
|
|
168
|
+
}
|
|
169
|
+
// Deactivate for OpenClaw
|
|
170
|
+
if (agent === 'openclaw') {
|
|
171
|
+
try {
|
|
172
|
+
await execFileAsync('openclaw', ['hooks', 'disable', name]);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Best effort
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { success: true, removed };
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
return { success: false, removed, error: err instanceof Error ? err.message : String(err) };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// hasHooksInstalled
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
/**
|
|
188
|
+
* Check if hooks are already installed for the given agent type.
|
|
189
|
+
*
|
|
190
|
+
* @internal — called by the Kit object returned from createKit().
|
|
191
|
+
*/
|
|
192
|
+
export async function hasHooksInstalled(name, agent) {
|
|
193
|
+
const home = os.homedir();
|
|
194
|
+
const entry = AGENT_REGISTRY[agent];
|
|
195
|
+
const hookDir = entry.getHookDir(home, name);
|
|
196
|
+
try {
|
|
197
|
+
const files = await fs.readdir(hookDir);
|
|
198
|
+
return files.length > 0;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Settings merge (Claude Code / Codex)
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
/**
|
|
208
|
+
* Merge hook entries into settings.json for Claude Code / Codex.
|
|
209
|
+
* Maps each shell script to its corresponding native hook event based on filename conventions.
|
|
210
|
+
*/
|
|
211
|
+
async function mergeHookSettings(settingsPath, hookDir, shellFiles, toolName) {
|
|
212
|
+
let settings = {};
|
|
213
|
+
try {
|
|
214
|
+
const raw = await fs.readFile(settingsPath, 'utf-8');
|
|
215
|
+
settings = JSON.parse(raw);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// Start fresh
|
|
219
|
+
}
|
|
220
|
+
if (!settings.hooks || typeof settings.hooks !== 'object') {
|
|
221
|
+
settings.hooks = {};
|
|
222
|
+
}
|
|
223
|
+
const hooks = settings.hooks;
|
|
224
|
+
// Map filename patterns to native hook events
|
|
225
|
+
const fileToEvent = {
|
|
226
|
+
inject: 'UserPromptSubmit',
|
|
227
|
+
'session-start': 'SessionStart',
|
|
228
|
+
'session-end': 'SessionEnd',
|
|
229
|
+
compaction: 'PreCompact',
|
|
230
|
+
'before-tool': 'PreToolUse',
|
|
231
|
+
'after-tool': 'PostToolUse',
|
|
232
|
+
'on-session-start': 'SessionStart',
|
|
233
|
+
'on-session-end': 'SessionEnd',
|
|
234
|
+
permission: 'PermissionRequest',
|
|
235
|
+
};
|
|
236
|
+
for (const fileName of shellFiles) {
|
|
237
|
+
const activatorPath = path.join(hookDir, fileName);
|
|
238
|
+
// Determine the event from filename
|
|
239
|
+
let event;
|
|
240
|
+
// Check raw hooks first (pattern: toolName-raw-hookname.sh)
|
|
241
|
+
const rawMatch = fileName.match(/^.+-raw-(.+)\.sh$/);
|
|
242
|
+
if (rawMatch) {
|
|
243
|
+
// Raw hooks use the hook name directly (case-insensitive lookup)
|
|
244
|
+
event = rawMatch[1].charAt(0).toUpperCase() + rawMatch[1].slice(1);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Intent-generated hooks use filename conventions
|
|
248
|
+
for (const [pattern, hookEvent] of Object.entries(fileToEvent)) {
|
|
249
|
+
if (fileName.includes(pattern)) {
|
|
250
|
+
event = hookEvent;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!event)
|
|
256
|
+
continue;
|
|
257
|
+
const hookEntry = {
|
|
258
|
+
matcher: '',
|
|
259
|
+
hooks: [{ type: 'command', command: activatorPath }],
|
|
260
|
+
};
|
|
261
|
+
if (!Array.isArray(hooks[event])) {
|
|
262
|
+
hooks[event] = [];
|
|
263
|
+
}
|
|
264
|
+
// Remove existing entries for this tool
|
|
265
|
+
hooks[event] = hooks[event].filter((entry) => {
|
|
266
|
+
if (!entry || typeof entry !== 'object')
|
|
267
|
+
return true;
|
|
268
|
+
const e = entry;
|
|
269
|
+
if (!Array.isArray(e.hooks))
|
|
270
|
+
return true;
|
|
271
|
+
return !e.hooks.some((h) => {
|
|
272
|
+
if (!h || typeof h !== 'object')
|
|
273
|
+
return false;
|
|
274
|
+
const hook = h;
|
|
275
|
+
return typeof hook.command === 'string' && hook.command.includes(toolName);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
hooks[event].push(hookEntry);
|
|
279
|
+
}
|
|
280
|
+
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
281
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Remove all hook entries for a tool from settings.json.
|
|
285
|
+
*/
|
|
286
|
+
async function cleanHookSettings(settingsPath, toolName) {
|
|
287
|
+
let settings;
|
|
288
|
+
try {
|
|
289
|
+
const raw = await fs.readFile(settingsPath, 'utf-8');
|
|
290
|
+
settings = JSON.parse(raw);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return; // No settings file to clean
|
|
294
|
+
}
|
|
295
|
+
if (!settings.hooks || typeof settings.hooks !== 'object')
|
|
296
|
+
return;
|
|
297
|
+
const hooks = settings.hooks;
|
|
298
|
+
let changed = false;
|
|
299
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
300
|
+
if (!Array.isArray(entries))
|
|
301
|
+
continue;
|
|
302
|
+
const filtered = entries.filter((entry) => {
|
|
303
|
+
if (!entry || typeof entry !== 'object')
|
|
304
|
+
return true;
|
|
305
|
+
const e = entry;
|
|
306
|
+
if (!Array.isArray(e.hooks))
|
|
307
|
+
return true;
|
|
308
|
+
const hasToolHook = e.hooks.some((h) => {
|
|
309
|
+
if (!h || typeof h !== 'object')
|
|
310
|
+
return false;
|
|
311
|
+
const hook = h;
|
|
312
|
+
return typeof hook.command === 'string' && hook.command.includes(toolName);
|
|
313
|
+
});
|
|
314
|
+
return !hasToolHook;
|
|
315
|
+
});
|
|
316
|
+
if (filtered.length !== entries.length) {
|
|
317
|
+
hooks[event] = filtered;
|
|
318
|
+
changed = true;
|
|
319
|
+
}
|
|
320
|
+
// Remove empty event arrays
|
|
321
|
+
if (filtered.length === 0) {
|
|
322
|
+
delete hooks[event];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Remove empty hooks object
|
|
326
|
+
if (Object.keys(hooks).length === 0) {
|
|
327
|
+
delete settings.hooks;
|
|
328
|
+
}
|
|
329
|
+
if (changed) {
|
|
330
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E,SAAS,aAAa,CAAC,KAAgB;IACnC,QAAQ,KAAK,EAAE,CAAC;QACZ,KAAK,aAAa;YACd,OAAO,IAAI,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACnD,KAAK,OAAO;YACR,OAAO,IAAI,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAC7C,KAAK,UAAU;YACX,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACpC,KAAK,UAAU;YACX,OAAO,IAAI,kBAAkB,EAAE,CAAC;IACxC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,KAAgB;IAC7D,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAsB;QAC9B,OAAO,EAAE,KAAK;QACd,OAAO;QACP,YAAY,EAAE,EAAE;QAChB,eAAe,EAAE,KAAK;QACtB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;KACd,CAAC;IAEF,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,uCAAuC;IACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,CAAC,KAAK;YACR,iGAAiG,CAAC;QACtG,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACD,iCAAiC;QACjC,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAiB,CAAC;QAC7E,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;gBAC5B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC;iBAAM,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC;QACL,CAAC;QAED,iDAAiD;QACjD,MAAM,aAAa,GAAG,IAAI,GAAG,EAA+B,CAAC;QAC7D,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC;YAChC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC/B,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAChC,CAAC;QACL,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAuE,CAAC;QACxG,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC/B,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACpC,CAAC;QACL,CAAC;QAED,2CAA2C;QAC3C,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAEzF,yCAAyC;QACzC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAE5C,2BAA2B;QAC3B,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YAC7D,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YACtB,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE7C,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAE/C,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YACpC,CAAC;YAED,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;QAED,4EAA4E;QAC5E,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YAEnF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;gBACjE,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC;YAClC,CAAC;QACL,CAAC;QAED,sCAAsC;QACtC,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC;gBACD,MAAM,aAAa,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC3D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,8CAA8C,IAAI,KAAK,CAAC,CAAC;YAC/E,CAAC;YAAC,MAAM,CAAC;gBACL,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,+BAA+B,IAAI,0BAA0B,CAAC,CAAC;YACrF,CAAC;QACL,CAAC;QAED,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAChC,IAAY,EACZ,KAAgB;IAEhB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,CAAC;QACD,wBAAwB;QACxB,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAC1C,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;YACD,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACL,wCAAwC;QAC5C,CAAC;QAED,8CAA8C;QAC9C,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACjD,MAAM,iBAAiB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAChD,CAAC;QAED,0BAA0B;QAC1B,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC;gBACD,MAAM,aAAa,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;YAChE,CAAC;YAAC,MAAM,CAAC;gBACL,cAAc;YAClB,CAAC;QACL,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChG,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAgB;IAClE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE7C,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E;;;GAGG;AACH,KAAK,UAAU,iBAAiB,CAC5B,YAAoB,EACpB,OAAe,EACf,UAAoB,EACpB,QAAgB;IAEhB,IAAI,QAAQ,GAA4B,EAAE,CAAC;IAC3C,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrD,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACL,cAAc;IAClB,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxD,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;IACxB,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAkC,CAAC;IAE1D,8CAA8C;IAC9C,MAAM,WAAW,GAA2B;QACxC,MAAM,EAAE,kBAAkB;QAC1B,eAAe,EAAE,cAAc;QAC/B,aAAa,EAAE,YAAY;QAC3B,UAAU,EAAE,YAAY;QACxB,aAAa,EAAE,YAAY;QAC3B,YAAY,EAAE,aAAa;QAC3B,kBAAkB,EAAE,cAAc;QAClC,gBAAgB,EAAE,YAAY;QAC9B,UAAU,EAAE,mBAAmB;KAClC,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEnD,oCAAoC;QACpC,IAAI,KAAyB,CAAC;QAE9B,4DAA4D;QAC5D,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACrD,IAAI,QAAQ,EAAE,CAAC;YACX,iEAAiE;YACjE,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACJ,kDAAkD;YAClD,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7D,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC7B,KAAK,GAAG,SAAS,CAAC;oBAClB,MAAM;gBACV,CAAC;YACL,CAAC;QACL,CAAC;QAED,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,SAAS,GAAG;YACd,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;SACvD,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACtB,CAAC;QAED,wCAAwC;QACxC,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACzC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACrD,MAAM,CAAC,GAAG,KAAgC,CAAC;YAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACzC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE;gBAChC,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;oBAAE,OAAO,KAAK,CAAC;gBAC9C,MAAM,IAAI,GAAG,CAA4B,CAAC;gBAC1C,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC/E,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACxF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB,CAAC,YAAoB,EAAE,QAAgB;IACnE,IAAI,QAAiC,CAAC;IACtC,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrD,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,4BAA4B;IACxC,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO;IAElE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAkC,CAAC;IAC1D,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QAEtC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACtC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACrD,MAAM,CAAC,GAAG,KAAgC,CAAC;YAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACzC,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE;gBAC5C,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;oBAAE,OAAO,KAAK,CAAC;gBAC9C,MAAM,IAAI,GAAG,CAA4B,CAAC;gBAC1C,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC/E,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,WAAW,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;YACrC,KAAK,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;YACxB,OAAO,GAAG,IAAI,CAAC;QACnB,CAAC;QAED,4BAA4B;QAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,QAAQ,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACxF,CAAC;AACL,CAAC"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createKit } from './create-kit.js';
|
|
2
|
+
export type { AgentType, StorageScope, ScopeOptions, KitOptions, Kit, HookInstallResult, SkippedIntent, } from './types.js';
|
|
3
|
+
export { AGENT_TYPES, CLIENT_NAME_MAP } from './types.js';
|
|
4
|
+
export type { InjectIntent, BeforeToolCallIntent, AfterToolCallIntent, OnSessionIntent, OnPermissionIntent, HookIntent, IntentType, ToolCallContext, ToolCallInterceptResult, ToolCallObserveContext, SessionContext, PermissionDecision, PermissionContext, RawHookRegistration, ExtendHookRegistration, } from './hook-types.js';
|
|
5
|
+
export type { SupportLevel, CapabilityEntry, DegradationWarning } from './hook-capabilities.js';
|
|
6
|
+
export { CAPABILITY_MATRIX, checkDegradation, checkAllDegradation, isIntentFullyUnsupported, } from './hook-capabilities.js';
|
|
7
|
+
export { detectProjectRoot } from './platform.js';
|
|
8
|
+
export { detectAgent, detectAgentFromClient } from './detect.js';
|
|
9
|
+
export { hooks } from './hook-registry.js';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Core factory
|
|
2
|
+
export { createKit } from './create-kit.js';
|
|
3
|
+
export { AGENT_TYPES, CLIENT_NAME_MAP } from './types.js';
|
|
4
|
+
export { CAPABILITY_MATRIX, checkDegradation, checkAllDegradation, isIntentFullyUnsupported, } from './hook-capabilities.js';
|
|
5
|
+
// Platform (only detectProjectRoot is standalone; getDataDir is on Kit)
|
|
6
|
+
export { detectProjectRoot } from './platform.js';
|
|
7
|
+
// Detection
|
|
8
|
+
export { detectAgent, detectAgentFromClient } from './detect.js';
|
|
9
|
+
// Hooks — declaration API (global, not bound to kit instance)
|
|
10
|
+
export { hooks } from './hook-registry.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAY5C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAuB1D,OAAO,EACH,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GAC3B,MAAM,wBAAwB,CAAC;AAEhC,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEjE,8DAA8D;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ResolvedKitConfig, ScopeOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Get platform-appropriate data directory path.
|
|
4
|
+
*
|
|
5
|
+
* - global: follows platform conventions (macOS ~/Library/Application Support, Linux XDG, Windows APPDATA)
|
|
6
|
+
* - project: `<projectRoot>/.<name>` (or custom dir name)
|
|
7
|
+
*
|
|
8
|
+
* @internal — called by the Kit object returned from createKit().
|
|
9
|
+
*/
|
|
10
|
+
export declare function getDataDir(config: ResolvedKitConfig, options?: ScopeOptions): string;
|
|
11
|
+
/**
|
|
12
|
+
* Detect project root directory.
|
|
13
|
+
*
|
|
14
|
+
* Resolution order: git root > project marker files (.git, package.json, etc.) > cwd fallback.
|
|
15
|
+
* This is an independent function — does not require createKit().
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectProjectRoot(cwd?: string): Promise<string>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const PROJECT_ROOT_MARKERS = ['.git', 'package.json', 'pyproject.toml', 'Cargo.toml', 'go.mod'];
|
|
8
|
+
/**
|
|
9
|
+
* Get platform-appropriate data directory path.
|
|
10
|
+
*
|
|
11
|
+
* - global: follows platform conventions (macOS ~/Library/Application Support, Linux XDG, Windows APPDATA)
|
|
12
|
+
* - project: `<projectRoot>/.<name>` (or custom dir name)
|
|
13
|
+
*
|
|
14
|
+
* @internal — called by the Kit object returned from createKit().
|
|
15
|
+
*/
|
|
16
|
+
export function getDataDir(config, options) {
|
|
17
|
+
const scope = options?.scope ?? 'global';
|
|
18
|
+
if (scope === 'project') {
|
|
19
|
+
if (!options?.projectRoot) {
|
|
20
|
+
throw new Error('getDataDir: projectRoot is required when scope is "project".');
|
|
21
|
+
}
|
|
22
|
+
const dirName = config.dirs?.project ?? `.${config.name}`;
|
|
23
|
+
return path.join(options.projectRoot, dirName);
|
|
24
|
+
}
|
|
25
|
+
// Global scope
|
|
26
|
+
const envVar = config.envOverride ?? `${config.name.toUpperCase().replace(/-/g, '_')}_DATA_DIR`;
|
|
27
|
+
const envDir = process.env[envVar];
|
|
28
|
+
if (envDir)
|
|
29
|
+
return envDir;
|
|
30
|
+
const dirName = config.dirs?.global ?? config.name;
|
|
31
|
+
const home = os.homedir();
|
|
32
|
+
const platform = process.platform;
|
|
33
|
+
switch (platform) {
|
|
34
|
+
case 'darwin':
|
|
35
|
+
return path.join(home, 'Library', 'Application Support', dirName);
|
|
36
|
+
case 'win32':
|
|
37
|
+
return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), dirName);
|
|
38
|
+
default:
|
|
39
|
+
return path.join(process.env.XDG_DATA_HOME || path.join(home, '.local', 'share'), dirName);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Detect project root directory.
|
|
44
|
+
*
|
|
45
|
+
* Resolution order: git root > project marker files (.git, package.json, etc.) > cwd fallback.
|
|
46
|
+
* This is an independent function — does not require createKit().
|
|
47
|
+
*/
|
|
48
|
+
export async function detectProjectRoot(cwd) {
|
|
49
|
+
const startDir = cwd ?? process.cwd();
|
|
50
|
+
// Try git root first
|
|
51
|
+
try {
|
|
52
|
+
const { stdout } = await execFileAsync('git', ['rev-parse', '--show-toplevel'], { cwd: startDir });
|
|
53
|
+
const gitRoot = stdout.trim();
|
|
54
|
+
if (gitRoot)
|
|
55
|
+
return gitRoot;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Not a git repo or git not available
|
|
59
|
+
}
|
|
60
|
+
// Walk up looking for project markers
|
|
61
|
+
let current = path.resolve(startDir);
|
|
62
|
+
while (true) {
|
|
63
|
+
for (const marker of PROJECT_ROOT_MARKERS) {
|
|
64
|
+
try {
|
|
65
|
+
await fs.access(path.join(current, marker));
|
|
66
|
+
return current;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const parent = path.dirname(current);
|
|
73
|
+
if (parent === current)
|
|
74
|
+
break;
|
|
75
|
+
current = parent;
|
|
76
|
+
}
|
|
77
|
+
return path.resolve(startDir);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=platform.js.map
|