Skip to content

🧰 bin/ — codemap-py runtime executables

This directory contains the installed runtime surface for codemap-py. The canonical user entry point is codemap-py; the scan-index and scan-query files remain compatibility launchers. The importable implementation lives under src/codemap_py.

All Python executables require Python 3.10 syntax at the source level, while the public codemap-py dispatcher and its POSIX/Windows launchers require CPython >=3.11,<3.15. The dispatcher probes an eligible interpreter before importing the package. Set CODEMAP_PYTHON to an eligible executable when PATH discovery is insufficient; an invalid override is authoritative and does not fall through to another interpreter.

The importable implementation lives under src/codemap_py; these files are thin launchers, deterministic path/argument transforms, or session-scoped helpers. setup_scan_env.sh is the only remaining shell file and is a deprecated exec shim for setup_scan_env.py, removed no earlier than 1.0.0 — the same window the scan-index/scan-query aliases carry. New callers use the Python entrypoint so the same behavior works on Windows.

Complexity gate. The repository-root pyproject.toml enables C901 plus PLR0911/PLR0912/PLR0915 at the standard limits (cyclomatic complexity ≤12, branches ≤12, statements ≤50, return points ≤6) and scopes enforcement to this plugin with a negated per-file-ignore (!plugins/codemap-py/**). Findings that predate the gate are listed as per-file-ignores with their counts — accepted debt, not a licence to add more. Re-measure after touching any listed file:

ruff check --select C901,PLR0911,PLR0912,PLR0915
Contents - [User-facing launchers](#-user-facing-launchers) - [Maintenance and analysis helpers](#-maintenance-and-analysis-helpers) - [Internal compatibility shims](#-internal-compatibility-shims) - [Portability and safety](#-portability-and-safety)

🧰 User-facing launchers

codemap-py and codemap-py.cmd

codemap-py is the POSIX launcher; codemap-py.cmd is its Windows batch equivalent. Both forward arguments to scripts/codemap_py_entry.py, which dispatches index, query, doctor, and integrate without shell-command mode or dependency installation.

codemap-py index [--root PATH] [--incremental] [--with-coverage PATH] [--timeout N]
codemap-py query [global flags] <subcommand> ...
codemap-py doctor [--json]
codemap-py integrate {audit,plan,apply,sync,demo} ...

The dispatcher returns 127 when no eligible CPython is found, 2 for invalid top-level syntax, and the underlying command's documented result for valid index, query, or integrate requests. doctor --json reports the selected interpreter, plugin root, support status, and resolved index path.

scan-index

Thin compatibility launcher over codemap_py.graph.main. It discovers .py and .pyi files, parses them with ast.parse, builds import/call/test/mock/fixture/subprocess and documentation-reference metadata, and writes .cache/codemap/<project>.json or the flat $CODEMAP_INDEX_DIR/<project>.json override.

scan-index [--root PATH] [--incremental] [--with-coverage PATH] [--timeout N]

Use codemap-py index in new scripts. --incremental requires an existing v3-or-newer index; --timeout uses SIGALRM where the host provides it.

scan-query

Thin compatibility launcher over codemap_py.query.main. It reads an existing index and emits JSON for module, symbol, call-graph, test-impact, fixture/mock/subprocess, documentation, coverage, dead-code, diff-impact, and batch queries. Run scan-query --help for the authoritative roster and flags.

scan-query rdeps mypackage.auth
scan-query fn-rdeps 'mypackage.auth::check' --exclude-tests

The canonical dispatcher calls the query engine in-process; this file remains for direct invocation and compatibility. Both paths use the shared read/write gate in the engine.

check-index-currency

Checks whether an index still matches the source tree. Git repositories use the stored Git identity plus dirty-file checks where possible; non-Git or older indexes fall back to stored file hashes. It reads the index under the shared read lease and prefers a false stale result to silently trusting uncertain state.

check-index-currency --index-path PATH [--root PATH] [--field NAME]

It emits {"status":"current"|"stale"|"no_index", ...}. Exit 0 means current, 1 stale, 2 missing/unreadable or unavailable index, and 3 argument error.

Launcher contracts and compatibility details `codemap-py` is the canonical dispatcher on POSIX and Windows. The POSIX launcher probes an explicit `CODEMAP_PYTHON` first and then eligible PATH interpreters; `codemap-py.cmd` performs the equivalent Windows probe through `py -3`, `python.exe`, and `python3.exe`. Both forward arguments unchanged to `scripts/codemap_py_entry.py`, which checks the supported CPython range before importing the package. `scan-index` and `scan-query` are compatibility launchers for direct callers and older skill paths. New scripts should use `codemap-py index` and `codemap-py query`; the aliases still share the package's read/write gate and return the same structured exit-code contract. `scan-index --incremental` requires an existing v3-or-newer index, and its `--timeout` option is Unix-specific because it uses `SIGALRM`. `check-index-currency` is a cheap preflight: in a Git repository it compares the stored Git identity and dirty tracked Python files, while non-Git or older indexes fall back to content hashes. It prefers a false stale answer to silently trusting uncertain state. Use it before treating a query list as complete, not as a replacement for the query's own coverage block.

🧰 Maintenance and analysis helpers

Helper inventory These helpers are called by skills or maintainer workflows rather than ordinary project queries. They are listed here so installed-path debugging has one reference: | File | Contract | Exit codes | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `anonymize.py` | Copy qualified names in JSONL telemetry to stable salted pseudonyms; accepts a file or recursive directory input, preserves runtime topology, and keeps the salt separate from export output. | `0` ok · `1` input unreadable · `2` refused output or directory `--output` | | `check_index_smoke.py` | Project `smoke_test_index.py` to compact `{ok, stale, age_hours}` JSON for shell callers. | `0` ok+fresh · `1` stale or failed · `2` bad args | | `smoke_test_index.py` | Validate an index file and report age-based freshness. | `0` ok and not stale · `1` invalid or stale | | `scan-stats.py` | Print module, degraded-module, symbol, call-edge, and centrality counts for the root in `SCAN_ARGS`. | `0` printed · `1` index missing/oversized · `2` root or cache escape | | `resolve_proj_index.py` | Resolve the project key and default index path; `--check` also reports existence. | `0` ok · `1` `--check` and index missing · `2` bad args | | `resolve_index_env.py` | Safely write resolved project/index values to session-scoped temporary files without `eval`. | `0` ok · `1` no resolver output or index missing · `2` unknown flag · `3` unsafe plugin root or prefix | | `locate_scan_query.py` | Resolve `scan-query` through PATH, the active plugin root, and the installed cache fallback. | `0` found · `1` not found · `2` bad args | | `join_avoidance.py` | Recursively join flat legacy and runtime-scoped CLI/tool telemetry, preserving runtime/session identity and reporting per-runtime plus unattributed avoidance metrics. | `0` ok, including zero events · `2` no `--logs` and no `--cli`/`--tools` pair | | `gen_deprecation_wrapper.py` | Generate the `pyDeprecate` wrapper used by `rename-refs --deprecate`. | `0` printed · `1` malformed decorator or type | | `parse_scan_args.py` | Parse scan skill arguments into the scan state consumed by setup helpers. | `0` parsed · `1` `--nul-output` path outside `TMPDIR` | | `parse_deprecate_args.py` | Parse rename deprecation flags into temporary state files. | `0` always; failures surface as unwritten temp files | | `setup_scan_env.py` | Prepare portable scan state, paths, sentinels, and parsed arguments for `scan-codebase`. | `0` ok · `1` `scan-index` missing · `2` arg parse failed · `3` bad args | | `setup_scan_env.sh` | Deprecated POSIX `exec` shim to `setup_scan_env.py`; retain only for old call sites. It is not a Windows entry point. | inherits the Python script's code | Exit codes are what a `SKILL.md` bash block branches on, so they are part of the contract, not an implementation detail. Use the installed plugin root supplied by the host when invoking helpers from a skill. Do not assume the source checkout, a sibling plugin, or a fixed home/temp directory exists. `resolve_index_env.py` exists so a skill can read `PROJ`/`INDEX` without `eval "$(...)"`. Its handoff files are session-scoped, so the caller derives `CSID` once and reads them with `read`, never command substitution:
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
python "${CLAUDE_PLUGIN_ROOT}/bin/resolve_index_env.py" --output-prefix "codemap-myproj"
IFS= read -r PROJ < "${TMPDIR:-/tmp}/codemap-myproj-resolve-proj-${CSID}" 2>/dev/null || PROJ=""
IFS= read -r INDEX < "${TMPDIR:-/tmp}/codemap-myproj-resolve-index-${CSID}" 2>/dev/null || INDEX=""
The helpers with security-sensitive boundaries are intentionally narrow: `anonymize.py` keeps its random salt separate from exported JSONL and refuses an output directory containing that salt; `resolve_index_env.py` writes resolved values to session-scoped temporary files without `eval` and validates plugin-root and output-prefix inputs; `locate_scan_query.py` rejects a launcher symlink that escapes the installed plugin root; and `join_avoidance.py` reports repeated manual searches only when a preceding complete query answered the same module.
Detailed helper API contracts The helpers below are stable skill-facing seams, not a second public CLI. Their arguments and output files are listed because installed-path debugging often happens outside the source checkout. | Helper | Invocation and output contract | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `anonymize.py` | `anonymize.py --input PATH [--output PATH] [--out-dir DIR] [--salt PATH]`; reads JSONL, writes a separate pseudonymized export, and refuses an output directory containing `.salt`. | | `check_index_smoke.py` | `check_index_smoke.py --index-path PATH [--max-age-hours N]`; projects the child validator to one JSON line with `ok`, `stale`, `age_hours`, and a bounded error. Exit 0 is fresh/valid, 1 is stale/failure, and 2 is invalid arguments. | | `smoke_test_index.py` | `smoke_test_index.py --index-path PATH [--max-age-hours N]`; emits the raw `ok/stale/age_hours/path` object after checking readable non-empty JSON and age. | | `scan-stats.py` | `SCAN_ARGS='--root PATH' scan-stats.py`; prints module, degraded-module, symbol, resolved-call, and top reverse-dependency counts. The root is intentionally passed through `SCAN_ARGS`, not a second parser. | | `resolve_proj_index.py` | `resolve_proj_index.py [--check]`; prints the project key and canonical index path, using Git root or current-directory fallback and `CODEMAP_INDEX_DIR`. `--check` also reports existence. | | `resolve_index_env.py` | `resolve_index_env.py [--check-exists] [--output-prefix NAME]`; resolves project/index values into exclusively-created session files without `eval`, validating the prefix and plugin-root boundary. | | `locate_scan_query.py` | `locate_scan_query.py`; prints one absolute launcher path from PATH, the active plugin root, or the installed-cache fallback. It rejects an escaping symlink and prints no diagnostic chatter on success. | | `join_avoidance.py` | `join_avoidance.py --logs DIR [--window-min N] [--json]`; joins CLI and tool JSONL layers and reports searches after a complete query. It is offline telemetry analysis, not a runtime guard. | | `gen_deprecation_wrapper.py` | `gen_deprecation_wrapper.py --old-name NAME [--type {function,method,class} --new-name NAME --since V or --decorator LINE] [--removed-in V]`; prints deprecation-wrapper source. | | `parse_scan_args.py` | `parse_scan_args.py RAW_ARGUMENTS [--nul-output PATH] [--print-root]`; extracts only `--root` and `--incremental` from the raw skill argument blob, using NUL-safe output when requested. | | `parse_deprecate_args.py` | `parse_deprecate_args.py --arguments RAW_ARGUMENTS`; parses `--deprecate`/decorator values and writes exclusive temporary files containing `DEPRECATE` and `DEPRECATE_DECORATOR`, printing their paths. | | `setup_scan_env.py` | `setup_scan_env.py [--arguments RAW]`; derives a sanitized project slug, validates the installed scanner, parses scan flags, records the incremental-without-index fallback, and writes portable per-invocation state. | | `setup_scan_env.sh` | `setup_scan_env.sh [args...]`; deprecated POSIX `exec` shim forwarding unchanged arguments to `setup_scan_env.py`. It is not a Windows entrypoint and should not receive new callers. | The helpers are stdlib-only and use host temporary-directory facilities. Callers should pass explicit scratch paths and treat non-zero exits as structured failure; no helper installs dependencies or mutates a remote service. Security-sensitive helpers reject unsafe roots, symlinks, pre-planted temp files, and salt/output co-location rather than attempting recovery.

🧰 Internal compatibility shims

The _exclusions.py, _index_identity.py, _runtime_log.py, _rwgate.py, _schema.py, and _telemetry.py files alias package implementations for legacy bare-module imports used by launchers and tests. They are not standalone commands and should not be imported by new application code; use codemap_py modules instead.

Shim map | Shim | Authoritative implementation | Boundary | | -------------------- | ---------------------------- | --------------------------------------------------- | | `_exclusions.py` | `codemap_py.scanner` | Shared scanner exclusions and source-root handling. | | `_index_identity.py` | `codemap_py.index_paths` | Project and index identity resolution. | | `_runtime_log.py` | `codemap_py.runtime_log` | Project-anchored telemetry log paths. | | `_rwgate.py` | `codemap_py.rwgate` | Cross-process index read/write coordination. | | `_schema.py` | `codemap_py.schema` | Index schema compatibility. | | `_telemetry.py` | `codemap_py.telemetry` | Runtime/version telemetry compatibility. | Each shim prepends the installed `src/` directory and replaces its own `sys.modules` entry with the real implementation, so tests and legacy bare imports cannot create a divergent shadow copy. Import the package modules directly in new code.

🧭 Portability and safety

The core uses standard-library Python and pathlib-based paths. Launchers preserve arguments, avoid shell evaluation, and keep the interpreter probe before package import. The index writer and reader lease the shared index, write atomically, and report bounded structured failures. The scan-index timeout flag is Unix-specific because it relies on SIGALRM; normal indexing and querying remain available on Windows through codemap-py.cmd.