semctx 0.4.5 ·Apache-2.0

Change-impact analysis for TypeScriptAnalyse d’impact des changements pour TypeScript

A diff goes in.
A verdict comes out.
Un diff entre.
Un verdict sort.

semctx reads the Git diff of a TypeScript repository, finds the code it touches and checks the rules you declared. It answers with one verdict: PASS, WARN or BLOCK. The analysis runs on your machine, with no network call and no LLM.

semctx lit le diff Git d’un dépôt TypeScript, retrouve le code touché et vérifie les règles que vous avez déclarées. Il répond par un seul verdict : PASS, WARN ou BLOCK. L’analyse tourne sur votre machine, sans appel réseau ni LLM.

Try it in your repositoryEssayez-le dans votre dépôt

bunx semctx@0.4.5 setup

# edit some code# modifiez du code

bunx semctx@0.4.5 verify diff

Needs Bun 1.4.0 or newer and a Git repository with at least one commit. Run setup on a clean tree, then edit and verify without re-indexing in between. For a branch, CI or a coding agent, see three ways to start.

Il faut Bun 1.4.0 ou plus récent et un dépôt Git avec au moins un commit. Lancez setup sur un arbre propre, puis modifiez et vérifiez sans réindexer entre les deux. Pour une branche, la CI ou un agent de code, voir trois façons de commencer.

Diagram: the changed files src/cart.ts, src/greeting.ts and src/pricing.ts run as lines into one signal. The added line discountCents in the exported interface CartTotal lights its route through a CartTotal node, and the signal shows WARN, its middle lamp.

Schéma : les fichiers modifiés src/cart.ts, src/greeting.ts et src/pricing.ts forment des voies qui rejoignent un signal. La ligne ajoutée discountCents dans l’interface exportée CartTotal allume sa voie à travers un nœud CartTotal, et le signal affiche WARN, son feu du milieu.

$ semctx verify diffexit 0
Verdict: WARN  range         : working tree  changed files : 3  impacted nodes: 7 Impacted contracts  Exported interface "CartTotal" is a public, compiler-enforced contract. [statically_verified] Recommended tests  none Unknowns  ? Could not confirm index binding (STALE: ANALYSIS_INPUT_MISMATCH, WORKING_DIFF_MISMATCH); impact is reported without a freshness proof. Findings  [WARN ] contract_changed_without_test: exported contract changed without a covering test: CartTotalstderrWARN non-blocking warnings present
Real output of semctx verify diff on the repository of the first-use demo, run as above: setup on a clean commit, three edits, then verify. One edit adds a required field to the exported interface CartTotal. No test imports it, so the verdict is WARN. The line under Unknowns is expected for uncommitted edits: the index describes your last commit, not your edits. Sortie réelle de semctx verify diff sur le dépôt de la démo de premier usage, lancée comme ci-dessus : setup sur un commit propre, trois modifications, puis vérification. L’une ajoute un champ obligatoire à l’interface exportée CartTotal. Aucun test ne l’importe, d’où le verdict WARN. La ligne sous Unknowns est normale pour des modifications non commitées : l’index décrit votre dernier commit, pas vos modifications.

01 · What it touchesCe qu’il touche

It maps your diff to the code it touches.Il relie votre diff au code qu’il touche.

semctx keeps a local index of your TypeScript. For every changed line it finds the declaration the line belongs to and the contracts and rules attached to it. For every changed file it lists the test files that import any of its symbols.

semctx tient un index local de votre TypeScript. Pour chaque ligne modifiée, il retrouve la déclaration qui la contient et les contrats et règles qui s’y rattachent. Pour chaque fichier modifié, il liste les fichiers de test qui importent l’un de ses symboles.

  1. SymbolsSymboles

    The functions, classes, interfaces, types and enums your changed lines land in, plus the modules that hold them.

    Les fonctions, classes, interfaces, types et enums où tombent vos lignes modifiées, plus les modules qui les contiennent.

    impacted nodes: 7
  2. Exported contractsContrats exportésWARN

    Exported interfaces and type aliases that other code compiles against. In this diff, CartTotal changed and nothing tests it.

    Les interfaces et alias de type exportés sur lesquels compile le reste du code. Dans ce diff, CartTotal a changé et rien ne le teste.

    Exported interface "CartTotal" is a public, compiler-enforced contract. [statically_verified]
  3. @invariant rulesRègles @invariant

    The rules you write in a JSDoc marker, like the one below. This diff touches none; section 02 shows this one at work.

    Les règles que vous écrivez dans un marqueur JSDoc, comme celui ci-dessous. Ce diff n’en touche aucune ; la section 02 montre celle-ci à l’œuvre.

    @invariant greeting-non-empty
  4. Linked test filesFichiers de test liés

    Test files that import any symbol from a changed file. semctx lists them so you know what to run. It never runs them itself.

    Les fichiers de test qui importent un symbole d’un fichier modifié. semctx les liste pour que vous sachiez quoi lancer. Il ne les exécute jamais lui-même.

    Recommended tests
      none

