semcod

← all projects

todo2code

todo2code (t2c): audited intent and team-communication extraction, evidence graphs, origin-to-workspace comparison and grounded summaries through CLI, MCP and A2A.

TypeScript updated 1h ago View on GitHub → Homepage

todo2code (t2c)

todo2code buduje wspólny Intent Evidence DSL z poleceń, historii Git, aktualnego kodu, list zadań, changelogu i dokumentacji. Następnie łączy rekordy w graf przepływu wiedzy, wykrywa rozbieżności i generuje raport dla zespołu.

Projekt działa na Node.js/TypeScript. Wielojęzykowe fakty kodu dostarczają
adaptery TypeScript/JavaScript, Python (ast), Go (go/ast), Java (JDK
Compiler Tree API) i Rust (syn). Toolchainy poza Node są opcjonalne — brak
narzędzia daje jawne ostrzeżenie tylko wtedy, gdy repo zawiera pasujące źródła.
Integracje są dostępne przez CLI, MCP/stdio i A2A v1.0/JSON-RPC.

Stan projektu

Wersja 0.4.0 ma działającą ścieżkę źródła → kanoniczny DSL → graf →
diagnostyka/Intent vs Reality → raport. Kontrakty t2c.conclusion/v1 i
t2c.todo-proposal/v1 wraz z walidacją cytowań i provenance są wdrożone, a API
biblioteki potrafi je syntetyzować z grafu i diagnostyki przez OpenRouter.
Integracja DSL2TODO nie jest jeszcze kompletna: obecna lista następnych
działań w raporcie pozostaje projekcją diagnostyki, a CLI/SDK i zatwierdzalny
TODO.patch są kolejnymi punktami P0. API syntezy waliduje już zależności,
priorytety, kryteria i klasyfikuje duplikaty względem istniejącego TODO.

Aktualna macierz komponentów, wyniki walidacji, znane ograniczenia i projekt
docelowego DSL2TODO znajdują się w
docs/PROJECT_STATUS.md. Priorytety implementacyjne
są utrzymywane w TODO.md.

Komunikację zespołu można zapisywać append-only w project/<ticket>/. Konwerter
zachowuje uczestnika i rolę human|agent, a t2c communication porównuje
wypowiedzi z dowodami Git/AST osobno dla każdego uczestnika. Kontrakt plików i
gotowe polecenia opisuje
docs/TEAM_COMMUNICATION.md.

Praktyczny przebieg CLI — od instalacji przez tryb offline/LLM po diff,
Intent vs Reality, komunikację i automatyczną kontrolę wszystkich przykładów —
opisuje docs/CLI_GUIDE.md.

Reality vs Intent

reality.svg

GUI

history-ui.png

Granica LLM

Etap Mechanizm LLM
NL → DSL OpenRouter structured output; jawny fallback heurystyczny/TensorFlow tak, domyślnie preferowany
10 commitów Git → DSL git log, diff, heurystyki symboli nie
TypeScript/JavaScript/Python/Go/Java/Rust AST → DSL natywne parsery języków; Java Tree API, Rust syn nie
TODO + CHANGELOG → DSL deterministyczna struktura + audytowane wzbogacanie OpenRouter tak, domyślnie preferowany
Dokumentacja → DSL OpenRouter structured outputs tak
project/<ticket>/ komunikacja → DSL deterministyczny kontrakt uczestnika, roli i typu wypowiedzi nie
Linkowanie i diagnostyka deterministyczny graf relacji nie
Graf DSL → raport NL OpenRouter; wejściem jest tylko graf i diagnostyka tak

Moduły deterministyczne nie importują klienta OpenRouter. Sprawdzają to
npm run verify:no-llm oraz bezcykliczny graf modułów npm run verify:modules.
Kompletność i brak duplikatów zmiennych sprawdza npm run verify:env.

Szybki start

