Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions plugins/axiomcode/skills/axiomcode/scripts/ax_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

CAP = 10 # places shown; the rest are counted
FAR = 8 # places more than one hop away, named without code
DIRECT_CODE = 3 # direct callers shown with code even when a word grep also finds them
PLAIN_WHY = ('calls it', 'reads it', 'writes it', 'writes/reads it', 'references it', 'instantiates it')
WHOLE = 14 # a function this short is shown whole
AROUND = 3 # else: its header, then this many lines either side of the line that matters
FENCE = {'.py': 'python', '.java': 'java', '.ts': 'typescript', '.tsx': 'tsx', '.js': 'javascript', '.jsx': 'jsx',
Expand Down Expand Up @@ -107,10 +109,27 @@ def render(verb, doc, repo):
plain = []
names = target_names(doc) if verb == 'impact' and os.environ.get('AXIOMCODE_GREP_AID', '1').lower() not in ('0', 'off', 'false') else []
if names:
spelled = lambda p: all(any(re.search(r'(?<![\w$])' + re.escape(nm) + r'(?![\w$])', statement(repo, p['f'], n)) for nm in names)
for n in p['marks'])
plain = [p for p in places.values() if spelled(p)]
places = {k: p for k, p in places.items() if not spelled(p)}
# WHY each direct place depends on it: a plain resolved call, read or write is what a word grep finds too; a call
# across a process boundary, an override or implementation, an injection or a call through a field holding it is
# a link grep cannot make, so it keeps its code and says what it is
why = {}
for r in doc.get('direct') or []:
for x in [r] + list(r.get('reasons') or []):
if x.get('at'): why.setdefault(x['at'], (x.get('why') or r.get('why') or '', x.get('certainty') or r.get('certainty') or 'resolved'))
for p in places.values():
for n in p['marks']:
w = why.get(f"{p['f']}:{n}")
if w and w[0] and w[0] not in PLAIN_WHY and w[0].split(' — ')[0].split(' (')[0] not in p['tags']: p['tags'].insert(0, w[0].split(' — ')[0].split(' (')[0])
def greppable(p):
ws = [why.get(f"{p['f']}:{n}") for n in p['marks']]
return all(w and w[0] in PLAIN_WHY and w[1] in ('resolved', 'sound') for w in ws) and \
all(any(re.search(r'(?<![\w$])' + re.escape(nm) + r'(?![\w$])', statement(repo, p['f'], n)) for nm in names) for n in p['marks'])
# the first few direct callers keep their code even when grep finds them: they are what an agent checks first
keep = 0
for k, p in list(places.items()):
if greppable(p):
if keep < DIRECT_CODE: keep += 1; continue
plain.append(p); del places[k]
# A PLACE FURTHER THAN ONE HOP AWAY IS NAMED, NOT SHOWN: no text search finds it, so it stays in the answer, but the
# function it sits in says enough; code goes to the direct places the agent will actually edit or check
far, tests = [], []
Expand All @@ -130,7 +149,7 @@ def render(verb, doc, repo):
out += [' ' + b for b in body]
out.append(' ```')
if far:
out.append(f"further away ({len(far)} place(s), reached through the ones above; no code shown):")
out.append(f"further away ({len(far)} place(s), reached through {'the places above' if places or plain else 'its callers'}; no code shown):")
for p in far[:FAR]:
fn = (p['span'][0] if p['span'] else '?')
out.append(f" {p['f']}:{','.join(map(str, sorted(p['marks'])))} {fn} [{p['tags'][0]}]")
Expand All @@ -143,7 +162,7 @@ def render(verb, doc, repo):
if plain:
refs = [f"{p['f']}:{','.join(map(str, sorted(p['marks'])))}" for p in plain]
g = ' -e '.join(names)
out.append(f"{'' if out else 'every place spells the name, so grep finds them all — '}+{len(plain)} place(s) `grep -nw {g}` also finds "
out.append(f"+{len(plain)} more direct caller(s) `grep -nw {g}` also finds "
f"(confirmed callers; no code shown): " + ', '.join(refs[:8]) + (f" +{len(refs) - 8}" if len(refs) > 8 else ''))
other = grep_others(repo, names, {(p['f'], n) for p in plain for n in p['marks']})
if other: out.append(f" {other} other line(s) grep matches for that name are NOT this declaration (another symbol of the same name, or text)")
Expand Down
8 changes: 4 additions & 4 deletions tests/front_door.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ def main():
rc, out, err = cli(repo, 'find', 'how is the invoice total computed')
check('find: numbered places, each with its code in a fenced block', rc == 0 and places(out) and 'def invoice' in out, out[:600] + err[-300:])
rc, out, err = cli(repo, 'impact', 'vat_rate')
check('impact <name>: its caller, which spells the name, by location on the grep line (no code)', rc == 0 and 'grep -nw vat_rate' in out and 'shop/pricing.py:6' in out
and 'return net * (1 + vat_rate())' not in out, out[:600] + err[-300:])
check('impact <name>: its direct caller as a numbered place with its code', rc == 0 and places(out) and 'shop/pricing.py:6' in out
and 'return net * (1 + vat_rate())' in out, out[:600] + err[-300:])
check('impact <name>: the test that reaches it is one of the places', 'tests/test_pricing.py' in out, out[:800])
rc, out, err = cli(repo, 'path', 'invoice', 'vat_rate')
check('path: every hop a numbered place with the code at the call', rc == 0 and places(out)
Expand All @@ -111,7 +111,7 @@ def main():
first = out.lstrip().split('\n', 1)[0]
check('impact with no name: the answer starts with "your edits:" and names the edited declaration',
rc == 0 and first.startswith('your edits:') and 'vat_rate' in first, out[:600] + err[-300:])
check('impact with no name: then what the edit reaches, its grep-visible caller by location', 'shop/pricing.py:6' in out, out[:600])
check('impact with no name: then what the edit reaches, as places with their code', places(out) and 'shop/pricing.py:6' in out, out[:600])
rc, out, err = cli(repo, 'tests')
last = [l for l in out.splitlines() if l.strip()][-1:] or ['']
check('tests: the reached test as a numbered place with its code', rc == 0 and places(out) and 'tests/test_pricing.py' in out, out[:600] + err[-300:])
Expand All @@ -124,7 +124,7 @@ def main():
check('MCP: every tool takes at most two parameters', bool(tools) and all(len(p) <= 2 for p in tools.values()), tools)
text = lambda i: ''.join(c.get('text', '') for c in got.get(i, {}).get('content', []))
check('MCP path answers as numbered places with their code', places(text(3)), text(3)[:600])
check('MCP impact names the caller grep also finds, by location', 'shop/pricing.py:6' in text(4) and 'grep -nw vat_rate' in text(4), text(4)[:600])
check('MCP impact answers as numbered places with their code', places(text(4)) and 'shop/pricing.py:6' in text(4), text(4)[:600])

# ── c. controls: the same question anywhere else gets the verb's own answer ──────────────────────────────────
r = subprocess.run(['bash', AX, 'impact', 'vat_rate', repo], cwd=repo, capture_output=True, text=True, timeout=600, env=ENV)
Expand Down
Loading