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,779 @@
|
|
|
1
|
+
# FR-3: Jump Location Tool
|
|
2
|
+
|
|
3
|
+
**Status**: Ready for Development
|
|
4
|
+
**Added**: 2025-12-13
|
|
5
|
+
**Priority**: High - enables unified location management across terminal, Claude Code, and generated config
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
A Ruby CLI tool for managing development folder locations with a single source of truth (JSON config) that serves:
|
|
12
|
+
- Terminal users (fuzzy search, jump aliases)
|
|
13
|
+
- Claude Code (via skill with JSON output)
|
|
14
|
+
- Generated shell configuration (aliases-jump.zsh)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Problem Statement
|
|
19
|
+
|
|
20
|
+
Current pain points:
|
|
21
|
+
1. Jump aliases (`jad`, `jss`, `jgb`) are hard to remember
|
|
22
|
+
2. No single source of truth - aliases defined in one file, help documentation duplicated elsewhere
|
|
23
|
+
3. In Claude Code, need to find folder paths but have no searchable system
|
|
24
|
+
4. Locations organized by multiple dimensions (brand, client, type, technology) but no way to view by these dimensions
|
|
25
|
+
5. Locations become stale (folders deleted/moved) with no validation
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
33
|
+
│ locations.json │
|
|
34
|
+
│ (Single Source of Truth) │
|
|
35
|
+
└─────────────────────────────────────────────────────────────┘
|
|
36
|
+
│
|
|
37
|
+
┌─────────────────────┼─────────────────────┐
|
|
38
|
+
▼ ▼ ▼
|
|
39
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
40
|
+
│ jump CLI │ │ Generated │ │ Claude Skill │
|
|
41
|
+
│ │ │ Files │ │ │
|
|
42
|
+
│ - search │ │ │ │ Calls CLI │
|
|
43
|
+
│ - add/remove │ │ aliases- │ │ with --format│
|
|
44
|
+
│ - validate │ │ jump.zsh │ │ json flag │
|
|
45
|
+
│ - reports │ │ │ │ │
|
|
46
|
+
│ - generate │ │ help content │ │ │
|
|
47
|
+
└──────────────┘ └──────────────┘ └──────────────┘
|
|
48
|
+
│
|
|
49
|
+
▼
|
|
50
|
+
┌──────────────┐
|
|
51
|
+
│ Terminal │
|
|
52
|
+
│ │
|
|
53
|
+
│ j alias │
|
|
54
|
+
│ ah + fzf │
|
|
55
|
+
└──────────────┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## File Locations
|
|
61
|
+
|
|
62
|
+
| File | Path |
|
|
63
|
+
|------|------|
|
|
64
|
+
| Tool code | `lib/appydave/tools/jump/` |
|
|
65
|
+
| Config file | `~/.config/appydave/locations.json` |
|
|
66
|
+
| Generated aliases | `~/.oh-my-zsh/custom/aliases-jump.zsh` |
|
|
67
|
+
| Help content | `~/.oh-my-zsh/custom/data/jump-help.txt` |
|
|
68
|
+
| Claude skill | `~/.claude/skills/jump-locations.md` |
|
|
69
|
+
|
|
70
|
+
**Note**: Config follows existing pattern - stored in `~/.config/appydave/` alongside `settings.json`, `channels.json`, etc. Uses the same config infrastructure with injectable paths for testing.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Data Model
|
|
75
|
+
|
|
76
|
+
### locations.json Structure
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"meta": {
|
|
81
|
+
"version": "1.0",
|
|
82
|
+
"last_validated": "2025-12-13T10:00:00Z"
|
|
83
|
+
},
|
|
84
|
+
"categories": {
|
|
85
|
+
"type": {
|
|
86
|
+
"description": "Kind of location",
|
|
87
|
+
"values": ["brand", "client", "gem", "video", "brain", "site", "tool", "config"]
|
|
88
|
+
},
|
|
89
|
+
"technology": {
|
|
90
|
+
"description": "Primary language/framework",
|
|
91
|
+
"values": ["ruby", "javascript", "typescript", "python", "astro"]
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"brands": {
|
|
95
|
+
"appydave": {
|
|
96
|
+
"aliases": ["ad", "appy", "dave"],
|
|
97
|
+
"description": "AppyDave brand"
|
|
98
|
+
},
|
|
99
|
+
"flivideo": {
|
|
100
|
+
"aliases": ["fli"],
|
|
101
|
+
"description": "FliVideo brand"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"clients": {
|
|
105
|
+
"supportsignal": {
|
|
106
|
+
"aliases": ["ss"],
|
|
107
|
+
"description": "SupportSignal client"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"locations": [
|
|
111
|
+
{
|
|
112
|
+
"key": "ad-tools",
|
|
113
|
+
"path": "~/dev/ad/appydave-tools",
|
|
114
|
+
"jump": "jad-tools",
|
|
115
|
+
"brand": "appydave",
|
|
116
|
+
"type": "tool",
|
|
117
|
+
"tags": ["ruby", "cli"],
|
|
118
|
+
"description": "AppyDave CLI tools"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Location Entry Fields
|
|
125
|
+
|
|
126
|
+
| Field | Required | Default | Description |
|
|
127
|
+
|-------|----------|---------|-------------|
|
|
128
|
+
| key | Yes | - | Unique identifier (alphanumeric + hyphens, lowercase) |
|
|
129
|
+
| path | Yes | - | Directory path (supports ~) |
|
|
130
|
+
| jump | No | `j` + key | Shell alias name |
|
|
131
|
+
| brand | No | - | Associated brand key |
|
|
132
|
+
| client | No | - | Associated client key |
|
|
133
|
+
| type | No | - | Category type |
|
|
134
|
+
| tags | No | [] | Searchable tags array |
|
|
135
|
+
| description | No | - | Human description |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## CLI Commands
|
|
140
|
+
|
|
141
|
+
### Command Name
|
|
142
|
+
|
|
143
|
+
`jump` (installed via gem as `jump`)
|
|
144
|
+
|
|
145
|
+
### Search & Retrieval
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Fuzzy search (primary use case)
|
|
149
|
+
jump search <terms>
|
|
150
|
+
jump search appydave ruby
|
|
151
|
+
jump search ss app
|
|
152
|
+
|
|
153
|
+
# Get by exact key
|
|
154
|
+
jump get <key>
|
|
155
|
+
jump get ad-tools
|
|
156
|
+
|
|
157
|
+
# List all
|
|
158
|
+
jump list
|
|
159
|
+
|
|
160
|
+
# All commands support --format
|
|
161
|
+
jump search appydave --format json|table|paths
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### CRUD Operations
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Add
|
|
168
|
+
jump add --key <key> --path <path> [--jump alias] [--brand brand] \
|
|
169
|
+
[--client client] [--type type] [--tags t1,t2] [--description "desc"]
|
|
170
|
+
|
|
171
|
+
# Update
|
|
172
|
+
jump update <key> [--path path] [--brand brand] [--tags tags] ...
|
|
173
|
+
|
|
174
|
+
# Remove
|
|
175
|
+
jump remove <key>
|
|
176
|
+
jump remove <key> --force
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Validation
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Validate all paths exist
|
|
183
|
+
jump validate
|
|
184
|
+
|
|
185
|
+
# Validate specific key
|
|
186
|
+
jump validate <key>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Behavior**: Report only - shows valid/invalid/missing paths. Does NOT auto-prompt for removal. User decides what to do.
|
|
190
|
+
|
|
191
|
+
### Reports (View Data by Dimension)
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# List all categories and their values
|
|
195
|
+
jump report categories
|
|
196
|
+
|
|
197
|
+
# List all brands with location counts
|
|
198
|
+
jump report brands
|
|
199
|
+
|
|
200
|
+
# List all clients with location counts
|
|
201
|
+
jump report clients
|
|
202
|
+
|
|
203
|
+
# List all types with location counts
|
|
204
|
+
jump report types
|
|
205
|
+
|
|
206
|
+
# List all tags with location counts
|
|
207
|
+
jump report tags
|
|
208
|
+
|
|
209
|
+
# List locations grouped by brand
|
|
210
|
+
jump report by-brand
|
|
211
|
+
jump report by-brand appydave
|
|
212
|
+
|
|
213
|
+
# List locations grouped by client
|
|
214
|
+
jump report by-client
|
|
215
|
+
|
|
216
|
+
# List locations grouped by type
|
|
217
|
+
jump report by-type
|
|
218
|
+
|
|
219
|
+
# List locations grouped by tag
|
|
220
|
+
jump report by-tag ruby
|
|
221
|
+
|
|
222
|
+
# Summary overview
|
|
223
|
+
jump report summary
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Generation
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Generate shell aliases (stdout by default)
|
|
230
|
+
jump generate aliases
|
|
231
|
+
jump generate aliases --output ~/.oh-my-zsh/custom/aliases-jump.zsh
|
|
232
|
+
|
|
233
|
+
# Generate help content for ah/fzf
|
|
234
|
+
jump generate help
|
|
235
|
+
jump generate help --output ~/.oh-my-zsh/custom/data/jump-help.txt
|
|
236
|
+
|
|
237
|
+
# Generate both
|
|
238
|
+
jump generate all
|
|
239
|
+
jump generate all --output-dir ~/.oh-my-zsh/custom/
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Behavior**:
|
|
243
|
+
- Stdout by default
|
|
244
|
+
- Use `--output <file>` to write to file
|
|
245
|
+
- **Important**: Before first use, manually backup existing `aliases-jump.zsh` once
|
|
246
|
+
|
|
247
|
+
### Info
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Show config path, location count, last validated
|
|
251
|
+
jump info
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Output Formats
|
|
257
|
+
|
|
258
|
+
| Format | Flag | Use Case |
|
|
259
|
+
|--------|------|----------|
|
|
260
|
+
| table | `--format table` (default) | Human terminal reading - **pretty with colors** |
|
|
261
|
+
| json | `--format json` | Claude skill, programmatic access |
|
|
262
|
+
| paths | `--format paths` | Scripting, piping |
|
|
263
|
+
|
|
264
|
+
### JSON Response Structure
|
|
265
|
+
|
|
266
|
+
**Success**:
|
|
267
|
+
```json
|
|
268
|
+
{
|
|
269
|
+
"success": true,
|
|
270
|
+
"count": 2,
|
|
271
|
+
"results": [
|
|
272
|
+
{
|
|
273
|
+
"index": 1,
|
|
274
|
+
"key": "ad-tools",
|
|
275
|
+
"path": "/Users/davidcruwys/dev/ad/appydave-tools",
|
|
276
|
+
"jump": "jad-tools",
|
|
277
|
+
"brand": "appydave",
|
|
278
|
+
"type": "tool",
|
|
279
|
+
"tags": ["ruby", "cli"],
|
|
280
|
+
"description": "AppyDave CLI tools",
|
|
281
|
+
"score": 85
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Error**:
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"success": false,
|
|
291
|
+
"error": "Location not found",
|
|
292
|
+
"code": "NOT_FOUND",
|
|
293
|
+
"suggestion": "Did you mean: ad-tools, ad-brand?"
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Search Algorithm
|
|
300
|
+
|
|
301
|
+
### Fields Searched
|
|
302
|
+
|
|
303
|
+
All fields concatenated for matching:
|
|
304
|
+
- key, path, brand (+ aliases), client (+ aliases), type, tags, description
|
|
305
|
+
|
|
306
|
+
### Scoring
|
|
307
|
+
|
|
308
|
+
| Match Type | Points |
|
|
309
|
+
|------------|--------|
|
|
310
|
+
| Exact key match | 100 |
|
|
311
|
+
| Key contains term | 50 |
|
|
312
|
+
| Brand/client alias match | 40 |
|
|
313
|
+
| Tag match | 30 |
|
|
314
|
+
| Type match | 20 |
|
|
315
|
+
| Description contains | 10 |
|
|
316
|
+
| Path contains | 5 |
|
|
317
|
+
|
|
318
|
+
Multiple search terms: sum scores for each matching term.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Exit Codes
|
|
323
|
+
|
|
324
|
+
| Code | Meaning |
|
|
325
|
+
|------|---------|
|
|
326
|
+
| 0 | Success |
|
|
327
|
+
| 1 | Not found |
|
|
328
|
+
| 2 | Invalid input |
|
|
329
|
+
| 3 | Config error |
|
|
330
|
+
| 4 | Path not found |
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Defensive Coding Requirements
|
|
335
|
+
|
|
336
|
+
### Input Validation
|
|
337
|
+
|
|
338
|
+
- **Keys**: alphanumeric + hyphens only, lowercase
|
|
339
|
+
- **Paths**: must start with `~` or `/`, no shell metacharacters
|
|
340
|
+
- **Tags**: lowercase, alphanumeric + hyphens
|
|
341
|
+
- **All strings**: strip whitespace, reasonable length limits
|
|
342
|
+
|
|
343
|
+
### Error Handling
|
|
344
|
+
|
|
345
|
+
- Never crash on bad input
|
|
346
|
+
- Always return structured response (success/error)
|
|
347
|
+
- Include suggestions on NOT_FOUND (fuzzy match alternatives)
|
|
348
|
+
|
|
349
|
+
### Config Safety
|
|
350
|
+
|
|
351
|
+
- Backup before writes (timestamped backup files)
|
|
352
|
+
- Atomic writes (temp file then rename)
|
|
353
|
+
- Handle missing/corrupt config (create defaults)
|
|
354
|
+
- Validate JSON structure on load
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Claude Skill Specification
|
|
359
|
+
|
|
360
|
+
**File**: `~/.claude/skills/jump-locations.md`
|
|
361
|
+
|
|
362
|
+
```markdown
|
|
363
|
+
# Jump - Location Finder
|
|
364
|
+
|
|
365
|
+
Find and manage development folder locations. Use when user asks
|
|
366
|
+
"where is", "find folder", "path to", "jump to", or needs to
|
|
367
|
+
locate project/brand/client directories.
|
|
368
|
+
|
|
369
|
+
## Prerequisites
|
|
370
|
+
|
|
371
|
+
Tool must be installed at ~/dev/ad/appydave-tools
|
|
372
|
+
|
|
373
|
+
## Commands
|
|
374
|
+
|
|
375
|
+
### Search for locations
|
|
376
|
+
jump search <terms> --format json
|
|
377
|
+
|
|
378
|
+
Returns ranked matches. Terms are fuzzy matched against all fields.
|
|
379
|
+
|
|
380
|
+
### Get specific location
|
|
381
|
+
jump get <key> --format json
|
|
382
|
+
|
|
383
|
+
Returns single location by exact key.
|
|
384
|
+
|
|
385
|
+
### List all locations
|
|
386
|
+
jump list --format json
|
|
387
|
+
|
|
388
|
+
### Add new location
|
|
389
|
+
jump add --key <key> --path <path> [options] --format json
|
|
390
|
+
|
|
391
|
+
Options:
|
|
392
|
+
--jump <alias> Jump alias (default: j + key)
|
|
393
|
+
--brand <brand> Associated brand
|
|
394
|
+
--client <client> Associated client
|
|
395
|
+
--type <type> Location type
|
|
396
|
+
--tags <t1,t2,t3> Comma-separated tags
|
|
397
|
+
--description <desc> Human description
|
|
398
|
+
|
|
399
|
+
### Remove location
|
|
400
|
+
jump remove <key> --force --format json
|
|
401
|
+
|
|
402
|
+
### Validate locations
|
|
403
|
+
jump validate --format json
|
|
404
|
+
|
|
405
|
+
### Reports
|
|
406
|
+
jump report brands --format json
|
|
407
|
+
jump report clients --format json
|
|
408
|
+
jump report types --format json
|
|
409
|
+
jump report tags --format json
|
|
410
|
+
jump report by-brand [brand] --format json
|
|
411
|
+
jump report summary --format json
|
|
412
|
+
|
|
413
|
+
## Natural Language Mappings
|
|
414
|
+
|
|
415
|
+
| User Says | Command |
|
|
416
|
+
|-----------|---------|
|
|
417
|
+
| "Where are the appydave tools?" | jump search appydave tools --format json |
|
|
418
|
+
| "Show me all client folders" | jump report by-client --format json |
|
|
419
|
+
| "What brands do I have?" | jump report brands --format json |
|
|
420
|
+
| "Add a location for xyz at ~/dev/xyz" | jump add --key xyz --path ~/dev/xyz --format json |
|
|
421
|
+
| "Is the ss folder still valid?" | jump validate ss --format json |
|
|
422
|
+
| "What ruby projects do I have?" | jump search ruby --format json |
|
|
423
|
+
| "Show me supportsignal locations" | jump search supportsignal --format json |
|
|
424
|
+
| "Remove old-project" | jump remove old-project --force --format json |
|
|
425
|
+
| "What types of locations exist?" | jump report types --format json |
|
|
426
|
+
| "Show all locations grouped by brand" | jump report by-brand --format json |
|
|
427
|
+
|
|
428
|
+
## Response Handling
|
|
429
|
+
|
|
430
|
+
All JSON responses have:
|
|
431
|
+
- success: boolean
|
|
432
|
+
- For lists: count + results array
|
|
433
|
+
- For single: result object
|
|
434
|
+
- On error: error message + code + suggestion
|
|
435
|
+
|
|
436
|
+
## Error Recovery
|
|
437
|
+
|
|
438
|
+
1. NOT_FOUND: Check suggestion field for alternatives
|
|
439
|
+
2. INVALID_INPUT: Report validation issue to user
|
|
440
|
+
3. PATH_NOT_FOUND: Ask user to verify path exists
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Testing Architecture
|
|
446
|
+
|
|
447
|
+
### The Problem
|
|
448
|
+
|
|
449
|
+
The `locations.json` config contains real filesystem paths (e.g., `~/dev/ad/appydave-tools`) that:
|
|
450
|
+
- Exist on David's development machine
|
|
451
|
+
- Won't exist in CI environment
|
|
452
|
+
- Are unique to each developer's system
|
|
453
|
+
|
|
454
|
+
We need to test search, validation, reports, and generation without depending on real filesystem paths.
|
|
455
|
+
|
|
456
|
+
### Solution: Dependency Injection
|
|
457
|
+
|
|
458
|
+
Use dependency injection for filesystem operations, following the existing codebase pattern where `spec_helper.rb` uses `Dir.mktmpdir` for config paths.
|
|
459
|
+
|
|
460
|
+
#### Config Path Injection (Existing Pattern)
|
|
461
|
+
|
|
462
|
+
```ruby
|
|
463
|
+
# spec_helper.rb - already established pattern
|
|
464
|
+
Appydave::Tools::Configuration::Config.set_default do |config|
|
|
465
|
+
config.config_path = Dir.mktmpdir
|
|
466
|
+
end
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
The Jump tool's config (`~/.config/appydave/locations.json`) follows this same pattern - config path is injectable.
|
|
470
|
+
|
|
471
|
+
#### Path Validator Injection (New for Jump)
|
|
472
|
+
|
|
473
|
+
Inject a `PathValidator` dependency that can be swapped for testing:
|
|
474
|
+
|
|
475
|
+
**Production implementation**:
|
|
476
|
+
```ruby
|
|
477
|
+
# lib/appydave/tools/jump/path_validator.rb
|
|
478
|
+
module Appydave
|
|
479
|
+
module Tools
|
|
480
|
+
module Jump
|
|
481
|
+
class PathValidator
|
|
482
|
+
def exists?(path)
|
|
483
|
+
File.directory?(File.expand_path(path))
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def expand(path)
|
|
487
|
+
File.expand_path(path)
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Test implementation**:
|
|
496
|
+
```ruby
|
|
497
|
+
# spec/support/jump_test_helpers.rb
|
|
498
|
+
class TestPathValidator
|
|
499
|
+
def initialize(valid_paths: [])
|
|
500
|
+
@valid_paths = valid_paths.map { |p| File.expand_path(p) }
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def exists?(path)
|
|
504
|
+
expanded = File.expand_path(path)
|
|
505
|
+
@valid_paths.include?(expanded)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def expand(path)
|
|
509
|
+
File.expand_path(path)
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### Usage in Classes
|
|
515
|
+
|
|
516
|
+
Classes that need filesystem access accept validator as dependency:
|
|
517
|
+
|
|
518
|
+
```ruby
|
|
519
|
+
# lib/appydave/tools/jump/commands/validate.rb
|
|
520
|
+
module Appydave
|
|
521
|
+
module Tools
|
|
522
|
+
module Jump
|
|
523
|
+
module Commands
|
|
524
|
+
class Validate
|
|
525
|
+
def initialize(config, path_validator: PathValidator.new)
|
|
526
|
+
@config = config
|
|
527
|
+
@path_validator = path_validator
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def run
|
|
531
|
+
@config.locations.map do |location|
|
|
532
|
+
{
|
|
533
|
+
key: location.key,
|
|
534
|
+
path: location.path,
|
|
535
|
+
valid: @path_validator.exists?(location.path)
|
|
536
|
+
}
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
#### Test Example
|
|
547
|
+
|
|
548
|
+
```ruby
|
|
549
|
+
# spec/appydave/tools/jump/commands/validate_spec.rb
|
|
550
|
+
RSpec.describe Appydave::Tools::Jump::Commands::Validate do
|
|
551
|
+
let(:config) { build_test_config(locations: test_locations) }
|
|
552
|
+
let(:path_validator) { TestPathValidator.new(valid_paths: ['~/real-path']) }
|
|
553
|
+
|
|
554
|
+
subject { described_class.new(config, path_validator: path_validator) }
|
|
555
|
+
|
|
556
|
+
let(:test_locations) do
|
|
557
|
+
[
|
|
558
|
+
{ key: 'valid-loc', path: '~/real-path' },
|
|
559
|
+
{ key: 'invalid-loc', path: '~/does-not-exist' }
|
|
560
|
+
]
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
it 'identifies valid and invalid paths' do
|
|
564
|
+
results = subject.run
|
|
565
|
+
|
|
566
|
+
expect(results.find { |r| r[:key] == 'valid-loc' }[:valid]).to be true
|
|
567
|
+
expect(results.find { |r| r[:key] == 'invalid-loc' }[:valid]).to be false
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### What Gets Injected
|
|
573
|
+
|
|
574
|
+
| Dependency | Production | Test |
|
|
575
|
+
|------------|------------|------|
|
|
576
|
+
| Config path | `~/.config/appydave/` | `Dir.mktmpdir` |
|
|
577
|
+
| Path validator | `PathValidator` (real filesystem) | `TestPathValidator` (mock valid paths) |
|
|
578
|
+
| Output stream | `$stdout` | `StringIO` (capture output) |
|
|
579
|
+
|
|
580
|
+
### What Doesn't Need Injection
|
|
581
|
+
|
|
582
|
+
These can be tested directly without mocking:
|
|
583
|
+
- **Search algorithm** - operates on in-memory data
|
|
584
|
+
- **Scoring logic** - pure functions
|
|
585
|
+
- **JSON formatting** - string transformation
|
|
586
|
+
- **Config parsing** - uses test fixture files
|
|
587
|
+
- **Report aggregation** - operates on config data
|
|
588
|
+
|
|
589
|
+
### Test Fixture Strategy
|
|
590
|
+
|
|
591
|
+
```
|
|
592
|
+
spec/fixtures/jump/
|
|
593
|
+
├── locations_basic.json # Simple config for unit tests
|
|
594
|
+
├── locations_full.json # Complete config with all fields
|
|
595
|
+
├── locations_empty.json # Edge case: no locations
|
|
596
|
+
├── locations_invalid.json # Malformed JSON for error handling
|
|
597
|
+
└── examples.yml # Data-driven test cases
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### CI Compatibility
|
|
601
|
+
|
|
602
|
+
With this approach:
|
|
603
|
+
- No real filesystem dependencies in tests
|
|
604
|
+
- All paths validated against mock validator
|
|
605
|
+
- Config loaded from temp directories with fixture data
|
|
606
|
+
- Tests run identically on dev machines and CI
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Test Strategy
|
|
611
|
+
|
|
612
|
+
### Data-Driven Testing
|
|
613
|
+
|
|
614
|
+
Tests use YAML fixtures for easy maintenance. See `spec/fixtures/jump/examples.yml`.
|
|
615
|
+
|
|
616
|
+
**Test categories**:
|
|
617
|
+
- Search examples (by brand, alias, tag, multiple terms, no matches)
|
|
618
|
+
- Get examples (exact key, non-existent, suggestions)
|
|
619
|
+
- Add examples (minimal, all fields, invalid input, duplicate)
|
|
620
|
+
- Validate examples (existing paths, missing paths, mixed)
|
|
621
|
+
- Report examples (brands, clients, tags, by-brand filtered)
|
|
622
|
+
- Generate examples (alias lines, help lines, default jump)
|
|
623
|
+
- Edge cases (empty query, special characters, paths with spaces, unicode)
|
|
624
|
+
|
|
625
|
+
### Test Runner Pattern
|
|
626
|
+
|
|
627
|
+
```ruby
|
|
628
|
+
# spec/appydave/tools/jump/search_spec.rb
|
|
629
|
+
RSpec.describe Appydave::Tools::Jump::Search do
|
|
630
|
+
examples = YAML.load_file('spec/fixtures/jump_examples.yml')
|
|
631
|
+
|
|
632
|
+
examples['search_examples'].each do |example|
|
|
633
|
+
it example['name'] do
|
|
634
|
+
config = build_test_config(example['given'])
|
|
635
|
+
searcher = described_class.new(config)
|
|
636
|
+
result = searcher.search(example['given']['query'])
|
|
637
|
+
|
|
638
|
+
expect(result[:success]).to eq(example['expect']['success'])
|
|
639
|
+
# ... additional assertions
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Seed Data Task
|
|
648
|
+
|
|
649
|
+
Before the tool is useful, `locations.json` needs initial data.
|
|
650
|
+
|
|
651
|
+
### Source
|
|
652
|
+
|
|
653
|
+
Current `~/.oh-my-zsh/custom/aliases-jump.zsh`
|
|
654
|
+
|
|
655
|
+
### Process
|
|
656
|
+
|
|
657
|
+
1. Parse existing alias lines: `alias jfoo="cd ~/path"`
|
|
658
|
+
2. Extract key (remove `j` prefix), path, jump alias
|
|
659
|
+
3. Infer brand from path (`/ad/` → appydave, `/clients/supportsignal` → supportsignal)
|
|
660
|
+
4. Infer type from path patterns
|
|
661
|
+
5. Validate all paths exist
|
|
662
|
+
6. Generate initial `locations.json`
|
|
663
|
+
|
|
664
|
+
### Implementation
|
|
665
|
+
|
|
666
|
+
Could be:
|
|
667
|
+
- One-time migration script
|
|
668
|
+
- `jump import <file>` command (preferred - reusable)
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## Phase 2 (Future, Not MVP)
|
|
673
|
+
|
|
674
|
+
Design should accommodate but NOT implement:
|
|
675
|
+
|
|
676
|
+
- **DAM integration**: Dynamic video project folders
|
|
677
|
+
- **FliHub integration**: Active/pinned project status
|
|
678
|
+
- **Watch mode**: Auto-regenerate on config change
|
|
679
|
+
- **Computed filters**: `--active` flag from external sources
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
## Acceptance Criteria
|
|
684
|
+
|
|
685
|
+
### Core Functionality
|
|
686
|
+
|
|
687
|
+
- [ ] `jump search <terms>` returns fuzzy-matched results with scores
|
|
688
|
+
- [ ] `jump get <key>` returns single location or error with suggestion
|
|
689
|
+
- [ ] `jump list` shows all locations
|
|
690
|
+
- [ ] `jump add` creates new location with validation
|
|
691
|
+
- [ ] `jump update` modifies existing location
|
|
692
|
+
- [ ] `jump remove` deletes location (with `--force` for no prompt)
|
|
693
|
+
- [ ] `jump validate` checks all paths exist, reports results
|
|
694
|
+
|
|
695
|
+
### Reports
|
|
696
|
+
|
|
697
|
+
- [ ] `jump report brands/clients/types/tags` shows counts
|
|
698
|
+
- [ ] `jump report by-brand/by-client/by-type/by-tag` groups locations
|
|
699
|
+
- [ ] `jump report summary` shows overview
|
|
700
|
+
|
|
701
|
+
### Generation
|
|
702
|
+
|
|
703
|
+
- [ ] `jump generate aliases` outputs shell alias format
|
|
704
|
+
- [ ] `jump generate help` outputs fzf-friendly help format
|
|
705
|
+
- [ ] `--output <file>` writes to file instead of stdout
|
|
706
|
+
|
|
707
|
+
### Output Formats
|
|
708
|
+
|
|
709
|
+
- [ ] `--format table` shows colored, pretty output (default)
|
|
710
|
+
- [ ] `--format json` returns structured JSON
|
|
711
|
+
- [ ] `--format paths` returns one path per line
|
|
712
|
+
|
|
713
|
+
### Error Handling
|
|
714
|
+
|
|
715
|
+
- [ ] Invalid input returns structured error with code
|
|
716
|
+
- [ ] NOT_FOUND includes fuzzy suggestions
|
|
717
|
+
- [ ] Config errors handled gracefully (create defaults if missing)
|
|
718
|
+
- [ ] Exit codes match specification
|
|
719
|
+
|
|
720
|
+
### Claude Skill
|
|
721
|
+
|
|
722
|
+
- [ ] Skill file generated and installable
|
|
723
|
+
- [ ] All commands work with `--format json`
|
|
724
|
+
- [ ] Error responses include recovery suggestions
|
|
725
|
+
|
|
726
|
+
---
|
|
727
|
+
|
|
728
|
+
## Deliverables Checklist
|
|
729
|
+
|
|
730
|
+
| Deliverable | Description |
|
|
731
|
+
|-------------|-------------|
|
|
732
|
+
| Data model | `locations.json` with meta, categories, brands, clients, locations |
|
|
733
|
+
| CLI commands | search, get, list, add, update, remove, validate, generate, report, info |
|
|
734
|
+
| Output formats | table (colored), json, paths |
|
|
735
|
+
| Search algorithm | Fuzzy match with weighted scoring |
|
|
736
|
+
| Reports | By brand, client, type, tag, summary |
|
|
737
|
+
| Generation | aliases-jump.zsh, help content (stdout default) |
|
|
738
|
+
| Claude skill | Skill file with command mappings and examples |
|
|
739
|
+
| Test fixtures | YAML-driven test cases |
|
|
740
|
+
| Seed data | Import from existing aliases-jump.zsh |
|
|
741
|
+
| Defensive coding | Input validation, error handling, config safety |
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## Implementation Notes
|
|
746
|
+
|
|
747
|
+
### CLI Pattern
|
|
748
|
+
|
|
749
|
+
This is a **Pattern 4: Method Dispatch (Full)** tool per the CLI patterns guide:
|
|
750
|
+
- 10+ commands
|
|
751
|
+
- Hierarchical help system (`jump help search`, `jump help report`)
|
|
752
|
+
- Complex argument parsing per command
|
|
753
|
+
|
|
754
|
+
### Module Structure
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
lib/appydave/tools/jump/
|
|
758
|
+
├── cli.rb # Command routing and help
|
|
759
|
+
├── config.rb # Load/save locations.json
|
|
760
|
+
├── search.rb # Search algorithm
|
|
761
|
+
├── location.rb # Location model/validation
|
|
762
|
+
├── commands/
|
|
763
|
+
│ ├── add.rb
|
|
764
|
+
│ ├── update.rb
|
|
765
|
+
│ ├── remove.rb
|
|
766
|
+
│ ├── validate.rb
|
|
767
|
+
│ ├── generate.rb
|
|
768
|
+
│ └── report.rb
|
|
769
|
+
├── formatters/
|
|
770
|
+
│ ├── table.rb # Colored table output
|
|
771
|
+
│ ├── json.rb
|
|
772
|
+
│ └── paths.rb
|
|
773
|
+
└── importers/
|
|
774
|
+
└── alias_file.rb # Import from aliases-jump.zsh
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
**Last updated**: 2025-12-13
|