@pennyfarthing/cyclist 10.0.3 → 10.2.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/dist/api/agent-load.d.ts +3 -0
- package/dist/api/agent-load.d.ts.map +1 -0
- package/dist/api/agent-load.js +124 -0
- package/dist/api/agent-load.js.map +1 -0
- package/dist/api/code-markers.d.ts +9 -0
- package/dist/api/code-markers.d.ts.map +1 -0
- package/dist/api/code-markers.js +62 -0
- package/dist/api/code-markers.js.map +1 -0
- package/dist/api/complexity.d.ts +3 -0
- package/dist/api/complexity.d.ts.map +1 -0
- package/dist/api/complexity.js +47 -0
- package/dist/api/complexity.js.map +1 -0
- package/dist/api/dead-code.d.ts +3 -0
- package/dist/api/dead-code.d.ts.map +1 -0
- package/dist/api/dead-code.js +70 -0
- package/dist/api/dead-code.js.map +1 -0
- package/dist/api/dependencies.d.ts +3 -0
- package/dist/api/dependencies.d.ts.map +1 -0
- package/dist/api/dependencies.js +43 -0
- package/dist/api/dependencies.js.map +1 -0
- package/dist/api/git.d.ts +3 -2
- package/dist/api/git.d.ts.map +1 -1
- package/dist/api/git.js +11 -6
- package/dist/api/git.js.map +1 -1
- package/dist/api/health-score.d.ts +3 -0
- package/dist/api/health-score.d.ts.map +1 -0
- package/dist/api/health-score.js +47 -0
- package/dist/api/health-score.js.map +1 -0
- package/dist/api/hotspots.d.ts.map +1 -1
- package/dist/api/hotspots.js +9 -1
- package/dist/api/hotspots.js.map +1 -1
- package/dist/api/index.d.ts +7 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +12 -2
- package/dist/api/index.js.map +1 -1
- package/dist/api/persona.d.ts +2 -0
- package/dist/api/persona.d.ts.map +1 -1
- package/dist/api/persona.js +19 -1
- package/dist/api/persona.js.map +1 -1
- package/dist/api/settings.js +1 -1
- package/dist/api/settings.js.map +1 -1
- package/dist/claude-service.d.ts +8 -2
- package/dist/claude-service.d.ts.map +1 -1
- package/dist/claude-service.js +21 -2
- package/dist/claude-service.js.map +1 -1
- package/dist/git-diff.d.ts.map +1 -1
- package/dist/git-diff.js +6 -5
- package/dist/git-diff.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +11 -2
- package/dist/main.js.map +1 -1
- package/dist/plugin-loader.d.ts +49 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +92 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/preload.js +12 -1
- package/dist/preload.js.map +1 -1
- package/dist/prime.d.ts +3 -2
- package/dist/prime.d.ts.map +1 -1
- package/dist/prime.js +25 -8
- package/dist/prime.js.map +1 -1
- package/dist/public/css/react.css +1 -1
- package/dist/public/js/react/react.js +50 -39
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19 -16
- package/dist/server.js.map +1 -1
- package/dist/sprint-data.d.ts +6 -0
- package/dist/sprint-data.d.ts.map +1 -1
- package/dist/sprint-data.js +118 -67
- package/dist/sprint-data.js.map +1 -1
- package/dist/story-parser.js +1 -1
- package/dist/story-parser.js.map +1 -1
- package/dist/theme-metadata.js +2 -2
- package/dist/theme-metadata.js.map +1 -1
- package/dist/websocket.d.ts +0 -6
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +36 -40
- package/dist/websocket.js.map +1 -1
- package/package.json +2 -1
- package/portraits/fifth-element/large/cornelius-54343.png +0 -0
- package/portraits/fifth-element/large/diva-53453.png +0 -0
- package/portraits/fifth-element/large/korben-34232.png +0 -0
- package/portraits/fifth-element/large/leeloo-54333.png +0 -0
- package/portraits/fifth-element/large/lindberg-34432.png +0 -0
- package/portraits/fifth-element/large/mondoshawan-55131.png +0 -0
- package/portraits/fifth-element/large/munro-25321.png +0 -0
- package/portraits/fifth-element/large/pacoli-45232.png +0 -0
- package/portraits/fifth-element/large/ruby-53544.png +0 -0
- package/portraits/fifth-element/large/zorg-45312.png +0 -0
- package/portraits/fifth-element/medium/cornelius-54343.png +0 -0
- package/portraits/fifth-element/medium/diva-53453.png +0 -0
- package/portraits/fifth-element/medium/korben-34232.png +0 -0
- package/portraits/fifth-element/medium/leeloo-54333.png +0 -0
- package/portraits/fifth-element/medium/lindberg-34432.png +0 -0
- package/portraits/fifth-element/medium/mondoshawan-55131.png +0 -0
- package/portraits/fifth-element/medium/munro-25321.png +0 -0
- package/portraits/fifth-element/medium/pacoli-45232.png +0 -0
- package/portraits/fifth-element/medium/ruby-53544.png +0 -0
- package/portraits/fifth-element/medium/zorg-45312.png +0 -0
- package/src/public/App.tsx +0 -2
- package/src/public/components/AgentLoadDialog.tsx +202 -0
- package/src/public/components/AgentPopup.tsx +3 -5
- package/src/public/components/ContextSparkline.tsx +56 -0
- package/src/public/components/ControlBar.tsx +140 -6
- package/src/public/components/DeadCodeDialog.tsx +169 -0
- package/src/public/components/DockviewWorkspace.tsx +0 -3
- package/src/public/components/FullFileTree.tsx +18 -4
- package/src/public/components/HealthGauge.tsx +181 -0
- package/src/public/components/MessageView.tsx +23 -6
- package/src/public/components/PersonaHeader.tsx +46 -3
- package/src/public/components/TandemPortrait.tsx +71 -0
- package/src/public/components/ToolCallBlock.tsx +21 -6
- package/src/public/components/dialogs/CodeMarkersDialog.tsx +169 -0
- package/src/public/components/dialogs/ComplexityDialog.tsx +163 -0
- package/src/public/components/dialogs/DependenciesDialog.tsx +120 -0
- package/src/public/components/dialogs/HotspotsDialog.tsx +451 -0
- package/src/public/components/dialogs/ToolDialog.tsx +43 -0
- package/src/public/components/panels/ACPanel.tsx +1 -1
- package/src/public/components/panels/AcceptanceCriteriaPanel.tsx +15 -30
- package/src/public/components/panels/DebugPanel.tsx +79 -3
- package/src/public/components/panels/GitPanel.tsx +25 -30
- package/src/public/components/panels/MessagePanel.tsx +44 -2
- package/src/public/components/panels/SettingsPanel.tsx +4 -4
- package/src/public/components/panels/SprintPanel.tsx +247 -123
- package/src/public/components/panels/index.ts +0 -1
- package/src/public/components/ui/dialog.tsx +3 -3
- package/src/public/css/theme-system.css +98 -11
- package/src/public/hooks/index.ts +4 -0
- package/src/public/hooks/useAgentLoad.ts +105 -0
- package/src/public/hooks/useCodeMarkers.ts +101 -0
- package/src/public/hooks/useColorScheme.ts +25 -10
- package/src/public/hooks/useComplexity.ts +80 -0
- package/src/public/hooks/useDeadCode.ts +99 -0
- package/src/public/hooks/useDependencies.ts +82 -0
- package/src/public/hooks/useHealthScore.ts +69 -0
- package/src/public/hooks/useHotspots.ts +11 -1
- package/src/public/hooks/usePersona.ts +26 -3
- package/src/public/hooks/useSprint.ts +7 -1
- package/src/public/styles/tailwind.css +389 -83
- package/src/public/utils/messageFilters.ts +77 -6
- package/src/public/utils/slash-commands.ts +3 -35
- package/dist/hooks/cyclist-pretooluse-hook.d.ts +0 -60
- package/dist/hooks/cyclist-pretooluse-hook.d.ts.map +0 -1
- package/dist/hooks/cyclist-pretooluse-hook.js +0 -57
- package/dist/hooks/cyclist-pretooluse-hook.js.map +0 -1
- package/dist/hooks/pretooluse-hook.d.ts +0 -89
- package/dist/hooks/pretooluse-hook.d.ts.map +0 -1
- package/dist/hooks/pretooluse-hook.js +0 -235
- package/dist/hooks/pretooluse-hook.js.map +0 -1
- package/dist/notification-sound.d.ts +0 -59
- package/dist/notification-sound.d.ts.map +0 -1
- package/dist/notification-sound.js +0 -219
- package/dist/notification-sound.js.map +0 -1
- package/src/public/types/electron.d.ts +0 -18
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Story MSSCI-12783 - Bug: Skill Content Displayed as User Message
|
|
5
5
|
*
|
|
6
|
-
* Functions to detect and filter skill
|
|
6
|
+
* Functions to detect and filter skill content from user messages.
|
|
7
7
|
* Skill content is meant for Claude's context, not for display in the UI.
|
|
8
|
+
* Detected skill invocations are replaced with a short label (e.g. "reviewer")
|
|
9
|
+
* so the user sees what happened without the raw skill dump.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -18,6 +20,36 @@ const SKILL_CONTENT_MARKERS = [
|
|
|
18
20
|
'Launching skill:',
|
|
19
21
|
] as const;
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Patterns that detect skill body content leaked as separate user messages.
|
|
25
|
+
* These are the raw skill markdown bodies that Claude Code sends as user messages
|
|
26
|
+
* after the initial <command-message> wrapper.
|
|
27
|
+
*/
|
|
28
|
+
const SKILL_BODY_PATTERNS = [
|
|
29
|
+
/^```bash\s*\npf agent start\b/, // Agent activation bash block
|
|
30
|
+
/^pf agent start\b/, // Bare agent start command
|
|
31
|
+
/^<purpose>/, // Skill body XML tags
|
|
32
|
+
/^<when-to-use>/,
|
|
33
|
+
/^<execution>/,
|
|
34
|
+
/^<critical>\s*\n/, // Skill body starting with critical block
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Agent name to human-readable label mapping.
|
|
39
|
+
*/
|
|
40
|
+
const AGENT_LABELS: Record<string, string> = {
|
|
41
|
+
sm: 'Scrum Master',
|
|
42
|
+
tea: 'Test Engineer',
|
|
43
|
+
dev: 'Developer',
|
|
44
|
+
reviewer: 'Reviewer',
|
|
45
|
+
architect: 'Architect',
|
|
46
|
+
pm: 'Product Manager',
|
|
47
|
+
'tech-writer': 'Tech Writer',
|
|
48
|
+
'ux-designer': 'UX Designer',
|
|
49
|
+
devops: 'DevOps',
|
|
50
|
+
orchestrator: 'Orchestrator',
|
|
51
|
+
};
|
|
52
|
+
|
|
21
53
|
/**
|
|
22
54
|
* Check if a message content string contains skill/command content
|
|
23
55
|
* that should be filtered from user message display.
|
|
@@ -26,13 +58,54 @@ const SKILL_CONTENT_MARKERS = [
|
|
|
26
58
|
* @returns true if the content contains skill markers and should be filtered
|
|
27
59
|
*/
|
|
28
60
|
export function isSkillContent(content: string | null | undefined): boolean {
|
|
29
|
-
// Handle null/undefined/empty gracefully
|
|
30
61
|
if (!content || typeof content !== 'string') {
|
|
31
62
|
return false;
|
|
32
63
|
}
|
|
33
64
|
|
|
34
|
-
|
|
35
|
-
|
|
65
|
+
if (SKILL_CONTENT_MARKERS.some(marker => content.includes(marker))) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const trimmed = content.trimStart();
|
|
70
|
+
return SKILL_BODY_PATTERNS.some(pattern => pattern.test(trimmed));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Extract the skill/agent name from skill content for labeling.
|
|
75
|
+
* Returns a human-readable label like "Scrum Master" or the raw skill name.
|
|
76
|
+
*/
|
|
77
|
+
export function extractSkillLabel(content: string | null | undefined): string | null {
|
|
78
|
+
if (!content || typeof content !== 'string') return null;
|
|
79
|
+
|
|
80
|
+
// Match <command-name>/foo</command-name>
|
|
81
|
+
const cmdMatch = content.match(/<command-name>\/?([^<]+)<\/command-name>/);
|
|
82
|
+
if (cmdMatch) {
|
|
83
|
+
const name = cmdMatch[1].trim();
|
|
84
|
+
return AGENT_LABELS[name] || name;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Match <command-message>foo</command-message>
|
|
88
|
+
const msgMatch = content.match(/<command-message>([^<]+)<\/command-message>/);
|
|
89
|
+
if (msgMatch) {
|
|
90
|
+
const name = msgMatch[1].trim();
|
|
91
|
+
return AGENT_LABELS[name] || name;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Match pf agent start "foo"
|
|
95
|
+
const agentMatch = content.match(/pf agent start\s+"([^"]+)"/);
|
|
96
|
+
if (agentMatch) {
|
|
97
|
+
const name = agentMatch[1].trim();
|
|
98
|
+
return AGENT_LABELS[name] || name;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Match "Launching skill: foo"
|
|
102
|
+
const launchMatch = content.match(/Launching skill:\s*(\S+)/);
|
|
103
|
+
if (launchMatch) {
|
|
104
|
+
const name = launchMatch[1].trim();
|
|
105
|
+
return AGENT_LABELS[name] || name;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
36
109
|
}
|
|
37
110
|
|
|
38
111
|
/**
|
|
@@ -46,11 +119,9 @@ export function filterSkillContentMessages<T extends { type: string; content?: s
|
|
|
46
119
|
messages: T[]
|
|
47
120
|
): T[] {
|
|
48
121
|
return messages.filter(msg => {
|
|
49
|
-
// Only filter user messages
|
|
50
122
|
if (msg.type !== 'user') {
|
|
51
123
|
return true;
|
|
52
124
|
}
|
|
53
|
-
// Filter out skill content
|
|
54
125
|
return !isSkillContent(msg.content);
|
|
55
126
|
});
|
|
56
127
|
}
|
|
@@ -36,14 +36,6 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
36
36
|
"name": "/architect",
|
|
37
37
|
"description": "System Architect - Technical design and architecture"
|
|
38
38
|
},
|
|
39
|
-
{
|
|
40
|
-
"name": "/benchmark",
|
|
41
|
-
"description": "Compare an agent's performance against a stored baseline"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"name": "/benchmark-control",
|
|
45
|
-
"description": "Create control baseline for a scenario (shortcut for /benchmark control <agent>)"
|
|
46
|
-
},
|
|
47
39
|
{
|
|
48
40
|
"name": "/brainstorming",
|
|
49
41
|
"description": "Structured problem-solving brainstorm session"
|
|
@@ -88,10 +80,6 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
88
80
|
"name": "/create-branches-from-story",
|
|
89
81
|
"description": "Create feature branches in both repos from a story"
|
|
90
82
|
},
|
|
91
|
-
{
|
|
92
|
-
"name": "/create-theme",
|
|
93
|
-
"description": "Create a new custom persona theme"
|
|
94
|
-
},
|
|
95
83
|
{
|
|
96
84
|
"name": "/dev",
|
|
97
85
|
"description": "Developer - Feature implementation and coding"
|
|
@@ -124,14 +112,6 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
124
112
|
"name": "/init",
|
|
125
113
|
"description": "Initialize CLAUDE.md"
|
|
126
114
|
},
|
|
127
|
-
{
|
|
128
|
-
"name": "/job-fair",
|
|
129
|
-
"description": "Discover which characters in a theme excel at each role"
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
"name": "/list-themes",
|
|
133
|
-
"description": "List all available persona themes"
|
|
134
|
-
},
|
|
135
115
|
{
|
|
136
116
|
"name": "/login",
|
|
137
117
|
"description": "Authenticate with Anthropic"
|
|
@@ -208,26 +188,14 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
208
188
|
"name": "/run-ci",
|
|
209
189
|
"description": "Detect and run CI locally"
|
|
210
190
|
},
|
|
211
|
-
{
|
|
212
|
-
"name": "/set-theme",
|
|
213
|
-
"description": "Set the active persona theme"
|
|
214
|
-
},
|
|
215
191
|
{
|
|
216
192
|
"name": "/setup",
|
|
217
|
-
"description": "setup"
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
"name": "/show-theme",
|
|
221
|
-
"description": "Show details of a theme including all agent personas"
|
|
193
|
+
"description": "/setup - Interactive Project Setup"
|
|
222
194
|
},
|
|
223
195
|
{
|
|
224
196
|
"name": "/sm",
|
|
225
197
|
"description": "Scrum Master - Story coordination and sprint management"
|
|
226
198
|
},
|
|
227
|
-
{
|
|
228
|
-
"name": "/solo",
|
|
229
|
-
"description": "Run a single agent on a scenario with absolute rubric scoring"
|
|
230
|
-
},
|
|
231
199
|
{
|
|
232
200
|
"name": "/sprint",
|
|
233
201
|
"description": "Sprint status, backlog, and story management - check status, find work, archive completed stories"
|
|
@@ -269,8 +237,8 @@ export const SLASH_COMMANDS: SlashCommand[] = [
|
|
|
269
237
|
"description": "Configure terminal"
|
|
270
238
|
},
|
|
271
239
|
{
|
|
272
|
-
"name": "/theme
|
|
273
|
-
"description": "
|
|
240
|
+
"name": "/theme",
|
|
241
|
+
"description": "Manage persona themes - list, show, set, create, or interactive wizard"
|
|
274
242
|
},
|
|
275
243
|
{
|
|
276
244
|
"name": "/update-domain-docs",
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cyclist PreToolUse Hook - Core Logic
|
|
3
|
-
*
|
|
4
|
-
* Testable module containing all hook logic. Invoked via cyclist-pretooluse-hook.sh
|
|
5
|
-
* which calls `npx tsx` on this file, matching the .sh wrapper pattern used by
|
|
6
|
-
* all other hooks in .claude/settings.local.json.
|
|
7
|
-
*
|
|
8
|
-
* Flow:
|
|
9
|
-
* 1. Claude Code calls cyclist-pretooluse-hook.sh with tool info via stdin (JSON)
|
|
10
|
-
* 2. Shell wrapper invokes this module via npx tsx
|
|
11
|
-
* 3. Script reads port from .cyclist-port in project directory
|
|
12
|
-
* 4. Script sends request to WheelHub's /api/hook-request endpoint
|
|
13
|
-
* 5. WheelHub checks grants / shows approval modal, user decides
|
|
14
|
-
* 6. Script receives response, outputs JSON decision to stdout
|
|
15
|
-
* 7. Claude Code proceeds or blocks based on decision
|
|
16
|
-
*
|
|
17
|
-
* Per ADR-0004: All communication converges through WheelHub.
|
|
18
|
-
*
|
|
19
|
-
* Story: MSSCI-14320 - Update and register PreToolUse hook
|
|
20
|
-
*/
|
|
21
|
-
export declare const DEFAULT_PORT = 7432;
|
|
22
|
-
export declare const HOST = "127.0.0.1";
|
|
23
|
-
export declare const TIMEOUT_MS = 120000;
|
|
24
|
-
export declare const PORT_FILE = ".cyclist-port";
|
|
25
|
-
export declare const LEGACY_PORT_FILE = ".cyclist-approval-port";
|
|
26
|
-
export declare const ENDPOINT = "/api/hook-request";
|
|
27
|
-
export interface ToolData {
|
|
28
|
-
tool_name: string;
|
|
29
|
-
tool_use_id: string;
|
|
30
|
-
tool_input: Record<string, unknown>;
|
|
31
|
-
session_id?: string;
|
|
32
|
-
}
|
|
33
|
-
export interface ApprovalRequest {
|
|
34
|
-
toolName: string;
|
|
35
|
-
toolId: string;
|
|
36
|
-
input: Record<string, unknown>;
|
|
37
|
-
sessionId?: string;
|
|
38
|
-
}
|
|
39
|
-
export interface ApprovalResponse {
|
|
40
|
-
decision: 'allow' | 'deny' | 'ask';
|
|
41
|
-
reason?: string;
|
|
42
|
-
data?: Record<string, unknown>;
|
|
43
|
-
}
|
|
44
|
-
export interface HookOutput {
|
|
45
|
-
hookSpecificOutput: {
|
|
46
|
-
hookEventName: 'PreToolUse';
|
|
47
|
-
permissionDecision: string;
|
|
48
|
-
permissionDecisionReason: string;
|
|
49
|
-
updatedInput?: Record<string, unknown>;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
export declare function findProjectRoot(startDir?: string): string | null;
|
|
53
|
-
export declare function getPort(projectRoot?: string | null): number;
|
|
54
|
-
export declare function readPortFile(filePath: string): number | null;
|
|
55
|
-
export declare function readStdin(): Promise<ToolData>;
|
|
56
|
-
export declare function requestApproval(toolData: ApprovalRequest, port?: number): Promise<ApprovalResponse>;
|
|
57
|
-
export declare function buildOutput(decision: string, reason: string, updatedInput?: Record<string, unknown> | null): HookOutput;
|
|
58
|
-
export declare function outputDecision(decision: string, reason: string, updatedInput?: Record<string, unknown> | null): void;
|
|
59
|
-
export declare function main(): Promise<void>;
|
|
60
|
-
//# sourceMappingURL=cyclist-pretooluse-hook.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cyclist-pretooluse-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/cyclist-pretooluse-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC,eAAO,MAAM,IAAI,cAAc,CAAC;AAChC,eAAO,MAAM,UAAU,SAAU,CAAC;AAClC,eAAO,MAAM,SAAS,kBAAkB,CAAC;AACzC,eAAO,MAAM,gBAAgB,2BAA2B,CAAC;AACzD,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAM5C,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,kBAAkB,EAAE;QAClB,aAAa,EAAE,YAAY,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,wBAAwB,EAAE,MAAM,CAAC;QACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACxC,CAAC;CACH;AAMD,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEhE;AAED,wBAAgB,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE5D;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,CAEnD;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,eAAe,EACzB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC5C,UAAU,CAEZ;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC5C,IAAI,CAEN;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cyclist PreToolUse Hook - Core Logic
|
|
3
|
-
*
|
|
4
|
-
* Testable module containing all hook logic. Invoked via cyclist-pretooluse-hook.sh
|
|
5
|
-
* which calls `npx tsx` on this file, matching the .sh wrapper pattern used by
|
|
6
|
-
* all other hooks in .claude/settings.local.json.
|
|
7
|
-
*
|
|
8
|
-
* Flow:
|
|
9
|
-
* 1. Claude Code calls cyclist-pretooluse-hook.sh with tool info via stdin (JSON)
|
|
10
|
-
* 2. Shell wrapper invokes this module via npx tsx
|
|
11
|
-
* 3. Script reads port from .cyclist-port in project directory
|
|
12
|
-
* 4. Script sends request to WheelHub's /api/hook-request endpoint
|
|
13
|
-
* 5. WheelHub checks grants / shows approval modal, user decides
|
|
14
|
-
* 6. Script receives response, outputs JSON decision to stdout
|
|
15
|
-
* 7. Claude Code proceeds or blocks based on decision
|
|
16
|
-
*
|
|
17
|
-
* Per ADR-0004: All communication converges through WheelHub.
|
|
18
|
-
*
|
|
19
|
-
* Story: MSSCI-14320 - Update and register PreToolUse hook
|
|
20
|
-
*/
|
|
21
|
-
// =============================================================================
|
|
22
|
-
// Constants
|
|
23
|
-
// =============================================================================
|
|
24
|
-
export const DEFAULT_PORT = 7432;
|
|
25
|
-
export const HOST = '127.0.0.1';
|
|
26
|
-
export const TIMEOUT_MS = 120_000; // 2 minutes for user to decide
|
|
27
|
-
export const PORT_FILE = '.cyclist-port';
|
|
28
|
-
export const LEGACY_PORT_FILE = '.cyclist-approval-port';
|
|
29
|
-
export const ENDPOINT = '/api/hook-request';
|
|
30
|
-
// =============================================================================
|
|
31
|
-
// Stubs - Dev implements these to GREEN
|
|
32
|
-
// =============================================================================
|
|
33
|
-
export function findProjectRoot(startDir) {
|
|
34
|
-
throw new Error('findProjectRoot not implemented');
|
|
35
|
-
}
|
|
36
|
-
export function getPort(projectRoot) {
|
|
37
|
-
throw new Error('getPort not implemented');
|
|
38
|
-
}
|
|
39
|
-
export function readPortFile(filePath) {
|
|
40
|
-
throw new Error('readPortFile not implemented');
|
|
41
|
-
}
|
|
42
|
-
export async function readStdin() {
|
|
43
|
-
throw new Error('readStdin not implemented');
|
|
44
|
-
}
|
|
45
|
-
export async function requestApproval(toolData, port) {
|
|
46
|
-
throw new Error('requestApproval not implemented');
|
|
47
|
-
}
|
|
48
|
-
export function buildOutput(decision, reason, updatedInput) {
|
|
49
|
-
throw new Error('buildOutput not implemented');
|
|
50
|
-
}
|
|
51
|
-
export function outputDecision(decision, reason, updatedInput) {
|
|
52
|
-
throw new Error('outputDecision not implemented');
|
|
53
|
-
}
|
|
54
|
-
export async function main() {
|
|
55
|
-
throw new Error('main not implemented');
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=cyclist-pretooluse-hook.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cyclist-pretooluse-hook.js","sourceRoot":"","sources":["../../src/hooks/cyclist-pretooluse-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,MAAM,IAAI,GAAG,WAAW,CAAC;AAChC,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,+BAA+B;AAClE,MAAM,CAAC,MAAM,SAAS,GAAG,eAAe,CAAC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AACzD,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AAmC5C,gFAAgF;AAChF,wCAAwC;AACxC,gFAAgF;AAEhF,MAAM,UAAU,eAAe,CAAC,QAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,WAA2B;IACjD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAyB,EACzB,IAAa;IAEb,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,MAAc,EACd,YAA6C;IAE7C,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,MAAc,EACd,YAA6C;IAE7C,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cyclist PreToolUse Hook - Core Logic
|
|
3
|
-
*
|
|
4
|
-
* Testable module containing all hook logic. The companion runner script
|
|
5
|
-
* (cyclist-pretooluse-hook.js) imports from the compiled output and invokes main().
|
|
6
|
-
*
|
|
7
|
-
* Flow:
|
|
8
|
-
* 1. Claude Code calls the runner script with tool info via stdin (JSON)
|
|
9
|
-
* 2. Script reads port from .cyclist-port in project directory
|
|
10
|
-
* 3. Script sends request to WheelHub's /api/hook-request endpoint
|
|
11
|
-
* 4. WheelHub checks grants / shows approval modal, user decides
|
|
12
|
-
* 5. Script receives response, outputs JSON decision to stdout
|
|
13
|
-
* 6. Claude Code proceeds or blocks based on decision
|
|
14
|
-
*
|
|
15
|
-
* Per ADR-0004: All communication converges through WheelHub.
|
|
16
|
-
*
|
|
17
|
-
* Story: MSSCI-14320 - Update and register PreToolUse hook
|
|
18
|
-
*/
|
|
19
|
-
export declare const DEFAULT_PORT = 7432;
|
|
20
|
-
export declare const HOST = "127.0.0.1";
|
|
21
|
-
export declare const TIMEOUT_MS = 120000;
|
|
22
|
-
export declare const PORT_FILE = ".cyclist-port";
|
|
23
|
-
export declare const LEGACY_PORT_FILE = ".cyclist-approval-port";
|
|
24
|
-
export declare const ENDPOINT = "/api/hook-request";
|
|
25
|
-
export interface ToolData {
|
|
26
|
-
tool_name: string;
|
|
27
|
-
tool_use_id: string;
|
|
28
|
-
tool_input: Record<string, unknown>;
|
|
29
|
-
session_id?: string;
|
|
30
|
-
}
|
|
31
|
-
export interface ApprovalRequest {
|
|
32
|
-
toolName: string;
|
|
33
|
-
toolId: string;
|
|
34
|
-
input: Record<string, unknown>;
|
|
35
|
-
sessionId?: string;
|
|
36
|
-
}
|
|
37
|
-
export interface ApprovalResponse {
|
|
38
|
-
decision: 'allow' | 'deny' | 'ask';
|
|
39
|
-
reason?: string;
|
|
40
|
-
data?: Record<string, unknown>;
|
|
41
|
-
}
|
|
42
|
-
export interface HookOutput {
|
|
43
|
-
hookSpecificOutput: {
|
|
44
|
-
hookEventName: 'PreToolUse';
|
|
45
|
-
permissionDecision: string;
|
|
46
|
-
permissionDecisionReason: string;
|
|
47
|
-
updatedInput?: Record<string, unknown>;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Find the project root by looking for port files or .claude directory.
|
|
52
|
-
* Walks up from startDir (defaults to cwd) until found or reaches filesystem root.
|
|
53
|
-
*/
|
|
54
|
-
export declare function findProjectRoot(startDir?: string): string | null;
|
|
55
|
-
/**
|
|
56
|
-
* Read the WheelHub server port. Prefers .cyclist-port, falls back to
|
|
57
|
-
* .cyclist-approval-port for migration compatibility, then default port.
|
|
58
|
-
*
|
|
59
|
-
* Mirrors hooks.py:get_cyclist_port() behavior.
|
|
60
|
-
*/
|
|
61
|
-
export declare function getPort(projectRoot?: string | null): number;
|
|
62
|
-
/**
|
|
63
|
-
* Read a port number from a file. Returns null if file doesn't exist or is invalid.
|
|
64
|
-
*/
|
|
65
|
-
export declare function readPortFile(filePath: string): number | null;
|
|
66
|
-
/**
|
|
67
|
-
* Read all stdin as JSON.
|
|
68
|
-
*/
|
|
69
|
-
export declare function readStdin(): Promise<ToolData>;
|
|
70
|
-
/**
|
|
71
|
-
* Send approval request to WheelHub and wait for response.
|
|
72
|
-
* Returns { decision: "ask" } when WheelHub is unreachable (ECONNREFUSED).
|
|
73
|
-
*/
|
|
74
|
-
export declare function requestApproval(toolData: ApprovalRequest, port?: number): Promise<ApprovalResponse>;
|
|
75
|
-
/**
|
|
76
|
-
* Build the Claude Code hook output object.
|
|
77
|
-
* MSSCI-11947: Supports updatedInput passthrough for interactive tools.
|
|
78
|
-
*/
|
|
79
|
-
export declare function buildOutput(decision: string, reason: string, updatedInput?: Record<string, unknown> | null): HookOutput;
|
|
80
|
-
/**
|
|
81
|
-
* Output decision to stdout in Claude Code hook format.
|
|
82
|
-
*/
|
|
83
|
-
export declare function outputDecision(decision: string, reason: string, updatedInput?: Record<string, unknown> | null): void;
|
|
84
|
-
/**
|
|
85
|
-
* Main hook logic. Reads stdin, sends to WheelHub, outputs decision.
|
|
86
|
-
* MSSCI-11947: Handles data field passthrough for interactive tools.
|
|
87
|
-
*/
|
|
88
|
-
export declare function main(): Promise<void>;
|
|
89
|
-
//# sourceMappingURL=pretooluse-hook.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pretooluse-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/pretooluse-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAUH,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC,eAAO,MAAM,IAAI,cAAc,CAAC;AAChC,eAAO,MAAM,UAAU,SAAU,CAAC;AAClC,eAAO,MAAM,SAAS,kBAAkB,CAAC;AACzC,eAAO,MAAM,gBAAgB,2BAA2B,CAAC;AACzD,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAM5C,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,kBAAkB,EAAE;QAClB,aAAa,EAAE,YAAY,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,wBAAwB,EAAE,MAAM,CAAC;QACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACxC,CAAC;CACH;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBhE;AAMD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAmB3D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB5D;AAMD;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,CAcnD;AAMD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,eAAe,EACzB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC5C,UAAU,CAcZ;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC5C,IAAI,CAEN;AAMD;;;GAGG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA2B1C"}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cyclist PreToolUse Hook - Core Logic
|
|
3
|
-
*
|
|
4
|
-
* Testable module containing all hook logic. The companion runner script
|
|
5
|
-
* (cyclist-pretooluse-hook.js) imports from the compiled output and invokes main().
|
|
6
|
-
*
|
|
7
|
-
* Flow:
|
|
8
|
-
* 1. Claude Code calls the runner script with tool info via stdin (JSON)
|
|
9
|
-
* 2. Script reads port from .cyclist-port in project directory
|
|
10
|
-
* 3. Script sends request to WheelHub's /api/hook-request endpoint
|
|
11
|
-
* 4. WheelHub checks grants / shows approval modal, user decides
|
|
12
|
-
* 5. Script receives response, outputs JSON decision to stdout
|
|
13
|
-
* 6. Claude Code proceeds or blocks based on decision
|
|
14
|
-
*
|
|
15
|
-
* Per ADR-0004: All communication converges through WheelHub.
|
|
16
|
-
*
|
|
17
|
-
* Story: MSSCI-14320 - Update and register PreToolUse hook
|
|
18
|
-
*/
|
|
19
|
-
import http from 'node:http';
|
|
20
|
-
import fs from 'node:fs';
|
|
21
|
-
import path from 'node:path';
|
|
22
|
-
// =============================================================================
|
|
23
|
-
// Constants
|
|
24
|
-
// =============================================================================
|
|
25
|
-
export const DEFAULT_PORT = 7432;
|
|
26
|
-
export const HOST = '127.0.0.1';
|
|
27
|
-
export const TIMEOUT_MS = 120_000; // 2 minutes for user to decide
|
|
28
|
-
export const PORT_FILE = '.cyclist-port';
|
|
29
|
-
export const LEGACY_PORT_FILE = '.cyclist-approval-port';
|
|
30
|
-
export const ENDPOINT = '/api/hook-request';
|
|
31
|
-
// =============================================================================
|
|
32
|
-
// Project Root Detection
|
|
33
|
-
// =============================================================================
|
|
34
|
-
/**
|
|
35
|
-
* Find the project root by looking for port files or .claude directory.
|
|
36
|
-
* Walks up from startDir (defaults to cwd) until found or reaches filesystem root.
|
|
37
|
-
*/
|
|
38
|
-
export function findProjectRoot(startDir) {
|
|
39
|
-
let dir = startDir ?? process.cwd();
|
|
40
|
-
const root = path.parse(dir).root;
|
|
41
|
-
while (dir !== root) {
|
|
42
|
-
if (fs.existsSync(path.join(dir, PORT_FILE))) {
|
|
43
|
-
return dir;
|
|
44
|
-
}
|
|
45
|
-
if (fs.existsSync(path.join(dir, LEGACY_PORT_FILE))) {
|
|
46
|
-
return dir;
|
|
47
|
-
}
|
|
48
|
-
if (fs.existsSync(path.join(dir, '.claude'))) {
|
|
49
|
-
return dir;
|
|
50
|
-
}
|
|
51
|
-
dir = path.dirname(dir);
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
// =============================================================================
|
|
56
|
-
// Port Discovery
|
|
57
|
-
// =============================================================================
|
|
58
|
-
/**
|
|
59
|
-
* Read the WheelHub server port. Prefers .cyclist-port, falls back to
|
|
60
|
-
* .cyclist-approval-port for migration compatibility, then default port.
|
|
61
|
-
*
|
|
62
|
-
* Mirrors hooks.py:get_cyclist_port() behavior.
|
|
63
|
-
*/
|
|
64
|
-
export function getPort(projectRoot) {
|
|
65
|
-
const root = projectRoot ?? findProjectRoot();
|
|
66
|
-
if (!root) {
|
|
67
|
-
return DEFAULT_PORT;
|
|
68
|
-
}
|
|
69
|
-
// Try canonical port file first
|
|
70
|
-
const port = readPortFile(path.join(root, PORT_FILE));
|
|
71
|
-
if (port !== null) {
|
|
72
|
-
return port;
|
|
73
|
-
}
|
|
74
|
-
// Fall back to legacy port file
|
|
75
|
-
const legacyPort = readPortFile(path.join(root, LEGACY_PORT_FILE));
|
|
76
|
-
if (legacyPort !== null) {
|
|
77
|
-
return legacyPort;
|
|
78
|
-
}
|
|
79
|
-
return DEFAULT_PORT;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Read a port number from a file. Returns null if file doesn't exist or is invalid.
|
|
83
|
-
*/
|
|
84
|
-
export function readPortFile(filePath) {
|
|
85
|
-
if (!fs.existsSync(filePath)) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
const content = fs.readFileSync(filePath, 'utf-8').trim();
|
|
90
|
-
const port = parseInt(content, 10);
|
|
91
|
-
if (!isNaN(port) && port > 0 && port < 65536) {
|
|
92
|
-
return port;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Fall through
|
|
97
|
-
}
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
// =============================================================================
|
|
101
|
-
// Stdin Reading
|
|
102
|
-
// =============================================================================
|
|
103
|
-
/**
|
|
104
|
-
* Read all stdin as JSON.
|
|
105
|
-
*/
|
|
106
|
-
export async function readStdin() {
|
|
107
|
-
return new Promise((resolve, reject) => {
|
|
108
|
-
let data = '';
|
|
109
|
-
process.stdin.setEncoding('utf8');
|
|
110
|
-
process.stdin.on('data', (chunk) => { data += chunk; });
|
|
111
|
-
process.stdin.on('end', () => {
|
|
112
|
-
try {
|
|
113
|
-
resolve(JSON.parse(data));
|
|
114
|
-
}
|
|
115
|
-
catch (e) {
|
|
116
|
-
reject(new Error(`Invalid JSON input: ${e.message}`));
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
process.stdin.on('error', reject);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
// =============================================================================
|
|
123
|
-
// HTTP Request
|
|
124
|
-
// =============================================================================
|
|
125
|
-
/**
|
|
126
|
-
* Send approval request to WheelHub and wait for response.
|
|
127
|
-
* Returns { decision: "ask" } when WheelHub is unreachable (ECONNREFUSED).
|
|
128
|
-
*/
|
|
129
|
-
export async function requestApproval(toolData, port) {
|
|
130
|
-
const resolvedPort = port ?? getPort();
|
|
131
|
-
const postData = JSON.stringify(toolData);
|
|
132
|
-
return new Promise((resolve, reject) => {
|
|
133
|
-
const options = {
|
|
134
|
-
hostname: HOST,
|
|
135
|
-
port: resolvedPort,
|
|
136
|
-
path: ENDPOINT,
|
|
137
|
-
method: 'POST',
|
|
138
|
-
headers: {
|
|
139
|
-
'Content-Type': 'application/json',
|
|
140
|
-
'Content-Length': Buffer.byteLength(postData),
|
|
141
|
-
},
|
|
142
|
-
timeout: TIMEOUT_MS,
|
|
143
|
-
};
|
|
144
|
-
const req = http.request(options, (res) => {
|
|
145
|
-
let data = '';
|
|
146
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
147
|
-
res.on('end', () => {
|
|
148
|
-
try {
|
|
149
|
-
resolve(JSON.parse(data));
|
|
150
|
-
}
|
|
151
|
-
catch (e) {
|
|
152
|
-
reject(new Error(`Invalid response from WheelHub: ${e.message}`));
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
req.on('error', (e) => {
|
|
157
|
-
if (e.code === 'ECONNREFUSED') {
|
|
158
|
-
resolve({ decision: 'ask', reason: 'WheelHub not running, deferring to Claude Code' });
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
reject(e);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
req.on('timeout', () => {
|
|
165
|
-
req.destroy();
|
|
166
|
-
reject(new Error('Approval request timed out'));
|
|
167
|
-
});
|
|
168
|
-
req.write(postData);
|
|
169
|
-
req.end();
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
// =============================================================================
|
|
173
|
-
// Output Formatting
|
|
174
|
-
// =============================================================================
|
|
175
|
-
/**
|
|
176
|
-
* Build the Claude Code hook output object.
|
|
177
|
-
* MSSCI-11947: Supports updatedInput passthrough for interactive tools.
|
|
178
|
-
*/
|
|
179
|
-
export function buildOutput(decision, reason, updatedInput) {
|
|
180
|
-
const output = {
|
|
181
|
-
hookSpecificOutput: {
|
|
182
|
-
hookEventName: 'PreToolUse',
|
|
183
|
-
permissionDecision: decision,
|
|
184
|
-
permissionDecisionReason: reason,
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
if (updatedInput) {
|
|
188
|
-
output.hookSpecificOutput.updatedInput = updatedInput;
|
|
189
|
-
}
|
|
190
|
-
return output;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Output decision to stdout in Claude Code hook format.
|
|
194
|
-
*/
|
|
195
|
-
export function outputDecision(decision, reason, updatedInput) {
|
|
196
|
-
console.log(JSON.stringify(buildOutput(decision, reason, updatedInput)));
|
|
197
|
-
}
|
|
198
|
-
// =============================================================================
|
|
199
|
-
// Main Entry Point
|
|
200
|
-
// =============================================================================
|
|
201
|
-
/**
|
|
202
|
-
* Main hook logic. Reads stdin, sends to WheelHub, outputs decision.
|
|
203
|
-
* MSSCI-11947: Handles data field passthrough for interactive tools.
|
|
204
|
-
*/
|
|
205
|
-
export async function main() {
|
|
206
|
-
try {
|
|
207
|
-
const toolData = await readStdin();
|
|
208
|
-
const { tool_name, tool_input, tool_use_id, session_id } = toolData;
|
|
209
|
-
const response = await requestApproval({
|
|
210
|
-
toolName: tool_name,
|
|
211
|
-
toolId: tool_use_id,
|
|
212
|
-
input: tool_input,
|
|
213
|
-
sessionId: session_id,
|
|
214
|
-
});
|
|
215
|
-
if (response.decision === 'allow') {
|
|
216
|
-
outputDecision('allow', response.reason || 'Approved by user', response.data || null);
|
|
217
|
-
}
|
|
218
|
-
else if (response.decision === 'deny') {
|
|
219
|
-
outputDecision('deny', response.reason || 'Rejected by user', response.data || null);
|
|
220
|
-
}
|
|
221
|
-
else if (response.decision === 'ask') {
|
|
222
|
-
outputDecision('ask', response.reason || 'Deferred to Claude Code');
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
// Unknown decision - defer to Claude Code rather than silently allowing
|
|
226
|
-
outputDecision('ask', 'Unknown response from WheelHub');
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
console.error(`[cyclist-hook] Error: ${error.message}`);
|
|
231
|
-
// Output ask so Claude Code shows its built-in dialog, don't silently allow
|
|
232
|
-
outputDecision('ask', 'Hook error, deferring to Claude Code');
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
//# sourceMappingURL=pretooluse-hook.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pretooluse-hook.js","sourceRoot":"","sources":["../../src/hooks/pretooluse-hook.ts"],"names":[],"mappings":"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"}
|