gencode-ai 0.1.3 → 0.3.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.
Files changed (260) hide show
  1. package/README.md +2 -1
  2. package/dist/agent/agent.d.ts +44 -2
  3. package/dist/agent/agent.d.ts.map +1 -1
  4. package/dist/agent/agent.js +130 -11
  5. package/dist/agent/agent.js.map +1 -1
  6. package/dist/agent/types.d.ts +11 -1
  7. package/dist/agent/types.d.ts.map +1 -1
  8. package/dist/checkpointing/checkpoint-manager.d.ts +87 -0
  9. package/dist/checkpointing/checkpoint-manager.d.ts.map +1 -0
  10. package/dist/checkpointing/checkpoint-manager.js +281 -0
  11. package/dist/checkpointing/checkpoint-manager.js.map +1 -0
  12. package/dist/checkpointing/index.d.ts +29 -0
  13. package/dist/checkpointing/index.d.ts.map +1 -0
  14. package/dist/checkpointing/index.js +29 -0
  15. package/dist/checkpointing/index.js.map +1 -0
  16. package/dist/checkpointing/types.d.ts +98 -0
  17. package/dist/checkpointing/types.d.ts.map +1 -0
  18. package/dist/checkpointing/types.js +7 -0
  19. package/dist/checkpointing/types.js.map +1 -0
  20. package/dist/cli/components/App.d.ts.map +1 -1
  21. package/dist/cli/components/App.js +171 -14
  22. package/dist/cli/components/App.js.map +1 -1
  23. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
  24. package/dist/cli/components/CommandSuggestions.js +5 -0
  25. package/dist/cli/components/CommandSuggestions.js.map +1 -1
  26. package/dist/cli/components/Messages.d.ts +7 -1
  27. package/dist/cli/components/Messages.d.ts.map +1 -1
  28. package/dist/cli/components/Messages.js +12 -3
  29. package/dist/cli/components/Messages.js.map +1 -1
  30. package/dist/cli/components/ModeIndicator.d.ts +42 -0
  31. package/dist/cli/components/ModeIndicator.d.ts.map +1 -0
  32. package/dist/cli/components/ModeIndicator.js +52 -0
  33. package/dist/cli/components/ModeIndicator.js.map +1 -0
  34. package/dist/cli/components/ModelSelector.d.ts +4 -3
  35. package/dist/cli/components/ModelSelector.d.ts.map +1 -1
  36. package/dist/cli/components/ModelSelector.js +54 -37
  37. package/dist/cli/components/ModelSelector.js.map +1 -1
  38. package/dist/cli/components/PlanApproval.d.ts +36 -0
  39. package/dist/cli/components/PlanApproval.d.ts.map +1 -0
  40. package/dist/cli/components/PlanApproval.js +154 -0
  41. package/dist/cli/components/PlanApproval.js.map +1 -0
  42. package/dist/cli/components/ProviderManager.d.ts +2 -2
  43. package/dist/cli/components/ProviderManager.d.ts.map +1 -1
  44. package/dist/cli/components/ProviderManager.js +137 -156
  45. package/dist/cli/components/ProviderManager.js.map +1 -1
  46. package/dist/cli/components/theme.d.ts +2 -0
  47. package/dist/cli/components/theme.d.ts.map +1 -1
  48. package/dist/cli/components/theme.js +3 -0
  49. package/dist/cli/components/theme.js.map +1 -1
  50. package/dist/cli/index.js +30 -13
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/config/index.d.ts +2 -2
  53. package/dist/config/index.d.ts.map +1 -1
  54. package/dist/config/index.js +1 -1
  55. package/dist/config/index.js.map +1 -1
  56. package/dist/config/levels.d.ts +5 -5
  57. package/dist/config/levels.d.ts.map +1 -1
  58. package/dist/config/levels.js +20 -20
  59. package/dist/config/levels.js.map +1 -1
  60. package/dist/config/merger.js +1 -1
  61. package/dist/config/merger.js.map +1 -1
  62. package/dist/config/providers-config.d.ts +8 -5
  63. package/dist/config/providers-config.d.ts.map +1 -1
  64. package/dist/config/providers-config.js +19 -22
  65. package/dist/config/providers-config.js.map +1 -1
  66. package/dist/config/test-utils.d.ts +2 -2
  67. package/dist/config/test-utils.d.ts.map +1 -1
  68. package/dist/config/test-utils.js +4 -4
  69. package/dist/config/test-utils.js.map +1 -1
  70. package/dist/config/types.d.ts +23 -17
  71. package/dist/config/types.d.ts.map +1 -1
  72. package/dist/config/types.js +14 -14
  73. package/dist/config/types.js.map +1 -1
  74. package/dist/index.d.ts +1 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/memory/memory-manager.d.ts +25 -12
  79. package/dist/memory/memory-manager.d.ts.map +1 -1
  80. package/dist/memory/memory-manager.js +241 -112
  81. package/dist/memory/memory-manager.js.map +1 -1
  82. package/dist/memory/test-utils.d.ts +1 -1
  83. package/dist/memory/test-utils.d.ts.map +1 -1
  84. package/dist/memory/test-utils.js +3 -3
  85. package/dist/memory/test-utils.js.map +1 -1
  86. package/dist/memory/types.d.ts +20 -10
  87. package/dist/memory/types.d.ts.map +1 -1
  88. package/dist/memory/types.js +13 -13
  89. package/dist/memory/types.js.map +1 -1
  90. package/dist/migration/migrate.d.ts +24 -0
  91. package/dist/migration/migrate.d.ts.map +1 -0
  92. package/dist/migration/migrate.js +164 -0
  93. package/dist/migration/migrate.js.map +1 -0
  94. package/dist/permissions/persistence.d.ts +2 -2
  95. package/dist/permissions/persistence.js +4 -4
  96. package/dist/permissions/persistence.js.map +1 -1
  97. package/dist/planning/index.d.ts +13 -0
  98. package/dist/planning/index.d.ts.map +1 -0
  99. package/dist/planning/index.js +15 -0
  100. package/dist/planning/index.js.map +1 -0
  101. package/dist/planning/plan-file.d.ts +59 -0
  102. package/dist/planning/plan-file.d.ts.map +1 -0
  103. package/dist/planning/plan-file.js +278 -0
  104. package/dist/planning/plan-file.js.map +1 -0
  105. package/dist/planning/state.d.ts +127 -0
  106. package/dist/planning/state.d.ts.map +1 -0
  107. package/dist/planning/state.js +261 -0
  108. package/dist/planning/state.js.map +1 -0
  109. package/dist/planning/tools/enter-plan-mode.d.ts +25 -0
  110. package/dist/planning/tools/enter-plan-mode.d.ts.map +1 -0
  111. package/dist/planning/tools/enter-plan-mode.js +98 -0
  112. package/dist/planning/tools/enter-plan-mode.js.map +1 -0
  113. package/dist/planning/tools/exit-plan-mode.d.ts +24 -0
  114. package/dist/planning/tools/exit-plan-mode.d.ts.map +1 -0
  115. package/dist/planning/tools/exit-plan-mode.js +149 -0
  116. package/dist/planning/tools/exit-plan-mode.js.map +1 -0
  117. package/dist/planning/types.d.ts +100 -0
  118. package/dist/planning/types.d.ts.map +1 -0
  119. package/dist/planning/types.js +28 -0
  120. package/dist/planning/types.js.map +1 -0
  121. package/dist/pricing/calculator.d.ts +21 -0
  122. package/dist/pricing/calculator.d.ts.map +1 -0
  123. package/dist/pricing/calculator.js +59 -0
  124. package/dist/pricing/calculator.js.map +1 -0
  125. package/dist/pricing/index.d.ts +7 -0
  126. package/dist/pricing/index.d.ts.map +1 -0
  127. package/dist/pricing/index.js +7 -0
  128. package/dist/pricing/index.js.map +1 -0
  129. package/dist/pricing/models.d.ts +20 -0
  130. package/dist/pricing/models.d.ts.map +1 -0
  131. package/dist/pricing/models.js +322 -0
  132. package/dist/pricing/models.js.map +1 -0
  133. package/dist/pricing/types.d.ts +30 -0
  134. package/dist/pricing/types.d.ts.map +1 -0
  135. package/dist/pricing/types.js +5 -0
  136. package/dist/pricing/types.js.map +1 -0
  137. package/dist/prompts/index.d.ts +5 -4
  138. package/dist/prompts/index.d.ts.map +1 -1
  139. package/dist/prompts/index.js +11 -8
  140. package/dist/prompts/index.js.map +1 -1
  141. package/dist/providers/anthropic.d.ts +2 -1
  142. package/dist/providers/anthropic.d.ts.map +1 -1
  143. package/dist/providers/anthropic.js +24 -10
  144. package/dist/providers/anthropic.js.map +1 -1
  145. package/dist/providers/gemini.d.ts +2 -1
  146. package/dist/providers/gemini.d.ts.map +1 -1
  147. package/dist/providers/gemini.js +28 -14
  148. package/dist/providers/gemini.js.map +1 -1
  149. package/dist/providers/index.d.ts +20 -10
  150. package/dist/providers/index.d.ts.map +1 -1
  151. package/dist/providers/index.js +48 -24
  152. package/dist/providers/index.js.map +1 -1
  153. package/dist/providers/openai.d.ts +2 -1
  154. package/dist/providers/openai.d.ts.map +1 -1
  155. package/dist/providers/openai.js +19 -8
  156. package/dist/providers/openai.js.map +1 -1
  157. package/dist/providers/registry.d.ts +48 -34
  158. package/dist/providers/registry.d.ts.map +1 -1
  159. package/dist/providers/registry.js +72 -88
  160. package/dist/providers/registry.js.map +1 -1
  161. package/dist/providers/store.d.ts +43 -17
  162. package/dist/providers/store.d.ts.map +1 -1
  163. package/dist/providers/store.js +112 -19
  164. package/dist/providers/store.js.map +1 -1
  165. package/dist/providers/types.d.ts +25 -0
  166. package/dist/providers/types.d.ts.map +1 -1
  167. package/dist/providers/vertex-ai.d.ts +15 -7
  168. package/dist/providers/vertex-ai.d.ts.map +1 -1
  169. package/dist/providers/vertex-ai.js +63 -23
  170. package/dist/providers/vertex-ai.js.map +1 -1
  171. package/dist/session/manager.d.ts +4 -0
  172. package/dist/session/manager.d.ts.map +1 -1
  173. package/dist/session/manager.js +8 -0
  174. package/dist/session/manager.js.map +1 -1
  175. package/dist/session/types.js +1 -1
  176. package/dist/session/types.js.map +1 -1
  177. package/dist/tools/index.d.ts +7 -1
  178. package/dist/tools/index.d.ts.map +1 -1
  179. package/dist/tools/index.js +7 -0
  180. package/dist/tools/index.js.map +1 -1
  181. package/dist/tools/registry.d.ts +13 -0
  182. package/dist/tools/registry.d.ts.map +1 -1
  183. package/dist/tools/registry.js +79 -2
  184. package/dist/tools/registry.js.map +1 -1
  185. package/docs/config-system-comparison.md +50 -50
  186. package/docs/cost-tracking-comparison.md +904 -0
  187. package/docs/memory-system.md +124 -31
  188. package/docs/operating-modes.md +96 -0
  189. package/docs/permissions.md +2 -2
  190. package/docs/proposals/0006-memory-system.md +4 -4
  191. package/docs/proposals/0008-checkpointing.md +109 -2
  192. package/docs/proposals/0011-custom-commands.md +2 -1
  193. package/docs/proposals/0021-skills-system.md +2 -1
  194. package/docs/proposals/0023-permission-enhancements.md +2 -2
  195. package/docs/proposals/0025-cost-tracking.md +60 -2
  196. package/docs/proposals/0033-enterprise-deployment.md +1 -1
  197. package/docs/proposals/0041-configuration-system.md +17 -19
  198. package/docs/proposals/0042-prompt-optimization.md +17 -9
  199. package/docs/proposals/README.md +6 -6
  200. package/docs/providers.md +94 -9
  201. package/examples/test-checkpointing.ts +121 -0
  202. package/examples/test-cost-tracking.ts +77 -0
  203. package/examples/test-interrupt-cleanup.ts +94 -0
  204. package/package.json +3 -2
  205. package/scripts/migrate.ts +449 -0
  206. package/src/agent/agent.ts +161 -12
  207. package/src/agent/types.ts +11 -1
  208. package/src/checkpointing/checkpoint-manager.ts +327 -0
  209. package/src/checkpointing/index.ts +45 -0
  210. package/src/checkpointing/types.ts +104 -0
  211. package/src/cli/components/App.tsx +221 -13
  212. package/src/cli/components/CommandSuggestions.tsx +5 -0
  213. package/src/cli/components/Messages.tsx +24 -5
  214. package/src/cli/components/ModeIndicator.tsx +174 -0
  215. package/src/cli/components/ModelSelector.tsx +62 -43
  216. package/src/cli/components/PlanApproval.tsx +327 -0
  217. package/src/cli/components/ProviderManager.tsx +278 -323
  218. package/src/cli/components/theme.ts +3 -0
  219. package/src/cli/index.tsx +36 -17
  220. package/src/config/index.ts +5 -3
  221. package/src/config/levels.test.ts +22 -22
  222. package/src/config/levels.ts +22 -22
  223. package/src/config/loader.test.ts +14 -14
  224. package/src/config/manager.test.ts +19 -19
  225. package/src/config/merger.test.ts +23 -23
  226. package/src/config/merger.ts +1 -1
  227. package/src/config/providers-config.ts +23 -21
  228. package/src/config/test-utils.ts +6 -6
  229. package/src/config/types.ts +30 -20
  230. package/src/index.ts +15 -0
  231. package/src/memory/memory-manager.test.ts +242 -24
  232. package/src/memory/memory-manager.ts +270 -141
  233. package/src/memory/test-utils.ts +4 -4
  234. package/src/memory/types.ts +28 -17
  235. package/src/permissions/persistence.ts +4 -4
  236. package/src/planning/index.ts +53 -0
  237. package/src/planning/plan-file.ts +326 -0
  238. package/src/planning/state.ts +305 -0
  239. package/src/planning/tools/enter-plan-mode.ts +111 -0
  240. package/src/planning/tools/exit-plan-mode.ts +170 -0
  241. package/src/planning/types.ts +150 -0
  242. package/src/pricing/calculator.ts +71 -0
  243. package/src/pricing/index.ts +7 -0
  244. package/src/pricing/models.ts +334 -0
  245. package/src/pricing/types.ts +32 -0
  246. package/src/prompts/index.ts +13 -9
  247. package/src/providers/anthropic.ts +30 -10
  248. package/src/providers/gemini.ts +34 -14
  249. package/src/providers/index.ts +76 -33
  250. package/src/providers/openai.ts +26 -8
  251. package/src/providers/registry.ts +116 -111
  252. package/src/providers/store.ts +130 -28
  253. package/src/providers/types.ts +36 -1
  254. package/src/providers/vertex-ai.ts +70 -23
  255. package/src/session/manager.ts +9 -0
  256. package/src/session/types.ts +1 -1
  257. package/src/tools/index.ts +8 -0
  258. package/src/tools/registry.ts +95 -2
  259. package/.gencode/settings.local.json +0 -7
  260. package/CLAUDE.md +0 -86
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Plan Mode State Management
3
+ *
4
+ * Singleton state manager for plan mode. Tracks whether plan mode
5
+ * is active, current phase, and manages tool filtering.
6
+ */
7
+
8
+ import type {
9
+ PlanModeState,
10
+ PlanPhase,
11
+ AllowedPrompt,
12
+ ModeType,
13
+ PlanModeEvent,
14
+ } from './types.js';
15
+ import { PLAN_MODE_ALLOWED_TOOLS, PLAN_MODE_BLOCKED_TOOLS } from './types.js';
16
+
17
+ // ============================================================================
18
+ // State Manager
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Plan Mode State Manager
23
+ *
24
+ * Manages the plan mode state and provides methods for:
25
+ * - Entering/exiting plan mode
26
+ * - Phase transitions
27
+ * - Tool filtering
28
+ * - Event notifications
29
+ */
30
+ export class PlanModeManager {
31
+ private state: PlanModeState;
32
+ private eventListeners: Set<(event: PlanModeEvent) => void>;
33
+
34
+ constructor() {
35
+ this.state = this.getInitialState();
36
+ this.eventListeners = new Set();
37
+ }
38
+
39
+ /**
40
+ * Get initial state
41
+ */
42
+ private getInitialState(): PlanModeState {
43
+ return {
44
+ active: false,
45
+ phase: 'understanding',
46
+ planFilePath: null,
47
+ originalRequest: null,
48
+ requestedPermissions: [],
49
+ enteredAt: null,
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Get current state (readonly)
55
+ */
56
+ getState(): Readonly<PlanModeState> {
57
+ return { ...this.state };
58
+ }
59
+
60
+ /**
61
+ * Check if plan mode is active
62
+ */
63
+ isActive(): boolean {
64
+ return this.state.active;
65
+ }
66
+
67
+ /**
68
+ * Get current mode type for UI
69
+ * Note: Returns 'plan' or 'normal'. 'accept' mode is managed at the App level.
70
+ */
71
+ getCurrentMode(): ModeType {
72
+ return this.state.active ? 'plan' : 'normal';
73
+ }
74
+
75
+ /**
76
+ * Enter plan mode
77
+ */
78
+ enter(planFilePath: string, originalRequest?: string): void {
79
+ this.state = {
80
+ active: true,
81
+ phase: 'understanding',
82
+ planFilePath,
83
+ originalRequest: originalRequest ?? null,
84
+ requestedPermissions: [],
85
+ enteredAt: new Date(),
86
+ };
87
+
88
+ this.emit({ type: 'enter', planFilePath });
89
+ }
90
+
91
+ /**
92
+ * Exit plan mode
93
+ */
94
+ exit(approved: boolean = false): void {
95
+ const wasActive = this.state.active;
96
+ this.state = this.getInitialState();
97
+
98
+ if (wasActive) {
99
+ this.emit({ type: 'exit', approved });
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Toggle plan mode (for Shift+Tab)
105
+ */
106
+ toggle(planFilePath?: string): void {
107
+ if (this.state.active) {
108
+ this.exit(false);
109
+ } else if (planFilePath) {
110
+ this.enter(planFilePath);
111
+ }
112
+ this.emit({ type: 'toggle' });
113
+ }
114
+
115
+ /**
116
+ * Update phase
117
+ */
118
+ setPhase(phase: PlanPhase): void {
119
+ if (this.state.active) {
120
+ this.state.phase = phase;
121
+ this.emit({ type: 'phase_change', phase });
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Get current phase
127
+ */
128
+ getPhase(): PlanPhase {
129
+ return this.state.phase;
130
+ }
131
+
132
+ /**
133
+ * Set requested permissions (from ExitPlanMode)
134
+ */
135
+ setRequestedPermissions(permissions: AllowedPrompt[]): void {
136
+ this.state.requestedPermissions = permissions;
137
+ }
138
+
139
+ /**
140
+ * Get requested permissions
141
+ */
142
+ getRequestedPermissions(): AllowedPrompt[] {
143
+ return [...this.state.requestedPermissions];
144
+ }
145
+
146
+ /**
147
+ * Get plan file path
148
+ */
149
+ getPlanFilePath(): string | null {
150
+ return this.state.planFilePath;
151
+ }
152
+
153
+ // ============================================================================
154
+ // Tool Filtering
155
+ // ============================================================================
156
+
157
+ /**
158
+ * Check if a tool is allowed in the current mode
159
+ */
160
+ isToolAllowed(toolName: string): boolean {
161
+ if (!this.state.active) {
162
+ return true; // All tools allowed in build mode
163
+ }
164
+
165
+ // In plan mode, only allow read-only tools
166
+ return (PLAN_MODE_ALLOWED_TOOLS as readonly string[]).includes(toolName);
167
+ }
168
+
169
+ /**
170
+ * Check if a tool is blocked in the current mode
171
+ */
172
+ isToolBlocked(toolName: string): boolean {
173
+ if (!this.state.active) {
174
+ return false; // No tools blocked in build mode
175
+ }
176
+
177
+ return (PLAN_MODE_BLOCKED_TOOLS as readonly string[]).includes(toolName);
178
+ }
179
+
180
+ /**
181
+ * Get list of allowed tools for current mode
182
+ */
183
+ getAllowedTools(): string[] {
184
+ if (!this.state.active) {
185
+ return []; // Empty means all allowed
186
+ }
187
+ return [...PLAN_MODE_ALLOWED_TOOLS];
188
+ }
189
+
190
+ /**
191
+ * Get list of blocked tools for current mode
192
+ */
193
+ getBlockedTools(): string[] {
194
+ if (!this.state.active) {
195
+ return [];
196
+ }
197
+ return [...PLAN_MODE_BLOCKED_TOOLS];
198
+ }
199
+
200
+ /**
201
+ * Filter tool list based on current mode
202
+ */
203
+ filterTools(toolNames: string[]): string[] {
204
+ if (!this.state.active) {
205
+ return toolNames;
206
+ }
207
+
208
+ return toolNames.filter((name) => this.isToolAllowed(name));
209
+ }
210
+
211
+ // ============================================================================
212
+ // Event System
213
+ // ============================================================================
214
+
215
+ /**
216
+ * Subscribe to plan mode events
217
+ */
218
+ subscribe(listener: (event: PlanModeEvent) => void): () => void {
219
+ this.eventListeners.add(listener);
220
+ return () => {
221
+ this.eventListeners.delete(listener);
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Emit an event to all listeners
227
+ */
228
+ private emit(event: PlanModeEvent): void {
229
+ for (const listener of this.eventListeners) {
230
+ try {
231
+ listener(event);
232
+ } catch (error) {
233
+ console.error('Error in plan mode event listener:', error);
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ // ============================================================================
240
+ // Singleton Instance
241
+ // ============================================================================
242
+
243
+ /**
244
+ * Global plan mode manager instance
245
+ */
246
+ let globalPlanModeManager: PlanModeManager | null = null;
247
+
248
+ /**
249
+ * Get the global plan mode manager
250
+ */
251
+ export function getPlanModeManager(): PlanModeManager {
252
+ if (!globalPlanModeManager) {
253
+ globalPlanModeManager = new PlanModeManager();
254
+ }
255
+ return globalPlanModeManager;
256
+ }
257
+
258
+ /**
259
+ * Reset the global plan mode manager (for testing)
260
+ */
261
+ export function resetPlanModeManager(): void {
262
+ if (globalPlanModeManager) {
263
+ globalPlanModeManager.exit(false);
264
+ }
265
+ globalPlanModeManager = null;
266
+ }
267
+
268
+ // ============================================================================
269
+ // Convenience Functions
270
+ // ============================================================================
271
+
272
+ /**
273
+ * Check if plan mode is currently active
274
+ */
275
+ export function isPlanModeActive(): boolean {
276
+ return getPlanModeManager().isActive();
277
+ }
278
+
279
+ /**
280
+ * Get the current mode type
281
+ */
282
+ export function getCurrentMode(): ModeType {
283
+ return getPlanModeManager().getCurrentMode();
284
+ }
285
+
286
+ /**
287
+ * Enter plan mode
288
+ */
289
+ export function enterPlanMode(planFilePath: string, originalRequest?: string): void {
290
+ getPlanModeManager().enter(planFilePath, originalRequest);
291
+ }
292
+
293
+ /**
294
+ * Exit plan mode
295
+ */
296
+ export function exitPlanMode(approved: boolean = false): void {
297
+ getPlanModeManager().exit(approved);
298
+ }
299
+
300
+ /**
301
+ * Toggle plan mode
302
+ */
303
+ export function togglePlanMode(planFilePath?: string): void {
304
+ getPlanModeManager().toggle(planFilePath);
305
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * EnterPlanMode Tool
3
+ *
4
+ * Transitions the agent into plan mode for designing implementation
5
+ * approaches before writing code. In plan mode, only read-only tools
6
+ * are available.
7
+ */
8
+
9
+ import { z } from 'zod';
10
+ import type { Tool, ToolResult, ToolContext } from '../../tools/types.js';
11
+ import { getPlanModeManager } from '../state.js';
12
+ import { createPlanFile, getDisplayPath } from '../plan-file.js';
13
+ import { PLAN_MODE_ALLOWED_TOOLS, PLAN_MODE_BLOCKED_TOOLS } from '../types.js';
14
+
15
+ // ============================================================================
16
+ // Tool Definition
17
+ // ============================================================================
18
+
19
+ const EnterPlanModeInputSchema = z.object({});
20
+
21
+ export type EnterPlanModeInput = z.infer<typeof EnterPlanModeInputSchema>;
22
+
23
+ /**
24
+ * EnterPlanMode Tool
25
+ *
26
+ * Use this tool when you need to plan an implementation before writing code.
27
+ * This is recommended for:
28
+ * - New feature implementations
29
+ * - Tasks with multiple valid approaches
30
+ * - Architectural decisions
31
+ * - Multi-file changes
32
+ * - Unclear requirements
33
+ */
34
+ export const enterPlanModeTool: Tool<EnterPlanModeInput> = {
35
+ name: 'EnterPlanMode',
36
+
37
+ description: `Transition into plan mode to design an implementation approach before writing code.
38
+
39
+ Use this tool when:
40
+ - Implementing new features that require design decisions
41
+ - Tasks with multiple valid approaches
42
+ - Changes that affect existing behavior or structure
43
+ - Architectural decisions between patterns or technologies
44
+ - Multi-file changes (more than 2-3 files)
45
+ - Unclear requirements that need exploration
46
+
47
+ In plan mode:
48
+ - Only read-only tools are available (Read, Glob, Grep, WebFetch, WebSearch, TodoWrite, AskUserQuestion)
49
+ - Write, Edit, and Bash tools are blocked
50
+ - You can explore the codebase and design an approach
51
+ - Use ExitPlanMode when ready to request user approval
52
+
53
+ This tool REQUIRES user approval before entering plan mode.`,
54
+
55
+ parameters: EnterPlanModeInputSchema,
56
+
57
+ async execute(_input: EnterPlanModeInput, context: ToolContext): Promise<ToolResult> {
58
+ const manager = getPlanModeManager();
59
+
60
+ // Check if already in plan mode
61
+ if (manager.isActive()) {
62
+ return {
63
+ success: false,
64
+ output: '',
65
+ error: 'Already in plan mode. Use ExitPlanMode to exit first.',
66
+ };
67
+ }
68
+
69
+ try {
70
+ // Create plan file
71
+ const planFile = await createPlanFile(context.cwd);
72
+ const displayPath = getDisplayPath(planFile.path, context.cwd);
73
+
74
+ // Enter plan mode
75
+ manager.enter(planFile.path);
76
+
77
+ // Build response
78
+ const output = `
79
+ Entered PLAN mode.
80
+
81
+ Plan file: ${displayPath}
82
+
83
+ In plan mode, you have access to read-only tools for exploration:
84
+ ${PLAN_MODE_ALLOWED_TOOLS.map((t) => ` - ${t}`).join('\n')}
85
+
86
+ Blocked tools (write/execute operations):
87
+ ${PLAN_MODE_BLOCKED_TOOLS.map((t) => ` - ${t}`).join('\n')}
88
+
89
+ Instructions:
90
+ 1. Explore the codebase using Read, Glob, Grep tools
91
+ 2. Research if needed using WebFetch, WebSearch
92
+ 3. Design your implementation approach
93
+ 4. Write your plan to the plan file using Write (plan file only)
94
+ 5. Call ExitPlanMode when ready for user approval
95
+
96
+ Use Shift+Tab to toggle between Plan and Build modes.
97
+ `.trim();
98
+
99
+ return {
100
+ success: true,
101
+ output,
102
+ };
103
+ } catch (error) {
104
+ return {
105
+ success: false,
106
+ output: '',
107
+ error: `Failed to enter plan mode: ${error instanceof Error ? error.message : String(error)}`,
108
+ };
109
+ }
110
+ },
111
+ };
@@ -0,0 +1,170 @@
1
+ /**
2
+ * ExitPlanMode Tool
3
+ *
4
+ * Exits plan mode and requests user approval for the implementation plan.
5
+ * Supports pre-approving permissions for bash commands needed during execution.
6
+ */
7
+
8
+ import { z } from 'zod';
9
+ import type { Tool, ToolResult, ToolContext } from '../../tools/types.js';
10
+ import { getPlanModeManager } from '../state.js';
11
+ import { readPlanFile, parseFilesToChange, parsePreApprovedPermissions, getDisplayPath } from '../plan-file.js';
12
+ import type { AllowedPrompt } from '../types.js';
13
+
14
+ // ============================================================================
15
+ // Tool Definition
16
+ // ============================================================================
17
+
18
+ const AllowedPromptSchema = z.object({
19
+ tool: z.literal('Bash').describe('The tool this permission applies to'),
20
+ prompt: z.string().describe('Semantic description of the action, e.g., "run tests", "install dependencies"'),
21
+ });
22
+
23
+ const ExitPlanModeInputSchema = z.object({
24
+ allowedPrompts: z
25
+ .array(AllowedPromptSchema)
26
+ .optional()
27
+ .describe('Prompt-based permissions needed to implement the plan'),
28
+ });
29
+
30
+ export type ExitPlanModeInput = z.infer<typeof ExitPlanModeInputSchema>;
31
+
32
+ /**
33
+ * ExitPlanMode Tool
34
+ *
35
+ * Call this when you have finished writing your plan and are ready
36
+ * for user approval. The plan file should already be written.
37
+ */
38
+ export const exitPlanModeTool: Tool<ExitPlanModeInput> = {
39
+ name: 'ExitPlanMode',
40
+
41
+ description: `Exit plan mode and request user approval for the implementation plan.
42
+
43
+ ## How This Tool Works
44
+ - You should have already written your plan to the plan file
45
+ - This tool reads the plan from the file you wrote
46
+ - The user will see the plan contents and approve/modify/cancel
47
+
48
+ ## Requesting Permissions (allowedPrompts)
49
+ Request prompt-based permissions for bash commands your plan will need:
50
+
51
+ \`\`\`json
52
+ {
53
+ "allowedPrompts": [
54
+ { "tool": "Bash", "prompt": "run tests" },
55
+ { "tool": "Bash", "prompt": "install dependencies" },
56
+ { "tool": "Bash", "prompt": "build the project" }
57
+ ]
58
+ }
59
+ \`\`\`
60
+
61
+ Guidelines for prompts:
62
+ - Use semantic descriptions that capture the action's purpose
63
+ - "run tests" matches: npm test, pytest, go test, etc.
64
+ - "install dependencies" matches: npm install, pip install, etc.
65
+ - Keep descriptions concise but descriptive
66
+ - Only request permissions you actually need
67
+ - Scope permissions narrowly
68
+
69
+ ## When to Use This Tool
70
+ Only use this tool when you have finished planning and are ready for approval.
71
+ Do NOT use this tool for research tasks - only for implementation planning.`,
72
+
73
+ parameters: ExitPlanModeInputSchema,
74
+
75
+ async execute(input: ExitPlanModeInput, context: ToolContext): Promise<ToolResult> {
76
+ const manager = getPlanModeManager();
77
+
78
+ // Check if in plan mode
79
+ if (!manager.isActive()) {
80
+ return {
81
+ success: false,
82
+ output: '',
83
+ error: 'Not in plan mode. Use EnterPlanMode first.',
84
+ };
85
+ }
86
+
87
+ const planFilePath = manager.getPlanFilePath();
88
+ if (!planFilePath) {
89
+ return {
90
+ success: false,
91
+ output: '',
92
+ error: 'No plan file found. This should not happen.',
93
+ };
94
+ }
95
+
96
+ try {
97
+ // Read plan file
98
+ const planFile = await readPlanFile(planFilePath);
99
+ if (!planFile) {
100
+ return {
101
+ success: false,
102
+ output: '',
103
+ error: `Could not read plan file: ${planFilePath}`,
104
+ };
105
+ }
106
+
107
+ // Parse plan content
108
+ const filesToChange = parseFilesToChange(planFile.content);
109
+ const planPermissions = parsePreApprovedPermissions(planFile.content);
110
+
111
+ // Combine with input permissions
112
+ const requestedPermissions: AllowedPrompt[] = [
113
+ ...planPermissions,
114
+ ...(input.allowedPrompts || []),
115
+ ];
116
+
117
+ // Store permissions for approval UI
118
+ manager.setRequestedPermissions(requestedPermissions);
119
+ manager.setPhase('approval');
120
+
121
+ // Build response for the LLM
122
+ const displayPath = getDisplayPath(planFilePath, context.cwd);
123
+ let output = `
124
+ Plan ready for approval.
125
+
126
+ Plan file: ${displayPath}
127
+
128
+ `;
129
+
130
+ // Summary of files to change
131
+ if (filesToChange.length > 0) {
132
+ output += 'Files to change:\n';
133
+ for (const file of filesToChange) {
134
+ const icon = file.action === 'create' ? '+' : file.action === 'modify' ? '~' : '-';
135
+ output += ` ${icon} ${file.path} (${file.action})\n`;
136
+ }
137
+ output += '\n';
138
+ }
139
+
140
+ // Requested permissions
141
+ if (requestedPermissions.length > 0) {
142
+ output += 'Requested permissions:\n';
143
+ for (const perm of requestedPermissions) {
144
+ output += ` - ${perm.tool}(prompt: ${perm.prompt})\n`;
145
+ }
146
+ output += '\n';
147
+ }
148
+
149
+ output += `
150
+ The user will now be prompted to:
151
+ 1. Approve - Accept plan and proceed with execution
152
+ 2. Modify - Go back and modify the plan
153
+ 3. Cancel - Exit plan mode without executing
154
+
155
+ Waiting for user decision...
156
+ `.trim();
157
+
158
+ return {
159
+ success: true,
160
+ output,
161
+ };
162
+ } catch (error) {
163
+ return {
164
+ success: false,
165
+ output: '',
166
+ error: `Failed to exit plan mode: ${error instanceof Error ? error.message : String(error)}`,
167
+ };
168
+ }
169
+ },
170
+ };