woods 2.0.1 → 2.1.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 +94 -7
- data/CONTRIBUTING.md +134 -19
- data/README.md +1 -1
- data/docs/AGENT_GUIDE.md +19 -0
- data/docs/AGENT_SETUP.md +22 -2
- data/docs/BACKEND_MATRIX.md +7 -0
- data/docs/CLIENT_HOOKS.md +6 -0
- data/docs/CONFIGURATION_REFERENCE.md +133 -25
- data/docs/CONSOLE_MCP_SETUP.md +82 -30
- data/docs/EMBEDDING_MODELS.md +16 -19
- data/docs/EXTRACTOR_REFERENCE.md +219 -21
- data/docs/FAQ.md +11 -25
- data/docs/GETTING_STARTED.md +7 -1
- data/docs/INCREMENTAL_EXTRACTION.md +261 -19
- data/docs/INDEX_LAYOUT.md +5 -0
- data/docs/INTERNALS.md +9 -0
- data/docs/MCP_HTTP_TRANSPORT.md +20 -15
- data/docs/MCP_SERVERS.md +87 -8
- data/docs/MCP_TOOL_COOKBOOK.md +13 -55
- data/docs/NOTION_INTEGRATION.md +7 -1
- data/docs/PUBLISHED_INDEX.md +6 -0
- data/docs/README.md +6 -1
- data/docs/RETRIEVAL_GUIDE.md +17 -0
- data/docs/SOURCE_FRESHNESS.md +157 -5
- data/docs/TOKEN_BENCHMARK.md +10 -18
- data/docs/TROUBLESHOOTING.md +70 -14
- data/docs/UNBLOCKED_INTEGRATION.md +60 -8
- data/docs/UPGRADING_TO_2.md +153 -38
- data/docs/WATCH_DAEMON.md +97 -14
- data/exe/woods-console-mcp +2 -2
- data/lib/generators/woods/templates/woods.rb.tt +2 -1
- data/lib/tasks/woods.rake +23 -7
- data/lib/tasks/woods_checks.rake +2 -2
- data/lib/woods/agent_configuration/cli.rb +1 -1
- data/lib/woods/agent_configuration/layout.rb +16 -2
- data/lib/woods/agent_configuration/plan.rb +13 -3
- data/lib/woods/agent_configuration/planner_validation.rb +4 -2
- data/lib/woods/agent_configuration/preflight.rb +5 -3
- data/lib/woods/builder.rb +17 -57
- data/lib/woods/cache/cache_middleware.rb +56 -30
- data/lib/woods/chunking/contributor_chunks.rb +119 -0
- data/lib/woods/chunking/semantic_chunker.rb +44 -21
- data/lib/woods/console/connection_manager.rb +56 -3
- data/lib/woods/console/embedded_executor.rb +30 -5
- data/lib/woods/console/rack_middleware.rb +29 -1
- data/lib/woods/dependency_graph.rb +34 -10
- data/lib/woods/embedding/fake.rb +12 -0
- data/lib/woods/embedding/indexer.rb +195 -98
- data/lib/woods/embedding/input_budget.rb +67 -0
- data/lib/woods/embedding/openai.rb +70 -20
- data/lib/woods/embedding/provider.rb +37 -25
- data/lib/woods/embedding/text_preparer.rb +76 -32
- data/lib/woods/embedding/token_counter.rb +18 -81
- data/lib/woods/embedding/vector_configuration.rb +48 -0
- data/lib/woods/extraction_identities.rb +175 -0
- data/lib/woods/extractor.rb +304 -107
- data/lib/woods/extractors/action_cable_extractor.rb +8 -3
- data/lib/woods/extractors/assigned_value_discovery.rb +74 -0
- data/lib/woods/extractors/class_declarations.rb +121 -0
- data/lib/woods/extractors/configuration_extractor.rb +11 -3
- data/lib/woods/extractors/declaration_ancestry.rb +92 -0
- data/lib/woods/extractors/event_extractor.rb +8 -0
- data/lib/woods/extractors/graphql_extractor.rb +134 -77
- data/lib/woods/extractors/job_extractor.rb +5 -1
- data/lib/woods/extractors/lib_extractor.rb +132 -15
- data/lib/woods/extractors/mailer_extractor.rb +3 -5
- data/lib/woods/extractors/manager_extractor.rb +7 -21
- data/lib/woods/extractors/migration_declaration.rb +87 -0
- data/lib/woods/extractors/migration_extractor.rb +5 -39
- data/lib/woods/extractors/phlex_extractor.rb +6 -2
- data/lib/woods/extractors/policy_extractor.rb +9 -5
- data/lib/woods/extractors/poro_extractor.rb +112 -53
- data/lib/woods/extractors/pundit_extractor.rb +11 -6
- data/lib/woods/extractors/scheduled_job_extractor.rb +45 -4
- data/lib/woods/extractors/serializer_extractor.rb +34 -22
- data/lib/woods/extractors/shared_utility_methods.rb +18 -1
- data/lib/woods/extractors/source_nesting.rb +142 -106
- data/lib/woods/extractors/standalone_module_discovery.rb +123 -0
- data/lib/woods/extractors/state_machine_extractor.rb +46 -40
- data/lib/woods/extractors/view_component_extractor.rb +9 -7
- data/lib/woods/flow_assembler.rb +4 -1
- data/lib/woods/generation.rb +25 -0
- data/lib/woods/hooks/context_hint.rb +7 -2
- data/lib/woods/mcp/bootstrapper.rb +33 -7
- data/lib/woods/mcp/config_resolver.rb +26 -7
- data/lib/woods/mcp/index_reader.rb +125 -24
- data/lib/woods/mcp/index_reader_pinning.rb +16 -0
- data/lib/woods/mcp/renderers/markdown_renderer.rb +7 -1
- data/lib/woods/mcp/renderers/plain_renderer.rb +3 -1
- data/lib/woods/mcp/search_results.rb +7 -1
- data/lib/woods/mcp/server.rb +24 -4
- data/lib/woods/module_reconciliation.rb +151 -0
- data/lib/woods/path_dispatcher.rb +7 -2
- data/lib/woods/rake_helpers.rb +43 -11
- data/lib/woods/release.rb +1 -1
- data/lib/woods/resilience/index_validator.rb +8 -3
- data/lib/woods/resilience/retryable_provider.rb +18 -1
- data/lib/woods/resolved_config.rb +68 -8
- data/lib/woods/retrieval/context_assembler.rb +3 -3
- data/lib/woods/retrieval/lexical_assembler.rb +3 -2
- data/lib/woods/retrieval/scope.rb +18 -2
- data/lib/woods/retrieval/source_evidence.rb +14 -2
- data/lib/woods/source_contributor_validation.rb +78 -0
- data/lib/woods/source_contributors.rb +116 -0
- data/lib/woods/source_inputs/handoff.rb +37 -0
- data/lib/woods/source_inputs/launcher.rb +53 -13
- data/lib/woods/source_inputs/manifest.rb +84 -3
- data/lib/woods/source_inputs/private_key.rb +44 -12
- data/lib/woods/source_inputs/scanner.rb +98 -27
- data/lib/woods/source_inputs/scopes.rb +1 -1
- data/lib/woods/source_inputs/session.rb +147 -15
- data/lib/woods/source_inputs/stable_reader.rb +127 -0
- data/lib/woods/source_inputs/status.rb +40 -8
- data/lib/woods/source_inputs/verifier.rb +28 -5
- data/lib/woods/source_path_encoding.rb +33 -0
- data/lib/woods/source_references/cache.rb +284 -0
- data/lib/woods/source_references/collector.rb +120 -0
- data/lib/woods/source_references/extraction.rb +185 -0
- data/lib/woods/source_references/inputs.rb +134 -0
- data/lib/woods/source_references/parser_adapter.rb +134 -0
- data/lib/woods/source_references/pass.rb +152 -0
- data/lib/woods/source_references/prism_adapter.rb +116 -0
- data/lib/woods/source_references/registry.rb +178 -0
- data/lib/woods/source_references/runtime_lookup.rb +127 -0
- data/lib/woods/source_references/value_class.rb +82 -0
- data/lib/woods/storage/metadata_store.rb +4 -1
- data/lib/woods/storage/qdrant.rb +2 -2
- data/lib/woods/unblocked/client.rb +12 -7
- data/lib/woods/unblocked/document_builder.rb +4 -1
- data/lib/woods/unblocked/exporter.rb +127 -37
- data/lib/woods/unblocked/sync_manifest.rb +137 -21
- data/lib/woods/unblocked/uri_migration.rb +105 -0
- data/lib/woods/util/host_guard.rb +3 -2
- data/lib/woods/version.rb +1 -1
- data/lib/woods/watch/catch_up.rb +138 -0
- data/lib/woods/watch/claim_lease.rb +150 -0
- data/lib/woods/watch/cli.rb +26 -2
- data/lib/woods/watch/daemon.rb +80 -59
- data/lib/woods/watch/installation/options.rb +1 -1
- data/lib/woods/watch/installation/receipt.rb +6 -1
- data/lib/woods/watch/managed_child.rb +1 -1
- data/lib/woods/watch/supervisor.rb +1 -1
- data/lib/woods/watch/tree_scan.rb +14 -2
- data/plugin/.claude-plugin/plugin.json +1 -1
- data/plugin/hooks/adapters/normalize.rb +3 -2
- data/plugin/hooks/woods-input-rules.sh +4 -0
- data/plugin/hooks/woods-refresh.sh +15 -7
- data/plugin/hooks/woods-session-start.sh +60 -3
- data/plugin/skills/woods-diagnose/SKILL.md +334 -11
- data/plugin/skills/woods-investigate/SKILL.md +11 -0
- data/plugin/skills/woods-mcp-config/SKILL.md +79 -8
- data/plugin/skills/woods-setup/SKILL.md +53 -6
- metadata +32 -5
|
@@ -25,7 +25,7 @@ module Woods
|
|
|
25
25
|
def initialize(command:, root:, env: ENV.to_h, logger: $stderr, boot_timeout: 300,
|
|
26
26
|
shutdown_timeout: 10, retry_delays: RETRY_DELAYS)
|
|
27
27
|
raise ArgumentError, 'WOODS_WATCH_IDLE_TIMEOUT must be unset for managed watching' unless
|
|
28
|
-
env['WOODS_WATCH_IDLE_TIMEOUT'].to_s.empty?
|
|
28
|
+
env['WOODS_WATCH_IDLE_TIMEOUT'].to_s.strip.empty?
|
|
29
29
|
|
|
30
30
|
@command = command
|
|
31
31
|
@root = File.realpath(root)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'find'
|
|
4
4
|
require 'set'
|
|
5
|
+
require 'woods/source_path_encoding'
|
|
5
6
|
|
|
6
7
|
module Woods
|
|
7
8
|
module Watch
|
|
@@ -48,12 +49,15 @@ module Woods
|
|
|
48
49
|
# @yieldparam path [String] absolute path to a regular file
|
|
49
50
|
# @return [void]
|
|
50
51
|
def each_file(root:, ignored:, visited: nil, &block)
|
|
51
|
-
base = root
|
|
52
|
+
base = SourcePathEncoding.utf8(root)
|
|
53
|
+
return unless base
|
|
54
|
+
|
|
52
55
|
prefix = "#{base}/"
|
|
53
56
|
visited = (visited || Set.new).dup
|
|
54
57
|
visited << real_dir(base)
|
|
55
58
|
|
|
56
59
|
Find.find(base) do |path|
|
|
60
|
+
path = decoded_entry(path)
|
|
57
61
|
next if path == base
|
|
58
62
|
|
|
59
63
|
skip = skip?(path.delete_prefix(prefix), ignored)
|
|
@@ -65,6 +69,11 @@ module Woods
|
|
|
65
69
|
nil
|
|
66
70
|
end
|
|
67
71
|
|
|
72
|
+
# Prune before any string matching or descent into an undecodable entry.
|
|
73
|
+
def decoded_entry(path)
|
|
74
|
+
SourcePathEncoding.utf8(path) || Find.prune
|
|
75
|
+
end
|
|
76
|
+
|
|
68
77
|
# Classify one entry and act on it.
|
|
69
78
|
#
|
|
70
79
|
# @return [Symbol, nil] `:prune` when the caller should not descend
|
|
@@ -126,7 +135,7 @@ module Woods
|
|
|
126
135
|
|
|
127
136
|
# @return [String, nil] resolved path, or nil for a broken/looping link
|
|
128
137
|
def real_dir(path)
|
|
129
|
-
File.realpath(path)
|
|
138
|
+
SourcePathEncoding.utf8(File.realpath(path))
|
|
130
139
|
rescue SystemCallError
|
|
131
140
|
nil
|
|
132
141
|
end
|
|
@@ -147,6 +156,9 @@ module Woods
|
|
|
147
156
|
# @param relative [String] path relative to the watched root
|
|
148
157
|
# @return [Boolean] whether any segment is a dotfile Woods does not read
|
|
149
158
|
def hidden?(relative)
|
|
159
|
+
relative = SourcePathEncoding.utf8(relative)
|
|
160
|
+
return true unless relative
|
|
161
|
+
|
|
150
162
|
relative.split(File::SEPARATOR).any? { |segment| hidden_segment?(segment) }
|
|
151
163
|
end
|
|
152
164
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "woods-plugin",
|
|
3
3
|
"description": "Woods user guides for Claude Code: set up, upgrade, configure MCP servers for, investigate codebases with, enable a repository's agents on, and diagnose the Woods Rails code-intelligence gem.",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.71",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "lost-in-the"
|
|
7
7
|
},
|
|
@@ -55,8 +55,9 @@ end
|
|
|
55
55
|
|
|
56
56
|
if $PROGRAM_NAME == __FILE__
|
|
57
57
|
begin
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
input = $stdin.binmode.read(1_048_577).force_encoding(Encoding::UTF_8)
|
|
59
|
+
puts JSON.generate(WoodsHookEvent.normalize(ARGV.fetch(0), JSON.parse(input)))
|
|
60
|
+
rescue JSON::ParserError, EncodingError, ArgumentError, KeyError, TypeError
|
|
60
61
|
warn '[Woods hooks] Unsupported or malformed edit event; no refresh queued.'
|
|
61
62
|
exit 1
|
|
62
63
|
end
|
|
@@ -111,6 +111,10 @@ woods_input_action() {
|
|
|
111
111
|
case "$operation" in delete|move) printf full ;; *) printf incremental ;; esac
|
|
112
112
|
return
|
|
113
113
|
fi
|
|
114
|
+
if { { [[ "$path" != */tasks/* ]] && [[ "$path" != */generators/* ]] && { [[ "$path" == *.rb ]]; } && { [[ "$path" == lib/* ]]; }; }; }; then
|
|
115
|
+
case "$operation" in delete|move) printf full ;; *) printf incremental ;; esac
|
|
116
|
+
return
|
|
117
|
+
fi
|
|
114
118
|
if { { { [[ "$path" == *.rake ]]; } && { [[ "$path" == lib/tasks/* ]]; }; }; }; then
|
|
115
119
|
case "$operation" in delete|move) printf full ;; *) printf incremental ;; esac
|
|
116
120
|
return
|
|
@@ -21,18 +21,26 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
21
21
|
printf '[Woods hooks] Unsupported or malformed edit event; no refresh queued.\n' >&2; exit 0;
|
|
22
22
|
}
|
|
23
23
|
elif command -v ruby >/dev/null 2>&1; then
|
|
24
|
-
payload="$(ruby "$hook_dir/adapters/normalize.rb" "$client" <"$input_file")" || exit 0
|
|
24
|
+
payload="$(ruby -EUTF-8:UTF-8 "$hook_dir/adapters/normalize.rb" "$client" <"$input_file")" || exit 0
|
|
25
25
|
else
|
|
26
26
|
printf '[Woods hooks] jq or Ruby is required; no refresh queued.\n' >&2
|
|
27
27
|
exit 0
|
|
28
28
|
fi
|
|
29
29
|
rm -f "$input_file"
|
|
30
30
|
trap - EXIT TERM INT
|
|
31
|
+
# JSON and client paths are UTF-8 regardless of the host process locale.
|
|
32
|
+
# Keep malformed queue/input diagnostics bounded and free of event contents.
|
|
33
|
+
ruby_json() {
|
|
34
|
+
ruby -EUTF-8:UTF-8 "$@" 2>/dev/null || {
|
|
35
|
+
printf '[Woods hooks] Invalid UTF-8 or JSON data; refresh not confirmed.\n' >&2
|
|
36
|
+
return 1
|
|
37
|
+
}
|
|
38
|
+
}
|
|
31
39
|
field() {
|
|
32
40
|
if command -v jq >/dev/null 2>&1; then
|
|
33
41
|
printf '%s' "$payload" | jq -ej '.root'
|
|
34
42
|
else
|
|
35
|
-
printf '%s' "$payload" |
|
|
43
|
+
printf '%s' "$payload" | ruby_json -rjson -e 'print JSON.parse($stdin.read).fetch("root")'
|
|
36
44
|
fi
|
|
37
45
|
}
|
|
38
46
|
cwd="$(field; printf .)"
|
|
@@ -50,7 +58,7 @@ event_fields() {
|
|
|
50
58
|
if command -v jq >/dev/null 2>&1; then
|
|
51
59
|
printf '%s' "$payload" | jq -j '.events[] | .operation,"\u0000",.path,"\u0000"'
|
|
52
60
|
else
|
|
53
|
-
printf '%s' "$payload" |
|
|
61
|
+
printf '%s' "$payload" | ruby_json -rjson -e 'JSON.parse($stdin.read).fetch("events").each { |e| print e.fetch("operation"), "\0", e.fetch("path"), "\0" }'
|
|
54
62
|
fi
|
|
55
63
|
}
|
|
56
64
|
relevant=0
|
|
@@ -83,7 +91,7 @@ enqueue() {
|
|
|
83
91
|
if command -v jq >/dev/null 2>&1; then
|
|
84
92
|
jq -nc --arg path "$1" '{path:$path,operation:"update"}' >"$event.tmp" || return 1
|
|
85
93
|
else
|
|
86
|
-
|
|
94
|
+
ruby_json -rjson -e 'puts JSON.generate(path: ARGV[0], operation: "update")' -- "$1" >"$event.tmp" || return 1
|
|
87
95
|
fi
|
|
88
96
|
mv "$event.tmp" "$event.json"
|
|
89
97
|
}
|
|
@@ -92,7 +100,7 @@ event="$queue/$$.$RANDOM.$RANDOM"
|
|
|
92
100
|
if command -v jq >/dev/null 2>&1; then
|
|
93
101
|
printf '%s' "$payload" | jq -c --arg root "$cwd" '.events | map(.path |= (if startswith($root + "/") then ltrimstr($root + "/") else . end))' >"$event.tmp" || exit 0
|
|
94
102
|
else
|
|
95
|
-
printf '%s' "$payload" |
|
|
103
|
+
printf '%s' "$payload" | ruby_json -rjson -e 'v = JSON.parse($stdin.read); puts JSON.generate(v.fetch("events").map { |e| e.merge("path" => e.fetch("path").delete_prefix(ARGV.fetch(0) + "/")) })' -- "$cwd" >"$event.tmp" || exit 0
|
|
96
104
|
fi
|
|
97
105
|
mv "$event.tmp" "$event.json" || exit 0
|
|
98
106
|
|
|
@@ -144,7 +152,7 @@ encode_batch() {
|
|
|
144
152
|
if command -v jq >/dev/null 2>&1; then
|
|
145
153
|
jq -sc --arg output "$configured_output" '{version:1,output:$output,events:(map(if type == "array" then . else [.] end) | add)} | @base64' "${batch[@]}" | tr -d '"\n'
|
|
146
154
|
else
|
|
147
|
-
|
|
155
|
+
ruby_json -rjson -rbase64 -e '
|
|
148
156
|
output = ARGV.shift
|
|
149
157
|
print Base64.strict_encode64(JSON.generate(version: 1, output: output,
|
|
150
158
|
events: ARGV.flat_map { |path| value = JSON.parse(File.read(path)); value.is_a?(Array) ? value : [value] }))
|
|
@@ -237,7 +245,7 @@ while :; do
|
|
|
237
245
|
if command -v jq >/dev/null 2>&1; then
|
|
238
246
|
event_count="$(jq 'if type == "array" then length else 1 end' "$candidate")" || break
|
|
239
247
|
else
|
|
240
|
-
event_count="$(
|
|
248
|
+
event_count="$(ruby_json -rjson -e 'v = JSON.parse(File.read(ARGV[0])); puts(v.is_a?(Array) ? v.size : 1)' "$candidate")" || break
|
|
241
249
|
fi
|
|
242
250
|
[ "$((batch_events + event_count))" -le 1000 ] || break
|
|
243
251
|
[ "$((batch_bytes + event_bytes))" -le 49152 ] || break
|
|
@@ -11,9 +11,59 @@ field() {
|
|
|
11
11
|
if command -v jq >/dev/null 2>&1; then
|
|
12
12
|
printf '%s' "$1" | jq -r --arg name "$2" '.[$name] // empty' 2>/dev/null
|
|
13
13
|
elif command -v ruby >/dev/null 2>&1; then
|
|
14
|
-
printf '%s' "$1" | ruby -rjson -e 'print JSON.parse($stdin.read).fetch(ARGV[0], "")' -- "$2" 2>/dev/null
|
|
14
|
+
printf '%s' "$1" | ruby -EUTF-8:UTF-8 -rjson -e 'print JSON.parse($stdin.read).fetch(ARGV[0], "")' -- "$2" 2>/dev/null
|
|
15
15
|
fi
|
|
16
16
|
}
|
|
17
|
+
unavailable_summary() {
|
|
18
|
+
if command -v jq >/dev/null 2>&1; then
|
|
19
|
+
printf '%s' "$1" | jq -r '.unavailable | select(type == "object") |
|
|
20
|
+
select(.reason == "source_manifest_too_large" and (.size_bytes | type) == "number" and
|
|
21
|
+
(.limit_bytes | type) == "number") |
|
|
22
|
+
select(.size_bytes > .limit_bytes and .limit_bytes > 0) |
|
|
23
|
+
"source_manifest_too_large; \(.size_bytes) bytes; limit \(.limit_bytes)"' 2>/dev/null
|
|
24
|
+
elif command -v ruby >/dev/null 2>&1; then
|
|
25
|
+
printf '%s' "$1" | ruby -EUTF-8:UTF-8 -rjson -e '
|
|
26
|
+
value = JSON.parse($stdin.read)["unavailable"]
|
|
27
|
+
if value.is_a?(Hash) && value["reason"] == "source_manifest_too_large"
|
|
28
|
+
size, limit = value.values_at("size_bytes", "limit_bytes")
|
|
29
|
+
if [size, limit].all?(Integer) && size > limit && limit > 0
|
|
30
|
+
print "source_manifest_too_large; #{size} bytes; limit #{limit}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
' 2>/dev/null
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
source_advice() {
|
|
37
|
+
if command -v jq >/dev/null 2>&1; then
|
|
38
|
+
printf '%s' "$1" | jq -r 'if (.recommendations | type) == "array" then
|
|
39
|
+
.recommendations[] | select(. == "deep_check" or . == "fresh_capture" or . == "inspect_source_scan")
|
|
40
|
+
else empty end' 2>/dev/null
|
|
41
|
+
elif command -v ruby >/dev/null 2>&1; then
|
|
42
|
+
printf '%s' "$1" | ruby -EUTF-8:UTF-8 -rjson -e '
|
|
43
|
+
values = JSON.parse($stdin.read)["recommendations"]
|
|
44
|
+
allowed = %w[deep_check fresh_capture inspect_source_scan]
|
|
45
|
+
puts values.select { |value| allowed.include?(value) } if values.is_a?(Array)
|
|
46
|
+
' 2>/dev/null
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
unknown_message() {
|
|
50
|
+
local advice action
|
|
51
|
+
advice="$(source_advice "$1")"
|
|
52
|
+
if [ -z "$advice" ]; then
|
|
53
|
+
echo 'Woods source freshness is unknown: this generation lacks complete verified source evidence. Inspect woods_status; use woods-extract full for a fresh capture.'
|
|
54
|
+
return
|
|
55
|
+
fi
|
|
56
|
+
printf '%s' 'Woods source freshness is unknown: this check could not verify the current source contents.'
|
|
57
|
+
# Only the three literal identifiers above reach this word split.
|
|
58
|
+
for action in $advice; do
|
|
59
|
+
case "$action" in
|
|
60
|
+
deep_check) printf '%s' ' Request woods_status with source_check: deep; the quick reader reached its time limit.' ;;
|
|
61
|
+
fresh_capture) printf '%s' ' Fix the capture or boot evidence reported by woods_status, then run woods-extract full in the application environment.' ;;
|
|
62
|
+
inspect_source_scan) printf '%s' ' Inspect woods_status verification_reasons, source mapping, permissions and scan limits.' ;;
|
|
63
|
+
esac
|
|
64
|
+
done
|
|
65
|
+
printf '\n'
|
|
66
|
+
}
|
|
17
67
|
cwd="$(field "$payload" cwd)"
|
|
18
68
|
[ -n "$cwd" ] && [ -d "$cwd" ] || exit 0
|
|
19
69
|
configured_output="${WOODS_OUTPUT:-tmp/woods}"
|
|
@@ -25,7 +75,10 @@ esac
|
|
|
25
75
|
if command -v jq >/dev/null 2>&1; then
|
|
26
76
|
encoded="$(jq -nr --arg output "$configured_output" '{output:$output,mode:"quick"} | @base64')"
|
|
27
77
|
elif command -v ruby >/dev/null 2>&1; then
|
|
28
|
-
encoded="$(ruby -rjson -rbase64 -e 'print Base64.strict_encode64(JSON.generate(output: ARGV[0], mode: "quick"))' -- "$configured_output")"
|
|
78
|
+
encoded="$(ruby -EUTF-8:UTF-8 -rjson -rbase64 -e 'print Base64.strict_encode64(JSON.generate(output: ARGV[0], mode: "quick"))' -- "$configured_output" 2>/dev/null)" || {
|
|
79
|
+
echo 'Woods source freshness is unknown: hook options could not be encoded. Inspect woods_status.'
|
|
80
|
+
exit 0
|
|
81
|
+
}
|
|
29
82
|
else
|
|
30
83
|
exit 0
|
|
31
84
|
fi
|
|
@@ -63,7 +116,11 @@ status="$(tail -n 1 "$record")"
|
|
|
63
116
|
state="$(field "$status" state)"
|
|
64
117
|
case "$state" in
|
|
65
118
|
current) ;;
|
|
119
|
+
unavailable)
|
|
120
|
+
detail="$(unavailable_summary "$status")"
|
|
121
|
+
printf 'Woods source freshness is unavailable: %s. The code index is usable; inspect woods_status. Repeating a full extraction will not reduce this size.\n' "${detail:-inspect woods_status for the unavailable evidence reason}"
|
|
122
|
+
;;
|
|
66
123
|
drifted) echo 'Woods source freshness is drifted: indexed application inputs differ from the working tree. Run woods-extract full, or inspect woods_status before choosing a targeted refresh.' ;;
|
|
67
|
-
*)
|
|
124
|
+
*) unknown_message "$status" ;;
|
|
68
125
|
esac
|
|
69
126
|
exit 0
|