Skip to content

Commit 9c35f5e

Browse files
amichneCopilot
andcommitted
Fix wrapper validator portability
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 89a051f commit 9c35f5e

7 files changed

Lines changed: 69 additions & 69 deletions

File tree

.agents/skills/kast/SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ marker as proof that the result is bounded.
209209

210210
After any code change, run `kast-diagnostics.sh` on the modified files. When
211211
you need a quick contract check for the wrappers themselves, run
212-
`validate-wrapper-json.sh`.
212+
`validate-wrapper-json.sh`. It creates a temporary sample Kotlin workspace and
213+
checks that each wrapper still emits valid JSON on both success and failure
214+
paths.
213215

214216
```bash
215217
bash "$SKILL_ROOT/scripts/validate-wrapper-json.sh" \

.agents/skills/kast/references/command-reference.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -910,9 +910,10 @@ bash "$SKILL_ROOT/scripts/validate-wrapper-json.sh" \
910910
/absolute/path/to/workspace
911911
```
912912

913-
The script discovers a sample Kotlin declaration from the workspace, runs the
914-
wrappers against that workspace, and emits aggregated validation JSON on
915-
stdout.
913+
The script creates a temporary sample Kotlin workspace, runs the wrappers
914+
against that workspace, and emits aggregated validation JSON on stdout. If you
915+
want `resolve-kast.sh` to prefer a local checkout, pass that source root as the
916+
positional argument.
916917

917918
---
918919

.agents/skills/kast/scripts/kast-callers.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ fi
110110
CALLERS_RESULT="${TMP_DIR}/callers.json"
111111
if ! kast_run_json \
112112
"${CALLERS_RESULT}" \
113-
"${KAST}" call hierarchy \
113+
"${KAST}" call-hierarchy \
114114
--workspace-root="${WORKSPACE_ROOT}" \
115115
--file-path="${RESOLVED_FILE_PATH}" \
116116
--offset="${RESOLVED_OFFSET}" \
117117
--direction="${DIRECTION}" \
118118
--depth="${DEPTH}"; then
119-
emit_failure "call_hierarchy" "kast call hierarchy failed." "${CALLERS_RESULT}"
119+
emit_failure "call_hierarchy" "kast call-hierarchy failed." "${CALLERS_RESULT}"
120120
exit 1
121121
fi
122122

