Naar inhoud

Nekyia

Het ritueel om de doden te ondervragen, gericht op de sessie die je kwijt bent.

Nekyia (Νέκυια) is het ritueel uit boek XI van de Odyssee. Odysseus graaft een kuil aan de rand van de wereld en de doden komen omhoog om ondervraagd te worden. Hij is er niet om ze te betreuren: Teiresias weet de weg naar huis en hij niet.

Wat het doet

De context die je nodig hebt zit meestal in een sessie die je al hebt gehad. Je hebt het probleem drie weken geleden uitgezocht, in een andere map, misschien in een andere client, en om het terug te vinden moet je er genoeg van weten om erop te kunnen zoeken. Dus leg je het uit aan een nieuwe agent en betaal je twee keer voor hetzelfde denkwerk.

Nekyia doorzoekt de transcripties die je agent-CLI's toch al op schijf bewaren, rangschikt wat eruit komt en start de client. Is het hervat-commando van die client getest, dan komt hij op ID terug in precies die sessie. Zo niet, dan begint hij een nieuwe sessie met een briefing uit de geïndexeerde prompts, de aangeraakte bestanden en de branch, en zegt er onomwonden bij dat het een briefing is en geen teruggehaalde staat.

De picker: sessies uit verschillende agent-CLI's onder elkaar, en daaronder de map, de branch, de prompts en de aangeraakte bestanden van de sessie onder de cursor

Dat onderscheid is het hele ontwerp. Een tool die beweert alles te hervatten en stilletjes iets nieuws begint, is slechter dan een tool die minder doet en je vertelt welke van de twee het werd.

Snel starten

Nekyia heeft Bun 1.1 of nieuwer nodig. Het is pre-release en staat nog niet op npm, dus je installeert vanuit de repository. Beide namen werken: nekyia en het kortere nek.

bash
bun install -g github:AraneaDev/Nekyia
nekyia index

De eerste run laat zien wat hij van plan is te lezen en wacht op je toestemming voordat hij een transcriptie-store opent of een index aanmaakt. Met --yes sla je die vraag over, zodra je die grens een keer bekeken hebt.

De picker

De picker opent op het project waar je staat. Start je hem in je home-map, boven in je bestandssysteem of ergens waar nog niets onder geïndexeerd is, dan opent hij op de hele index, want een lijst die tot die map beperkt is, zou leeg zijn.

Tikken filtert meteen mee en het stuk dat matcht licht op in elke titel, zodat de lijst antwoord geeft op elke toetsaanslag in plaats van alleen korter te worden.

Zoeken in de picker: de zoekterm licht op binnen elke titel die matcht, en de regel onder de cursor is in de kantlijn gemarkeerd

tab trekt het open naar alle mappen, en nog een keer drukken beperkt het tot het project van de regel waar je staat, dus je kunt overal beginnen en ergens specifieks uitkomen. Het aantal naast de zoekregel benoemt altijd wat er doorzocht wordt, en gaat het om één project, dan staat de naam ernaast.

De picker beperkt tot één project, met die naam naast het aantal sessies

Een zoekopdracht die niets oplevert zegt wat je kunt proberen in plaats van je op een leeg scherm achter te laten.

Een zoekopdracht die niets vond, met de tekst "Nothing came up. Try fewer words."

ctrl+f loopt alleen langs de clients die in je index zitten, zodat je niet langs lege filters bladert om bij de jouwe te komen. ctrl+p en ctrl+y kopiëren de openingsprompt en het commando dat hij zou draaien, als je er een ergens anders nodig hebt.

De sessie lezen voordat je hem hervat

Met ctrl+o open je de sessie onder de cursor en krijgt die het hele scherm: wat je vroeg en wat eruit kwam, in de volgorde waarin het gezegd is, en welke bestanden er verschoven. Een lang antwoord wordt over meerdere regels afgebroken en blijft binnen het scherm. De pijltjestoetsen scrollen een regel, page up en page down een scherm, en met esc sluit je hem weer.

Een sessie teruglezen: het paneel vult het scherm met het gesprek in de volgorde waarin het gevoerd is

De picker schikt zich naar de terminal waarin hij draait, dus een smal venster krijgt dezelfde interface op een kleiner formaat.

De picker in een terminal van tachtig kolommen, met dezelfde onderdelen op minder ruimte

Vanaf de commandoregel

Echt gedraaid op deze machine, ingekort met '…'.

Wat hij vond:

bash
$ nekyia doctor
clients
  agy           99 sessions  (built-in)
  claude       105 sessions  (built-in)
  codebuff      56 sessions  (built-in)  [partial]
  codex          6 sessions  (built-in)
  copilot        0 sessions  (built-in)
  kilo           1 sessions  (built-in)
  opencode       2 sessions  (built-in)

index
  267 sessions, 16 size-capped, 0 partly unreadable, 6 missing from disk
  size-capped: codebuff:18843754-fafe-4461-bf1a-6b9b878dc4a8

Zeven clients, één index. Vier daarvan hervatten op ID; de andere drie beginnen met een briefing, en bij doctor zie je welke van de twee het wordt voordat het uitmaakt. Een sessie die tegen de groottelimiet aan liep telt hij apart van een sessie die hij niet kon lezen, want de eerste haal je alsnog binnen door die limiet te verhogen en opnieuw te indexeren.

Zoeken zonder de picker:

bash
$ nekyia search reconnect race
$ nekyia search reconnect --json      # machine-readable, with the transcript paths
$ nekyia blame src/sse.ts             # sessions that touched this exact file
$ nekyia timeline --dir .             # every file operation recorded under a directory
$ nekyia last                         # newest session under this directory
$ nekyia show <uid>                   # the handover, as Markdown, before you send it

Zoeken blijft standaard binnen de huidige map. --all trekt dat open, --client <id> beperkt tot één client, en --file <path> vindt de sessies die een bestand hebben aangeraakt. blame keert die standaard om: hij rekent het pad uit vanaf waar je staat en zoekt daarna overal, met de nieuwste sessie bovenaan. Aangeraakt betekent dat het pad in de geïndexeerde tool-input stond, niet dat de sessie het bestand ook veranderd heeft. Met --json krijgt elke regel de paden van de transcripties mee, zodat een agent het origineel zelf kan lezen en niet op de geïndexeerde samenvatting hoeft af te gaan.

Wat er met je bestanden gebeurde

blame gaat over één bestand. timeline gaat over een map: elke bestandsbewerking die daaronder is vastgelegd, gegroepeerd per sessie die hem uitvoerde.

bash
$ nekyia timeline --dir . --since 7d --limit 2

/home/dev/work/api-gateway · 2 sessions · 12 events · git was not consulted
exact order inside a session, end-time order between them

*  claude      23m  api-gateway       the retry budget is shared across tenants, it should be per tenant
      2  read    src/gateway/retry-budget.ts
      3  edit    src/gateway/retry-budget.ts
      8  read    src/gateway/tenant.ts
      9  edit    src/gateway/tenant.ts
     14  read    test/retry-budget.test.ts
     15  write   test/retry-budget.test.ts

o  codex        6h  api-gateway       add structured logging around the upstream timeout path
      2  read    src/gateway/logging.ts
      3  edit    src/gateway/logging.ts

Het getal vóór elke bewerking is de beurt waarin die plaatsvond, zodat elke regel terugwijst naar de geschiedenis van de sessie zelf.

Binnen een sessie klopt de volgorde precies. Tussen sessies kent de index alleen de eindtijden, dus blijven de bewerkingen per sessie bij elkaar staan in plaats van samengevoegd te worden tot één stroom die preciezer lijkt dan hij is. Is de map een git-repository, dan krijgt elk pad dat git niet volgt de markering untracked. Kon git helemaal niet bevraagd worden, dan meldt de kop dat met zoveel woorden, want een ontbrekende markering leest anders als "git volgt dit". Wil je terughalen wat een sessie deed, bekijk dan beide soorten paden: dat git een pad kent, betekent niet dat je laatste wijzigingen er ook in zitten. Dezelfde grens als bij blame geldt hier ook: een sessie die ergens anders draaide en deze bestanden met een relatief pad noemde, vind je niet terug.

Eén ding om te weten voordat je hem op een oudere index loslaat. Bestandsbewerkingen worden pas vastgelegd vanaf de eerstvolgende keer dat een sessie wordt uitgelezen, dus sessies die al geïndexeerd waren voordat dit bestond, laten wel hun bestanden zien maar geen bewerkingen. Een gewone nekyia index vult dat niet aan: die leest alleen sessies opnieuw waarvan de transcriptie veranderd is, en dat is een oude transcriptie niet. nekyia index --rebuild doet het wel. De uitvoer benoemt welke sessies er zo bij staan, in plaats van ze te tonen alsof er niets gebeurd is.

Hoe het indexeert

Indexeren gaat in twee fases. De eerste leest begrensde metadata en een stabiele vingerafdruk per sessie; de tweede draait alleen voor wat veranderd is, en schrijft metadata en zoekfacetten atomair naar SQLite. Zoeken gaat via FTS5, dat titels, jouw prompts en delen van wat de assistent terugschreef verschillend weegt, en relevantie daarna mengt met een halfwaardetijd op recentheid. Fork-ketens vallen samen tot één regel.

Tool-output wordt niet geïndexeerd. Commando-resultaten en bestandsdumps zijn groot en rommelig, en het is de plek waar het vaakst iets privés tussen zit. Het is ook niet waar je een sessie op terugzoekt.

Configuratie

De index staat in ~/.local/share/nekyia/index.db en de configuratie in ~/.config/nekyia/config.json, die allebei XDG_DATA_HOME en XDG_CONFIG_HOME volgen.

Er is geen netwerkdienst, geen API-sleutel en geen telemetrie, en de briefing is deterministisch, dus er komt geen modelaanroep aan te pas. Wat de index wél bewaart is een kopie van tekst die al op je schijf stond, en die kopie overleeft de transcriptie waar hij vandaan komt. Daar zijn drie commando's voor:

bash
nekyia forget <uid>              # one session, and every facet of it
nekyia prune --missing           # sessions whose source files are gone
nekyia exclude '/work/private/**'  # then: nekyia index --rebuild

Het belooft niet dat secrets worden weggelakt, en de index is niet versleuteld, dus lees wat show, doctor en --json uitprinten voordat je er iets van in een openbaar issue plakt.

Een client die hij nog niet kent, kun je zelf beschrijven: gebruikersmanifesten staan in ~/.config/nekyia/clients/*.json, en nekyia doctor --sniff inspecteert waarschijnlijke transcriptie-stores en zet een concept klaar in plaats van naar een pad te gissen.