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 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