gencode-ai 0.1.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 (274) hide show
  1. package/.env.example +11 -0
  2. package/CLAUDE.md +70 -0
  3. package/LICENSE +21 -0
  4. package/README.md +117 -0
  5. package/dist/agent/agent.d.ts +84 -0
  6. package/dist/agent/agent.d.ts.map +1 -0
  7. package/dist/agent/agent.js +233 -0
  8. package/dist/agent/agent.js.map +1 -0
  9. package/dist/agent/index.d.ts +6 -0
  10. package/dist/agent/index.d.ts.map +1 -0
  11. package/dist/agent/index.js +6 -0
  12. package/dist/agent/index.js.map +1 -0
  13. package/dist/agent/types.d.ts +47 -0
  14. package/dist/agent/types.d.ts.map +1 -0
  15. package/dist/agent/types.js +5 -0
  16. package/dist/agent/types.js.map +1 -0
  17. package/dist/cli/components/App.d.ts +14 -0
  18. package/dist/cli/components/App.d.ts.map +1 -0
  19. package/dist/cli/components/App.js +395 -0
  20. package/dist/cli/components/App.js.map +1 -0
  21. package/dist/cli/components/CommandSuggestions.d.ts +13 -0
  22. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
  23. package/dist/cli/components/CommandSuggestions.js +32 -0
  24. package/dist/cli/components/CommandSuggestions.js.map +1 -0
  25. package/dist/cli/components/Header.d.ts +9 -0
  26. package/dist/cli/components/Header.d.ts.map +1 -0
  27. package/dist/cli/components/Header.js +13 -0
  28. package/dist/cli/components/Header.js.map +1 -0
  29. package/dist/cli/components/Input.d.ts +13 -0
  30. package/dist/cli/components/Input.d.ts.map +1 -0
  31. package/dist/cli/components/Input.js +27 -0
  32. package/dist/cli/components/Input.js.map +1 -0
  33. package/dist/cli/components/Logo.d.ts +2 -0
  34. package/dist/cli/components/Logo.d.ts.map +1 -0
  35. package/dist/cli/components/Logo.js +8 -0
  36. package/dist/cli/components/Logo.js.map +1 -0
  37. package/dist/cli/components/Messages.d.ts +37 -0
  38. package/dist/cli/components/Messages.d.ts.map +1 -0
  39. package/dist/cli/components/Messages.js +106 -0
  40. package/dist/cli/components/Messages.js.map +1 -0
  41. package/dist/cli/components/ModelSelector.d.ts +13 -0
  42. package/dist/cli/components/ModelSelector.d.ts.map +1 -0
  43. package/dist/cli/components/ModelSelector.js +72 -0
  44. package/dist/cli/components/ModelSelector.js.map +1 -0
  45. package/dist/cli/components/Spinner.d.ts +12 -0
  46. package/dist/cli/components/Spinner.d.ts.map +1 -0
  47. package/dist/cli/components/Spinner.js +45 -0
  48. package/dist/cli/components/Spinner.js.map +1 -0
  49. package/dist/cli/components/index.d.ts +12 -0
  50. package/dist/cli/components/index.d.ts.map +1 -0
  51. package/dist/cli/components/index.js +12 -0
  52. package/dist/cli/components/index.js.map +1 -0
  53. package/dist/cli/components/theme.d.ts +31 -0
  54. package/dist/cli/components/theme.d.ts.map +1 -0
  55. package/dist/cli/components/theme.js +36 -0
  56. package/dist/cli/components/theme.js.map +1 -0
  57. package/dist/cli/index-legacy.d.ts +7 -0
  58. package/dist/cli/index-legacy.d.ts.map +1 -0
  59. package/dist/cli/index-legacy.js +431 -0
  60. package/dist/cli/index-legacy.js.map +1 -0
  61. package/dist/cli/index.d.ts +7 -0
  62. package/dist/cli/index.d.ts.map +1 -0
  63. package/dist/cli/index.js +116 -0
  64. package/dist/cli/index.js.map +1 -0
  65. package/dist/cli/ink-cli.d.ts +7 -0
  66. package/dist/cli/ink-cli.d.ts.map +1 -0
  67. package/dist/cli/ink-cli.js +105 -0
  68. package/dist/cli/ink-cli.js.map +1 -0
  69. package/dist/cli/session-picker.d.ts +16 -0
  70. package/dist/cli/session-picker.d.ts.map +1 -0
  71. package/dist/cli/session-picker.js +280 -0
  72. package/dist/cli/session-picker.js.map +1 -0
  73. package/dist/cli/ui.d.ts +61 -0
  74. package/dist/cli/ui.d.ts.map +1 -0
  75. package/dist/cli/ui.js +364 -0
  76. package/dist/cli/ui.js.map +1 -0
  77. package/dist/config/index.d.ts +7 -0
  78. package/dist/config/index.d.ts.map +1 -0
  79. package/dist/config/index.js +6 -0
  80. package/dist/config/index.js.map +1 -0
  81. package/dist/config/manager.d.ts +31 -0
  82. package/dist/config/manager.d.ts.map +1 -0
  83. package/dist/config/manager.js +65 -0
  84. package/dist/config/manager.js.map +1 -0
  85. package/dist/config/types.d.ts +22 -0
  86. package/dist/config/types.d.ts.map +1 -0
  87. package/dist/config/types.js +6 -0
  88. package/dist/config/types.js.map +1 -0
  89. package/dist/index.d.ts +12 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +21 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/memory/index.d.ts +10 -0
  94. package/dist/memory/index.d.ts.map +1 -0
  95. package/dist/memory/index.js +9 -0
  96. package/dist/memory/index.js.map +1 -0
  97. package/dist/memory/init.d.ts +20 -0
  98. package/dist/memory/init.d.ts.map +1 -0
  99. package/dist/memory/init.js +332 -0
  100. package/dist/memory/init.js.map +1 -0
  101. package/dist/memory/manager.d.ts +85 -0
  102. package/dist/memory/manager.d.ts.map +1 -0
  103. package/dist/memory/manager.js +234 -0
  104. package/dist/memory/manager.js.map +1 -0
  105. package/dist/memory/types.d.ts +74 -0
  106. package/dist/memory/types.d.ts.map +1 -0
  107. package/dist/memory/types.js +6 -0
  108. package/dist/memory/types.js.map +1 -0
  109. package/dist/permissions/index.d.ts +7 -0
  110. package/dist/permissions/index.d.ts.map +1 -0
  111. package/dist/permissions/index.js +6 -0
  112. package/dist/permissions/index.js.map +1 -0
  113. package/dist/permissions/manager.d.ts +32 -0
  114. package/dist/permissions/manager.d.ts.map +1 -0
  115. package/dist/permissions/manager.js +79 -0
  116. package/dist/permissions/manager.js.map +1 -0
  117. package/dist/permissions/types.d.ts +14 -0
  118. package/dist/permissions/types.d.ts.map +1 -0
  119. package/dist/permissions/types.js +17 -0
  120. package/dist/permissions/types.js.map +1 -0
  121. package/dist/providers/anthropic.d.ts +20 -0
  122. package/dist/providers/anthropic.d.ts.map +1 -0
  123. package/dist/providers/anthropic.js +185 -0
  124. package/dist/providers/anthropic.js.map +1 -0
  125. package/dist/providers/gemini.d.ts +21 -0
  126. package/dist/providers/gemini.d.ts.map +1 -0
  127. package/dist/providers/gemini.js +241 -0
  128. package/dist/providers/gemini.js.map +1 -0
  129. package/dist/providers/index.d.ts +34 -0
  130. package/dist/providers/index.d.ts.map +1 -0
  131. package/dist/providers/index.js +72 -0
  132. package/dist/providers/index.js.map +1 -0
  133. package/dist/providers/openai.d.ts +19 -0
  134. package/dist/providers/openai.d.ts.map +1 -0
  135. package/dist/providers/openai.js +221 -0
  136. package/dist/providers/openai.js.map +1 -0
  137. package/dist/providers/types.d.ts +125 -0
  138. package/dist/providers/types.d.ts.map +1 -0
  139. package/dist/providers/types.js +6 -0
  140. package/dist/providers/types.js.map +1 -0
  141. package/dist/session/index.d.ts +6 -0
  142. package/dist/session/index.d.ts.map +1 -0
  143. package/dist/session/index.js +6 -0
  144. package/dist/session/index.js.map +1 -0
  145. package/dist/session/manager.d.ts +101 -0
  146. package/dist/session/manager.d.ts.map +1 -0
  147. package/dist/session/manager.js +295 -0
  148. package/dist/session/manager.js.map +1 -0
  149. package/dist/session/types.d.ts +39 -0
  150. package/dist/session/types.d.ts.map +1 -0
  151. package/dist/session/types.js +10 -0
  152. package/dist/session/types.js.map +1 -0
  153. package/dist/tools/builtin/bash.d.ts +7 -0
  154. package/dist/tools/builtin/bash.d.ts.map +1 -0
  155. package/dist/tools/builtin/bash.js +80 -0
  156. package/dist/tools/builtin/bash.js.map +1 -0
  157. package/dist/tools/builtin/edit.d.ts +7 -0
  158. package/dist/tools/builtin/edit.d.ts.map +1 -0
  159. package/dist/tools/builtin/edit.js +32 -0
  160. package/dist/tools/builtin/edit.js.map +1 -0
  161. package/dist/tools/builtin/glob.d.ts +7 -0
  162. package/dist/tools/builtin/glob.d.ts.map +1 -0
  163. package/dist/tools/builtin/glob.js +36 -0
  164. package/dist/tools/builtin/glob.js.map +1 -0
  165. package/dist/tools/builtin/grep.d.ts +7 -0
  166. package/dist/tools/builtin/grep.d.ts.map +1 -0
  167. package/dist/tools/builtin/grep.js +59 -0
  168. package/dist/tools/builtin/grep.js.map +1 -0
  169. package/dist/tools/builtin/read.d.ts +7 -0
  170. package/dist/tools/builtin/read.d.ts.map +1 -0
  171. package/dist/tools/builtin/read.js +29 -0
  172. package/dist/tools/builtin/read.js.map +1 -0
  173. package/dist/tools/builtin/write.d.ts +7 -0
  174. package/dist/tools/builtin/write.d.ts.map +1 -0
  175. package/dist/tools/builtin/write.js +24 -0
  176. package/dist/tools/builtin/write.js.map +1 -0
  177. package/dist/tools/index.d.ts +38 -0
  178. package/dist/tools/index.d.ts.map +1 -0
  179. package/dist/tools/index.js +32 -0
  180. package/dist/tools/index.js.map +1 -0
  181. package/dist/tools/registry.d.ts +22 -0
  182. package/dist/tools/registry.d.ts.map +1 -0
  183. package/dist/tools/registry.js +71 -0
  184. package/dist/tools/registry.js.map +1 -0
  185. package/dist/tools/types.d.ts +62 -0
  186. package/dist/tools/types.d.ts.map +1 -0
  187. package/dist/tools/types.js +126 -0
  188. package/dist/tools/types.js.map +1 -0
  189. package/docs/README.md +16 -0
  190. package/docs/proposals/0001-web-fetch-tool.md +293 -0
  191. package/docs/proposals/0002-web-search-tool.md +306 -0
  192. package/docs/proposals/0003-task-subagents.md +333 -0
  193. package/docs/proposals/0004-plan-mode.md +338 -0
  194. package/docs/proposals/0005-todo-system.md +299 -0
  195. package/docs/proposals/0006-memory-system.md +539 -0
  196. package/docs/proposals/0007-context-management.md +429 -0
  197. package/docs/proposals/0008-checkpointing.md +327 -0
  198. package/docs/proposals/0009-hooks-system.md +343 -0
  199. package/docs/proposals/0010-mcp-integration.md +382 -0
  200. package/docs/proposals/0011-custom-commands.md +374 -0
  201. package/docs/proposals/0012-ask-user-question.md +317 -0
  202. package/docs/proposals/0013-multi-edit-tool.md +345 -0
  203. package/docs/proposals/0014-lsp-tool.md +478 -0
  204. package/docs/proposals/0015-ls-tool.md +407 -0
  205. package/docs/proposals/0016-kill-shell-tool.md +455 -0
  206. package/docs/proposals/0017-background-tasks.md +489 -0
  207. package/docs/proposals/0018-parallel-tool-execution.md +415 -0
  208. package/docs/proposals/0019-session-enhancements.md +462 -0
  209. package/docs/proposals/0020-session-summarization.md +447 -0
  210. package/docs/proposals/0021-skills-system.md +409 -0
  211. package/docs/proposals/0022-plugin-system.md +467 -0
  212. package/docs/proposals/0023-permission-enhancements.md +470 -0
  213. package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
  214. package/docs/proposals/0025-cost-tracking.md +447 -0
  215. package/docs/proposals/0026-git-integration.md +475 -0
  216. package/docs/proposals/0027-enhanced-read-tool.md +514 -0
  217. package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
  218. package/docs/proposals/0029-notebook-edit-tool.md +413 -0
  219. package/docs/proposals/0030-plugin-marketplace.md +360 -0
  220. package/docs/proposals/0031-command-suggestions.md +295 -0
  221. package/docs/proposals/0032-ide-integrations.md +328 -0
  222. package/docs/proposals/0033-enterprise-deployment.md +221 -0
  223. package/docs/proposals/0034-sandboxing.md +273 -0
  224. package/docs/proposals/0035-auto-updater.md +311 -0
  225. package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
  226. package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
  227. package/docs/proposals/0038-interactive-cli-ui.md +373 -0
  228. package/docs/proposals/0039-streaming-enhancements.md +359 -0
  229. package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
  230. package/docs/proposals/README.md +84 -0
  231. package/docs/proposals/TEMPLATE.md +57 -0
  232. package/docs/proposals/research/claude-code-research.md +307 -0
  233. package/examples/agent-demo.ts +115 -0
  234. package/examples/basic.ts +166 -0
  235. package/package.json +50 -0
  236. package/src/agent/agent.ts +276 -0
  237. package/src/agent/index.ts +6 -0
  238. package/src/agent/types.ts +62 -0
  239. package/src/cli/components/App.tsx +565 -0
  240. package/src/cli/components/CommandSuggestions.tsx +58 -0
  241. package/src/cli/components/Header.tsx +36 -0
  242. package/src/cli/components/Input.tsx +60 -0
  243. package/src/cli/components/Logo.tsx +16 -0
  244. package/src/cli/components/Messages.tsx +210 -0
  245. package/src/cli/components/ModelSelector.tsx +135 -0
  246. package/src/cli/components/Spinner.tsx +72 -0
  247. package/src/cli/components/index.ts +21 -0
  248. package/src/cli/components/theme.ts +36 -0
  249. package/src/cli/index.tsx +136 -0
  250. package/src/config/index.ts +7 -0
  251. package/src/config/manager.ts +77 -0
  252. package/src/config/types.ts +25 -0
  253. package/src/index.ts +86 -0
  254. package/src/permissions/index.ts +7 -0
  255. package/src/permissions/manager.ts +97 -0
  256. package/src/permissions/types.ts +29 -0
  257. package/src/providers/anthropic.ts +224 -0
  258. package/src/providers/gemini.ts +295 -0
  259. package/src/providers/index.ts +97 -0
  260. package/src/providers/openai.ts +261 -0
  261. package/src/providers/types.ts +181 -0
  262. package/src/session/index.ts +6 -0
  263. package/src/session/manager.ts +354 -0
  264. package/src/session/types.ts +49 -0
  265. package/src/tools/builtin/bash.ts +92 -0
  266. package/src/tools/builtin/edit.ts +37 -0
  267. package/src/tools/builtin/glob.ts +42 -0
  268. package/src/tools/builtin/grep.ts +67 -0
  269. package/src/tools/builtin/read.ts +34 -0
  270. package/src/tools/builtin/write.ts +27 -0
  271. package/src/tools/index.ts +36 -0
  272. package/src/tools/registry.ts +83 -0
  273. package/src/tools/types.ts +172 -0
  274. package/tsconfig.json +21 -0
