woods 2.0.0.beta2 → 2.0.0.beta4
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 +339 -1
- data/CONTRIBUTING.md +188 -12
- data/README.md +93 -174
- data/SECURITY.md +9 -6
- data/docs/AGENT_GUIDE.md +109 -8
- data/docs/AGENT_SETUP.md +98 -7
- data/docs/BACKEND_MATRIX.md +25 -0
- data/docs/CLIENT_HOOKS.md +111 -0
- data/docs/CONFIGURATION_REFERENCE.md +267 -16
- data/docs/CONSOLE_MCP_SETUP.md +80 -7
- data/docs/DOCKER_SETUP.md +22 -3
- data/docs/EVALUATION.md +464 -1
- data/docs/EXTRACTOR_REFERENCE.md +45 -6
- data/docs/FAQ.md +11 -12
- data/docs/GETTING_STARTED.md +17 -5
- data/docs/INCREMENTAL_EXTRACTION.md +147 -7
- data/docs/INDEX_LAYOUT.md +382 -0
- data/docs/INTERNALS.md +7 -2
- data/docs/MCP_SERVERS.md +276 -5
- data/docs/MCP_TOOL_COOKBOOK.md +37 -22
- data/docs/MCP_WORKTREE_SETUP.md +43 -83
- data/docs/NOTION_INTEGRATION.md +13 -0
- data/docs/OBSIDIAN_INTEGRATION.md +57 -9
- data/docs/PUBLISHED_INDEX.md +72 -0
- data/docs/README.md +7 -0
- data/docs/RETRIEVAL_GUIDE.md +273 -12
- data/docs/RUNTIME_TRACING.md +71 -0
- data/docs/SOURCE_FRESHNESS.md +143 -0
- data/docs/TROUBLESHOOTING.md +129 -18
- data/docs/UNBLOCKED_INTEGRATION.md +25 -0
- data/docs/UPGRADING_TO_2.md +48 -22
- data/docs/WATCH_DAEMON.md +277 -67
- data/exe/woods-agent-config +6 -0
- data/exe/woods-extract +5 -0
- data/exe/woods-hook-context +6 -0
- data/exe/woods-mcp-start +14 -9
- data/lib/generators/woods/pgvector_generator.rb +8 -2
- data/lib/generators/woods/templates/woods.rb.tt +1 -3
- data/lib/tasks/woods.rake +47 -397
- data/lib/woods/agent_configuration/applier.rb +135 -0
- data/lib/woods/agent_configuration/cli.rb +101 -0
- data/lib/woods/agent_configuration/cli_options.rb +29 -0
- data/lib/woods/agent_configuration/document.rb +105 -0
- data/lib/woods/agent_configuration/error.rb +7 -0
- data/lib/woods/agent_configuration/launcher.rb +75 -0
- data/lib/woods/agent_configuration/layout.rb +72 -0
- data/lib/woods/agent_configuration/managed_section.rb +62 -0
- data/lib/woods/agent_configuration/plan.rb +98 -0
- data/lib/woods/agent_configuration/plan_diff.rb +38 -0
- data/lib/woods/agent_configuration/planned_files.rb +61 -0
- data/lib/woods/agent_configuration/planner.rb +63 -0
- data/lib/woods/agent_configuration/planner_validation.rb +77 -0
- data/lib/woods/agent_configuration/preflight.rb +100 -0
- data/lib/woods/agent_configuration/recovery.rb +49 -0
- data/lib/woods/ast/node.rb +2 -0
- data/lib/woods/ast/parser.rb +38 -5
- data/lib/woods/builder.rb +21 -5
- data/lib/woods/cache/cache_middleware.rb +28 -7
- data/lib/woods/cache/cache_store.rb +4 -5
- data/lib/woods/change_set.rb +5 -4
- data/lib/woods/console/credential_index.rb +20 -2
- data/lib/woods/console/credential_scanner.rb +18 -17
- data/lib/woods/console/credential_scanner_registry.rb +36 -0
- data/lib/woods/console/dispatch_pipeline.rb +7 -0
- data/lib/woods/console/embedded_executor.rb +32 -10
- data/lib/woods/console/encrypted_credential_snapshot.rb +16 -0
- data/lib/woods/console/rack_middleware.rb +22 -13
- data/lib/woods/console/server.rb +18 -16
- data/lib/woods/console/sql_noise_stripper.rb +9 -7
- data/lib/woods/console/sql_table_scanner.rb +47 -7
- data/lib/woods/console/sql_validator.rb +49 -9
- data/lib/woods/console/sqlite_read_guard.rb +46 -0
- data/lib/woods/coordination/pipeline_lock.rb +3 -2
- data/lib/woods/dependency_graph.rb +65 -13
- data/lib/woods/embedding/corpus.rb +94 -0
- data/lib/woods/embedding/indexer.rb +114 -60
- data/lib/woods/embedding/openai.rb +17 -6
- data/lib/woods/evaluation/ablation_executor.rb +6 -1
- data/lib/woods/evaluation/ablation_timed_executor.rb +22 -4
- data/lib/woods/export/typed_reader.rb +56 -0
- data/lib/woods/extractor.rb +277 -149
- data/lib/woods/extractors/action_cable_extractor.rb +3 -1
- data/lib/woods/extractors/behavioral_profile.rb +9 -7
- data/lib/woods/extractors/caching_extractor.rb +3 -1
- data/lib/woods/extractors/concern_extractor.rb +64 -6
- data/lib/woods/extractors/configuration_extractor.rb +7 -3
- data/lib/woods/extractors/controller_extractor.rb +13 -4
- data/lib/woods/extractors/database_view_extractor.rb +3 -1
- data/lib/woods/extractors/declared_parent.rb +55 -0
- data/lib/woods/extractors/decorator_extractor.rb +3 -1
- data/lib/woods/extractors/engine_extractor.rb +3 -1
- data/lib/woods/extractors/event_extractor.rb +4 -2
- data/lib/woods/extractors/factory_extractor.rb +3 -1
- data/lib/woods/extractors/graphql_extractor.rb +10 -13
- data/lib/woods/extractors/i18n_extractor.rb +3 -1
- data/lib/woods/extractors/job_extractor.rb +6 -19
- data/lib/woods/extractors/lib_extractor.rb +13 -9
- data/lib/woods/extractors/mailer_extractor.rb +26 -15
- data/lib/woods/extractors/manager_extractor.rb +3 -1
- data/lib/woods/extractors/method_parameters.rb +53 -0
- data/lib/woods/extractors/middleware_argument.rb +65 -0
- data/lib/woods/extractors/middleware_extractor.rb +9 -3
- data/lib/woods/extractors/migration_extractor.rb +3 -1
- data/lib/woods/extractors/model_extractor.rb +26 -34
- data/lib/woods/extractors/package_extractor.rb +24 -4
- data/lib/woods/extractors/phlex_extractor.rb +3 -1
- data/lib/woods/extractors/policy_extractor.rb +3 -1
- data/lib/woods/extractors/poro_extractor.rb +13 -9
- data/lib/woods/extractors/pundit_extractor.rb +3 -1
- data/lib/woods/extractors/rails_source_extractor.rb +4 -2
- data/lib/woods/extractors/rake_task_extractor.rb +4 -2
- data/lib/woods/extractors/route_extractor.rb +3 -1
- data/lib/woods/extractors/route_helper_resolver.rb +10 -33
- data/lib/woods/extractors/scheduled_job_extractor.rb +41 -15
- data/lib/woods/extractors/serializer_extractor.rb +4 -2
- data/lib/woods/extractors/service_extractor.rb +3 -1
- data/lib/woods/extractors/shared_dependency_scanner.rb +2 -2
- data/lib/woods/extractors/shared_utility_methods.rb +48 -19
- data/lib/woods/extractors/source_nesting.rb +1 -1
- data/lib/woods/extractors/state_machine_extractor.rb +3 -1
- data/lib/woods/extractors/test_mapping_extractor.rb +3 -1
- data/lib/woods/extractors/validator_extractor.rb +3 -1
- data/lib/woods/extractors/view_component_extractor.rb +3 -1
- data/lib/woods/extractors/view_template_extractor.rb +3 -1
- data/lib/woods/gem_mapper.rb +2 -0
- data/lib/woods/git_history.rb +116 -0
- data/lib/woods/graph_analyzer.rb +35 -6
- data/lib/woods/hooks/context_cli.rb +54 -0
- data/lib/woods/hooks/context_event.rb +88 -0
- data/lib/woods/hooks/context_hint.rb +73 -0
- data/lib/woods/hooks/context_impact.rb +77 -0
- data/lib/woods/hooks/context_output.rb +47 -0
- data/lib/woods/hooks/context_state.rb +102 -0
- data/lib/woods/hooks/refresh.rb +79 -0
- data/lib/woods/hooks/rule_projection.rb +78 -0
- data/lib/woods/input_rules.rb +19 -0
- data/lib/woods/mcp/bearer_auth.rb +22 -13
- data/lib/woods/mcp/bootstrapper.rb +79 -4
- data/lib/woods/mcp/config_resolver.rb +2 -1
- data/lib/woods/mcp/index_reader.rb +334 -162
- data/lib/woods/mcp/initialization_guidance.rb +27 -0
- data/lib/woods/mcp/origin_guard.rb +17 -9
- data/lib/woods/mcp/published_lexical_retriever.rb +115 -0
- data/lib/woods/mcp/renderers/markdown_renderer.rb +22 -9
- data/lib/woods/mcp/renderers/plain_renderer.rb +18 -8
- data/lib/woods/mcp/search_results.rb +74 -0
- data/lib/woods/mcp/server.rb +178 -63
- data/lib/woods/mcp/tool_contract.rb +3 -1
- data/lib/woods/mcp/tool_response_renderer.rb +41 -0
- data/lib/woods/mcp/traversal_evidence.rb +113 -0
- data/lib/woods/mcp/traversal_evidence_index.rb +100 -0
- data/lib/woods/mcp/traversal_evidence_page.rb +41 -0
- data/lib/woods/mcp/traversal_evidence_text.rb +52 -0
- data/lib/woods/mcp/traversal_response.rb +22 -0
- data/lib/woods/notion/exporter.rb +56 -17
- data/lib/woods/obsidian/destination_plan.rb +98 -0
- data/lib/woods/obsidian/name_mapper.rb +19 -3
- data/lib/woods/obsidian/note_builder.rb +19 -10
- data/lib/woods/obsidian/vault_exporter.rb +88 -32
- data/lib/woods/operator/pipeline_guard.rb +18 -13
- data/lib/woods/path_dispatcher.rb +13 -6
- data/lib/woods/payload_store.rb +27 -26
- data/lib/woods/published_index/typed_unit_reader.rb +40 -3
- data/lib/woods/published_index.rb +2 -2
- data/lib/woods/railtie.rb +3 -3
- data/lib/woods/railtie_support.rb +12 -12
- data/lib/woods/rake_helpers.rb +382 -0
- data/lib/woods/resilience/graph_invariant_validator/membership_checks.rb +71 -0
- data/lib/woods/resilience/graph_invariant_validator/node_checks.rb +61 -0
- data/lib/woods/resilience/graph_invariant_validator/reverse_relationship_checks.rb +46 -0
- data/lib/woods/resilience/graph_invariant_validator.rb +119 -0
- data/lib/woods/resilience/index_validator/graph_checks.rb +80 -0
- data/lib/woods/resilience/index_validator.rb +112 -23
- data/lib/woods/retrieval/context_assembler.rb +50 -15
- data/lib/woods/retrieval/lexical_assembler.rb +84 -0
- data/lib/woods/retrieval/lexical_index.rb +120 -0
- data/lib/woods/retrieval/ranker.rb +4 -2
- data/lib/woods/retrieval/scope.rb +108 -0
- data/lib/woods/retrieval/scoped_graph_store.rb +32 -0
- data/lib/woods/retrieval/scoped_vector_store.rb +55 -0
- data/lib/woods/retrieval/search_executor.rb +86 -27
- data/lib/woods/retrieval/source_evidence.rb +200 -0
- data/lib/woods/retriever.rb +98 -22
- data/lib/woods/ruby_analyzer/trace_enricher.rb +77 -38
- data/lib/woods/session_tracer/file_store.rb +6 -1
- data/lib/woods/session_tracer/middleware.rb +10 -12
- data/lib/woods/session_tracer/redis_store.rb +22 -6
- data/lib/woods/session_tracer/session_flow_assembler.rb +23 -17
- data/lib/woods/session_tracer/solid_cache_coordination.rb +6 -4
- data/lib/woods/session_tracer/unit_resolver.rb +63 -0
- data/lib/woods/source_inputs/consumer_errors.rb +31 -0
- data/lib/woods/source_inputs/handoff.rb +102 -0
- data/lib/woods/source_inputs/launcher.rb +157 -0
- data/lib/woods/source_inputs/manifest.rb +124 -0
- data/lib/woods/source_inputs/private_key.rb +55 -0
- data/lib/woods/source_inputs/scanner.rb +171 -0
- data/lib/woods/source_inputs/scopes.rb +71 -0
- data/lib/woods/source_inputs/session.rb +214 -0
- data/lib/woods/source_inputs/status.rb +84 -0
- data/lib/woods/source_inputs/verifier.rb +107 -0
- data/lib/woods/storage/metadata_store.rb +25 -25
- data/lib/woods/storage/pgvector.rb +35 -10
- data/lib/woods/storage/qdrant.rb +17 -7
- data/lib/woods/storage/vector_store.rb +18 -6
- data/lib/woods/tasks.rb +3 -2
- data/lib/woods/temporal/json_snapshot_store.rb +58 -9
- data/lib/woods/unblocked/exporter.rb +59 -70
- data/lib/woods/version.rb +1 -1
- data/lib/woods/watch/boot_snapshot.rb +52 -0
- data/lib/woods/watch/daemon.rb +154 -32
- data/lib/woods/watch/listen_watcher.rb +4 -0
- data/lib/woods/watch/polling_watcher.rb +5 -1
- data/lib/woods/watch/status.rb +20 -15
- data/lib/woods/watch/tree_scan.rb +21 -13
- data/lib/woods/watch/watcher.rb +4 -1
- data/lib/woods.rb +50 -11
- data/plugin/.claude-plugin/plugin.json +1 -1
- data/plugin/hooks/adapters/normalize.jq +15 -0
- data/plugin/hooks/adapters/normalize.rb +63 -0
- data/plugin/hooks/hooks.json +20 -0
- data/plugin/hooks/woods-context.sh +50 -0
- data/plugin/hooks/woods-input-rules.sh +159 -0
- data/plugin/hooks/woods-opencode.mjs +65 -0
- data/plugin/hooks/woods-post-edit.sh +2 -225
- data/plugin/hooks/woods-refresh.sh +260 -0
- data/plugin/hooks/woods-session-start.sh +47 -55
- data/plugin/skills/woods-agent-enable/SKILL.md +19 -0
- data/plugin/skills/woods-diagnose/SKILL.md +319 -1
- data/plugin/skills/woods-investigate/SKILL.md +145 -0
- data/plugin/skills/woods-mcp-config/SKILL.md +90 -2
- data/plugin/skills/woods-setup/SKILL.md +110 -6
- metadata +87 -5
|
@@ -6,6 +6,7 @@ require_relative 'errors'
|
|
|
6
6
|
require_relative 'bootstrap_state'
|
|
7
7
|
require_relative 'config_resolver'
|
|
8
8
|
require_relative 'provider_probe'
|
|
9
|
+
require_relative 'published_lexical_retriever'
|
|
9
10
|
require_relative '../index_artifact'
|
|
10
11
|
require_relative '../builder'
|
|
11
12
|
require_relative '../resolved_config'
|
|
@@ -27,16 +28,19 @@ module Woods
|
|
|
27
28
|
# @param argv [Array<String>] Command-line arguments
|
|
28
29
|
# @return [String] Validated index directory path
|
|
29
30
|
def self.resolve_index_dir(argv)
|
|
30
|
-
dir = argv[0] || ENV['WOODS_DIR'] || Dir.pwd
|
|
31
|
+
dir = argv[0] || ENV['WOODS_DIR'] || ENV['WOODS_OUTPUT'] || Dir.pwd
|
|
32
|
+
examined = dir.empty? ? '(empty path)' : File.expand_path(dir)
|
|
31
33
|
|
|
32
34
|
unless Dir.exist?(dir)
|
|
33
|
-
warn "Error: Index directory does not exist: #{
|
|
35
|
+
warn "Error: Index directory does not exist: #{examined}"
|
|
36
|
+
warn index_path_remedy
|
|
34
37
|
exit 1
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
unless manifest_present?(dir)
|
|
38
|
-
warn "Error:
|
|
39
|
-
warn '
|
|
41
|
+
warn "Error: Could not resolve a published Woods index in: #{examined}"
|
|
42
|
+
warn 'Expected generation.json pointing to a payload manifest.json, or a legacy flat manifest.json.'
|
|
43
|
+
warn index_path_remedy
|
|
40
44
|
exit 1
|
|
41
45
|
end
|
|
42
46
|
|
|
@@ -60,9 +64,19 @@ module Woods
|
|
|
60
64
|
|
|
61
65
|
generation = Woods::Generation.new(output_dir: dir)
|
|
62
66
|
generation.payload_dir(generation.current).join('manifest.json').file?
|
|
67
|
+
rescue TypeError, NoMethodError
|
|
68
|
+
# A malformed marker must remain a startup failure, with the same
|
|
69
|
+
# selected-path guidance as a missing or unresolved payload.
|
|
70
|
+
false
|
|
63
71
|
end
|
|
64
72
|
private_class_method :manifest_present?
|
|
65
73
|
|
|
74
|
+
def self.index_path_remedy
|
|
75
|
+
'Point at the existing index with an explicit path, WOODS_DIR, or WOODS_OUTPUT. ' \
|
|
76
|
+
'If no index exists, run `bundle exec rake woods:extract` in your Rails app.'
|
|
77
|
+
end
|
|
78
|
+
private_class_method :index_path_remedy
|
|
79
|
+
|
|
66
80
|
# Build a snapshot store for temporal tracking.
|
|
67
81
|
#
|
|
68
82
|
# Auto-enables when a SQLite database already exists in the index directory,
|
|
@@ -128,6 +142,12 @@ module Woods
|
|
|
128
142
|
# credentials, dimension mismatch, unsupported artifact, or a missing
|
|
129
143
|
# artifact under WOODS_REQUIRE_INDEX=1).
|
|
130
144
|
def self.build_retriever(index_dir: nil)
|
|
145
|
+
mode = ENV.fetch('WOODS_RETRIEVAL_MODE', Woods.configuration.retrieval_mode.to_s)
|
|
146
|
+
unless %w[semantic lexical].include?(mode)
|
|
147
|
+
raise BootstrapError, "WOODS_RETRIEVAL_MODE must be semantic or lexical, got #{mode.inspect}"
|
|
148
|
+
end
|
|
149
|
+
return build_lexical_retriever(index_dir) if mode == 'lexical'
|
|
150
|
+
|
|
131
151
|
state = BootstrapState.new
|
|
132
152
|
state.mark(:hydrating)
|
|
133
153
|
|
|
@@ -161,6 +181,20 @@ module Woods
|
|
|
161
181
|
[retriever, state]
|
|
162
182
|
end
|
|
163
183
|
|
|
184
|
+
def self.build_lexical_retriever(index_dir)
|
|
185
|
+
state = BootstrapState.new
|
|
186
|
+
state.mark(:hydrating)
|
|
187
|
+
retriever = PublishedLexicalRetriever.new(index_dir: index_dir || Woods.configuration.output_dir,
|
|
188
|
+
default_budget: Woods.configuration.max_context_tokens)
|
|
189
|
+
retriever.warmup!
|
|
190
|
+
state.mark(:hydrated)
|
|
191
|
+
warn '[woods-mcp] lexical retrieval: hydrated (published extraction units; no embeddings)'
|
|
192
|
+
[retriever, state]
|
|
193
|
+
rescue StandardError => e
|
|
194
|
+
raise BootstrapError, "lexical index could not be loaded: #{e.class}: #{e.message}"
|
|
195
|
+
end
|
|
196
|
+
private_class_method :build_lexical_retriever
|
|
197
|
+
|
|
164
198
|
def self.static_source_map_without_embeddings?(artifact)
|
|
165
199
|
return false unless artifact
|
|
166
200
|
|
|
@@ -248,6 +282,10 @@ module Woods
|
|
|
248
282
|
zero_counts = { vectors: 0, metadata: 0, graph: 0 }
|
|
249
283
|
return refresh_reader_only(reader, zero_counts) unless retriever
|
|
250
284
|
|
|
285
|
+
if retriever.is_a?(PublishedLexicalRetriever)
|
|
286
|
+
return reload_lexical_stores!(retriever, index_dir, reader || retriever.reader, state, hooks)
|
|
287
|
+
end
|
|
288
|
+
|
|
251
289
|
target = swap_target(retriever)
|
|
252
290
|
return refresh_reader_only(reader, zero_counts) unless target
|
|
253
291
|
|
|
@@ -312,6 +350,43 @@ module Woods
|
|
|
312
350
|
# Refresh the reader's cached index state and answer the zero-count
|
|
313
351
|
# no-op (MCP-2).
|
|
314
352
|
#
|
|
353
|
+
def self.reload_lexical_stores!(retriever, index_dir, reader, state, hooks)
|
|
354
|
+
generation = Woods::Generation.new(output_dir: index_dir)
|
|
355
|
+
captured = generation.current
|
|
356
|
+
candidate = PublishedLexicalRetriever.new(index_dir: index_dir).warmup!
|
|
357
|
+
run_hook(hooks, :after_candidates)
|
|
358
|
+
lock = reload_extraction_lock(index_dir)
|
|
359
|
+
unless acquire_writer_lock_briefly(lock)
|
|
360
|
+
raise ReloadDegraded.new('lexical reload could not acquire the extraction writer lock',
|
|
361
|
+
generation: reader.loaded_generation || 0, stores: ['metadata'])
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
begin
|
|
365
|
+
reader.with_exclusive_generation do
|
|
366
|
+
identity = candidate.snapshot.first
|
|
367
|
+
expected = [captured.number, captured.token, generation.payload_dir(captured).to_s]
|
|
368
|
+
unless same_generation_marker?(captured, generation.current) &&
|
|
369
|
+
[identity.first.to_i, *identity.drop(1)] == expected
|
|
370
|
+
raise ReloadGenerationMoved.new('generation moved during lexical reload; nothing was swapped',
|
|
371
|
+
generation: reader.loaded_generation || 0, stores: ['metadata'])
|
|
372
|
+
end
|
|
373
|
+
counts = { vectors: 0, metadata: candidate.metadata_store.count, graph: 0 }
|
|
374
|
+
reader.reload!
|
|
375
|
+
retriever.install_snapshot!(candidate.snapshot)
|
|
376
|
+
state&.clear_reload_failure!
|
|
377
|
+
counts
|
|
378
|
+
end
|
|
379
|
+
ensure
|
|
380
|
+
lock.release
|
|
381
|
+
end
|
|
382
|
+
rescue ReloadDegraded
|
|
383
|
+
raise
|
|
384
|
+
rescue StandardError => e
|
|
385
|
+
raise ReloadDegraded.new("lexical snapshot reload failed: #{e.class}: #{e.message}",
|
|
386
|
+
generation: reader.loaded_generation || 0, stores: ['metadata'])
|
|
387
|
+
end
|
|
388
|
+
private_class_method :reload_lexical_stores!
|
|
389
|
+
|
|
315
390
|
# A store no-op is not an index no-op: the caller invoked `reload`
|
|
316
391
|
# because something on disk moved, and on a flat (pre-2.0) index
|
|
317
392
|
# {IndexReader} never self-refreshes, so skipping this reports
|
|
@@ -295,7 +295,8 @@ module Woods
|
|
|
295
295
|
unless resolved.embedding_provider
|
|
296
296
|
warn '[woods-mcp] no woods.json and no embedding provider — serving ' \
|
|
297
297
|
'pattern/structural tools only. Run `rake woods:embed`, or set ' \
|
|
298
|
-
'OPENAI_API_KEY / run Ollama, to enable semantic search.'
|
|
298
|
+
'OPENAI_API_KEY / run Ollama, to enable semantic search. For ranked discovery with no embeddings, ' \
|
|
299
|
+
'set WOODS_RETRIEVAL_MODE=lexical in the MCP process environment and restart the server.'
|
|
299
300
|
end
|
|
300
301
|
[resolved, :autodetect]
|
|
301
302
|
end
|