appydave-tools 0.84.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 +7 -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-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/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 +5 -0
- data/package.json +1 -1
- metadata +16 -2
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Appydave
|
|
6
|
+
module Tools
|
|
7
|
+
module AppContext
|
|
8
|
+
# Queries locations.json to find app files via context.globs.json patterns.
|
|
9
|
+
#
|
|
10
|
+
# Follows the same find/find_meta pattern as BrainQuery and OmiQuery.
|
|
11
|
+
class AppQuery
|
|
12
|
+
def initialize(options, jump_config: nil)
|
|
13
|
+
@options = options
|
|
14
|
+
@jump_config = jump_config
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Return absolute file paths matching the query
|
|
18
|
+
def find
|
|
19
|
+
return [] unless @options.query?
|
|
20
|
+
|
|
21
|
+
apps = resolve_apps
|
|
22
|
+
return [] if apps.empty?
|
|
23
|
+
|
|
24
|
+
apps.flat_map { |app| expand_app(app) }.uniq.sort
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Return structured metadata about the query results
|
|
28
|
+
def find_meta
|
|
29
|
+
return [] unless @options.query?
|
|
30
|
+
|
|
31
|
+
apps = resolve_apps
|
|
32
|
+
return [] if apps.empty?
|
|
33
|
+
|
|
34
|
+
apps.map { |app| build_meta(app) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# List all available glob names for a specific app
|
|
38
|
+
def list_globs(app_name)
|
|
39
|
+
location = find_location(app_name)
|
|
40
|
+
return [] unless location
|
|
41
|
+
|
|
42
|
+
loader = build_loader(location)
|
|
43
|
+
return [] unless loader.available?
|
|
44
|
+
|
|
45
|
+
loader.available_names
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# List all apps that have context.globs.json
|
|
49
|
+
def list_apps
|
|
50
|
+
jump_config.locations
|
|
51
|
+
.select { |loc| File.exist?(File.join(expand_path(loc.path), 'context.globs.json')) }
|
|
52
|
+
.map do |loc|
|
|
53
|
+
loader = build_loader(loc)
|
|
54
|
+
{
|
|
55
|
+
'key' => loc.key,
|
|
56
|
+
'path' => expand_path(loc.path),
|
|
57
|
+
'pattern' => loader.pattern,
|
|
58
|
+
'glob_count' => loader.globs.size
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def jump_config
|
|
66
|
+
@jump_config ||= Jump::Config.new
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Resolve app names to Location objects
|
|
70
|
+
def resolve_apps
|
|
71
|
+
if @options.pattern_filter
|
|
72
|
+
resolve_by_pattern(@options.pattern_filter)
|
|
73
|
+
else
|
|
74
|
+
@options.app_names.flat_map { |name| resolve_app(name) }.compact.uniq(&:key)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# 4-tier app resolution
|
|
79
|
+
def resolve_app(name)
|
|
80
|
+
name_down = name.downcase
|
|
81
|
+
|
|
82
|
+
# Tier 1: exact key match
|
|
83
|
+
loc = jump_config.find(name_down)
|
|
84
|
+
return [loc] if loc
|
|
85
|
+
|
|
86
|
+
# Tier 2: jump alias match
|
|
87
|
+
loc = jump_config.locations.find { |l| l.jump&.downcase == name_down }
|
|
88
|
+
return [loc] if loc
|
|
89
|
+
|
|
90
|
+
# Tier 3: substring match on key
|
|
91
|
+
matches = jump_config.locations.select { |l| l.key.downcase.include?(name_down) }
|
|
92
|
+
return matches unless matches.empty?
|
|
93
|
+
|
|
94
|
+
# Tier 4: substring match on description
|
|
95
|
+
jump_config.locations.select { |l| l.description&.downcase&.include?(name_down) }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def resolve_by_pattern(pattern)
|
|
99
|
+
jump_config.locations.select do |loc|
|
|
100
|
+
loader = build_loader(loc)
|
|
101
|
+
loader.available? && loader.pattern&.downcase == pattern.downcase
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def expand_app(location)
|
|
106
|
+
loader = build_loader(location)
|
|
107
|
+
return [] unless loader.available?
|
|
108
|
+
|
|
109
|
+
glob_names = @options.glob_names
|
|
110
|
+
return [] if glob_names.empty?
|
|
111
|
+
|
|
112
|
+
loader.expand(glob_names)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def build_meta(location)
|
|
116
|
+
loader = build_loader(location)
|
|
117
|
+
glob_names = @options.glob_names
|
|
118
|
+
file_count = loader.available? && glob_names.any? ? loader.expand(glob_names).size : 0
|
|
119
|
+
|
|
120
|
+
resolved_from = glob_names.map { |n| describe_resolution(loader, n) }.join(', ')
|
|
121
|
+
|
|
122
|
+
{
|
|
123
|
+
'app' => location.key,
|
|
124
|
+
'path' => expand_path(location.path),
|
|
125
|
+
'pattern' => loader.pattern,
|
|
126
|
+
'matched_globs' => resolve_glob_names(loader, glob_names),
|
|
127
|
+
'resolved_from' => resolved_from,
|
|
128
|
+
'file_count' => file_count
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def build_loader(location)
|
|
133
|
+
GlobsLoader.new(expand_path(location.path))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def expand_path(path)
|
|
137
|
+
File.expand_path(path)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def find_location(app_name)
|
|
141
|
+
results = resolve_app(app_name)
|
|
142
|
+
results&.first
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Describe how a glob name was resolved (for meta output)
|
|
146
|
+
def describe_resolution(loader, name)
|
|
147
|
+
name = name.strip.downcase
|
|
148
|
+
return "#{name} (glob)" if loader.globs.key?(name)
|
|
149
|
+
return "#{name} (alias)" if loader.aliases.key?(name)
|
|
150
|
+
return "#{name} (composite)" if loader.composites.key?(name)
|
|
151
|
+
|
|
152
|
+
"#{name} (fuzzy)"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Resolve glob names to their constituent direct glob names
|
|
156
|
+
def resolve_glob_names(loader, names)
|
|
157
|
+
return [] unless loader.available?
|
|
158
|
+
|
|
159
|
+
names.flat_map do |name|
|
|
160
|
+
name = name.strip.downcase
|
|
161
|
+
if loader.globs.key?(name)
|
|
162
|
+
[name]
|
|
163
|
+
elsif loader.aliases.key?(name)
|
|
164
|
+
loader.aliases[name]
|
|
165
|
+
elsif loader.composites.key?(name)
|
|
166
|
+
members = loader.composites[name]
|
|
167
|
+
members == ['*'] ? loader.globs.keys : members
|
|
168
|
+
else # rubocop:disable Lint/DuplicateBranch
|
|
169
|
+
[name]
|
|
170
|
+
end
|
|
171
|
+
end.uniq
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Appydave
|
|
6
|
+
module Tools
|
|
7
|
+
module AppContext
|
|
8
|
+
# Loads and resolves named glob patterns from a context.globs.json file.
|
|
9
|
+
#
|
|
10
|
+
# Resolution is 3-tier:
|
|
11
|
+
# 1. Direct glob name — "services" → globs["services"]
|
|
12
|
+
# 2. Alias match — "backend" → aliases["backend"] → ["services", "routes"]
|
|
13
|
+
# 3. Composite match — "understand" → composites["understand"] → ["context", "docs", ...]
|
|
14
|
+
class GlobsLoader
|
|
15
|
+
attr_reader :project_path, :data
|
|
16
|
+
|
|
17
|
+
def initialize(project_path)
|
|
18
|
+
@project_path = project_path
|
|
19
|
+
@data = load_globs_file
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def available?
|
|
23
|
+
!data.nil?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def globs
|
|
27
|
+
data&.fetch('globs', {}) || {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def aliases
|
|
31
|
+
data&.fetch('aliases', {}) || {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def composites
|
|
35
|
+
data&.fetch('composites', {}) || {}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pattern
|
|
39
|
+
data&.fetch('pattern', nil)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def project_name
|
|
43
|
+
data&.fetch('project', nil)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# List all available glob names (direct + aliases + composites)
|
|
47
|
+
def available_names
|
|
48
|
+
names = globs.keys.map { |k| { name: k, type: 'glob' } }
|
|
49
|
+
names += aliases.keys.map { |k| { name: k, type: 'alias' } }
|
|
50
|
+
names += composites.keys.map { |k| { name: k, type: 'composite' } }
|
|
51
|
+
names
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Resolve a single name through the 3-tier hierarchy.
|
|
55
|
+
# Returns an array of raw glob patterns (strings).
|
|
56
|
+
def resolve(name)
|
|
57
|
+
name = name.strip.downcase
|
|
58
|
+
|
|
59
|
+
# Tier 1: direct glob name
|
|
60
|
+
return globs[name] if globs.key?(name)
|
|
61
|
+
|
|
62
|
+
# Tier 2: alias → list of glob names
|
|
63
|
+
return aliases[name].flat_map { |glob_name| globs[glob_name] || [] } if aliases.key?(name)
|
|
64
|
+
|
|
65
|
+
# Tier 3: composite → list of glob names (or "*" for all)
|
|
66
|
+
if composites.key?(name)
|
|
67
|
+
members = composites[name]
|
|
68
|
+
return globs.values.flatten if members == ['*']
|
|
69
|
+
|
|
70
|
+
return members.flat_map { |glob_name| resolve_single_glob(glob_name) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Tier 4: substring fallback
|
|
74
|
+
match = find_substring_match(name)
|
|
75
|
+
return resolve(match) if match
|
|
76
|
+
|
|
77
|
+
[]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Resolve multiple names, expand globs against the filesystem, return absolute paths.
|
|
81
|
+
def expand(names)
|
|
82
|
+
patterns = names.flat_map { |name| resolve(name) }.uniq
|
|
83
|
+
|
|
84
|
+
patterns.flat_map { |pat| Dir.glob(File.join(project_path, pat)) }
|
|
85
|
+
.select { |f| File.file?(f) }
|
|
86
|
+
.uniq
|
|
87
|
+
.sort
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def globs_file_path
|
|
93
|
+
File.join(project_path, 'context.globs.json')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def load_globs_file
|
|
97
|
+
path = globs_file_path
|
|
98
|
+
return nil unless File.exist?(path)
|
|
99
|
+
|
|
100
|
+
JSON.parse(File.read(path))
|
|
101
|
+
rescue JSON::ParserError
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def resolve_single_glob(glob_name)
|
|
106
|
+
globs[glob_name] || []
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def find_substring_match(name)
|
|
110
|
+
all_names = globs.keys + aliases.keys + composites.keys
|
|
111
|
+
all_names.find { |n| n.include?(name) }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Appydave
|
|
4
|
+
module Tools
|
|
5
|
+
module AppContext
|
|
6
|
+
# Options struct for app context query tool
|
|
7
|
+
class Options
|
|
8
|
+
attr_accessor :app_names, :glob_names, :pattern_filter,
|
|
9
|
+
:meta, :list, :list_apps,
|
|
10
|
+
:debug_level
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@app_names = []
|
|
14
|
+
@glob_names = []
|
|
15
|
+
@pattern_filter = nil
|
|
16
|
+
@meta = false
|
|
17
|
+
@list = false
|
|
18
|
+
@list_apps = false
|
|
19
|
+
@debug_level = 'none'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def query?
|
|
23
|
+
app_names.any? || !pattern_filter.nil?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -34,11 +34,15 @@ module Appydave
|
|
|
34
34
|
@tokens = false
|
|
35
35
|
@base_dir = Dir.pwd
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
configured_omi = read_setting('omi-directory-path')
|
|
38
|
+
@omi_dir = configured_omi || File.expand_path('~/dev/raw-intake/omi')
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def brains_root
|
|
41
|
-
@brains_root ||=
|
|
42
|
+
@brains_root ||= begin
|
|
43
|
+
configured = read_setting('brains-root-path')
|
|
44
|
+
configured || File.expand_path('~/dev/ad/brains')
|
|
45
|
+
end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
def brains_index_path
|
|
@@ -52,6 +56,19 @@ module Appydave
|
|
|
52
56
|
def omi_query?
|
|
53
57
|
omi
|
|
54
58
|
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# Read a path value from settings config, expanding ~ if set.
|
|
63
|
+
# Returns nil if config is unavailable or key is absent/blank.
|
|
64
|
+
def read_setting(key)
|
|
65
|
+
value = Appydave::Tools::Configuration::Config.settings.get(key)
|
|
66
|
+
return nil if value.nil? || value.to_s.strip.empty?
|
|
67
|
+
|
|
68
|
+
File.expand_path(value)
|
|
69
|
+
rescue StandardError
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
55
72
|
end
|
|
56
73
|
end
|
|
57
74
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Appydave
|
|
4
|
+
module Tools
|
|
5
|
+
module Configuration
|
|
6
|
+
# Installs bundled example configuration files into the user's config directory.
|
|
7
|
+
#
|
|
8
|
+
# Example files live at config/examples/*.example.json inside the gem.
|
|
9
|
+
# Each file is installed without the `.example` segment in its name so that
|
|
10
|
+
# `settings.example.json` becomes `settings.json` in the target directory.
|
|
11
|
+
#
|
|
12
|
+
# Files are never overwritten — existing files are skipped and reported.
|
|
13
|
+
#
|
|
14
|
+
# @example Install all examples
|
|
15
|
+
# result = ExampleInstaller.new.install
|
|
16
|
+
# result[:installed] #=> ["settings.json", "locations.json"]
|
|
17
|
+
# result[:skipped] #=> []
|
|
18
|
+
class ExampleInstaller
|
|
19
|
+
EXAMPLES_PATH = File.expand_path('../../../../config/examples', __dir__)
|
|
20
|
+
|
|
21
|
+
# @param target_path [String, nil] Directory to install into.
|
|
22
|
+
# Defaults to the active Config.config_path (~/.config/appydave).
|
|
23
|
+
def initialize(target_path: nil)
|
|
24
|
+
@target_path = target_path || Config.config_path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Install all bundled example files that do not yet exist.
|
|
28
|
+
#
|
|
29
|
+
# @return [Hash] with keys :installed (Array<String>) and :skipped (Array<String>)
|
|
30
|
+
def install
|
|
31
|
+
FileUtils.mkdir_p(@target_path)
|
|
32
|
+
results = { installed: [], skipped: [] }
|
|
33
|
+
|
|
34
|
+
example_files.each do |src|
|
|
35
|
+
dest = destination_for(src)
|
|
36
|
+
basename = File.basename(dest)
|
|
37
|
+
|
|
38
|
+
if File.exist?(dest)
|
|
39
|
+
results[:skipped] << basename
|
|
40
|
+
else
|
|
41
|
+
FileUtils.cp(src, dest)
|
|
42
|
+
results[:installed] << basename
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
results
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# List the filenames that would be installed (target names, not source names).
|
|
50
|
+
#
|
|
51
|
+
# @return [Array<String>]
|
|
52
|
+
def available
|
|
53
|
+
example_files.map { |f| target_name(f) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def example_files
|
|
59
|
+
Dir.glob(File.join(EXAMPLES_PATH, '*.example.*')).sort
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def destination_for(src)
|
|
63
|
+
File.join(@target_path, target_name(src))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def target_name(src)
|
|
67
|
+
File.basename(src).sub('.example', '')
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -42,6 +42,18 @@ module Appydave
|
|
|
42
42
|
get('current_user')
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
# Path to the root brains directory (second-brain knowledge base)
|
|
46
|
+
# Configure via settings.json key: brains-root-path
|
|
47
|
+
def brains_root_path
|
|
48
|
+
get('brains-root-path')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Path to the OMI wearable transcripts directory
|
|
52
|
+
# Configure via settings.json key: omi-directory-path
|
|
53
|
+
def omi_directory_path
|
|
54
|
+
get('omi-directory-path')
|
|
55
|
+
end
|
|
56
|
+
|
|
45
57
|
def print
|
|
46
58
|
log.subheading 'Settings Configuration'
|
|
47
59
|
|
|
@@ -24,11 +24,12 @@ module Appydave
|
|
|
24
24
|
#
|
|
25
25
|
# @return [String] Formatted table
|
|
26
26
|
def format
|
|
27
|
-
return format_error
|
|
28
|
-
return format_info
|
|
29
|
-
return format_summary
|
|
30
|
-
return format_groups
|
|
31
|
-
return
|
|
27
|
+
return format_error unless success?
|
|
28
|
+
return format_info if info_result?
|
|
29
|
+
return format_summary if summary_result?
|
|
30
|
+
return format_groups unless groups.empty?
|
|
31
|
+
return format_mutation if mutation_result?
|
|
32
|
+
return format_empty if results.empty?
|
|
32
33
|
return format_definition_report if definition_report?
|
|
33
34
|
return format_count_report if count_report?
|
|
34
35
|
return format_category_report if category_report?
|
|
@@ -45,6 +46,16 @@ module Appydave
|
|
|
45
46
|
lines.join("\n")
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
def mutation_result?
|
|
50
|
+
data.key?(:message) && (data.key?(:location) || data[:message].to_s.match?(/removed|updated|added/i))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def format_mutation
|
|
54
|
+
lines = [colorize(data[:message], :green)]
|
|
55
|
+
lines << colorize(data[:warning], :yellow) if data[:warning]
|
|
56
|
+
lines.join("\n")
|
|
57
|
+
end
|
|
58
|
+
|
|
48
59
|
def format_empty
|
|
49
60
|
message = case data[:report]
|
|
50
61
|
when 'brands'
|
|
@@ -108,8 +108,8 @@ module Appydave
|
|
|
108
108
|
^Finished in \\d
|
|
109
109
|
^\\d+ examples, \\d+ failures
|
|
110
110
|
|
|
111
|
-
# Process listing output
|
|
112
|
-
|
|
111
|
+
# Process listing output (current user)
|
|
112
|
+
^#{ENV.fetch('USER', ENV.fetch('USERNAME', ''))}\\s+\\d+
|
|
113
113
|
PATTERNS
|
|
114
114
|
|
|
115
115
|
# Create crash-recovery profile
|
|
@@ -34,8 +34,7 @@ module Appydave
|
|
|
34
34
|
'^head ',
|
|
35
35
|
'^tail ',
|
|
36
36
|
'^echo \\$',
|
|
37
|
-
'^\\[\\d+\\]',
|
|
38
|
-
'^davidcruwys\\s+\\d+', # Process listing output
|
|
37
|
+
'^\\[\\d+\\]', # Output like [1234]
|
|
39
38
|
'^zsh: command not found',
|
|
40
39
|
'^X Process completed',
|
|
41
40
|
'^Coverage report',
|
|
@@ -138,9 +137,16 @@ module Appydave
|
|
|
138
137
|
end
|
|
139
138
|
|
|
140
139
|
def load_exclude_patterns
|
|
141
|
-
# Try loading from config, fall back to defaults
|
|
140
|
+
# Try loading from config, fall back to defaults (with dynamic user pattern appended)
|
|
142
141
|
config_patterns = config.exclude_patterns
|
|
143
|
-
config_patterns ||
|
|
142
|
+
config_patterns || default_exclude_patterns_with_user
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def default_exclude_patterns_with_user
|
|
146
|
+
username = ENV.fetch('USER', ENV.fetch('USERNAME', ''))
|
|
147
|
+
return DEFAULT_EXCLUDE_PATTERNS if username.empty?
|
|
148
|
+
|
|
149
|
+
DEFAULT_EXCLUDE_PATTERNS + ["^#{username}\\s+\\d+"]
|
|
144
150
|
end
|
|
145
151
|
|
|
146
152
|
def load_include_patterns
|
data/lib/appydave/tools.rb
CHANGED
|
@@ -41,6 +41,10 @@ require 'appydave/tools/brain_context/options'
|
|
|
41
41
|
require 'appydave/tools/brain_context/brain_finder'
|
|
42
42
|
require 'appydave/tools/brain_context/omi_finder'
|
|
43
43
|
|
|
44
|
+
require 'appydave/tools/app_context/options'
|
|
45
|
+
require 'appydave/tools/app_context/globs_loader'
|
|
46
|
+
require 'appydave/tools/app_context/app_finder'
|
|
47
|
+
|
|
44
48
|
require 'appydave/tools/random_context/query_entry'
|
|
45
49
|
require 'appydave/tools/random_context/randomizer'
|
|
46
50
|
|
|
@@ -52,6 +56,7 @@ require 'appydave/tools/configuration/models/settings_config'
|
|
|
52
56
|
require 'appydave/tools/configuration/models/brands_config'
|
|
53
57
|
require 'appydave/tools/configuration/models/channels_config'
|
|
54
58
|
require 'appydave/tools/configuration/models/youtube_automation_config'
|
|
59
|
+
require 'appydave/tools/configuration/example_installer'
|
|
55
60
|
require 'appydave/tools/name_manager/project_name'
|
|
56
61
|
|
|
57
62
|
require 'appydave/tools/prompt_tools/prompt_completion'
|
data/package.json
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: appydave-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.85.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Cruwys
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activemodel
|
|
@@ -190,6 +190,7 @@ executables:
|
|
|
190
190
|
- jump
|
|
191
191
|
- llm_context
|
|
192
192
|
- prompt_tools
|
|
193
|
+
- query_apps
|
|
193
194
|
- query_brain
|
|
194
195
|
- query_omi
|
|
195
196
|
- subtitle_processor
|
|
@@ -231,6 +232,7 @@ files:
|
|
|
231
232
|
- bin/llm_context.rb
|
|
232
233
|
- bin/move_images.rb
|
|
233
234
|
- bin/prompt_tools.rb
|
|
235
|
+
- bin/query_apps.rb
|
|
234
236
|
- bin/query_brain.rb
|
|
235
237
|
- bin/query_omi.rb
|
|
236
238
|
- bin/random_context.rb
|
|
@@ -242,7 +244,10 @@ files:
|
|
|
242
244
|
- bin/youtube_automation.rb
|
|
243
245
|
- bin/youtube_manager.rb
|
|
244
246
|
- bin/zsh_history.rb
|
|
247
|
+
- config/examples/locations.example.json
|
|
248
|
+
- config/examples/settings.example.json
|
|
245
249
|
- config/random-queries.yml
|
|
250
|
+
- context.globs.json
|
|
246
251
|
- docs/README.md
|
|
247
252
|
- docs/ai-instructions/behavioral-regression-audit.md
|
|
248
253
|
- docs/ai-instructions/code-quality-retrospective.md
|
|
@@ -329,8 +334,11 @@ files:
|
|
|
329
334
|
- docs/planning/micro-cleanup/AGENTS.md
|
|
330
335
|
- docs/planning/micro-cleanup/IMPLEMENTATION_PLAN.md
|
|
331
336
|
- docs/planning/micro-cleanup/assessment.md
|
|
337
|
+
- docs/planning/multi-user-support.md
|
|
338
|
+
- docs/planning/query-apps-design.md
|
|
332
339
|
- docs/planning/query-location-feature/IMPLEMENTATION_PLAN.md
|
|
333
340
|
- docs/planning/query-location-feature/system-context-gap-analysis.md
|
|
341
|
+
- docs/planning/query-skills-plan.md
|
|
334
342
|
- docs/planning/s3-operations-split/AGENTS.md
|
|
335
343
|
- docs/planning/s3-operations-split/IMPLEMENTATION_PLAN.md
|
|
336
344
|
- docs/planning/test-coverage-gaps/AGENTS.md
|
|
@@ -338,6 +346,7 @@ files:
|
|
|
338
346
|
- docs/planning/test-coverage-gaps/assessment.md
|
|
339
347
|
- docs/specs/fr-002-gpt-context-help-system.md
|
|
340
348
|
- docs/specs/fr-003-jump-location-tool.md
|
|
349
|
+
- docs/specs/jump-add-display-fix.md
|
|
341
350
|
- docs/specs/zsh-history-tool.md
|
|
342
351
|
- docs/templates/.env.example
|
|
343
352
|
- docs/templates/channels.example.json
|
|
@@ -348,6 +357,7 @@ files:
|
|
|
348
357
|
- exe/jump
|
|
349
358
|
- exe/llm_context
|
|
350
359
|
- exe/prompt_tools
|
|
360
|
+
- exe/query_apps
|
|
351
361
|
- exe/query_brain
|
|
352
362
|
- exe/query_omi
|
|
353
363
|
- exe/subtitle_processor
|
|
@@ -356,6 +366,9 @@ files:
|
|
|
356
366
|
- exe/zsh_history
|
|
357
367
|
- images.log
|
|
358
368
|
- lib/appydave/tools.rb
|
|
369
|
+
- lib/appydave/tools/app_context/app_finder.rb
|
|
370
|
+
- lib/appydave/tools/app_context/globs_loader.rb
|
|
371
|
+
- lib/appydave/tools/app_context/options.rb
|
|
359
372
|
- lib/appydave/tools/brain_context/brain_finder.rb
|
|
360
373
|
- lib/appydave/tools/brain_context/omi_finder.rb
|
|
361
374
|
- lib/appydave/tools/brain_context/options.rb
|
|
@@ -367,6 +380,7 @@ files:
|
|
|
367
380
|
- lib/appydave/tools/configuration/_doc.md
|
|
368
381
|
- lib/appydave/tools/configuration/config.rb
|
|
369
382
|
- lib/appydave/tools/configuration/configurable.rb
|
|
383
|
+
- lib/appydave/tools/configuration/example_installer.rb
|
|
370
384
|
- lib/appydave/tools/configuration/models/brands_config.rb
|
|
371
385
|
- lib/appydave/tools/configuration/models/channels_config.rb
|
|
372
386
|
- lib/appydave/tools/configuration/models/config_base.rb
|