Wymagania: Node.js 20+ i Git. Opcjonalne adaptery wymagają odpowiednio Python
3.10+, Go, JDK 17+ lub Cargo/Rust.

cp .env.example .env
npm install
npm run build
node dist/src/cli.js doctor

Zwykłe npm install i make install instalują wyłącznie rdzeń, dla którego
audyt z 2026-07-29 ma 0 podatności. make install-tf instaluje
@tensorflow/tfjs-node@4.22.0 w odizolowanym adapters/tensorflow/node_modules;
jego 8 zgłoszeń nie trafia do drzewa zależności rdzenia. Nie należy stosować
npm audit fix --force, ponieważ proponuje niekompatybilny downgrade.

Demonstracja działania 0.4.0

Poniższa demonstracja używa wersjonowanego repozytorium examples/, nie wymaga
klucza ani połączenia z OpenRouter i pozostawia jednoznaczny audyt. Uruchom:

make demo

Polecenie wykonuje kolejno NL → DSL, Git → DSL, AST → DSL, osobne konwertery
TODO/CHANGELOG, linker, diagnostykę i deterministyczne podsumowanie. Następnie
analizuje komunikację examples/project/DEMO-101 osobno dla ludzi i agentów.
Wyniki trafiają do examples/.intent-demo/runs/<run-id>/ oraz
examples/.intent-communication/. Stan ostatniego runu można wyświetlić bez
dodatkowych narzędzi:

node --input-type=module <<'NODE'
import { readFile } from 'node:fs/promises';

const latest = JSON.parse(await readFile('examples/.intent-demo/latest.json', 'utf8'));
const manifest = JSON.parse(await readFile(`examples/${latest.runDirectory}/manifest.json`, 'utf8'));
const graph = JSON.parse(await readFile(`examples/${manifest.files.graph}`, 'utf8'));
const stages = Object.fromEntries(Object.entries(manifest.stages).map(([name, stage]) => [name, {
  status: stage.status,
  effectiveMode: stage.effectiveMode,
  reason: stage.reason?.code ?? null,
  runtimeVersion: stage.runtimeVersion,
}]));
console.log({ status: manifest.status, runtime: manifest.runtime, stages });
console.log({ records: graph.records.length, relations: graph.relations.length, bySource: graph.stats.bySource });
NODE

Weryfikowany wynik dla 0.4.0 ma 202 rekordy. Liczba relacji zależy również od
ostatnich 10 commitów Git, dlatego po każdym commicie może się prawidłowo
zmienić i należy odczytać ją z bieżącego grafu:

status: succeeded, runtime: todo2code 0.4.0
naturalLanguageExtraction: succeeded / deterministic
markdownExtraction:        succeeded / deterministic
documentationExtraction:   skipped / none
summary:                   skipped / deterministic / LLM_DISABLED
records: 202, relations: <zależne od ostatnich 10 commitów>
bySource: ast=180, changelog=2, git=10, nl=7, todo=3

Demo jawnie wyłącza LLM dokumentacji i podsumowania, więc nie korzysta z
prywatnego .env, sieci ani fallbacku. Każdy audyt zawiera runtimeVersion, requested/effective
mode, model, czas, licznik rekordów/ostrzeżeń, powód i bezpieczne parametry;
apiKey nigdy nie jest zapisywany.

A2A, SDK i UI

Uruchom backend:

npm run a2a

Następnie otwórz http://localhost:8787/ui. Widok pobierze historię z
GET /api/runs, domyślnie wybierze dwa ostatnie kompletne runy i pokaże ich
diff SVG. Stan serwera można sprawdzić przez:

curl -fsS http://localhost:8787/healthz
# {"status":"ok","service":"todo2code","protocol":"A2A","version":"1.0"}

Ten sam runtime jest dostępny przez SDK. Przykład TypeScript wykonuje
deterministyczne NL → DSL i sprawdza audyt, zamiast zakładać, że LLM zadziałał:

