@aramassa/ai-rules 0.2.1 → 0.3.0

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,22 @@
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
+ /**
12
+ * Finds the package root directory by looking for package.json
13
+ */
14
+ private findPackageRoot;
15
+ validateRecipe(recipeData: any): ValidationResult;
16
+ private formatSchemaError;
17
+ private getHelpfulMessageForProperty;
18
+ private isCommonMistake;
19
+ private checkCommonMistakes;
20
+ }
21
+ export declare function getRecipeValidator(): Promise<RecipeValidator>;
22
+ //# sourceMappingURL=recipeValidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipeValidator.d.ts","sourceRoot":"","sources":["../src/recipeValidator.ts"],"names":[],"mappings":"AAOA,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;IAqBjC;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB,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,180 @@
1
+ import Ajv from 'ajv';
2
+ import addFormats from 'ajv-formats';
3
+ import fs from 'fs/promises';
4
+ import fsSync from 'fs';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ export class RecipeValidator {
8
+ constructor() {
9
+ this.ajv = new Ajv({
10
+ allErrors: true,
11
+ verbose: true,
12
+ strict: false // Allow additional properties for better error messages
13
+ });
14
+ addFormats(this.ajv);
15
+ }
16
+ async initialize() {
17
+ try {
18
+ const packageRoot = this.findPackageRoot();
19
+ const schemaPath = path.join(packageRoot, 'schemas/recipe.schema.json');
20
+ const schemaContent = await fs.readFile(schemaPath, 'utf-8');
21
+ this.schema = JSON.parse(schemaContent);
22
+ this.ajv.addSchema(this.schema, 'recipe');
23
+ }
24
+ catch (error) {
25
+ console.warn('Warning: Could not load recipe schema for validation:', error);
26
+ // Create a minimal schema as fallback
27
+ this.schema = {
28
+ type: 'object',
29
+ properties: {
30
+ recipe: { type: 'array' }
31
+ },
32
+ required: ['recipe']
33
+ };
34
+ this.ajv.addSchema(this.schema, 'recipe');
35
+ }
36
+ }
37
+ /**
38
+ * Finds the package root directory by looking for package.json
39
+ */
40
+ findPackageRoot() {
41
+ // For ES modules, get current file path
42
+ const currentFile = fileURLToPath(import.meta.url);
43
+ let dir = path.dirname(currentFile);
44
+ // Walk up directory tree looking for package.json
45
+ while (dir !== path.dirname(dir)) {
46
+ const packageJsonPath = path.join(dir, 'package.json');
47
+ try {
48
+ // Use synchronous check since this is initialization
49
+ fsSync.accessSync(packageJsonPath);
50
+ return dir;
51
+ }
52
+ catch {
53
+ // Continue searching
54
+ }
55
+ dir = path.dirname(dir);
56
+ }
57
+ // Fallback to current working directory
58
+ return process.cwd();
59
+ }
60
+ validateRecipe(recipeData) {
61
+ const result = {
62
+ isValid: true,
63
+ warnings: [],
64
+ errors: []
65
+ };
66
+ // Basic structure validation
67
+ if (!recipeData || typeof recipeData !== 'object') {
68
+ result.isValid = false;
69
+ result.errors.push('Recipe must be a valid YAML/JSON object');
70
+ return result;
71
+ }
72
+ if (!Array.isArray(recipeData.recipe)) {
73
+ result.isValid = false;
74
+ result.errors.push('Recipe file must contain a "recipe" array');
75
+ return result;
76
+ }
77
+ // Validate against JSON schema
78
+ const valid = this.ajv.validate('recipe', recipeData);
79
+ if (!valid && this.ajv.errors) {
80
+ for (const error of this.ajv.errors) {
81
+ const errorMessage = this.formatSchemaError(error);
82
+ // Convert schema errors to warnings to maintain backward compatibility
83
+ if (this.isCommonMistake(error)) {
84
+ result.warnings.push(`Warning: ${errorMessage}`);
85
+ }
86
+ else {
87
+ result.warnings.push(`Schema validation warning: ${errorMessage}`);
88
+ }
89
+ }
90
+ }
91
+ // Check for common mistakes and provide helpful warnings
92
+ this.checkCommonMistakes(recipeData, result);
93
+ return result;
94
+ }
95
+ formatSchemaError(error) {
96
+ const instancePath = error.instancePath || 'root';
97
+ switch (error.keyword) {
98
+ case 'additionalProperties':
99
+ const extraProperty = error.params?.additionalProperty;
100
+ if (extraProperty) {
101
+ return this.getHelpfulMessageForProperty(extraProperty, instancePath);
102
+ }
103
+ return `Unknown property "${extraProperty}" at ${instancePath}`;
104
+ case 'required':
105
+ const missingProperty = error.params?.missingProperty;
106
+ return `Missing required property "${missingProperty}" at ${instancePath}`;
107
+ case 'type':
108
+ return `Property at ${instancePath} should be of type ${error.schema}`;
109
+ case 'enum':
110
+ return `Property at ${instancePath} should be one of: ${error.schema.join(', ')}`;
111
+ default:
112
+ return `${error.message} at ${instancePath}`;
113
+ }
114
+ }
115
+ getHelpfulMessageForProperty(property, path) {
116
+ const commonMistakes = {
117
+ 'category': 'Property "category" should be under "filters" object, not at the recipe item root level',
118
+ 'focus': 'Property "focus" should be under "filters" object, not at the recipe item root level',
119
+ 'description': 'Property "description" should be under "frontmatter" object, not at the recipe item root level',
120
+ 'applyTo': 'Property "applyTo" should be under "frontmatter" object, not at the recipe item root level',
121
+ 'target': 'Property "target" should be under "filters" object, not at the recipe item root level'
122
+ };
123
+ return commonMistakes[property] || `Unknown property "${property}" at ${path}`;
124
+ }
125
+ isCommonMistake(error) {
126
+ if (error.keyword !== 'additionalProperties') {
127
+ return false;
128
+ }
129
+ const property = error.params?.additionalProperty;
130
+ const commonMistakeProps = ['category', 'focus', 'description', 'applyTo', 'target'];
131
+ return commonMistakeProps.includes(property);
132
+ }
133
+ checkCommonMistakes(recipeData, result) {
134
+ // Check for properties that should be under filters or frontmatter
135
+ if (Array.isArray(recipeData.recipe)) {
136
+ recipeData.recipe.forEach((item, index) => {
137
+ if (!item || typeof item !== 'object')
138
+ return;
139
+ // Check for filter properties at root level
140
+ const filterProps = ['category', 'focus', 'target'];
141
+ filterProps.forEach(prop => {
142
+ if (item[prop] !== undefined) {
143
+ result.warnings.push(`Warning: Recipe item ${index + 1} has "${prop}" at root level. Consider moving it under "filters" object for better organization.`);
144
+ }
145
+ });
146
+ // Check for frontmatter properties at root level
147
+ const frontmatterProps = ['description', 'applyTo'];
148
+ frontmatterProps.forEach(prop => {
149
+ if (item[prop] !== undefined) {
150
+ result.warnings.push(`Warning: Recipe item ${index + 1} has "${prop}" at root level. Consider moving it under "frontmatter" object for better organization.`);
151
+ }
152
+ });
153
+ // Check for both filters and individual filter properties
154
+ if (item.filters && typeof item.filters === 'object') {
155
+ filterProps.forEach(prop => {
156
+ if (item[prop] !== undefined) {
157
+ result.warnings.push(`Warning: Recipe item ${index + 1} has both "filters.${prop}" and root-level "${prop}". The root-level property will be ignored.`);
158
+ }
159
+ });
160
+ }
161
+ });
162
+ }
163
+ // Check for root-level properties that should be under config
164
+ const rootLevelProps = ['baseDir'];
165
+ rootLevelProps.forEach(prop => {
166
+ if (recipeData[prop] !== undefined && !recipeData.config?.[prop]) {
167
+ result.warnings.push(`Warning: Root-level "${prop}" found. Consider moving it under "config" object for better organization.`);
168
+ }
169
+ });
170
+ }
171
+ }
172
+ // Singleton instance
173
+ let validatorInstance = null;
174
+ export async function getRecipeValidator() {
175
+ if (!validatorInstance) {
176
+ validatorInstance = new RecipeValidator();
177
+ await validatorInstance.initialize();
178
+ }
179
+ return validatorInstance;
180
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aramassa/ai-rules",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
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"
@@ -4,7 +4,16 @@ recipe:
4
4
  - title: "Prompts 作成ルール"
5
5
  out: "create_prompts.prompt.md"
6
6
  type: "prompt"
7
- category: "content-creation"
7
+ filters:
8
+ category: "content-creation"
8
9
  frontmatter:
9
10
  description: "AI向けプロンプト作成のための包括的なルールとガイドライン。プロンプト工学のベストプラクティスを体系化。"
10
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計画を管理するためのワークフローとガイドライン"