heraspec 0.1.0 → 0.1.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.
- package/bin/heraspec.js +261 -20
- package/bin/heraspec.js.map +4 -4
- package/dist/core/templates/skills/ux-element/templates/Controller.php +50 -0
- package/dist/core/templates/skills/ux-element/templates/Shortcode.php +23 -0
- package/dist/core/templates/skills/ux-element/templates/Template.html +20 -0
- package/dist/core/templates/skills/ux-element/templates/Thumbnail.svg +8 -0
- package/dist/core/templates/skills/ux-element/templates/View.php +21 -0
- package/dist/core/templates/skills/ux-element-skill.md +83 -0
- package/dist/index.js +27 -20
- package/package.json +1 -2
package/bin/heraspec.js
CHANGED
|
@@ -185,6 +185,14 @@ This document defines the workflow for AI agents working with HeraSpec.
|
|
|
185
185
|
- Agent implements with proper colors, fonts, styles from search results
|
|
186
186
|
- Agent verifies with pre-delivery checklist
|
|
187
187
|
|
|
188
|
+
**Special case - Flatsome UX Element skill:**
|
|
189
|
+
- Task: \`(projectType: wordpress, skill: ux-element)\`
|
|
190
|
+
- Agent reads: \`heraspec/skills/wordpress/ux-element/skill.md\`
|
|
191
|
+
- Agent MUST follow the **Wrapping Rule**: Use \`<span>\` with \`id="{{:: shortcode.$id }}"\`
|
|
192
|
+
- Agent uses templates from \`heraspec/skills/wordpress/ux-element/templates/\` (Controller, Shortcode, HTML Template, SVG Thumbnail)
|
|
193
|
+
- Agent ensures real-time preview support in AngularJS template.
|
|
194
|
+
- **Variable Translation**: Variables with underscores in PHP (e.g., \`bg_color\`) MUST be accessed via camelCase in AngularJS (e.g., \`shortcode.options.bgColor\`).
|
|
195
|
+
|
|
188
196
|
- Follow tasks.md
|
|
189
197
|
- Mark tasks completed: \`- [x]\`
|
|
190
198
|
|
|
@@ -320,29 +328,17 @@ var SKILLS_SECTION_TEMPLATE = `## Skills System
|
|
|
320
328
|
- Agent follows: Steps, uses templates, runs scripts
|
|
321
329
|
- Agent implements: According to skill.md guidelines
|
|
322
330
|
|
|
323
|
-
**Special case - UI/UX skill:**
|
|
324
|
-
- Task: \`(skill: ui-ux)\`
|
|
325
|
-
- Agent reads: \`heraspec/skills/ui-ux/skill.md\`
|
|
326
|
-
- Agent MUST use search scripts before implementing:
|
|
327
|
-
\`\`\`bash
|
|
328
|
-
# Search for design intelligence (BM25 - default, fast)
|
|
329
|
-
python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain>
|
|
330
|
-
|
|
331
|
-
# For better semantic results, use Vector or Hybrid mode:
|
|
332
|
-
python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain> --mode vector
|
|
333
|
-
python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain> --mode hybrid
|
|
334
|
-
|
|
335
|
-
# Stack-specific search
|
|
336
|
-
python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --stack <stack>
|
|
337
|
-
\`\`\`
|
|
338
|
-
- **Search Modes:**
|
|
339
|
-
- **BM25 (default)**: Fast keyword-based search, zero dependencies
|
|
340
|
-
- **Vector**: Semantic search, ~15-20% better results (requires: pip install sentence-transformers scikit-learn)
|
|
341
|
-
- **Hybrid**: Best of both, ~25% better results (requires: pip install sentence-transformers scikit-learn)
|
|
342
|
-
- Agent synthesizes search results
|
|
343
331
|
- Agent implements with proper colors, fonts, styles from search results
|
|
344
332
|
- Agent verifies with pre-delivery checklist
|
|
345
333
|
|
|
334
|
+
**Special case - Flatsome UX Element skill:**
|
|
335
|
+
- Task: \`(projectType: wordpress, skill: ux-element)\`
|
|
336
|
+
- Agent reads: \`heraspec/skills/wordpress/ux-element/skill.md\`
|
|
337
|
+
- Agent MUST follow the **Wrapping Rule**: Use \`<span>\` with \`id="{{:: shortcode.$id }}"\`
|
|
338
|
+
- Agent uses templates from \`heraspec/skills/wordpress/ux-element/templates/\` (Controller, Shortcode, HTML Template, SVG Thumbnail)
|
|
339
|
+
- Agent ensures real-time preview support in AngularJS template.
|
|
340
|
+
- **Variable Translation**: Variables with underscores in PHP (e.g., \`bg_color\`) MUST be accessed via camelCase in AngularJS (e.g., \`shortcode.options.bgColor\`).
|
|
341
|
+
|
|
346
342
|
### Skill Discovery
|
|
347
343
|
|
|
348
344
|
- List all skills: Check \`heraspec/skills/\` directory
|
|
@@ -710,6 +706,17 @@ python3 heraspec/skills/ui-ux/scripts/search.py "pricing plans" --domain pages
|
|
|
710
706
|
|
|
711
707
|
3. Read \`heraspec/skills/ui-ux/ui-ux-skill.md\` for complete documentation
|
|
712
708
|
|
|
709
|
+
### Flatsome UX Element Skill
|
|
710
|
+
|
|
711
|
+
Use the **ux-element** skill when developing elements for UX Builder in Flatsome themes.
|
|
712
|
+
|
|
713
|
+
**Usage:**
|
|
714
|
+
\`\`\`bash
|
|
715
|
+
heraspec skill add ux-element --project-type wordpress
|
|
716
|
+
\`\`\`
|
|
717
|
+
|
|
718
|
+
Read \`heraspec/skills/wordpress/ux-element/skill.md\` for the **Wrapping Rule** and template usage.
|
|
719
|
+
|
|
713
720
|
## Creating New Skills
|
|
714
721
|
|
|
715
722
|
1. Create skill folder structure
|
|
@@ -2353,6 +2360,12 @@ var SKILL_TEMPLATE_MAP = {
|
|
|
2353
2360
|
templateFileName: "module-codebase-skill.md",
|
|
2354
2361
|
isCrossCutting: false,
|
|
2355
2362
|
projectType: "perfex-module"
|
|
2363
|
+
},
|
|
2364
|
+
"wordpress:ux-element": {
|
|
2365
|
+
templateFileName: "ux-element-skill.md",
|
|
2366
|
+
isCrossCutting: false,
|
|
2367
|
+
projectType: "wordpress",
|
|
2368
|
+
resourceDirs: ["ux-element/templates"]
|
|
2356
2369
|
}
|
|
2357
2370
|
};
|
|
2358
2371
|
function getSkillTemplateInfo(skillName, projectType) {
|
|
@@ -2713,6 +2726,9 @@ var SkillCommand = class {
|
|
|
2713
2726
|
process.exitCode = 1;
|
|
2714
2727
|
}
|
|
2715
2728
|
}
|
|
2729
|
+
async update(skillName, projectType, projectPath = ".") {
|
|
2730
|
+
return this.add(skillName, projectType, projectPath);
|
|
2731
|
+
}
|
|
2716
2732
|
};
|
|
2717
2733
|
|
|
2718
2734
|
// src/commands/helper.ts
|
|
@@ -3641,6 +3657,222 @@ Specs analyzed: ${specFiles.length}`));
|
|
|
3641
3657
|
}
|
|
3642
3658
|
};
|
|
3643
3659
|
|
|
3660
|
+
// src/commands/suggest.ts
|
|
3661
|
+
import path15 from "path";
|
|
3662
|
+
import chalk10 from "chalk";
|
|
3663
|
+
import ora8 from "ora";
|
|
3664
|
+
var SuggestCommand = class {
|
|
3665
|
+
async execute(projectPath = ".") {
|
|
3666
|
+
const spinner = ora8("Analyzing project and generating suggestions...").start();
|
|
3667
|
+
try {
|
|
3668
|
+
const resolvedPath = path15.resolve(projectPath);
|
|
3669
|
+
const specsDir = path15.join(resolvedPath, HERASPEC_DIR_NAME, SPECS_DIR_NAME);
|
|
3670
|
+
const suggestionsDir = path15.join(resolvedPath, HERASPEC_DIR_NAME, "suggestions");
|
|
3671
|
+
const projectMdPath = path15.join(resolvedPath, HERASPEC_DIR_NAME, HERASPEC_MARKERS.PROJECT_MD);
|
|
3672
|
+
if (!await FileSystemUtils.fileExists(specsDir)) {
|
|
3673
|
+
spinner.fail('Specs directory not found. Run "heraspec init" first.');
|
|
3674
|
+
process.exitCode = 1;
|
|
3675
|
+
return;
|
|
3676
|
+
}
|
|
3677
|
+
await FileSystemUtils.createDirectory(suggestionsDir);
|
|
3678
|
+
let projectInfo = {};
|
|
3679
|
+
if (await FileSystemUtils.fileExists(projectMdPath)) {
|
|
3680
|
+
const projectContent = await FileSystemUtils.readFile(projectMdPath);
|
|
3681
|
+
projectInfo = this.extractProjectInfo(projectContent);
|
|
3682
|
+
}
|
|
3683
|
+
const specFiles = await this.findSpecFiles(specsDir);
|
|
3684
|
+
const specs = specFiles.length > 0 ? await this.readSpecs(specFiles) : [];
|
|
3685
|
+
const suggestions = this.generateSuggestions(specs, projectInfo);
|
|
3686
|
+
const suggestionsFilePath = path15.join(suggestionsDir, "feature-suggestions.md");
|
|
3687
|
+
const suggestionsContent = this.formatSuggestions(suggestions, projectInfo);
|
|
3688
|
+
await FileSystemUtils.writeFile(suggestionsFilePath, suggestionsContent);
|
|
3689
|
+
spinner.succeed(`Feature suggestions generated: ${suggestionsFilePath}`);
|
|
3690
|
+
console.log(chalk10.gray(`
|
|
3691
|
+
Found ${specs.length} existing feature(s)`));
|
|
3692
|
+
console.log(chalk10.gray(`Generated ${suggestions.length} suggestion(s)`));
|
|
3693
|
+
} catch (error) {
|
|
3694
|
+
spinner.fail(`Error: ${error.message}`);
|
|
3695
|
+
process.exitCode = 1;
|
|
3696
|
+
throw error;
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
/**
|
|
3700
|
+
* Extract project information from project.md
|
|
3701
|
+
*/
|
|
3702
|
+
extractProjectInfo(projectContent) {
|
|
3703
|
+
const info = {};
|
|
3704
|
+
const nameMatch = projectContent.match(/^#\s+(.+)$/m);
|
|
3705
|
+
if (nameMatch) {
|
|
3706
|
+
info.name = nameMatch[1].trim();
|
|
3707
|
+
}
|
|
3708
|
+
const projectTypeMatch = projectContent.match(/project[-\s]type[s]?[:\s]+([^\n]+)/i);
|
|
3709
|
+
if (projectTypeMatch) {
|
|
3710
|
+
info.projectType = projectTypeMatch[1].trim();
|
|
3711
|
+
}
|
|
3712
|
+
const stackMatch = projectContent.match(/tech[-\s]stack[:\s]+([^\n]+)/i);
|
|
3713
|
+
if (stackMatch) {
|
|
3714
|
+
info.stack = stackMatch[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3715
|
+
}
|
|
3716
|
+
return info;
|
|
3717
|
+
}
|
|
3718
|
+
/**
|
|
3719
|
+
* Find all spec files recursively
|
|
3720
|
+
*/
|
|
3721
|
+
async findSpecFiles(specsDir) {
|
|
3722
|
+
const specFiles = [];
|
|
3723
|
+
await this.findSpecFilesRecursive(specsDir, specFiles);
|
|
3724
|
+
return specFiles.sort();
|
|
3725
|
+
}
|
|
3726
|
+
/**
|
|
3727
|
+
* Recursively find spec files
|
|
3728
|
+
*/
|
|
3729
|
+
async findSpecFilesRecursive(dir, files) {
|
|
3730
|
+
const fs2 = await import("fs/promises");
|
|
3731
|
+
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
3732
|
+
for (const entry of entries) {
|
|
3733
|
+
const entryPath = path15.join(dir, entry.name);
|
|
3734
|
+
if (entry.isDirectory()) {
|
|
3735
|
+
await this.findSpecFilesRecursive(entryPath, files);
|
|
3736
|
+
} else if (entry.name.endsWith(".md")) {
|
|
3737
|
+
files.push(entryPath);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
/**
|
|
3742
|
+
* Read all spec files
|
|
3743
|
+
*/
|
|
3744
|
+
async readSpecs(specFiles) {
|
|
3745
|
+
const specs = [];
|
|
3746
|
+
for (const specFile of specFiles) {
|
|
3747
|
+
const content = await FileSystemUtils.readFile(specFile);
|
|
3748
|
+
const name = this.extractFeatureName(specFile, content);
|
|
3749
|
+
specs.push({
|
|
3750
|
+
path: specFile,
|
|
3751
|
+
content,
|
|
3752
|
+
name
|
|
3753
|
+
});
|
|
3754
|
+
}
|
|
3755
|
+
return specs;
|
|
3756
|
+
}
|
|
3757
|
+
/**
|
|
3758
|
+
* Extract feature name from spec file
|
|
3759
|
+
*/
|
|
3760
|
+
extractFeatureName(filePath, content) {
|
|
3761
|
+
const headingMatch = content.match(/^#+\s+(.+)$/m);
|
|
3762
|
+
if (headingMatch) {
|
|
3763
|
+
return headingMatch[1].trim();
|
|
3764
|
+
}
|
|
3765
|
+
return path15.basename(filePath, ".md");
|
|
3766
|
+
}
|
|
3767
|
+
/**
|
|
3768
|
+
* Generate feature suggestions based on existing specs
|
|
3769
|
+
*/
|
|
3770
|
+
generateSuggestions(specs, projectInfo) {
|
|
3771
|
+
const suggestions = [];
|
|
3772
|
+
const existingFeatures = specs.map((s) => s.name.toLowerCase());
|
|
3773
|
+
if (projectInfo.projectType?.toLowerCase().includes("plugin") || projectInfo.projectType?.toLowerCase().includes("module")) {
|
|
3774
|
+
if (!existingFeatures.some((f) => f.includes("settings") || f.includes("config"))) {
|
|
3775
|
+
suggestions.push({
|
|
3776
|
+
title: "Settings/Configuration Management",
|
|
3777
|
+
description: "Add a centralized settings management feature to allow users to configure plugin/module behavior.",
|
|
3778
|
+
category: "Configuration",
|
|
3779
|
+
integrationPoints: ["Admin interface", "Database storage"]
|
|
3780
|
+
});
|
|
3781
|
+
}
|
|
3782
|
+
if (!existingFeatures.some((f) => f.includes("report") || f.includes("analytics"))) {
|
|
3783
|
+
suggestions.push({
|
|
3784
|
+
title: "Reporting and Analytics",
|
|
3785
|
+
description: "Add reporting functionality to track usage, generate analytics, and provide insights.",
|
|
3786
|
+
category: "Analytics",
|
|
3787
|
+
integrationPoints: ["Data collection", "Dashboard"]
|
|
3788
|
+
});
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
if (!existingFeatures.some((f) => f.includes("export") || f.includes("import"))) {
|
|
3792
|
+
suggestions.push({
|
|
3793
|
+
title: "Data Import/Export",
|
|
3794
|
+
description: "Add functionality to import and export data in various formats (CSV, JSON, XML).",
|
|
3795
|
+
category: "Data Management",
|
|
3796
|
+
integrationPoints: ["File handling", "Data processing"]
|
|
3797
|
+
});
|
|
3798
|
+
}
|
|
3799
|
+
if (!existingFeatures.some((f) => f.includes("search") || f.includes("filter"))) {
|
|
3800
|
+
suggestions.push({
|
|
3801
|
+
title: "Advanced Search and Filtering",
|
|
3802
|
+
description: "Implement advanced search and filtering capabilities to help users find information quickly.",
|
|
3803
|
+
category: "User Experience",
|
|
3804
|
+
integrationPoints: ["Search interface", "Data indexing"]
|
|
3805
|
+
});
|
|
3806
|
+
}
|
|
3807
|
+
if (!existingFeatures.some((f) => f.includes("notification") || f.includes("alert"))) {
|
|
3808
|
+
suggestions.push({
|
|
3809
|
+
title: "Notifications and Alerts",
|
|
3810
|
+
description: "Add notification system to alert users about important events and updates.",
|
|
3811
|
+
category: "Communication",
|
|
3812
|
+
integrationPoints: ["Event system", "User preferences"]
|
|
3813
|
+
});
|
|
3814
|
+
}
|
|
3815
|
+
return suggestions;
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3818
|
+
* Format suggestions as markdown
|
|
3819
|
+
*/
|
|
3820
|
+
formatSuggestions(suggestions, projectInfo) {
|
|
3821
|
+
const lines = [];
|
|
3822
|
+
lines.push("# Feature Suggestions");
|
|
3823
|
+
lines.push("");
|
|
3824
|
+
lines.push(`Generated for: ${projectInfo.name || "Project"}`);
|
|
3825
|
+
lines.push(`Project Type: ${projectInfo.projectType || "Not specified"}`);
|
|
3826
|
+
lines.push(`Generated on: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
3827
|
+
lines.push("");
|
|
3828
|
+
lines.push("---");
|
|
3829
|
+
lines.push("");
|
|
3830
|
+
lines.push("## Overview");
|
|
3831
|
+
lines.push("");
|
|
3832
|
+
lines.push(`This document contains ${suggestions.length} feature suggestions that could enhance the project.`);
|
|
3833
|
+
lines.push("Each suggestion includes integration points and considerations for implementation.");
|
|
3834
|
+
lines.push("");
|
|
3835
|
+
lines.push("---");
|
|
3836
|
+
lines.push("");
|
|
3837
|
+
const byCategory = {};
|
|
3838
|
+
suggestions.forEach((suggestion) => {
|
|
3839
|
+
if (!byCategory[suggestion.category]) {
|
|
3840
|
+
byCategory[suggestion.category] = [];
|
|
3841
|
+
}
|
|
3842
|
+
byCategory[suggestion.category].push(suggestion);
|
|
3843
|
+
});
|
|
3844
|
+
Object.entries(byCategory).forEach(([category, categorySuggestions]) => {
|
|
3845
|
+
lines.push(`## ${category}`);
|
|
3846
|
+
lines.push("");
|
|
3847
|
+
categorySuggestions.forEach((suggestion, index) => {
|
|
3848
|
+
lines.push(`### ${index + 1}. ${suggestion.title}`);
|
|
3849
|
+
lines.push("");
|
|
3850
|
+
lines.push(suggestion.description);
|
|
3851
|
+
lines.push("");
|
|
3852
|
+
if (suggestion.integrationPoints.length > 0) {
|
|
3853
|
+
lines.push("**Integration Points:**");
|
|
3854
|
+
lines.push("");
|
|
3855
|
+
suggestion.integrationPoints.forEach((point) => {
|
|
3856
|
+
lines.push(`- ${point}`);
|
|
3857
|
+
});
|
|
3858
|
+
lines.push("");
|
|
3859
|
+
}
|
|
3860
|
+
if (index < categorySuggestions.length - 1) {
|
|
3861
|
+
lines.push("---");
|
|
3862
|
+
lines.push("");
|
|
3863
|
+
}
|
|
3864
|
+
});
|
|
3865
|
+
lines.push("");
|
|
3866
|
+
});
|
|
3867
|
+
lines.push("---");
|
|
3868
|
+
lines.push("");
|
|
3869
|
+
lines.push("*These suggestions are generated based on analysis of existing features and common patterns.*");
|
|
3870
|
+
lines.push("*Review and prioritize suggestions based on project requirements and goals.*");
|
|
3871
|
+
lines.push("");
|
|
3872
|
+
return lines.join("\n");
|
|
3873
|
+
}
|
|
3874
|
+
};
|
|
3875
|
+
|
|
3644
3876
|
// src/cli/index.ts
|
|
3645
3877
|
var require3 = createRequire2(import.meta.url);
|
|
3646
3878
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
@@ -3799,5 +4031,14 @@ skillCmd.command("add <skill-name>").description("Add a default skill to the pro
|
|
|
3799
4031
|
process.exit(1);
|
|
3800
4032
|
}
|
|
3801
4033
|
});
|
|
4034
|
+
skillCmd.command("update <skill-name>").description("Update an existing skill with the latest from HeraSpec templates").option("--project-type <type>", "Specify project type for project-specific skills").action(async (skillName, options) => {
|
|
4035
|
+
try {
|
|
4036
|
+
const skillCommand = new SkillCommand();
|
|
4037
|
+
await skillCommand.update(skillName, options?.projectType, ".");
|
|
4038
|
+
} catch (error) {
|
|
4039
|
+
console.error(`Error: ${error.message}`);
|
|
4040
|
+
process.exit(1);
|
|
4041
|
+
}
|
|
4042
|
+
});
|
|
3802
4043
|
program.parse();
|
|
3803
4044
|
//# sourceMappingURL=heraspec.js.map
|