import { Todo2CodeClient } from 'todo2code/sdk';

const client = new Todo2CodeClient({ baseUrl: 'http://localhost:8787' });
const result = await client.extractNl('TASK.md', '.', 'deterministic');

console.log(result.records.length);                 // 10 dla bieżącego TASK.md
console.log(result.audit?.status);                  // succeeded
console.log(result.audit?.effectiveMode);           // deterministic
console.log(result.audit?.runtimeVersion);          // 0.4.0
console.log(result.audit?.configuration);           // bez apiKey

Odpowiedniki extractNl/extractDocs są dostępne również w Pythonie, Go,
Ruście i PHP; kompletne uruchamialne przykłady znajdują się w sdk/*/examples/.

Widoczna awaria LLM

require-llm nigdy nie przechodzi po cichu na parser deterministyczny. Ten
kontrolowany test kończy się kodem procesu 1:

OPENROUTER_API_KEY= T2C_NL_MODE=require-llm \
node dist/src/cli.js pipeline examples \
  --task task.md --todo TODO.md --changelog CHANGELOG.md \
  --no-docs-llm --out .intent-failure-demo

Mimo błędu powstaje examples/.intent-failure-demo/runs/<run-id>/manifest.json:

{
  "status": "failed",
  "failure": {
    "stage": "naturalLanguageExtraction",
    "code": "LLM_NOT_CONFIGURED",
    "message": "OPENROUTER_API_KEY is not configured"
  },
  "graphFingerprint": null,
  "files": {}
}

Manifest zachowuje pełny audyt nieudanego etapu i wersję runtime, ale nie
publikuje nieistniejącego grafu ani nie zmienia latest.json. Przy błędnym ID
modelu kod LLM_INVALID_MODEL zawiera dodatkowo aktualną, posortowaną listę ID
z endpointu OpenRouter /models.

Pełny pipeline bez połączeń LLM (również wtedy, gdy lokalny .env zawiera klucz):

node dist/src/cli.js pipeline examples \
  --task task.md \
  --todo TODO.md \
  --changelog CHANGELOG.md \
  --docs 'docs/**/*.md' \
  --nl-mode deterministic \
  --markdown-mode deterministic \
  --no-docs-llm \
  --no-summary-llm \
  --out .intent-demo

Pełny pipeline z OpenRouter:

# w .env:
# OPENROUTER_API_KEY=...
# T2C_NL_MODE=prefer-llm
# T2C_MARKDOWN_MODE=prefer-llm
# OPENROUTER_NL_MODEL=qwen/qwen3.7-plus
# OPENROUTER_MARKDOWN_MODEL=qwen/qwen3.7-plus
# OPENROUTER_DOC_MODEL=openrouter/auto-beta
# OPENROUTER_SUMMARY_MODEL=openrouter/auto-beta
# OPENROUTER_TASK_MODEL=qwen/qwen3.7-plus

node dist/src/cli.js pipeline /ścieżka/do/repo \
  --task project/ticket-014/README.md \
  --todo TODO.md \
  --changelog CHANGELOG.md \
  --docs 'README.md,docs/**/*.md,project/**/*.md'

Tryb ciągły skanuje repozytorium deterministycznie i generuje raport najwyżej
raz na wskazany interwał:

node dist/src/cli.js watch . \
  --interval 60 \
  --scan-interval 2 \
  --no-docs-llm \
  --out .intent

Watcher scala reguły z .gitignore, .dockerignore i .intentignore, pomija
symlinki oraz po raporcie odświeża snapshot, więc własne artefakty nie tworzą
pętli. t2c init instaluje bazowy .intentignore; --no-initial-report
pozwala czekać na pierwszą rzeczywistą zmianę.

CLI

t2c init [root]
t2c doctor

