@raindrop-ai/wizard 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +47 -0
  2. package/dist/bin.d.ts +2 -0
  3. package/dist/bin.js +117 -0
  4. package/dist/bin.js.map +1 -0
  5. package/dist/src/docs/browser.md +105 -0
  6. package/dist/src/docs/python.md +618 -0
  7. package/dist/src/docs/typescript.md +584 -0
  8. package/dist/src/docs/vercel-ai-sdk.md +304 -0
  9. package/dist/src/lib/agent-interface.d.ts +46 -0
  10. package/dist/src/lib/agent-interface.js +292 -0
  11. package/dist/src/lib/agent-interface.js.map +1 -0
  12. package/dist/src/lib/agent-prompts.d.ts +10 -0
  13. package/dist/src/lib/agent-prompts.js +49 -0
  14. package/dist/src/lib/agent-prompts.js.map +1 -0
  15. package/dist/src/lib/config.d.ts +39 -0
  16. package/dist/src/lib/config.js +549 -0
  17. package/dist/src/lib/config.js.map +1 -0
  18. package/dist/src/lib/constants.d.ts +27 -0
  19. package/dist/src/lib/constants.js +165 -0
  20. package/dist/src/lib/constants.js.map +1 -0
  21. package/dist/src/lib/handlers.d.ts +68 -0
  22. package/dist/src/lib/handlers.js +420 -0
  23. package/dist/src/lib/handlers.js.map +1 -0
  24. package/dist/src/lib/integration-testing.d.ts +44 -0
  25. package/dist/src/lib/integration-testing.js +123 -0
  26. package/dist/src/lib/integration-testing.js.map +1 -0
  27. package/dist/src/lib/mcp.d.ts +14 -0
  28. package/dist/src/lib/mcp.js +134 -0
  29. package/dist/src/lib/mcp.js.map +1 -0
  30. package/dist/src/lib/sdk-messages.d.ts +17 -0
  31. package/dist/src/lib/sdk-messages.js +278 -0
  32. package/dist/src/lib/sdk-messages.js.map +1 -0
  33. package/dist/src/lib/wizard.d.ts +6 -0
  34. package/dist/src/lib/wizard.js +131 -0
  35. package/dist/src/lib/wizard.js.map +1 -0
  36. package/dist/src/run.d.ts +8 -0
  37. package/dist/src/run.js +53 -0
  38. package/dist/src/run.js.map +1 -0
  39. package/dist/src/ui/App.d.ts +15 -0
  40. package/dist/src/ui/App.js +27 -0
  41. package/dist/src/ui/App.js.map +1 -0
  42. package/dist/src/ui/cancellation.d.ts +14 -0
  43. package/dist/src/ui/cancellation.js +17 -0
  44. package/dist/src/ui/cancellation.js.map +1 -0
  45. package/dist/src/ui/components/ClarifyingQuestionsPrompt.d.ts +17 -0
  46. package/dist/src/ui/components/ClarifyingQuestionsPrompt.js +359 -0
  47. package/dist/src/ui/components/ClarifyingQuestionsPrompt.js.map +1 -0
  48. package/dist/src/ui/components/ContinuePrompt.d.ts +14 -0
  49. package/dist/src/ui/components/ContinuePrompt.js +23 -0
  50. package/dist/src/ui/components/ContinuePrompt.js.map +1 -0
  51. package/dist/src/ui/components/DiffDisplay.d.ts +18 -0
  52. package/dist/src/ui/components/DiffDisplay.js +110 -0
  53. package/dist/src/ui/components/DiffDisplay.js.map +1 -0
  54. package/dist/src/ui/components/FeedbackSelectPrompt.d.ts +20 -0
  55. package/dist/src/ui/components/FeedbackSelectPrompt.js +132 -0
  56. package/dist/src/ui/components/FeedbackSelectPrompt.js.map +1 -0
  57. package/dist/src/ui/components/HistoryItemDisplay.d.ts +14 -0
  58. package/dist/src/ui/components/HistoryItemDisplay.js +140 -0
  59. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -0
  60. package/dist/src/ui/components/Logo.d.ts +10 -0
  61. package/dist/src/ui/components/Logo.js +47 -0
  62. package/dist/src/ui/components/Logo.js.map +1 -0
  63. package/dist/src/ui/components/OrgInfoBox.d.ts +11 -0
  64. package/dist/src/ui/components/OrgInfoBox.js +16 -0
  65. package/dist/src/ui/components/OrgInfoBox.js.map +1 -0
  66. package/dist/src/ui/components/PendingPrompt.d.ts +18 -0
  67. package/dist/src/ui/components/PendingPrompt.js +57 -0
  68. package/dist/src/ui/components/PendingPrompt.js.map +1 -0
  69. package/dist/src/ui/components/PersistentTextInput.d.ts +21 -0
  70. package/dist/src/ui/components/PersistentTextInput.js +117 -0
  71. package/dist/src/ui/components/PersistentTextInput.js.map +1 -0
  72. package/dist/src/ui/components/PlanApprovalPrompt.d.ts +19 -0
  73. package/dist/src/ui/components/PlanApprovalPrompt.js +62 -0
  74. package/dist/src/ui/components/PlanApprovalPrompt.js.map +1 -0
  75. package/dist/src/ui/components/PromptContainer.d.ts +14 -0
  76. package/dist/src/ui/components/PromptContainer.js +18 -0
  77. package/dist/src/ui/components/PromptContainer.js.map +1 -0
  78. package/dist/src/ui/components/SelectPrompt.d.ts +14 -0
  79. package/dist/src/ui/components/SelectPrompt.js +62 -0
  80. package/dist/src/ui/components/SelectPrompt.js.map +1 -0
  81. package/dist/src/ui/components/SpinnerDisplay.d.ts +13 -0
  82. package/dist/src/ui/components/SpinnerDisplay.js +11 -0
  83. package/dist/src/ui/components/SpinnerDisplay.js.map +1 -0
  84. package/dist/src/ui/components/ToolApprovalPrompt.d.ts +14 -0
  85. package/dist/src/ui/components/ToolApprovalPrompt.js +142 -0
  86. package/dist/src/ui/components/ToolApprovalPrompt.js.map +1 -0
  87. package/dist/src/ui/components/ToolCallDisplay.d.ts +14 -0
  88. package/dist/src/ui/components/ToolCallDisplay.js +83 -0
  89. package/dist/src/ui/components/ToolCallDisplay.js.map +1 -0
  90. package/dist/src/ui/components/WriteKeyDisplay.d.ts +15 -0
  91. package/dist/src/ui/components/WriteKeyDisplay.js +13 -0
  92. package/dist/src/ui/components/WriteKeyDisplay.js.map +1 -0
  93. package/dist/src/ui/contexts/WizardContext.d.ts +210 -0
  94. package/dist/src/ui/contexts/WizardContext.js +362 -0
  95. package/dist/src/ui/contexts/WizardContext.js.map +1 -0
  96. package/dist/src/ui/hooks/useCancellation.d.ts +15 -0
  97. package/dist/src/ui/hooks/useCancellation.js +25 -0
  98. package/dist/src/ui/hooks/useCancellation.js.map +1 -0
  99. package/dist/src/ui/render.d.ts +34 -0
  100. package/dist/src/ui/render.js +94 -0
  101. package/dist/src/ui/render.js.map +1 -0
  102. package/dist/src/ui/types.d.ts +184 -0
  103. package/dist/src/ui/types.js +6 -0
  104. package/dist/src/ui/types.js.map +1 -0
  105. package/dist/src/utils/clack-utils.d.ts +13 -0
  106. package/dist/src/utils/clack-utils.js +131 -0
  107. package/dist/src/utils/clack-utils.js.map +1 -0
  108. package/dist/src/utils/debug.d.ts +13 -0
  109. package/dist/src/utils/debug.js +47 -0
  110. package/dist/src/utils/debug.js.map +1 -0
  111. package/dist/src/utils/environment.d.ts +5 -0
  112. package/dist/src/utils/environment.js +131 -0
  113. package/dist/src/utils/environment.js.map +1 -0
  114. package/dist/src/utils/logging.d.ts +9 -0
  115. package/dist/src/utils/logging.js +38 -0
  116. package/dist/src/utils/logging.js.map +1 -0
  117. package/dist/src/utils/oauth.d.ts +12 -0
  118. package/dist/src/utils/oauth.js +497 -0
  119. package/dist/src/utils/oauth.js.map +1 -0
  120. package/dist/src/utils/package-json-types.d.ts +44 -0
  121. package/dist/src/utils/package-json-types.js +6 -0
  122. package/dist/src/utils/package-json-types.js.map +1 -0
  123. package/dist/src/utils/package-json.d.ts +19 -0
  124. package/dist/src/utils/package-json.js +22 -0
  125. package/dist/src/utils/package-json.js.map +1 -0
  126. package/dist/src/utils/session.d.ts +2 -0
  127. package/dist/src/utils/session.js +87 -0
  128. package/dist/src/utils/session.js.map +1 -0
  129. package/dist/src/utils/types.d.ts +61 -0
  130. package/dist/src/utils/types.js +2 -0
  131. package/dist/src/utils/types.js.map +1 -0
  132. package/dist/src/utils/ui.d.ts +120 -0
  133. package/dist/src/utils/ui.js +164 -0
  134. package/dist/src/utils/ui.js.map +1 -0
  135. package/package.json +140 -0
