heraspec 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +57 -0
  3. package/bin/heraspec.js +3803 -0
  4. package/bin/heraspec.js.map +7 -0
  5. package/dist/core/templates/skills/CHANGELOG.md +117 -0
  6. package/dist/core/templates/skills/README-template.md +58 -0
  7. package/dist/core/templates/skills/README.md +36 -0
  8. package/dist/core/templates/skills/content-optimization-skill.md +104 -0
  9. package/dist/core/templates/skills/data/charts.csv +26 -0
  10. package/dist/core/templates/skills/data/colors.csv +97 -0
  11. package/dist/core/templates/skills/data/landing.csv +31 -0
  12. package/dist/core/templates/skills/data/pages-proposed.csv +22 -0
  13. package/dist/core/templates/skills/data/pages.csv +10 -0
  14. package/dist/core/templates/skills/data/products.csv +97 -0
  15. package/dist/core/templates/skills/data/prompts.csv +24 -0
  16. package/dist/core/templates/skills/data/stacks/flutter.csv +53 -0
  17. package/dist/core/templates/skills/data/stacks/html-tailwind.csv +56 -0
  18. package/dist/core/templates/skills/data/stacks/nextjs.csv +53 -0
  19. package/dist/core/templates/skills/data/stacks/react-native.csv +52 -0
  20. package/dist/core/templates/skills/data/stacks/react.csv +54 -0
  21. package/dist/core/templates/skills/data/stacks/svelte.csv +54 -0
  22. package/dist/core/templates/skills/data/stacks/swiftui.csv +51 -0
  23. package/dist/core/templates/skills/data/stacks/vue.csv +50 -0
  24. package/dist/core/templates/skills/data/styles.csv +59 -0
  25. package/dist/core/templates/skills/data/typography.csv +58 -0
  26. package/dist/core/templates/skills/data/ux-guidelines.csv +100 -0
  27. package/dist/core/templates/skills/documents-skill.md +114 -0
  28. package/dist/core/templates/skills/e2e-test-skill.md +119 -0
  29. package/dist/core/templates/skills/integration-test-skill.md +118 -0
  30. package/dist/core/templates/skills/module-codebase-skill.md +110 -0
  31. package/dist/core/templates/skills/scripts/CODE_EXPLANATION.md +394 -0
  32. package/dist/core/templates/skills/scripts/SEARCH_ALGORITHMS_COMPARISON.md +421 -0
  33. package/dist/core/templates/skills/scripts/SEARCH_MODES_GUIDE.md +238 -0
  34. package/dist/core/templates/skills/scripts/core.py +385 -0
  35. package/dist/core/templates/skills/scripts/search.py +73 -0
  36. package/dist/core/templates/skills/suggestion-skill.md +118 -0
  37. package/dist/core/templates/skills/templates/accessibility-checklist.md +40 -0
  38. package/dist/core/templates/skills/templates/example-prompt-full-theme.md +333 -0
  39. package/dist/core/templates/skills/templates/page-types-guide.md +338 -0
  40. package/dist/core/templates/skills/templates/pages-proposed-summary.md +273 -0
  41. package/dist/core/templates/skills/templates/pre-delivery-checklist.md +42 -0
  42. package/dist/core/templates/skills/templates/prompt-template-full-theme.md +313 -0
  43. package/dist/core/templates/skills/templates/responsive-design.md +40 -0
  44. package/dist/core/templates/skills/ui-ux-skill.md +584 -0
  45. package/dist/core/templates/skills/unit-test-skill.md +111 -0
  46. package/dist/index.js +1736 -0
  47. package/package.json +71 -0