t2c extract nl <file> [--root .] [--out nl.intent.jsonl]
t2c extract git [--root .] [--count 10] [--out git.intent.jsonl]
t2c extract ast [root] [--out ast.intent.jsonl]
t2c extract markdown [--todo TODO.md] [--changelog CHANGELOG.md] [--markdown-mode deterministic|prefer-llm|require-llm]
t2c extract docs [--patterns 'README.md,docs/**/*.md']

t2c link <*.intent.jsonl>... --out intent.graph.json
t2c diagnose intent.graph.json --out diagnostics.json
t2c diff before.graph.json after.graph.json --out graph.diff.json --svg graph.diff.svg
t2c diff --mode files before.ts after.ts --svg files.diff.svg --html files.diff.html
t2c diff --mode git . --rev HEAD --svg worktree.diff.svg
t2c reality intent.graph.json --diagnostics diagnostics.json --svg reality.svg --md reality.md
t2c summarize intent.graph.json --diagnostics diagnostics.json --out team-summary.md
t2c watch [root] [--interval 60] [--scan-interval 2] [--no-initial-report]
t2c compare-workspace [root] [--base origin/main] [--task TASK.md] [--docs-llm]
t2c pipeline [root] --task TASK.md --todo TODO.md --changelog CHANGELOG.md
t2c mcp
t2c a2a

extract nl, extract markdown, extract docs i summarize mogą korzystać z
OpenRouter. Dla NL oraz Markdown prefer-llm jest trybem domyślnym: awaria daje
oznaczony fallback; require-llm kończy operację błędem, a deterministic
świadomie pomija sieć. W Markdown LLM nie może zmienić checkboxa, lifecycle,
wersji, daty, kategorii ani provenance — wzbogaca wyłącznie semantykę wpisu.
Dokumentacja bez klucza jest pomijana, a raport może użyć oznaczonego fallbacku.
Etap dokumentacji ma osobne limity fragmentu, liczby fragmentów, rekordów,
współbieżności i timeoutu (T2C_DOC_*). Najpierw analizuje fragmenty pasujące
do ścieżek, symboli, ticketów i wersji wykrytych w pozostałych źródłach; obcięcie
budżetu zapisuje ostrzeżenie DOC_CHUNK_BUDGET.

Origin vs bieżący workspace

Porównanie nie wykonuje checkoutu w katalogu użytkownika. Runtime rozwiązuje bazę
do pełnego SHA, tworzy prywatny tymczasowy Git worktree i uruchamia ten sam
TypeScript pipeline na bazie oraz aktualnym filesystemie:

node dist/src/cli.js compare-workspace . --base origin/main --out .intent

Stan workspace obejmuje lokalne commity, indeks, zmiany unstaged i pliki
untracked. Wynik t2c.workspace-comparison/v1 zawiera ahead/behind, listę
zmienionych plików, diff rekordów i relacji oraz zmianę metryk Intent vs Reality:
pełne alignmentRate, pokrycie deklarowanej intencji implementacją, udział kodu
posiadającego plan i dokumentację, gaps oraz liczniki diagnostyk. Trend może być
improved, regressed, mixed albo unchanged. Artefakty trafiają do:

.intent/comparisons/<comparison-id>/
├── comparison.json
├── trend.md
├── intent-diff.svg
├── base.graph.json
├── workspace.graph.json
├── base-reality.md
├── workspace-reality.md
└── workspace-reality.svg

Narracyjne podsumowania obu przebiegów są zawsze deterministyczne i nie wykonują
zbędnych zapytań LLM. Dokumentacja LLM po obu stronach jest opcjonalna, ponieważ
podwaja liczbę zapytań i może wprowadzać niedeterministyczny szum. Jeśli podano
--task, ekstrakcja NL respektuje T2C_NL_MODE i jest osobno audytowana:

t2c compare-workspace . --base origin/main --docs-llm \
  --docs 'README.md,docs/**/*.md,.intent/runs/<run-id>/team-summary.md' \
  --doc-excludes 'node_modules/**,.git/**,dist/**,TODO.md,CHANGELOG.md'