02 · Your rulesVos règles

You decide what must not break.Vous décidez de ce qui ne doit pas casser.

Write the rule as a JSDoc marker right above the code, commit it and run semctx index. From then on, if that code changes and no test imports it, the verdict is BLOCK and, by default, the exit code is 3. semctx never guesses what is critical.

Écrivez la règle sous forme de marqueur JSDoc, juste au-dessus du code, commitez-la et lancez semctx index. Dès lors, si ce code change et qu’aucun test ne l’importe, le verdict est BLOCK et, par défaut, le code de sortie vaut 3. semctx ne devine jamais ce qui est critique.

examples/consumer-typescript-repo/src/index.ts
1/**5 * @capability greeting6 * @invariant greeting-non-empty: a greeting must never be an empty string7 */8export function greet(name: string): string {9  return `Hello, ${name}!`;10}
git diff -U0 -- src/index.ts
@@ -9 +9 @@ export function greet(name: string): string {-  return `Hello, ${name}!`;+  return name.length > 0 ? `Hello, ${name}!` : "";
$ semctx verify diff --base mainexit 3
Verdict: BLOCKlines omittedlignes omisesImpacted invariants  ! a greeting must never be an empty string [inferred]lines omittedlignes omisesFindings  [BLOCK] invariant_touched_without_test: invariant-constrained code changed without a covering test: greet
Excerpt; the dots mark omitted lines. Historical observation: these are the lines semctx 0.4.2 prints for this edit of examples/consumer-typescript-repo, committed on a branch. To clear the BLOCK, add a test file that imports greet from src/index.ts, commit, run semctx index, then verify again. Extrait ; les points marquent les lignes omises. Observation historique : ce sont les lignes que semctx 0.4.2 affiche pour cette modification de examples/consumer-typescript-repo, commitée sur une branche. Pour lever le BLOCK, ajoutez un fichier de test qui importe greet depuis src/index.ts, commitez, lancez semctx index, puis vérifiez à nouveau.
  • Markers work above a function, a constant holding an arrow function, a class, an interface, a type alias or an enum. On a method, a property or a plain constant they are ignored, so to guard a method, mark its class.Les marqueurs fonctionnent au-dessus d’une fonction, d’une constante qui contient une fonction fléchée, d’une classe, d’une interface, d’un alias de type ou d’un enum. Sur une méthode, une propriété ou une simple constante, ils sont ignorés : pour protéger une méthode, marquez sa classe.
  • In the default TypeScript-only flow, a test covers a symbol through a direct import under its declared name. Aliased, namespace, type-only and barrel imports do not count in that flow.Dans le parcours TypeScript seul par défaut, un test couvre un symbole par un import direct qui conserve son nom déclaré. Les imports renommés, par espace de noms, de type seul ou via un barrel ne comptent pas dans ce parcours.
  • The opt-in config v2 qualified-static-v1 profile also follows canonical renamed imports, modeled namespace members and re-exports. Unknown or unsupported targets do not establish coverage. See the qualified profile and admission contract. These links are static associations; semctx does not run the tests.Le profil optionnel qualified-static-v1, en config v2, suit aussi les imports renommés, les membres d’espaces de noms modélisés et les réexports. Une cible inconnue ou non prise en charge ne prouve pas la couverture. Voir le profil qualifié et le contrat d’admission, en anglais. Ces liens restent statiques ; semctx n’exécute pas les tests.
  • @tag security on a function, class, interface, type alias or enum (methods and plain constants are ignored), or @tag critical on an exported interface or type, also turns a missing test into BLOCK.@tag security sur une fonction, une classe, une interface, un alias de type ou un enum (les méthodes et les simples constantes sont ignorées), ou @tag critical sur une interface ou un type exporté, transforme aussi un test manquant en BLOCK.

03 · Three verdictsTrois verdicts

Three verdicts, one exit code to gate on.Trois verdicts, un code de sortie pour trancher.

The verdict is the most severe finding in the report. A hook, a CI job or a coding agent can stop on the exit code alone; to tell PASS from WARN, read the verdict line or --format json. The lamps keep their order: BLOCK on top, WARN in the middle, PASS at the bottom.

Le verdict reprend le constat le plus grave du rapport. Un hook, une CI ou un agent de code peut s’arrêter sur le seul code de sortie ; pour distinguer PASS de WARN, lisez la ligne du verdict ou --format json. Les feux gardent leur ordre : BLOCK en haut, WARN au milieu, PASS en bas.

  1. BLOCKexit 3
    • Code under an @invariant changed and no test covers it.Du code sous un @invariant a changé et aucun test ne le couvre.invariant_touched_without_test
    • An exported interface or type tagged @tag critical or @tag security changed and no test covers it.Une interface ou un type exporté marqué @tag critical ou @tag security a changé et aucun test ne le couvre.critical_contract_changed_without_test
    • A @tag security symbol changed and no test covers it.Un symbole @tag security a changé et aucun test ne le couvre.security_surface_without_verification
    • Built in: the index does not describe the code under review (see section 06).Intégré : l’index ne décrit pas le code examiné (voir la section 06).index_binding_stale
  2. WARNexit 0
    • Another exported interface or type alias changed and no test covers it.Une autre interface ou un autre alias de type exporté a changé et aucun test ne le couvre.contract_changed_without_test
    • The change touches a document marked contradicted or deprecated.Le changement touche un document marqué contredit ou obsolète.contradiction_unresolved
  3. PASSexit 0

    No rule fired. A PASS covers only what the rules check; it does not prove the code is correct.

    Aucune règle ne s’est déclenchée. Un PASS ne couvre que ce que vérifient les règles ; il ne prouve pas que le code est correct.

Exit code by --fail-onCode de sortie selon --fail-on
blockdefaultdéfautwarnnone
BLOCK330
WARN030
PASS000

--fail-on warn also fails on WARN, --fail-on none never fails. An error, such as a repository with no commit yet, exits 1.

--fail-on warn échoue aussi sur WARN, --fail-on none n’échoue jamais. Une erreur, comme un dépôt encore sans commit, sort en 1.

04 · Where it runsOù il tourne

The same verdict in a terminal, a pull request or an agent.Le même verdict dans un terminal, une pull request ou un agent.

  1. CLI

    semctx verify diff

    Human text, a versioned JSON report (--format json) or GitHub annotations (--format github). CLI referenceTexte lisible, rapport JSON versionné (--format json) ou annotations GitHub (--format github). Référence CLI

  2. GitHub Actions

    uses: hoklims/semctx/packages/github-action@v0.4.5::warning title=semctx%3A contract_changed_without_test,file=src/cart.ts,line=1::exported contract changed without a covering test: CartTotal

    Indexes the checked-out commit, checks the pull request range, annotates each finding at the declaration it concerns and fails the job on BLOCK. No secrets, no PR comments. GuideIndexe le commit extrait, vérifie la plage de la pull request, annote chaque constat sur la déclaration concernée et fait échouer le job sur BLOCK. Sans secret, sans commentaire de PR. Guide

  3. Claude Code

    bunx semctx@0.4.5 installsemctx guarded mode: no verification on record.

    A plugin with MCP tools and skills. Optional guarded mode: git commit and git push wait until a non-BLOCK verdict is recorded for that exact content. Guarded modeUn plugin avec outils MCP et skills. Mode protégé en option : git commit et git push attendent qu’un verdict autre que BLOCK soit enregistré pour ce contenu exact. Mode protégé

  4. Codex

    bunx semctx@0.4.5 install --host codex

    The same MCP tools and skills. Codex has no guarded mode. GuideLes mêmes outils MCP et skills. Codex n’a pas de mode protégé. Guide

  5. MCP

    semctx_verify_change

    Other MCP clients can run the server from a source checkout. An agent then checks its own diff before it says it is done. SetupLes autres clients MCP peuvent lancer le serveur depuis une copie du dépôt. Un agent vérifie alors son propre diff avant de dire qu’il a fini. Configuration

05 · Run itLancez-le

Three ways to start.Trois façons de commencer.

The CLI and the plugins need Bun 1.4.0 or newer, and the plugin hooks also need Node. The GitHub Action installs Bun itself. Every path needs a Git repository with at least one commit.

La CLI et les plugins demandent Bun 1.4.0 ou plus récent, et les hooks des plugins demandent aussi Node. L’action GitHub installe Bun elle-même. Dans tous les cas, il faut un dépôt Git avec au moins un commit.

CLI, on a branchCLI, sur une branche
# start from a clean, committed tree# partez d’un arbre propre, déjà commité
bunx semctx@0.4.5 setup
git switch -c try-semctx
# edit your code, then commit# modifiez le code, puis commitez
git add -A && git commit -m "my change"
bunx semctx@0.4.5 index
bunx semctx@0.4.5 verify diff --base main
  • This checks the whole branch against its merge base, new files and new tests included.
  • Replace main with your default branch. It must exist locally: semctx never fetches.
  • Run index again after every commit, pull or branch switch (section 06).
  • The first setup often ends with OK ready (analysis partial). On a small repository that is normal.
  • Cela vérifie toute la branche depuis sa merge base, nouveaux fichiers et nouveaux tests compris.
  • Remplacez main par votre branche par défaut. Elle doit exister en local : semctx ne fait jamais de fetch.
  • Relancez index après chaque commit, pull ou changement de branche (section 06).
  • Le premier setup se termine souvent par OK ready (analysis partial). Sur un petit dépôt, c’est normal.
With Claude Code or CodexAvec Claude Code ou Codex
# preview, change nothing# aperçu, rien n’est modifié
bunx semctx@0.4.5 install --dry-run
bunx semctx@0.4.5 install

Claude Code or Codex must already be installed. The command installs or updates the semctx plugin for each one it finds, then runs setup in this repository. With neither, it stops without changing anything and exits 1.

Claude Code ou Codex doit déjà être installé. La commande installe ou met à jour le plugin semctx pour chaque hôte trouvé, puis lance setup dans ce dépôt. Sans aucun des deux, elle s’arrête sans rien modifier et sort en 1.

Running it again is harmless on a clean, committed tree; it runs setup, which re-indexes, so add --skip-setup when you have uncommitted edits. Afterwards, open a new Codex task or run /reload-plugins in Claude Code.

La relancer est sans risque sur une copie propre et commitée ; comme elle lance setup, qui réindexe, ajoutez --skip-setup si vous avez des modifications non commitées. Ensuite, ouvrez une nouvelle tâche Codex ou lancez /reload-plugins dans Claude Code.

In CI, on GitHub ActionsEn CI, sur GitHub Actions
- uses: actions/checkout@v4
  with:
    fetch-depth: 0
- uses: hoklims/semctx/packages/github-action@v0.4.5
  with:
    base: ${{ github.event.pull_request.base.sha }}
    head: ${{ github.sha }}
    fail-on: block

fetch-depth: 0 keeps the merge base local, since semctx never fetches. BLOCK fails the job; WARN fails it only with fail-on: warn. Pin a release tag. Full workflow

fetch-depth: 0 garde la merge base en local, car semctx ne fait jamais de fetch. BLOCK fait échouer le job ; WARN seulement avec fail-on: warn. Épinglez un tag de version. Workflow complet

06 · Keep the index currentGardez l’index à jour

Re-index after every commit.Réindexez après chaque commit.

semctx reads your diff through a local index of your code, so the index has to describe the commit you start from. Three situations cover it.

semctx lit votre diff à travers un index local de votre code : l’index doit donc décrire le commit dont vous partez. Trois situations couvrent l’essentiel.

  1. Uncommitted editsModifications non commitées

    semctx setup# edit, no re-index# modifiez, sans réindexersemctx verify diff? Could not confirm index binding (STALE: ANALYSIS_INPUT_MISMATCH, WORKING_DIFF_MISMATCH); impact is reported without a freshness proof.

    The verdict stands, with this note under Unknowns. Do not re-index in between: the index would hold your edited line numbers while the diff counts from your last commit, and a finding can disappear.Le verdict tient, avec cette note sous Unknowns. Ne réindexez pas entre les deux : l’index prendrait vos numéros de ligne modifiés alors que le diff compte depuis votre dernier commit, et un constat peut disparaître.

  2. After a commit, pull or branch switchAprès un commit, un pull ou un changement de branche

    semctx index[BLOCK] index_binding_stale

    Run it before the next check. Until you do, verify diff returns BLOCK with index_binding_stale, even for a harmless change.Lancez-le avant la vérification suivante. Tant que vous ne le faites pas, verify diff rend BLOCK avec index_binding_stale, même pour un changement anodin.

  3. A branch or a pull requestUne branche ou une pull request

    git commitsemctx indexsemctx verify diff --base main

    The most complete check: new files and new tests count, and no freshness note appears. The GitHub Action runs this flow for you.La vérification la plus complète : les nouveaux fichiers et les nouveaux tests comptent, et aucune note de fraîcheur n’apparaît. L’action GitHub suit ce chemin pour vous.

With setup --polyglot (config v2), use the branch flow for files the config selects: an uncommitted check after editing one returns BLOCK. Commit the change, run semctx index, then verify with --base; never re-index uncommitted edits.

Avec setup --polyglot (config v2), passez par la branche pour les fichiers que la config sélectionne : une vérification non commitée après la modification de l’un d’eux rend BLOCK. Commitez la modification, lancez semctx index, puis vérifiez avec --base ; ne réindexez jamais des modifications non commitées.

07 · LimitsLimites

What it can’t see.Ce qu’il ne voit pas.

  1. It reads your code. It never builds it or runs your tests, so treat the report as a list of what to re-check.Il lit votre code. Il ne le compile jamais et ne lance pas vos tests : traitez le rapport comme une liste de choses à revérifier.
  2. Without a marker, a behaviour change inside a function gives no finding. In the demo, an off-by-one in a discount helper goes unreported.Sans marqueur, un changement de comportement dans une fonction ne donne aucun constat. Dans la démo, une erreur d’un dans un calcul de remise passe inaperçue.
  3. It sees what git diff sees. Untracked files are ignored, and uncommitted checks skip symbols declared in brand-new files. Commit them and use --base to include them.Il voit ce que voit git diff. Les fichiers non suivis sont ignorés, et les vérifications non commitées sautent les symboles déclarés dans des fichiers tout nouveaux. Commitez-les et utilisez --base pour les inclure.
  4. Calls it cannot resolve statically are left out, and runtime behaviour such as races is out of reach.Les appels qu’il ne peut pas résoudre statiquement sont laissés de côté, et le comportement à l’exécution, comme les situations de concurrence, reste hors de portée.
  5. It analyses TypeScript, except declaration files (.d.ts), which yield no symbols. Markdown and SQL files only count as documents and migrations, limited Python support needs setup --polyglot, and other languages are skipped unless config v2 selects them, in which case changing such a file blocks.Il analyse le TypeScript, sauf les fichiers de déclaration (.d.ts), qui ne produisent aucun symbole. Les fichiers Markdown et SQL ne comptent que comme documents et migrations, la prise en charge limitée de Python demande setup --polyglot, et les autres langages sont ignorés, sauf si la config v2 les sélectionne : modifier un tel fichier bloque alors.

semctx says the same on its first run, on stderr:

semctx le dit aussi à sa première exécution, sur stderr :

$ semctx setupexit 0
WARN setup succeeded, but incomplete analysis cannot justify negative conclusions; run 'semctx index-health --json' for the exact gates and reasons.

In plain words: an empty report does not prove that nothing was affected.

Autrement dit : un rapport vide ne prouve pas que rien n’a été touché.

Try itEssayez-le

bunx semctx@0.4.5 setup

Then edit, and run bunx semctx@0.4.5 verify diff. Other paths: three ways to start.

Puis modifiez, et lancez bunx semctx@0.4.5 verify diff. Autres chemins : trois façons de commencer.