@@ -0,0 +1,165 @@
1
+ export var Integration;
2
+ (function (Integration) {
3
+ Integration["python"] = "python";
4
+ Integration["typescript"] = "typescript";
5
+ Integration["vercelAiSdk"] = "vercel-ai-sdk";
6
+ })(Integration || (Integration = {}));
7
+ export function getIntegrationDescription(type) {
8
+ switch (type) {
9
+ case Integration.python:
10
+ return 'a Python SDK';
11
+ case Integration.typescript:
12
+ return 'a TypeScript SDK';
13
+ case Integration.vercelAiSdk:
14
+ return 'the Vercel AI SDK';
15
+ default:
16
+ throw new Error(`Unknown integration ${type}`);
17
+ }
18
+ }
19
+ export const IS_DEV = ['test', 'development'].includes(process.env.NODE_ENV ?? '');
20
+ export const ISSUES_URL = 'https://github.com/raindrop/wizard/issues';
21
+ export const CALLBACK_PORT = 8259;
22
+ const RAINDROP_APP_URL = 'https://app.raindrop.ai';
23
+ const RAINDROP_API_URL = 'https://api.dawnai.com';
24
+ export const API_BASE_URL = RAINDROP_API_URL;
25
+ export const APP_URL = RAINDROP_APP_URL;
26
+ // For local dev:
27
+ // export const API_BASE_URL = 'http://localhost:3000';
28
+ // export const APP_URL = 'http://localhost:5173';
29
+ export const WRITE_KEY_ENDPOINT = `${API_BASE_URL}/api/cli/users/key`;
30
+ export const EVENTS_LIST_ENDPOINT = `${API_BASE_URL}/api/cli/events/list`;
31
+ export const ANTHROPIC_BASE_URL = `${API_BASE_URL}/api/cli`;
32
+ export const SESSION_START_ENDPOINT = `${API_BASE_URL}/api/cli/session/init`;
33
+ export const SESSION_UPDATE_ENDPOINT = `${API_BASE_URL}/api/cli/session/update`;
34
+ /**
35
+ * Spinner message shown during wizard execution
36
+ */
37
+ export const SPINNER_MESSAGE = 'Raindrop wizard is working...';
38
+ /**
39
+ * Safe bash command patterns that can be auto-approved without user confirmation.
40
+ * Uses simple prefix matching with optional * wildcard at the end.
41
+ *
42
+ * NOTE: Package manager install/build/run commands are NOT auto-approved and require user confirmation.
43
+ */
44
+ export const SAFE_BASH_PATTERNS = [
45
+ // === Package Managers - Read-only commands ===
46
+ 'npm ls*',
47
+ 'npm outdated*',
48
+ 'npm audit*',
49
+ 'pip list*',
50
+ 'pip show*',
51
+ 'pip freeze*',
52
+ 'poetry show*',
53
+ 'uv pip list*',
54
+ 'cargo check*',
55
+ 'cargo clippy*',
56
+ 'cargo fmt*',
57
+ 'go fmt*',
58
+ // === Type Checking / Linting / Formatting ===
59
+ 'tsc*',
60
+ 'eslint *',
61
+ 'prettier *',
62
+ 'biome *',
63
+ 'mypy *',
64
+ 'python -m mypy*',
65
+ 'ruff *',
66
+ 'black *',
67
+ 'flake8 *',
68
+ 'pylint *',
69
+ 'pyright*',
70
+ 'rubocop*',
71
+ 'rustfmt*',
72
+ 'gofmt*',
73
+ 'swiftlint*',
74
+ 'ktlint*',
75
+ // === Build Tools ===
76
+ 'make',
77
+ 'make build*',
78
+ 'make test*',
79
+ 'make lint*',
80
+ 'make check*',
81
+ 'cmake *',
82
+ 'gradle build*',
83
+ 'gradle test*',
84
+ './gradlew build*',
85
+ './gradlew test*',
86
+ 'mvn compile*',
87
+ 'mvn test*',
88
+ 'mvn package*',
89
+ // === Testing ===
90
+ 'jest*',
91
+ 'vitest*',
92
+ 'mocha*',
93
+ 'pytest*',
94
+ 'python -m pytest*',
95
+ 'npx playwright test*',
96
+ 'npx cypress run*',
97
+ 'npx jest*',
98
+ 'npx vitest*',
99
+ // === Git (read-only operations) ===
100
+ 'git status*',
101
+ 'git diff*',
102
+ 'git log*',
103
+ 'git show*',
104
+ 'git branch*',
105
+ 'git fetch*',
106
+ 'git remote -v*',
107
+ 'git ls-files*',
108
+ 'git rev-parse*',
109
+ // === File/Directory Info (read-only) ===
110
+ 'ls *',
111
+ 'ls',
112
+ 'cat *',
113
+ 'head *',
114
+ 'tail *',
115
+ 'less *',
116
+ 'find *',
117
+ 'grep *',
118
+ 'rg *',
119
+ 'ag *',
120
+ 'tree*',
121
+ 'pwd',
122
+ 'wc *',
123
+ 'file *',
124
+ 'stat *',
125
+ 'du *',
126
+ 'df *',
127
+ // === Environment/System Info ===
128
+ 'which *',
129
+ 'whereis *',
130
+ 'type *',
131
+ 'command -v *',
132
+ 'node --version*',
133
+ 'node -v*',
134
+ 'npm --version*',
135
+ 'npm -v*',
136
+ 'python --version*',
137
+ 'python -V*',
138
+ 'python3 --version*',
139
+ '*--version',
140
+ '*-v',
141
+ '*-V',
142
+ 'env',
143
+ 'printenv*',
144
+ 'echo *',
145
+ 'uname*',
146
+ 'hostname',
147
+ 'whoami',
148
+ 'date',
149
+ 'uptime',
150
+ // === Docker (read-only / safe) ===
151
+ 'docker ps*',
152
+ 'docker images*',
153
+ 'docker logs*',
154
+ 'docker inspect*',
155
+ 'docker-compose ps*',
156
+ 'docker-compose logs*',
157
+ 'docker build*',
158
+ 'docker compose build*',
159
+ 'docker compose up*',
160
+ // === Directory Navigation ===
161
+ 'cd *',
162
+ 'pushd *',
163
+ 'popd',
164
+ ];
165
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/constants.ts"],"names":[],"mappings":"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","sourcesContent":["export enum Integration {\n python = 'python',\n typescript = 'typescript',\n vercelAiSdk = 'vercel-ai-sdk',\n}\n\nexport function getIntegrationDescription(type: string): string {\n switch (type) {\n case Integration.python:\n return 'a Python SDK';\n case Integration.typescript:\n return 'a TypeScript SDK';\n case Integration.vercelAiSdk:\n return 'the Vercel AI SDK';\n default:\n throw new Error(`Unknown integration ${type}`);\n }\n}\n\nexport const IS_DEV = ['test', 'development'].includes(\n process.env.NODE_ENV ?? '',\n);\n\nexport const ISSUES_URL = 'https://github.com/raindrop/wizard/issues';\n\nexport const CALLBACK_PORT = 8259;\nconst RAINDROP_APP_URL = 'https://app.raindrop.ai';\nconst RAINDROP_API_URL = 'https://api.dawnai.com';\nexport const API_BASE_URL = RAINDROP_API_URL;\nexport const APP_URL = RAINDROP_APP_URL;\n// For local dev:\n// export const API_BASE_URL = 'http://localhost:3000';\n// export const APP_URL = 'http://localhost:5173';\nexport const WRITE_KEY_ENDPOINT = `${API_BASE_URL}/api/cli/users/key`;\nexport const EVENTS_LIST_ENDPOINT = `${API_BASE_URL}/api/cli/events/list`;\nexport const ANTHROPIC_BASE_URL = `${API_BASE_URL}/api/cli`;\nexport const SESSION_START_ENDPOINT = `${API_BASE_URL}/api/cli/session/init`;\nexport const SESSION_UPDATE_ENDPOINT = `${API_BASE_URL}/api/cli/session/update`;\n\n/**\n * Spinner message shown during wizard execution\n */\nexport const SPINNER_MESSAGE = 'Raindrop wizard is working...';\n\n/**\n * Safe bash command patterns that can be auto-approved without user confirmation.\n * Uses simple prefix matching with optional * wildcard at the end.\n *\n * NOTE: Package manager install/build/run commands are NOT auto-approved and require user confirmation.\n */\nexport const SAFE_BASH_PATTERNS: string[] = [\n // === Package Managers - Read-only commands ===\n 'npm ls*',\n 'npm outdated*',\n 'npm audit*',\n\n 'pip list*',\n 'pip show*',\n 'pip freeze*',\n\n 'poetry show*',\n\n 'uv pip list*',\n\n 'cargo check*',\n 'cargo clippy*',\n 'cargo fmt*',\n\n 'go fmt*',\n\n // === Type Checking / Linting / Formatting ===\n 'tsc*',\n 'eslint *',\n 'prettier *',\n 'biome *',\n 'mypy *',\n 'python -m mypy*',\n 'ruff *',\n 'black *',\n 'flake8 *',\n 'pylint *',\n 'pyright*',\n 'rubocop*',\n 'rustfmt*',\n 'gofmt*',\n 'swiftlint*',\n 'ktlint*',\n\n // === Build Tools ===\n 'make',\n 'make build*',\n 'make test*',\n 'make lint*',\n 'make check*',\n 'cmake *',\n 'gradle build*',\n 'gradle test*',\n './gradlew build*',\n './gradlew test*',\n 'mvn compile*',\n 'mvn test*',\n 'mvn package*',\n\n // === Testing ===\n 'jest*',\n 'vitest*',\n 'mocha*',\n 'pytest*',\n 'python -m pytest*',\n 'npx playwright test*',\n 'npx cypress run*',\n 'npx jest*',\n 'npx vitest*',\n\n // === Git (read-only operations) ===\n 'git status*',\n 'git diff*',\n 'git log*',\n 'git show*',\n 'git branch*',\n 'git fetch*',\n 'git remote -v*',\n 'git ls-files*',\n 'git rev-parse*',\n\n // === File/Directory Info (read-only) ===\n 'ls *',\n 'ls',\n 'cat *',\n 'head *',\n 'tail *',\n 'less *',\n 'find *',\n 'grep *',\n 'rg *',\n 'ag *',\n 'tree*',\n 'pwd',\n 'wc *',\n 'file *',\n 'stat *',\n 'du *',\n 'df *',\n\n // === Environment/System Info ===\n 'which *',\n 'whereis *',\n 'type *',\n 'command -v *',\n 'node --version*',\n 'node -v*',\n 'npm --version*',\n 'npm -v*',\n 'python --version*',\n 'python -V*',\n 'python3 --version*',\n '*--version',\n '*-v',\n '*-V',\n 'env',\n 'printenv*',\n 'echo *',\n 'uname*',\n 'hostname',\n 'whoami',\n 'date',\n 'uptime',\n\n // === Docker (read-only / safe) ===\n 'docker ps*',\n 'docker images*',\n 'docker logs*',\n 'docker inspect*',\n 'docker-compose ps*',\n 'docker-compose logs*',\n 'docker build*',\n 'docker compose build*',\n 'docker compose up*',\n\n // === Directory Navigation ===\n 'cd *',\n 'pushd *',\n 'popd',\n];\n"]}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Tool approval handlers for the Claude agent
3
+ * Handles UI integration for tool approvals and clarifying questions
4
+ */
5
+ import type { ToolApprovalResult, AgentQueryHandle } from '../ui/types.js';
6
+ /**
7
+ * Clear all session-wide file approvals.
8
+ * Useful when starting a new session or resetting permissions.
9
+ */
10
+ export declare function clearApprovedFiles(): void;
11
+ /**
12
+ * Clear all session-wide command pattern approvals.
13
+ * Useful when starting a new session or resetting permissions.
14
+ */
15
+ export declare function clearApprovedCommandPatterns(): void;
16
+ /**
17
+ * Get the list of currently approved files (for debugging/display purposes)
18
+ */
19
+ export declare function getApprovedFiles(): string[];
20
+ /**
21
+ * Get the list of currently approved command patterns (for debugging/display purposes)
22
+ */
23
+ export declare function getApprovedCommandPatterns(): string[];
24
+ /**
25
+ * Pending tool call info stored while waiting for result
26
+ */
27
+ export interface PendingToolCall {
28
+ toolName: string;
29
+ input: Record<string, unknown>;
30
+ description?: string;
31
+ }
32
+ /**
33
+ * Dependencies required for creating an AgentQueryHandle
34
+ */
35
+ export interface AgentQueryHandleDeps {
36
+ isInterruptingRef: {
37
+ value: boolean;
38
+ };
39
+ waitingForUserInputRef: {
40
+ value: boolean;
41
+ };
42
+ pendingToolCalls: Map<string, PendingToolCall>;
43
+ getQueryObject: () => {
44
+ interrupt?: () => Promise<void>;
45
+ } | null;
46
+ }
47
+ /**
48
+ * Create an AgentQueryHandle for external control of the agent.
49
+ * Handles interrupts by marking pending tool calls (keeps persistent input running).
50
+ */
51
+ export declare function createAgentQueryHandle(deps: AgentQueryHandleDeps): AgentQueryHandle;
52
+ /**
53
+ * Session info for notifications
54
+ */
55
+ export interface SessionInfo {
56
+ sessionId: string;
57
+ accessToken: string;
58
+ orgId: string;
59
+ }
60
+ /**
61
+ * Create a canUseTool handler that integrates with the UI for approvals.
62
+ * - Handles AskUserQuestion by showing clarifying questions UI
63
+ * - Handles ExitPlanMode by showing plan approval UI
64
+ * - Handles WebSearch by restricting to allowed domains
65
+ * - Shows approval UI for other tools
66
+ * - Supports caching file approvals for "allow all edits to this file"
67
+ */
68
+ export declare function createCanUseToolHandler(sessionInfo?: SessionInfo, approvedFilesCache?: Set<string>): (toolName: string, input: unknown) => Promise<ToolApprovalResult>;
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Tool approval handlers for the Claude agent
3
+ * Handles UI integration for tool approvals and clarifying questions
4
+ */
5
+ import ui from '../utils/ui.js';
6
+ import { logToFile } from '../utils/debug.js';
7
+ import { createTwoFilesPatch } from 'diff';
8
+ import { SAFE_BASH_PATTERNS } from './constants.js';
9
+ import { sendSessionUpdate } from '../utils/session.js';
10
+ // ============================================================================
11
+ // Session-wide Approval Tracking
12
+ // ============================================================================
13
+ /**
14
+ * Set of file paths that have been granted blanket approval for the session.
15
+ * When a file is in this set, Edit/Write operations on it are auto-approved.
16
+ */
17
+ const approvedFilesForSession = new Set();
18
+ /**
19
+ * Set of command patterns that have been granted blanket approval for the session.
20
+ * When a command matches a pattern in this set, Bash operations are auto-approved.
21
+ * Patterns use simple prefix matching (e.g., "npm install" matches "npm install axios").
22
+ */
23
+ const approvedCommandPatternsForSession = new Set();
24
+ /**
25
+ * Clear all session-wide file approvals.
26
+ * Useful when starting a new session or resetting permissions.
27
+ */
28
+ export function clearApprovedFiles() {
29
+ approvedFilesForSession.clear();
30
+ logToFile('Cleared all approved files for session');
31
+ }
32
+ /**
33
+ * Clear all session-wide command pattern approvals.
34
+ * Useful when starting a new session or resetting permissions.
35
+ */
36
+ export function clearApprovedCommandPatterns() {
37
+ approvedCommandPatternsForSession.clear();
38
+ logToFile('Cleared all approved command patterns for session');
39
+ }
40
+ /**
41
+ * Get the list of currently approved files (for debugging/display purposes)
42
+ */
43
+ export function getApprovedFiles() {
44
+ return Array.from(approvedFilesForSession);
45
+ }
46
+ /**
47
+ * Get the list of currently approved command patterns (for debugging/display purposes)
48
+ */
49
+ export function getApprovedCommandPatterns() {
50
+ return Array.from(approvedCommandPatternsForSession);
51
+ }
52
+ /**
53
+ * Extract the command pattern from a bash command for session approval.
54
+ * Returns the base command with the first argument(s) to match similar commands.
55
+ * Examples:
56
+ * "npm install axios" -> "npm install"
57
+ * "npm run build" -> "npm run"
58
+ * "pip install requests" -> "pip install"
59
+ * "yarn add lodash" -> "yarn add"
60
+ */
61
+ function extractCommandPattern(command) {
62
+ const trimmed = command.trim();
63
+ const parts = trimmed.split(/\s+/);
64
+ // For package managers with subcommands, include the subcommand
65
+ if (parts.length >= 2) {
66
+ const [cmd, subcmd] = parts;
67
+ // Common package managers
68
+ if (['npm', 'yarn', 'bun', 'pip', 'pip3', 'poetry', 'cargo', 'go'].includes(cmd)) {
69
+ return `${cmd} ${subcmd}`;
70
+ }
71
+ }
72
+ // For other commands, just return the first part
73
+ return parts[0] || trimmed;
74
+ }
75
+ /**
76
+ * Check if a bash command matches any approved command pattern for the session.
77
+ */
78
+ function isCommandApprovedForSession(command) {
79
+ const pattern = extractCommandPattern(command);
80
+ return approvedCommandPatternsForSession.has(pattern);
81
+ }
82
+ /**
83
+ * Create an AgentQueryHandle for external control of the agent.
84
+ * Handles interrupts by marking pending tool calls (keeps persistent input running).
85
+ */
86
+ export function createAgentQueryHandle(deps) {
87
+ const { isInterruptingRef, waitingForUserInputRef, pendingToolCalls, getQueryObject, } = deps;
88
+ return {
89
+ interrupt: async () => {
90
+ logToFile('Soft interrupt requested (Esc)');
91
+ // If already interrupted, don't add another "Interrupted" item
92
+ if (isInterruptingRef.value || waitingForUserInputRef.value) {
93
+ logToFile('Already interrupted - ignoring duplicate interrupt request');
94
+ return;
95
+ }
96
+ isInterruptingRef.value = true;
97
+ waitingForUserInputRef.value = true;
98
+ // Mark all pending tool calls as interrupted and show them in history
99
+ if (pendingToolCalls.size > 0) {
100
+ for (const [toolUseId, pendingCall] of pendingToolCalls) {
101
+ ui.addItem({
102
+ type: 'tool-call',
103
+ text: pendingCall.toolName,
104
+ toolCall: {
105
+ toolName: pendingCall.toolName,
106
+ status: 'interrupted',
107
+ input: pendingCall.input,
108
+ description: pendingCall.description,
109
+ },
110
+ });
111
+ pendingToolCalls.delete(toolUseId);
112
+ }
113
+ }
114
+ else {
115
+ // Only show generic "Interrupted" if no pending tool calls to display
116
+ ui.addItem({
117
+ type: 'error',
118
+ text: 'Interrupted',
119
+ });
120
+ }
121
+ // Note: Don't stop persistent input here - keep it visible for user to type their message
122
+ // The spinner will be stopped separately, and the input remains for user to submit
123
+ // Call SDK interrupt
124
+ const queryObject = getQueryObject();
125
+ if (queryObject?.interrupt) {
126
+ logToFile('Calling queryObject.interrupt()');
127
+ await queryObject.interrupt();
128
+ logToFile('queryObject.interrupt() completed');
129
+ }
130
+ else {
131
+ logToFile('No queryObject.interrupt available');
132
+ }
133
+ },
134
+ sendMessage: () => {
135
+ // No-op: use interrupt + resume pattern instead of streaming messages
136
+ logToFile('sendMessage called but not supported - use interrupt and resume');
137
+ },
138
+ };
139
+ }
140
+ // ============================================================================
141
+ // Enhanced canUseTool Handler with UI Integration
142
+ // ============================================================================
143
+ /**
144
+ * Generate a unified diff for Edit tool inputs (old_string -> new_string)
145
+ */
146
+ function generateEditDiff(filePath, oldString, newString) {
147
+ // createTwoFilesPatch generates a unified diff
148
+ return createTwoFilesPatch(filePath, filePath, oldString, newString, '', // old header
149
+ '', // new header
150
+ { context: 3 });
151
+ }
152
+ /**
153
+ * Handle tool approval request by showing approval UI
154
+ */
155
+ async function handleToolApproval(toolName, input, approvedFilesCache) {
156
+ logToFile('Showing tool approval UI:', { toolName, input });
157
+ // Extract file path
158
+ const fileName = typeof input.file_path === 'string'
159
+ ? input.file_path
160
+ : typeof input.path === 'string'
161
+ ? input.path
162
+ : undefined;
163
+ // Check if this file has been approved for all edits
164
+ if (approvedFilesCache &&
165
+ fileName &&
166
+ (toolName === 'Edit' || toolName === 'Write') &&
167
+ approvedFilesCache.has(fileName)) {
168
+ logToFile('Auto-approving edit to previously approved file:', fileName);
169
+ return {
170
+ behavior: 'allow',
171
+ updatedInput: input,
172
+ };
173
+ }
174
+ // Stop spinner while waiting for user approval
175
+ const spinner = ui.spinner();
176
+ spinner.stop();
177
+ // Generate diff content for file modification tools
178
+ let diffContent;
179
+ if (toolName === 'Edit' &&
180
+ typeof input.old_string === 'string' &&
181
+ typeof input.new_string === 'string' &&
182
+ fileName) {
183
+ // Edit: show old -> new diff
184
+ diffContent = generateEditDiff(fileName, input.old_string, input.new_string);
185
+ }
186
+ else if (toolName === 'Write' &&
187
+ typeof input.content === 'string' &&
188
+ fileName) {
189
+ // Write: show content as all additions (empty -> content)
190
+ diffContent = generateEditDiff(fileName, '', input.content);
191
+ }
192
+ else if (typeof input.file_diff === 'string') {
193
+ // Use pre-generated diff if available
194
+ diffContent = input.file_diff;
195
+ }
196
+ // Build props for the approval prompt
197
+ const props = {
198
+ toolName,
199
+ input,
200
+ description: typeof input.description === 'string' ? input.description : undefined,
201
+ diffContent,
202
+ fileName,
203
+ };
204
+ try {
205
+ const result = await ui.toolApproval(props);
206
+ logToFile('Tool approval result:', result);
207
+ // If user chose "allow all edits to this file", add to both caches
208
+ if (result.behavior === 'allow' && 'allowAllForFile' in result && result.allowAllForFile) {
209
+ approvedFilesForSession.add(result.allowAllForFile);
210
+ logToFile('Added file to approved list:', result.allowAllForFile);
211
+ }
212
+ if (result.behavior === 'allow' &&
213
+ result.allowAllEdits &&
214
+ fileName &&
215
+ approvedFilesCache) {
216
+ logToFile('Adding file to approved files cache:', fileName);
217
+ approvedFilesCache.add(fileName);
218
+ }
219
+ // If user selected "allow all for this command pattern", add to approved set
220
+ if (result.behavior === 'allow' &&
221
+ 'allowAllForCommandPattern' in result &&
222
+ result.allowAllForCommandPattern) {
223
+ approvedCommandPatternsForSession.add(result.allowAllForCommandPattern);
224
+ logToFile('Added command pattern to approved list:', result.allowAllForCommandPattern);
225
+ }
226
+ // Restart spinner after user responds
227
+ spinner.start();
228
+ return result;
229
+ }
230
+ catch (error) {
231
+ logToFile('Error in tool approval:', error);
232
+ return {
233
+ behavior: 'deny',
234
+ message: 'Failed to get user approval',
235
+ };
236
+ }
237
+ }
238
+ /**
239
+ * Handle the AskUserQuestion tool by showing clarifying questions UI.
240
+ * Input already contains { questions: [...] } in the correct format.
241
+ * Returns { questions, answers } as expected by the SDK.
242
+ */
243
+ async function handleClarifyingQuestions(input) {
244
+ logToFile('Handling AskUserQuestion:', input);
245
+ // Stop spinner while waiting for user input
246
+ const spinner = ui.spinner();
247
+ spinner.stop();
248
+ try {
249
+ // Input is already in ClarifyingQuestionsProps format
250
+ const result = await ui.clarifyingQuestions(input);
251
+ logToFile('Clarifying questions result:', result);
252
+ // Restart spinner after user responds
253
+ spinner.start();
254
+ // Return questions + answers as expected by SDK
255
+ return {
256
+ behavior: 'allow',
257
+ updatedInput: {
258
+ questions: result.questions,
259
+ answers: result.answers,
260
+ },
261
+ };
262
+ }
263
+ catch (error) {
264
+ logToFile('Error in clarifying questions:', error);
265
+ return {
266
+ behavior: 'deny',
267
+ message: 'Failed to get user answers',
268
+ };
269
+ }
270
+ }
271
+ /**
272
+ * Handle the ExitPlanMode tool by showing plan approval UI.
273
+ * Input contains { plan: "..." } with the plan in markdown format.
274
+ * If user approves, returns allow. If user rejects, returns deny with feedback.
275
+ */
276
+ async function handlePlanApproval(input, sessionInfo) {
277
+ logToFile('Handling ExitPlanMode:', input);
278
+ const planContent = typeof input.plan === 'string' ? input.plan : '';
279
+ // Stop spinner while waiting for user approval
280
+ const spinner = ui.spinner();
281
+ spinner.stop();
282
+ try {
283
+ const result = await ui.planApproval({ planContent });
284
+ logToFile('Plan approval result:', result);
285
+ // Restart spinner after user responds
286
+ spinner.start();
287
+ if (result.approved) {
288
+ // Send session update with approved plan
289
+ if (sessionInfo) {
290
+ sendSessionUpdate(sessionInfo.sessionId, planContent, sessionInfo.accessToken, sessionInfo.orgId);
291
+ }
292
+ // User approved the plan - allow the tool
293
+ return {
294
+ behavior: 'allow',
295
+ updatedInput: input,
296
+ };
297
+ }
298
+ else {
299
+ // User rejected with feedback - deny the tool
300
+ return {
301
+ behavior: 'deny',
302
+ message: result.feedback || 'User rejected plan',
303
+ };
304
+ }
305
+ }
306
+ catch (error) {
307
+ logToFile('Error in plan approval:', error);
308
+ return {
309
+ behavior: 'deny',
310
+ message: 'Failed to get user response',
311
+ };
312
+ }
313
+ }
314
+ /**
315
+ * Tools that are automatically approved without user confirmation
316
+ */
317
+ const AUTO_APPROVED_TOOLS = new Set([
318
+ 'mcp__raindrop-wizard__CompleteIntegration',
319
+ 'mcp__raindrop-wizard__LoadPythonDocumentation',
320
+ 'mcp__raindrop-wizard__LoadTypeScriptDocumentation',
321
+ 'mcp__raindrop-wizard__LoadVercelAiSdkDocumentation',
322
+ 'mcp__raindrop-wizard__LoadBrowserDocumentation',
323
+ 'mcp__raindrop-wizard__InitializeSession',
324
+ 'EnterPlanMode',
325
+ ]);
326
+ /**
327
+ * Check if a bash command matches a safe pattern and can be auto-approved.
328
+ */
329
+ function isSafeBashCommand(command) {
330
+ const trimmed = command.trim();
331
+ return SAFE_BASH_PATTERNS.some((pattern) => {
332
+ if (pattern.startsWith('*')) {
333
+ // Suffix match (e.g., '*--version')
334
+ return trimmed.endsWith(pattern.slice(1));
335
+ }
336
+ else if (pattern.endsWith('*')) {
337
+ // Prefix match (e.g., 'npm install*')
338
+ return trimmed.startsWith(pattern.slice(0, -1));
339
+ }
340
+ else {
341
+ // Exact match
342
+ return trimmed === pattern;
343
+ }
344
+ });
345
+ }
346
+ /**
347
+ * Create a canUseTool handler that integrates with the UI for approvals.
348
+ * - Handles AskUserQuestion by showing clarifying questions UI
349
+ * - Handles ExitPlanMode by showing plan approval UI
350
+ * - Handles WebSearch by restricting to allowed domains
351
+ * - Shows approval UI for other tools
352
+ * - Supports caching file approvals for "allow all edits to this file"
353
+ */
354
+ export function createCanUseToolHandler(sessionInfo, approvedFilesCache) {
355
+ return async (toolName, input) => {
356
+ const inputRecord = input;
357
+ logToFile('canUseTool called:', { toolName, input: inputRecord });
358
+ if (AUTO_APPROVED_TOOLS.has(toolName)) {
359
+ return {
360
+ behavior: 'allow',
361
+ updatedInput: inputRecord,
362
+ };
363
+ }
364
+ // Auto-approve safe bash commands
365
+ if (toolName === 'Bash' && typeof inputRecord.command === 'string') {
366
+ // Check if command matches safe patterns
367
+ if (isSafeBashCommand(inputRecord.command)) {
368
+ logToFile('Auto-approving safe bash command:', inputRecord.command);
369
+ return {
370
+ behavior: 'allow',
371
+ updatedInput: inputRecord,
372
+ };
373
+ }
374
+ // Check if command pattern has been approved for the session
375
+ if (isCommandApprovedForSession(inputRecord.command)) {
376
+ logToFile('Auto-approving bash command based on session approval:', inputRecord.command);
377
+ return {
378
+ behavior: 'allow',
379
+ updatedInput: inputRecord,
380
+ };
381
+ }
382
+ }
383
+ // Handle WebSearch by adding allowed domains restriction
384
+ if (toolName === 'WebSearch') {
385
+ return {
386
+ behavior: 'allow',
387
+ updatedInput: {
388
+ ...inputRecord,
389
+ allowed_domains: ['raindrop.ai/docs', 'ai-sdk.dev/docs/'],
390
+ },
391
+ };
392
+ }
393
+ // Handle AskUserQuestion specially
394
+ if (toolName === 'AskUserQuestion') {
395
+ return handleClarifyingQuestions(inputRecord);
396
+ }
397
+ // Handle ExitPlanMode specially
398
+ if (toolName === 'ExitPlanMode') {
399
+ return handlePlanApproval(inputRecord, sessionInfo);
400
+ }
401
+ // Check if file has been granted blanket approval for this session
402
+ if (toolName === 'Edit' || toolName === 'Write') {
403
+ const fileName = typeof inputRecord.file_path === 'string'
404
+ ? inputRecord.file_path
405
+ : typeof inputRecord.path === 'string'
406
+ ? inputRecord.path
407
+ : undefined;
408
+ if (fileName && approvedFilesForSession.has(fileName)) {
409
+ logToFile('Auto-approving edit to previously approved file:', fileName);
410
+ return {
411
+ behavior: 'allow',
412
+ updatedInput: inputRecord,
413
+ };
414
+ }
415
+ }
416
+ // Show approval UI for other tools
417
+ return handleToolApproval(toolName, inputRecord, approvedFilesCache);
418
+ };
419
+ }
420
+ //# sourceMappingURL=handlers.js.map