package/dist/index.js ADDED
@@ -0,0 +1,1736 @@
1
+ import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
+
3
+ // src/core/config.ts
4
+ var HERASPEC_DIR_NAME = "heraspec";
5
+ var SPECS_DIR_NAME = "specs";
6
+ var CHANGES_DIR_NAME = "changes";
7
+ var ARCHIVES_DIR_NAME = "archives";
8
+ var SKILLS_DIR_NAME = "skills";
9
+ var PROJECT_TYPES = [
10
+ "wordpress-plugin",
11
+ "wordpress-theme",
12
+ "perfex-module",
13
+ "laravel-package",
14
+ "node-service",
15
+ "generic-webapp",
16
+ "backend-api",
17
+ "frontend-app",
18
+ "multi-stack"
19
+ ];
20
+ var SKILLS = {
21
+ "wordpress-plugin": [
22
+ "admin-settings-page",
23
+ "custom-post-type",
24
+ "shortcode",
25
+ "rest-endpoint",
26
+ "ajax-handler",
27
+ "activation-hook",
28
+ "deactivation-hook",
29
+ "admin-menu-item",
30
+ "meta-box",
31
+ "taxonomy"
32
+ ],
33
+ "wordpress-theme": [
34
+ "theme-setup",
35
+ "custom-post-type",
36
+ "template-part",
37
+ "widget-area",
38
+ "customizer-setting",
39
+ "theme-option"
40
+ ],
41
+ "perfex-module": [
42
+ "module-codebase",
43
+ "module-registration",
44
+ "permission-group",
45
+ "admin-menu-item",
46
+ "login-hook",
47
+ "database-table",
48
+ "api-endpoint"
49
+ ],
50
+ "laravel-package": [
51
+ "service-provider",
52
+ "config-file",
53
+ "artisan-command",
54
+ "migration",
55
+ "model",
56
+ "controller",
57
+ "middleware",
58
+ "route"
59
+ ],
60
+ "node-service": [
61
+ "express-route",
62
+ "middleware",
63
+ "database-model",
64
+ "service-layer",
65
+ "api-endpoint",
66
+ "background-job"
67
+ ],
68
+ "generic-webapp": [
69
+ "page",
70
+ "component",
71
+ "api-endpoint",
72
+ "database-table",
73
+ "authentication",
74
+ "authorization"
75
+ ],
76
+ "backend-api": [
77
+ "endpoint",
78
+ "middleware",
79
+ "authentication",
80
+ "authorization",
81
+ "database-model",
82
+ "validation"
83
+ ],
84
+ "frontend-app": [
85
+ "page",
86
+ "component",
87
+ "route",
88
+ "store",
89
+ "service",
90
+ "hook"
91
+ ],
92
+ "multi-stack": [
93
+ "cross-platform-feature",
94
+ "api-contract",
95
+ "shared-type",
96
+ "integration-point"
97
+ ]
98
+ };
99
+ var HERASPEC_MARKERS = {
100
+ PROJECT_MD: "project.md",
101
+ AGENTS_MD: "AGENTS.heraspec.md",
102
+ CONFIG_YAML: "config.yaml",
103
+ PROPOSAL_MD: "proposal.md",
104
+ TASKS_MD: "tasks.md",
105
+ DESIGN_MD: "design.md"
106
+ };
107
+
108
+ // src/core/schemas/base.schema.ts
109
+ import { z } from "zod";
110
+ var ProjectTypeSchema = z.enum([
111
+ "wordpress-plugin",
112
+ "wordpress-theme",
113
+ "perfex-module",
114
+ "laravel-package",
115
+ "node-service",
116
+ "generic-webapp",
117
+ "backend-api",
118
+ "frontend-app",
119
+ "multi-stack"
120
+ ]);
121
+ var SpecMetaSchema = z.object({
122
+ projectType: z.union([
123
+ ProjectTypeSchema,
124
+ z.array(ProjectTypeSchema)
125
+ ]).optional(),
126
+ domain: z.string().optional(),
127
+ stack: z.union([
128
+ z.string(),
129
+ z.array(z.string())
130
+ ]).optional()
131
+ }).optional();
132
+ var ScenarioSchema = z.object({
133
+ name: z.string(),
134
+ steps: z.array(z.string()).min(1)
135
+ });
136
+ var RequirementSchema = z.object({
137
+ id: z.string().optional(),
138
+ name: z.string(),
139
+ description: z.string(),
140
+ scenarios: z.array(ScenarioSchema).optional(),
141
+ constraints: z.array(z.string()).optional()
142
+ });
143
+
144
+ // src/core/schemas/spec.schema.ts
145
+ import { z as z2 } from "zod";
146
+ var SpecSchema = z2.object({
147
+ name: z2.string().min(1),
148
+ meta: SpecMetaSchema,
149
+ overview: z2.string().min(1),
150
+ requirements: z2.array(RequirementSchema).min(1),
151
+ metadata: z2.object({
152
+ version: z2.string().default("1.0.0"),
153
+ format: z2.literal("heraspec"),
154
+ sourcePath: z2.string().optional()
155
+ }).optional()
156
+ });
157
+
158
+ // src/core/schemas/change.schema.ts
159
+ import { z as z3 } from "zod";
160
+ var DeltaTypeSchema = z3.enum(["ADDED", "MODIFIED", "REMOVED"]);
161
+ var DeltaRequirementSchema = z3.object({
162
+ type: DeltaTypeSchema,
163
+ requirement: z3.object({
164
+ id: z3.string().optional(),
165
+ name: z3.string(),
166
+ description: z3.string(),
167
+ scenarios: z3.array(z3.object({
168
+ name: z3.string(),
169
+ steps: z3.array(z3.string())
170
+ })).optional(),
171
+ constraints: z3.array(z3.string()).optional()
172
+ })
173
+ });
174
+ var ChangeSchema = z3.object({
175
+ name: z3.string(),
176
+ proposal: z3.string().min(1),
177
+ tasks: z3.array(z3.string()).optional(),
178
+ design: z3.string().optional()
179
+ });
180
+
181
+ // src/core/init.ts
182
+ import ora from "ora";
183
+ import chalk from "chalk";
184
+ import path2 from "path";
185
+
186
+ // src/utils/file-system.ts
187
+ import { promises as fs } from "fs";
188
+ import path from "path";
189
+ var FileSystemUtils = class {
190
+ static async createDirectory(dirPath) {
191
+ await fs.mkdir(dirPath, { recursive: true });
192
+ }
193
+ static async fileExists(filePath) {
194
+ try {
195
+ await fs.access(filePath);
196
+ return true;
197
+ } catch {
198
+ return false;
199
+ }
200
+ }
201
+ static async readFile(filePath) {
202
+ return await fs.readFile(filePath, "utf-8");
203
+ }
204
+ static async writeFile(filePath, content) {
205
+ await fs.writeFile(filePath, content, "utf-8");
206
+ }
207
+ static async readDirectory(dirPath) {
208
+ return await fs.readdir(dirPath);
209
+ }
210
+ static async stat(filePath) {
211
+ return await fs.stat(filePath);
212
+ }
213
+ static async copyFile(src, dest) {
214
+ await fs.copyFile(src, dest);
215
+ }
216
+ static async copyDirectory(src, dest) {
217
+ await fs.mkdir(dest, { recursive: true });
218
+ const entries = await fs.readdir(src, { withFileTypes: true });
219
+ for (const entry of entries) {
220
+ const srcPath = path.join(src, entry.name);
221
+ const destPath = path.join(dest, entry.name);
222
+ if (entry.isDirectory()) {
223
+ await this.copyDirectory(srcPath, destPath);
224
+ } else {
225
+ await fs.copyFile(srcPath, destPath);
226
+ }
227
+ }
228
+ }
229
+ static async removeFile(filePath) {
230
+ await fs.unlink(filePath);
231
+ }
232
+ static async removeDirectory(dirPath, recursive = true) {
233
+ if (typeof fs.rm === "function") {
234
+ await fs.rm(dirPath, { recursive, force: true });
235
+ } else {
236
+ await fs.rmdir(dirPath, { recursive });
237
+ }
238
+ }
239
+ static async moveFile(src, dest) {
240
+ await fs.rename(src, dest);
241
+ }
242
+ static joinPath(...segments) {
243
+ return path.join(...segments);
244
+ }
245
+ static resolvePath(...segments) {
246
+ return path.resolve(...segments);
247
+ }
248
+ static getDirectoryName(filePath) {
249
+ return path.dirname(filePath);
250
+ }
251
+ static getBaseName(filePath) {
252
+ return path.basename(filePath);
253
+ }
254
+ };
255
+
256
+ // src/core/templates/index.ts
257
+ var PROJECT_TEMPLATE = `# HeraSpec Project
258
+
259
+ ## Overview
260
+ Describe your project here.
261
+
262
+ ## Project Types
263
+ - wordpress-plugin
264
+ - wordpress-theme
265
+ - perfex-module
266
+ - laravel-package
267
+ - node-service
268
+ - generic-webapp
269
+ - backend-api
270
+ - frontend-app
271
+ - multi-stack
272
+
273
+ ## Tech Stack
274
+ List your technologies here (e.g., PHP 8.1, WordPress 6.0, Laravel 10, etc.)
275
+
276
+ ## Conventions
277
+ Define coding standards, architectural patterns, and conventions to follow.
278
+
279
+ `;
280
+ var AGENTS_TEMPLATE = `# HeraSpec \u2014 AI Agent Instructions
281
+
282
+ This document defines the workflow for AI agents working with HeraSpec.
283
+
284
+ ## Core Workflow
285
+
286
+ ### Step 1 \u2014 Create a Change
287
+
288
+ **When creating changes, ALWAYS read heraspec/project.md first to understand:**
289
+ - Project types being used
290
+ - Tech stack and conventions
291
+ - Existing architecture patterns
292
+ - Coding standards
293
+
294
+ **Then scaffold:**
295
+ - \`heraspec/changes/<slug>/\` - Create proposal.md, tasks.md, design.md (optional)
296
+ - \`heraspec/specs/<slug>/\` - Create delta specs here (NOT inside changes folder)
297
+
298
+ **If user asks to create changes based on project.md:**
299
+ 1. Read \`heraspec/project.md\` thoroughly
300
+ 2. Identify all features/capabilities mentioned
301
+ 3. Create separate changes for each major feature
302
+ 4. Ensure each change follows project.md conventions
303
+ 5. Use correct project types and skills from project.md
304
+
305
+ ### Step 2 \u2014 Refine Specs
306
+ - Update delta specs in \`heraspec/specs/<slug>/\`
307
+ - Never modify source-of-truth specs directly
308
+
309
+ ### Step 3 \u2014 Approval
310
+ - Wait for user: "Specs approved."
311
+
312
+ ### Step 4 \u2014 Implementation
313
+
314
+ **CRITICAL: When implementing tasks, ALWAYS use Skills system:**
315
+
316
+ 1. **Read task line** to identify skill:
317
+ \`\`\`markdown
318
+ ## 1. Perfex module \u2013 Category Management (projectType: perfex-module, skill: module-codebase)
319
+ - [ ] 1.1 Create module structure
320
+ \`\`\`
321
+
322
+ 2. **Find skill folder**:
323
+ - Project-specific: \`heraspec/skills/<project-type>/<skill-name>/\`
324
+ - Cross-cutting: \`heraspec/skills/<skill-name>/\`
325
+
326
+ 3. **Read skill.md**:
327
+ - Understand purpose, steps, inputs, outputs
328
+ - Follow tone, rules, and limitations
329
+ - Check available templates and scripts
330
+
331
+ 4. **Use skill resources**:
332
+ - Run scripts from \`scripts/\` folder if needed
333
+ - Use templates from \`templates/\` folder
334
+ - Reference examples from \`examples/\` folder
335
+
336
+ 5. **Implement following skill.md guidance**:
337
+ - Follow step-by-step process
338
+ - Use correct naming conventions
339
+ - Apply code style rules
340
+ - Respect limitations
341
+
342
+ **Example workflow:**
343
+ - Task: \`(projectType: perfex-module, skill: module-codebase)\`
344
+ - Agent reads: \`heraspec/skills/perfex-module/module-codebase/skill.md\`
345
+ - Agent follows: Steps, uses templates, runs scripts
346
+ - Agent implements: According to skill.md guidelines
347
+
348
+ **Special case - UI/UX skill:**
349
+ - Task: \`(skill: ui-ux)\`
350
+ - Agent reads: \`heraspec/skills/ui-ux/skill.md\`
351
+ - Agent MUST use search scripts before implementing:
352
+ \`\`\`bash
353
+ # Search for design intelligence
354
+ python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain>
355
+ python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --stack <stack>
356
+ \`\`\`
357
+ - Agent synthesizes search results
358
+ - Agent implements with proper colors, fonts, styles from search results
359
+ - Agent verifies with pre-delivery checklist
360
+
361
+ - Follow tasks.md
362
+ - Mark tasks completed: \`- [x]\`
363
+
364
+ ### Step 5 \u2014 Archive
365
+ - Run: \`heraspec archive <slug> --yes\`
366
+ - This merges delta specs into source specs
367
+ - Moves change folder to archives
368
+
369
+ ## Spec Format
370
+
371
+ Specs must include:
372
+ - \`## Meta\` section with project type, domain, stack
373
+ - \`## Purpose\`
374
+ - \`## Requirements\` with scenarios
375
+
376
+ ## Delta Spec Format
377
+
378
+ Delta specs use:
379
+ - \`## ADDED Requirements\`
380
+ - \`## MODIFIED Requirements\`
381
+ - \`## REMOVED Requirements\`
382
+
383
+ ## Tasks Format
384
+
385
+ Tasks grouped by project type and skill:
386
+ \`\`\`
387
+ ## 1. WordPress plugin \u2013 admin settings page (projectType: wordpress-plugin, skill: admin-settings-page)
388
+ - [ ] Task description
389
+ \`\`\`
390
+
391
+ ## Skills System
392
+
393
+ **CRITICAL: Always use Skills when implementing tasks!**
394
+
395
+ ### How Skills Work
396
+
397
+ 1. **Tasks reference skills**: \`(projectType: perfex-module, skill: module-codebase)\`
398
+ 2. **Find skill folder**:
399
+ - Project-specific: \`heraspec/skills/<project-type>/<skill-name>/\`
400
+ - Cross-cutting: \`heraspec/skills/<skill-name>/\`
401
+ 3. **Read skill.md**: Understand purpose, steps, inputs, outputs, rules
402
+ 4. **Use skill resources**: Scripts, templates, examples
403
+ 5. **Implement following skill.md**: Follow step-by-step process
404
+
405
+ ### Skill Discovery
406
+
407
+ - List all skills: Check \`heraspec/skills/\` directory
408
+ - Project-specific skills: \`heraspec/skills/<project-type>/\`
409
+ - Cross-cutting skills: \`heraspec/skills/<skill-name>/\` (root level)
410
+
411
+ ### When Change Has Multiple Skills
412
+
413
+ **Important**: Each task group uses ONE skill. When working on a task group, agent MUST use that skill's skill.md.
414
+
415
+ Example with multiple skills in one change:
416
+ \`\`\`
417
+ ## 1. Perfex module \u2013 Feature (projectType: perfex-module, skill: module-codebase)
418
+ - [ ] Task 1.1 Create module structure
419
+ - [ ] Task 1.2 Configure registration
420
+
421
+ ## 2. UI/UX \u2013 Admin Interface (skill: ui-ux)
422
+ - [ ] Task 2.1 Design color palette
423
+ - [ ] Task 2.2 Create component styles
424
+
425
+ ## 3. Documents \u2013 User Guide (skill: documents)
426
+ - [ ] Task 3.1 Write technical docs
427
+ \`\`\`
428
+
429
+ **Agent workflow:**
430
+ 1. **For task group 1** (module-codebase):
431
+ - Read: \`heraspec/skills/perfex-module/module-codebase/skill.md\`
432
+ - Follow: Module codebase process
433
+ - Use: Module codebase templates/scripts
434
+ - Implement: Tasks 1.1, 1.2
435
+
436
+ 2. **For task group 2** (ui-ux):
437
+ - Read: \`heraspec/skills/ui-ux/skill.md\`
438
+ - Follow: UI/UX process
439
+ - Use: UI/UX templates/scripts
440
+ - Implement: Tasks 2.1, 2.2
441
+
442
+ 3. **For task group 3** (documents):
443
+ - Read: \`heraspec/skills/documents/skill.md\`
444
+ - Follow: Documents process
445
+ - Use: Documents templates/scripts
446
+ - Implement: Task 3.1
447
+
448
+ **Key rule**: Switch skill.md when switching task groups!
449
+
450
+ ## Rules
451
+
452
+ 1. **Specs first, tasks second, implementation last.**
453
+ 2. **Always use Skills**: When task has skill tag, MUST read and follow skill.md
454
+ 3. Never modify source-of-truth specs directly.
455
+ 4. Delta specs go in \`heraspec/specs/<slug>/\` (NOT in changes folder).
456
+ 5. Always wait for approval before implementation.
457
+ 6. **One skill per task group**: Each task group should use one skill consistently.
458
+
459
+ `;
460
+ var SKILLS_SECTION_TEMPLATE = `## Skills System
461
+
462
+ **CRITICAL: When implementing tasks, ALWAYS use Skills system:**
463
+
464
+ 1. **Read task line** to identify skill:
465
+ \`\`\`markdown
466
+ ## 1. Perfex module \u2013 Category Management (projectType: perfex-module, skill: module-codebase)
467
+ - [ ] 1.1 Create module structure
468
+ \`\`\`
469
+
470
+ 2. **Find skill folder**:
471
+ - Project-specific: \`heraspec/skills/<project-type>/<skill-name>/\`
472
+ - Cross-cutting: \`heraspec/skills/<skill-name>/\`
473
+
474
+ 3. **Read skill.md**:
475
+ - Understand purpose, steps, inputs, outputs
476
+ - Follow tone, rules, and limitations
477
+ - Check available templates and scripts
478
+
479
+ 4. **Use skill resources**:
480
+ - Run scripts from \`scripts/\` folder if needed
481
+ - Use templates from \`templates/\` folder
482
+ - Reference examples from \`examples/\` folder
483
+
484
+ 5. **Implement following skill.md guidance**:
485
+ - Follow step-by-step process
486
+ - Use correct naming conventions
487
+ - Apply code style rules
488
+ - Respect limitations
489
+
490
+ **Example workflow:**
491
+ - Task: \`(projectType: perfex-module, skill: module-codebase)\`
492
+ - Agent reads: \`heraspec/skills/perfex-module/module-codebase/skill.md\`
493
+ - Agent follows: Steps, uses templates, runs scripts
494
+ - Agent implements: According to skill.md guidelines
495
+
496
+ **Special case - UI/UX skill:**
497
+ - Task: \`(skill: ui-ux)\`
498
+ - Agent reads: \`heraspec/skills/ui-ux/skill.md\`
499
+ - Agent MUST use search scripts before implementing:
500
+ \`\`\`bash
501
+ # Search for design intelligence (BM25 - default, fast)
502
+ python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain>
503
+
504
+ # For better semantic results, use Vector or Hybrid mode:
505
+ python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain> --mode vector
506
+ python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --domain <domain> --mode hybrid
507
+
508
+ # Stack-specific search
509
+ python3 heraspec/skills/ui-ux/scripts/search.py "<keyword>" --stack <stack>
510
+ \`\`\`
511
+ - **Search Modes:**
512
+ - **BM25 (default)**: Fast keyword-based search, zero dependencies
513
+ - **Vector**: Semantic search, ~15-20% better results (requires: pip install sentence-transformers scikit-learn)
514
+ - **Hybrid**: Best of both, ~25% better results (requires: pip install sentence-transformers scikit-learn)
515
+ - Agent synthesizes search results
516
+ - Agent implements with proper colors, fonts, styles from search results
517
+ - Agent verifies with pre-delivery checklist
518
+
519
+ ### Skill Discovery
520
+
521
+ - List all skills: Check \`heraspec/skills/\` directory
522
+ - Project-specific skills: \`heraspec/skills/<project-type>/\`
523
+ - Cross-cutting skills: \`heraspec/skills/<skill-name>/\` (root level)
524
+
525
+ ### When Change Has Multiple Skills
526
+
527
+ **Important**: Each task group uses ONE skill. When working on a task group, agent MUST use that skill's skill.md.
528
+
529
+ Example with multiple skills in one change:
530
+ \`\`\`
531
+ ## 1. Perfex module \u2013 Feature (projectType: perfex-module, skill: module-codebase)
532
+ - [ ] Task 1.1 Create module structure
533
+ - [ ] Task 1.2 Configure registration
534
+
535
+ ## 2. UI/UX \u2013 Admin Interface (skill: ui-ux)
536
+ - [ ] Task 2.1 Design color palette
537
+ - [ ] Task 2.2 Create component styles
538
+
539
+ ## 3. Documents \u2013 User Guide (skill: documents)
540
+ - [ ] Task 3.1 Write technical docs
541
+ \`\`\`
542
+
543
+ **Agent workflow:**
544
+ 1. **For task group 1** (module-codebase):
545
+ - Read: \`heraspec/skills/perfex-module/module-codebase/skill.md\`
546
+ - Follow: Module codebase process
547
+ - Use: Module codebase templates/scripts
548
+ - Implement: Tasks 1.1, 1.2
549
+
550
+ 2. **For task group 2** (ui-ux):
551
+ - Read: \`heraspec/skills/ui-ux/skill.md\`
552
+ - Follow: UI/UX process
553
+ - Use: UI/UX templates/scripts
554
+ - Implement: Tasks 2.1, 2.2
555
+
556
+ 3. **For task group 3** (documents):
557
+ - Read: \`heraspec/skills/documents/skill.md\`
558
+ - Follow: Documents process
559
+ - Use: Documents templates/scripts
560
+ - Implement: Task 3.1
561
+
562
+ **Key rule**: Switch skill.md when switching task groups!
563
+ `;
564
+ var CONFIG_TEMPLATE = `# HeraSpec Configuration
565
+
566
+ projectTypes:
567
+ - wordpress-plugin
568
+ # Add other project types as needed
569
+
570
+ defaultDomain: global
571
+
572
+ `;
573
+ var PROPOSAL_TEMPLATE = `# Change Proposal: <slug>
574
+
575
+ ## Purpose
576
+ Describe why this change is needed.
577
+
578
+ ## Scope
579
+ What will be changed?
580
+
581
+ ## Project Types
582
+ - wordpress-plugin
583
+ - perfex-module
584
+
585
+ ## Impact
586
+ What parts of the system will be affected?
587
+
588
+ `;
589
+ var TASKS_TEMPLATE = `# Tasks
590
+
591
+ ## 1. WordPress plugin \u2013 feature name (projectType: wordpress-plugin, skill: admin-settings-page)
592
+ - [ ] Task 1.1
593
+ - [ ] Task 1.2
594
+
595
+ ## 2. Perfex module \u2013 feature name (projectType: perfex-module, skill: module-registration)
596
+ - [ ] Task 2.1
597
+
598
+ `;
599
+ var TemplateManager = class {
600
+ static getProjectTemplate() {
601
+ return PROJECT_TEMPLATE;
602
+ }
603
+ static getAgentsTemplate() {
604
+ return AGENTS_TEMPLATE;
605
+ }
606
+ static getConfigTemplate() {
607
+ return CONFIG_TEMPLATE;
608
+ }
609
+ static getProposalTemplate(slug) {
610
+ return PROPOSAL_TEMPLATE.replace("<slug>", slug);
611
+ }
612
+ static getTasksTemplate() {
613
+ return TASKS_TEMPLATE;
614
+ }
615
+ static getSkillsSection() {
616
+ return SKILLS_SECTION_TEMPLATE;
617
+ }
618
+ };
619
+
620
+ // src/core/init.ts
621
+ var InitCommand = class {
622
+ async execute(targetPath = ".") {
623
+ const resolvedPath = path2.resolve(targetPath);
624
+ const heraspecPath = path2.join(resolvedPath, HERASPEC_DIR_NAME);
625
+ const alreadyInitialized = await FileSystemUtils.fileExists(
626
+ path2.join(heraspecPath, HERASPEC_MARKERS.PROJECT_MD)
627
+ );
628
+ const spinner = ora({
629
+ text: alreadyInitialized ? "Updating HeraSpec..." : "Initializing HeraSpec...",
630
+ color: "blue"
631
+ }).start();
632
+ try {
633
+ await FileSystemUtils.createDirectory(heraspecPath);
634
+ await FileSystemUtils.createDirectory(path2.join(heraspecPath, SPECS_DIR_NAME));
635
+ await FileSystemUtils.createDirectory(path2.join(heraspecPath, CHANGES_DIR_NAME));
636
+ await FileSystemUtils.createDirectory(path2.join(heraspecPath, ARCHIVES_DIR_NAME));
637
+ await FileSystemUtils.createDirectory(path2.join(heraspecPath, SKILLS_DIR_NAME));
638
+ const skillsReadmePath = path2.join(heraspecPath, SKILLS_DIR_NAME, "README.md");
639
+ if (!await FileSystemUtils.fileExists(skillsReadmePath)) {
640
+ const skillsReadme = await this.getSkillsReadmeTemplate();
641
+ await FileSystemUtils.writeFile(skillsReadmePath, skillsReadme);
642
+ }
643
+ const uiuxGuidePath = path2.join(heraspecPath, SKILLS_DIR_NAME, "UI_UX_SKILL_QUICK_REFERENCE.md");
644
+ if (!await FileSystemUtils.fileExists(uiuxGuidePath)) {
645
+ const uiuxGuide = await this.getUIUXQuickReference();
646
+ await FileSystemUtils.writeFile(uiuxGuidePath, uiuxGuide);
647
+ }
648
+ await this.createTemplateFiles(heraspecPath, alreadyInitialized);
649
+ const agentsPath = path2.join(resolvedPath, HERASPEC_MARKERS.AGENTS_MD);
650
+ await this.updateAgentsFile(agentsPath, alreadyInitialized);
651
+ await this.updateRelatedMarkdownFiles(resolvedPath);
652
+ spinner.succeed(
653
+ chalk.green(
654
+ alreadyInitialized ? "HeraSpec updated successfully" : "HeraSpec initialized successfully"
655
+ )
656
+ );
657
+ console.log();
658
+ console.log(chalk.cyan("Next steps:"));
659
+ console.log(
660
+ chalk.gray("1. Review and update heraspec/project.md with your project details")
661
+ );
662
+ console.log(
663
+ chalk.gray('2. Create your first change: "Create a HeraSpec change to..."')
664
+ );
665
+ console.log(
666
+ chalk.gray("3. List changes: heraspec list")
667
+ );
668
+ } catch (error) {
669
+ spinner.fail(chalk.red(`Error: ${error.message}`));
670
+ throw error;
671
+ }
672
+ }
673
+ async createTemplateFiles(heraspecPath, skipExisting) {
674
+ const projectMdPath = path2.join(heraspecPath, HERASPEC_MARKERS.PROJECT_MD);
675
+ const configYamlPath = path2.join(heraspecPath, HERASPEC_MARKERS.CONFIG_YAML);
676
+ if (!await FileSystemUtils.fileExists(projectMdPath) || !skipExisting) {
677
+ await FileSystemUtils.writeFile(
678
+ projectMdPath,
679
+ TemplateManager.getProjectTemplate()
680
+ );
681
+ }
682
+ if (!await FileSystemUtils.fileExists(configYamlPath) || !skipExisting) {
683
+ await FileSystemUtils.writeFile(
684
+ configYamlPath,
685
+ TemplateManager.getConfigTemplate()
686
+ );
687
+ }
688
+ }
689
+ async updateAgentsFile(agentsPath, alreadyInitialized) {
690
+ const skillsSectionMarker = "## Skills System";
691
+ const skillsSectionEndMarker = "**Key rule**: Switch skill.md when switching task groups!";
692
+ if (!alreadyInitialized) {
693
+ await FileSystemUtils.writeFile(
694
+ agentsPath,
695
+ TemplateManager.getAgentsTemplate()
696
+ );
697
+ return;
698
+ }
699
+ let existingContent = "";
700
+ if (await FileSystemUtils.fileExists(agentsPath)) {
701
+ existingContent = await FileSystemUtils.readFile(agentsPath);
702
+ }
703
+ if (existingContent.includes(skillsSectionMarker)) {
704
+ const skillsSection = await this.getSkillsSection();
705
+ const updatedContent = this.replaceSkillsSection(existingContent, skillsSection);
706
+ await FileSystemUtils.writeFile(agentsPath, updatedContent);
707
+ } else {
708
+ const skillsSection = await this.getSkillsSection();
709
+ const updatedContent = this.appendSkillsSection(existingContent, skillsSection);
710
+ await FileSystemUtils.writeFile(agentsPath, updatedContent);
711
+ }
712
+ }
713
+ replaceSkillsSection(existingContent, newSkillsSection) {
714
+ const startMarker = "## Skills System";
715
+ const startIndex = existingContent.indexOf(startMarker);
716
+ if (startIndex === -1) {
717
+ return this.appendSkillsSection(existingContent, newSkillsSection);
718
+ }
719
+ let endIndex = existingContent.indexOf("\n## ", startIndex + startMarker.length);
720
+ if (endIndex === -1) {
721
+ endIndex = existingContent.length;
722
+ }
723
+ const before = existingContent.substring(0, startIndex).trimEnd();
724
+ const after = existingContent.substring(endIndex);
725
+ return before + "\n\n" + newSkillsSection + (after.trimStart().startsWith("\n") ? "" : "\n\n") + after;
726
+ }
727
+ appendSkillsSection(existingContent, skillsSection) {
728
+ const rulesMarker = "\n## Rules\n";
729
+ const rulesIndex = existingContent.indexOf(rulesMarker);
730
+ if (rulesIndex !== -1) {
731
+ const before = existingContent.substring(0, rulesIndex).trimEnd();
732
+ const after = existingContent.substring(rulesIndex);
733
+ return before + "\n\n" + skillsSection + "\n\n" + after;
734
+ }
735
+ const rulesMarker2 = "## Rules";
736
+ const rulesIndex2 = existingContent.indexOf(rulesMarker2);
737
+ if (rulesIndex2 !== -1 && rulesIndex2 > 0) {
738
+ const before = existingContent.substring(0, rulesIndex2).trimEnd();
739
+ const after = existingContent.substring(rulesIndex2);
740
+ return before + "\n\n" + skillsSection + "\n\n" + after;
741
+ }
742
+ return existingContent.trimEnd() + "\n\n" + skillsSection;
743
+ }
744
+ async getSkillsSection() {
745
+ return TemplateManager.getSkillsSection();
746
+ }
747
+ async getSkillsReadmeTemplate() {
748
+ return `# Skills Directory
749
+
750
+ This directory contains reusable skills for HeraSpec projects.
751
+
752
+ ## What Are Skills?
753
+
754
+ Skills are reusable patterns and workflows that help AI agents implement tasks consistently. Each skill contains:
755
+
756
+ - **skill.md**: Complete guide on how to use the skill
757
+ - **templates/**: Reusable templates
758
+ - **scripts/**: Automation scripts
759
+ - **examples/**: Good and bad examples
760
+
761
+ ## How Agents Use Skills
762
+
763
+ When a task has a skill tag:
764
+ \`\`\`markdown
765
+ ## 1. Feature (projectType: perfex-module, skill: module-codebase)
766
+ - [ ] Task 1.1
767
+ \`\`\`
768
+
769
+ The agent will:
770
+ 1. Find skill folder: \`heraspec/skills/perfex-module/module-codebase/\`
771
+ 2. Read \`skill.md\` to understand process
772
+ 3. Use templates and scripts from skill folder
773
+ 4. Follow guidelines in skill.md
774
+
775
+ ## Available Skills
776
+
777
+ Run \`heraspec skill list\` to see all available skills.
778
+
779
+ ## UI/UX Skill - Creating Full Theme Packages
780
+
781
+ The **UI/UX skill** is particularly useful for creating complete website themes with multiple pages.
782
+
783
+ ### Quick Start
784
+
785
+ When you need to create a full website package, use prompts like:
786
+
787
+ \`\`\`
788
+ T\u1EA1o g\xF3i website \u0111\u1EA7y \u0111\u1EE7 cho [PRODUCT_TYPE] v\u1EDBi style [STYLE_KEYWORDS].
789
+ S\u1EED d\u1EE5ng skill ui-ux v\u1EDBi hybrid mode \u0111\u1EC3 search design intelligence.
790
+ T\u1EA1o c\xE1c trang: home, about, [other pages].
791
+ Stack: [html-tailwind/react/nextjs].
792
+ \u0110\u1EA3m b\u1EA3o responsive, accessible, consistent design system.
793
+ \`\`\`
794
+
795
+ ### Prompt Templates
796
+
797
+ For detailed prompt examples and templates, see:
798
+ - **Example Prompts**: \`heraspec/skills/ui-ux/templates/example-prompt-full-theme.md\`
799
+ - **Prompt Templates**: \`heraspec/skills/ui-ux/templates/prompt-template-full-theme.md\`
800
+
801
+ These templates include:
802
+ - Ready-to-use prompts for different website types (E-commerce, SaaS, Service, Blog, Portfolio)
803
+ - Step-by-step instructions
804
+ - Search command examples
805
+ - Best practices
806
+
807
+ ### Search Modes
808
+
809
+ UI/UX skill supports 3 search modes:
810
+ - **BM25 (default)**: Fast keyword-based search, zero dependencies
811
+ - **Vector**: Semantic search, ~15-20% better results (requires: \`pip install sentence-transformers scikit-learn\`)
812
+ - **Hybrid**: Best of both, ~25% better results (requires: \`pip install sentence-transformers scikit-learn\`)
813
+
814
+ **Usage:**
815
+ \`\`\`bash
816
+ # BM25 (default)
817
+ python3 heraspec/skills/ui-ux/scripts/search.py "minimalism" --domain style
818
+
819
+ # Vector (semantic)
820
+ python3 heraspec/skills/ui-ux/scripts/search.py "elegant dark theme" --domain style --mode vector
821
+
822
+ # Hybrid (best)
823
+ python3 heraspec/skills/ui-ux/scripts/search.py "modern minimal design" --domain style --mode hybrid
824
+ \`\`\`
825
+
826
+ ### Multi-Page Support
827
+
828
+ Default page set includes:
829
+ 1. Home
830
+ 2. About
831
+ 3. Post Details
832
+ 4. Category
833
+ 5. Pricing
834
+ 6. FAQ
835
+ 7. Contact
836
+ 8. Product Category (e-commerce)
837
+ 9. Product Details (e-commerce)
838
+
839
+ Search page types:
840
+ \`\`\`bash
841
+ python3 heraspec/skills/ui-ux/scripts/search.py "home homepage" --domain pages
842
+ python3 heraspec/skills/ui-ux/scripts/search.py "pricing plans" --domain pages
843
+ \`\`\`
844
+
845
+ ### Adding UI/UX Skill to Your Project
846
+
847
+ 1. Copy skill from HeraSpec core:
848
+ \`\`\`bash
849
+ # Copy UI/UX skill
850
+ cp -r /path/to/HeraSpec/src/core/templates/skills/ui-ux-skill.md heraspec/skills/ui-ux/
851
+ cp -r /path/to/HeraSpec/src/core/templates/skills/scripts heraspec/skills/ui-ux/
852
+ cp -r /path/to/HeraSpec/src/core/templates/skills/data heraspec/skills/ui-ux/
853
+ cp -r /path/to/HeraSpec/src/core/templates/skills/templates heraspec/skills/ui-ux/
854
+ \`\`\`
855
+
856
+ 2. Or use \`heraspec skill add ui-ux\` (if available)
857
+
858
+ 3. Read \`heraspec/skills/ui-ux/ui-ux-skill.md\` for complete documentation
859
+
860
+ ## Creating New Skills
861
+
862
+ 1. Create skill folder structure
863
+ 2. Write \`skill.md\` following the template
864
+ 3. Add templates, scripts, examples as needed
865
+
866
+ See \`docs/SKILLS_STRUCTURE_PROPOSAL.md\` for detailed structure.
867
+ `;
868
+ }
869
+ async getUIUXQuickReference() {
870
+ return `# UI/UX Skill - Quick Reference Guide
871
+
872
+ Quick guide for creating prompts to build full theme packages with multiple pages using the ui-ux skill.
873
+
874
+ ## \u{1F4CB} Basic Prompt Template
875
+
876
+ \`\`\`
877
+ Create a complete website package for [PRODUCT_TYPE] with the following requirements:
878
+
879
+ **Project Information:**
880
+ - Product type: [SaaS / E-commerce / Service / Portfolio / etc.]
881
+ - Style: [minimal / elegant / modern / bold / etc.]
882
+ - Industry: [Healthcare / Fintech / Beauty / etc.]
883
+ - Stack: [html-tailwind / react / nextjs / etc.]
884
+ - Pages to create: home, about, [add other pages if needed]
885
+
886
+ **Process:**
887
+ 1. Use skill ui-ux to search design intelligence with hybrid mode
888
+ 2. Create shared components first (Header, Footer, Button, Card)
889
+ 3. Implement pages in order
890
+ 4. Ensure consistency in colors, typography, spacing
891
+ 5. Verify with pre-delivery checklist
892
+
893
+ **Quality Requirements:**
894
+ - \u2705 Consistent design system
895
+ - \u2705 Responsive (320px, 768px, 1024px, 1440px)
896
+ - \u2705 Accessible (WCAG AA minimum)
897
+ - \u2705 Performance optimized
898
+ \`\`\`
899
+
900
+ ## \u{1F3AF} Specific Prompt Examples
901
+
902
+ ### E-Commerce
903
+ \`\`\`
904
+ Create a complete website package for an online fashion store.
905
+
906
+ Product type: E-commerce Luxury
907
+ Style: elegant, premium, sophisticated
908
+ Stack: Next.js with Tailwind CSS
909
+ Pages: home, about, product category, product details, cart, checkout, thank you, faq, contact
910
+
911
+ Use skill ui-ux with hybrid mode. Focus on conversion optimization.
912
+ \`\`\`
913
+
914
+ ### SaaS
915
+ \`\`\`
916
+ Create a complete website package for a project management SaaS platform.
917
+
918
+ Product type: SaaS (General)
919
+ Style: modern, clean, professional
920
+ Stack: React with Tailwind CSS
921
+ Pages: home, about, pricing, features, faq, contact, login, register, dashboard
922
+
923
+ Use skill ui-ux with hybrid mode. Ensure professional and trustworthy.
924
+ \`\`\`
925
+
926
+ ### Service Business
927
+ \`\`\`
928
+ Create a complete website package for a healthcare service.
929
+
930
+ Product type: Beauty & Wellness Service
931
+ Style: elegant, minimal, soft, professional
932
+ Stack: html-tailwind
933
+ Pages: home, about, services, blog listing, post details, category, pricing, faq, contact
934
+
935
+ Use skill ui-ux with hybrid mode. Focus on trust and credibility.
936
+ \`\`\`
937
+
938
+ ## \u{1F50D} Search Modes
939
+
940
+ ### BM25 (Default)
941
+ \`\`\`bash
942
+ python3 heraspec/skills/ui-ux/scripts/search.py "minimalism" --domain style
943
+ \`\`\`
944
+ - \u2705 Fast, zero dependencies
945
+ - \u2705 Best for exact keyword matches
946
+
947
+ ### Vector (Semantic)
948
+ \`\`\`bash
949
+ python3 heraspec/skills/ui-ux/scripts/search.py "elegant dark theme" --domain style --mode vector
950
+ \`\`\`
951
+ - \u2705 Understands meaning and synonyms
952
+ - \u2705 ~15-20% better results
953
+ - \u26A0\uFE0F Requires: \`pip install sentence-transformers scikit-learn\`
954
+
955
+ ### Hybrid (Best)
956
+ \`\`\`bash
957
+ python3 heraspec/skills/ui-ux/scripts/search.py "modern minimal design" --domain style --mode hybrid
958
+ \`\`\`
959
+ - \u2705 Combines BM25 + Vector
960
+ - \u2705 ~25% better results
961
+ - \u26A0\uFE0F Requires: \`pip install sentence-transformers scikit-learn\`
962
+
963
+ ## \u{1F4C4} Default Page Set
964
+
965
+ When creating a "complete website package", the default set includes 9 pages:
966
+
967
+ 1. **Home** - Main homepage
968
+ 2. **About** - Company/story page
969
+ 3. **Post Details** - Blog/article detail
970
+ 4. **Category** - Blog/category listing
971
+ 5. **Pricing** - Pricing plans
972
+ 6. **FAQ** - Frequently asked questions
973
+ 7. **Contact** - Contact form
974
+ 8. **Product Category** - E-commerce category (if applicable)
975
+ 9. **Product Details** - E-commerce product detail (if applicable)
976
+
977
+ ## \u{1F527} Search Page Types
978
+
979
+ \`\`\`bash
980
+ # Home page
981
+ python3 heraspec/skills/ui-ux/scripts/search.py "home homepage" --domain pages
982
+
983
+ # About page
984
+ python3 heraspec/skills/ui-ux/scripts/search.py "about company story" --domain pages
985
+
986
+ # Pricing page
987
+ python3 heraspec/skills/ui-ux/scripts/search.py "pricing plans tiers" --domain pages
988
+
989
+ # E-commerce pages
990
+ python3 heraspec/skills/ui-ux/scripts/search.py "product-category shop catalog" --domain pages
991
+ python3 heraspec/skills/ui-ux/scripts/search.py "product-detail single-product" --domain pages
992
+ \`\`\`
993
+
994
+ ## \u{1F4DA} Detailed Documentation
995
+
996
+ After copying UI/UX skill to your project, see:
997
+ - \`heraspec/skills/ui-ux/ui-ux-skill.md\` - Complete skill documentation
998
+ - \`heraspec/skills/ui-ux/templates/example-prompt-full-theme.md\` - Detailed prompt examples
999
+ - \`heraspec/skills/ui-ux/templates/prompt-template-full-theme.md\` - Copy-paste templates
1000
+
1001
+ ## \u{1F4A1} Tips
1002
+
1003
+ 1. **Always mention "skill ui-ux"** - Agent will know to use this skill
1004
+ 2. **Encourage using hybrid mode** - Best results
1005
+ 3. **List all pages clearly** - Agent knows exact scope
1006
+ 4. **Require consistency** - Ensures unified design system
1007
+ 5. **Mention pre-delivery checklist** - Agent will verify before delivering
1008
+
1009
+ ## \u{1F680} Quick Start
1010
+
1011
+ 1. Copy UI/UX skill to project:
1012
+ \`\`\`bash
1013
+ cp -r /path/to/HeraSpec/src/core/templates/skills/ui-ux-skill.md heraspec/skills/ui-ux/
1014
+ cp -r /path/to/HeraSpec/src/core/templates/skills/scripts heraspec/skills/ui-ux/
1015
+ cp -r /path/to/HeraSpec/src/core/templates/skills/data heraspec/skills/ui-ux/
1016
+ cp -r /path/to/HeraSpec/src/core/templates/skills/templates heraspec/skills/ui-ux/
1017
+ \`\`\`
1018
+
1019
+ 2. Use prompt template from above
1020
+
1021
+ 3. Agent will automatically:
1022
+ - Search design intelligence with skill ui-ux
1023
+ - Create shared components
1024
+ - Implement each page
1025
+ - Verify with checklist
1026
+ `;
1027
+ }
1028
+ /**
1029
+ * Update related markdown files in the project (README.md, etc.)
1030
+ */
1031
+ async updateRelatedMarkdownFiles(projectPath) {
1032
+ const readmePath = path2.join(projectPath, "README.md");
1033
+ if (await FileSystemUtils.fileExists(readmePath)) {
1034
+ await this.updateReadmeFile(readmePath);
1035
+ }
1036
+ }
1037
+ /**
1038
+ * Update README.md with HeraSpec information
1039
+ */
1040
+ async updateReadmeFile(readmePath) {
1041
+ const existingContent = await FileSystemUtils.readFile(readmePath);
1042
+ const heraspecSection = this.getHeraSpecReadmeSection();
1043
+ const sectionMarkers = [
1044
+ "## HeraSpec",
1045
+ "## HeraSpec Development",
1046
+ "### HeraSpec",
1047
+ "### HeraSpec Development",
1048
+ "<!-- HeraSpec Section -->"
1049
+ ];
1050
+ let hasHeraSpecSection = false;
1051
+ let sectionStartIndex = -1;
1052
+ let sectionEndIndex = -1;
1053
+ for (const marker of sectionMarkers) {
1054
+ const index = existingContent.indexOf(marker);
1055
+ if (index !== -1) {
1056
+ hasHeraSpecSection = true;
1057
+ sectionStartIndex = index;
1058
+ sectionEndIndex = existingContent.indexOf("\n## ", index + marker.length);
1059
+ if (sectionEndIndex === -1) {
1060
+ sectionEndIndex = existingContent.indexOf("\n### ", index + marker.length);
1061
+ }
1062
+ if (sectionEndIndex === -1) {
1063
+ sectionEndIndex = existingContent.length;
1064
+ }
1065
+ break;
1066
+ }
1067
+ }
1068
+ if (hasHeraSpecSection && sectionStartIndex !== -1) {
1069
+ const before = existingContent.substring(0, sectionStartIndex).trimEnd();
1070
+ const after = existingContent.substring(sectionEndIndex);
1071
+ const updatedContent = before + "\n\n" + heraspecSection + (after.trimStart().startsWith("\n") ? "" : "\n\n") + after;
1072
+ await FileSystemUtils.writeFile(readmePath, updatedContent);
1073
+ } else {
1074
+ const insertBeforeMarkers = [
1075
+ "\n## Development",
1076
+ "\n## Setup",
1077
+ "\n## Contributing",
1078
+ "\n## Installation",
1079
+ "\n## Getting Started"
1080
+ ];
1081
+ let inserted = false;
1082
+ for (const marker of insertBeforeMarkers) {
1083
+ const index = existingContent.indexOf(marker);
1084
+ if (index !== -1) {
1085
+ const before = existingContent.substring(0, index).trimEnd();
1086
+ const after = existingContent.substring(index);
1087
+ const updatedContent = before + "\n\n" + heraspecSection + "\n\n" + after;
1088
+ await FileSystemUtils.writeFile(readmePath, updatedContent);
1089
+ inserted = true;
1090
+ break;
1091
+ }
1092
+ }
1093
+ if (!inserted) {
1094
+ const updatedContent = existingContent.trimEnd() + "\n\n" + heraspecSection;
1095
+ await FileSystemUtils.writeFile(readmePath, updatedContent);
1096
+ }
1097
+ }
1098
+ }
1099
+ /**
1100
+ * Get HeraSpec section content for README.md
1101
+ */
1102
+ getHeraSpecReadmeSection() {
1103
+ return `<!-- HeraSpec Section -->
1104
+ ## HeraSpec Development
1105
+
1106
+ This project uses [HeraSpec](https://github.com/your-org/heraspec) for spec-driven development.
1107
+
1108
+ ### Quick Start
1109
+
1110
+ \`\`\`bash
1111
+ # Initialize HeraSpec (if not already done)
1112
+ heraspec init
1113
+
1114
+ # List active changes
1115
+ heraspec list
1116
+
1117
+ # View a change
1118
+ heraspec show <change-name>
1119
+
1120
+ # Validate changes
1121
+ heraspec validate <change-name>
1122
+ \`\`\`
1123
+
1124
+ ### Project Structure
1125
+
1126
+ - \`heraspec/project.md\` - Project overview and configuration
1127
+ - \`heraspec/specs/\` - Source of truth specifications
1128
+ - \`heraspec/changes/\` - Active changes in progress
1129
+ - \`heraspec/skills/\` - Reusable skills for AI agents
1130
+ - \`AGENTS.heraspec.md\` - AI agent instructions
1131
+
1132
+ ### Working with Changes
1133
+
1134
+ 1. **Create a change**: Ask AI to create a HeraSpec change, or create manually
1135
+ 2. **Refine specs**: Review and update delta specs in \`heraspec/specs/<change-name>/\`
1136
+ 3. **Implement**: Follow tasks in \`heraspec/changes/<change-name>/tasks.md\`
1137
+ 4. **Archive**: Run \`heraspec archive <change-name> --yes\` when complete
1138
+
1139
+ ### Skills
1140
+
1141
+ Add skills to your project:
1142
+
1143
+ \`\`\`bash
1144
+ # List available skills
1145
+ heraspec skill list
1146
+
1147
+ # Add a skill
1148
+ heraspec skill add ui-ux
1149
+ heraspec skill add unit-test
1150
+
1151
+ # View skill details
1152
+ heraspec skill show ui-ux
1153
+ \`\`\`
1154
+
1155
+ For more information, see the [HeraSpec documentation](https://github.com/your-org/heraspec/docs).
1156
+
1157
+ ---
1158
+
1159
+ *This section is automatically updated by \`heraspec init\`. Last updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}*`;
1160
+ }
1161
+ };
1162
+
1163
+ // src/core/list.ts
1164
+ import path3 from "path";
1165
+ var ListCommand = class {
1166
+ async execute(targetPath = ".", mode = "changes") {
1167
+ const heraspecPath = path3.join(targetPath, HERASPEC_DIR_NAME);
1168
+ if (mode === "changes") {
1169
+ await this.listChanges(heraspecPath);
1170
+ } else {
1171
+ await this.listSpecs(heraspecPath);
1172
+ }
1173
+ }
1174
+ async listChanges(heraspecPath) {
1175
+ const changesDir = path3.join(heraspecPath, CHANGES_DIR_NAME);
1176
+ try {
1177
+ await FileSystemUtils.stat(changesDir);
1178
+ } catch {
1179
+ console.log('No HeraSpec changes directory found. Run "heraspec init" first.');
1180
+ return;
1181
+ }
1182
+ const entries = await FileSystemUtils.readDirectory(changesDir);
1183
+ const changeDirs = [];
1184
+ for (const entry of entries) {
1185
+ const entryPath = path3.join(changesDir, entry);
1186
+ const stats = await FileSystemUtils.stat(entryPath);
1187
+ if (stats.isDirectory() && entry !== ARCHIVES_DIR_NAME) {
1188
+ changeDirs.push(entry);
1189
+ }
1190
+ }
1191
+ if (changeDirs.length === 0) {
1192
+ console.log("No active changes found.");
1193
+ return;
1194
+ }
1195
+ changeDirs.sort();
1196
+ console.log("\nActive changes:");
1197
+ console.log("\u2500".repeat(50));
1198
+ for (const change of changeDirs) {
1199
+ console.log(` \u2022 ${change}`);
1200
+ }
1201
+ console.log();
1202
+ }
1203
+ async listSpecs(heraspecPath) {
1204
+ const specsDir = path3.join(heraspecPath, SPECS_DIR_NAME);
1205
+ try {
1206
+ await FileSystemUtils.stat(specsDir);
1207
+ } catch {
1208
+ console.log('No HeraSpec specs directory found. Run "heraspec init" first.');
1209
+ return;
1210
+ }
1211
+ const specs = await this.findSpecFiles(specsDir, "");
1212
+ if (specs.length === 0) {
1213
+ console.log("No specs found.");
1214
+ return;
1215
+ }
1216
+ specs.sort();
1217
+ console.log("\nSpecs:");
1218
+ console.log("\u2500".repeat(50));
1219
+ for (const spec of specs) {
1220
+ console.log(` \u2022 ${spec}`);
1221
+ }
1222
+ console.log();
1223
+ }
1224
+ async findSpecFiles(dir, prefix) {
1225
+ const specs = [];
1226
+ const entries = await FileSystemUtils.readDirectory(dir);
1227
+ for (const entry of entries) {
1228
+ const entryPath = path3.join(dir, entry);
1229
+ const stats = await FileSystemUtils.stat(entryPath);
1230
+ if (stats.isDirectory()) {
1231
+ const subSpecs = await this.findSpecFiles(
1232
+ entryPath,
1233
+ prefix ? `${prefix}/${entry}` : entry
1234
+ );
1235
+ specs.push(...subSpecs);
1236
+ } else if (entry === "spec.md") {
1237
+ specs.push(prefix || "global");
1238
+ }
1239
+ }
1240
+ return specs;
1241
+ }
1242
+ };
1243
+
1244
+ // src/core/archive.ts
1245
+ import path4 from "path";
1246
+ import ora2 from "ora";
1247
+ import chalk2 from "chalk";
1248
+
1249
+ // src/core/parsers/markdown-parser.ts
1250
+ var MarkdownParser = class _MarkdownParser {
1251
+ lines;
1252
+ constructor(content) {
1253
+ this.lines = _MarkdownParser.normalizeContent(content).split("\n");
1254
+ }
1255
+ static normalizeContent(content) {
1256
+ return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1257
+ }
1258
+ parseSpec(name) {
1259
+ const sections = this.parseSections();
1260
+ const meta = this.parseMeta(sections);
1261
+ const purpose = this.findSection(sections, "Purpose")?.content.join("\n").trim() || "";
1262
+ const requirementsSection = this.findSection(sections, "Requirements");
1263
+ if (!purpose) {
1264
+ throw new Error("Spec must have a Purpose section");
1265
+ }
1266
+ if (!requirementsSection) {
1267
+ throw new Error("Spec must have a Requirements section");
1268
+ }
1269
+ const requirements = this.parseRequirements(requirementsSection);
1270
+ return {
1271
+ name,
1272
+ meta,
1273
+ overview: purpose.trim(),
1274
+ requirements,
1275
+ metadata: {
1276
+ version: "1.0.0",
1277
+ format: "heraspec"
1278
+ }
1279
+ };
1280
+ }
1281
+ parseDeltaSpec(content) {
1282
+ const sections = this.parseSections();
1283
+ const added = this.findSection(sections, "ADDED Requirements");
1284
+ const modified = this.findSection(sections, "MODIFIED Requirements");
1285
+ const removed = this.findSection(sections, "REMOVED Requirements");
1286
+ return {
1287
+ added: added ? this.parseDeltaRequirements(added) : [],
1288
+ modified: modified ? this.parseDeltaRequirements(modified) : [],
1289
+ removed: removed ? this.parseDeltaRequirements(removed) : []
1290
+ };
1291
+ }
1292
+ parseMeta(sections) {
1293
+ const metaSection = this.findSection(sections, "Meta");
1294
+ if (!metaSection) {
1295
+ return void 0;
1296
+ }
1297
+ const meta = {};
1298
+ const content = metaSection.content.join("\n");
1299
+ const projectTypeMatch = content.match(/project type:\s*(.+)/i);
1300
+ if (projectTypeMatch) {
1301
+ const types = projectTypeMatch[1].split("|").map((t) => t.trim());
1302
+ meta.projectType = types.length === 1 ? types[0] : types;
1303
+ }
1304
+ const domainMatch = content.match(/domain:\s*(.+)/i);
1305
+ if (domainMatch) {
1306
+ meta.domain = domainMatch[1].trim();
1307
+ }
1308
+ const stackMatch = content.match(/stack:\s*(.+)/i);
1309
+ if (stackMatch) {
1310
+ const stacks = stackMatch[1].split("|").map((s) => s.trim());
1311
+ meta.stack = stacks.length === 1 ? stacks[0] : stacks;
1312
+ }
1313
+ return Object.keys(meta).length > 0 ? meta : void 0;
1314
+ }
1315
+ parseSections() {
1316
+ const sections = [];
1317
+ let currentSection = null;
1318
+ for (const line of this.lines) {
1319
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
1320
+ if (headerMatch) {
1321
+ if (currentSection) {
1322
+ sections.push(currentSection);
1323
+ }
1324
+ currentSection = {
1325
+ level: headerMatch[1].length,
1326
+ title: headerMatch[2].trim(),
1327
+ content: []
1328
+ };
1329
+ } else if (currentSection) {
1330
+ currentSection.content.push(line);
1331
+ }
1332
+ }
1333
+ if (currentSection) {
1334
+ sections.push(currentSection);
1335
+ }
1336
+ return sections;
1337
+ }
1338
+ findSection(sections, title) {
1339
+ return sections.find(
1340
+ (s) => s.title.toLowerCase() === title.toLowerCase() || s.title.toLowerCase().includes(title.toLowerCase())
1341
+ );
1342
+ }
1343
+ parseRequirements(section) {
1344
+ const requirements = [];
1345
+ const lines = section.content;
1346
+ let currentRequirement = null;
1347
+ let inRequirement = false;
1348
+ let inScenario = false;
1349
+ let currentScenario = null;
1350
+ for (let i = 0; i < lines.length; i++) {
1351
+ const line = lines[i];
1352
+ const reqMatch = line.match(/^###\s+Requirement:\s*(.+)$/i);
1353
+ if (reqMatch) {
1354
+ if (currentRequirement) {
1355
+ requirements.push(currentRequirement);
1356
+ }
1357
+ currentRequirement = {
1358
+ name: reqMatch[1].trim(),
1359
+ description: "",
1360
+ scenarios: []
1361
+ };
1362
+ inRequirement = true;
1363
+ continue;
1364
+ }
1365
+ const scenarioMatch = line.match(/^####\s+Scenario:\s*(.+)$/i);
1366
+ if (scenarioMatch && currentRequirement) {
1367
+ if (currentScenario) {
1368
+ currentRequirement.scenarios.push(currentScenario);
1369
+ }
1370
+ currentScenario = {
1371
+ name: scenarioMatch[1].trim(),
1372
+ steps: []
1373
+ };
1374
+ inScenario = true;
1375
+ continue;
1376
+ }
1377
+ if (currentScenario && line.match(/^-\s*(GIVEN|WHEN|THEN|AND|BUT)\s+/i)) {
1378
+ const step = line.replace(/^-\s*/, "").trim();
1379
+ currentScenario.steps.push(step);
1380
+ continue;
1381
+ }
1382
+ if (currentRequirement && !inScenario) {
1383
+ const trimmed = line.trim();
1384
+ if (trimmed && !trimmed.startsWith("#")) {
1385
+ currentRequirement.description += (currentRequirement.description ? "\n" : "") + trimmed;
1386
+ }
1387
+ }
1388
+ }
1389
+ if (currentScenario && currentRequirement) {
1390
+ currentRequirement.scenarios.push(currentScenario);
1391
+ }
1392
+ if (currentRequirement) {
1393
+ requirements.push(currentRequirement);
1394
+ }
1395
+ return requirements;
1396
+ }
1397
+ parseDeltaRequirements(section) {
1398
+ return this.parseRequirements(section);
1399
+ }
1400
+ };
1401
+
1402
+ // src/core/archive.ts
1403
+ var ArchiveCommand = class {
1404
+ async execute(changeName, options) {
1405
+ if (!changeName) {
1406
+ console.error("Error: Please specify a change name");
1407
+ console.log("Usage: heraspec archive <change-name> [--yes]");
1408
+ process.exitCode = 1;
1409
+ return;
1410
+ }
1411
+ const changePath = path4.join(".", HERASPEC_DIR_NAME, CHANGES_DIR_NAME, changeName);
1412
+ if (!await FileSystemUtils.fileExists(changePath)) {
1413
+ console.error(`Error: Change "${changeName}" not found`);
1414
+ process.exitCode = 1;
1415
+ return;
1416
+ }
1417
+ if (!options?.yes) {
1418
+ console.log(`
1419
+ This will archive "${changeName}" and merge delta specs into source specs.`);
1420
+ console.log("This action cannot be undone.\n");
1421
+ console.error("Error: Please use --yes flag to confirm");
1422
+ process.exitCode = 1;
1423
+ return;
1424
+ }
1425
+ const spinner = ora2({
1426
+ text: `Archiving change "${changeName}"...`,
1427
+ color: "blue"
1428
+ }).start();
1429
+ try {
1430
+ await this.mergeDeltaSpecs(changePath, changeName);
1431
+ const specsDir = path4.join(
1432
+ ".",
1433
+ HERASPEC_DIR_NAME,
1434
+ SPECS_DIR_NAME,
1435
+ changeName
1436
+ );
1437
+ if (await FileSystemUtils.fileExists(specsDir)) {
1438
+ await FileSystemUtils.removeDirectory(specsDir, true);
1439
+ }
1440
+ const archiveDir = path4.join(
1441
+ ".",
1442
+ HERASPEC_DIR_NAME,
1443
+ CHANGES_DIR_NAME,
1444
+ ARCHIVES_DIR_NAME
1445
+ );
1446
+ await FileSystemUtils.createDirectory(archiveDir);
1447
+ const datePrefix = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1448
+ const archivePath = path4.join(archiveDir, `${datePrefix}-${changeName}`);
1449
+ await FileSystemUtils.createDirectory(archivePath);
1450
+ await this.moveChangeToArchive(changePath, archivePath);
1451
+ await FileSystemUtils.removeDirectory(changePath, true);
1452
+ spinner.succeed(chalk2.green(`Change "${changeName}" archived successfully`));
1453
+ } catch (error) {
1454
+ spinner.fail(chalk2.red(`Error: ${error.message}`));
1455
+ throw error;
1456
+ }
1457
+ }
1458
+ async mergeDeltaSpecs(changePath, changeName) {
1459
+ const deltaSpecsDir = path4.join(
1460
+ ".",
1461
+ HERASPEC_DIR_NAME,
1462
+ SPECS_DIR_NAME,
1463
+ changeName
1464
+ );
1465
+ if (!await FileSystemUtils.fileExists(deltaSpecsDir)) {
1466
+ return;
1467
+ }
1468
+ const deltaSpecs = await this.findDeltaSpecFiles(deltaSpecsDir);
1469
+ for (const deltaSpec of deltaSpecs) {
1470
+ const relativePath = path4.relative(deltaSpecsDir, deltaSpec.path);
1471
+ const targetSpecPath = path4.join(
1472
+ ".",
1473
+ HERASPEC_DIR_NAME,
1474
+ SPECS_DIR_NAME,
1475
+ relativePath
1476
+ );
1477
+ const deltaContent = await FileSystemUtils.readFile(deltaSpec.path);
1478
+ const parser = new MarkdownParser(deltaContent);
1479
+ const delta = parser.parseDeltaSpec(deltaContent);
1480
+ let targetContent = "";
1481
+ if (await FileSystemUtils.fileExists(targetSpecPath)) {
1482
+ targetContent = await FileSystemUtils.readFile(targetSpecPath);
1483
+ }
1484
+ const mergedContent = this.mergeDeltaIntoSpec(targetContent, delta, deltaSpec.name);
1485
+ await FileSystemUtils.createDirectory(path4.dirname(targetSpecPath));
1486
+ await FileSystemUtils.writeFile(targetSpecPath, mergedContent);
1487
+ }
1488
+ }
1489
+ mergeDeltaIntoSpec(existingContent, delta, specName) {
1490
+ let merged = existingContent || `# Spec: ${specName}
1491
+
1492
+ ## Purpose
1493
+
1494
+ ## Requirements
1495
+
1496
+ `;
1497
+ if (delta.added.length > 0) {
1498
+ merged += "\n## ADDED Requirements\n\n";
1499
+ for (const req of delta.added) {
1500
+ merged += `### Requirement: ${req.name}
1501
+ ${req.description}
1502
+
1503
+ `;
1504
+ }
1505
+ }
1506
+ return merged;
1507
+ }
1508
+ async moveChangeToArchive(sourcePath, archivePath) {
1509
+ const entries = await FileSystemUtils.readDirectory(sourcePath);
1510
+ for (const entry of entries) {
1511
+ const sourceEntry = path4.join(sourcePath, entry);
1512
+ const archiveEntry = path4.join(archivePath, entry);
1513
+ const stats = await FileSystemUtils.stat(sourceEntry);
1514
+ if (stats.isDirectory()) {
1515
+ await FileSystemUtils.createDirectory(archiveEntry);
1516
+ await this.moveChangeToArchive(sourceEntry, archiveEntry);
1517
+ } else {
1518
+ await FileSystemUtils.moveFile(sourceEntry, archiveEntry);
1519
+ }
1520
+ }
1521
+ }
1522
+ async findDeltaSpecFiles(dir, prefix = "") {
1523
+ const specs = [];
1524
+ const entries = await FileSystemUtils.readDirectory(dir);
1525
+ for (const entry of entries) {
1526
+ const entryPath = path4.join(dir, entry);
1527
+ const stats = await FileSystemUtils.stat(entryPath);
1528
+ if (stats.isDirectory()) {
1529
+ const subSpecs = await this.findDeltaSpecFiles(
1530
+ entryPath,
1531
+ prefix ? `${prefix}/${entry}` : entry
1532
+ );
1533
+ specs.push(...subSpecs);
1534
+ } else if (entry.endsWith(".md")) {
1535
+ specs.push({
1536
+ name: prefix || path4.basename(entry, ".md"),
1537
+ path: entryPath
1538
+ });
1539
+ }
1540
+ }
1541
+ return specs;
1542
+ }
1543
+ };
1544
+
1545
+ // src/core/validation/validator.ts
1546
+ import { readFileSync } from "fs";
1547
+ import path5 from "path";
1548
+ var Validator = class {
1549
+ strictMode;
1550
+ constructor(strictMode = false) {
1551
+ this.strictMode = strictMode;
1552
+ }
1553
+ async validateSpec(filePath) {
1554
+ const errors = [];
1555
+ const warnings = [];
1556
+ try {
1557
+ const content = readFileSync(filePath, "utf-8");
1558
+ const parser = new MarkdownParser(content);
1559
+ const specName = this.extractNameFromPath(filePath);
1560
+ const spec = parser.parseSpec(specName);
1561
+ const result = SpecSchema.safeParse(spec);
1562
+ if (!result.success) {
1563
+ result.error.errors.forEach((err) => {
1564
+ errors.push(`${err.path.join(".")}: ${err.message}`);
1565
+ });
1566
+ }
1567
+ if (spec.requirements.length === 0) {
1568
+ errors.push("Spec must have at least one requirement");
1569
+ }
1570
+ for (const req of spec.requirements) {
1571
+ if (!req.description || req.description.trim().length === 0) {
1572
+ errors.push(`Requirement "${req.name}" must have a description`);
1573
+ }
1574
+ if (!req.scenarios || req.scenarios.length === 0) {
1575
+ warnings.push(`Requirement "${req.name}" has no scenarios`);
1576
+ }
1577
+ }
1578
+ return {
1579
+ valid: errors.length === 0,
1580
+ errors,
1581
+ warnings
1582
+ };
1583
+ } catch (error) {
1584
+ return {
1585
+ valid: false,
1586
+ errors: [error instanceof Error ? error.message : "Unknown error"],
1587
+ warnings: []
1588
+ };
1589
+ }
1590
+ }
1591
+ async validateChange(changePath) {
1592
+ const errors = [];
1593
+ const warnings = [];
1594
+ const suggestions = [];
1595
+ const proposalPath = path5.join(changePath, "proposal.md");
1596
+ if (!await FileSystemUtils.fileExists(proposalPath)) {
1597
+ errors.push({
1598
+ message: "Change must have a proposal.md file",
1599
+ path: proposalPath,
1600
+ suggestion: `Create proposal.md in ${changePath} with: # Change Proposal: [name]
1601
+
1602
+ ## Purpose
1603
+ [Description]
1604
+
1605
+ ## Scope
1606
+ [What will change]`,
1607
+ autoFixable: false
1608
+ });
1609
+ suggestions.push(`Create proposal.md file at ${proposalPath}`);
1610
+ }
1611
+ const tasksPath = path5.join(changePath, "tasks.md");
1612
+ if (!await FileSystemUtils.fileExists(tasksPath)) {
1613
+ warnings.push({
1614
+ message: "Change has no tasks.md file",
1615
+ path: tasksPath,
1616
+ suggestion: `Create tasks.md with implementation tasks grouped by project type and skill`,
1617
+ autoFixable: false
1618
+ });
1619
+ }
1620
+ const changeName = path5.basename(changePath);
1621
+ const specsDir = path5.join(
1622
+ ".",
1623
+ HERASPEC_DIR_NAME,
1624
+ SPECS_DIR_NAME,
1625
+ changeName
1626
+ );
1627
+ if (await FileSystemUtils.fileExists(specsDir)) {
1628
+ const deltaSpecs = await this.findDeltaSpecs(specsDir);
1629
+ for (const specPath of deltaSpecs) {
1630
+ const report = await this.validateDeltaSpec(specPath);
1631
+ errors.push(...report.errors);
1632
+ warnings.push(...report.warnings);
1633
+ if (report.suggestions) {
1634
+ suggestions.push(...report.suggestions);
1635
+ }
1636
+ }
1637
+ } else {
1638
+ warnings.push({
1639
+ message: "No delta specs found for this change",
1640
+ suggestion: `Create delta specs in ${specsDir}/spec.md using ADDED/MODIFIED/REMOVED sections`,
1641
+ autoFixable: false
1642
+ });
1643
+ }
1644
+ return {
1645
+ valid: errors.length === 0,
1646
+ errors,
1647
+ warnings,
1648
+ suggestions: suggestions.length > 0 ? suggestions : void 0
1649
+ };
1650
+ }
1651
+ async validateDeltaSpec(filePath) {
1652
+ const errors = [];
1653
+ const warnings = [];
1654
+ const suggestions = [];
1655
+ try {
1656
+ const content = readFileSync(filePath, "utf-8");
1657
+ const parser = new MarkdownParser(content);
1658
+ const delta = parser.parseDeltaSpec(content);
1659
+ if (delta.added.length === 0 && delta.modified.length === 0 && delta.removed.length === 0) {
1660
+ warnings.push({
1661
+ message: "Delta spec has no changes",
1662
+ suggestion: "Add at least one section: ## ADDED Requirements, ## MODIFIED Requirements, or ## REMOVED Requirements",
1663
+ autoFixable: false
1664
+ });
1665
+ }
1666
+ if (!content.includes("## ADDED") && !content.includes("## MODIFIED") && !content.includes("## REMOVED")) {
1667
+ warnings.push({
1668
+ message: "Delta spec may not follow proper format",
1669
+ suggestion: "Use sections: ## ADDED Requirements, ## MODIFIED Requirements, ## REMOVED Requirements",
1670
+ autoFixable: false
1671
+ });
1672
+ }
1673
+ return {
1674
+ valid: errors.length === 0,
1675
+ errors,
1676
+ warnings,
1677
+ suggestions: suggestions.length > 0 ? suggestions : void 0
1678
+ };
1679
+ } catch (error) {
1680
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
1681
+ const suggestion = this.getParseErrorSuggestion(errorMessage);
1682
+ return {
1683
+ valid: false,
1684
+ errors: [{
1685
+ message: errorMessage,
1686
+ suggestion,
1687
+ autoFixable: false
1688
+ }],
1689
+ warnings: [],
1690
+ suggestions: suggestion ? [suggestion] : void 0
1691
+ };
1692
+ }
1693
+ }
1694
+ extractNameFromPath(filePath) {
1695
+ const baseName = path5.basename(filePath, ".md");
1696
+ return baseName === "spec" ? path5.basename(path5.dirname(filePath)) : baseName;
1697
+ }
1698
+ async findDeltaSpecs(dir) {
1699
+ const specs = [];
1700
+ const entries = await FileSystemUtils.readDirectory(dir);
1701
+ for (const entry of entries) {
1702
+ const entryPath = path5.join(dir, entry);
1703
+ const stats = await FileSystemUtils.stat(entryPath);
1704
+ if (stats.isDirectory()) {
1705
+ const subSpecs = await this.findDeltaSpecs(entryPath);
1706
+ specs.push(...subSpecs);
1707
+ } else if (entry.endsWith(".md")) {
1708
+ specs.push(entryPath);
1709
+ }
1710
+ }
1711
+ return specs;
1712
+ }
1713
+ };
1714
+ export {
1715
+ ARCHIVES_DIR_NAME,
1716
+ ArchiveCommand,
1717
+ CHANGES_DIR_NAME,
1718
+ ChangeSchema,
1719
+ DeltaRequirementSchema,
1720
+ DeltaTypeSchema,
1721
+ HERASPEC_DIR_NAME,
1722
+ HERASPEC_MARKERS,
1723
+ InitCommand,
1724
+ ListCommand,
1725
+ PROJECT_TYPES,
1726
+ ProjectTypeSchema,
1727
+ RequirementSchema,
1728
+ SKILLS,
1729
+ SKILLS_DIR_NAME,
1730
+ SPECS_DIR_NAME,
1731
+ ScenarioSchema,
1732
+ SpecMetaSchema,
1733
+ SpecSchema,
1734
+ Validator
1735
+ };
1736
+ //# sourceMappingURL=index.js.map