Usunięcie .intent/** z --doc-excludes jest wymagane tylko dla jawnie
wskazanego historycznego raportu. Nie należy używać szerokiego .intent/**/*.md,
bo bieżące raporty zaczęłyby zasilać kolejne runy.

Tryb obserwowania

t2c watch pilnuje lokalnych zmian i generuje świeży raport najwyżej raz na minutę:

node dist/src/cli.js watch . --task TASK.md --no-docs-llm

Obowiązują dwa niezależne czasy:

Opcja Domyślnie Znaczenie
--scan-interval 2 s jak szybko zmiana zostaje zauważona
--interval 60 s minimalny odstęp między dwoma raportami

Zmiany napływające częściej niż --interval są kumulowane, a nie kolejkowane: po
upływie progu powstaje jeden raport obejmujący wszystko, co się zmieniło. Raport
nigdy nie startuje, gdy poprzedni jeszcze trwa, więc wolny pipeline nie tworzy
nakładających się runów. --no-initial-report pomija raport startowy i czeka na
pierwszą realną zmianę.

Detekcja opiera się na cyklicznym skanowaniu (rozmiar + mtime), a nie na
fs.watch, który zależy od platformy i gubi zdarzenia pod obciążeniem. Skan jest
tani, bo katalogi wykluczone są odcinane przed odczytem — node_modules nigdy
nie jest czytane.

Pliki ignorowane

Watch pomija ścieżki wymienione w trzech plikach, czytanych w tej kolejności:

  1. .gitignore
  2. .dockerignore
  3. .intentignore

Późniejszy plik wygrywa, więc .intentignore może przywrócić ścieżkę przez !wzorzec.

.intentignore jest zakładany przez t2c init i wyklucza m.in. wszystkie katalogi
kropkowe
(.*/.git, .idea, .venv, .github, .cache), katalog .intent/
z własnymi raportami, wyjścia buildu (node_modules/, dist/, target/,
__pycache__/), lockfile'e oraz logi i pliki tymczasowe.

Składnia jest zgodna z gitignore: komentarze #, negacja !, końcowy /
ogranicza regułę do katalogów, wzorzec bez ukośnika dopasowuje się na dowolnej
głębokości, a ** przechodzi przez katalogi. Reguły .dockerignore
interpretowane tą samą semantyką, czyli nieco szerzej niż robi to Docker
(kotwiczący wzorce do korzenia kontekstu) — wpisy w tym pliku nazywają wyjścia
buildu, więc wykluczenie zagnieżdżonej kopii jest zamierzone.

Artefakty runu

.intent/
├── latest.json
└── runs/<run-id>/
    ├── nl.intent.jsonl
    ├── git.intent.jsonl
    ├── ast.intent.jsonl
    ├── todo.intent.jsonl
    ├── changelog.intent.jsonl
    ├── document.intent.jsonl
    ├── intent.graph.json
    ├── diagnostics.json
    ├── team-summary.md
    └── manifest.json

Każdy rekord zawiera identyfikator, statement, lifecycle, dokładne źródło, hash treści, klasę epistemiczną, confidence i podstawy wnioskowania. Fakty AST mają confidence 1.0. Rekordy wygenerowane przez LLM są oznaczone jako llm_inference i mają pułap zależny od struktury źródła: 0.94 dla wzbogaconych pozycji TODO/CHANGELOG, 0.90 dla prozy NL i 0.85 dla dokumentacji. Żaden z nich nie sięga poziomu obserwacji deterministycznej — pełną tabelę zawiera docs/DSL.md.

