Skip to content
Open
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
24 changes: 24 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Grep",
"hooks": [
{
"type": "command",
"command": "command -v graphify >/dev/null 2>&1 || exit 0; graphify hook-guard search"
}
]
},
{
"matcher": "Read|Glob",
"hooks": [
{
"type": "command",
"command": "command -v graphify >/dev/null 2>&1 || exit 0; graphify hook-guard read"
}
]
}
]
}
}
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@ _build/
__pycache__/
*.pyc
*.zip

# graphify knowledge graph. Generated artifacts are not committed, but the
# semantic extraction cache IS: it is the expensive LLM output, and committing
# it lets a teammate rebuild the whole graph with no API key. Listing
# graphify-out/* rather than graphify-out/ is required — git will not descend
# into an ignored directory, which would make the !negations unreachable.
graphify-out/*
!graphify-out/cache/
graphify-out/cache/*
!graphify-out/cache/semantic/
22 changes: 22 additions & 0 deletions .graphifyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# graphify scan scope. Composes after .gitignore (which is read first); this
# file can only exclude further, never re-include a path .gitignore dropped.

# Generated graph artifacts.
graphify-out/

# Images cost one vision LLM call each and are UI chrome, not architecture.
*.png
*.jpg
*.jpeg
*.gif
*.svg
*.webp
*.ico

# Build output, dependency trees and virtualenvs.
dist/
build/
.next/
coverage/
.venv/
venv/
9 changes: 9 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## graphify

This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.

Rules:
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"nodes": [{"id": "_github_workflows_publish_pypi_pypi_publish", "label": "pypi-publish job (Publish to PyPI)", "file_type": "code", "source_file": ".github/workflows/publish-pypi.yaml", "source_location": "lines 8-27", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Release-triggered build with uv and trusted publishing via OIDC (id-token: write) so no long-lived PyPI token is stored in the repo."}], "edges": [{"source": "_github_workflows_publish_pypi_pypi_publish", "target": "_readthedocs_build", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": ".github/workflows/publish-pypi.yaml", "source_location": "lines 20-27", "weight": 1.0}, {"source": "_github_workflows_publish_pypi_pypi_publish", "target": "changelog_0_2_4", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": ".github/workflows/publish-pypi.yaml", "source_location": "lines 3-5", "weight": 1.0}], "hyperedges": []}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"nodes": [{"id": "readme_cm_proxy", "label": "cm-proxy (serverless localhost proxy)", "file_type": "concept", "source_file": "README.rst", "source_location": "lines 1-21", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Built as a cheap, pay-as-you-go AWS serverless replacement for ngrok/localtunnel so engineers can receive third-party webhooks on a local dev server without exposing localhost to the public internet and without paying for a commercial tunnel subscription."}, {"id": "readme_non_goals", "label": "cm-proxy Non-goals", "file_type": "rationale", "source_file": "README.rst", "source_location": "lines 33-39", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Explicitly out of scope: high performance, non-http protocols, streaming responses, non-utf8 responses. Scope is deliberately narrow to keep the codebase small enough to be audited by users who must trust it with AWS credentials."}, {"id": "readme_localhost_not_exposed", "label": "localhost is not publicly exposed", "file_type": "rationale", "source_file": "README.rst", "source_location": "line 28", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The local machine only ever polls outbound for queued messages, so no inbound port is opened on the dev host. This is the core security property that motivated the queue-based design over a tunnel."}], "edges": [{"source": "readme_cm_proxy", "target": "docs_security_least_privilege", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.rst", "source_location": "lines 47,69", "weight": 1.0}, {"source": "readme_cm_proxy", "target": "readme_localhost_not_exposed", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.rst", "source_location": "line 28", "weight": 1.0}, {"source": "readme_non_goals", "target": "readme_cm_proxy", "relation": "rationale_for", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.rst", "source_location": "lines 33-39", "weight": 1.0}], "hyperedges": [{"id": "cm_proxy_design_tradeoffs", "label": "Design trade-offs justifying the serverless queue architecture", "nodes": ["readme_non_goals", "readme_localhost_not_exposed", "docs_cost_cost_estimate", "docs_security_review_the_code", "docs_how_it_works_unidirectional_proxy", "docs_how_it_works_bidirectional_proxy"], "relation": "form", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.rst"}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"nodes": [{"id": "cm_proxy_unidirectional_proxy_cmproxyfunction", "label": "CMProxyFunction (unidirectional lambda)", "file_type": "code", "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 62-82", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Inline python3.14 arm64 lambda that only sends the event to SQS and immediately returns a fixed 200 body, keeping cost and latency minimal."}, {"id": "cm_proxy_unidirectional_proxy_handler", "label": "handler (unidirectional inline lambda handler)", "file_type": "code", "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 71-76", "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "cm_proxy_unidirectional_proxy_cmproxyrequestqueue", "label": "CMProxyRequestQueue (unidirectional)", "file_type": "code", "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 43-44", "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "cm_proxy_unidirectional_proxy_lambdarole", "label": "LambdaRole (unidirectional)", "file_type": "code", "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 14-41", "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "cm_proxy_unidirectional_proxy_cmproxyurl", "label": "CMProxyUrl (unidirectional Function URL)", "file_type": "code", "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 84-88", "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "cm_proxy_unidirectional_proxy_permissionsboundary", "label": "PermissionsBoundary parameter (unidirectional)", "file_type": "code", "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 4-10", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Optional template parameter; the HavePermissionsBoundary condition compares against the literal string \"null\" so the same template works with and without least-privilege deployment."}], "edges": [{"source": "cm_proxy_unidirectional_proxy_cmproxyfunction", "target": "docs_how_it_works_unidirectional_proxy", "relation": "implements", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 62-82", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_handler", "target": "cm_proxy_unidirectional_proxy_cmproxyrequestqueue", "relation": "shares_data_with", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 71-76", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_cmproxyurl", "target": "cm_proxy_unidirectional_proxy_cmproxyfunction", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 84-88", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_cmproxyfunction", "target": "cm_proxy_unidirectional_proxy_lambdarole", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "line 82", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_lambdarole", "target": "cm_proxy_unidirectional_proxy_cmproxyrequestqueue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 32-41", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_lambdarole", "target": "cm_proxy_unidirectional_proxy_permissionsboundary", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 25-28", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_handler", "target": "cm_proxy_bidirectional_proxy_handler", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 71-76", "weight": 1.0}, {"source": "cm_proxy_unidirectional_proxy_cmproxyrequestqueue", "target": "cm_proxy_bidirectional_proxy_cmproxyrequestqueue", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "cm_proxy/unidirectional-proxy.yaml", "source_location": "lines 43-44", "weight": 1.0}], "hyperedges": []}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"nodes": [{"id": "docs_use_autoprogram", "label": "CLI reference via sphinx autoprogram", "file_type": "document", "source_file": "docs/use.rst", "source_location": "line 4", "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The usage page is generated directly from cm_proxy.proxy:get_parser() so CLI docs cannot drift from the argparse definition."}], "edges": [{"source": "docs_use_autoprogram", "target": "cm_proxy_proxy_get_parser", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/use.rst", "source_location": "line 4", "weight": 1.0}], "hyperedges": []}
Loading