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.
- package/.env.example +11 -0
- package/CLAUDE.md +70 -0
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/agent/agent.d.ts +84 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +233 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/index.d.ts +6 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +6 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/types.d.ts +47 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/components/App.d.ts +14 -0
- package/dist/cli/components/App.d.ts.map +1 -0
- package/dist/cli/components/App.js +395 -0
- package/dist/cli/components/App.js.map +1 -0
- package/dist/cli/components/CommandSuggestions.d.ts +13 -0
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
- package/dist/cli/components/CommandSuggestions.js +32 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -0
- package/dist/cli/components/Header.d.ts +9 -0
- package/dist/cli/components/Header.d.ts.map +1 -0
- package/dist/cli/components/Header.js +13 -0
- package/dist/cli/components/Header.js.map +1 -0
- package/dist/cli/components/Input.d.ts +13 -0
- package/dist/cli/components/Input.d.ts.map +1 -0
- package/dist/cli/components/Input.js +27 -0
- package/dist/cli/components/Input.js.map +1 -0
- package/dist/cli/components/Logo.d.ts +2 -0
- package/dist/cli/components/Logo.d.ts.map +1 -0
- package/dist/cli/components/Logo.js +8 -0
- package/dist/cli/components/Logo.js.map +1 -0
- package/dist/cli/components/Messages.d.ts +37 -0
- package/dist/cli/components/Messages.d.ts.map +1 -0
- package/dist/cli/components/Messages.js +106 -0
- package/dist/cli/components/Messages.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +13 -0
- package/dist/cli/components/ModelSelector.d.ts.map +1 -0
- package/dist/cli/components/ModelSelector.js +72 -0
- package/dist/cli/components/ModelSelector.js.map +1 -0
- package/dist/cli/components/Spinner.d.ts +12 -0
- package/dist/cli/components/Spinner.d.ts.map +1 -0
- package/dist/cli/components/Spinner.js +45 -0
- package/dist/cli/components/Spinner.js.map +1 -0
- package/dist/cli/components/index.d.ts +12 -0
- package/dist/cli/components/index.d.ts.map +1 -0
- package/dist/cli/components/index.js +12 -0
- package/dist/cli/components/index.js.map +1 -0
- package/dist/cli/components/theme.d.ts +31 -0
- package/dist/cli/components/theme.d.ts.map +1 -0
- package/dist/cli/components/theme.js +36 -0
- package/dist/cli/components/theme.js.map +1 -0
- package/dist/cli/index-legacy.d.ts +7 -0
- package/dist/cli/index-legacy.d.ts.map +1 -0
- package/dist/cli/index-legacy.js +431 -0
- package/dist/cli/index-legacy.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ink-cli.d.ts +7 -0
- package/dist/cli/ink-cli.d.ts.map +1 -0
- package/dist/cli/ink-cli.js +105 -0
- package/dist/cli/ink-cli.js.map +1 -0
- package/dist/cli/session-picker.d.ts +16 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +280 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/ui.d.ts +61 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +364 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +31 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +65 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/types.d.ts +22 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +6 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +10 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/init.d.ts +20 -0
- package/dist/memory/init.d.ts.map +1 -0
- package/dist/memory/init.js +332 -0
- package/dist/memory/init.js.map +1 -0
- package/dist/memory/manager.d.ts +85 -0
- package/dist/memory/manager.d.ts.map +1 -0
- package/dist/memory/manager.js +234 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/types.d.ts +74 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +6 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/permissions/index.d.ts +7 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/manager.d.ts +32 -0
- package/dist/permissions/manager.d.ts.map +1 -0
- package/dist/permissions/manager.js +79 -0
- package/dist/permissions/manager.js.map +1 -0
- package/dist/permissions/types.d.ts +14 -0
- package/dist/permissions/types.d.ts.map +1 -0
- package/dist/permissions/types.js +17 -0
- package/dist/permissions/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +20 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +185 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +21 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +241 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/index.d.ts +34 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +72 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +19 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +221 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +125 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +6 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +101 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +295 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/types.d.ts +39 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +10 -0
- package/dist/session/types.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +7 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +80 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit.d.ts +7 -0
- package/dist/tools/builtin/edit.d.ts.map +1 -0
- package/dist/tools/builtin/edit.js +32 -0
- package/dist/tools/builtin/edit.js.map +1 -0
- package/dist/tools/builtin/glob.d.ts +7 -0
- package/dist/tools/builtin/glob.d.ts.map +1 -0
- package/dist/tools/builtin/glob.js +36 -0
- package/dist/tools/builtin/glob.js.map +1 -0
- package/dist/tools/builtin/grep.d.ts +7 -0
- package/dist/tools/builtin/grep.d.ts.map +1 -0
- package/dist/tools/builtin/grep.js +59 -0
- package/dist/tools/builtin/grep.js.map +1 -0
- package/dist/tools/builtin/read.d.ts +7 -0
- package/dist/tools/builtin/read.d.ts.map +1 -0
- package/dist/tools/builtin/read.js +29 -0
- package/dist/tools/builtin/read.js.map +1 -0
- package/dist/tools/builtin/write.d.ts +7 -0
- package/dist/tools/builtin/write.d.ts.map +1 -0
- package/dist/tools/builtin/write.js +24 -0
- package/dist/tools/builtin/write.js.map +1 -0
- package/dist/tools/index.d.ts +38 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +32 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +22 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +71 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +62 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +126 -0
- package/dist/tools/types.js.map +1 -0
- package/docs/README.md +16 -0
- package/docs/proposals/0001-web-fetch-tool.md +293 -0
- package/docs/proposals/0002-web-search-tool.md +306 -0
- package/docs/proposals/0003-task-subagents.md +333 -0
- package/docs/proposals/0004-plan-mode.md +338 -0
- package/docs/proposals/0005-todo-system.md +299 -0
- package/docs/proposals/0006-memory-system.md +539 -0
- package/docs/proposals/0007-context-management.md +429 -0
- package/docs/proposals/0008-checkpointing.md +327 -0
- package/docs/proposals/0009-hooks-system.md +343 -0
- package/docs/proposals/0010-mcp-integration.md +382 -0
- package/docs/proposals/0011-custom-commands.md +374 -0
- package/docs/proposals/0012-ask-user-question.md +317 -0
- package/docs/proposals/0013-multi-edit-tool.md +345 -0
- package/docs/proposals/0014-lsp-tool.md +478 -0
- package/docs/proposals/0015-ls-tool.md +407 -0
- package/docs/proposals/0016-kill-shell-tool.md +455 -0
- package/docs/proposals/0017-background-tasks.md +489 -0
- package/docs/proposals/0018-parallel-tool-execution.md +415 -0
- package/docs/proposals/0019-session-enhancements.md +462 -0
- package/docs/proposals/0020-session-summarization.md +447 -0
- package/docs/proposals/0021-skills-system.md +409 -0
- package/docs/proposals/0022-plugin-system.md +467 -0
- package/docs/proposals/0023-permission-enhancements.md +470 -0
- package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
- package/docs/proposals/0025-cost-tracking.md +447 -0
- package/docs/proposals/0026-git-integration.md +475 -0
- package/docs/proposals/0027-enhanced-read-tool.md +514 -0
- package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
- package/docs/proposals/0029-notebook-edit-tool.md +413 -0
- package/docs/proposals/0030-plugin-marketplace.md +360 -0
- package/docs/proposals/0031-command-suggestions.md +295 -0
- package/docs/proposals/0032-ide-integrations.md +328 -0
- package/docs/proposals/0033-enterprise-deployment.md +221 -0
- package/docs/proposals/0034-sandboxing.md +273 -0
- package/docs/proposals/0035-auto-updater.md +311 -0
- package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
- package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
- package/docs/proposals/0038-interactive-cli-ui.md +373 -0
- package/docs/proposals/0039-streaming-enhancements.md +359 -0
- package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
- package/docs/proposals/README.md +84 -0
- package/docs/proposals/TEMPLATE.md +57 -0
- package/docs/proposals/research/claude-code-research.md +307 -0
- package/examples/agent-demo.ts +115 -0
- package/examples/basic.ts +166 -0
- package/package.json +50 -0
- package/src/agent/agent.ts +276 -0
- package/src/agent/index.ts +6 -0
- package/src/agent/types.ts +62 -0
- package/src/cli/components/App.tsx +565 -0
- package/src/cli/components/CommandSuggestions.tsx +58 -0
- package/src/cli/components/Header.tsx +36 -0
- package/src/cli/components/Input.tsx +60 -0
- package/src/cli/components/Logo.tsx +16 -0
- package/src/cli/components/Messages.tsx +210 -0
- package/src/cli/components/ModelSelector.tsx +135 -0
- package/src/cli/components/Spinner.tsx +72 -0
- package/src/cli/components/index.ts +21 -0
- package/src/cli/components/theme.ts +36 -0
- package/src/cli/index.tsx +136 -0
- package/src/config/index.ts +7 -0
- package/src/config/manager.ts +77 -0
- package/src/config/types.ts +25 -0
- package/src/index.ts +86 -0
- package/src/permissions/index.ts +7 -0
- package/src/permissions/manager.ts +97 -0
- package/src/permissions/types.ts +29 -0
- package/src/providers/anthropic.ts +224 -0
- package/src/providers/gemini.ts +295 -0
- package/src/providers/index.ts +97 -0
- package/src/providers/openai.ts +261 -0
- package/src/providers/types.ts +181 -0
- package/src/session/index.ts +6 -0
- package/src/session/manager.ts +354 -0
- package/src/session/types.ts +49 -0
- package/src/tools/builtin/bash.ts +92 -0
- package/src/tools/builtin/edit.ts +37 -0
- package/src/tools/builtin/glob.ts +42 -0
- package/src/tools/builtin/grep.ts +67 -0
- package/src/tools/builtin/read.ts +34 -0
- package/src/tools/builtin/write.ts +27 -0
- package/src/tools/index.ts +36 -0
- package/src/tools/registry.ts +83 -0
- package/src/tools/types.ts +172 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# Proposal: Checkpointing (Edit Undo/Rewind)
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0008
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Implement a checkpointing system that tracks all file changes made by the agent and enables users to rewind/undo unwanted modifications. This provides a safety net for experimentation and quick recovery from undesired changes.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
Currently, mycode makes irreversible file changes. This leads to:
|
|
16
|
+
|
|
17
|
+
1. **Fear of damage**: Users hesitant to let agent modify files
|
|
18
|
+
2. **Manual recovery**: Must use git or backups to undo changes
|
|
19
|
+
3. **Lost work**: Accidental overwrites can't be recovered
|
|
20
|
+
4. **No visibility**: Can't see what files were changed
|
|
21
|
+
5. **No experimentation**: Can't easily try approaches and revert
|
|
22
|
+
|
|
23
|
+
A checkpointing system enables safe experimentation with easy rollback.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code provides automatic checkpointing:
|
|
28
|
+
|
|
29
|
+
### Key Features
|
|
30
|
+
- Automatic tracking of all file edits
|
|
31
|
+
- `/rewind` command to undo changes
|
|
32
|
+
- Change history visible in session
|
|
33
|
+
- Per-file and bulk rollback options
|
|
34
|
+
- Integration with git (commits as checkpoints)
|
|
35
|
+
|
|
36
|
+
### Example Usage
|
|
37
|
+
```
|
|
38
|
+
Agent: [Makes several file changes]
|
|
39
|
+
- Modified src/auth.ts
|
|
40
|
+
- Created src/middleware.ts
|
|
41
|
+
- Modified package.json
|
|
42
|
+
|
|
43
|
+
User: Actually, I don't like that approach. Undo those changes.
|
|
44
|
+
|
|
45
|
+
Agent: I'll rewind the file changes.
|
|
46
|
+
[Reverting 3 file changes...]
|
|
47
|
+
- Reverted src/auth.ts
|
|
48
|
+
- Deleted src/middleware.ts
|
|
49
|
+
- Reverted package.json
|
|
50
|
+
|
|
51
|
+
Changes have been undone.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Change Tracking Display
|
|
55
|
+
```
|
|
56
|
+
Session Changes:
|
|
57
|
+
[1] src/auth.ts (modified) - 10 minutes ago
|
|
58
|
+
[2] src/middleware.ts (created) - 8 minutes ago
|
|
59
|
+
[3] package.json (modified) - 5 minutes ago
|
|
60
|
+
|
|
61
|
+
Use /rewind [number] to undo specific changes
|
|
62
|
+
Use /rewind all to undo all changes
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Detailed Design
|
|
66
|
+
|
|
67
|
+
### API Design
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// src/checkpointing/types.ts
|
|
71
|
+
type ChangeType = 'create' | 'modify' | 'delete';
|
|
72
|
+
|
|
73
|
+
interface FileCheckpoint {
|
|
74
|
+
id: string;
|
|
75
|
+
path: string;
|
|
76
|
+
changeType: ChangeType;
|
|
77
|
+
timestamp: Date;
|
|
78
|
+
previousContent: string | null; // null for create
|
|
79
|
+
newContent: string | null; // null for delete
|
|
80
|
+
toolName: string; // Which tool made the change
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface CheckpointSession {
|
|
84
|
+
id: string;
|
|
85
|
+
sessionId: string;
|
|
86
|
+
checkpoints: FileCheckpoint[];
|
|
87
|
+
createdAt: Date;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface RewindOptions {
|
|
91
|
+
checkpointId?: string; // Specific checkpoint
|
|
92
|
+
path?: string; // Specific file
|
|
93
|
+
all?: boolean; // All changes
|
|
94
|
+
count?: number; // Last N changes
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface RewindResult {
|
|
98
|
+
success: boolean;
|
|
99
|
+
revertedFiles: string[];
|
|
100
|
+
errors: Array<{ path: string; error: string }>;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// src/checkpointing/checkpoint-manager.ts
|
|
106
|
+
class CheckpointManager {
|
|
107
|
+
private session: CheckpointSession;
|
|
108
|
+
|
|
109
|
+
constructor(sessionId: string);
|
|
110
|
+
|
|
111
|
+
// Record a file change
|
|
112
|
+
recordChange(change: Omit<FileCheckpoint, 'id' | 'timestamp'>): void;
|
|
113
|
+
|
|
114
|
+
// Get all checkpoints
|
|
115
|
+
getCheckpoints(): FileCheckpoint[];
|
|
116
|
+
|
|
117
|
+
// Get changes for a specific file
|
|
118
|
+
getFileHistory(path: string): FileCheckpoint[];
|
|
119
|
+
|
|
120
|
+
// Rewind changes
|
|
121
|
+
async rewind(options: RewindOptions): Promise<RewindResult>;
|
|
122
|
+
|
|
123
|
+
// Get summary of changes
|
|
124
|
+
getSummary(): { created: number; modified: number; deleted: number };
|
|
125
|
+
|
|
126
|
+
// Clear checkpoints (e.g., after git commit)
|
|
127
|
+
clearCheckpoints(): void;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Implementation Approach
|
|
132
|
+
|
|
133
|
+
1. **Tool Hooking**: Intercept Write and Edit tool executions
|
|
134
|
+
2. **Pre-Change Capture**: Store file content before modification
|
|
135
|
+
3. **Change Recording**: Log all changes with metadata
|
|
136
|
+
4. **Rewind Logic**: Apply inverse operations to restore state
|
|
137
|
+
5. **Persistence**: Store checkpoints in session data
|
|
138
|
+
6. **Git Integration**: Option to use git for versioning
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Wrapping tools with checkpoint tracking
|
|
142
|
+
function withCheckpointing(tool: Tool, checkpointManager: CheckpointManager): Tool {
|
|
143
|
+
return {
|
|
144
|
+
...tool,
|
|
145
|
+
execute: async (input, context) => {
|
|
146
|
+
// Capture pre-change state
|
|
147
|
+
let previousContent: string | null = null;
|
|
148
|
+
if (tool.name === 'Write' || tool.name === 'Edit') {
|
|
149
|
+
previousContent = await safeReadFile(input.file_path);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Execute original tool
|
|
153
|
+
const result = await tool.execute(input, context);
|
|
154
|
+
|
|
155
|
+
// Record checkpoint on success
|
|
156
|
+
if (result.success) {
|
|
157
|
+
const newContent = await safeReadFile(input.file_path);
|
|
158
|
+
checkpointManager.recordChange({
|
|
159
|
+
path: input.file_path,
|
|
160
|
+
changeType: previousContent === null ? 'create' : 'modify',
|
|
161
|
+
previousContent,
|
|
162
|
+
newContent,
|
|
163
|
+
toolName: tool.name
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Rewind Command
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// src/commands/rewind-command.ts
|
|
177
|
+
async function rewindCommand(args: string[], context: CommandContext): Promise<void> {
|
|
178
|
+
const { checkpointManager, ui } = context;
|
|
179
|
+
|
|
180
|
+
if (args[0] === 'all') {
|
|
181
|
+
const result = await checkpointManager.rewind({ all: true });
|
|
182
|
+
ui.showMessage(`Reverted ${result.revertedFiles.length} files`);
|
|
183
|
+
} else if (args[0]) {
|
|
184
|
+
const checkpointId = args[0];
|
|
185
|
+
const result = await checkpointManager.rewind({ checkpointId });
|
|
186
|
+
ui.showMessage(`Reverted: ${result.revertedFiles.join(', ')}`);
|
|
187
|
+
} else {
|
|
188
|
+
// Show change list
|
|
189
|
+
const checkpoints = checkpointManager.getCheckpoints();
|
|
190
|
+
ui.showChangeList(checkpoints);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### File Changes
|
|
196
|
+
|
|
197
|
+
| File | Action | Description |
|
|
198
|
+
|------|--------|-------------|
|
|
199
|
+
| `src/checkpointing/types.ts` | Create | Checkpoint type definitions |
|
|
200
|
+
| `src/checkpointing/checkpoint-manager.ts` | Create | Core checkpoint management |
|
|
201
|
+
| `src/checkpointing/tool-wrapper.ts` | Create | Tool wrapping for tracking |
|
|
202
|
+
| `src/checkpointing/index.ts` | Create | Module exports |
|
|
203
|
+
| `src/commands/rewind-command.ts` | Create | /rewind command |
|
|
204
|
+
| `src/tools/write.ts` | Modify | Add checkpoint integration |
|
|
205
|
+
| `src/tools/edit.ts` | Modify | Add checkpoint integration |
|
|
206
|
+
| `src/session/types.ts` | Modify | Add checkpoint data |
|
|
207
|
+
|
|
208
|
+
## User Experience
|
|
209
|
+
|
|
210
|
+
### Change Tracking Display
|
|
211
|
+
```
|
|
212
|
+
┌─ File Changes This Session ───────────────────┐
|
|
213
|
+
│ │
|
|
214
|
+
│ [1] 5 min ago src/auth.ts (modified) │
|
|
215
|
+
│ [2] 4 min ago src/middleware.ts (created) │
|
|
216
|
+
│ [3] 2 min ago package.json (modified) │
|
|
217
|
+
│ │
|
|
218
|
+
│ Total: 2 modified, 1 created, 0 deleted │
|
|
219
|
+
└───────────────────────────────────────────────┘
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Rewind Command
|
|
223
|
+
```
|
|
224
|
+
> /rewind
|
|
225
|
+
|
|
226
|
+
Session has 3 file changes:
|
|
227
|
+
[1] src/auth.ts (modified) - 5 min ago
|
|
228
|
+
[2] src/middleware.ts (created) - 4 min ago
|
|
229
|
+
[3] package.json (modified) - 2 min ago
|
|
230
|
+
|
|
231
|
+
Usage:
|
|
232
|
+
/rewind 1 - Revert specific change
|
|
233
|
+
/rewind all - Revert all changes
|
|
234
|
+
/rewind last 2 - Revert last 2 changes
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Rewind Confirmation
|
|
238
|
+
```
|
|
239
|
+
> /rewind all
|
|
240
|
+
|
|
241
|
+
Are you sure you want to revert 3 file changes?
|
|
242
|
+
- src/auth.ts (restore previous)
|
|
243
|
+
- src/middleware.ts (delete)
|
|
244
|
+
- package.json (restore previous)
|
|
245
|
+
|
|
246
|
+
[Confirm] [Cancel]
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Rewind Success
|
|
250
|
+
```
|
|
251
|
+
✓ Reverted 3 files:
|
|
252
|
+
• src/auth.ts - restored
|
|
253
|
+
• src/middleware.ts - deleted
|
|
254
|
+
• package.json - restored
|
|
255
|
+
|
|
256
|
+
Checkpoints cleared.
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Inline Change Indicator
|
|
260
|
+
After each file modification:
|
|
261
|
+
```
|
|
262
|
+
Agent: [Edit] Modified src/auth.ts
|
|
263
|
+
(checkpoint saved - use /rewind to undo)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Alternatives Considered
|
|
267
|
+
|
|
268
|
+
### Alternative 1: Git-Only Versioning
|
|
269
|
+
Rely solely on git for undo.
|
|
270
|
+
|
|
271
|
+
**Pros**: No extra storage, standard workflow
|
|
272
|
+
**Cons**: Requires git, must commit to checkpoint
|
|
273
|
+
**Decision**: Rejected as primary - git integration as optional enhancement
|
|
274
|
+
|
|
275
|
+
### Alternative 2: Full File Backups
|
|
276
|
+
Store complete file copies.
|
|
277
|
+
|
|
278
|
+
**Pros**: Simple, reliable
|
|
279
|
+
**Cons**: Storage intensive for large files
|
|
280
|
+
**Decision**: Partially adopted - store content for reasonable sizes
|
|
281
|
+
|
|
282
|
+
### Alternative 3: Diff-Based Storage
|
|
283
|
+
Store only diffs between versions.
|
|
284
|
+
|
|
285
|
+
**Pros**: Space efficient
|
|
286
|
+
**Cons**: Complex, harder to apply reverts
|
|
287
|
+
**Decision**: Deferred - start simple, optimize later
|
|
288
|
+
|
|
289
|
+
## Security Considerations
|
|
290
|
+
|
|
291
|
+
1. **Sensitive Data**: Checkpoints may contain sensitive content
|
|
292
|
+
2. **Storage Limits**: Limit total checkpoint size
|
|
293
|
+
3. **Cleanup**: Clear checkpoints on session end
|
|
294
|
+
4. **File Permissions**: Respect file permissions when reverting
|
|
295
|
+
5. **Concurrent Access**: Handle external file modifications
|
|
296
|
+
|
|
297
|
+
## Testing Strategy
|
|
298
|
+
|
|
299
|
+
1. **Unit Tests**:
|
|
300
|
+
- Change recording
|
|
301
|
+
- Rewind logic for each change type
|
|
302
|
+
- Multiple file reverts
|
|
303
|
+
|
|
304
|
+
2. **Integration Tests**:
|
|
305
|
+
- Tool wrapping
|
|
306
|
+
- Session persistence
|
|
307
|
+
- Command handling
|
|
308
|
+
|
|
309
|
+
3. **Manual Testing**:
|
|
310
|
+
- Various file operations
|
|
311
|
+
- Partial reverts
|
|
312
|
+
- Edge cases (binary files, permissions)
|
|
313
|
+
|
|
314
|
+
## Migration Path
|
|
315
|
+
|
|
316
|
+
1. **Phase 1**: Basic Write/Edit tracking and rewind
|
|
317
|
+
2. **Phase 2**: /rewind command with UI
|
|
318
|
+
3. **Phase 3**: Git integration (optional)
|
|
319
|
+
4. **Phase 4**: Selective rewind (by file, by time range)
|
|
320
|
+
5. **Phase 5**: Checkpoint browsing and diff viewing
|
|
321
|
+
|
|
322
|
+
No breaking changes to existing functionality.
|
|
323
|
+
|
|
324
|
+
## References
|
|
325
|
+
|
|
326
|
+
- [Claude Code Checkpointing](https://code.claude.com/docs/en/checkpointing)
|
|
327
|
+
- [Git Reset and Revert](https://git-scm.com/docs/git-reset)
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# Proposal: Hooks System
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0009
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Implement an event-driven hooks system that allows users to register shell commands or scripts to run automatically in response to specific events (tool execution, prompt submission, session events). This enables automated workflows, quality gates, and custom integrations.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
Currently, mycode has no extensibility for automated actions. This limits:
|
|
16
|
+
|
|
17
|
+
1. **Quality enforcement**: Can't auto-run linters after edits
|
|
18
|
+
2. **Notifications**: Can't alert users on completion
|
|
19
|
+
3. **Integration**: Can't trigger external systems
|
|
20
|
+
4. **Automation**: Can't enforce coding standards automatically
|
|
21
|
+
5. **Customization**: No way to extend agent behavior
|
|
22
|
+
|
|
23
|
+
A hooks system enables powerful automation and customization.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code provides a comprehensive hooks system:
|
|
28
|
+
|
|
29
|
+
### Configuration
|
|
30
|
+
```json
|
|
31
|
+
// .claude/settings.json
|
|
32
|
+
{
|
|
33
|
+
"hooks": {
|
|
34
|
+
"PostToolUse": [
|
|
35
|
+
{
|
|
36
|
+
"matcher": "Edit|Write",
|
|
37
|
+
"type": "command",
|
|
38
|
+
"command": ".claude/hooks/run-linter.sh $FILE_PATH"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"Notification": [
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"command": "afplay ~/.claude/sounds/notify.mp3"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Available Events
|
|
52
|
+
| Event | Description | Context |
|
|
53
|
+
|-------|-------------|---------|
|
|
54
|
+
| `PreToolUse` | Before tool execution | Tool name, inputs |
|
|
55
|
+
| `PostToolUse` | After tool execution | Tool name, inputs, result |
|
|
56
|
+
| `UserPromptSubmit` | User sends message | Prompt text |
|
|
57
|
+
| `Notification` | Agent needs attention | Message |
|
|
58
|
+
| `Stop` | Agent stops working | Reason |
|
|
59
|
+
| `SessionStart` | Session begins | Session info |
|
|
60
|
+
|
|
61
|
+
### Hook Types
|
|
62
|
+
1. **Command Mode**: Run shell command directly
|
|
63
|
+
2. **Prompt Mode**: Let LLM decide actions (advanced)
|
|
64
|
+
|
|
65
|
+
### Matcher Patterns
|
|
66
|
+
- Simple string: `"Edit"` - matches Edit tool
|
|
67
|
+
- Regex: `"Edit|Write"` - matches Edit or Write
|
|
68
|
+
- Wildcard: `"*"` - matches all
|
|
69
|
+
|
|
70
|
+
### Environment Variables
|
|
71
|
+
Hooks receive context via environment:
|
|
72
|
+
- `$TOOL_NAME` - Name of the tool
|
|
73
|
+
- `$FILE_PATH` - Path of affected file
|
|
74
|
+
- `$EXIT_CODE` - Tool exit code
|
|
75
|
+
- `$SESSION_ID` - Current session
|
|
76
|
+
|
|
77
|
+
## Detailed Design
|
|
78
|
+
|
|
79
|
+
### API Design
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// src/hooks/types.ts
|
|
83
|
+
type HookEvent =
|
|
84
|
+
| 'PreToolUse'
|
|
85
|
+
| 'PostToolUse'
|
|
86
|
+
| 'UserPromptSubmit'
|
|
87
|
+
| 'Notification'
|
|
88
|
+
| 'Stop'
|
|
89
|
+
| 'SessionStart'
|
|
90
|
+
| 'SubagentStop';
|
|
91
|
+
|
|
92
|
+
type HookType = 'command' | 'prompt';
|
|
93
|
+
|
|
94
|
+
interface HookDefinition {
|
|
95
|
+
matcher?: string | RegExp; // For tool events
|
|
96
|
+
type: HookType;
|
|
97
|
+
command?: string; // For command type
|
|
98
|
+
prompt?: string; // For prompt type
|
|
99
|
+
timeout?: number; // Max execution time
|
|
100
|
+
blocking?: boolean; // Wait for completion
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface HooksConfig {
|
|
104
|
+
[event: string]: HookDefinition[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface HookContext {
|
|
108
|
+
event: HookEvent;
|
|
109
|
+
toolName?: string;
|
|
110
|
+
toolInput?: Record<string, any>;
|
|
111
|
+
toolResult?: any;
|
|
112
|
+
prompt?: string;
|
|
113
|
+
sessionId: string;
|
|
114
|
+
cwd: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface HookResult {
|
|
118
|
+
success: boolean;
|
|
119
|
+
output?: string;
|
|
120
|
+
error?: string;
|
|
121
|
+
blocked?: boolean; // Hook blocked the action
|
|
122
|
+
message?: string; // Message to show user
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// src/hooks/hooks-manager.ts
|
|
128
|
+
class HooksManager {
|
|
129
|
+
private config: HooksConfig;
|
|
130
|
+
|
|
131
|
+
constructor(config: HooksConfig);
|
|
132
|
+
|
|
133
|
+
// Register hooks from settings
|
|
134
|
+
loadFromSettings(settings: Settings): void;
|
|
135
|
+
|
|
136
|
+
// Trigger hooks for an event
|
|
137
|
+
async trigger(event: HookEvent, context: HookContext): Promise<HookResult[]>;
|
|
138
|
+
|
|
139
|
+
// Check if event has hooks
|
|
140
|
+
hasHooks(event: HookEvent): boolean;
|
|
141
|
+
|
|
142
|
+
// Get hooks for event
|
|
143
|
+
getHooks(event: HookEvent): HookDefinition[];
|
|
144
|
+
|
|
145
|
+
// Add hook programmatically
|
|
146
|
+
addHook(event: HookEvent, hook: HookDefinition): void;
|
|
147
|
+
|
|
148
|
+
// Remove hook
|
|
149
|
+
removeHook(event: HookEvent, index: number): void;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Implementation Approach
|
|
154
|
+
|
|
155
|
+
1. **Configuration Loading**: Parse hooks from settings.json
|
|
156
|
+
2. **Event Integration**: Hook into tool execution, prompts, etc.
|
|
157
|
+
3. **Matcher Evaluation**: Match hooks to relevant events
|
|
158
|
+
4. **Command Execution**: Run shell commands with context
|
|
159
|
+
5. **Environment Setup**: Pass context via environment variables
|
|
160
|
+
6. **Result Handling**: Process hook output and blocking
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Hook execution
|
|
164
|
+
async function executeHook(hook: HookDefinition, context: HookContext): Promise<HookResult> {
|
|
165
|
+
if (hook.type === 'command') {
|
|
166
|
+
// Build environment
|
|
167
|
+
const env = {
|
|
168
|
+
...process.env,
|
|
169
|
+
TOOL_NAME: context.toolName || '',
|
|
170
|
+
FILE_PATH: context.toolInput?.file_path || '',
|
|
171
|
+
SESSION_ID: context.sessionId,
|
|
172
|
+
CWD: context.cwd,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Expand variables in command
|
|
176
|
+
const command = expandVariables(hook.command, context);
|
|
177
|
+
|
|
178
|
+
// Execute with timeout
|
|
179
|
+
const result = await execWithTimeout(command, {
|
|
180
|
+
env,
|
|
181
|
+
cwd: context.cwd,
|
|
182
|
+
timeout: hook.timeout || 30000
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
success: result.exitCode === 0,
|
|
187
|
+
output: result.stdout,
|
|
188
|
+
error: result.stderr,
|
|
189
|
+
blocked: result.exitCode !== 0 && hook.blocking
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Prompt mode - more complex, uses LLM
|
|
194
|
+
// ...
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### File Changes
|
|
199
|
+
|
|
200
|
+
| File | Action | Description |
|
|
201
|
+
|------|--------|-------------|
|
|
202
|
+
| `src/hooks/types.ts` | Create | Hook type definitions |
|
|
203
|
+
| `src/hooks/hooks-manager.ts` | Create | Core hooks management |
|
|
204
|
+
| `src/hooks/executor.ts` | Create | Hook execution logic |
|
|
205
|
+
| `src/hooks/index.ts` | Create | Module exports |
|
|
206
|
+
| `src/agent/agent.ts` | Modify | Integrate hook triggers |
|
|
207
|
+
| `src/tools/registry.ts` | Modify | Tool execution hooks |
|
|
208
|
+
| `src/config/settings-manager.ts` | Modify | Load hooks config |
|
|
209
|
+
|
|
210
|
+
## User Experience
|
|
211
|
+
|
|
212
|
+
### Configuration
|
|
213
|
+
Users configure hooks in `.mycode/settings.json`:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"hooks": {
|
|
218
|
+
"PostToolUse": [
|
|
219
|
+
{
|
|
220
|
+
"matcher": "Write|Edit",
|
|
221
|
+
"type": "command",
|
|
222
|
+
"command": "npm run lint:fix $FILE_PATH",
|
|
223
|
+
"blocking": false
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
"Stop": [
|
|
227
|
+
{
|
|
228
|
+
"type": "command",
|
|
229
|
+
"command": "afplay ~/.mycode/sounds/done.mp3"
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Hook Execution Display
|
|
237
|
+
```
|
|
238
|
+
Agent: [Write] Created src/component.tsx
|
|
239
|
+
|
|
240
|
+
Running hooks...
|
|
241
|
+
▶ PostToolUse: npm run lint:fix src/component.tsx
|
|
242
|
+
✓ Lint completed (2 issues fixed)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Blocking Hook
|
|
246
|
+
```
|
|
247
|
+
Agent: [Edit] Modified src/auth.ts
|
|
248
|
+
|
|
249
|
+
Running hooks...
|
|
250
|
+
▶ PostToolUse: npm test -- --related src/auth.ts
|
|
251
|
+
✗ Tests failed (2 failures)
|
|
252
|
+
|
|
253
|
+
Hook blocked further execution. Please fix the failing tests.
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Hook Status
|
|
257
|
+
```
|
|
258
|
+
> /hooks
|
|
259
|
+
|
|
260
|
+
Active Hooks:
|
|
261
|
+
┌────────────────────┬─────────────────────────────────────┐
|
|
262
|
+
│ Event │ Action │
|
|
263
|
+
├────────────────────┼─────────────────────────────────────┤
|
|
264
|
+
│ PostToolUse │ npm run lint:fix (Edit|Write) │
|
|
265
|
+
│ Stop │ afplay done.mp3 │
|
|
266
|
+
│ SessionStart │ echo "Welcome!" │
|
|
267
|
+
└────────────────────┴─────────────────────────────────────┘
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Alternatives Considered
|
|
271
|
+
|
|
272
|
+
### Alternative 1: Plugin-Based Hooks
|
|
273
|
+
Hooks as JavaScript/TypeScript modules.
|
|
274
|
+
|
|
275
|
+
**Pros**: More powerful, type-safe
|
|
276
|
+
**Cons**: Requires JS knowledge, security concerns
|
|
277
|
+
**Decision**: Deferred - start with shell commands
|
|
278
|
+
|
|
279
|
+
### Alternative 2: Built-in Actions Only
|
|
280
|
+
Predefined hook actions (lint, test, notify).
|
|
281
|
+
|
|
282
|
+
**Pros**: Simpler, safer
|
|
283
|
+
**Cons**: Limited flexibility
|
|
284
|
+
**Decision**: Rejected - custom commands are essential
|
|
285
|
+
|
|
286
|
+
### Alternative 3: Webhook-Based
|
|
287
|
+
HTTP webhooks instead of shell commands.
|
|
288
|
+
|
|
289
|
+
**Pros**: Remote integration
|
|
290
|
+
**Cons**: Complexity, security, network dependency
|
|
291
|
+
**Decision**: Deferred - can add alongside shell hooks
|
|
292
|
+
|
|
293
|
+
## Security Considerations
|
|
294
|
+
|
|
295
|
+
1. **Command Injection**: Sanitize context values in commands
|
|
296
|
+
2. **Timeout Enforcement**: Prevent runaway hooks
|
|
297
|
+
3. **Resource Limits**: Limit concurrent hook executions
|
|
298
|
+
4. **Path Restrictions**: Consider limiting executable paths
|
|
299
|
+
5. **Output Limits**: Truncate large hook outputs
|
|
300
|
+
6. **Privilege Escalation**: Hooks run with user's permissions
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Safe variable expansion
|
|
304
|
+
function expandVariables(command: string, context: HookContext): string {
|
|
305
|
+
return command
|
|
306
|
+
.replace(/\$FILE_PATH/g, shellescape([context.toolInput?.file_path || '']))
|
|
307
|
+
.replace(/\$TOOL_NAME/g, shellescape([context.toolName || '']))
|
|
308
|
+
// ... etc
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Testing Strategy
|
|
313
|
+
|
|
314
|
+
1. **Unit Tests**:
|
|
315
|
+
- Matcher evaluation
|
|
316
|
+
- Environment building
|
|
317
|
+
- Variable expansion
|
|
318
|
+
|
|
319
|
+
2. **Integration Tests**:
|
|
320
|
+
- Hook triggering from tools
|
|
321
|
+
- Blocking behavior
|
|
322
|
+
- Multiple hooks
|
|
323
|
+
|
|
324
|
+
3. **Manual Testing**:
|
|
325
|
+
- Various hook configurations
|
|
326
|
+
- Error handling
|
|
327
|
+
- Timeout behavior
|
|
328
|
+
|
|
329
|
+
## Migration Path
|
|
330
|
+
|
|
331
|
+
1. **Phase 1**: Basic PostToolUse and Stop hooks
|
|
332
|
+
2. **Phase 2**: All event types
|
|
333
|
+
3. **Phase 3**: Blocking hooks
|
|
334
|
+
4. **Phase 4**: /hooks command and UI
|
|
335
|
+
5. **Phase 5**: Prompt-mode hooks
|
|
336
|
+
|
|
337
|
+
No breaking changes to existing functionality.
|
|
338
|
+
|
|
339
|
+
## References
|
|
340
|
+
|
|
341
|
+
- [Claude Code Hooks Documentation](https://code.claude.com/docs/en/hooks)
|
|
342
|
+
- [Understanding Claude Code Full Stack](https://alexop.dev/posts/understanding-claude-code-full-stack/)
|
|
343
|
+
- [Git Hooks](https://git-scm.com/docs/githooks) (similar concept)
|