appydave-tools 0.70.0 → 0.71.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.
- checksums.yaml +4 -4
- data/.claude/commands/brainstorming-agent.md +227 -0
- data/.claude/commands/cli-test.md +251 -0
- data/.claude/commands/dev.md +234 -0
- data/.claude/commands/po.md +227 -0
- data/.claude/commands/progress.md +51 -0
- data/.claude/commands/uat.md +321 -0
- data/.rubocop.yml +9 -0
- data/AGENTS.md +43 -0
- data/CHANGELOG.md +12 -0
- data/CLAUDE.md +26 -3
- data/README.md +15 -0
- data/bin/dam +21 -1
- data/bin/jump.rb +29 -0
- data/bin/subtitle_processor.rb +54 -1
- data/bin/zsh_history.rb +846 -0
- data/docs/README.md +162 -69
- data/docs/architecture/cli/exe-bin-convention.md +434 -0
- data/docs/architecture/cli-patterns.md +631 -0
- data/docs/architecture/gpt-context/gpt-context-architecture.md +325 -0
- data/docs/architecture/gpt-context/gpt-context-implementation-guide.md +419 -0
- data/docs/architecture/gpt-context/gpt-context-vision.md +179 -0
- data/docs/architecture/testing/testing-patterns.md +762 -0
- data/docs/backlog.md +120 -0
- data/docs/cli-tests/FR-3-jump-location-tool.md +515 -0
- data/docs/specs/fr-002-gpt-context-help-system.md +265 -0
- data/docs/specs/fr-003-jump-location-tool.md +779 -0
- data/docs/specs/zsh-history-tool.md +820 -0
- data/docs/uat/FR-3-jump-location-tool.md +741 -0
- data/exe/jump +11 -0
- data/exe/{subtitle_manager → subtitle_processor} +1 -1
- data/exe/zsh_history +11 -0
- data/lib/appydave/tools/configuration/openai.rb +1 -1
- data/lib/appydave/tools/dam/file_helper.rb +28 -0
- data/lib/appydave/tools/dam/project_listing.rb +4 -30
- data/lib/appydave/tools/dam/s3_operations.rb +2 -1
- data/lib/appydave/tools/dam/ssd_status.rb +226 -0
- data/lib/appydave/tools/dam/status.rb +3 -51
- data/lib/appydave/tools/jump/cli.rb +561 -0
- data/lib/appydave/tools/jump/commands/add.rb +52 -0
- data/lib/appydave/tools/jump/commands/base.rb +43 -0
- data/lib/appydave/tools/jump/commands/generate.rb +153 -0
- data/lib/appydave/tools/jump/commands/remove.rb +58 -0
- data/lib/appydave/tools/jump/commands/report.rb +214 -0
- data/lib/appydave/tools/jump/commands/update.rb +42 -0
- data/lib/appydave/tools/jump/commands/validate.rb +54 -0
- data/lib/appydave/tools/jump/config.rb +233 -0
- data/lib/appydave/tools/jump/formatters/base.rb +48 -0
- data/lib/appydave/tools/jump/formatters/json_formatter.rb +19 -0
- data/lib/appydave/tools/jump/formatters/paths_formatter.rb +21 -0
- data/lib/appydave/tools/jump/formatters/table_formatter.rb +183 -0
- data/lib/appydave/tools/jump/location.rb +134 -0
- data/lib/appydave/tools/jump/path_validator.rb +47 -0
- data/lib/appydave/tools/jump/search.rb +230 -0
- data/lib/appydave/tools/subtitle_processor/transcript.rb +51 -0
- data/lib/appydave/tools/version.rb +1 -1
- data/lib/appydave/tools/zsh_history/command.rb +37 -0
- data/lib/appydave/tools/zsh_history/config.rb +235 -0
- data/lib/appydave/tools/zsh_history/filter.rb +184 -0
- data/lib/appydave/tools/zsh_history/formatter.rb +75 -0
- data/lib/appydave/tools/zsh_history/parser.rb +101 -0
- data/lib/appydave/tools.rb +25 -0
- data/package.json +1 -1
- metadata +51 -4
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
# ZSH History Tool - Implementation Specification
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
A Ruby CLI tool to parse, filter, and clean ZSH history files. Part of the appydave-tools gem.
|
|
6
|
+
|
|
7
|
+
**Primary Goals:**
|
|
8
|
+
1. View clean, filtered ZSH history (remove noise)
|
|
9
|
+
2. Optionally rewrite ~/.zsh_history with filtered content
|
|
10
|
+
3. Filter by time range (last N days)
|
|
11
|
+
4. Categorize commands as wanted/unwanted/unsure using configurable patterns
|
|
12
|
+
|
|
13
|
+
**CLI Pattern:** Pattern 1 (Simple Procedural) - single-purpose tool with OptionParser
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
1. [Requirements](#requirements)
|
|
20
|
+
2. [Architecture](#architecture)
|
|
21
|
+
3. [Data Structures](#data-structures)
|
|
22
|
+
4. [ZSH History Format](#zsh-history-format)
|
|
23
|
+
5. [Parsing Algorithm](#parsing-algorithm)
|
|
24
|
+
6. [Filter System](#filter-system)
|
|
25
|
+
7. [CLI Interface](#cli-interface)
|
|
26
|
+
8. [Configuration](#configuration)
|
|
27
|
+
9. [File Structure](#file-structure)
|
|
28
|
+
10. [Implementation Phases](#implementation-phases)
|
|
29
|
+
11. [Testing Strategy](#testing-strategy)
|
|
30
|
+
12. [Future Enhancements](#future-enhancements)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
### Functional Requirements
|
|
37
|
+
|
|
38
|
+
| ID | Requirement | Priority |
|
|
39
|
+
|----|-------------|----------|
|
|
40
|
+
| FR-1 | Parse ~/.zsh_history including multi-line commands | Must |
|
|
41
|
+
| FR-2 | Extract timestamps and convert to datetime | Must |
|
|
42
|
+
| FR-3 | Apply exclude patterns to filter out noise | Must |
|
|
43
|
+
| FR-4 | Apply include patterns to keep valuable commands | Must |
|
|
44
|
+
| FR-5 | Filter by date range (--days N) | Must |
|
|
45
|
+
| FR-6 | Display clean history to stdout | Must |
|
|
46
|
+
| FR-7 | Rewrite ~/.zsh_history with filtered content (--write) | Should |
|
|
47
|
+
| FR-8 | Show statistics (counts per category) | Should |
|
|
48
|
+
| FR-9 | Search within history (--grep) | Could |
|
|
49
|
+
| FR-10 | Interactive review of unsure commands | Could |
|
|
50
|
+
|
|
51
|
+
### Non-Functional Requirements
|
|
52
|
+
|
|
53
|
+
| ID | Requirement |
|
|
54
|
+
|----|-------------|
|
|
55
|
+
| NFR-1 | Process 50,000+ history entries in < 5 seconds |
|
|
56
|
+
| NFR-2 | Create backup before rewriting history file |
|
|
57
|
+
| NFR-3 | Handle corrupted/malformed history entries gracefully |
|
|
58
|
+
| NFR-4 | UTF-8 support for international characters |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Architecture
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
66
|
+
│ CLI Layer │
|
|
67
|
+
│ bin/zsh_history.rb │
|
|
68
|
+
│ (OptionParser, argument handling, output) │
|
|
69
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
73
|
+
│ Service Layer │
|
|
74
|
+
│ │
|
|
75
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
76
|
+
│ │ Parser │ │ Filter │ │ Formatter │ │
|
|
77
|
+
│ │ │ │ │ │ │ │
|
|
78
|
+
│ │ - read() │ │ - apply() │ │ - clean() │ │
|
|
79
|
+
│ │ - parse() │ │ - match() │ │ - stats() │ │
|
|
80
|
+
│ │ - join() │ │ - categorize│ │ - write() │ │
|
|
81
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
82
|
+
│ │
|
|
83
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
87
|
+
│ Config Layer │
|
|
88
|
+
│ ~/.config/appydave/zsh_history.json │
|
|
89
|
+
│ │
|
|
90
|
+
│ { "exclude_patterns": [...], "include_patterns": [...] } │
|
|
91
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Component Responsibilities
|
|
95
|
+
|
|
96
|
+
| Component | Responsibility |
|
|
97
|
+
|-----------|----------------|
|
|
98
|
+
| **CLI** | Parse arguments, orchestrate workflow, handle I/O |
|
|
99
|
+
| **Parser** | Read ZSH history file, reconstruct multi-line commands |
|
|
100
|
+
| **Filter** | Apply patterns, categorize commands, filter by date |
|
|
101
|
+
| **Formatter** | Format output for display, generate stats, write files |
|
|
102
|
+
| **Config** | Load/save filter patterns from JSON config |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Data Structures
|
|
107
|
+
|
|
108
|
+
### Command Struct
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
Command = Struct.new(
|
|
112
|
+
:timestamp, # Integer - Unix timestamp from history
|
|
113
|
+
:datetime, # Time - Parsed datetime
|
|
114
|
+
:text, # String - Full command text (multi-line joined)
|
|
115
|
+
:is_multiline, # Boolean - Was this a continuation command?
|
|
116
|
+
:category, # Symbol - :wanted, :unwanted, :unsure
|
|
117
|
+
:raw_lines, # Array<String> - Original lines from file
|
|
118
|
+
keyword_init: true
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### FilterResult Struct
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
FilterResult = Struct.new(
|
|
126
|
+
:wanted, # Array<Command>
|
|
127
|
+
:unwanted, # Array<Command>
|
|
128
|
+
:unsure, # Array<Command>
|
|
129
|
+
:stats, # Hash - { total:, wanted:, unwanted:, unsure: }
|
|
130
|
+
keyword_init: true
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Config Structure
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
Config = Struct.new(
|
|
138
|
+
:exclude_patterns, # Array<String> - Regex patterns to exclude
|
|
139
|
+
:include_patterns, # Array<String> - Regex patterns to include
|
|
140
|
+
:history_path, # String - Path to history file (default: ~/.zsh_history)
|
|
141
|
+
keyword_init: true
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## ZSH History Format
|
|
148
|
+
|
|
149
|
+
### Standard Format
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
: 1699876543:0;cd ~/dev
|
|
153
|
+
: 1699876550:0;git status
|
|
154
|
+
: 1699876560:0;ls -la
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Pattern:** `: <timestamp>:<duration>;<command>`
|
|
158
|
+
|
|
159
|
+
- `timestamp` - Unix epoch seconds
|
|
160
|
+
- `duration` - Command duration (often 0)
|
|
161
|
+
- `command` - The actual command text
|
|
162
|
+
|
|
163
|
+
### Multi-line Commands
|
|
164
|
+
|
|
165
|
+
Commands ending with `\` continue on the next line:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
: 1699876570:0;docker run \
|
|
169
|
+
--name myapp \
|
|
170
|
+
-p 3000:3000 \
|
|
171
|
+
myimage:latest
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Reconstruction:** Join lines, remove trailing `\`, preserve internal whitespace.
|
|
175
|
+
|
|
176
|
+
### Edge Cases
|
|
177
|
+
|
|
178
|
+
| Case | Example | Handling |
|
|
179
|
+
|------|---------|----------|
|
|
180
|
+
| Corrupted timestamp | `: abc:0;cmd` | Skip or use epoch 0 |
|
|
181
|
+
| Missing semicolon | `: 123:0cmd` | Skip line |
|
|
182
|
+
| Binary/garbled data | `\x00\x01\x02` | Skip line |
|
|
183
|
+
| Empty command | `: 123:0;` | Skip line |
|
|
184
|
+
| Very long command | 10000+ chars | Truncate for display, keep for filtering |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Parsing Algorithm
|
|
189
|
+
|
|
190
|
+
### Pseudocode
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
function parse_history(file_path):
|
|
194
|
+
lines = read_all_lines(file_path)
|
|
195
|
+
commands = []
|
|
196
|
+
current_command = nil
|
|
197
|
+
|
|
198
|
+
for each line in lines:
|
|
199
|
+
# Try to match history entry format
|
|
200
|
+
if match = line.match(/^: (\d+):\d+;(.*)$/):
|
|
201
|
+
timestamp = match[1].to_i
|
|
202
|
+
command_text = match[2]
|
|
203
|
+
|
|
204
|
+
# If we were building a multi-line command, finalize it
|
|
205
|
+
if current_command:
|
|
206
|
+
commands.append(current_command)
|
|
207
|
+
current_command = nil
|
|
208
|
+
|
|
209
|
+
# Check if this is a continuation command
|
|
210
|
+
if command_text.ends_with?('\'):
|
|
211
|
+
current_command = Command.new(
|
|
212
|
+
timestamp: timestamp,
|
|
213
|
+
text: command_text.chomp('\\'),
|
|
214
|
+
is_multiline: true,
|
|
215
|
+
raw_lines: [line]
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
commands.append(Command.new(
|
|
219
|
+
timestamp: timestamp,
|
|
220
|
+
text: command_text,
|
|
221
|
+
is_multiline: false,
|
|
222
|
+
raw_lines: [line]
|
|
223
|
+
))
|
|
224
|
+
|
|
225
|
+
# Line doesn't match format - might be continuation
|
|
226
|
+
else:
|
|
227
|
+
if current_command:
|
|
228
|
+
# Append to current multi-line command
|
|
229
|
+
current_command.raw_lines.append(line)
|
|
230
|
+
|
|
231
|
+
if line.ends_with?('\'):
|
|
232
|
+
current_command.text += "\n" + line.chomp('\\')
|
|
233
|
+
else:
|
|
234
|
+
current_command.text += "\n" + line
|
|
235
|
+
commands.append(current_command)
|
|
236
|
+
current_command = nil
|
|
237
|
+
else:
|
|
238
|
+
# Orphan line - skip or log warning
|
|
239
|
+
log_warning("Orphan line: #{line}")
|
|
240
|
+
|
|
241
|
+
# Don't forget trailing command
|
|
242
|
+
if current_command:
|
|
243
|
+
commands.append(current_command)
|
|
244
|
+
|
|
245
|
+
return commands
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Implementation Notes
|
|
249
|
+
|
|
250
|
+
1. **Read entire file** - ZSH history is typically < 10MB, safe to load into memory
|
|
251
|
+
2. **Handle encoding** - Use `File.read(path, encoding: 'UTF-8', invalid: :replace)`
|
|
252
|
+
3. **Preserve raw lines** - Needed for accurate rewriting
|
|
253
|
+
4. **Convert timestamps** - `Time.at(timestamp)` for datetime
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Filter System
|
|
258
|
+
|
|
259
|
+
### Filter Logic
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
for each command in commands:
|
|
263
|
+
# First, check date range
|
|
264
|
+
if options.days and command.datetime < (now - days):
|
|
265
|
+
skip command entirely
|
|
266
|
+
|
|
267
|
+
# Check exclude patterns first (noise removal)
|
|
268
|
+
for pattern in exclude_patterns:
|
|
269
|
+
if command.text matches pattern:
|
|
270
|
+
command.category = :unwanted
|
|
271
|
+
break
|
|
272
|
+
|
|
273
|
+
# If not excluded, check include patterns
|
|
274
|
+
if command.category != :unwanted:
|
|
275
|
+
for pattern in include_patterns:
|
|
276
|
+
if command.text matches pattern:
|
|
277
|
+
command.category = :wanted
|
|
278
|
+
break
|
|
279
|
+
|
|
280
|
+
# If neither matched, it's unsure
|
|
281
|
+
if command.category == nil:
|
|
282
|
+
command.category = :unsure
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Default Exclude Patterns
|
|
286
|
+
|
|
287
|
+
Based on analysis of the user's history files:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"exclude_patterns": [
|
|
292
|
+
"^[a-z]$",
|
|
293
|
+
"^[a-z]{2}$",
|
|
294
|
+
"^ls$",
|
|
295
|
+
"^ls -",
|
|
296
|
+
"^pwd$",
|
|
297
|
+
"^clear$",
|
|
298
|
+
"^exit$",
|
|
299
|
+
"^cd$",
|
|
300
|
+
"^cd -$",
|
|
301
|
+
"^\\.$",
|
|
302
|
+
"^\\.\\.$",
|
|
303
|
+
"^git status$",
|
|
304
|
+
"^git diff$",
|
|
305
|
+
"^git log$",
|
|
306
|
+
"^git pull$",
|
|
307
|
+
"^gs$",
|
|
308
|
+
"^gd$",
|
|
309
|
+
"^gl$",
|
|
310
|
+
"^h$",
|
|
311
|
+
"^history",
|
|
312
|
+
"^which ",
|
|
313
|
+
"^type ",
|
|
314
|
+
"^cat ",
|
|
315
|
+
"^head ",
|
|
316
|
+
"^tail ",
|
|
317
|
+
"^echo \\$",
|
|
318
|
+
"^\\[\\d+\\]",
|
|
319
|
+
"^davidcruwys\\s+\\d+",
|
|
320
|
+
"^zsh: command not found",
|
|
321
|
+
"^X Process completed",
|
|
322
|
+
"^Coverage report",
|
|
323
|
+
"^Line Coverage:",
|
|
324
|
+
"^Finished in \\d",
|
|
325
|
+
"^\\d+ examples, \\d+ failures"
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Pattern Categories:**
|
|
331
|
+
|
|
332
|
+
| Category | Examples | Rationale |
|
|
333
|
+
|----------|----------|-----------|
|
|
334
|
+
| Single chars | `^[a-z]$` | Typos, not real commands |
|
|
335
|
+
| Navigation | `^cd$`, `^pwd$`, `^ls` | High frequency, low value |
|
|
336
|
+
| Git read-only | `^git status$`, `^git diff$` | Exploratory, not actions |
|
|
337
|
+
| Output lines | `^davidcruwys\\s+\\d+`, `^\[\\d+\]` | Command output, not commands |
|
|
338
|
+
| Error messages | `^zsh: command not found` | Noise |
|
|
339
|
+
| Test output | `^Finished in`, `^\\d+ examples` | RSpec/test runner output |
|
|
340
|
+
|
|
341
|
+
### Default Include Patterns
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"include_patterns": [
|
|
346
|
+
"^j[a-z]",
|
|
347
|
+
"^dam ",
|
|
348
|
+
"^vat ",
|
|
349
|
+
"^claude ",
|
|
350
|
+
"^c-sonnet",
|
|
351
|
+
"^bun run ",
|
|
352
|
+
"^npm run ",
|
|
353
|
+
"^rake ",
|
|
354
|
+
"^bundle ",
|
|
355
|
+
"^git commit",
|
|
356
|
+
"^git push",
|
|
357
|
+
"^git add",
|
|
358
|
+
"^gac ",
|
|
359
|
+
"^kfeat ",
|
|
360
|
+
"^kfix ",
|
|
361
|
+
"^docker ",
|
|
362
|
+
"^brew install",
|
|
363
|
+
"^gem install",
|
|
364
|
+
"^npm install"
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Pattern Categories:**
|
|
370
|
+
|
|
371
|
+
| Category | Examples | Rationale |
|
|
372
|
+
|----------|----------|-----------|
|
|
373
|
+
| Jump aliases | `^j[a-z]` | Navigation shortcuts, useful reference |
|
|
374
|
+
| AppyDave tools | `^dam `, `^vat ` | Custom tooling usage |
|
|
375
|
+
| Claude | `^claude `, `^c-sonnet` | AI assistant commands |
|
|
376
|
+
| Build/run | `^bun run `, `^npm run ` | Development workflow |
|
|
377
|
+
| Git writes | `^git commit`, `^git push` | Meaningful actions |
|
|
378
|
+
| Installs | `^brew install`, `^gem install` | System changes worth tracking |
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## CLI Interface
|
|
383
|
+
|
|
384
|
+
### Command Structure
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
# Primary command - show clean history
|
|
388
|
+
zsh_history clean [options]
|
|
389
|
+
|
|
390
|
+
# Options
|
|
391
|
+
-d, --days N Only show last N days
|
|
392
|
+
-w, --write Rewrite ~/.zsh_history (creates backup)
|
|
393
|
+
-s, --stats Show statistics only
|
|
394
|
+
-g, --grep PATTERN Search within history
|
|
395
|
+
-u, --unsure Include unsure commands in output
|
|
396
|
+
-a, --all Show all commands (no filtering)
|
|
397
|
+
-v, --verbose Show which pattern matched each command
|
|
398
|
+
-h, --help Show help
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Usage Examples
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
# View clean history (default: wanted only)
|
|
405
|
+
zsh_history clean
|
|
406
|
+
|
|
407
|
+
# View last 7 days
|
|
408
|
+
zsh_history clean --days 7
|
|
409
|
+
|
|
410
|
+
# View with unsure commands included
|
|
411
|
+
zsh_history clean --unsure
|
|
412
|
+
|
|
413
|
+
# Search for docker commands in last 30 days
|
|
414
|
+
zsh_history clean --days 30 --grep docker
|
|
415
|
+
|
|
416
|
+
# Show statistics
|
|
417
|
+
zsh_history clean --stats
|
|
418
|
+
|
|
419
|
+
# Rewrite history file (creates backup first)
|
|
420
|
+
zsh_history clean --days 90 --write
|
|
421
|
+
|
|
422
|
+
# Debug: see which patterns matched
|
|
423
|
+
zsh_history clean --verbose
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Output Formats
|
|
427
|
+
|
|
428
|
+
**Default (clean):**
|
|
429
|
+
```
|
|
430
|
+
2024-11-15 09:30:22 dam s3-up appydave b70
|
|
431
|
+
2024-11-15 09:35:10 bun run dev
|
|
432
|
+
2024-11-15 10:00:00 git commit -m 'update docs'
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
**Stats:**
|
|
436
|
+
```
|
|
437
|
+
ZSH History Statistics
|
|
438
|
+
═══════════════════════════════════════
|
|
439
|
+
Total commands: 12,543
|
|
440
|
+
Wanted: 2,341 (18.7%)
|
|
441
|
+
Unwanted: 9,876 (78.7%)
|
|
442
|
+
Unsure: 326 ( 2.6%)
|
|
443
|
+
|
|
444
|
+
Date range: 2024-01-01 to 2024-11-15 (319 days)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Verbose:**
|
|
448
|
+
```
|
|
449
|
+
2024-11-15 09:30:22 [WANTED: ^dam ] dam s3-up appydave b70
|
|
450
|
+
2024-11-15 09:31:00 [EXCLUDE: ^ls$] ls
|
|
451
|
+
2024-11-15 09:35:10 [WANTED: ^bun run ] bun run dev
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Configuration
|
|
457
|
+
|
|
458
|
+
### Config File Location
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
~/.config/appydave/zsh_history.json
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Full Config Schema
|
|
465
|
+
|
|
466
|
+
```json
|
|
467
|
+
{
|
|
468
|
+
"history_path": "~/.zsh_history",
|
|
469
|
+
"backup_before_write": true,
|
|
470
|
+
"backup_dir": "~/.config/appydave/backups",
|
|
471
|
+
"exclude_patterns": [
|
|
472
|
+
"^[a-z]$",
|
|
473
|
+
"^ls$",
|
|
474
|
+
"^pwd$",
|
|
475
|
+
"^clear$",
|
|
476
|
+
"^exit$",
|
|
477
|
+
"^cd$",
|
|
478
|
+
"^git status$",
|
|
479
|
+
"^git diff$",
|
|
480
|
+
"^git log$"
|
|
481
|
+
],
|
|
482
|
+
"include_patterns": [
|
|
483
|
+
"^j[a-z]",
|
|
484
|
+
"^dam ",
|
|
485
|
+
"^claude ",
|
|
486
|
+
"^bun run ",
|
|
487
|
+
"^npm run ",
|
|
488
|
+
"^git commit",
|
|
489
|
+
"^git push"
|
|
490
|
+
],
|
|
491
|
+
"output": {
|
|
492
|
+
"datetime_format": "%Y-%m-%d %H:%M:%S",
|
|
493
|
+
"show_multiline_indicator": true,
|
|
494
|
+
"max_command_length": 200
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Config Integration
|
|
500
|
+
|
|
501
|
+
Uses existing `Appydave::Tools::Configuration` system:
|
|
502
|
+
|
|
503
|
+
```ruby
|
|
504
|
+
module Appydave::Tools::Configuration::Models
|
|
505
|
+
class ZshHistoryConfig < ConfigBase
|
|
506
|
+
# Auto-loads from ~/.config/appydave/zsh_history.json
|
|
507
|
+
|
|
508
|
+
def exclude_patterns
|
|
509
|
+
data['exclude_patterns'] || DEFAULT_EXCLUDE_PATTERNS
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def include_patterns
|
|
513
|
+
data['include_patterns'] || DEFAULT_INCLUDE_PATTERNS
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## File Structure
|
|
522
|
+
|
|
523
|
+
### New Files to Create
|
|
524
|
+
|
|
525
|
+
```
|
|
526
|
+
lib/appydave/tools/zsh_history/
|
|
527
|
+
├── parser.rb # Parse ZSH history file
|
|
528
|
+
├── filter.rb # Apply patterns, categorize
|
|
529
|
+
├── formatter.rb # Output formatting, stats
|
|
530
|
+
└── command.rb # Command struct definition
|
|
531
|
+
|
|
532
|
+
lib/appydave/tools/configuration/models/
|
|
533
|
+
└── zsh_history_config.rb # Config model
|
|
534
|
+
|
|
535
|
+
bin/
|
|
536
|
+
└── zsh_history.rb # CLI entry point
|
|
537
|
+
|
|
538
|
+
exe/
|
|
539
|
+
└── zsh_history # Gem executable wrapper
|
|
540
|
+
|
|
541
|
+
spec/appydave/tools/zsh_history/
|
|
542
|
+
├── parser_spec.rb
|
|
543
|
+
├── filter_spec.rb
|
|
544
|
+
├── formatter_spec.rb
|
|
545
|
+
└── fixtures/
|
|
546
|
+
├── simple_history.txt
|
|
547
|
+
├── multiline_history.txt
|
|
548
|
+
└── corrupted_history.txt
|
|
549
|
+
|
|
550
|
+
docs/
|
|
551
|
+
├── specs/
|
|
552
|
+
│ └── zsh-history-tool.md # This document
|
|
553
|
+
└── usage/
|
|
554
|
+
└── zsh-history.md # User guide (create after implementation)
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Module Registration
|
|
558
|
+
|
|
559
|
+
Add to `lib/appydave/tools.rb`:
|
|
560
|
+
|
|
561
|
+
```ruby
|
|
562
|
+
require_relative 'tools/zsh_history/command'
|
|
563
|
+
require_relative 'tools/zsh_history/parser'
|
|
564
|
+
require_relative 'tools/zsh_history/filter'
|
|
565
|
+
require_relative 'tools/zsh_history/formatter'
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Add config to `lib/appydave/tools/configuration/config.rb`:
|
|
569
|
+
|
|
570
|
+
```ruby
|
|
571
|
+
register_config(:zsh_history, Models::ZshHistoryConfig)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Implementation Phases
|
|
577
|
+
|
|
578
|
+
### Phase 1: Core Parser (MVP)
|
|
579
|
+
|
|
580
|
+
**Goal:** Parse history file and display commands with timestamps
|
|
581
|
+
|
|
582
|
+
**Tasks:**
|
|
583
|
+
1. Create `Command` struct
|
|
584
|
+
2. Implement `Parser.parse(file_path)`
|
|
585
|
+
3. Handle single-line commands
|
|
586
|
+
4. Handle multi-line commands (\ continuations)
|
|
587
|
+
5. Basic CLI that outputs parsed commands
|
|
588
|
+
6. Unit tests for parser
|
|
589
|
+
|
|
590
|
+
**Deliverable:**
|
|
591
|
+
```bash
|
|
592
|
+
zsh_history clean # Shows all commands with timestamps
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Phase 2: Filtering
|
|
596
|
+
|
|
597
|
+
**Goal:** Apply include/exclude patterns
|
|
598
|
+
|
|
599
|
+
**Tasks:**
|
|
600
|
+
1. Create `Filter` class with `apply(commands, config)`
|
|
601
|
+
2. Implement pattern matching (Regexp)
|
|
602
|
+
3. Categorize as wanted/unwanted/unsure
|
|
603
|
+
4. Add `--stats` flag
|
|
604
|
+
5. Unit tests for filter
|
|
605
|
+
|
|
606
|
+
**Deliverable:**
|
|
607
|
+
```bash
|
|
608
|
+
zsh_history clean # Shows filtered (wanted) commands
|
|
609
|
+
zsh_history clean --stats # Shows counts
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Phase 3: Date Filtering
|
|
613
|
+
|
|
614
|
+
**Goal:** Filter by time range
|
|
615
|
+
|
|
616
|
+
**Tasks:**
|
|
617
|
+
1. Add `--days N` option
|
|
618
|
+
2. Parse timestamps into Time objects
|
|
619
|
+
3. Filter commands older than N days
|
|
620
|
+
4. Handle timezone correctly
|
|
621
|
+
|
|
622
|
+
**Deliverable:**
|
|
623
|
+
```bash
|
|
624
|
+
zsh_history clean --days 7
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Phase 4: Configuration
|
|
628
|
+
|
|
629
|
+
**Goal:** Load patterns from config file
|
|
630
|
+
|
|
631
|
+
**Tasks:**
|
|
632
|
+
1. Create `ZshHistoryConfig` model
|
|
633
|
+
2. Register with configuration system
|
|
634
|
+
3. Load exclude/include patterns from JSON
|
|
635
|
+
4. Use defaults if config doesn't exist
|
|
636
|
+
5. Document default patterns
|
|
637
|
+
|
|
638
|
+
**Deliverable:**
|
|
639
|
+
```bash
|
|
640
|
+
# Uses ~/.config/appydave/zsh_history.json
|
|
641
|
+
zsh_history clean
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Phase 5: Write Mode
|
|
645
|
+
|
|
646
|
+
**Goal:** Rewrite history file
|
|
647
|
+
|
|
648
|
+
**Tasks:**
|
|
649
|
+
1. Add `--write` flag
|
|
650
|
+
2. Create backup before writing
|
|
651
|
+
3. Write filtered commands in ZSH history format
|
|
652
|
+
4. Validate output before overwriting
|
|
653
|
+
5. Safety checks (don't write if < 10% of original)
|
|
654
|
+
|
|
655
|
+
**Deliverable:**
|
|
656
|
+
```bash
|
|
657
|
+
zsh_history clean --days 90 --write
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### Phase 6: Polish
|
|
661
|
+
|
|
662
|
+
**Goal:** User experience improvements
|
|
663
|
+
|
|
664
|
+
**Tasks:**
|
|
665
|
+
1. Add `--grep` for searching
|
|
666
|
+
2. Add `--verbose` for debugging patterns
|
|
667
|
+
3. Add `--unsure` to include unsure commands
|
|
668
|
+
4. Improve output formatting
|
|
669
|
+
5. Create user documentation
|
|
670
|
+
6. Add to CLAUDE.md tool list
|
|
671
|
+
|
|
672
|
+
---
|
|
673
|
+
|
|
674
|
+
## Testing Strategy
|
|
675
|
+
|
|
676
|
+
### Unit Tests
|
|
677
|
+
|
|
678
|
+
**Parser:**
|
|
679
|
+
```ruby
|
|
680
|
+
RSpec.describe Appydave::Tools::ZshHistory::Parser do
|
|
681
|
+
describe '#parse' do
|
|
682
|
+
it 'parses single-line commands'
|
|
683
|
+
it 'parses multi-line commands with continuations'
|
|
684
|
+
it 'extracts timestamps correctly'
|
|
685
|
+
it 'handles corrupted lines gracefully'
|
|
686
|
+
it 'handles empty file'
|
|
687
|
+
it 'handles missing file'
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
**Filter:**
|
|
693
|
+
```ruby
|
|
694
|
+
RSpec.describe Appydave::Tools::ZshHistory::Filter do
|
|
695
|
+
describe '#apply' do
|
|
696
|
+
it 'excludes commands matching exclude patterns'
|
|
697
|
+
it 'includes commands matching include patterns'
|
|
698
|
+
it 'marks unmatched commands as unsure'
|
|
699
|
+
it 'respects pattern priority (exclude first)'
|
|
700
|
+
it 'handles empty pattern lists'
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
describe '#filter_by_date' do
|
|
704
|
+
it 'filters commands older than N days'
|
|
705
|
+
it 'keeps commands within N days'
|
|
706
|
+
it 'handles commands at boundary'
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### Test Fixtures
|
|
712
|
+
|
|
713
|
+
**simple_history.txt:**
|
|
714
|
+
```
|
|
715
|
+
: 1699876543:0;cd ~/dev
|
|
716
|
+
: 1699876550:0;git status
|
|
717
|
+
: 1699876560:0;ls -la
|
|
718
|
+
: 1699876570:0;dam s3-up appydave b70
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**multiline_history.txt:**
|
|
722
|
+
```
|
|
723
|
+
: 1699876543:0;docker run \
|
|
724
|
+
--name myapp \
|
|
725
|
+
-p 3000:3000 \
|
|
726
|
+
myimage:latest
|
|
727
|
+
: 1699876600:0;echo "done"
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**corrupted_history.txt:**
|
|
731
|
+
```
|
|
732
|
+
: 1699876543:0;valid command
|
|
733
|
+
garbage line here
|
|
734
|
+
: badtimestamp:0;another
|
|
735
|
+
: 1699876600:0;valid again
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### Integration Tests
|
|
739
|
+
|
|
740
|
+
```ruby
|
|
741
|
+
RSpec.describe 'zsh_history CLI' do
|
|
742
|
+
it 'displays clean history'
|
|
743
|
+
it 'filters by days'
|
|
744
|
+
it 'shows stats'
|
|
745
|
+
it 'creates backup before writing'
|
|
746
|
+
it 'refuses to write if too few commands remain'
|
|
747
|
+
end
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
---
|
|
751
|
+
|
|
752
|
+
## Future Enhancements
|
|
753
|
+
|
|
754
|
+
### v1.1 - Interactive Review
|
|
755
|
+
|
|
756
|
+
```bash
|
|
757
|
+
zsh_history review
|
|
758
|
+
# Shows unsure commands one by one
|
|
759
|
+
# User types 'w' (wanted), 'u' (unwanted), 's' (skip)
|
|
760
|
+
# Updates config with new patterns
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### v1.2 - Pattern Suggestions
|
|
764
|
+
|
|
765
|
+
```bash
|
|
766
|
+
zsh_history suggest
|
|
767
|
+
# Analyzes unsure commands
|
|
768
|
+
# Suggests patterns based on frequency
|
|
769
|
+
# "Found 47 commands starting with 'npx' - add to include? (y/n)"
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### v1.3 - Export/Import
|
|
773
|
+
|
|
774
|
+
```bash
|
|
775
|
+
zsh_history export --format json > history.json
|
|
776
|
+
zsh_history export --format txt > history.txt
|
|
777
|
+
zsh_history import --from other_machine.txt
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### v1.4 - Deduplication
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
zsh_history clean --dedupe
|
|
784
|
+
# Removes duplicate commands, keeping most recent
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## Appendix: Pattern Reference
|
|
790
|
+
|
|
791
|
+
### Regex Cheat Sheet
|
|
792
|
+
|
|
793
|
+
| Pattern | Matches | Example |
|
|
794
|
+
|---------|---------|---------|
|
|
795
|
+
| `^` | Start of line | `^git` matches "git status" |
|
|
796
|
+
| `$` | End of line | `^ls$` matches only "ls" |
|
|
797
|
+
| `\s` | Whitespace | `^cd\s` matches "cd ~/dev" |
|
|
798
|
+
| `\d+` | One or more digits | `^\d+` matches "123 foo" |
|
|
799
|
+
| `[a-z]` | Single lowercase letter | `^[a-z]$` matches "a", "b" |
|
|
800
|
+
| `.*` | Any characters | `^git.*push` matches "git push origin main" |
|
|
801
|
+
| `\|` | Literal pipe (escaped) | `\|` matches commands with pipes |
|
|
802
|
+
|
|
803
|
+
### Common Pattern Examples
|
|
804
|
+
|
|
805
|
+
```ruby
|
|
806
|
+
# Match all npm/yarn commands
|
|
807
|
+
"^(npm|yarn|bun) "
|
|
808
|
+
|
|
809
|
+
# Match all git write operations
|
|
810
|
+
"^git (add|commit|push|rebase|merge|cherry-pick)"
|
|
811
|
+
|
|
812
|
+
# Match any command with a pipe
|
|
813
|
+
"\\|"
|
|
814
|
+
|
|
815
|
+
# Match any command with sudo
|
|
816
|
+
"^sudo "
|
|
817
|
+
|
|
818
|
+
# Match docker/docker-compose
|
|
819
|
+
"^docker(-compose)? "
|
|
820
|
+
```
|