.agents/skills/kast/scripts/kast-common.sh

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,32 +99,45 @@ kast_collect_candidate_files() {
9999
local kind="${4:-}"
100100
local candidate_file="${TMP_DIR}/candidate-files.raw"
101101
local regex
102-
local status
103-
104102
regex="$(kast_build_search_regex "${symbol}" "${kind}")"
105103
: > "${candidate_file}"
106104

107-
if (cd "${workspace_root}" && rg -l --glob '*.kt' -e "${regex}" . >"${candidate_file}") 2>>"${LOG_FILE}"; then
108-
:
109-
else
110-
status=$?
111-
if [[ "${status}" -gt 1 ]]; then
112-
printf 'rg declaration search failed with exit status %s\n' "${status}" >>"${LOG_FILE}"
113-
fi
114-
fi
105+
python3 - "${workspace_root}" "${regex}" "${symbol}" "${candidate_file}" <<'PY' 2>>"${LOG_FILE}"
106+
import re
107+
import sys
108+
from pathlib import Path
115109
116-
if [[ ! -s "${candidate_file}" ]]; then
117-
printf 'No declaration-pattern candidates found for %s; widening to plain symbol search.\n' \
118-
"${symbol}" >>"${LOG_FILE}"
119-
if (cd "${workspace_root}" && rg -l -F --glob '*.kt' -- "${symbol}" . >"${candidate_file}") 2>>"${LOG_FILE}"; then
120-
:
121-
else
122-
status=$?
123-
if [[ "${status}" -gt 1 ]]; then
124-
printf 'rg fallback search failed with exit status %s\n' "${status}" >>"${LOG_FILE}"
125-
fi
126-
fi
127-
fi
110+
workspace_root = Path(sys.argv[1]).resolve()
111+
regex = re.compile(sys.argv[2], re.MULTILINE)
112+
symbol = sys.argv[3]
113+
candidate_file = Path(sys.argv[4])
114+
115+
def matching_files(predicate):
116+
matches = []
117+
for path in sorted(workspace_root.rglob("*.kt")):
118+
if not path.is_file():
119+
continue
120+
try:
121+
content = path.read_text(encoding="utf-8")
122+
except (OSError, UnicodeDecodeError):
123+
continue
124+
if predicate(content):
125+
matches.append(str(path))
126+
return matches
127+
128+
matches = matching_files(lambda content: bool(regex.search(content)))
129+
if not matches:
130+
print(
131+
f"No declaration-pattern candidates found for {symbol}; widening to plain symbol search.",
132+
file=sys.stderr,
133+
)
134+
matches = matching_files(lambda content: symbol in content)
135+
136+
candidate_file.write_text(
137+
"".join(f"{path}\n" for path in matches),
138+
encoding="utf-8",
139+
)
140+
PY
128141

129142
python3 - "${workspace_root}" "${file_hint}" "${candidate_file}" <<'PY'
130143
from fnmatch import fnmatchcase

.agents/skills/kast/scripts/kast-impact.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@ if [[ "${INCLUDE_CALLERS}" == "true" ]]; then
115115
CALLERS_RESULT="${TMP_DIR}/callers.json"
116116
if ! kast_run_json \
117117
"${CALLERS_RESULT}" \
118-
"${KAST}" call hierarchy \
118+
"${KAST}" call-hierarchy \
119119
--workspace-root="${WORKSPACE_ROOT}" \
120120
--file-path="${RESOLVED_FILE_PATH}" \
121121
--offset="${RESOLVED_OFFSET}" \
122122
--direction=incoming \
123123
--depth=2; then
124-
emit_failure "call_hierarchy" "kast call hierarchy failed." "${CALLERS_RESULT}"
124+
emit_failure "call_hierarchy" "kast call-hierarchy failed." "${CALLERS_RESULT}"
125125
exit 1
126126
fi
127127
fi

.agents/skills/kast/scripts/validate-wrapper-json.sh

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,29 @@
22
set -euo pipefail
33

44
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
5-
WORKSPACE_ROOT="${1:-$(git -C "${SCRIPT_DIR}" rev-parse --show-toplevel)}"
5+
REQUEST_ROOT="${1:-$(git -C "${SCRIPT_DIR}" rev-parse --show-toplevel)}"
66
TMP_DIR="$(mktemp -d)"
77
trap 'rm -rf "${TMP_DIR}"' EXIT
88

9-
DISCOVERY_TEXT="${TMP_DIR}/discovery.txt"
10-
if ! rg -n --glob '*.kt' '^\s*(class|object|interface)\s+[A-Za-z_][A-Za-z0-9_]*' \
11-
"${WORKSPACE_ROOT}" >"${DISCOVERY_TEXT}"; then
12-
python3 - "${DISCOVERY_TEXT}" <<'PY'
13-
import json
14-
import sys
15-
from pathlib import Path
9+
WORKSPACE_ROOT="${TMP_DIR}/workspace"
10+
mkdir -p "${WORKSPACE_ROOT}/src/main/kotlin/sample"
1611

17-
log_path = Path(sys.argv[1])
18-
payload = {
19-
"ok": False,
20-
"stage": "discovery",
21-
"message": "Could not discover a Kotlin class, object, or interface in the workspace.",
22-
"log_file": str(log_path),
23-
}
24-
print(json.dumps(payload, indent=2))
25-
PY
26-
exit 1
27-
fi
12+
cat >"${WORKSPACE_ROOT}/src/main/kotlin/sample/Greeter.kt" <<'EOF'
13+
package sample
2814
29-
read -r SAMPLE_SYMBOL SAMPLE_FILE <<<"$(
30-
python3 - "${DISCOVERY_TEXT}" <<'PY'
31-
import re
32-
import sys
33-
from pathlib import Path
15+
fun greet(name: String): String = "hi $name"
16+
EOF
3417

35-
pattern = re.compile(r"^(.*?):\d+:\s*(?:class|object|interface)\s+([A-Za-z_][A-Za-z0-9_]*)")
36-
for line in Path(sys.argv[1]).read_text(encoding="utf-8").splitlines():
37-
match = pattern.match(line)
38-
if match:
39-
print(match.group(2), match.group(1))
40-
break
41-
else:
42-
raise SystemExit("No sample symbol found in discovery output.")
43-
PY
44-
)"
18+
cat >"${WORKSPACE_ROOT}/src/main/kotlin/sample/UseGreeter.kt" <<'EOF'
19+
package sample
20+
21+
fun greetTwice(): String = greet("kast") + greet("again")
22+
EOF
23+
24+
export KAST_SOURCE_ROOT="${REQUEST_ROOT}"
4525

26+
SAMPLE_SYMBOL="greet"
27+
SAMPLE_FILE="${WORKSPACE_ROOT}/src/main/kotlin/sample/Greeter.kt"
4628
MISSING_SYMBOL="DefinitelyMissingSymbolForWrapperValidation"
4729
SUCCESS_DIAGNOSTICS_FILE="${SAMPLE_FILE}"
4830
FAILURE_DIAGNOSTICS_FILE="${WORKSPACE_ROOT}/definitely-missing-file.kt"
@@ -106,7 +88,7 @@ print(json.dumps(entry))
10688
PY
10789
done
10890

109-
python3 - "${RESULTS_FILE}" "${WORKSPACE_ROOT}" "${SAMPLE_SYMBOL}" "${SAMPLE_FILE}" <<'PY'
91+
python3 - "${RESULTS_FILE}" "${REQUEST_ROOT}" "${WORKSPACE_ROOT}" "${SAMPLE_SYMBOL}" "${SAMPLE_FILE}" <<'PY'
11092
import json
11193
import sys
11294
from pathlib import Path
@@ -115,9 +97,10 @@ results = [json.loads(line) for line in Path(sys.argv[1]).read_text(encoding="ut
11597
ok = all(item.get("valid_json") and item.get("matches_expectation") for item in results)
11698
payload = {
11799
"ok": ok,
118-
"workspace_root": sys.argv[2],
119-
"sample_symbol": sys.argv[3],
120-
"sample_file": sys.argv[4],
100+
"request_root": sys.argv[2],
101+
"workspace_root": sys.argv[3],
102+
"sample_symbol": sys.argv[4],
103+
"sample_file": sys.argv[5],
121104
"checks": results,
122105
}
123106
print(json.dumps(payload, indent=2))

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ jobs:
7171
run: |
7272
set -euo pipefail
7373
dist_dir="$RUNNER_TEMP/kast-dist"
74-
bash .agents/skills/kast/scripts/validate-wrapper-json.sh "$dist_dir/kast"
74+
KAST_CLI_PATH="$dist_dir/kast/kast" \
75+
bash .agents/skills/kast/scripts/validate-wrapper-json.sh "$PWD"
7576
7677
- name: Smoke installer
7778
shell: bash

0 commit comments

Comments
 (0)