refactor: move AiResource types to @backstage/catalog-model alpha
Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
@@ -3,13 +3,39 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import type { Entity } from '@backstage/catalog-model';
|
||||
import type { Entity as Entity_2 } from '@backstage/catalog-model';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { SerializedError } from '@backstage/errors';
|
||||
|
||||
// @alpha
|
||||
export interface AlphaEntity extends Entity {
|
||||
export const aiResourceEntityModel: CatalogModelLayer;
|
||||
|
||||
// @alpha
|
||||
export type AiResourceEntityV1alpha1 =
|
||||
| AiResourceEntityV1alpha1Default
|
||||
| SkillAiResourceEntityV1alpha1;
|
||||
|
||||
// @alpha
|
||||
export interface AiResourceEntityV1alpha1Default extends Entity {
|
||||
// (undocumented)
|
||||
apiVersion: 'backstage.io/v1alpha1';
|
||||
// (undocumented)
|
||||
kind: 'AiResource';
|
||||
// (undocumented)
|
||||
spec: {
|
||||
type: string;
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export const aiResourceEntityV1alpha1Validator: KindValidator;
|
||||
|
||||
// @alpha
|
||||
export interface AlphaEntity extends Entity_2 {
|
||||
status?: EntityStatus;
|
||||
}
|
||||
|
||||
@@ -400,6 +426,43 @@ export function createCatalogModelLayerBuilder(options: {
|
||||
// @alpha
|
||||
export const defaultCatalogEntityModel: CatalogModelLayer;
|
||||
|
||||
// @public
|
||||
export type Entity = {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: EntityMeta;
|
||||
spec?: JsonObject;
|
||||
relations?: EntityRelation[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export type EntityLink = {
|
||||
url: string;
|
||||
title?: string;
|
||||
icon?: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type EntityMeta = JsonObject & {
|
||||
uid?: string;
|
||||
etag?: string;
|
||||
name: string;
|
||||
namespace?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
labels?: Record<string, string>;
|
||||
annotations?: Record<string, string>;
|
||||
tags?: string[];
|
||||
links?: EntityLink[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export type EntityRelation = {
|
||||
type: string;
|
||||
targetRef: string;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export type EntityStatus = {
|
||||
items?: EntityStatusItem[];
|
||||
@@ -416,5 +479,42 @@ export type EntityStatusItem = {
|
||||
// @alpha
|
||||
export type EntityStatusLevel = 'info' | 'warning' | 'error';
|
||||
|
||||
// @alpha
|
||||
export const isAiResourceEntity: (
|
||||
entity: Entity,
|
||||
) => entity is AiResourceEntityV1alpha1;
|
||||
|
||||
// @alpha
|
||||
export const isSkillAiResourceEntity: (
|
||||
entity: Entity,
|
||||
) => entity is SkillAiResourceEntityV1alpha1;
|
||||
|
||||
// @public
|
||||
export type KindValidator = {
|
||||
check(entity: Entity): Promise<boolean>;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export interface SkillAiResourceEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
apiVersion: 'backstage.io/v1alpha1';
|
||||
// (undocumented)
|
||||
kind: 'AiResource';
|
||||
// (undocumented)
|
||||
spec: {
|
||||
type: 'skill';
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
disciplines?: string[];
|
||||
categories?: string[];
|
||||
agents?: string[];
|
||||
dependsOn?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export const skillAiResourceEntityV1alpha1Validator: KindValidator;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -14,11 +14,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type {
|
||||
Entity,
|
||||
EntityLink,
|
||||
EntityMeta,
|
||||
EntityRelation,
|
||||
} from './entity/Entity';
|
||||
export type { KindValidator } from './kinds/types';
|
||||
export type { AlphaEntity } from './entity/AlphaEntity';
|
||||
export type {
|
||||
EntityStatus,
|
||||
EntityStatusItem,
|
||||
EntityStatusLevel,
|
||||
} from './entity/EntityStatus';
|
||||
export type {
|
||||
AiResourceEntityV1alpha1,
|
||||
AiResourceEntityV1alpha1Default,
|
||||
SkillAiResourceEntityV1alpha1,
|
||||
} from './kinds/AiResourceEntityV1alpha1';
|
||||
export {
|
||||
aiResourceEntityV1alpha1Validator,
|
||||
skillAiResourceEntityV1alpha1Validator,
|
||||
isAiResourceEntity,
|
||||
isSkillAiResourceEntity,
|
||||
aiResourceEntityModel,
|
||||
} from './kinds/AiResourceEntityV1alpha1';
|
||||
export * from './model';
|
||||
export { defaultCatalogEntityModel } from './model/defaultCatalogEntityModel';
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import {
|
||||
type AiResourceEntityV1alpha1Default,
|
||||
type SkillAiResourceEntityV1alpha1,
|
||||
aiResourceEntityV1alpha1Validator as defaultValidator,
|
||||
skillAiResourceEntityV1alpha1Validator as skillValidator,
|
||||
isAiResourceEntity,
|
||||
isSkillAiResourceEntity,
|
||||
} from './AiResourceEntityV1alpha1';
|
||||
|
||||
describe('AiResourceV1alpha1 default validator', () => {
|
||||
let entity: AiResourceEntityV1alpha1Default;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AiResource',
|
||||
metadata: {
|
||||
name: 'internal-design-system',
|
||||
},
|
||||
spec: {
|
||||
type: 'rule',
|
||||
lifecycle: 'production',
|
||||
owner: 'frontend-platform',
|
||||
system: 'ai-tooling',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('accepts valid data', async () => {
|
||||
await expect(defaultValidator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('ignores unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(defaultValidator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('ignores unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(defaultValidator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('rejects missing type', async () => {
|
||||
delete (entity as any).spec.type;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects wrong type', async () => {
|
||||
(entity as any).spec.type = 7;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects empty type', async () => {
|
||||
(entity as any).spec.type = '';
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects missing lifecycle', async () => {
|
||||
delete (entity as any).spec.lifecycle;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects wrong lifecycle', async () => {
|
||||
(entity as any).spec.lifecycle = 7;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects empty lifecycle', async () => {
|
||||
(entity as any).spec.lifecycle = '';
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects wrong owner', async () => {
|
||||
(entity as any).spec.owner = 7;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects empty owner', async () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('accepts missing system', async () => {
|
||||
delete (entity as any).spec.system;
|
||||
await expect(defaultValidator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong system', async () => {
|
||||
(entity as any).spec.system = 7;
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
|
||||
it('rejects empty system', async () => {
|
||||
(entity as any).spec.system = '';
|
||||
await expect(defaultValidator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AiResourceV1alpha1 skill validator', () => {
|
||||
let entity: SkillAiResourceEntityV1alpha1;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AiResource',
|
||||
metadata: {
|
||||
name: 'frontend-design',
|
||||
},
|
||||
spec: {
|
||||
type: 'skill',
|
||||
lifecycle: 'production',
|
||||
owner: 'ai-platform-team',
|
||||
system: 'ai-tooling',
|
||||
disciplines: ['web', 'backend'],
|
||||
categories: ['framework'],
|
||||
agents: ['claude-code'],
|
||||
dependsOn: ['airesource:default/base-coding-standards'],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('accepts valid skill data with all fields', async () => {
|
||||
await expect(skillValidator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('accepts skill with only required fields', async () => {
|
||||
entity.spec = {
|
||||
type: 'skill',
|
||||
lifecycle: 'experimental',
|
||||
owner: 'team-a',
|
||||
};
|
||||
await expect(skillValidator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects non-skill type', async () => {
|
||||
(entity as any).spec.type = 'rule';
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects missing lifecycle', async () => {
|
||||
delete (entity as any).spec.lifecycle;
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('accepts missing optional fields', async () => {
|
||||
delete (entity as any).spec.system;
|
||||
delete (entity as any).spec.disciplines;
|
||||
delete (entity as any).spec.categories;
|
||||
delete (entity as any).spec.agents;
|
||||
delete (entity as any).spec.dependsOn;
|
||||
await expect(skillValidator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects disciplines with empty strings', async () => {
|
||||
(entity as any).spec.disciplines = [''];
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/disciplines/);
|
||||
});
|
||||
|
||||
it('rejects categories with wrong type', async () => {
|
||||
(entity as any).spec.categories = 'not-an-array';
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/categories/);
|
||||
});
|
||||
|
||||
it('rejects agents with wrong item type', async () => {
|
||||
(entity as any).spec.agents = [42];
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/agents/);
|
||||
});
|
||||
|
||||
it('rejects dependsOn with empty strings', async () => {
|
||||
(entity as any).spec.dependsOn = [''];
|
||||
await expect(skillValidator.check(entity)).rejects.toThrow(/dependsOn/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAiResourceEntity', () => {
|
||||
it('returns true when apiVersion and kind match', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AiResource',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
expect(isAiResourceEntity(entity)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for a different kind', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
expect(isAiResourceEntity(entity)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for a different apiVersion', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
kind: 'AiResource',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
expect(isAiResourceEntity(entity)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSkillAiResourceEntity', () => {
|
||||
it('returns true for a skill AiResource', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AiResource',
|
||||
metadata: { name: 'test' },
|
||||
spec: { type: 'skill' },
|
||||
};
|
||||
expect(isSkillAiResourceEntity(entity)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for a non-skill AiResource', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AiResource',
|
||||
metadata: { name: 'test' },
|
||||
spec: { type: 'rule' },
|
||||
};
|
||||
expect(isSkillAiResourceEntity(entity)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for a different kind', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: { name: 'test' },
|
||||
spec: { type: 'skill' },
|
||||
};
|
||||
expect(isSkillAiResourceEntity(entity)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createCatalogModelLayer } from '../model/createCatalogModelLayer';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import { entityKindSchemaValidator } from '../validation';
|
||||
import type { KindValidator } from './types';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import defaultJsonSchema from '../schema/kinds/AiResource.v1alpha1.schema.json';
|
||||
import skillJsonSchema from '../schema/kinds/AiResource.v1alpha1.skill.schema.json';
|
||||
|
||||
/**
|
||||
* Default AiResource entity for types that don't have a structured spec.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface AiResourceEntityV1alpha1Default extends Entity {
|
||||
apiVersion: 'backstage.io/v1alpha1';
|
||||
kind: 'AiResource';
|
||||
spec: {
|
||||
type: string;
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* AiResource entity with spec.type 'skill'. Represents reusable contextual
|
||||
* knowledge consumed by AI coding tools.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface SkillAiResourceEntityV1alpha1 extends Entity {
|
||||
apiVersion: 'backstage.io/v1alpha1';
|
||||
kind: 'AiResource';
|
||||
spec: {
|
||||
type: 'skill';
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
disciplines?: string[];
|
||||
categories?: string[];
|
||||
agents?: string[];
|
||||
dependsOn?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Backstage catalog AiResource kind Entity. Represents contextual information
|
||||
* consumed by AI coding tools, such as skills and rules.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type AiResourceEntityV1alpha1 =
|
||||
| AiResourceEntityV1alpha1Default
|
||||
| SkillAiResourceEntityV1alpha1;
|
||||
|
||||
const defaultValidator = entityKindSchemaValidator(defaultJsonSchema);
|
||||
|
||||
/**
|
||||
* Entity data validator for the default {@link AiResourceEntityV1alpha1}.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const aiResourceEntityV1alpha1Validator: KindValidator = {
|
||||
async check(data: Entity) {
|
||||
return defaultValidator(data) === data;
|
||||
},
|
||||
};
|
||||
|
||||
const skillValidator = entityKindSchemaValidator(skillJsonSchema);
|
||||
|
||||
/**
|
||||
* Entity data validator for {@link SkillAiResourceEntityV1alpha1}.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const skillAiResourceEntityV1alpha1Validator: KindValidator = {
|
||||
async check(data: Entity) {
|
||||
return skillValidator(data) === data;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Type guard for {@link AiResourceEntityV1alpha1}.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const isAiResourceEntity = (
|
||||
entity: Entity,
|
||||
): entity is AiResourceEntityV1alpha1 =>
|
||||
entity.apiVersion === 'backstage.io/v1alpha1' && entity.kind === 'AiResource';
|
||||
|
||||
/**
|
||||
* Type guard for {@link SkillAiResourceEntityV1alpha1}.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const isSkillAiResourceEntity = (
|
||||
entity: Entity,
|
||||
): entity is SkillAiResourceEntityV1alpha1 =>
|
||||
isAiResourceEntity(entity) && entity.spec?.type === 'skill';
|
||||
|
||||
const baseRelationFields = [
|
||||
{
|
||||
selector: { path: 'spec.owner' },
|
||||
relation: 'ownedBy',
|
||||
defaultKind: 'Group',
|
||||
defaultNamespace: 'inherit' as const,
|
||||
allowedKinds: ['Group', 'User'],
|
||||
},
|
||||
{
|
||||
selector: { path: 'spec.system' },
|
||||
relation: 'partOf',
|
||||
defaultKind: 'System',
|
||||
defaultNamespace: 'inherit' as const,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Extends the catalog model with the AiResource kind.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const aiResourceEntityModel = createCatalogModelLayer({
|
||||
layerId: 'catalog.backstage.io/kind-ai-resource',
|
||||
builder: model => {
|
||||
model.addKind({
|
||||
group: 'backstage.io',
|
||||
names: {
|
||||
kind: 'AiResource',
|
||||
singular: 'airesource',
|
||||
plural: 'airesources',
|
||||
},
|
||||
description:
|
||||
'An AI resource represents contextual information consumed by AI coding tools, such as skills and rules.',
|
||||
versions: [
|
||||
{
|
||||
name: 'v1alpha1',
|
||||
relationFields: baseRelationFields,
|
||||
schema: {
|
||||
jsonSchema: defaultJsonSchema as JsonObject,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'v1alpha1',
|
||||
specType: 'skill',
|
||||
relationFields: [
|
||||
...baseRelationFields,
|
||||
{
|
||||
selector: { path: 'spec.dependsOn' },
|
||||
relation: 'dependsOn',
|
||||
defaultKind: 'AiResource',
|
||||
defaultNamespace: 'inherit' as const,
|
||||
},
|
||||
],
|
||||
schema: {
|
||||
jsonSchema: skillJsonSchema as JsonObject,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "AiResourceV1alpha1",
|
||||
"description": "An AI resource represents contextual information consumed by AI coding tools, such as skills and rules.",
|
||||
"examples": [
|
||||
{
|
||||
"apiVersion": "backstage.io/v1alpha1",
|
||||
"kind": "AiResource",
|
||||
"metadata": {
|
||||
"name": "frontend-design",
|
||||
"description": "Skill for creating production-grade frontend interfaces"
|
||||
},
|
||||
"spec": {
|
||||
"type": "skill",
|
||||
"lifecycle": "production",
|
||||
"owner": "ai-platform-team",
|
||||
"system": "ai-tooling"
|
||||
}
|
||||
}
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "Entity"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["spec"],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": ["backstage.io/v1alpha1"]
|
||||
},
|
||||
"kind": {
|
||||
"enum": ["AiResource"]
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"required": ["type", "lifecycle", "owner"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "The type of AI resource.",
|
||||
"examples": ["skill", "rule"],
|
||||
"minLength": 1
|
||||
},
|
||||
"lifecycle": {
|
||||
"type": "string",
|
||||
"description": "The lifecycle state of the AI resource.",
|
||||
"examples": ["experimental", "production", "deprecated"],
|
||||
"minLength": 1
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to the owner of the AI resource.",
|
||||
"examples": ["ai-platform-team", "user:john.johnson"],
|
||||
"minLength": 1
|
||||
},
|
||||
"system": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to the system that the AI resource belongs to.",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "AiResourceV1alpha1Skill",
|
||||
"description": "An AI resource of type 'skill', representing reusable contextual knowledge consumed by AI coding tools.",
|
||||
"examples": [
|
||||
{
|
||||
"apiVersion": "backstage.io/v1alpha1",
|
||||
"kind": "AiResource",
|
||||
"metadata": {
|
||||
"name": "frontend-design",
|
||||
"description": "Skill for creating production-grade frontend interfaces"
|
||||
},
|
||||
"spec": {
|
||||
"type": "skill",
|
||||
"lifecycle": "production",
|
||||
"owner": "ai-platform-team",
|
||||
"system": "ai-tooling",
|
||||
"disciplines": ["web"],
|
||||
"categories": ["framework"],
|
||||
"agents": ["claude-code"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "Entity"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["spec"],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": ["backstage.io/v1alpha1"]
|
||||
},
|
||||
"kind": {
|
||||
"enum": ["AiResource"]
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"required": ["type", "lifecycle", "owner"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["skill"],
|
||||
"description": "The type of AI resource."
|
||||
},
|
||||
"lifecycle": {
|
||||
"type": "string",
|
||||
"description": "The lifecycle state of the AI resource.",
|
||||
"examples": ["experimental", "production", "deprecated"],
|
||||
"minLength": 1
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to the owner of the AI resource.",
|
||||
"examples": ["ai-platform-team", "user:john.johnson"],
|
||||
"minLength": 1
|
||||
},
|
||||
"system": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to the system that the AI resource belongs to.",
|
||||
"minLength": 1
|
||||
},
|
||||
"disciplines": {
|
||||
"type": "array",
|
||||
"description": "Engineering disciplines this skill applies to.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"categories": {
|
||||
"type": "array",
|
||||
"description": "Descriptive categories for the skill.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"agents": {
|
||||
"type": "array",
|
||||
"description": "AI coding tools this skill is designed for.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"dependsOn": {
|
||||
"type": "array",
|
||||
"description": "Entity references to other AI resources this skill depends on.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user