manifest.json zapisuje również runtime.version, bezpieczny snapshot i
fingerprint konfiguracji oraz statusy naturalLanguageExtraction,
markdownExtraction, documentationExtraction i summary. Status runu degraded jest pokazywany
w CLI, GET /api/runs i UI. Parametry obejmują modele, timeout, temperaturę,
limit tokenów, budżet dokumentów, konfigurację adapterów i tryb structured
output; klucz API nigdy nie jest zapisywany. Odpowiedzi LLM zapisują zwrócone
przez provider responseId, resolved model/provider oraz usage/cost. Każdy
audyt ekstrakcji zawiera też wersję runtime i bezpieczne parametry. Każda awaria
pipeline po utworzeniu runu tworzy manifest status=failed z kodem i etapem, ale bez
nieistniejącego grafu ani aktualizacji latest.json.

MCP

Uruchomienie serwera stdio:

node dist/src/interfaces/mcp.js

Przykładowa konfiguracja hosta MCP:

{
  "mcpServers": {
    "todo2code": {
      "command": "node",
      "args": ["/absolute/path/todo2code/dist/src/interfaces/mcp.js"],
      "env": {
        "T2C_ROOT": "/absolute/path/workspace",
        "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}"
      }
    }
  }
}

Dostępne narzędzia: extract_nl, extract_git, extract_ast, extract_markdown, extract_docs, extract_communication, analyze_communication, link, diagnose, diff, diff_files, diff_git, reality, compare_workspace, summarize, pipeline. Serwer udostępnia też zasoby t2c://latest/*.

Diff DSL, SVG i SDK

Porównanie dwóch grafów zwraca kanoniczny t2c.diff/v1 z rekordami added, removed, changed i liczbą elementów bez zmian. --mode files tworzy deterministyczny diff linii t2c.filediff/v1, a --mode git stosuje ten sam silnik do rewizji, indeksu lub drzewa roboczego. Dostępne są widoki SVG, HTML oraz unified diff; nie wymagają bibliotek renderujących i nie wykonują treści pochodzącej z plików.

Polecenie t2c reality projektuje pojedynczy graf do t2c.reality/v1: zestawia deklaracje z taska, TODO i dokumentacji z faktami Git/AST, a rozbieżności pokazuje jako SVG albo tabelę Markdown.

Po uruchomieniu A2A dostępne są:

  • frontend: http://localhost:8787/ui — pobiera historię z .intent/runs, domyślnie wybiera dwa najnowsze kompletne runy i automatycznie pokazuje ich diff SVG;
  • historia runów: GET http://localhost:8787/api/runs;
  • REST diff: POST http://localhost:8787/api/diff;
  • A2A/MCP action: diff.

POST /api/diff domyślnie zwraca pełny t2c.diff/v1. Ustawienie compact: true
zwraca projekcję przeznaczoną dla UI: fingerprinty, liczniki summary i opcjonalny
SVG, bez pełnych tablic rekordów oraz relacji.

SDK TypeScript/JavaScript:

import { Todo2CodeClient } from 'todo2code/sdk';

const client = new Todo2CodeClient({ baseUrl: 'http://localhost:8787' });
const result = await client.diffGraphs(beforeGraph, afterGraph);
console.log(result.diff.summary, result.svg);

const files = await client.diffTextFiles('before.ts', 'after.ts', { includeHtml: true });
const reality = await client.reality(afterGraph);
const comparison = await client.compareWorkspace({ root: '.', base: 'origin/main' });

SDK Python nie ma zewnętrznych zależności:

from sdk.python import Todo2CodeClient

client = Todo2CodeClient("http://localhost:8787")
result = client.diff_graphs(before_graph, after_graph)
print(result["diff"]["summary"])

files = client.diff_text_files("before.ts", "after.ts", include_html=True)
reality = client.reality(after_graph)
comparison = client.compare_workspace(root=".", base="origin/main")

Można go także zainstalować przez python3 -m pip install ./sdk/python i importować jako todo2code_sdk.

Uruchamialne przykłady znajdują się w examples/sdk/typescript.mjs i examples/sdk/python.py.

