appydave-tools 0.83.0 → 0.85.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/CHANGELOG.md +19 -0
- data/CLAUDE.md +1 -1
- data/CONTEXT.md +133 -23
- data/bin/configuration.rb +20 -0
- data/bin/query_apps.rb +74 -0
- data/config/examples/locations.example.json +48 -0
- data/config/examples/settings.example.json +9 -0
- data/config/random-queries.yml +24 -45
- data/context.globs.json +24 -0
- data/docs/planning/multi-user-support.md +108 -0
- data/docs/planning/query-apps-design.md +344 -0
- data/docs/planning/query-location-feature/IMPLEMENTATION_PLAN.md +142 -0
- data/docs/planning/query-location-feature/system-context-gap-analysis.md +107 -0
- data/docs/planning/query-skills-plan.md +354 -0
- data/docs/specs/jump-add-display-fix.md +249 -0
- data/exe/query_apps +7 -0
- data/lib/appydave/tools/app_context/app_finder.rb +176 -0
- data/lib/appydave/tools/app_context/globs_loader.rb +116 -0
- data/lib/appydave/tools/app_context/options.rb +28 -0
- data/lib/appydave/tools/brain_context/options.rb +19 -2
- data/lib/appydave/tools/configuration/example_installer.rb +72 -0
- data/lib/appydave/tools/configuration/models/settings_config.rb +12 -0
- data/lib/appydave/tools/jump/cli.rb +78 -0
- data/lib/appydave/tools/jump/commands/query.rb +112 -0
- data/lib/appydave/tools/jump/formatters/table_formatter.rb +16 -5
- data/lib/appydave/tools/version.rb +1 -1
- data/lib/appydave/tools/zsh_history/config.rb +2 -2
- data/lib/appydave/tools/zsh_history/filter.rb +10 -4
- data/lib/appydave/tools.rb +6 -0
- data/package.json +1 -1
- metadata +19 -2
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# Query Ecosystem — Skill Wrappers Plan
|
|
2
|
+
|
|
3
|
+
**Purpose**: Plan for creating thin skill wrappers around the four CLI tools in the query/gather ecosystem. Skills are shims — they call the Ruby CLI tools, not reimplementations.
|
|
4
|
+
|
|
5
|
+
**Status**: Planning
|
|
6
|
+
**Created**: 2026-04-05
|
|
7
|
+
**Depends on**: `query_apps` CLI tool (Phase 2 — complete, 76 tests passing)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The Ecosystem
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
QUERY TOOLS (find files) GATHER TOOL (assemble content)
|
|
15
|
+
───────────────────────── ────────────────────────────
|
|
16
|
+
query_brain → brain files ─┐
|
|
17
|
+
query_omi → OMI transcripts ├──→ llm_context → LLM payload
|
|
18
|
+
query_apps → app files ─┘
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each query tool outputs file paths (one per line). `llm_context` consumes those paths via `--stdin` and assembles content. The skills wrap each CLI tool to make them discoverable by Claude Code.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Current State
|
|
26
|
+
|
|
27
|
+
| CLI Tool | Skill Exists? | Location | Notes |
|
|
28
|
+
|----------|--------------|----------|-------|
|
|
29
|
+
| `query_omi` | **Yes** — `omi-query` | `appydave-plugins/appydave/skills/omi-query/` | Gold standard pattern |
|
|
30
|
+
| `query_brain` | **No** | — | `focus` skill has `resolve_brain.py` doing subset of same work |
|
|
31
|
+
| `query_apps` | **No** | — | CLI tool just completed |
|
|
32
|
+
| `llm_context` | **No** | — | Downstream assembler, different trigger pattern |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Skill 1: `brain-query`
|
|
37
|
+
|
|
38
|
+
**Plugin**: `appydave-plugins/appydave/skills/brain-query/SKILL.md`
|
|
39
|
+
|
|
40
|
+
**Triggers**: "search brains for X", "find brain about X", "which brains cover X", "brain files for X", "what's in the X brain", "active brains", "brains tagged X", "brains in category X"
|
|
41
|
+
|
|
42
|
+
**Relationship to `focus`**: `focus` is an orientation skill — it reads and summarises a brain's INDEX.md. `brain-query` is a file discovery skill — it returns file paths for piping into `llm_context`. Different intents, complementary tools.
|
|
43
|
+
|
|
44
|
+
**Relationship to `resolve_brain.py`**: The Python script in the `focus` skill does brain name resolution (exact → fuzzy → ambiguous). `query_brain` in Ruby does the same thing plus tag lookup, category search, alias resolution, and file path output. Long term, `focus` should call `query_brain --find --meta` instead of `resolve_brain.py`. But that's a separate refactor — don't block this skill on it.
|
|
45
|
+
|
|
46
|
+
### CLI Interface (what the skill wraps)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Find by name/alias/tag (4-tier resolution: exact → alias → substring → tag)
|
|
50
|
+
query_brain --find anthropic-claude
|
|
51
|
+
query_brain --find claude # substring match
|
|
52
|
+
query_brain --find agent-systems # tag match
|
|
53
|
+
|
|
54
|
+
# Find by category
|
|
55
|
+
query_brain --category claude-core
|
|
56
|
+
query_brain --category agent-frameworks
|
|
57
|
+
|
|
58
|
+
# Active (high-activity) brains
|
|
59
|
+
query_brain --active
|
|
60
|
+
|
|
61
|
+
# Metadata mode — JSON with name, category, activity_level, status, tags, file_count
|
|
62
|
+
query_brain --find claude --meta
|
|
63
|
+
query_brain --active --meta
|
|
64
|
+
|
|
65
|
+
# Exclude INDEX.md from results
|
|
66
|
+
query_brain --find paperclip --files-only
|
|
67
|
+
|
|
68
|
+
# Combine with llm_context
|
|
69
|
+
query_brain --find paperclip | llm_context --stdin -f content --smart
|
|
70
|
+
query_brain --find paperclip --files-only | llm_context --stdin -f tree
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Skill Structure
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
brain-query/
|
|
77
|
+
SKILL.md # Thin wrapper — triggers, CLI commands, common workflows
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
No scripts, no Python, no reference files. Just the SKILL.md documenting how to call `query_brain`.
|
|
81
|
+
|
|
82
|
+
### Skill Content Outline
|
|
83
|
+
|
|
84
|
+
1. **Description/triggers** in YAML frontmatter
|
|
85
|
+
2. **CLI Tool** section — show all flags with examples
|
|
86
|
+
3. **Two Output Modes** — file paths (default) vs `--meta` (JSON)
|
|
87
|
+
4. **Common Workflows**:
|
|
88
|
+
- "What brains cover topic X?" → `query_brain --find X --meta`
|
|
89
|
+
- "Load all files from brain X" → `query_brain --find X | llm_context --stdin -f content --smart`
|
|
90
|
+
- "Which brains are active?" → `query_brain --active --meta`
|
|
91
|
+
- "All brains in a category" → `query_brain --category claude-core --meta`
|
|
92
|
+
- "Get brain docs without INDEX.md" → `query_brain --find X --files-only`
|
|
93
|
+
5. **Prerequisites** — `query_brain` installed (part of `appydave-tools`), `brains-index.json` must exist (built by brain-librarian's `build_brain_index.py`)
|
|
94
|
+
6. **Related Skills** — `focus` (orientation), `brain-librarian` (curation), `brain-bridge` (write to brain)
|
|
95
|
+
|
|
96
|
+
### Key Design Notes
|
|
97
|
+
|
|
98
|
+
- The skill should NOT reimplement resolution logic — just document the CLI flags
|
|
99
|
+
- `query_brain` already reads `brains-index.json` which is built by `build_brain_index.py` in the brain-librarian skill. The skill should mention this prerequisite
|
|
100
|
+
- The `--find` flag does 4-tier resolution internally (exact key → alias → substring → tag) — the skill doesn't need to know the tiers, just that `--find` handles fuzzy matching
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Skill 2: `app-query`
|
|
105
|
+
|
|
106
|
+
**Plugin**: `appydave-plugins/appydave/skills/app-query/SKILL.md`
|
|
107
|
+
|
|
108
|
+
**Triggers**: "get files from X app", "show me X's backend", "load X docs", "what apps have context", "list globs for X", "app files for X", "codebase of X", "understand X app", "X services", "X components", "X frontend", "X api"
|
|
109
|
+
|
|
110
|
+
### CLI Interface (what the skill wraps)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Basic: app name + glob category
|
|
114
|
+
query_apps flihub --glob docs
|
|
115
|
+
query_apps angeleye --glob services
|
|
116
|
+
|
|
117
|
+
# Aliases resolve to multiple categories
|
|
118
|
+
query_apps flihub --glob backend # → services + routes
|
|
119
|
+
query_apps flihub --glob frontend # → components + views + styles
|
|
120
|
+
|
|
121
|
+
# Composites — pre-built bundles
|
|
122
|
+
query_apps flihub --glob understand # → context + docs + types + config
|
|
123
|
+
query_apps flihub --glob codebase # → services + routes + components + views
|
|
124
|
+
|
|
125
|
+
# Multiple globs (comma-separated)
|
|
126
|
+
query_apps flihub --glob docs,types,config
|
|
127
|
+
|
|
128
|
+
# Cross-app by pattern type
|
|
129
|
+
query_apps --pattern rvets --glob backend
|
|
130
|
+
query_apps --pattern nextjs --glob schema
|
|
131
|
+
|
|
132
|
+
# Discovery
|
|
133
|
+
query_apps flihub --list # available glob names for this app
|
|
134
|
+
query_apps --list-apps # all apps with context.globs.json
|
|
135
|
+
|
|
136
|
+
# Metadata mode
|
|
137
|
+
query_apps flihub --glob backend --meta
|
|
138
|
+
|
|
139
|
+
# Pipe to llm_context
|
|
140
|
+
query_apps flihub --glob understand | llm_context --stdin -f content --smart
|
|
141
|
+
query_apps angeleye --glob services | llm_context --stdin -f tree
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Skill Content Outline
|
|
145
|
+
|
|
146
|
+
1. **Description/triggers** in YAML frontmatter
|
|
147
|
+
2. **CLI Tool** section — all flags with examples
|
|
148
|
+
3. **Glob Resolution** — direct name → alias → composite → substring fallback
|
|
149
|
+
4. **Standard Vocabulary** — table of common category names and what they typically contain
|
|
150
|
+
5. **Common Workflows**:
|
|
151
|
+
- "Help me understand app X" → `query_apps X --glob understand | llm_context --stdin -f content --smart`
|
|
152
|
+
- "What's the API look like?" → `query_apps X --glob api`
|
|
153
|
+
- "Show me all React code" → `query_apps X --glob react`
|
|
154
|
+
- "What globs are available?" → `query_apps X --list`
|
|
155
|
+
- "Which apps are queryable?" → `query_apps --list-apps`
|
|
156
|
+
- "All RVETS backend code" → `query_apps --pattern rvets --glob backend`
|
|
157
|
+
6. **Prerequisites** — `query_apps` installed (appydave-tools), `context.globs.json` must exist in project root (generated by `system-context` skill)
|
|
158
|
+
7. **Related Skills** — `system-context` (generates context.globs.json), `llm-context` (downstream assembler)
|
|
159
|
+
|
|
160
|
+
### Key Design Notes
|
|
161
|
+
|
|
162
|
+
- The skill should explain the alias/composite concept so the agent knows "backend" is valid even though the globs file has "services" and "routes"
|
|
163
|
+
- Include the standard vocabulary table so agents can try common names without running `--list` first
|
|
164
|
+
- `context.globs.json` is generated by `system-context` — if it doesn't exist for an app, the skill should suggest running `/system-context` there first
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Skill 3: `llm-context`
|
|
169
|
+
|
|
170
|
+
**Plugin**: `appydave-plugins/appydave/skills/llm-context/SKILL.md`
|
|
171
|
+
|
|
172
|
+
**Triggers**: "gather files for context", "build llm context", "assemble context from files", "package files for llm", "get context from these files", "collect codebase", "gather code"
|
|
173
|
+
|
|
174
|
+
**Important distinction**: This is NOT a query tool. It's the downstream assembler. It takes file paths (from stdin or glob patterns) and produces formatted content for LLM consumption. The trigger pattern is different — it activates when the user wants to package/assemble, not when they want to search/find.
|
|
175
|
+
|
|
176
|
+
### CLI Interface (what the skill wraps)
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Direct glob patterns
|
|
180
|
+
llm_context -i 'lib/**/*.rb' -f content
|
|
181
|
+
llm_context -i 'src/**/*.ts' -e 'node_modules/**/*' -f tree,content
|
|
182
|
+
|
|
183
|
+
# Stdin mode — receive file paths from query tools
|
|
184
|
+
query_brain --find paperclip | llm_context --stdin -f content --smart
|
|
185
|
+
query_apps flihub --glob backend | llm_context --stdin -f content --smart
|
|
186
|
+
query_omi --brain til --days 7 | llm_context --stdin -f content --smart
|
|
187
|
+
|
|
188
|
+
# Output formats
|
|
189
|
+
llm_context -i 'lib/**/*.rb' -f tree # directory tree only
|
|
190
|
+
llm_context -i 'lib/**/*.rb' -f content # file contents with headers
|
|
191
|
+
llm_context -i 'lib/**/*.rb' -f json # structured JSON
|
|
192
|
+
llm_context -i 'lib/**/*.rb' -f files # file paths only
|
|
193
|
+
llm_context -i 'lib/**/*.rb' -f tree,content # multiple formats
|
|
194
|
+
llm_context -i 'lib/**/*.rb' -f aider -p 'Add logging' # aider command
|
|
195
|
+
|
|
196
|
+
# Output targets
|
|
197
|
+
llm_context -i 'lib/**/*.rb' -o clipboard # copy to clipboard (default)
|
|
198
|
+
llm_context -i 'lib/**/*.rb' -o temp # write to temp file, path on clipboard
|
|
199
|
+
llm_context -i 'lib/**/*.rb' -o context.txt # write to specific file
|
|
200
|
+
llm_context -i 'lib/**/*.rb' -o stdout # print to stdout
|
|
201
|
+
llm_context -i 'lib/**/*.rb' --smart # auto-route: clipboard if ≤100k tokens, else temp
|
|
202
|
+
|
|
203
|
+
# Other options
|
|
204
|
+
llm_context -i 'lib/**/*.rb' -l 50 # limit to first 50 lines per file
|
|
205
|
+
llm_context -i 'lib/**/*.rb' -t # show token estimate
|
|
206
|
+
llm_context -i 'lib/**/*.rb' -b /some/other/dir # set base directory
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Skill Content Outline
|
|
210
|
+
|
|
211
|
+
1. **Description/triggers** in YAML frontmatter
|
|
212
|
+
2. **Position in the chain** — this is the ASSEMBLER, not a query tool. Diagram showing query tools → llm_context → LLM
|
|
213
|
+
3. **Two Input Modes**:
|
|
214
|
+
- **Pattern mode**: `-i` and `-e` glob patterns, run from any directory
|
|
215
|
+
- **Stdin mode**: `--stdin` receives file paths piped from query tools
|
|
216
|
+
4. **Output Formats** — table explaining tree, content, json, files, aider
|
|
217
|
+
5. **Output Targets** — clipboard, temp, file, stdout, `--smart` auto-routing
|
|
218
|
+
6. **Common Workflows**:
|
|
219
|
+
- "Package this project's Ruby code" → `llm_context -i 'lib/**/*.rb' --smart`
|
|
220
|
+
- "Get a tree view of the project" → `llm_context -i '**/*' -e 'node_modules/**/*' -f tree`
|
|
221
|
+
- "Load brain files into context" → `query_brain --find X | llm_context --stdin -f content --smart`
|
|
222
|
+
- "Load app backend for review" → `query_apps X --glob backend | llm_context --stdin -f content --smart`
|
|
223
|
+
- "How big is this context?" → `llm_context -i 'src/**/*.ts' -t` (shows token estimate)
|
|
224
|
+
- "Save context to a file" → `llm_context -i 'lib/**/*.rb' -f tree,content -o context.txt`
|
|
225
|
+
7. **The `--smart` flag** — auto-routes based on token count: clipboard if ≤100k tokens, temp file (path copied to clipboard) if larger. Mutually exclusive with explicit `-o clipboard` or `-o temp`
|
|
226
|
+
8. **Prerequisites** — `llm_context` installed (part of `appydave-tools`)
|
|
227
|
+
9. **Related Skills** — `brain-query`, `app-query`, `omi-query` (upstream query tools)
|
|
228
|
+
|
|
229
|
+
### Key Design Notes
|
|
230
|
+
|
|
231
|
+
- The skill description must NOT overlap with query tool triggers. "Get files from FliHub" → `app-query`. "Package these files for an LLM" → `llm-context`
|
|
232
|
+
- The `--smart` flag is the recommended default for most workflows — mention it prominently
|
|
233
|
+
- The skill should show the full pipeline pattern: `query_X ... | llm_context --stdin -f content --smart`
|
|
234
|
+
- Token estimation (`-t`) is useful for checking context window fit before sending to an LLM
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Skill 4: `system-context` Update (not a new skill)
|
|
239
|
+
|
|
240
|
+
The existing `system-context` skill in `appydave-plugins/appydave/skills/system-context/SKILL.md` needs an addition to generate `context.globs.json` alongside `CONTEXT.md`.
|
|
241
|
+
|
|
242
|
+
**This is NOT a new skill** — it's a modification to the existing skill.
|
|
243
|
+
|
|
244
|
+
### What to Add
|
|
245
|
+
|
|
246
|
+
After writing `CONTEXT.md`, the skill should also:
|
|
247
|
+
|
|
248
|
+
1. **Detect project pattern** from project files:
|
|
249
|
+
- `package.json` with `workspaces` containing `shared/`, `server/`, `client/` → `rvets`
|
|
250
|
+
- `package.json` with `next` dependency → `nextjs`
|
|
251
|
+
- `Gemfile` or `*.gemspec` → `ruby-gem`
|
|
252
|
+
- `pyproject.toml` or `requirements.txt` → `python`
|
|
253
|
+
- Fallback: `unknown`
|
|
254
|
+
|
|
255
|
+
2. **Scan directory structure** and map to standard vocabulary:
|
|
256
|
+
|
|
257
|
+
| Category | RVETS | Next.js | Ruby Gem | Python |
|
|
258
|
+
|----------|-------|---------|----------|--------|
|
|
259
|
+
| `docs` | `docs/**/*.md` | `docs/**/*.md` | `docs/**/*.md` | `docs/**/*.md` |
|
|
260
|
+
| `types` | `shared/**/*.ts` | `types/**/*.ts`, `lib/db/schema/**/*.ts` | — | — |
|
|
261
|
+
| `config` | `server/config.json`, `*.config.*` | `*.config.*`, `middleware.ts` | `config/**/*.json` | `*.yaml`, `*.toml` |
|
|
262
|
+
| `services` | `server/src/services/**/*.ts` | — | — | — |
|
|
263
|
+
| `routes` | `server/src/routes/**/*.ts` | `app/api/**/*.ts` | — | — |
|
|
264
|
+
| `components` | `client/src/components/**/*.tsx` | `components/**/*.tsx` | — | — |
|
|
265
|
+
| `views` | `client/src/views/**/*.tsx` | `app/**/*.tsx` (pages) | — | — |
|
|
266
|
+
| `tests` | `**/*.test.ts`, `**/*.spec.ts` | `**/*.test.*` | `spec/**/*_spec.rb` | `tests/**/*.py` |
|
|
267
|
+
| `styles` | `**/*.css` | `**/*.css` | — | — |
|
|
268
|
+
| `context` | `CLAUDE.md`, `CONTEXT.md`, `STEERING.md` | same | same | same |
|
|
269
|
+
| `lib` | — | — | `lib/**/*.rb` | `src/**/*.py` |
|
|
270
|
+
| `bin` | — | — | `bin/*` | — |
|
|
271
|
+
| `actions` | — | `lib/actions/**/*.ts` | — | — |
|
|
272
|
+
| `validation` | — | `lib/validation/**/*.ts` | — | — |
|
|
273
|
+
| `auth` | — | `lib/auth/**/*.ts` | — | — |
|
|
274
|
+
| `mockups` | `.mochaccino/designs/**/*.html` | — | — | — |
|
|
275
|
+
| `planning` | `docs/prd/**/*.md`, `docs/planning/**/*.md` | same | same | same |
|
|
276
|
+
|
|
277
|
+
3. **Only include categories that actually match files** — don't generate a `mockups` entry if `.mochaccino/` doesn't exist
|
|
278
|
+
|
|
279
|
+
4. **Generate standard aliases and composites**:
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
"aliases": {
|
|
283
|
+
"backend": ["services", "routes"],
|
|
284
|
+
"frontend": ["components", "views", "styles"],
|
|
285
|
+
"api": ["routes", "types"],
|
|
286
|
+
"data-layer": ["types", "schema"],
|
|
287
|
+
"react": ["components", "views"],
|
|
288
|
+
"ui": ["components", "views", "styles"]
|
|
289
|
+
},
|
|
290
|
+
"composites": {
|
|
291
|
+
"understand": ["context", "docs", "types", "config"],
|
|
292
|
+
"codebase": ["services", "routes", "components", "views"],
|
|
293
|
+
"full": ["*"]
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Only include alias/composite entries where the referenced categories exist in `globs`.
|
|
298
|
+
|
|
299
|
+
5. **Write `context.globs.json`** to project root alongside `CONTEXT.md`
|
|
300
|
+
|
|
301
|
+
6. **Add to CONTEXT.md sources** — include `context.globs.json` in the frontmatter sources list
|
|
302
|
+
|
|
303
|
+
### Validation
|
|
304
|
+
|
|
305
|
+
After generating, the skill should verify:
|
|
306
|
+
- Every glob in `globs` matches at least one file on disk
|
|
307
|
+
- Every alias references only categories that exist in `globs`
|
|
308
|
+
- Every composite references only categories that exist in `globs`
|
|
309
|
+
- `pattern` field is set
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Implementation Order
|
|
314
|
+
|
|
315
|
+
| Phase | What | Where | Depends On |
|
|
316
|
+
|-------|------|-------|------------|
|
|
317
|
+
| 1 | `brain-query` skill | `appydave-plugins/appydave/skills/brain-query/` | `query_brain` CLI (exists) |
|
|
318
|
+
| 2 | `app-query` skill | `appydave-plugins/appydave/skills/app-query/` | `query_apps` CLI (exists) |
|
|
319
|
+
| 3 | `llm-context` skill | `appydave-plugins/appydave/skills/llm-context/` | `llm_context` CLI (exists) |
|
|
320
|
+
| 4 | `system-context` update | `appydave-plugins/appydave/skills/system-context/SKILL.md` | `app-query` skill (to validate output) |
|
|
321
|
+
| 5 | Generate `context.globs.json` for 12 existing apps | Run `/system-context` in each project | Phase 4 |
|
|
322
|
+
|
|
323
|
+
Phases 1-3 are independent — can be done in any order or in parallel. Phase 4 depends on the `context.globs.json` format being finalized (which it is — see `query-apps-design.md`). Phase 5 is a batch operation.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Decisions Made
|
|
328
|
+
|
|
329
|
+
| Decision | Choice | Why |
|
|
330
|
+
|----------|--------|-----|
|
|
331
|
+
| Skills are thin shims | Yes | Ruby tools have robust test suites; reimplementing in Python/Bash loses coverage |
|
|
332
|
+
| No scripts in skill folders | Correct | Skills just document CLI flags — no `scripts/` subdirectory needed |
|
|
333
|
+
| `focus` stays separate from `brain-query` | Yes | Different intent: orientation vs file discovery |
|
|
334
|
+
| `resolve_brain.py` stays for now | Yes | Refactor to use `query_brain` later; don't block skills on it |
|
|
335
|
+
| `llm-context` has different triggers than query skills | Yes | It's a downstream assembler, not a query tool |
|
|
336
|
+
| All skills go in `appydave` plugin | Yes | These are appydave-tools wrappers, not project-specific |
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Related Files
|
|
341
|
+
|
|
342
|
+
| What | Where |
|
|
343
|
+
|------|-------|
|
|
344
|
+
| `query-apps-design.md` (Phase 2 design) | `docs/planning/query-apps-design.md` |
|
|
345
|
+
| `omi-query` skill (pattern to follow) | `appydave-plugins/appydave/skills/omi-query/SKILL.md` |
|
|
346
|
+
| `query_brain` CLI | `bin/query_brain.rb` |
|
|
347
|
+
| `query_apps` CLI | `bin/query_apps.rb` |
|
|
348
|
+
| `llm_context` CLI | `bin/llm_context.rb` |
|
|
349
|
+
| BrainQuery implementation | `lib/appydave/tools/brain_context/brain_finder.rb` |
|
|
350
|
+
| AppQuery implementation | `lib/appydave/tools/app_context/app_finder.rb` |
|
|
351
|
+
| FileCollector implementation | `lib/appydave/tools/llm_context/file_collector.rb` |
|
|
352
|
+
| Locations registry | `~/.config/appydave/locations.json` |
|
|
353
|
+
| Brain index | `~/dev/ad/brains/brains-index.json` |
|
|
354
|
+
| System-context skill | `appydave-plugins/appydave/skills/system-context/SKILL.md` |
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Spec: Fix `jump add` (and `update`/`remove`) Output Display
|
|
2
|
+
|
|
3
|
+
**Status**: Ready for implementation
|
|
4
|
+
**Filed by**: Claude Code session (brains repo), 2026-04-06
|
|
5
|
+
**Root cause session**: User ran `jump add --key awb --path ... --type app`, got "No locations found." — the add succeeded but the formatter showed a misleading empty-results message.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
`jump add`, `jump update`, and `jump remove` all return a **mutation result** shape:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
{ success: true, message: "Location 'awb' added successfully", location: location.to_h }
|
|
15
|
+
{ success: true, message: "Location 'awb' removed successfully" }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`TableFormatter#format` dispatches on result shape in this order:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
def format
|
|
22
|
+
return format_error unless success?
|
|
23
|
+
return format_info if info_result?
|
|
24
|
+
return format_summary if summary_result?
|
|
25
|
+
return format_groups unless groups.empty?
|
|
26
|
+
return format_empty if results.empty? # ← mutation results land here
|
|
27
|
+
...
|
|
28
|
+
format_results
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`results` reads `data[:results] || []`. Mutation results have no `:results` key, so `results` is always `[]`, so `format_empty` fires and prints **"No locations found."**
|
|
33
|
+
|
|
34
|
+
The operation worked. The data was saved. The message was misleading noise.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Fix Required
|
|
39
|
+
|
|
40
|
+
### `lib/appydave/tools/jump/formatters/table_formatter.rb`
|
|
41
|
+
|
|
42
|
+
Add a `mutation_result?` guard **before** the `format_empty` check, and a corresponding `format_mutation` method.
|
|
43
|
+
|
|
44
|
+
#### Change 1 — dispatch order
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
def format
|
|
48
|
+
return format_error unless success?
|
|
49
|
+
return format_info if info_result?
|
|
50
|
+
return format_summary if summary_result?
|
|
51
|
+
return format_groups unless groups.empty?
|
|
52
|
+
return format_mutation if mutation_result? # ← insert here
|
|
53
|
+
return format_empty if results.empty?
|
|
54
|
+
return format_definition_report if definition_report?
|
|
55
|
+
return format_count_report if count_report?
|
|
56
|
+
return format_category_report if category_report?
|
|
57
|
+
|
|
58
|
+
format_results
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### Change 2 — new private methods
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
def mutation_result?
|
|
66
|
+
data.key?(:message) && (data.key?(:location) || data[:message].to_s.match?(/removed|updated|added/i))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def format_mutation
|
|
70
|
+
lines = [colorize(data[:message], :green)]
|
|
71
|
+
lines << colorize(data[:warning], :yellow) if data[:warning]
|
|
72
|
+
lines.join("\n")
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**No other files need changing.** `Commands::Add`, `Commands::Update`, and `Commands::Remove` already return the right shape.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Unit Tests Required
|
|
81
|
+
|
|
82
|
+
Add these to `spec/appydave/tools/jump/formatters/table_formatter_spec.rb`.
|
|
83
|
+
|
|
84
|
+
Insert as a new context block after the existing `'when formatting empty results'` context (around line 31):
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
context 'when formatting a mutation result (add/update/remove)' do
|
|
88
|
+
context 'with a successful add' do
|
|
89
|
+
let(:data) do
|
|
90
|
+
{
|
|
91
|
+
success: true,
|
|
92
|
+
message: "Location 'awb' added successfully",
|
|
93
|
+
location: {
|
|
94
|
+
key: 'awb',
|
|
95
|
+
path: '/Users/davidcruwys/dev/ad/apps/awb',
|
|
96
|
+
jump: 'jawb',
|
|
97
|
+
type: 'app'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'displays the success message' do
|
|
103
|
+
output = formatter.format
|
|
104
|
+
expect(output).to include("Location 'awb' added successfully")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'does not display "No locations found"' do
|
|
108
|
+
output = formatter.format
|
|
109
|
+
expect(output).not_to include('No locations found')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'does not display a table header' do
|
|
113
|
+
output = formatter.format
|
|
114
|
+
expect(output).not_to include('KEY')
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
context 'with a successful add and a path warning' do
|
|
119
|
+
let(:data) do
|
|
120
|
+
{
|
|
121
|
+
success: true,
|
|
122
|
+
message: "Location 'awb' added successfully",
|
|
123
|
+
warning: "Warning: Path '/Users/davidcruwys/dev/ad/apps/awb' does not exist",
|
|
124
|
+
location: {
|
|
125
|
+
key: 'awb',
|
|
126
|
+
path: '/Users/davidcruwys/dev/ad/apps/awb'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'displays the success message' do
|
|
132
|
+
output = formatter.format
|
|
133
|
+
expect(output).to include("Location 'awb' added successfully")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'displays the path warning' do
|
|
137
|
+
output = formatter.format
|
|
138
|
+
expect(output).to include('does not exist')
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context 'with a successful update' do
|
|
143
|
+
let(:data) do
|
|
144
|
+
{
|
|
145
|
+
success: true,
|
|
146
|
+
message: "Location 'awb' updated successfully",
|
|
147
|
+
location: { key: 'awb', path: '/Users/davidcruwys/dev/ad/apps/awb' }
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'displays the update success message' do
|
|
152
|
+
output = formatter.format
|
|
153
|
+
expect(output).to include("Location 'awb' updated successfully")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'does not display "No locations found"' do
|
|
157
|
+
output = formatter.format
|
|
158
|
+
expect(output).not_to include('No locations found')
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
context 'with a successful remove' do
|
|
163
|
+
let(:data) do
|
|
164
|
+
{
|
|
165
|
+
success: true,
|
|
166
|
+
message: "Location 'awb' removed successfully"
|
|
167
|
+
# remove has no :location key
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'displays the remove success message' do
|
|
172
|
+
output = formatter.format
|
|
173
|
+
expect(output).to include("Location 'awb' removed successfully")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'does not display "No locations found"' do
|
|
177
|
+
output = formatter.format
|
|
178
|
+
expect(output).not_to include('No locations found')
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## CLI Integration Test
|
|
187
|
+
|
|
188
|
+
The existing `cli_spec.rb` tests auto-regeneration but never asserts what the user actually *sees*. Add these to
|
|
189
|
+
`spec/appydave/tools/jump/cli_spec.rb` inside the existing `'when adding a location'` context (around line 43):
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
it 'shows success message after add' do
|
|
193
|
+
cli.run(['add', '--key', 'new-project', '--path', '~/dev/new-path'])
|
|
194
|
+
|
|
195
|
+
expect(output.string).to include("Location 'new-project' added successfully")
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'does not show "No locations found" after add' do
|
|
199
|
+
cli.run(['add', '--key', 'new-project', '--path', '~/dev/new-path'])
|
|
200
|
+
|
|
201
|
+
expect(output.string).not_to include('No locations found')
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'shows warning when path does not exist' do
|
|
205
|
+
no_path_validator = TestPathValidator.new(valid_paths: [])
|
|
206
|
+
bad_cli = described_class.new(config: config, path_validator: no_path_validator, output: output)
|
|
207
|
+
bad_cli.run(['add', '--key', 'ghost', '--path', '~/dev/ghost'])
|
|
208
|
+
|
|
209
|
+
expect(output.string).to include('does not exist')
|
|
210
|
+
expect(output.string).to include("Location 'ghost' added successfully")
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## After This Is Done — Skill Update
|
|
217
|
+
|
|
218
|
+
Once the fix is merged and released, the jump skill at
|
|
219
|
+
`~/.claude/skills/jump/SKILL.md` needs its **"Modifying Locations"** section rewritten.
|
|
220
|
+
|
|
221
|
+
**Current (wrong):**
|
|
222
|
+
```
|
|
223
|
+
"Add a new jump alias for my-project"
|
|
224
|
+
- Action:
|
|
225
|
+
1. Add new entry to ~/.config/appydave/locations.json
|
|
226
|
+
2. Run /Users/.../jump.rb generate aliases > ~/.oh-my-zsh/custom/aliases-jump.zsh
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Correct (after fix):**
|
|
230
|
+
```
|
|
231
|
+
"Add a new jump alias for my-project"
|
|
232
|
+
- Action:
|
|
233
|
+
jump add --key my-project --path ~/dev/my-project [--type X] [--brand X]
|
|
234
|
+
# aliases auto-regenerate if aliases-output-path is set in settings.json
|
|
235
|
+
# use --no-generate to skip
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The skill should use the tool. Direct JSON editing is a workaround, not a workflow.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Summary of Files to Touch
|
|
243
|
+
|
|
244
|
+
| File | Change |
|
|
245
|
+
|------|--------|
|
|
246
|
+
| `lib/.../formatters/table_formatter.rb` | Add `mutation_result?` + `format_mutation`, insert guard in `format` dispatch |
|
|
247
|
+
| `spec/.../formatters/table_formatter_spec.rb` | Add 4 new contexts covering add/update/remove/warning |
|
|
248
|
+
| `spec/.../jump/cli_spec.rb` | Add 3 assertions inside existing `'when adding a location'` context |
|
|
249
|
+
| `~/.claude/skills/jump/SKILL.md` | Update after gem is released (done in brains repo, not here) |
|