feat: add claude-code llm backend with runtime port (#115)

* docs: revise claude-code ingest backend spec

* docs: keep claude-code spec focused on ingest

* docs: expand claude-code spec to full llm parity

* Refine claude-code backend spec after adversarial review iteration 1

* Refine claude-code backend spec after adversarial review iteration 2

* Refine claude-code backend spec after adversarial review iteration 3

* feat: recognize claude-code llm backend

* feat: add ktx llm runtime port

* feat: add claude-code llm runtime

* feat: route non-agent llm calls through runtime

* feat: run ingest agents through llm runtime

* feat: support claude-code setup and status

* test: verify claude-code backend runtime

* docs: add claude-code backend v1 runtime plan

* fix: close claude-code runtime isolation checks

* fix: warn on claude-code prompt caching during setup

* chore: verify claude-code v1 closure

* docs: add claude-code backend v1 isolation closure plan

* fix: update claude-code ingest setup guidance

* docs: add claude-code backend v1 ingest guidance closure plan

* docs: align claude-code isolation spec with sdk metadata

* test: cover claude-code host discovery metadata

* fix: tolerate claude-code host discovery metadata

* docs: clarify claude-code host discovery metadata

* docs: add claude-code auth-probe isolation fix plan

* chore: prepare kaelio ktx rc1 release

* chore: add semantic release workflow

* fix: unblock ci checks

* chore(release): 0.1.0-rc.1

* feat: add Claude Code model selection to setup

* fix: keep git maintenance attached in local repos
This commit is contained in:
Andrey Avtomonov 2026-05-16 12:06:34 +02:00 committed by GitHub
parent e6d578c03f
commit b565e44a22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
109 changed files with 10218 additions and 1093 deletions

View file

@ -1,4 +1,5 @@
import { basename } from 'node:path';
import { runClaudeCodeAuthProbe } from '@ktx/context';
import type {
KtxConfigIssue,
KtxLocalProject,
@ -8,6 +9,11 @@ import type {
KtxProjectLlmConfig,
} from '@ktx/context/project';
import type { PostgresPgssProbeResult } from '@ktx/context/ingest';
import {
formatClaudeCodePromptCachingFix,
formatClaudeCodePromptCachingWarning,
ignoredClaudeCodePromptCachingFields,
} from './claude-code-prompt-caching.js';
import type { DoctorCheck } from './doctor.js';
import {
bold as _bold,
@ -77,6 +83,12 @@ interface WarningItem {
fix?: string;
}
type ClaudeCodeAuthProbe = (input: {
projectDir: string;
model: string;
env?: NodeJS.ProcessEnv;
}) => Promise<{ ok: true } | { ok: false; message: string }>;
const PROJECT_READY_COMMANDS = KTX_NEXT_STEP_DIRECT_COMMANDS.map((step) => step.command);
function isRecord(value: unknown): value is Record<string, unknown> {
@ -134,7 +146,15 @@ function envHint(value: unknown): string | undefined {
return undefined;
}
function buildLlmStatus(config: KtxProjectLlmConfig, env: NodeJS.ProcessEnv): LlmStatus {
async function buildLlmStatus(
config: KtxProjectLlmConfig,
options: {
projectDir: string;
env: NodeJS.ProcessEnv;
claudeCodeAuthProbe?: ClaudeCodeAuthProbe;
},
): Promise<LlmStatus> {
const env = options.env;
const backend = config.provider.backend;
const model = config.models?.default;
if (backend === 'none') {
@ -186,6 +206,26 @@ function buildLlmStatus(config: KtxProjectLlmConfig, env: NodeJS.ProcessEnv): Ll
fix: hint ? `Set ${hint}` : 'Set the gateway api_key or rerun `ktx setup`',
};
}
if (backend === 'claude-code') {
const modelName = model ?? 'sonnet';
const probe = options.claudeCodeAuthProbe ?? runClaudeCodeAuthProbe;
const auth = await probe({ projectDir: options.projectDir, model: modelName, env });
if (auth.ok) {
return {
backend,
model: modelName,
status: 'ok',
detail: 'local Claude Code session authenticated',
};
}
return {
backend,
model: modelName,
status: 'fail',
detail: auth.message,
fix: 'Authenticate Claude Code locally with the Claude Code CLI, then rerun `ktx status`.',
};
}
return { backend, model, status: 'warn', detail: 'unknown LLM backend' };
}
@ -568,6 +608,14 @@ function buildWarnings(
});
}
const warning = formatClaudeCodePromptCachingWarning(ignoredClaudeCodePromptCachingFields(config.llm));
if (warning) {
warnings.push({
message: warning,
fix: formatClaudeCodePromptCachingFix(),
});
}
return warnings;
}
@ -629,6 +677,7 @@ function buildVerdict(
export interface BuildProjectStatusOptions {
env?: NodeJS.ProcessEnv;
postgresQueryHistoryProbe?: PostgresQueryHistoryProbe;
claudeCodeAuthProbe?: ClaudeCodeAuthProbe;
configIssues?: KtxConfigIssue[];
}
@ -649,7 +698,11 @@ export async function buildProjectStatus(project: KtxLocalProject, options: Buil
const config = project.config;
const configStatus = buildConfigStatus(options.configIssues);
const llm = buildLlmStatus(config.llm, env);
const llm = await buildLlmStatus(config.llm, {
projectDir: project.projectDir,
env,
claudeCodeAuthProbe: options.claudeCodeAuthProbe,
});
const embeddings = buildEmbeddingsStatus(config.ingest.embeddings, env);
const storage = buildStorageStatus(config);
const connections = Object.entries(config.connections).map(([name, conn]) =>