@defai.digital/ax-cli 3.8.6 → 3.8.8
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 +28 -393
- package/config-defaults/models.yaml +0 -24
- package/config-defaults/settings.yaml +16 -16
- package/dist/agent/dependency-resolver.js +22 -1
- package/dist/agent/dependency-resolver.js.map +1 -1
- package/dist/agent/llm-agent.d.ts +23 -2
- package/dist/agent/llm-agent.js +126 -122
- package/dist/agent/llm-agent.js.map +1 -1
- package/dist/agent/loop-detector.d.ts +70 -0
- package/dist/agent/loop-detector.js +339 -0
- package/dist/agent/loop-detector.js.map +1 -0
- package/dist/agent/progress-tracker.d.ts +94 -0
- package/dist/agent/progress-tracker.js +222 -0
- package/dist/agent/progress-tracker.js.map +1 -0
- package/dist/agent/status-reporter.js +2 -2
- package/dist/agent/status-reporter.js.map +1 -1
- package/dist/agent/subagent.js +7 -3
- package/dist/agent/subagent.js.map +1 -1
- package/dist/analyzers/architecture/project-structure-scanner.js +6 -2
- package/dist/analyzers/architecture/project-structure-scanner.js.map +1 -1
- package/dist/analyzers/git/churn-calculator.js +2 -1
- package/dist/analyzers/git/churn-calculator.js.map +1 -1
- package/dist/checkpoint/manager.js +18 -4
- package/dist/checkpoint/manager.js.map +1 -1
- package/dist/checkpoint/storage.d.ts +6 -0
- package/dist/checkpoint/storage.js +96 -49
- package/dist/checkpoint/storage.js.map +1 -1
- package/dist/commands/cache.js +8 -6
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/doctor.js +19 -27
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/mcp-migrate.js +6 -5
- package/dist/commands/mcp-migrate.js.map +1 -1
- package/dist/commands/mcp.js +14 -2
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/models.js +8 -12
- package/dist/commands/models.js.map +1 -1
- package/dist/commands/plan.js +1 -10
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/setup.js +4 -3
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.js +40 -14
- package/dist/commands/status.js.map +1 -1
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +16 -4
- package/dist/constants.js.map +1 -1
- package/dist/hooks/hook-runner.d.ts +138 -0
- package/dist/hooks/hook-runner.js +429 -0
- package/dist/hooks/hook-runner.js.map +1 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.js +3 -21
- package/dist/index.js.map +1 -1
- package/dist/llm/client.d.ts +9 -0
- package/dist/llm/client.js +306 -45
- package/dist/llm/client.js.map +1 -1
- package/dist/llm/tools.js +2 -39
- package/dist/llm/tools.js.map +1 -1
- package/dist/llm/types.d.ts +1 -47
- package/dist/llm/types.js +0 -18
- package/dist/llm/types.js.map +1 -1
- package/dist/mcp/automatosx-loader.js +2 -1
- package/dist/mcp/automatosx-loader.js.map +1 -1
- package/dist/mcp/client-v2.d.ts +3 -0
- package/dist/mcp/client-v2.js +85 -19
- package/dist/mcp/client-v2.js.map +1 -1
- package/dist/mcp/config-migrator.js +3 -2
- package/dist/mcp/config-migrator.js.map +1 -1
- package/dist/mcp/config-v2.d.ts +5 -0
- package/dist/mcp/config-v2.js +26 -0
- package/dist/mcp/config-v2.js.map +1 -1
- package/dist/mcp/error-formatter.js +4 -1
- package/dist/mcp/error-formatter.js.map +1 -1
- package/dist/mcp/health.js +1 -1
- package/dist/mcp/health.js.map +1 -1
- package/dist/mcp/reconnection.js +2 -1
- package/dist/mcp/reconnection.js.map +1 -1
- package/dist/mcp/registry.js +3 -2
- package/dist/mcp/registry.js.map +1 -1
- package/dist/mcp/resources.js +2 -1
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/validation.js +9 -0
- package/dist/mcp/validation.js.map +1 -1
- package/dist/memory/context-store.js +4 -6
- package/dist/memory/context-store.js.map +1 -1
- package/dist/memory/types.d.ts +2 -0
- package/dist/memory/types.js +4 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/permissions/index.d.ts +6 -0
- package/dist/permissions/index.js +7 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/permission-manager.d.ts +145 -0
- package/dist/permissions/permission-manager.js +401 -0
- package/dist/permissions/permission-manager.js.map +1 -0
- package/dist/planner/plan-storage.js +3 -2
- package/dist/planner/plan-storage.js.map +1 -1
- package/dist/planner/task-planner.js +2 -1
- package/dist/planner/task-planner.js.map +1 -1
- package/dist/planner/types.d.ts +6 -6
- package/dist/schemas/settings-schemas.d.ts +0 -14
- package/dist/schemas/settings-schemas.js +0 -10
- package/dist/schemas/settings-schemas.js.map +1 -1
- package/dist/schemas/tool-schemas.d.ts +2 -2
- package/dist/schemas/yaml-schemas.d.ts +15 -0
- package/dist/schemas/yaml-schemas.js +3 -0
- package/dist/schemas/yaml-schemas.js.map +1 -1
- package/dist/tools/bash.js +35 -10
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/confirmation-tool.js +3 -2
- package/dist/tools/confirmation-tool.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.js +2 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/search.js +12 -13
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/text-editor.d.ts +46 -0
- package/dist/tools/text-editor.js +455 -11
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/tools/todo-tool.js +5 -4
- package/dist/tools/todo-tool.js.map +1 -1
- package/dist/ui/components/chat-input.js +10 -1
- package/dist/ui/components/chat-input.js.map +1 -1
- package/dist/ui/components/chat-interface.js +1 -0
- package/dist/ui/components/chat-interface.js.map +1 -1
- package/dist/ui/components/tool-group-display.js +0 -6
- package/dist/ui/components/tool-group-display.js.map +1 -1
- package/dist/ui/hooks/use-input-handler.js +7 -6
- package/dist/ui/hooks/use-input-handler.js.map +1 -1
- package/dist/ui/hooks/use-input-history.js +21 -13
- package/dist/ui/hooks/use-input-history.js.map +1 -1
- package/dist/ui/utils/tool-grouper.d.ts +1 -2
- package/dist/ui/utils/tool-grouper.js +4 -15
- package/dist/ui/utils/tool-grouper.js.map +1 -1
- package/dist/utils/api-error.d.ts +61 -0
- package/dist/utils/api-error.js +176 -0
- package/dist/utils/api-error.js.map +1 -0
- package/dist/utils/audit-logger.js +2 -1
- package/dist/utils/audit-logger.js.map +1 -1
- package/dist/utils/auto-accept-logger.js +3 -3
- package/dist/utils/auto-accept-logger.js.map +1 -1
- package/dist/utils/config-loader.d.ts +3 -0
- package/dist/utils/config-loader.js +27 -2
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/encryption.js +2 -1
- package/dist/utils/encryption.js.map +1 -1
- package/dist/utils/file-cache.js +4 -2
- package/dist/utils/file-cache.js.map +1 -1
- package/dist/utils/history-migration.js +5 -4
- package/dist/utils/history-migration.js.map +1 -1
- package/dist/utils/onboarding-manager.js +2 -1
- package/dist/utils/onboarding-manager.js.map +1 -1
- package/dist/utils/path-helpers.d.ts +8 -0
- package/dist/utils/path-helpers.js +35 -0
- package/dist/utils/path-helpers.js.map +1 -0
- package/dist/utils/path-security.js +3 -2
- package/dist/utils/path-security.js.map +1 -1
- package/dist/utils/retry-helper.d.ts +61 -0
- package/dist/utils/retry-helper.js +206 -0
- package/dist/utils/retry-helper.js.map +1 -0
- package/dist/utils/settings-manager.d.ts +1 -21
- package/dist/utils/settings-manager.js +2 -82
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/utils/streaming-analyzer.d.ts +2 -13
- package/dist/utils/streaming-analyzer.js +3 -25
- package/dist/utils/streaming-analyzer.js.map +1 -1
- package/dist/utils/token-counter.d.ts +13 -1
- package/dist/utils/token-counter.js +31 -6
- package/dist/utils/token-counter.js.map +1 -1
- package/package.json +3 -2
- package/packages/schemas/README.md +1 -1
- package/packages/schemas/package.json +1 -1
- package/dist/tools/web-search/cache.d.ts +0 -62
- package/dist/tools/web-search/cache.js +0 -105
- package/dist/tools/web-search/cache.js.map +0 -1
- package/dist/tools/web-search/engines/crates.d.ts +0 -19
- package/dist/tools/web-search/engines/crates.js +0 -87
- package/dist/tools/web-search/engines/crates.js.map +0 -1
- package/dist/tools/web-search/engines/npm.d.ts +0 -18
- package/dist/tools/web-search/engines/npm.js +0 -86
- package/dist/tools/web-search/engines/npm.js.map +0 -1
- package/dist/tools/web-search/engines/pypi.d.ts +0 -18
- package/dist/tools/web-search/engines/pypi.js +0 -75
- package/dist/tools/web-search/engines/pypi.js.map +0 -1
- package/dist/tools/web-search/index.d.ts +0 -11
- package/dist/tools/web-search/index.js +0 -11
- package/dist/tools/web-search/index.js.map +0 -1
- package/dist/tools/web-search/router.d.ts +0 -34
- package/dist/tools/web-search/router.js +0 -245
- package/dist/tools/web-search/router.js.map +0 -1
- package/dist/tools/web-search/types.d.ts +0 -45
- package/dist/tools/web-search/types.js +0 -6
- package/dist/tools/web-search/types.js.map +0 -1
- package/dist/tools/web-search/web-search-tool.d.ts +0 -51
- package/dist/tools/web-search/web-search-tool.js +0 -246
- package/dist/tools/web-search/web-search-tool.js.map +0 -1
package/dist/constants.d.ts
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
* Application-wide constants
|
|
3
3
|
* Now loaded from YAML configuration files for better maintainability
|
|
4
4
|
*/
|
|
5
|
+
/** Configuration directory name */
|
|
6
|
+
export declare const CONFIG_DIR_NAME = ".ax-cli";
|
|
5
7
|
export declare const CONFIG_PATHS: {
|
|
8
|
+
/** Configuration directory name */
|
|
9
|
+
readonly DIR_NAME: ".ax-cli";
|
|
6
10
|
/** User-level settings directory */
|
|
7
11
|
readonly USER_DIR: string;
|
|
8
12
|
/** User-level configuration file */
|
|
@@ -11,12 +15,16 @@ export declare const CONFIG_PATHS: {
|
|
|
11
15
|
readonly PROJECT_DIR: string;
|
|
12
16
|
/** Project-level settings file */
|
|
13
17
|
readonly PROJECT_SETTINGS: string;
|
|
18
|
+
/** AutomatosX temporary files directory */
|
|
19
|
+
readonly AUTOMATOSX_TMP: string;
|
|
14
20
|
};
|
|
15
21
|
export declare const AGENT_CONFIG: {
|
|
16
22
|
readonly MAX_TOOL_ROUNDS: number;
|
|
17
23
|
readonly DEFAULT_TIMEOUT: number;
|
|
18
24
|
readonly DEFAULT_MAX_TOKENS: number;
|
|
19
25
|
readonly MAX_RECENT_TOOL_CALLS: number;
|
|
26
|
+
/** Maximum number of messages to keep in chat history */
|
|
27
|
+
readonly MAX_MESSAGES: number;
|
|
20
28
|
readonly LOOP_DETECTION_THRESHOLD: number;
|
|
21
29
|
readonly ENABLE_LOOP_DETECTION: boolean;
|
|
22
30
|
};
|
|
@@ -80,6 +88,10 @@ export declare const TOKEN_CONFIG: {
|
|
|
80
88
|
export declare const CACHE_CONFIG: {
|
|
81
89
|
readonly DEFAULT_MAX_SIZE: number;
|
|
82
90
|
readonly DEFAULT_TTL: number;
|
|
91
|
+
/** Max entries for tool call arguments cache */
|
|
92
|
+
readonly TOOL_ARGS_CACHE_MAX_SIZE: number;
|
|
93
|
+
/** Number of entries to remove when cache is full */
|
|
94
|
+
readonly TOOL_ARGS_CACHE_PRUNE_COUNT: number;
|
|
83
95
|
};
|
|
84
96
|
export declare const PERF_CONFIG: {
|
|
85
97
|
readonly DEBOUNCE_DELAY: number;
|
package/dist/constants.js
CHANGED
|
@@ -9,16 +9,22 @@ import { loadModelsConfig, loadSettingsConfig, loadMessagesConfig, formatMessage
|
|
|
9
9
|
const modelsYaml = loadModelsConfig();
|
|
10
10
|
const settingsYaml = loadSettingsConfig();
|
|
11
11
|
const messagesYaml = loadMessagesConfig();
|
|
12
|
+
/** Configuration directory name */
|
|
13
|
+
export const CONFIG_DIR_NAME = '.ax-cli';
|
|
12
14
|
// Configuration Paths
|
|
13
15
|
export const CONFIG_PATHS = {
|
|
16
|
+
/** Configuration directory name */
|
|
17
|
+
DIR_NAME: CONFIG_DIR_NAME,
|
|
14
18
|
/** User-level settings directory */
|
|
15
|
-
USER_DIR: join(homedir(),
|
|
19
|
+
USER_DIR: join(homedir(), CONFIG_DIR_NAME),
|
|
16
20
|
/** User-level configuration file */
|
|
17
|
-
USER_CONFIG: join(homedir(),
|
|
21
|
+
USER_CONFIG: join(homedir(), CONFIG_DIR_NAME, 'config.json'),
|
|
18
22
|
/** Project-level settings directory */
|
|
19
|
-
PROJECT_DIR: join(process.cwd(),
|
|
23
|
+
PROJECT_DIR: join(process.cwd(), CONFIG_DIR_NAME),
|
|
20
24
|
/** Project-level settings file */
|
|
21
|
-
PROJECT_SETTINGS: join(process.cwd(),
|
|
25
|
+
PROJECT_SETTINGS: join(process.cwd(), CONFIG_DIR_NAME, 'settings.json'),
|
|
26
|
+
/** AutomatosX temporary files directory */
|
|
27
|
+
AUTOMATOSX_TMP: join(homedir(), 'automatosx', 'tmp'),
|
|
22
28
|
};
|
|
23
29
|
// Agent Configuration
|
|
24
30
|
export const AGENT_CONFIG = {
|
|
@@ -26,6 +32,8 @@ export const AGENT_CONFIG = {
|
|
|
26
32
|
DEFAULT_TIMEOUT: settingsYaml.agent.default_timeout,
|
|
27
33
|
DEFAULT_MAX_TOKENS: settingsYaml.agent.default_max_tokens,
|
|
28
34
|
MAX_RECENT_TOOL_CALLS: settingsYaml.agent.max_recent_tool_calls,
|
|
35
|
+
/** Maximum number of messages to keep in chat history */
|
|
36
|
+
MAX_MESSAGES: settingsYaml.agent.max_messages || 500,
|
|
29
37
|
LOOP_DETECTION_THRESHOLD: settingsYaml.agent.loop_detection_threshold,
|
|
30
38
|
ENABLE_LOOP_DETECTION: settingsYaml.agent.enable_loop_detection,
|
|
31
39
|
};
|
|
@@ -113,6 +121,10 @@ export const TOKEN_CONFIG = {
|
|
|
113
121
|
export const CACHE_CONFIG = {
|
|
114
122
|
DEFAULT_MAX_SIZE: settingsYaml.cache.default_max_size,
|
|
115
123
|
DEFAULT_TTL: settingsYaml.cache.default_ttl,
|
|
124
|
+
/** Max entries for tool call arguments cache */
|
|
125
|
+
TOOL_ARGS_CACHE_MAX_SIZE: settingsYaml.cache.tool_args_cache_max_size || 500,
|
|
126
|
+
/** Number of entries to remove when cache is full */
|
|
127
|
+
TOOL_ARGS_CACHE_PRUNE_COUNT: settingsYaml.cache.tool_args_cache_prune_count || 100,
|
|
116
128
|
};
|
|
117
129
|
// Performance Monitoring
|
|
118
130
|
export const PERF_CONFIG = {
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEnH,sCAAsC;AACtC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;AACtC,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;AAC1C,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;AAE1C,sBAAsB;AACtB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,oCAAoC;IACpC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEnH,sCAAsC;AACtC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;AACtC,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;AAC1C,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;AAE1C,mCAAmC;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AAEzC,sBAAsB;AACtB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,mCAAmC;IACnC,QAAQ,EAAE,eAAe;IACzB,oCAAoC;IACpC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC;IAC1C,oCAAoC;IACpC,WAAW,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC;IAC5D,uCAAuC;IACvC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC;IACjD,kCAAkC;IAClC,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,EAAE,eAAe,CAAC;IACvE,2CAA2C;IAC3C,cAAc,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC;CAC5C,CAAC;AAEX,sBAAsB;AACtB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,eAAe,EAAE,YAAY,CAAC,KAAK,CAAC,eAAe;IACnD,eAAe,EAAE,YAAY,CAAC,KAAK,CAAC,eAAe;IACnD,kBAAkB,EAAE,YAAY,CAAC,KAAK,CAAC,kBAAkB;IACzD,qBAAqB,EAAE,YAAY,CAAC,KAAK,CAAC,qBAAqB;IAC/D,yDAAyD;IACzD,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,YAAY,IAAI,GAAG;IACpD,wBAAwB,EAAE,YAAY,CAAC,KAAK,CAAC,wBAAwB;IACrE,qBAAqB,EAAE,YAAY,CAAC,KAAK,CAAC,qBAAqB;CACvD,CAAC;AAEX,8CAA8C;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;IACvF,GAAG,CAAC,GAAG,CAAC,GAAG;QACT,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,aAAa,EAAE,KAAK,CAAC,cAAc;QACnC,eAAe,EAAE,KAAK,CAAC,iBAAiB;QACxC,gBAAgB,EAAE,KAAK,CAAC,kBAAkB;QAC1C,gBAAgB,EAAE,KAAK,CAAC,iBAAiB;QACzC,cAAc,EAAE,KAAK,CAAC,eAAe,IAAI,KAAK;QAC9C,kBAAkB,EAAE,KAAK,CAAC,mBAAmB;QAC7C,gBAAgB,EAAE;YAChB,GAAG,EAAE,KAAK,CAAC,iBAAiB,CAAC,GAAG;YAChC,GAAG,EAAE,KAAK,CAAC,iBAAiB,CAAC,GAAG;SACjC;QACD,eAAe,EAAE,KAAK,CAAC,gBAAgB;KACxC,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC,EAAE,EAUD,CAAC,CAAC;AAGJ,MAAM,CAAC,MAAM,aAAa,GAAmB,UAAU,CAAC,aAA+B,CAAC;AAExF,kBAAkB;AAClB,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,aAAa,EAAE,YAAY,CAAC,IAAI,CAAC,aAAa;IAC9C,eAAe,EAAE,YAAY,CAAC,IAAI,CAAC,eAAe;IAClD,kBAAkB,EAAE,YAAY,CAAC,IAAI,CAAC,kBAAkB;CAChD,CAAC;AAEX,wBAAwB;AACxB,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,gBAAgB,EAAE,YAAY,CAAC,OAAO,CAAC,gBAAgB;CAC/C,CAAC;AAEX,oBAAoB;AACpB,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,WAAW,EAAE,YAAY,CAAC,GAAG,CAAC,WAAW;IACzC,cAAc,EAAE,YAAY,CAAC,GAAG,CAAC,cAAc;IAC/C,eAAe,EAAE,YAAY,CAAC,GAAG,CAAC,eAAe;IACjD,uBAAuB,EAAE,YAAY,CAAC,GAAG,CAAC,uBAAuB;IACjE,gBAAgB,EAAE,YAAY,CAAC,GAAG,CAAC,gBAAgB;IACnD,kBAAkB,EAAE,YAAY,CAAC,GAAG,CAAC,kBAAkB;CAC/C,CAAC;AAEX,mBAAmB;AACnB,MAAM,CAAN,IAAY,cAOX;AAPD,WAAY,cAAc;IACxB,sDAAsD;IACtD,qDAAS,CAAA;IACT,kCAAkC;IAClC,yDAAW,CAAA;IACX,8CAA8C;IAC9C,yDAAW,CAAA;AACb,CAAC,EAPW,cAAc,KAAd,cAAc,QAOzB;AAED,wCAAwC;AACxC,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,QAAQ,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5B,KAAK,OAAO;YACV,OAAO,cAAc,CAAC,KAAK,CAAC;QAC9B,KAAK,SAAS;YACZ,OAAO,cAAc,CAAC,OAAO,CAAC;QAChC,KAAK,SAAS;YACZ,OAAO,cAAc,CAAC,OAAO,CAAC;QAChC;YACE,OAAO,cAAc,CAAC,KAAK,CAAC;IAChC,CAAC;AACH,CAAC;AAED,mBAAmB;AACnB,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,sBAAsB,EAAE,YAAY,CAAC,EAAE,CAAC,sBAAsB;IAC9D,yBAAyB,EAAE,YAAY,CAAC,EAAE,CAAC,yBAAyB;IACpE,qBAAqB;IACrB,uBAAuB,EAAE,mBAAmB,CAAC,YAAY,CAAC,EAAE,CAAC,eAAe,IAAI,OAAO,CAAC;IACxF,gBAAgB,EAAE,YAAY,CAAC,EAAE,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI;IAC1G,cAAc,EAAE,YAAY,CAAC,EAAE,CAAC,cAAc,IAAI,EAAE;IACpD,iBAAiB,EAAE,YAAY,CAAC,EAAE,CAAC,iBAAiB,IAAI,GAAG;CACnD,CAAC;AAEX,iBAAiB;AACjB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,kBAAkB,EAAE,YAAY,CAAC,KAAK,CAAC,kBAAkB;IACzD,wBAAwB,EAAE,YAAY,CAAC,KAAK,CAAC,wBAAwB;IACrE,aAAa,EAAE,YAAY,CAAC,KAAK,CAAC,aAAa;IAC/C,gBAAgB,EAAE,YAAY,CAAC,KAAK,CAAC,gBAAgB;IACrD,cAAc,EAAE,YAAY,CAAC,KAAK,CAAC,cAAc;IACjD,wBAAwB,EAAE,YAAY,CAAC,KAAK,CAAC,wBAAwB;CAC7D,CAAC;AAEX,sBAAsB;AACtB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB,EAAE,YAAY,CAAC,KAAK,CAAC,gBAAgB;IACrD,WAAW,EAAE,YAAY,CAAC,KAAK,CAAC,WAAW;IAC3C,gDAAgD;IAChD,wBAAwB,EAAE,YAAY,CAAC,KAAK,CAAC,wBAAwB,IAAI,GAAG;IAC5E,qDAAqD;IACrD,2BAA2B,EAAE,YAAY,CAAC,KAAK,CAAC,2BAA2B,IAAI,GAAG;CAC1E,CAAC;AAEX,yBAAyB;AACzB,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,cAAc,EAAE,YAAY,CAAC,WAAW,CAAC,cAAc;IACvD,cAAc,EAAE,YAAY,CAAC,WAAW,CAAC,cAAc;IACvD,wBAAwB,EAAE,YAAY,CAAC,WAAW,CAAC,wBAAwB;CACnE,CAAC;AAEX,aAAa;AACb,MAAM,CAAC,MAAM,UAAU,GAAG,YAAY,CAAC,UAAoC,CAAC;AAE5E,uCAAuC;AACvC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,gBAAgB,EAAE,YAAY,CAAC,MAAM,CAAC,gBAAgB;IACtD,yBAAyB,EAAE,YAAY,CAAC,MAAM,CAAC,yBAAyB;IACxE,cAAc,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC;IACrG,oBAAoB,EAAE,CAAC,UAAkB,EAAE,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,oBAAoB,EAAE,EAAE,UAAU,EAAE,CAAC;IACrH,cAAc,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC;IACrG,mBAAmB,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,CAAC;CACrG,CAAC;AAEX,yCAAyC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,kCAAkC;IAClC,OAAO,EAAE,IAAI;IAEb,8FAA8F;IAC9F,mBAAmB,EAAE,CAAC;IAEtB,8BAA8B;IAC9B,UAAU,EAAE,EAAE;IAEd,iDAAiD;IACjD,gBAAgB,EAAE,MAAM;IAExB,8BAA8B;IAC9B,mBAAmB,EAAE,CAAC;IAEtB,2CAA2C;IAC3C,eAAe,EAAE,IAAI;IAErB,8FAA8F;IAC9F,qBAAqB,EAAE,KAAK;IAE5B,4CAA4C;IAC5C,0BAA0B,EAAE,IAAI;IAEhC,mFAAmF;IACnF,qBAAqB,EAAE,IAAI;IAE3B,mCAAmC;IACnC,iBAAiB,EAAE,IAAI;IAEvB,8CAA8C;IAC9C,yBAAyB,EAAE,EAAE;IAE7B,oCAAoC;IACpC,mBAAmB,EAAE,EAAE;IAEvB,6BAA6B;IAC7B,YAAY,EAAE,IAAI;IAElB,0EAA0E;IAC1E,WAAW,EAAE,IAAI;IAEjB,oDAAoD;IACpD,cAAc,EAAE,KAAK;CACb,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook System (Phase 4)
|
|
3
|
+
*
|
|
4
|
+
* Based on Claude Code's hook architecture.
|
|
5
|
+
* Allows custom logic intercept points for tool execution.
|
|
6
|
+
*
|
|
7
|
+
* Hook Types:
|
|
8
|
+
* - PreToolUse: Before tool execution (validation, permission checks, logging)
|
|
9
|
+
* - PostToolUse: After tool execution (result transformation, analytics)
|
|
10
|
+
* - OnError: On tool failure (custom error handling, retry logic)
|
|
11
|
+
* - SessionStart: On session begin (setup, context loading)
|
|
12
|
+
* - SessionEnd: On session end (cleanup, reporting)
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
import { LLMToolCall } from '../llm/client.js';
|
|
16
|
+
import { ToolResult } from '../types/index.js';
|
|
17
|
+
/**
|
|
18
|
+
* Hook execution timing
|
|
19
|
+
*/
|
|
20
|
+
export declare enum HookType {
|
|
21
|
+
PreToolUse = "pre_tool_use",
|
|
22
|
+
PostToolUse = "post_tool_use",
|
|
23
|
+
OnError = "on_error",
|
|
24
|
+
SessionStart = "session_start",
|
|
25
|
+
SessionEnd = "session_end"
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Input passed to hook scripts (via stdin as JSON)
|
|
29
|
+
*/
|
|
30
|
+
export interface HookInput {
|
|
31
|
+
event: HookType;
|
|
32
|
+
tool: string;
|
|
33
|
+
args: Record<string, unknown>;
|
|
34
|
+
session: {
|
|
35
|
+
id: string;
|
|
36
|
+
startedAt: string;
|
|
37
|
+
messageCount: number;
|
|
38
|
+
};
|
|
39
|
+
result?: ToolResult;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Output expected from hook scripts (via stdout as JSON)
|
|
44
|
+
*/
|
|
45
|
+
export interface HookOutput {
|
|
46
|
+
action: 'continue' | 'block' | 'modify';
|
|
47
|
+
reason?: string;
|
|
48
|
+
modifiedArgs?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Hook configuration
|
|
52
|
+
*/
|
|
53
|
+
interface HookDefinition {
|
|
54
|
+
name: string;
|
|
55
|
+
script: string;
|
|
56
|
+
tools: string[];
|
|
57
|
+
timeout?: number;
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Hook configuration file structure
|
|
62
|
+
*/
|
|
63
|
+
interface HookConfig {
|
|
64
|
+
hooks: {
|
|
65
|
+
pre_tool_use?: HookDefinition[];
|
|
66
|
+
post_tool_use?: HookDefinition[];
|
|
67
|
+
on_error?: HookDefinition[];
|
|
68
|
+
session_start?: HookDefinition[];
|
|
69
|
+
session_end?: HookDefinition[];
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Hook Runner
|
|
74
|
+
*
|
|
75
|
+
* Executes hooks at various points in tool execution lifecycle.
|
|
76
|
+
*/
|
|
77
|
+
export declare class HookRunner extends EventEmitter {
|
|
78
|
+
private config;
|
|
79
|
+
private configPath;
|
|
80
|
+
private sessionId;
|
|
81
|
+
private sessionStartedAt;
|
|
82
|
+
private messageCount;
|
|
83
|
+
/** Running hook processes for cleanup */
|
|
84
|
+
private runningProcesses;
|
|
85
|
+
constructor();
|
|
86
|
+
/**
|
|
87
|
+
* Initialize the hook runner (load config)
|
|
88
|
+
*/
|
|
89
|
+
initialize(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Run pre-tool-use hooks
|
|
92
|
+
* Returns modified args or blocks execution
|
|
93
|
+
*/
|
|
94
|
+
runPreToolUse(toolCall: LLMToolCall, args: Record<string, unknown>): Promise<HookOutput>;
|
|
95
|
+
/**
|
|
96
|
+
* Run post-tool-use hooks
|
|
97
|
+
*/
|
|
98
|
+
runPostToolUse(toolCall: LLMToolCall, args: Record<string, unknown>, result: ToolResult): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Run on-error hooks
|
|
101
|
+
*/
|
|
102
|
+
runOnError(toolCall: LLMToolCall, args: Record<string, unknown>, error: string): Promise<HookOutput>;
|
|
103
|
+
/**
|
|
104
|
+
* Run session start hooks
|
|
105
|
+
*/
|
|
106
|
+
private runSessionStartHooks;
|
|
107
|
+
/**
|
|
108
|
+
* Run session end hooks
|
|
109
|
+
*/
|
|
110
|
+
runSessionEndHooks(): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Increment message count (for session tracking)
|
|
113
|
+
*/
|
|
114
|
+
incrementMessageCount(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Get current hook configuration
|
|
117
|
+
*/
|
|
118
|
+
getConfig(): HookConfig;
|
|
119
|
+
/**
|
|
120
|
+
* Reload configuration from file
|
|
121
|
+
*/
|
|
122
|
+
reloadConfig(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Cleanup running processes
|
|
125
|
+
*/
|
|
126
|
+
dispose(): Promise<void>;
|
|
127
|
+
private getHooksForType;
|
|
128
|
+
private shouldRunHook;
|
|
129
|
+
private executeHook;
|
|
130
|
+
private resolveScriptPath;
|
|
131
|
+
private getSessionInfo;
|
|
132
|
+
private generateSessionId;
|
|
133
|
+
private loadConfig;
|
|
134
|
+
}
|
|
135
|
+
export declare function getHookRunner(): HookRunner;
|
|
136
|
+
export declare function initializeHookRunner(): Promise<HookRunner>;
|
|
137
|
+
export declare function disposeHookRunner(): Promise<void>;
|
|
138
|
+
export {};
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook System (Phase 4)
|
|
3
|
+
*
|
|
4
|
+
* Based on Claude Code's hook architecture.
|
|
5
|
+
* Allows custom logic intercept points for tool execution.
|
|
6
|
+
*
|
|
7
|
+
* Hook Types:
|
|
8
|
+
* - PreToolUse: Before tool execution (validation, permission checks, logging)
|
|
9
|
+
* - PostToolUse: After tool execution (result transformation, analytics)
|
|
10
|
+
* - OnError: On tool failure (custom error handling, retry logic)
|
|
11
|
+
* - SessionStart: On session begin (setup, context loading)
|
|
12
|
+
* - SessionEnd: On session end (cleanup, reporting)
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
import { spawn } from 'child_process';
|
|
16
|
+
import { readFile, stat } from 'fs/promises';
|
|
17
|
+
import { homedir } from 'os';
|
|
18
|
+
import { join, dirname, resolve } from 'path';
|
|
19
|
+
import * as yaml from 'js-yaml';
|
|
20
|
+
import { extractErrorMessage } from '../utils/error-handler.js';
|
|
21
|
+
import { CONFIG_DIR_NAME } from '../constants.js';
|
|
22
|
+
/**
|
|
23
|
+
* Hook execution timing
|
|
24
|
+
*/
|
|
25
|
+
export var HookType;
|
|
26
|
+
(function (HookType) {
|
|
27
|
+
HookType["PreToolUse"] = "pre_tool_use";
|
|
28
|
+
HookType["PostToolUse"] = "post_tool_use";
|
|
29
|
+
HookType["OnError"] = "on_error";
|
|
30
|
+
HookType["SessionStart"] = "session_start";
|
|
31
|
+
HookType["SessionEnd"] = "session_end";
|
|
32
|
+
})(HookType || (HookType = {}));
|
|
33
|
+
/**
|
|
34
|
+
* Default hook configuration
|
|
35
|
+
*/
|
|
36
|
+
const DEFAULT_CONFIG = {
|
|
37
|
+
hooks: {
|
|
38
|
+
pre_tool_use: [],
|
|
39
|
+
post_tool_use: [],
|
|
40
|
+
on_error: [],
|
|
41
|
+
session_start: [],
|
|
42
|
+
session_end: [],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Hook exit codes
|
|
47
|
+
*/
|
|
48
|
+
const EXIT_CODES = {
|
|
49
|
+
CONTINUE: 0, // Continue (approve)
|
|
50
|
+
ERROR: 1, // Error (log and continue)
|
|
51
|
+
BLOCK: 2, // Block execution
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Hook Runner
|
|
55
|
+
*
|
|
56
|
+
* Executes hooks at various points in tool execution lifecycle.
|
|
57
|
+
*/
|
|
58
|
+
export class HookRunner extends EventEmitter {
|
|
59
|
+
config;
|
|
60
|
+
configPath;
|
|
61
|
+
sessionId;
|
|
62
|
+
sessionStartedAt;
|
|
63
|
+
messageCount = 0;
|
|
64
|
+
/** Running hook processes for cleanup */
|
|
65
|
+
runningProcesses = new Set();
|
|
66
|
+
constructor() {
|
|
67
|
+
super();
|
|
68
|
+
this.configPath = join(homedir(), CONFIG_DIR_NAME, 'hooks.yaml');
|
|
69
|
+
this.config = DEFAULT_CONFIG;
|
|
70
|
+
this.sessionId = this.generateSessionId();
|
|
71
|
+
this.sessionStartedAt = new Date().toISOString();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Initialize the hook runner (load config)
|
|
75
|
+
*/
|
|
76
|
+
async initialize() {
|
|
77
|
+
try {
|
|
78
|
+
await this.loadConfig();
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
// Use default config if loading fails
|
|
82
|
+
console.warn('Using default hook configuration');
|
|
83
|
+
}
|
|
84
|
+
// Run session start hooks
|
|
85
|
+
await this.runSessionStartHooks();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Run pre-tool-use hooks
|
|
89
|
+
* Returns modified args or blocks execution
|
|
90
|
+
*/
|
|
91
|
+
async runPreToolUse(toolCall, args) {
|
|
92
|
+
const hooks = this.getHooksForType(HookType.PreToolUse);
|
|
93
|
+
let currentArgs = { ...args };
|
|
94
|
+
for (const hook of hooks) {
|
|
95
|
+
if (!this.shouldRunHook(hook, toolCall.function.name))
|
|
96
|
+
continue;
|
|
97
|
+
const input = {
|
|
98
|
+
event: HookType.PreToolUse,
|
|
99
|
+
tool: toolCall.function.name,
|
|
100
|
+
args: currentArgs,
|
|
101
|
+
session: this.getSessionInfo(),
|
|
102
|
+
};
|
|
103
|
+
try {
|
|
104
|
+
const result = await this.executeHook(hook, input);
|
|
105
|
+
if (result.action === 'block') {
|
|
106
|
+
this.emit('hook:blocked', {
|
|
107
|
+
hook: hook.name,
|
|
108
|
+
tool: toolCall.function.name,
|
|
109
|
+
reason: result.reason,
|
|
110
|
+
});
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
if (result.action === 'modify' && result.modifiedArgs) {
|
|
114
|
+
currentArgs = result.modifiedArgs;
|
|
115
|
+
this.emit('hook:modified', {
|
|
116
|
+
hook: hook.name,
|
|
117
|
+
tool: toolCall.function.name,
|
|
118
|
+
originalArgs: args,
|
|
119
|
+
modifiedArgs: currentArgs,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this.emit('hook:error', {
|
|
125
|
+
hook: hook.name,
|
|
126
|
+
tool: toolCall.function.name,
|
|
127
|
+
error: extractErrorMessage(error),
|
|
128
|
+
});
|
|
129
|
+
// Continue on hook error (don't block tool execution)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
action: 'continue',
|
|
134
|
+
modifiedArgs: currentArgs,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Run post-tool-use hooks
|
|
139
|
+
*/
|
|
140
|
+
async runPostToolUse(toolCall, args, result) {
|
|
141
|
+
const hooks = this.getHooksForType(HookType.PostToolUse);
|
|
142
|
+
for (const hook of hooks) {
|
|
143
|
+
if (!this.shouldRunHook(hook, toolCall.function.name))
|
|
144
|
+
continue;
|
|
145
|
+
const input = {
|
|
146
|
+
event: HookType.PostToolUse,
|
|
147
|
+
tool: toolCall.function.name,
|
|
148
|
+
args,
|
|
149
|
+
session: this.getSessionInfo(),
|
|
150
|
+
result,
|
|
151
|
+
};
|
|
152
|
+
try {
|
|
153
|
+
await this.executeHook(hook, input);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
this.emit('hook:error', {
|
|
157
|
+
hook: hook.name,
|
|
158
|
+
tool: toolCall.function.name,
|
|
159
|
+
error: extractErrorMessage(error),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Run on-error hooks
|
|
166
|
+
*/
|
|
167
|
+
async runOnError(toolCall, args, error) {
|
|
168
|
+
const hooks = this.getHooksForType(HookType.OnError);
|
|
169
|
+
for (const hook of hooks) {
|
|
170
|
+
if (!this.shouldRunHook(hook, toolCall.function.name))
|
|
171
|
+
continue;
|
|
172
|
+
const input = {
|
|
173
|
+
event: HookType.OnError,
|
|
174
|
+
tool: toolCall.function.name,
|
|
175
|
+
args,
|
|
176
|
+
session: this.getSessionInfo(),
|
|
177
|
+
error,
|
|
178
|
+
};
|
|
179
|
+
try {
|
|
180
|
+
const result = await this.executeHook(hook, input);
|
|
181
|
+
if (result.action === 'modify' && result.modifiedArgs) {
|
|
182
|
+
// Hook wants to retry with modified args
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (hookError) {
|
|
187
|
+
this.emit('hook:error', {
|
|
188
|
+
hook: hook.name,
|
|
189
|
+
tool: toolCall.function.name,
|
|
190
|
+
error: hookError instanceof Error ? hookError.message : String(hookError),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { action: 'continue' };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Run session start hooks
|
|
198
|
+
*/
|
|
199
|
+
async runSessionStartHooks() {
|
|
200
|
+
const hooks = this.getHooksForType(HookType.SessionStart);
|
|
201
|
+
for (const hook of hooks) {
|
|
202
|
+
const input = {
|
|
203
|
+
event: HookType.SessionStart,
|
|
204
|
+
tool: '',
|
|
205
|
+
args: {},
|
|
206
|
+
session: this.getSessionInfo(),
|
|
207
|
+
};
|
|
208
|
+
try {
|
|
209
|
+
await this.executeHook(hook, input);
|
|
210
|
+
this.emit('hook:session_start', { hook: hook.name });
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.emit('hook:error', {
|
|
214
|
+
hook: hook.name,
|
|
215
|
+
error: extractErrorMessage(error),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Run session end hooks
|
|
222
|
+
*/
|
|
223
|
+
async runSessionEndHooks() {
|
|
224
|
+
const hooks = this.getHooksForType(HookType.SessionEnd);
|
|
225
|
+
for (const hook of hooks) {
|
|
226
|
+
const input = {
|
|
227
|
+
event: HookType.SessionEnd,
|
|
228
|
+
tool: '',
|
|
229
|
+
args: {},
|
|
230
|
+
session: this.getSessionInfo(),
|
|
231
|
+
};
|
|
232
|
+
try {
|
|
233
|
+
await this.executeHook(hook, input);
|
|
234
|
+
this.emit('hook:session_end', { hook: hook.name });
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
this.emit('hook:error', {
|
|
238
|
+
hook: hook.name,
|
|
239
|
+
error: extractErrorMessage(error),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Increment message count (for session tracking)
|
|
246
|
+
*/
|
|
247
|
+
incrementMessageCount() {
|
|
248
|
+
this.messageCount++;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get current hook configuration
|
|
252
|
+
*/
|
|
253
|
+
getConfig() {
|
|
254
|
+
return { ...this.config };
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Reload configuration from file
|
|
258
|
+
*/
|
|
259
|
+
async reloadConfig() {
|
|
260
|
+
await this.loadConfig();
|
|
261
|
+
this.emit('hook:config_reloaded');
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Cleanup running processes
|
|
265
|
+
*/
|
|
266
|
+
async dispose() {
|
|
267
|
+
// Run session end hooks
|
|
268
|
+
await this.runSessionEndHooks();
|
|
269
|
+
// Kill any running processes
|
|
270
|
+
for (const proc of this.runningProcesses) {
|
|
271
|
+
try {
|
|
272
|
+
proc.kill('SIGTERM');
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Process may have already exited
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
this.runningProcesses.clear();
|
|
279
|
+
}
|
|
280
|
+
// ============================================================================
|
|
281
|
+
// Private Methods
|
|
282
|
+
// ============================================================================
|
|
283
|
+
getHooksForType(type) {
|
|
284
|
+
const hookKey = type;
|
|
285
|
+
const hooks = this.config.hooks[hookKey] || [];
|
|
286
|
+
return hooks.filter((h) => h.enabled !== false);
|
|
287
|
+
}
|
|
288
|
+
shouldRunHook(hook, toolName) {
|
|
289
|
+
if (!hook.tools || hook.tools.length === 0)
|
|
290
|
+
return true;
|
|
291
|
+
if (hook.tools.includes('*'))
|
|
292
|
+
return true;
|
|
293
|
+
return hook.tools.some((pattern) => {
|
|
294
|
+
if (pattern.endsWith('*')) {
|
|
295
|
+
return toolName.startsWith(pattern.slice(0, -1));
|
|
296
|
+
}
|
|
297
|
+
return toolName === pattern;
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
async executeHook(hook, input) {
|
|
301
|
+
const scriptPath = this.resolveScriptPath(hook.script);
|
|
302
|
+
const timeout = hook.timeout || 30000;
|
|
303
|
+
// Check if script exists
|
|
304
|
+
try {
|
|
305
|
+
await stat(scriptPath);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
throw new Error(`Hook script not found: ${scriptPath}`);
|
|
309
|
+
}
|
|
310
|
+
return new Promise((resolve, reject) => {
|
|
311
|
+
const proc = spawn(scriptPath, [], {
|
|
312
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
313
|
+
timeout,
|
|
314
|
+
shell: true,
|
|
315
|
+
});
|
|
316
|
+
this.runningProcesses.add(proc);
|
|
317
|
+
let stdout = '';
|
|
318
|
+
let stderr = '';
|
|
319
|
+
proc.stdout?.on('data', (data) => {
|
|
320
|
+
stdout += data.toString();
|
|
321
|
+
});
|
|
322
|
+
proc.stderr?.on('data', (data) => {
|
|
323
|
+
stderr += data.toString();
|
|
324
|
+
});
|
|
325
|
+
proc.on('close', (code) => {
|
|
326
|
+
this.runningProcesses.delete(proc);
|
|
327
|
+
// Handle exit codes
|
|
328
|
+
if (code === EXIT_CODES.BLOCK) {
|
|
329
|
+
resolve({
|
|
330
|
+
action: 'block',
|
|
331
|
+
reason: stderr || stdout || 'Blocked by hook',
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (code === EXIT_CODES.ERROR) {
|
|
336
|
+
// Log error but continue
|
|
337
|
+
this.emit('hook:warning', {
|
|
338
|
+
hook: hook.name,
|
|
339
|
+
message: stderr || stdout || 'Hook returned error',
|
|
340
|
+
});
|
|
341
|
+
resolve({ action: 'continue' });
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
// Try to parse JSON output
|
|
345
|
+
try {
|
|
346
|
+
if (stdout.trim()) {
|
|
347
|
+
const output = JSON.parse(stdout.trim());
|
|
348
|
+
resolve(output);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
resolve({ action: 'continue' });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
// Non-JSON output, treat as continue
|
|
356
|
+
resolve({ action: 'continue' });
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
proc.on('error', (error) => {
|
|
360
|
+
this.runningProcesses.delete(proc);
|
|
361
|
+
reject(error);
|
|
362
|
+
});
|
|
363
|
+
// Send input via stdin
|
|
364
|
+
proc.stdin?.write(JSON.stringify(input));
|
|
365
|
+
proc.stdin?.end();
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
resolveScriptPath(script) {
|
|
369
|
+
// Expand ~ to home directory
|
|
370
|
+
if (script.startsWith('~')) {
|
|
371
|
+
return join(homedir(), script.slice(1));
|
|
372
|
+
}
|
|
373
|
+
// Resolve relative paths from config directory
|
|
374
|
+
if (!script.startsWith('/')) {
|
|
375
|
+
return resolve(dirname(this.configPath), script);
|
|
376
|
+
}
|
|
377
|
+
return script;
|
|
378
|
+
}
|
|
379
|
+
getSessionInfo() {
|
|
380
|
+
return {
|
|
381
|
+
id: this.sessionId,
|
|
382
|
+
startedAt: this.sessionStartedAt,
|
|
383
|
+
messageCount: this.messageCount,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
generateSessionId() {
|
|
387
|
+
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
388
|
+
}
|
|
389
|
+
async loadConfig() {
|
|
390
|
+
try {
|
|
391
|
+
const content = await readFile(this.configPath, 'utf-8');
|
|
392
|
+
const loaded = yaml.load(content);
|
|
393
|
+
if (loaded?.hooks) {
|
|
394
|
+
this.config = {
|
|
395
|
+
hooks: {
|
|
396
|
+
...DEFAULT_CONFIG.hooks,
|
|
397
|
+
...loaded.hooks,
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
// File doesn't exist or is invalid, use defaults
|
|
404
|
+
this.config = DEFAULT_CONFIG;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Singleton instance
|
|
410
|
+
*/
|
|
411
|
+
let hookRunnerInstance = null;
|
|
412
|
+
export function getHookRunner() {
|
|
413
|
+
if (!hookRunnerInstance) {
|
|
414
|
+
hookRunnerInstance = new HookRunner();
|
|
415
|
+
}
|
|
416
|
+
return hookRunnerInstance;
|
|
417
|
+
}
|
|
418
|
+
export async function initializeHookRunner() {
|
|
419
|
+
const runner = getHookRunner();
|
|
420
|
+
await runner.initialize();
|
|
421
|
+
return runner;
|
|
422
|
+
}
|
|
423
|
+
export async function disposeHookRunner() {
|
|
424
|
+
if (hookRunnerInstance) {
|
|
425
|
+
await hookRunnerInstance.dispose();
|
|
426
|
+
hookRunnerInstance = null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
//# sourceMappingURL=hook-runner.js.map
|