Metadata-Version: 2.5
Name: salak
Version: 0.1.0
Summary: Deterministic, provenance-tagged code knowledge graph generator
Project-URL: Homepage, https://github.com/edhoferdian/salak
Author: Edho Ferdian
License-Expression: MIT
License-File: LICENSE
Keywords: knowledge-graph,lsp,static-analysis,tree-sitter
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Requires-Dist: jsonschema<5,>=4.23
Requires-Dist: tree-sitter-language-pack<2,>=0.7
Requires-Dist: tree-sitter<0.27,>=0.23
Description-Content-Type: text/markdown

# Salak

Deterministic, provenance-tagged code knowledge graph generator.

Salak parses a source repository and emits a single JSON file — `repo-graph.json` —
describing its structure and the relations between its symbols, so an AI coding
assistant can answer *"what depends on this module?"* from a recorded fact instead of
a guess.

**The graph is a build artifact, not a database.** It is regenerated from source,
committed to git, and read by tools. Salak owns the generation and nothing else.

## Package name

Will publish on **PyPI as `salak`**. The name `salak` is taken on npm; if a JavaScript
distribution is ever needed it ships as **`salak-graph`** (decision D5).

## Status

**Pre-release — `0.1.0.dev0`.** Phases 1–3 are shipped: each phase gate (TASK-039,
TASK-062, TASK-083) returned `ship`, and `salak scan`, `salak check`, `salak diff`,
`salak validate`, `salak doctor` and `salak version` all work today, against real
repositories. **Not yet on PyPI** — publication is TASK-086/087, deliberately
deferred until the owner runs it with publish credentials. Install from source for
now (below).

| Phase | Deliverable | State |
|---|---|---|
| Spike 0 | Feasibility and readiness | done |
| **v0.1** | Structure pass: `files`/`nodes`/`defines`/`imports`/`depends_on`, published JSON Schema, `salak validate` | **shipped** |
| v0.2 | Resolution pass (§4.1 Levels 1–2), `extends`/`implements`/`instantiates`/`references`, full provenance | **shipped** |
| v0.3 | Incremental scan (the `run_incremental` library entry point), `salak check`, `salak diff` | **shipped** |
| v1.0 | Packaging, `salak doctor`, `--stdout`, docs | in progress |

Level 3 (method-call resolution) was decided **void** at the v0.1 exit gate (K15,
decision D43): it resolved only 23.1% of import edges against 95.4–99.6% for Levels
1–2, with no language server at all. See "Degraded mode and resolution levels" below.

## Design principles

1. **Deterministic over clever.** Same input, same bytes out. No LLM anywhere — the
   pipeline is offline, free and reproducible.
2. **Precision over recall.** An `extracted` edge that is wrong is worse than a
   missing edge. When unsure, downgrade the provenance tag; never upgrade it.
3. **The schema is the contract.** Consumers talk to the schema, never to Salak
   internals. A formal JSON Schema ships with the package and is authoritative over
   the prose (SDD §3.7).
4. **One language, one adapter.** Adding a language must not modify core; adapters
   register through an entry-point group (decision D3), not an import in `core`.
5. **A graph declares its own scope.** Reading the file's `scan` block tells you
   which languages ran, whether anything failed, and what the run did not attempt.

## Requirements

- **Python 3.11+**
- **Node.js — optional.** `pyright` and `typescript-language-server` are the two
  language servers Salak knows how to detect, but **method-call resolution (Level 3)
  is void (K15/D43)**, and Levels 1–2 resolve from tree-sitter and filesystem
  heuristics alone — no module under `salak.resolution` imports `salak.lsp`. The
  practical result: **today, `repo-graph.json` comes out byte-for-byte the same with
  or without Node installed.** `salak doctor` still reports Node/server presence and
  version drift as *information* (decision D45 — a missing server is never treated as
  unhealthy), because that detection work is what a future Level-3 revival would need,
  not because anything currently emitted depends on it.
- **TypeScript ≤ 5.9.x** as the tsserver host, only relevant if you install the
  language servers for `salak doctor` to report on. TypeScript 7 ships no
  `lib/tsserver.js`, and `typescript-language-server` works by spawning tsserver, so
  the `latest` dist-tag is broken for this purpose (decision D15).

## Installing from source

Not on PyPI yet, so install from a clone:

```bash
git clone https://github.com/edhoferdian/salak
cd salak
uv sync
```

That's it — no Node, no language servers required to run `salak scan` and get a full
graph. `uv run salak ...` now works from inside the `salak` checkout.

To get a plain `salak` command on your `PATH` instead of prefixing every call with
`uv run` from inside this checkout:

```bash
uv tool install --editable .
```

(Verified on this machine: `uv tool install --editable .` installs cleanly, and the
resulting `salak version` prints the same output as `uv run salak version`.)

If you also want `salak doctor` to report on the language servers (informational only
— see Requirements above), install Node.js, then from this checkout:

```bash
npm ci
```

## Quickstart

Four commands, from install to a generated graph, run against any repository on your
machine — not just this one:

```bash
uv sync
uv run salak scan /path/to/your/repo
uv run salak validate /path/to/your/repo/project-memory/repo-graph.json
uv run salak check /path/to/your/repo
```

`scan` writes the graph; `validate` confirms it matches its own declared schema
version; `check` confirms the graph is still fresh against the working tree (it will
be, immediately after a scan). Run against this repository itself, `scan` reports:

```
salak: wrote <repo>/project-memory/repo-graph.json
salak: 213 parsed, 2 failed, 0 skipped · 2824 nodes, 9524 edges · 2 error(s), 0 warning(s)
```

(Exact counts drift as the repository grows; the shape — most files parsed, two
deliberately-broken fixtures failing, the run still completing — is what to expect.)

The two `PARSE_FAILED` errors above are two deliberately-broken fixture files under
`tests/fixtures/broken/` used to test the parser's recovery path — exit code `2`
("completed with diagnostics", not a crash) is expected there. On an ordinary
repository, `scan` exits `0`.

## Default output path

`salak scan PATH` writes to **`PATH/project-memory/repo-graph.json`** unless `--out`
overrides it. From SDD §5: *"chosen so the artifact lands where `dev-kickoff` already
keeps project memory."* Overridable per run; `--stdout` skips the file entirely and
writes the graph to stdout instead (for CI or piping into `salak validate -`).

Point `--out` outside the repository when scanning *this* repository, or the scan
overwrites its own working tree — use an absolute path or a sibling directory that
isn't reached through `..`:

```bash
uv run salak scan . --out /path/outside/this/repo/salak-graph.json
```

## Degraded mode and resolution levels

A run with no language server available, or one whose version doesn't match the pin,
is **not an error** — it's a supported, expected state (rule 6). `health_check()`
never raises, and a missing or mismatched server is reported as an `LSP_VERSION_MISMATCH`
*warning* or plain information in `doctor`, never as `unavailable` (decision D45).

Concretely, on this machine right now:

```
$ uv run salak doctor
Node.js: found (...)
python: ok — tree-sitter grammars available; resolution is §4.1 Levels 1-2 and needs
  no language server (K15/D43). pyright 1.1.411 ...
typescript: ok — tree-sitter grammars available; resolution is §4.1 Levels 1-2 and
  needs no language server (K15/D43). typescript-language-server 5.3.0 ...
```

Both adapters report `ok` regardless of whether the language server was actually
consulted, because after K15/D43 neither adapter's resolution ever queries one — the
`scan.lsp` field in the artifact records whether a server was *available to be
consulted*, not whether an edge came from it (note N2). Every edge in `repo-graph.json`
today is confirmed by tree-sitter structure and filesystem/alias/barrel heuristics
alone (§4.1 Levels 1–2), tagged `extracted` only when the relation needs no further
guessing (rule 1, rule 10).

A broken source file behaves the same way at the file level: a parse failure produces
a `PARSE_FAILED` diagnostic and a thinner entry for that one file (no declarations,
but its `file` node and any import edges above the syntax error still appear, D41/D42)
— the rest of the scan completes and the graph is still written (rule 7).

## Documentation

| Document | Role |
|---|---|
| `docs/SALAK-SDD-v1_4.md` | **Binding specification.** §3 is a contract. |
| `docs/salak-wbs-v1_5.md` | **Binding plan.** Task list, dependency graph, sprints. |
| `docs/schema-guide.md` | How to read `repo-graph.json` as a consumer: node/edge kinds, provenance semantics. |
| `docs/cli-reference.md` | Every subcommand, flag, and exit code. |
| `docs/troubleshooting.md` | What each diagnostic code means and how to act on it. |
| `docs/RELEASING.md` | TestPyPI rehearsal and real PyPI publish runbook (owner-only). |
| `project-memory/` | Decision register, gap analysis, progress ledger, Spike 0 findings. |
| `CLAUDE.md` · `AGENTS.md` · `context-pack.md` | Context packs for AI assistants working in this repo. |

Earlier SDD (`v1_3`, `v1_2`, `v1_1`, `v1.0`) and WBS (`v1_4`, `v1_3`, `v1_2`, `v1_1`)
revisions are **superseded archives**. Do not implement from them.

## Development

```bash
uv sync
```
```bash
uv run ruff check . && uv run mypy src tests && uv run pytest
```

`mypy` is given `src tests` explicitly — the test suite carries part of the contract
(a Protocol-conformance check lives in a test file), so excluding `tests` would turn
that guarantee into a comment.

## Licence

**MIT** — see [`LICENSE`](LICENSE).

Chosen 2026-07-29, closing gap-analysis item G2. MIT rather than Apache-2.0 because
Salak's job is to *unblock* other projects: it is a build tool that emits a JSON file,
its own §1 says it is "not meant to become a product", and the fewest possible
conditions on reuse serves that. Apache-2.0's express patent grant is the one thing MIT
lacks; it would be the better choice for something with patentable algorithms or
corporate contributors, and Salak has neither. Reversible at no cost until the first
PyPI upload, and effectively permanent after.