Pomiary oraz bezpieczne i semantycznie istotne dalsze optymalizacje opisuje docs/OPTIMIZATION.md.

SDK dla pięciu języków

Katalog sdk/ zawiera pełne klienty A2A v1.0 udostępniające wszystkie akcje runtime'u (nie tylko diff), wraz z typami Intent DSL:

Język Katalog Zależności Klasa
TypeScript / Node sdk/typescript/ brak T2CClient
Python 3.10+ sdk/python/ brak T2CClient
Go 1.21+ sdk/go/ brak todo2code.Client
Rust 1.70+ sdk/rust/ serde_json todo2code::Client
PHP 8.1+ sdk/php/ brak Todo2Code\Client

Każdy język ma uruchamialny przykład w sdk/<język>/examples/. Wszystkie przepuszczają ten sam zbiór rekordów przez link i muszą otrzymać identyczny fingerprint grafu — to test wierności round-tripu typów. Szczegóły: sdk/README.md.

Python udostępnia także lokalny TypeScriptRuntime. Nie kopiuje implementacji
DSL do Pythona, tylko uruchamia przez Node.js skompilowany dist/src/cli.js:

make python-wheel
python3 -m pip install .intent-packages/python/todo2code_sdk-*.whl
T2C_TYPESCRIPT_CLI="$PWD/dist/src/cli.js" python3 sdk/python/examples/local_runtime.py

Most obsługuje pipeline, diagnose, graph diff oraz reality bez serwera
A2A. Szczegóły i przykład API: sdk/python/README.md.

Przykładowe repozytoria

examples/backend (HTTP API bez zależności) i examples/frontend (panel DOM bez frameworka) to gotowe wejścia dla runtime'u DSL. Każde ma task.md, TODO.md, CHANGELOG.md, README.md i src/, i celowo zawiera rozbieżności plan↔kod, żeby t2c reality miał co pokazać:

node dist/src/cli.js pipeline examples/backend \
  --task task.md --todo TODO.md --changelog CHANGELOG.md \
  --docs 'README.md' --no-docs-llm --out .intent

node dist/src/cli.js reality examples/backend/.intent/runs/<run-id>/intent.graph.json \
  --diagnostics examples/backend/.intent/runs/<run-id>/diagnostics.json \
  --svg reality.svg --md reality.md

A2A v1.0

node dist/src/interfaces/a2a.js

Agent Card:

curl http://localhost:8787/.well-known/agent-card.json

Uruchomienie pipeline przez SendMessage:

curl -s http://localhost:8787/a2a \
  -H 'Content-Type: application/json' \
  -H 'A2A-Version: 1.0' \
  -d '{
    "jsonrpc":"2.0",
    "id":"req-1",
    "method":"SendMessage",
    "params":{
      "message":{
        "messageId":"msg-1",
        "role":"ROLE_USER",
        "parts":[{
          "data":{
            "action":"pipeline",
            "input":{
              "root":".",
              "task":"TASK.md",
              "includeDocsLlm":false
            }
          },
          "mediaType":"application/json"
        }]
      }
    }
  }'

Interfejs A2A jest v1-only: nagłówek A2A-Version: 1.0 (albo parametr zapytania o tej nazwie) jest wymagany. Brak nagłówka oznacza protokół 0.3 i jest odrzucany kodem -32009; aliasy metod v0.3 nie są przyjmowane. GetTask i CancelTask zwracają task bez wrappera, a ListTasks obsługuje filtry, cursor pagination, historyLength oraz includeArtifacts (domyślnie false).

Ustawienie T2C_A2A_TOKEN włącza Bearer authentication i izolację tasków według principalu. Domyślnie MCP i A2A nie mogą analizować ścieżek poza T2C_ROOT; wyjątek wymaga jawnego T2C_ALLOW_OUTSIDE_ROOT=true.

