Repository navigation
Skill should use CLI subcommands instead of inline python -c #197
Description
Activity
Previous issue #114 does not solve all of them.
Fixed in #4275 — and here's exactly what was wrong, how it's fixed, and what's deliberately left out.
Why #114 didn't cover this
#114 replaced one block — the inline
save_query_resultPython — withgraphify save-result(v0.3.18), then closed. Every other pipeline step and every on-demand reference flow kept its inline Python. biggates' follow-up comment in #114 flagged exactly that ("there are still a lot of other raw python scripts in skills… are you going to do all of them?"), but it was never turned into work — which is what this issue tracks.Root cause
Each step ran as:
$(cat graphify-out/.graphify_python) -c " import json from graphify.detect import detect # ... 10-20 lines of Python with # comments "
Two Claude Code heuristics fire on that, once per step, and neither is allowlistable:
command_substitution(the$(cat …)) and "newline followed by#in a quoted argument" (the Python comments). ~30 manual approvals per run.How it's fixed
Every step is now a plain console command. The step bodies moved into a new
graphify/pipeline.pyand are exposed asgraphify pipeline <step>—detect,extract-ast,empty-semantic,cache-check,merge-chunks,save-cache,merge-semantic,merge-extraction,build,diagnose,label,save-manifest,cleanup, plusvocab,transcribe,detect-incremental,code-only-check,empty-extract,update-merge,graph-difffor the reference flows. The references now call those plus existing commands (graphify query/path/explain/save-result/add/watch) and thegraphify-mcpconsole script.Result: no
$(cat graphify-out/.graphify_python), no inlinepython -c, andBash(graphify *)is now allowlistable — so the ~30 prompts per run are gone and the injection surface shrinks (arguments go through argparse instead of string interpolation).The skill is generated from
tools/skillgen/fragments/; the edits are in the fragments and all generated artifacts were regenerated + blessed, so nothing drifts.Verified
- Old vs new produce byte-identical
.graphify_*sidecars,graph.json,manifest.json, andGRAPH_REPORT.mdon a real corpus — same graph, different delivery. - Full test suite: no new failures vs the pre-change baseline.
- New tests cover the pipeline steps end to end (including the incremental
--updateflow and the#479shrink guard) and a drift guard that fails if any split host or reference reintroduces an inline-cblock. - Reviewed twice by a separate agent; all findings fixed.
What's intentionally not in this PR
skill-aider.mdandskill-devin.mdstill contain inline Python (37 and 39 blocks). That's not Claude Code — those are the Aider and Devin hosts, which #197 doesn't report. They're also a genuinely wide change: unlike the split hosts, they aren't generated fromcore.md+ references — each is a hand-maintained ~1,300–1,400-line monolith that inlines everything, and they're frozen against a pinned v8 blob bymonolith_roundtrip, which requires every changed line to match a sanctioned-diff predicate. Converting them means rewriting 76 blocks and adding a new sanctioned-diff predicate and re-validating the round-trip. That's a clean follow-up PR on its own; folding it in would have ballooned the blast radius of this one.So: #197 (Claude Code) is fully resolved; the aider/devin monoliths are the one tracked follow-up. Step 1's one-time interpreter detection still uses
$(…)— that's inherent to installing/detecting graphify, and it's a single block, not per-step.- Old vs new produce byte-identical
- added a commit that references this issue
on Oct 10, 2026
Problem
The Claude Code skill (
skill.md) passes all pipeline steps as inline Python viapython -c "...". This triggers multiple security heuristics in Claude Code:$(cat graphify-out/.graphify_python)to resolve the interpreter triggers "Contains command_substitution" prompts#comments triggers "Newline followed by # inside a quoted argument can hide arguments from path validation"These prompts cannot be auto-accepted via Claude Code's permission allow-rules, so users must click "Yes" on every single step (~30 times per pipeline run). This makes the skill nearly unusable without manually patching the skill file.
Root cause
The skill uses Bash as a code delivery mechanism. Every step is:
This is the wrong abstraction layer. The Python code should live behind CLI entry points, not inside shell strings.
What already works
graphify querywas added as a CLI subcommand in v0.3.8 (#43), andgraphify hook,graphify save-result,graphify claudealso exist as CLI commands. But the skill doesn't usegraphify query— it still constructs inline Python for queries.Proposed fix
Expose the remaining pipeline steps as CLI subcommands:
Then update
skill.mdto call these instead of inline Python. Benefits:graphify detect .is a normal command, no heuristics triggeredBash(graphify *)which is constrained to what the CLI exposes, not arbitrary Python executionWorkarounds users are currently doing
skill.mdlocally to replace$(cat graphify-out/.graphify_python)with the literal path (avoids v3: semantic query with embeddings #1 but not v3: Codex and OpenClaw support #2)Environment