From d33b06b25b2a0051288a2d39882131575b71ca7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 19 Jul 2026 02:41:00 -0400 Subject: [PATCH] fix: model Kiro CLI hook variants --- integrations/README.md | 2 + integrations/catalog.json | 45 +++++++---- integrations/catalog.schema.json | 128 ++++++++++++++++++++++++++++++ test/integration-catalog.test.mjs | 42 ++++++++++ 4 files changed, 202 insertions(+), 15 deletions(-) diff --git a/integrations/README.md b/integrations/README.md index 11db945..874c9cb 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -2,6 +2,8 @@ [`catalog.json`](catalog.json) is the canonical inventory for agent integration IDs, official hook contracts, blocking behavior, and SDK delivery state. Backend validation, Console controls, Docs, and SDK registries derive their integration facts from this catalog. +The provider-level contract represents the default stable runtime. `contract_variants` records opt-in or transitional runtimes with their activation command and release channel. Kiro CLI uses this field for its v2 embedded hooks and v3 standalone hooks. + `delivery_state` is computed from the `node`, `python`, and `go` adapter flags: | State | Meaning | diff --git a/integrations/catalog.json b/integrations/catalog.json index 903ec3a..50f5c79 100644 --- a/integrations/catalog.json +++ b/integrations/catalog.json @@ -10,11 +10,7 @@ "surface": "cli", "integration_mode": "command_hooks", "config_format": "json", - "config_paths": [ - "~/.augment/settings.json", - ".augment/settings.json", - ".augment/settings.local.json" - ], + "config_paths": ["~/.augment/settings.json", ".augment/settings.json", ".augment/settings.local.json"], "events": { "before_tool": "PreToolUse", "after_tool": "PostToolUse" @@ -43,11 +39,7 @@ "surface": "cli", "integration_mode": "command_hooks", "config_format": "json", - "config_paths": [ - "~/.claude/settings.json", - ".claude/settings.json", - ".claude/settings.local.json" - ], + "config_paths": ["~/.claude/settings.json", ".claude/settings.json", ".claude/settings.local.json"], "events": { "before_tool": "PreToolUse", "after_tool": "PostToolUse" @@ -100,10 +92,7 @@ "surface": "cli", "integration_mode": "command_hooks", "config_format": "json", - "config_paths": [ - "~/.codex/hooks.json", - ".codex/hooks.json" - ], + "config_paths": ["~/.codex/hooks.json", ".codex/hooks.json"], "events": { "before_tool": "PreToolUse", "after_tool": "PostToolUse" @@ -336,7 +325,33 @@ "go": true }, "delivery_state": "available", - "source_url": "https://kiro.dev/docs/cli/hooks/" + "source_url": "https://kiro.dev/docs/cli/hooks/", + "contract_variants": [ + { + "id": "v3", + "release_channel": "early_access", + "activation": "kiro-cli --v3", + "config_format": "json", + "config_paths": [ + "~/.kiro/hooks/*.json", + ".kiro/hooks/*.json" + ], + "events": { + "before_tool": "PreToolUse", + "after_tool": "PostToolUse" + }, + "event_fields": { + "name": "tool_name", + "input": "tool_input", + "session": "session_id" + }, + "blocking": { + "mechanism": "exit_code_2", + "failure_mode": "fail_open" + }, + "source_url": "https://kiro.dev/docs/cli/v3/hooks/" + } + ] }, { "id": "kiroide", diff --git a/integrations/catalog.schema.json b/integrations/catalog.schema.json index dace43c..6b71a7c 100644 --- a/integrations/catalog.schema.json +++ b/integrations/catalog.schema.json @@ -180,6 +180,13 @@ "type": "string", "format": "uri", "pattern": "^https://" + }, + "contract_variants": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/contractVariant" + } } } } @@ -210,5 +217,126 @@ } } } + }, + "$defs": { + "contractVariant": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "release_channel", + "activation", + "config_format", + "config_paths", + "events", + "blocking", + "source_url" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z][a-z0-9]*$" + }, + "release_channel": { + "enum": [ + "stable", + "early_access", + "legacy" + ] + }, + "activation": { + "type": "string", + "minLength": 1 + }, + "config_format": { + "enum": [ + "json", + "toml", + "plugin" + ] + }, + "config_paths": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "events": { + "type": "object", + "additionalProperties": false, + "required": [ + "before_tool", + "after_tool" + ], + "properties": { + "before_tool": { + "type": "string", + "minLength": 1 + }, + "after_tool": { + "type": "string", + "minLength": 1 + } + } + }, + "event_fields": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "input", + "session" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "input": { + "type": "string", + "minLength": 1 + }, + "session": { + "type": "string", + "minLength": 1 + } + } + }, + "blocking": { + "type": "object", + "additionalProperties": false, + "required": [ + "mechanism", + "failure_mode" + ], + "properties": { + "mechanism": { + "enum": [ + "any_nonzero_exit", + "exception", + "exit_code_2", + "hook_policy" + ] + }, + "failure_mode": { + "enum": [ + "adapter_controlled", + "configurable", + "fail_closed", + "fail_open" + ] + } + } + }, + "source_url": { + "type": "string", + "format": "uri", + "pattern": "^https://" + } + } + } } } diff --git a/test/integration-catalog.test.mjs b/test/integration-catalog.test.mjs index b60b545..45118ce 100644 --- a/test/integration-catalog.test.mjs +++ b/test/integration-catalog.test.mjs @@ -108,6 +108,19 @@ test('every provider exposes a complete, machine-readable hook contract', async assert.equal(typeof provider.event_fields.input, 'string'); assert.equal(typeof provider.event_fields.session, 'string'); } + + for (const variant of provider.contract_variants ?? []) { + assert.match(variant.id, /^[a-z][a-z0-9]*$/); + assert.ok(['stable', 'early_access', 'legacy'].includes(variant.release_channel)); + assert.equal(typeof variant.activation, 'string'); + assert.ok(['json', 'toml', 'plugin'].includes(variant.config_format)); + assert.ok(Array.isArray(variant.config_paths) && variant.config_paths.length > 0); + assert.equal(typeof variant.events.before_tool, 'string'); + assert.equal(typeof variant.events.after_tool, 'string'); + assert.equal(typeof variant.blocking.mechanism, 'string'); + assert.equal(typeof variant.blocking.failure_mode, 'string'); + assert.match(variant.source_url, /^https:\/\//); + } } }); @@ -121,6 +134,12 @@ test('schema freezes the provider contract and supported enums', async () => { assert.deepEqual(providerSchema.properties.surface.enum, ['cli', 'ide', 'cli_and_ide']); assert.deepEqual(providerSchema.properties.integration_mode.enum, ['command_hooks', 'plugin_api']); assert.deepEqual(providerSchema.properties.delivery_state.enum, ['available', 'partial', 'planned']); + assert.equal(providerSchema.properties.contract_variants.items.$ref, '#/$defs/contractVariant'); + assert.deepEqual(schema.$defs.contractVariant.properties.release_channel.enum, [ + 'stable', + 'early_access', + 'legacy', + ]); }); test('high-drift providers retain their verified hook contracts', async () => { @@ -145,6 +164,29 @@ test('high-drift providers retain their verified hook contracts', async () => { '~/.kiro/agents/*.json', '.kiro/agents/*.json', ]); + assert.deepEqual(providers.get('kirocli').contract_variants, [ + { + id: 'v3', + release_channel: 'early_access', + activation: 'kiro-cli --v3', + config_format: 'json', + config_paths: ['~/.kiro/hooks/*.json', '.kiro/hooks/*.json'], + events: { + before_tool: 'PreToolUse', + after_tool: 'PostToolUse', + }, + event_fields: { + name: 'tool_name', + input: 'tool_input', + session: 'session_id', + }, + blocking: { + mechanism: 'exit_code_2', + failure_mode: 'fail_open', + }, + source_url: 'https://kiro.dev/docs/cli/v3/hooks/', + }, + ]); assert.deepEqual(providers.get('kiroide').config_paths, [ '.kiro/hooks/*.json', ]);