@@ -0,0 +1,470 @@
1
+ # Proposal: Permission Enhancements
2
+
3
+ - **Proposal ID**: 0023
4
+ - **Author**: mycode team
5
+ - **Status**: Draft
6
+ - **Created**: 2025-01-15
7
+ - **Updated**: 2025-01-15
8
+
9
+ ## Summary
10
+
11
+ Enhance the permission system with pattern-based rules, prompt-based approvals, persistent allowlists, and fine-grained control over tool execution. This provides users with flexible, secure control over agent capabilities.
12
+
13
+ ## Motivation
14
+
15
+ The current permission system is basic:
16
+
17
+ 1. **Tool-level only**: Can't distinguish safe vs dangerous uses
18
+ 2. **No persistence**: Approvals lost between sessions
19
+ 3. **No patterns**: Can't approve "all git commands"
20
+ 4. **Manual each time**: Repetitive confirmation fatigue
21
+ 5. **No audit trail**: No record of what was approved
22
+
23
+ Enhanced permissions balance security with usability.
24
+
25
+ ## Claude Code Reference
26
+
27
+ Claude Code provides sophisticated permission handling:
28
+
29
+ ### ExitPlanMode Permissions
30
+ ```typescript
31
+ ExitPlanMode({
32
+ allowedPrompts: [
33
+ { tool: "Bash", prompt: "run tests" },
34
+ { tool: "Bash", prompt: "install dependencies" },
35
+ { tool: "Bash", prompt: "build the project" }
36
+ ]
37
+ })
38
+ ```
39
+
40
+ ### Settings-Based Permissions
41
+ ```json
42
+ {
43
+ "permissions": {
44
+ "allow": [
45
+ "Bash(git add:*)",
46
+ "Bash(git commit:*)",
47
+ "Bash(npm install:*)"
48
+ ]
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### Permission Guidelines
54
+ - Scope permissions narrowly
55
+ - Use semantic descriptions, not literal commands
56
+ - Read-only permissions for read-only operations
57
+ - Session-scoped by default
58
+ - Input-aware caching
59
+
60
+ ## Detailed Design
61
+
62
+ ### API Design
63
+
64
+ ```typescript
65
+ // src/permissions/types.ts
66
+ type PermissionMode = 'auto' | 'confirm' | 'deny';
67
+
68
+ interface PermissionRule {
69
+ tool: string | RegExp;
70
+ mode: PermissionMode;
71
+ pattern?: string | RegExp; // Input pattern matching
72
+ prompt?: string; // Semantic description
73
+ scope?: 'session' | 'project' | 'global';
74
+ expiresAt?: Date;
75
+ }
76
+
77
+ interface PromptPermission {
78
+ tool: string;
79
+ prompt: string; // Semantic description
80
+ }
81
+
82
+ interface PermissionConfig {
83
+ defaultMode: PermissionMode;
84
+ rules: PermissionRule[];
85
+ allowedPrompts: PromptPermission[];
86
+ }
87
+
88
+ interface PermissionContext {
89
+ tool: string;
90
+ input: unknown;
91
+ sessionId: string;
92
+ projectPath: string;
93
+ }
94
+
95
+ interface PermissionDecision {
96
+ allowed: boolean;
97
+ reason: string;
98
+ matchedRule?: PermissionRule;
99
+ requiresConfirmation: boolean;
100
+ }
101
+
102
+ interface PermissionAuditEntry {
103
+ timestamp: Date;
104
+ tool: string;
105
+ input: unknown;
106
+ decision: 'allowed' | 'denied' | 'confirmed';
107
+ rule?: string;
108
+ userId?: string;
109
+ }
110
+ ```
111
+
112
+ ### Enhanced Permission Manager
113
+
114
+ ```typescript
115
+ // src/permissions/manager.ts
116
+ class PermissionManager {
117
+ private config: PermissionConfig;
118
+ private sessionApprovals: Map<string, Set<string>> = new Map();
119
+ private persistentRules: PermissionRule[] = [];
120
+ private auditLog: PermissionAuditEntry[] = [];
121
+ private promptMatcher: PromptMatcher;
122
+
123
+ constructor(config?: Partial<PermissionConfig>) {
124
+ this.config = {
125
+ defaultMode: 'confirm',
126
+ rules: DEFAULT_RULES,
127
+ allowedPrompts: [],
128
+ ...config
129
+ };
130
+ this.loadPersistentRules();
131
+ this.promptMatcher = new PromptMatcher();
132
+ }
133
+
134
+ async checkPermission(
135
+ context: PermissionContext
136
+ ): Promise<PermissionDecision> {
137
+ const { tool, input, sessionId } = context;
138
+
139
+ // Check explicit deny rules first
140
+ const denyRule = this.findMatchingRule(context, 'deny');
141
+ if (denyRule) {
142
+ this.logAudit(context, 'denied', denyRule);
143
+ return {
144
+ allowed: false,
145
+ reason: `Denied by rule: ${denyRule.prompt || denyRule.pattern}`,
146
+ matchedRule: denyRule,
147
+ requiresConfirmation: false
148
+ };
149
+ }
150
+
151
+ // Check auto-allow rules
152
+ const autoRule = this.findMatchingRule(context, 'auto');
153
+ if (autoRule) {
154
+ this.logAudit(context, 'allowed', autoRule);
155
+ return {
156
+ allowed: true,
157
+ reason: 'Auto-approved by rule',
158
+ matchedRule: autoRule,
159
+ requiresConfirmation: false
160
+ };
161
+ }
162
+
163
+ // Check prompt-based permissions
164
+ const promptMatch = await this.matchPrompt(tool, input);
165
+ if (promptMatch) {
166
+ this.logAudit(context, 'allowed', { prompt: promptMatch });
167
+ return {
168
+ allowed: true,
169
+ reason: `Matches approved prompt: ${promptMatch}`,
170
+ requiresConfirmation: false
171
+ };
172
+ }
173
+
174
+ // Check session approvals cache
175
+ const cacheKey = this.getCacheKey(tool, input);
176
+ const sessionCache = this.sessionApprovals.get(sessionId);
177
+ if (sessionCache?.has(cacheKey)) {
178
+ return {
179
+ allowed: true,
180
+ reason: 'Previously approved in session',
181
+ requiresConfirmation: false
182
+ };
183
+ }
184
+
185
+ // Default: requires confirmation
186
+ return {
187
+ allowed: false,
188
+ reason: 'Requires user confirmation',
189
+ requiresConfirmation: true
190
+ };
191
+ }
192
+
193
+ async requestPermission(
194
+ context: PermissionContext,
195
+ confirmCallback: ConfirmCallback
196
+ ): Promise<boolean> {
197
+ const decision = await this.checkPermission(context);
198
+
199
+ if (decision.allowed) return true;
200
+ if (!decision.requiresConfirmation) return false;
201
+
202
+ // Request user confirmation
203
+ const confirmed = await confirmCallback(
204
+ context.tool,
205
+ context.input,
206
+ this.getSuggestions(context)
207
+ );
208
+
209
+ if (confirmed) {
210
+ this.cacheApproval(context);
211
+ this.logAudit(context, 'confirmed');
212
+ } else {
213
+ this.logAudit(context, 'denied');
214
+ }
215
+
216
+ return confirmed;
217
+ }
218
+
219
+ addAllowedPrompts(prompts: PromptPermission[]): void {
220
+ this.config.allowedPrompts.push(...prompts);
221
+ }
222
+
223
+ clearSessionApprovals(sessionId: string): void {
224
+ this.sessionApprovals.delete(sessionId);
225
+ }
226
+
227
+ private async matchPrompt(tool: string, input: unknown): Promise<string | null> {
228
+ for (const permission of this.config.allowedPrompts) {
229
+ if (permission.tool !== tool) continue;
230
+
231
+ const matches = await this.promptMatcher.matches(
232
+ permission.prompt,
233
+ input
234
+ );
235
+ if (matches) return permission.prompt;
236
+ }
237
+ return null;
238
+ }
239
+
240
+ private findMatchingRule(
241
+ context: PermissionContext,
242
+ mode: PermissionMode
243
+ ): PermissionRule | undefined {
244
+ const allRules = [...this.config.rules, ...this.persistentRules];
245
+
246
+ return allRules.find(rule => {
247
+ if (rule.mode !== mode) return false;
248
+ if (!this.matchesTool(rule.tool, context.tool)) return false;
249
+ if (rule.pattern && !this.matchesPattern(rule.pattern, context.input)) {
250
+ return false;
251
+ }
252
+ return true;
253
+ });
254
+ }
255
+
256
+ private matchesTool(pattern: string | RegExp, tool: string): boolean {
257
+ if (typeof pattern === 'string') {
258
+ return pattern === tool || pattern === '*';
259
+ }
260
+ return pattern.test(tool);
261
+ }
262
+
263
+ private matchesPattern(pattern: string | RegExp, input: unknown): boolean {
264
+ const inputStr = JSON.stringify(input);
265
+ if (typeof pattern === 'string') {
266
+ // Support glob-like patterns
267
+ const regex = new RegExp(pattern.replace(/\*/g, '.*'));
268
+ return regex.test(inputStr);
269
+ }
270
+ return pattern.test(inputStr);
271
+ }
272
+
273
+ // Persistence
274
+ async saveRule(rule: PermissionRule): Promise<void> {
275
+ this.persistentRules.push(rule);
276
+ await this.savePersistentRules();
277
+ }
278
+
279
+ private getSuggestions(context: PermissionContext): PermissionSuggestion[] {
280
+ return [
281
+ { action: 'allow_once', label: 'Allow this time' },
282
+ { action: 'allow_session', label: 'Allow for this session' },
283
+ { action: 'allow_always', label: 'Always allow this' },
284
+ { action: 'deny', label: 'Deny' }
285
+ ];
286
+ }
287
+ }
288
+ ```
289
+
290
+ ### Prompt Matcher
291
+
292
+ ```typescript
293
+ // src/permissions/prompt-matcher.ts
294
+ class PromptMatcher {
295
+ private patterns: Map<string, (input: unknown) => boolean> = new Map();
296
+
297
+ constructor() {
298
+ this.registerBuiltInPatterns();
299
+ }
300
+
301
+ private registerBuiltInPatterns(): void {
302
+ // Git operations
303
+ this.patterns.set('run tests', input =>
304
+ this.matchesCommand(input, ['npm test', 'pytest', 'go test', 'jest'])
305
+ );
306
+
307
+ this.patterns.set('install dependencies', input =>
308
+ this.matchesCommand(input, ['npm install', 'pip install', 'cargo build'])
309
+ );
310
+
311
+ this.patterns.set('build the project', input =>
312
+ this.matchesCommand(input, ['npm run build', 'make', 'cargo build'])
313
+ );
314
+
315
+ this.patterns.set('git operations', input =>
316
+ this.matchesCommand(input, ['git '])
317
+ );
318
+ }
319
+
320
+ async matches(prompt: string, input: unknown): Promise<boolean> {
321
+ // Check exact pattern match
322
+ const pattern = this.patterns.get(prompt.toLowerCase());
323
+ if (pattern) return pattern(input);
324
+
325
+ // Fuzzy semantic matching (for complex prompts)
326
+ return this.semanticMatch(prompt, input);
327
+ }
328
+
329
+ private matchesCommand(input: unknown, prefixes: string[]): boolean {
330
+ const command = (input as { command?: string })?.command || '';
331
+ return prefixes.some(prefix => command.startsWith(prefix));
332
+ }
333
+
334
+ private semanticMatch(prompt: string, input: unknown): boolean {
335
+ // Simple keyword matching for now
336
+ const inputStr = JSON.stringify(input).toLowerCase();
337
+ const keywords = prompt.toLowerCase().split(/\s+/);
338
+ return keywords.some(kw => inputStr.includes(kw));
339
+ }
340
+ }
341
+ ```
342
+
343
+ ### File Changes
344
+
345
+ | File | Action | Description |
346
+ |------|--------|-------------|
347
+ | `src/permissions/types.ts` | Modify | Enhanced types |
348
+ | `src/permissions/manager.ts` | Modify | Enhanced manager |
349
+ | `src/permissions/prompt-matcher.ts` | Create | Semantic matching |
350
+ | `src/permissions/persistence.ts` | Create | Rule persistence |
351
+ | `src/permissions/audit.ts` | Create | Audit logging |
352
+ | `src/cli/commands/permissions.ts` | Create | Permission CLI |
353
+
354
+ ## User Experience
355
+
356
+ ### Permission Prompt with Options
357
+ ```
358
+ ┌─ Permission Request ──────────────────────────────┐
359
+ │ Tool: Bash │
360
+ │ Command: npm install lodash │
361
+ │ │
362
+ │ This operation requires your approval. │
363
+ │ │
364
+ │ [1] Allow once │
365
+ │ [2] Allow for this session │
366
+ │ [3] Always allow "npm install" commands │
367
+ │ [4] Deny │
368
+ └───────────────────────────────────────────────────┘
369
+ ```
370
+
371
+ ### View Permissions
372
+ ```
373
+ User: /permissions
374
+
375
+ Permission Rules:
376
+ ┌────────────────────────────────────────────────────────────┐
377
+ │ Type Tool Pattern Scope Mode │
378
+ ├────────────────────────────────────────────────────────────┤
379
+ │ Built-in Read * session auto │
380
+ │ Built-in Glob * session auto │
381
+ │ Built-in Grep * session auto │
382
+ │ Custom Bash npm install:* project auto │
383
+ │ Custom Bash git add:* global auto │
384
+ │ Session Bash pytest session auto │
385
+ └────────────────────────────────────────────────────────────┘
386
+
387
+ Pending Prompts (from plan approval):
388
+ • Bash: run tests
389
+ • Bash: build the project
390
+ ```
391
+
392
+ ### Permission Audit
393
+ ```
394
+ User: /permissions audit
395
+
396
+ Recent Permission Decisions:
397
+ ┌────────────────────────────────────────────────────────────────┐
398
+ │ Time Tool Input Decision Rule │
399
+ ├────────────────────────────────────────────────────────────────┤
400
+ │ 10:42 Bash npm test allowed prompt:tests │
401
+ │ 10:41 Read src/index.ts allowed built-in │
402
+ │ 10:40 Write src/new.ts confirmed user-approval │
403
+ │ 10:38 Bash rm -rf /tmp/test denied blocked-cmd │
404
+ └────────────────────────────────────────────────────────────────┘
405
+ ```
406
+
407
+ ## Alternatives Considered
408
+
409
+ ### Alternative 1: Capability-Based Security
410
+ Use capability tokens instead of rules.
411
+
412
+ **Pros**: More formal model
413
+ **Cons**: Complex for users
414
+ **Decision**: Deferred - Consider for enterprise
415
+
416
+ ### Alternative 2: AI-Based Permission Decisions
417
+ Let AI assess risk of operations.
418
+
419
+ **Pros**: Intelligent decisions
420
+ **Cons**: Unpredictable, trust issues
421
+ **Decision**: Rejected - Users need control
422
+
423
+ ### Alternative 3: Whitelist-Only Mode
424
+ Only allow explicitly approved operations.
425
+
426
+ **Pros**: Maximum security
427
+ **Cons**: Too restrictive for usability
428
+ **Decision**: Available as strict mode
429
+
430
+ ## Security Considerations
431
+
432
+ 1. **Rule Validation**: Validate rule patterns
433
+ 2. **Audit Trail**: Immutable audit log
434
+ 3. **Escalation Prevention**: Can't grant more permissions than held
435
+ 4. **Expiration**: Time-limited approvals
436
+ 5. **Scope Limits**: Limit global rules
437
+
438
+ ## Testing Strategy
439
+
440
+ 1. **Unit Tests**:
441
+ - Rule matching logic
442
+ - Pattern matching
443
+ - Prompt matching
444
+ - Cache behavior
445
+
446
+ 2. **Integration Tests**:
447
+ - Full permission flow
448
+ - Persistence
449
+ - Audit logging
450
+
451
+ 3. **Security Tests**:
452
+ - Bypass attempts
453
+ - Injection attacks
454
+ - Escalation scenarios
455
+
456
+ ## Migration Path
457
+
458
+ 1. **Phase 1**: Enhanced rule matching
459
+ 2. **Phase 2**: Prompt-based permissions
460
+ 3. **Phase 3**: Persistence layer
461
+ 4. **Phase 4**: Audit logging
462
+ 5. **Phase 5**: CLI management
463
+
464
+ Backward compatible with existing rules.
465
+
466
+ ## References
467
+
468
+ - [Claude Code Permission System](https://code.claude.com/docs/en/permissions)
469
+ - [Capability-Based Security](https://en.wikipedia.org/wiki/Capability-based_security)
470
+ - [Principle of Least Privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)