Domyślny task store A2A pozostaje pamięciowy. Aby zachować taski po restarcie
i współdzielić je między replikami używającymi tego samego wolumenu, ustaw:

T2C_A2A_TASK_STORE=.intent/a2a-tasks.json

Snapshot jest zapisywany atomowo z uprawnieniami 0600. Blokada katalogowa
chroni idempotency i aktualizacje między procesami; ścieżka podlega tym samym
ograniczeniom T2C_ROOT co pozostałe operacje runtime'u.

OpenRouter

Runtime używa POST /api/v1/chat/completions. Ekstraktory NL i dokumentacji
oraz synteza zadań proszą o response_format: json_schema, wymuszają
provider.require_parameters, a przy braku wsparcia endpointu próbują
kontrolowanego fallbacku json_object. Opcjonalny plugin response-healing
jest sterowany przez .env. Osobny OPENROUTER_TASK_MODEL wybiera model dla
graf + diagnostyka → zadania i domyślnie dziedziczy OPENROUTER_MODEL.

Klucz nie jest zapisywany do artefaktów, logów ani odpowiedzi MCP/A2A. doctor pokazuje jedynie status configured/not configured.

Do czasu dodania komendy propose-todo etap jest publicznym API TypeScript:

import { readFile } from 'node:fs/promises';
import { getConfig, synthesizeTodoProposals } from 'todo2code';

const graph = JSON.parse(await readFile('.intent/runs/<run>/intent.graph.json', 'utf8'));
const diagnostics = JSON.parse(await readFile('.intent/runs/<run>/diagnostics.json', 'utf8'));
const result = await synthesizeTodoProposals(graph, diagnostics, getConfig(), 'require-llm');
console.log(JSON.stringify(result, null, 2));

W prefer-llm awaria daje puste conclusions/proposals i osobne
rawDiagnosticActions; nie są one oznaczane jako wynik semantycznej syntezy.

Opcjonalny TensorFlow

NL i Git zawsze mają deterministyczny klasyfikator słownikowy. Lokalny model TensorFlow można włączyć przez:

T2C_ENABLE_TF=true
T2C_TF_MODEL_PATH=/models/action/model.json
T2C_TF_MODULE_PATH=adapters/tensorflow/node_modules/@tensorflow/tfjs-node/dist/index.js
T2C_TF_LABELS=add,fix,remove,refactor,test,document,configure,analyze,unknown

Najpierw należy wykonać make install-tf. Obok model.json musi znajdować się
vocabulary.json, czyli mapa token → indeks. Model powinien przyjmować tensor
[1, vocabulary_size] i zwracać rozkład klas. Przy braku adaptera lub błędzie
modelu runtime wraca do heurystyk i zapisuje heuristic_fallback:<powód>.

Docker i Makefile

make setup
make verify
make demo
make docker-build
make docker-up

Jedynym plikiem Compose jest docker-compose.yml. Montuje repozytorium
T2C_WORKSPACE pod /workspace, wystawia kontenerowy port 8787 jako
T2C_DOCKER_HOST_PORT i zachowuje .intent w analizowanym workspace. Przy
zmianie portu hosta należy odpowiednio ustawić również publiczny
T2C_A2A_PUBLIC_URL oraz kliencki T2C_A2A_URL.

Diagnostyka

Wbudowane klasy obejmują m.in.:

  • PLANNED_NOT_IMPLEMENTED;
  • IMPLEMENTED_NOT_PLANNED;
  • IMPLEMENTED_NOT_DOCUMENTED;
  • CHANGELOG_WITHOUT_IMPLEMENTATION;
  • CONFLICTING_INTENT;
  • AMBIGUOUS_REQUIREMENT;
  • UNLINKED_RECORD.

ALIGNED oznacza wyłącznie brak wykrytej blokującej rozbieżności w dostępnych źródłach. Nie nadaje automatycznie statusu DONE i nie zastępuje decyzji człowieka.

Dokumentacja projektu