refactor: enforce ktx naming and AGENTS.md compliance sweep (#289)

Align the tree with AGENTS.md/CLAUDE.md conventions:

- Rewrite user-facing strings, docs, and tests to lowercase `ktx`
  (no bare uppercase `KTX` tokens remain outside literal identifiers).
- Drop the legacy `historicSql` migration path and its now-unused
  helpers, per the no-backward-compat rule.
- Remove `as unknown as` / `any` casts: narrow `BaseTool` generics to
  `z.ZodObject`, add a typed `createLookerClient`, and delete the dead
  `getParametersSchema`/`toAnthropicFormat` pre-AI-SDK helpers.
- Use `InvalidArgumentError` for Commander parse failures.
- Finish the adapter→connector prose conversion in the `ktx.yaml` docs
  while keeping the literal `adapters` config key.
This commit is contained in:
Andrey Avtomonov 2026-06-11 13:49:45 +02:00 committed by GitHub
parent 005c5fc860
commit 00cdf2de90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
237 changed files with 844 additions and 974 deletions

View file

@ -569,7 +569,7 @@ export class KtxDescriptionGenerator {
if (!connector.sampleTable) {
fallbackReason = 'capability_missing';
this.logger?.warn('KTX scan connector does not support table sampling; falling back to metadata-only prompt', {
this.logger?.warn('ktx scan connector does not support table sampling; falling back to metadata-only prompt', {
connectorId: input.connector.id,
table: input.table.name,
});
@ -690,7 +690,7 @@ export class KtxDescriptionGenerator {
let fallbackReason: 'capability_missing' | 'sampling_failed' | 'empty_sample' | null = null;
if (!input.connector.sampleTable) {
fallbackReason = 'capability_missing';
this.logger?.warn('KTX scan connector does not support table sampling; falling back to metadata-only prompt', {
this.logger?.warn('ktx scan connector does not support table sampling; falling back to metadata-only prompt', {
connectorId: input.connector.id,
table: input.table.name,
});
@ -846,7 +846,7 @@ export class KtxDescriptionGenerator {
}
if (!input.connector.sampleTable) {
this.logger?.warn('KTX scan connector does not support table sampling for data-source description generation', {
this.logger?.warn('ktx scan connector does not support table sampling for data-source description generation', {
connectorId: input.connector.id,
});
return 'No accessible tables found in database';
@ -927,7 +927,7 @@ export class KtxDescriptionGenerator {
let columnValues = column.sampleValues;
if (!columnValues || columnValues.length === 0) {
if (!input.connector.sampleColumn) {
this.logger?.warn('KTX scan connector does not support column sampling; using available metadata only', {
this.logger?.warn('ktx scan connector does not support column sampling; using available metadata only', {
connectorId: input.connector.id,
table: input.table.name,
column: column.name,

View file

@ -154,7 +154,7 @@ function scanReportPath(connectionId: string, syncId: string): string {
function assertSupportedMode(mode: KtxScanMode): void {
if (mode !== 'structural' && mode !== 'relationships' && mode !== 'enriched') {
throw new Error(`Unsupported KTX scan mode: ${mode}`);
throw new Error(`Unsupported ktx scan mode: ${mode}`);
}
}
@ -544,7 +544,7 @@ export async function runLocalScan(options: RunLocalScanOptions): Promise<LocalS
}
report.warnings.push({
code: 'enrichment_failed',
message: `KTX scan enrichment failed after structural scan completed: ${message}`,
message: `ktx scan enrichment failed after structural scan completed: ${message}`,
recoverable: true,
metadata: { mode, detectRelationships: options.detectRelationships ?? false },
});

View file

@ -55,7 +55,7 @@ function parseWarning(rawWarning: unknown, path: string): KtxScanWarning {
typeof rawWarning.message !== 'string' ||
typeof rawWarning.recoverable !== 'boolean'
) {
throw new Error(`Invalid KTX schema warning artifact: ${path}`);
throw new Error(`Invalid ktx schema warning artifact: ${path}`);
}
return {
code: rawWarning.code as KtxScanWarning['code'],
@ -73,7 +73,7 @@ async function readWarnings(input: ReadLocalScanStructuralSnapshotInput): Promis
const warningRaw = await input.project.fileStore.readFile(path);
const parsed = JSON.parse(warningRaw.content) as unknown;
if (!isRecord(parsed) || !Array.isArray(parsed.warnings)) {
throw new Error(`Invalid KTX schema warnings artifact: ${path}`);
throw new Error(`Invalid ktx schema warnings artifact: ${path}`);
}
return parsed.warnings.map((warning) => parseWarning(warning, path));
} catch (error) {
@ -102,7 +102,7 @@ function parseColumn(rawColumn: unknown, path: string): KtxSchemaColumn {
rawColumn.dimensionType !== 'number' &&
rawColumn.dimensionType !== 'boolean')
) {
throw new Error(`Invalid KTX schema column artifact: ${path}`);
throw new Error(`Invalid ktx schema column artifact: ${path}`);
}
return {
name: rawColumn.name,
@ -122,7 +122,7 @@ function parseForeignKey(rawForeignKey: unknown, path: string): KtxSchemaForeign
typeof rawForeignKey.toTable !== 'string' ||
typeof rawForeignKey.toColumn !== 'string'
) {
throw new Error(`Invalid KTX schema foreign key artifact: ${path}`);
throw new Error(`Invalid ktx schema foreign key artifact: ${path}`);
}
return {
fromColumn: rawForeignKey.fromColumn,
@ -137,7 +137,7 @@ function parseForeignKey(rawForeignKey: unknown, path: string): KtxSchemaForeign
function parseTable(raw: string, path: string): KtxSchemaTable {
const parsed = JSON.parse(raw) as unknown;
if (!isRecord(parsed) || typeof parsed.name !== 'string' || !Array.isArray(parsed.columns)) {
throw new Error(`Invalid KTX schema table artifact: ${path}`);
throw new Error(`Invalid ktx schema table artifact: ${path}`);
}
return {
catalog: optionalStringOrNull(parsed.catalog) ?? null,

View file

@ -317,7 +317,7 @@ function compositeSkipBlocks(report: KtxRelationshipBenchmarkReport): string[] {
export function formatKtxRelationshipBenchmarkReportMarkdown(report: KtxRelationshipBenchmarkReport): string {
const lines = [
'# KTX Relationship Discovery Benchmark Evidence',
'# ktx Relationship Discovery Benchmark Evidence',
'',
`Generated: ${report.generatedAt}`,
'',

View file

@ -153,7 +153,7 @@ async function detectCompositeRelationships(input: {
} catch (error) {
input.warnings.push({
code: 'relationship_validation_failed',
message: `KTX composite relationship detection failed: ${error instanceof Error ? error.message : String(error)}`,
message: `ktx composite relationship detection failed: ${error instanceof Error ? error.message : String(error)}`,
recoverable: true,
metadata: { source: 'composite_relationship_detection' },
});
@ -185,7 +185,7 @@ function sqlExecutor(input: DiscoverKtxRelationshipsInput): {
warnings: [
{
code: 'connector_capability_missing',
message: 'KTX scan connector cannot run read-only SQL relationship validation',
message: 'ktx scan connector cannot run read-only SQL relationship validation',
recoverable: true,
metadata: { capability: 'readOnlySql' },
},
@ -199,7 +199,7 @@ function sqlExecutor(input: DiscoverKtxRelationshipsInput): {
warnings: [
{
code: 'relationship_validation_failed',
message: 'KTX scan connector advertises readOnlySql but does not expose executeReadOnly',
message: 'ktx scan connector advertises readOnlySql but does not expose executeReadOnly',
recoverable: true,
metadata: { capability: 'readOnlySql' },
},

View file

@ -182,7 +182,7 @@ function mapValidProposals(
const toColumn = toTable ? findColumn(toTable, item.toColumn) : null;
if (!fromTable || !toTable || !fromColumn || !toColumn) {
warnings.push(
invalidReferenceWarning('KTX relationship LLM proposal referenced a table or column that is not in the schema.', {
invalidReferenceWarning('ktx relationship LLM proposal referenced a table or column that is not in the schema.', {
proposal: item,
}),
);
@ -218,7 +218,7 @@ function generationFailureWarning(error: unknown): KtxScanWarning {
const message = error instanceof Error ? error.message : String(error);
return {
code: 'relationship_llm_proposal_failed',
message: `KTX relationship LLM proposal failed: ${message}`,
message: `ktx relationship LLM proposal failed: ${message}`,
recoverable: true,
};
}
@ -233,7 +233,7 @@ export async function proposeKtxRelationshipCandidatesWithLlm(
const settings = mergeSettings(input.settings);
const evidence = buildEvidencePacket(input.schema, input.profile, settings);
const system = [
'You are helping KTX review possible SQL relationships before validation.',
'You are helping ktx review possible SQL relationships before validation.',
'Use only the compact schema evidence. Propose likely primary keys and foreign keys for later SQL validation.',
'Return structured output only; never assume a join is accepted.',
].join('\n');