@aramassa/ai-rules 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ export interface ValidationResult {
2
+ isValid: boolean;
3
+ warnings: string[];
4
+ errors: string[];
5
+ }
6
+ export declare class RecipeValidator {
7
+ private ajv;
8
+ private schema;
9
+ constructor();
10
+ initialize(): Promise<void>;
11
+ validateRecipe(recipeData: any): ValidationResult;
12
+ private formatSchemaError;
13
+ private getHelpfulMessageForProperty;
14
+ private isCommonMistake;
15
+ private checkCommonMistakes;
16
+ }
17
+ export declare function getRecipeValidator(): Promise<RecipeValidator>;
18
+ //# sourceMappingURL=recipeValidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipeValidator.d.ts","sourceRoot":"","sources":["../src/recipeValidator.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,MAAM,CAAM;;IAWd,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBjC,cAAc,CAAC,UAAU,EAAE,GAAG,GAAG,gBAAgB;IA0CjD,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,4BAA4B;IAYpC,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;CAyC5B;AAKD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMnE"}
@@ -0,0 +1,158 @@
1
+ import Ajv from 'ajv';
2
+ import addFormats from 'ajv-formats';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ // Get the current file's directory for schema loading
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ export class RecipeValidator {
10
+ constructor() {
11
+ this.ajv = new Ajv({
12
+ allErrors: true,
13
+ verbose: true,
14
+ strict: false // Allow additional properties for better error messages
15
+ });
16
+ addFormats(this.ajv);
17
+ }
18
+ async initialize() {
19
+ try {
20
+ const schemaPath = path.join(__dirname, '../schemas/recipe.schema.json');
21
+ const schemaContent = await fs.readFile(schemaPath, 'utf-8');
22
+ this.schema = JSON.parse(schemaContent);
23
+ this.ajv.addSchema(this.schema, 'recipe');
24
+ }
25
+ catch (error) {
26
+ console.warn('Warning: Could not load recipe schema for validation:', error);
27
+ // Create a minimal schema as fallback
28
+ this.schema = {
29
+ type: 'object',
30
+ properties: {
31
+ recipe: { type: 'array' }
32
+ },
33
+ required: ['recipe']
34
+ };
35
+ this.ajv.addSchema(this.schema, 'recipe');
36
+ }
37
+ }
38
+ validateRecipe(recipeData) {
39
+ const result = {
40
+ isValid: true,
41
+ warnings: [],
42
+ errors: []
43
+ };
44
+ // Basic structure validation
45
+ if (!recipeData || typeof recipeData !== 'object') {
46
+ result.isValid = false;
47
+ result.errors.push('Recipe must be a valid YAML/JSON object');
48
+ return result;
49
+ }
50
+ if (!Array.isArray(recipeData.recipe)) {
51
+ result.isValid = false;
52
+ result.errors.push('Recipe file must contain a "recipe" array');
53
+ return result;
54
+ }
55
+ // Validate against JSON schema
56
+ const valid = this.ajv.validate('recipe', recipeData);
57
+ if (!valid && this.ajv.errors) {
58
+ for (const error of this.ajv.errors) {
59
+ const errorMessage = this.formatSchemaError(error);
60
+ // Convert schema errors to warnings to maintain backward compatibility
61
+ if (this.isCommonMistake(error)) {
62
+ result.warnings.push(`Warning: ${errorMessage}`);
63
+ }
64
+ else {
65
+ result.warnings.push(`Schema validation warning: ${errorMessage}`);
66
+ }
67
+ }
68
+ }
69
+ // Check for common mistakes and provide helpful warnings
70
+ this.checkCommonMistakes(recipeData, result);
71
+ return result;
72
+ }
73
+ formatSchemaError(error) {
74
+ const instancePath = error.instancePath || 'root';
75
+ switch (error.keyword) {
76
+ case 'additionalProperties':
77
+ const extraProperty = error.params?.additionalProperty;
78
+ if (extraProperty) {
79
+ return this.getHelpfulMessageForProperty(extraProperty, instancePath);
80
+ }
81
+ return `Unknown property "${extraProperty}" at ${instancePath}`;
82
+ case 'required':
83
+ const missingProperty = error.params?.missingProperty;
84
+ return `Missing required property "${missingProperty}" at ${instancePath}`;
85
+ case 'type':
86
+ return `Property at ${instancePath} should be of type ${error.schema}`;
87
+ case 'enum':
88
+ return `Property at ${instancePath} should be one of: ${error.schema.join(', ')}`;
89
+ default:
90
+ return `${error.message} at ${instancePath}`;
91
+ }
92
+ }
93
+ getHelpfulMessageForProperty(property, path) {
94
+ const commonMistakes = {
95
+ 'category': 'Property "category" should be under "filters" object, not at the recipe item root level',
96
+ 'focus': 'Property "focus" should be under "filters" object, not at the recipe item root level',
97
+ 'description': 'Property "description" should be under "frontmatter" object, not at the recipe item root level',
98
+ 'applyTo': 'Property "applyTo" should be under "frontmatter" object, not at the recipe item root level',
99
+ 'target': 'Property "target" should be under "filters" object, not at the recipe item root level'
100
+ };
101
+ return commonMistakes[property] || `Unknown property "${property}" at ${path}`;
102
+ }
103
+ isCommonMistake(error) {
104
+ if (error.keyword !== 'additionalProperties') {
105
+ return false;
106
+ }
107
+ const property = error.params?.additionalProperty;
108
+ const commonMistakeProps = ['category', 'focus', 'description', 'applyTo', 'target'];
109
+ return commonMistakeProps.includes(property);
110
+ }
111
+ checkCommonMistakes(recipeData, result) {
112
+ // Check for properties that should be under filters or frontmatter
113
+ if (Array.isArray(recipeData.recipe)) {
114
+ recipeData.recipe.forEach((item, index) => {
115
+ if (!item || typeof item !== 'object')
116
+ return;
117
+ // Check for filter properties at root level
118
+ const filterProps = ['category', 'focus', 'target'];
119
+ filterProps.forEach(prop => {
120
+ if (item[prop] !== undefined) {
121
+ result.warnings.push(`Warning: Recipe item ${index + 1} has "${prop}" at root level. Consider moving it under "filters" object for better organization.`);
122
+ }
123
+ });
124
+ // Check for frontmatter properties at root level
125
+ const frontmatterProps = ['description', 'applyTo'];
126
+ frontmatterProps.forEach(prop => {
127
+ if (item[prop] !== undefined) {
128
+ result.warnings.push(`Warning: Recipe item ${index + 1} has "${prop}" at root level. Consider moving it under "frontmatter" object for better organization.`);
129
+ }
130
+ });
131
+ // Check for both filters and individual filter properties
132
+ if (item.filters && typeof item.filters === 'object') {
133
+ filterProps.forEach(prop => {
134
+ if (item[prop] !== undefined) {
135
+ result.warnings.push(`Warning: Recipe item ${index + 1} has both "filters.${prop}" and root-level "${prop}". The root-level property will be ignored.`);
136
+ }
137
+ });
138
+ }
139
+ });
140
+ }
141
+ // Check for root-level properties that should be under config
142
+ const rootLevelProps = ['baseDir'];
143
+ rootLevelProps.forEach(prop => {
144
+ if (recipeData[prop] !== undefined && !recipeData.config?.[prop]) {
145
+ result.warnings.push(`Warning: Root-level "${prop}" found. Consider moving it under "config" object for better organization.`);
146
+ }
147
+ });
148
+ }
149
+ }
150
+ // Singleton instance
151
+ let validatorInstance = null;
152
+ export async function getRecipeValidator() {
153
+ if (!validatorInstance) {
154
+ validatorInstance = new RecipeValidator();
155
+ await validatorInstance.initialize();
156
+ }
157
+ return validatorInstance;
158
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aramassa/ai-rules",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "This repository collects guidelines and instructions for developing AI agents. It contains documents covering communication rules, coding standards, testing strategies, and general operational practices.",
5
5
  "workspaces": [
6
6
  "packages/extract",
@@ -14,6 +14,7 @@
14
14
  "dist/",
15
15
  "artifact/",
16
16
  "presets/",
17
+ "schemas/",
17
18
  "README-npmjs.md"
18
19
  ],
19
20
  "publishConfig": {
@@ -52,6 +53,7 @@
52
53
  "@rollup/plugin-json": "^6.1.0",
53
54
  "@rollup/plugin-node-resolve": "^16.0.1",
54
55
  "@rollup/plugin-typescript": "^12.1.4",
56
+ "@types/ajv": "^0.0.5",
55
57
  "@types/commander": "^2.12.0",
56
58
  "@types/node": "^24.5.2",
57
59
  "fast-glob": "^3.3.3",
@@ -64,6 +66,8 @@
64
66
  "vitest": "^3.2.4"
65
67
  },
66
68
  "dependencies": {
69
+ "ajv": "^8.17.1",
70
+ "ajv-formats": "^3.0.1",
67
71
  "commander": "^14.0.0",
68
72
  "glob": "^11.0.3",
69
73
  "yaml": "^2.8.0"
@@ -0,0 +1,19 @@
1
+ config:
2
+ baseDir: .github/prompts
3
+ recipe:
4
+ - title: "Prompts 作成ルール"
5
+ out: "create_prompts.prompt.md"
6
+ type: "prompt"
7
+ filters:
8
+ category: "content-creation"
9
+ frontmatter:
10
+ description: "AI向けプロンプト作成のための包括的なルールとガイドライン。プロンプト工学のベストプラクティスを体系化。"
11
+ applyTo: "artifact/prompts/**/*.prompt.md"
12
+ - title: "Recipe ファイル作成ルール"
13
+ out: "create_recipe.prompt.md"
14
+ type: "prompt"
15
+ filters:
16
+ category: "instruction-generation"
17
+ frontmatter:
18
+ description: "AI-rulesプロジェクトにおけるレシピファイル作成のための包括的なガイドライン。複数のinstructionファイルを組み合わせた自動生成設定を支援。"
19
+ applyTo: "presets/**/*.yaml"
@@ -4,7 +4,8 @@ recipe:
4
4
  - title: "TODO Plans 作成ルール"
5
5
  out: "todo_plans.prompt.md"
6
6
  type: "prompt"
7
- category: "todo-planning"
8
- focus: "workflow-management"
7
+ filters:
8
+ category: "todo-planning"
9
+ focus: "workflow-management"
9
10
  frontmatter:
10
11
  description: "プロジェクトのTODO計画を管理するためのワークフローとガイドライン"