specweave 0.23.2 → 0.23.4

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 (98) hide show
  1. package/CLAUDE.md +268 -0
  2. package/dist/plugins/specweave/lib/utils/fs-native.d.ts +133 -0
  3. package/dist/plugins/specweave/lib/utils/fs-native.d.ts.map +1 -0
  4. package/dist/plugins/specweave/lib/utils/fs-native.js +224 -0
  5. package/dist/plugins/specweave/lib/utils/fs-native.js.map +1 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
  7. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
  9. package/dist/plugins/specweave-github/lib/github-feature-sync.js +52 -20
  10. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
  11. package/dist/src/cli/helpers/init/initial-increment-generator.d.ts.map +1 -1
  12. package/dist/src/cli/helpers/init/initial-increment-generator.js +2 -1
  13. package/dist/src/cli/helpers/init/initial-increment-generator.js.map +1 -1
  14. package/dist/src/core/ac-test-validator-cli.d.ts +16 -0
  15. package/dist/src/core/ac-test-validator-cli.d.ts.map +1 -0
  16. package/dist/src/core/ac-test-validator-cli.js +118 -0
  17. package/dist/src/core/ac-test-validator-cli.js.map +1 -0
  18. package/dist/src/core/ac-test-validator.d.ts +111 -0
  19. package/dist/src/core/ac-test-validator.d.ts.map +1 -0
  20. package/dist/src/core/ac-test-validator.js +292 -0
  21. package/dist/src/core/ac-test-validator.js.map +1 -0
  22. package/dist/src/core/increment/desync-detector.d.ts +142 -0
  23. package/dist/src/core/increment/desync-detector.d.ts.map +1 -0
  24. package/dist/src/core/increment/desync-detector.js +270 -0
  25. package/dist/src/core/increment/desync-detector.js.map +1 -0
  26. package/dist/src/core/increment/metadata-manager.d.ts +8 -4
  27. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  28. package/dist/src/core/increment/metadata-manager.js +45 -21
  29. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  30. package/dist/src/core/qa/qa-runner.js +9 -2
  31. package/dist/src/core/qa/qa-runner.js.map +1 -1
  32. package/dist/src/sync/sync-coordinator.d.ts +1 -1
  33. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  34. package/dist/src/sync/sync-coordinator.js +40 -2
  35. package/dist/src/sync/sync-coordinator.js.map +1 -1
  36. package/dist/src/utils/fs-native.d.ts +133 -0
  37. package/dist/src/utils/fs-native.d.ts.map +1 -0
  38. package/dist/src/utils/fs-native.js +224 -0
  39. package/dist/src/utils/fs-native.js.map +1 -0
  40. package/package.json +1 -1
  41. package/plugins/specweave/.claude-plugin/plugin.json +12 -0
  42. package/plugins/specweave/agents/AGENTS-INDEX.md +216 -0
  43. package/plugins/specweave/agents/architect/AGENT.md +17 -0
  44. package/plugins/specweave/agents/code-standards-detective/AGENT.md +16 -0
  45. package/plugins/specweave/agents/docs-writer/AGENT.md +16 -0
  46. package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +704 -0
  47. package/plugins/specweave/agents/infrastructure/AGENT.md +16 -0
  48. package/plugins/specweave/agents/performance/AGENT.md +16 -0
  49. package/plugins/specweave/agents/pm/AGENT.md +17 -0
  50. package/plugins/specweave/agents/qa-lead/AGENT.md +15 -0
  51. package/plugins/specweave/agents/reflective-reviewer/AGENT.md +16 -0
  52. package/plugins/specweave/agents/security/AGENT.md +16 -0
  53. package/plugins/specweave/agents/tdd-orchestrator/AGENT.md +16 -0
  54. package/plugins/specweave/agents/tech-lead/AGENT.md +16 -0
  55. package/plugins/specweave/agents/test-aware-planner/AGENT.md +16 -0
  56. package/plugins/specweave/agents/translator/AGENT.md +13 -0
  57. package/plugins/specweave/commands/specweave-done.md +14 -0
  58. package/plugins/specweave/commands/specweave-qa.md +11 -1
  59. package/plugins/specweave/commands/specweave-sync-status.md +356 -0
  60. package/plugins/specweave/commands/specweave-validate.md +10 -1
  61. package/plugins/specweave/hooks/pre-task-completion.sh +196 -0
  62. package/plugins/specweave/lib/hooks/git-diff-analyzer.js +3 -3
  63. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts +3 -3
  64. package/plugins/specweave/lib/hooks/invoke-translator-skill.js +3 -2
  65. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +3 -2
  66. package/plugins/specweave/lib/hooks/prepare-reflection-context.js +3 -3
  67. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts +3 -3
  68. package/plugins/specweave/lib/hooks/reflection-config-loader.js +4 -4
  69. package/plugins/specweave/lib/hooks/reflection-config-loader.ts +4 -4
  70. package/plugins/specweave/lib/hooks/reflection-storage.js +9 -9
  71. package/plugins/specweave/lib/hooks/reflection-storage.ts +9 -9
  72. package/plugins/specweave/lib/hooks/sync-cache.js +9 -8
  73. package/plugins/specweave/lib/hooks/sync-living-docs.js +57 -6
  74. package/plugins/specweave/lib/hooks/sync-us-tasks.js +6 -6
  75. package/plugins/specweave/lib/hooks/translate-file.js +3 -2
  76. package/plugins/specweave/lib/hooks/translate-file.ts +3 -2
  77. package/plugins/specweave/lib/hooks/translate-living-docs.js +4 -3
  78. package/plugins/specweave/lib/hooks/translate-living-docs.ts +4 -3
  79. package/plugins/specweave/lib/utils/fs-native.js +182 -0
  80. package/plugins/specweave/lib/utils/fs-native.ts +283 -0
  81. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +8 -4
  82. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +45 -21
  83. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  84. package/plugins/specweave/skills/SKILLS-INDEX.md +26 -2
  85. package/plugins/specweave/skills/increment-planner/SKILL.md +2 -2
  86. package/plugins/specweave-ado/commands/specweave-ado-close-workitem.md +1 -1
  87. package/plugins/specweave-ado/commands/specweave-ado-create-workitem.md +1 -1
  88. package/plugins/specweave-ado/commands/specweave-ado-status.md +1 -1
  89. package/plugins/specweave-ado/commands/specweave-ado-sync.md +1 -1
  90. package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +1 -1
  91. package/plugins/specweave-diagrams/skills/diagrams-generator/SKILL.md +4 -4
  92. package/plugins/specweave-github/lib/github-client-v2.js +2 -1
  93. package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
  94. package/plugins/specweave-github/lib/github-feature-sync.js +30 -17
  95. package/plugins/specweave-github/lib/github-feature-sync.ts +54 -24
  96. package/plugins/specweave-mobile/README.md +1 -1
  97. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +72 -0
  98. package/plugins/specweave/skills/task-builder/README.md +0 -84
package/CLAUDE.md CHANGED
@@ -314,6 +314,90 @@ npx tsx src/core/status-line-validator.ts
314
314
 
315
315
  **Incident Reference**: 2025-11-20 - Status line showed 21/52 tasks when actually 26/52 were complete (10% desync). Root cause: Tasks marked complete without using TodoWrite, so hooks never fired. Added validation layer and tests to prevent future occurrences.
316
316
 
317
+ ### 7b. GitHub Duplicate Prevention (CRITICAL!)
318
+
319
+ **CRITICAL**: External tool items (GitHub issues, JIRA tickets, ADO work items) MUST NEVER be duplicated.
320
+
321
+ **Rule**: ALWAYS use DuplicateDetector for GitHub issue creation.
322
+
323
+ **Why This Matters**:
324
+ - GitHub search has eventual consistency (2-5 second lag)
325
+ - Race conditions can create duplicates if sync runs multiple times quickly
326
+ - Manual search with `--limit 1` hides duplicates
327
+
328
+ **The ONLY Way to Create GitHub Issues**:
329
+ ```typescript
330
+ import { DuplicateDetector } from './duplicate-detector.js';
331
+
332
+ // ✅ CORRECT: Use DuplicateDetector.createWithProtection()
333
+ const titlePattern = `[${featureId}][${userStory.id}]`; // e.g., "[FS-047][US-001]"
334
+ const result = await DuplicateDetector.createWithProtection({
335
+ title: issueContent.title,
336
+ body: issueContent.body,
337
+ titlePattern,
338
+ labels: issueContent.labels,
339
+ milestone: milestoneTitle,
340
+ repo: `${owner}/${repo}`
341
+ });
342
+
343
+ // Provides automatic:
344
+ // ✅ Phase 1 (Detection): Search for existing before create
345
+ // ✅ Phase 2 (Verification): Count-check after creation
346
+ // ✅ Phase 3 (Reflection): Auto-close duplicates, keep oldest
347
+
348
+ // ❌ WRONG: Manual gh issue create (no duplicate protection!)
349
+ execSync('gh issue create --title "..." --body "..."');
350
+ ```
351
+
352
+ **3-Phase Protection**:
353
+ 1. **Detection** - Search GitHub for existing issues BEFORE creating
354
+ 2. **Verification** - Count-check AFTER creation (handles eventual consistency)
355
+ 3. **Reflection** - Auto-close duplicates if detected, keep oldest
356
+
357
+ **Search Limits** (CRITICAL!):
358
+ ```typescript
359
+ // ❌ WRONG: --limit 1 hides duplicates!
360
+ gh issue list --search "[FS-047][US-001] in:title" --limit 1
361
+ // Returns ONLY 1 result even if 10 duplicates exist!
362
+
363
+ // ✅ CORRECT: --limit 50 detects duplicates
364
+ gh issue list --search "[FS-047][US-001] in:title" --limit 50
365
+ // Returns up to 50 results, enabling duplicate detection
366
+ ```
367
+
368
+ **Cleanup Existing Duplicates**:
369
+ ```bash
370
+ # Preview what will be cleaned up:
371
+ bash scripts/cleanup-duplicate-github-issues.sh --dry-run
372
+
373
+ # Actually close duplicates (keeps oldest):
374
+ bash scripts/cleanup-duplicate-github-issues.sh
375
+ ```
376
+
377
+ **Validation**:
378
+ ```bash
379
+ # Check for duplicates:
380
+ gh issue list --json title,createdAt --limit 100 | \
381
+ jq -r '.[] | .title' | sort | uniq -d
382
+
383
+ # If output is empty → No duplicates! ✅
384
+ # If output shows titles → Duplicates exist, investigate!
385
+ ```
386
+
387
+ **Why This Matters**:
388
+ - Incident 2025-11-20: 10+ duplicate User Story issues created
389
+ - Root Cause #1: Race condition (GitHub search eventual consistency lag)
390
+ - Root Cause #2: `--limit 1` bug (hid duplicates in search results)
391
+ - Root Cause #3: No post-create verification
392
+ - **Fix**: DuplicateDetector integration (github-feature-sync.ts:149-227)
393
+
394
+ **Protected Files**:
395
+ - `plugins/specweave-github/lib/github-feature-sync.ts` - Uses DuplicateDetector
396
+ - `plugins/specweave-github/lib/duplicate-detector.ts` - 3-phase protection
397
+ - `plugins/specweave-github/lib/github-client-v2.ts` - Fixed --limit 50
398
+
399
+ **Incident Reference**: 2025-11-20 - Duplicate GitHub issues for User Stories ([SP-US-006], [SP-US-007], etc.). Root cause: Race conditions + --limit 1 bug + missing DuplicateDetector integration. Fixed by implementing 3-phase protection. See `.specweave/increments/0047-us-task-linkage/reports/DUPLICATE-GITHUB-ISSUES-ROOT-CAUSE.md`.
400
+
317
401
  ### 8. NEVER Use `console.*` in Production Code
318
402
 
319
403
  **Rule**: ALL `src/` code MUST use logger abstraction, NEVER `console.log/error/warn`.
@@ -762,6 +846,190 @@ bash scripts/validate-marketplace-plugins.sh
762
846
  - Validation script: `scripts/validate-marketplace-plugins.sh`
763
847
  - Fix script: `bin/fix-marketplace-errors.sh`
764
848
 
849
+ ### 15. Skills vs Agents: Understanding the Distinction (CRITICAL!)
850
+
851
+ **NEVER confuse skills with agents** - They are different components with different invocation methods.
852
+
853
+ **The Problem**:
854
+ Empty agent directories cause "Agent type not found" errors when someone tries to invoke them. This happened with `specweave:increment-quality-judge-v2` which existed as a skill but had an empty agent directory.
855
+
856
+ **Key Differences**:
857
+
858
+ | Aspect | Skills | Agents |
859
+ |--------|--------|--------|
860
+ | **Location** | `plugins/*/skills/name/SKILL.md` | `plugins/*/agents/name/AGENT.md` |
861
+ | **Invocation** | Skill tool or slash commands | Task tool with `subagent_type` |
862
+ | **Activation** | Automatic (based on keywords) | Explicit (you call them) |
863
+ | **Required File** | `SKILL.md` with YAML frontmatter | `AGENT.md` or agent config |
864
+ | **Purpose** | Expand context with knowledge | Execute multi-step tasks |
865
+
866
+ **Correct Invocation Examples**:
867
+
868
+ ```typescript
869
+ // ✅ CORRECT: Invoking a skill
870
+ Skill({ skill: "increment-quality-judge-v2" });
871
+ // OR use slash command:
872
+ /specweave:qa 0047
873
+
874
+ // ✅ CORRECT: Invoking an agent
875
+ Task({
876
+ subagent_type: "specweave:qa-lead:qa-lead",
877
+ prompt: "Create test plan for increment 0047"
878
+ });
879
+
880
+ // ❌ WRONG: Trying to invoke skill as agent
881
+ Task({
882
+ subagent_type: "specweave:increment-quality-judge-v2", // ERROR!
883
+ prompt: "Quality assessment"
884
+ });
885
+ ```
886
+
887
+ **Agent Naming Convention** (CRITICAL!):
888
+
889
+ Claude Code agents follow a strict naming pattern for `subagent_type`:
890
+
891
+ **Directory-based agents**: `{plugin-name}:{directory-name}:{name-from-yaml}`
892
+
893
+ Examples:
894
+ - Agent at `plugins/specweave/agents/qa-lead/AGENT.md` with `name: qa-lead`
895
+ → Invoke as: `specweave:qa-lead:qa-lead`
896
+ - Agent at `plugins/specweave/agents/pm/AGENT.md` with `name: pm`
897
+ → Invoke as: `specweave:pm:pm`
898
+ - Agent at `plugins/specweave/agents/architect/AGENT.md` with `name: architect`
899
+ → Invoke as: `specweave:architect:architect`
900
+
901
+ **Why the "duplication"?**
902
+ The pattern is `{plugin}:{directory}:{yaml-name}`. When the directory name matches the YAML `name` field (best practice), it creates the appearance of duplication: `qa-lead:qa-lead`.
903
+
904
+ **File-based agents** (legacy pattern):
905
+ - Agent at `plugins/specweave/agents/code-reviewer.md`
906
+ → Invoke as: `specweave:code-reviewer` (no duplication)
907
+
908
+ **How to find the correct agent type**:
909
+ ```bash
910
+ # List all available agents
911
+ ls -la plugins/specweave/agents/
912
+
913
+ # Check YAML name field
914
+ head -5 plugins/specweave/agents/qa-lead/AGENT.md
915
+ # Output: name: qa-lead
916
+
917
+ # Construct agent type: specweave:qa-lead:qa-lead
918
+ ```
919
+
920
+ **Common Mistakes**:
921
+ ```typescript
922
+ // ❌ WRONG: Missing the directory/name duplication
923
+ Task({ subagent_type: "specweave:qa-lead", ... });
924
+ // Error: Agent type 'specweave:qa-lead' not found
925
+
926
+ // ✅ CORRECT: Full pattern with duplication
927
+ Task({ subagent_type: "specweave:qa-lead:qa-lead", ... });
928
+ ```
929
+
930
+ **Directory Structure Requirements**:
931
+
932
+ ```bash
933
+ # ✅ CORRECT: Skill with SKILL.md
934
+ plugins/specweave/skills/increment-quality-judge-v2/
935
+ └── SKILL.md (with YAML frontmatter)
936
+
937
+ # ✅ CORRECT: Agent with AGENT.md
938
+ plugins/specweave/agents/qa-lead/
939
+ └── AGENT.md
940
+
941
+ # ❌ WRONG: Empty agent directory
942
+ plugins/specweave/agents/increment-quality-judge-v2/
943
+ (empty - no AGENT.md!)
944
+
945
+ # ❌ WRONG: Skill missing SKILL.md
946
+ plugins/specweave/skills/my-skill/
947
+ └── some-file.txt (not SKILL.md!)
948
+ ```
949
+
950
+ **SKILL.md Format** (MANDATORY):
951
+ ```yaml
952
+ ---
953
+ name: skill-name
954
+ description: What it does AND trigger keywords. Include all variations.
955
+ ---
956
+
957
+ # Skill Content
958
+
959
+ Your skill documentation here...
960
+ ```
961
+
962
+ **Validation** (MANDATORY before commits):
963
+
964
+ ```bash
965
+ # Check for empty/invalid directories
966
+ bash scripts/validate-plugin-directories.sh
967
+
968
+ # Auto-fix empty directories
969
+ bash scripts/validate-plugin-directories.sh --fix
970
+
971
+ # This catches:
972
+ # - Empty agent/skill directories
973
+ # - Missing SKILL.md files
974
+ # - Missing YAML frontmatter
975
+ # - Invalid plugin structures
976
+ ```
977
+
978
+ **Common Mistakes**:
979
+
980
+ 1. **Creating empty agent directories during scaffolding**
981
+ ```bash
982
+ # ❌ WRONG
983
+ mkdir -p plugins/specweave/agents/new-agent
984
+ git add . && git commit # Empty directory!
985
+
986
+ # ✅ CORRECT
987
+ mkdir -p plugins/specweave/agents/new-agent
988
+ echo "---" > plugins/specweave/agents/new-agent/AGENT.md
989
+ # ... add agent content ...
990
+ git add . && git commit
991
+ ```
992
+
993
+ 2. **Copying skill as agent without changing structure**
994
+ - Skills need `SKILL.md` with YAML
995
+ - Agents need `AGENT.md` or config
996
+ - Don't copy-paste between them!
997
+
998
+ 3. **Using wrong invocation method**
999
+ - Check available skills: Skills list in context
1000
+ - Check available agents: Error message shows all agents
1001
+ - Skills → Skill tool or slash commands
1002
+ - Agents → Task tool
1003
+
1004
+ **When to Use Skills vs Agents**:
1005
+
1006
+ **Use Skills when**:
1007
+ - Providing domain knowledge (e.g., Kafka best practices)
1008
+ - Expanding Claude's context with project-specific info
1009
+ - Explaining concepts, patterns, frameworks
1010
+ - Want automatic activation based on keywords
1011
+
1012
+ **Use Agents when**:
1013
+ - Need multi-step task execution (e.g., generate docs)
1014
+ - Require specialized sub-agent capabilities
1015
+ - Building complex workflows with tools
1016
+ - Want explicit control over when they run
1017
+
1018
+ **Incident History**:
1019
+ - **2025-11-20**: `specweave:increment-quality-judge-v2` agent invocation failed because only skill existed. Empty agent directory caused "Agent type not found" error. Fixed by removing empty directory and documenting distinction.
1020
+
1021
+ **Prevention**:
1022
+ 1. Run `bash scripts/validate-plugin-directories.sh` before commits
1023
+ 2. Never create empty agent/skill directories
1024
+ 3. Always include required files (SKILL.md, AGENT.md)
1025
+ 4. Test invocation method matches component type
1026
+ 5. Use pre-commit hook (blocks invalid structures)
1027
+
1028
+ **See Also**:
1029
+ - Validation script: `scripts/validate-plugin-directories.sh`
1030
+ - Skills index: `plugins/specweave/skills/SKILLS-INDEX.md`
1031
+ - Claude Code Skills docs: `~/CLAUDE.md` (Personal skills reference)
1032
+
765
1033
  ---
766
1034
 
767
1035
  ## Project Structure
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Native Node.js fs API Helpers
3
+ *
4
+ * Drop-in replacements for fs-extra methods using only Node.js stdlib.
5
+ * All methods use native Node.js 20+ APIs with no external dependencies.
6
+ *
7
+ * Migration from fs-extra:
8
+ * - import fs from 'fs-extra' → import * as fs from './utils/fs-native.js'
9
+ * - All fs-extra methods work as drop-in replacements
10
+ *
11
+ * Benefits:
12
+ * - Zero bundle overhead (no npm packages)
13
+ * - Works in marketplace (no node_modules needed)
14
+ * - Faster startup (native APIs)
15
+ * - Better debugging (native stack traces)
16
+ */
17
+ import { promises as fsPromises, existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync, rmSync, unlinkSync } from 'fs';
18
+ /**
19
+ * Ensures that a directory exists. If the directory does not exist, it is created.
20
+ * @param dirPath - The directory path to ensure
21
+ */
22
+ export declare function ensureDir(dirPath: string): Promise<void>;
23
+ /**
24
+ * Synchronous version of ensureDir
25
+ */
26
+ export declare function ensureDirSync(dirPath: string): void;
27
+ /**
28
+ * Alias for ensureDirSync (fs-extra compatibility)
29
+ */
30
+ export declare function mkdirpSync(dirPath: string): void;
31
+ /**
32
+ * Check if a path exists
33
+ * @param filePath - The path to check
34
+ */
35
+ export declare function pathExists(filePath: string): Promise<boolean>;
36
+ /**
37
+ * Synchronous version of pathExists
38
+ */
39
+ export { existsSync };
40
+ /**
41
+ * Read a JSON file and parse it
42
+ * @param filePath - The JSON file path
43
+ */
44
+ export declare function readJson(filePath: string): Promise<any>;
45
+ /**
46
+ * Synchronous version of readJson
47
+ */
48
+ export declare function readJsonSync(filePath: string): any;
49
+ /**
50
+ * Write a JSON file with formatting
51
+ * @param filePath - The JSON file path
52
+ * @param data - The data to write
53
+ * @param options - Options (spaces for indentation)
54
+ */
55
+ export declare function writeJson(filePath: string, data: any, options?: {
56
+ spaces?: number;
57
+ }): Promise<void>;
58
+ /**
59
+ * Synchronous version of writeJson
60
+ */
61
+ export declare function writeJsonSync(filePath: string, data: any, options?: {
62
+ spaces?: number;
63
+ }): void;
64
+ /**
65
+ * Remove a file or directory (recursively)
66
+ * @param targetPath - The path to remove
67
+ */
68
+ export declare function remove(targetPath: string): Promise<void>;
69
+ /**
70
+ * Synchronous version of remove
71
+ */
72
+ export declare function removeSync(targetPath: string): void;
73
+ /**
74
+ * Copy a file or directory
75
+ * @param src - Source path
76
+ * @param dest - Destination path
77
+ * @param options - Copy options
78
+ */
79
+ export declare function copy(src: string, dest: string, options?: {
80
+ overwrite?: boolean;
81
+ filter?: (src: string) => boolean;
82
+ }): Promise<void>;
83
+ /**
84
+ * Synchronous version of copy
85
+ */
86
+ export declare function copySync(src: string, dest: string, options?: {
87
+ overwrite?: boolean;
88
+ filter?: (src: string) => boolean;
89
+ }): void;
90
+ /**
91
+ * Ensure a file exists (create if it doesn't)
92
+ * @param filePath - The file path
93
+ */
94
+ export declare function ensureFile(filePath: string): Promise<void>;
95
+ /**
96
+ * Synchronous version of ensureFile
97
+ */
98
+ export declare function ensureFileSync(filePath: string): void;
99
+ export declare const readFile: typeof fsPromises.readFile, writeFile: typeof fsPromises.writeFile, appendFile: typeof fsPromises.appendFile, stat: typeof fsPromises.stat, readdir: typeof fsPromises.readdir, access: typeof fsPromises.access, unlink: typeof fsPromises.unlink, rmdir: typeof fsPromises.rmdir, rename: typeof fsPromises.rename, chmod: typeof fsPromises.chmod;
100
+ export { readFileSync, writeFileSync, statSync, readdirSync, unlinkSync, mkdirSync, rmSync, };
101
+ declare const _default: {
102
+ ensureDir: typeof ensureDir;
103
+ pathExists: typeof pathExists;
104
+ readJson: typeof readJson;
105
+ writeJson: typeof writeJson;
106
+ remove: typeof remove;
107
+ copy: typeof copy;
108
+ ensureFile: typeof ensureFile;
109
+ readFile: typeof fsPromises.readFile;
110
+ writeFile: typeof fsPromises.writeFile;
111
+ appendFile: typeof fsPromises.appendFile;
112
+ stat: typeof fsPromises.stat;
113
+ readdir: typeof fsPromises.readdir;
114
+ access: typeof fsPromises.access;
115
+ unlink: typeof fsPromises.unlink;
116
+ ensureDirSync: typeof ensureDirSync;
117
+ mkdirpSync: typeof mkdirpSync;
118
+ existsSync: typeof existsSync;
119
+ readJsonSync: typeof readJsonSync;
120
+ writeJsonSync: typeof writeJsonSync;
121
+ removeSync: typeof removeSync;
122
+ copySync: typeof copySync;
123
+ ensureFileSync: typeof ensureFileSync;
124
+ readFileSync: typeof readFileSync;
125
+ writeFileSync: typeof writeFileSync;
126
+ statSync: import("fs").StatSyncFn;
127
+ readdirSync: typeof readdirSync;
128
+ unlinkSync: typeof unlinkSync;
129
+ mkdirSync: typeof mkdirSync;
130
+ rmSync: typeof rmSync;
131
+ };
132
+ export default _default;
133
+ //# sourceMappingURL=fs-native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-native.d.ts","sourceRoot":"","sources":["../../../../../plugins/specweave/lib/utils/fs-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAgB,MAAM,IAAI,CAAC;AAIzJ;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAInD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnE;AAED;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAG7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAGlD;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,IAAI,CAIN;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAInD;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CACxB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACnE,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACnE,IAAI,CA4BN;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAKrD;AAGD,eAAO,MACL,QAAQ,8BACR,SAAS,+BACT,UAAU,gCACV,IAAI,0BACJ,OAAO,6BACP,MAAM,4BACN,MAAM,4BACN,KAAK,2BACL,MAAM,4BACN,KAAK,yBACO,CAAC;AAGf,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,WAAW,EACX,UAAU,EACV,SAAS,EACT,MAAM,GACP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGF,wBAiCE"}
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Native Node.js fs API Helpers
3
+ *
4
+ * Drop-in replacements for fs-extra methods using only Node.js stdlib.
5
+ * All methods use native Node.js 20+ APIs with no external dependencies.
6
+ *
7
+ * Migration from fs-extra:
8
+ * - import fs from 'fs-extra' → import * as fs from './utils/fs-native.js'
9
+ * - All fs-extra methods work as drop-in replacements
10
+ *
11
+ * Benefits:
12
+ * - Zero bundle overhead (no npm packages)
13
+ * - Works in marketplace (no node_modules needed)
14
+ * - Faster startup (native APIs)
15
+ * - Better debugging (native stack traces)
16
+ */
17
+ import { promises as fsPromises, existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync, rmSync, unlinkSync, copyFileSync } from 'fs';
18
+ import path from 'path';
19
+ /**
20
+ * Ensures that a directory exists. If the directory does not exist, it is created.
21
+ * @param dirPath - The directory path to ensure
22
+ */
23
+ export async function ensureDir(dirPath) {
24
+ if (!existsSync(dirPath)) {
25
+ await fsPromises.mkdir(dirPath, { recursive: true });
26
+ }
27
+ }
28
+ /**
29
+ * Synchronous version of ensureDir
30
+ */
31
+ export function ensureDirSync(dirPath) {
32
+ if (!existsSync(dirPath)) {
33
+ mkdirSync(dirPath, { recursive: true });
34
+ }
35
+ }
36
+ /**
37
+ * Alias for ensureDirSync (fs-extra compatibility)
38
+ */
39
+ export function mkdirpSync(dirPath) {
40
+ ensureDirSync(dirPath);
41
+ }
42
+ /**
43
+ * Check if a path exists
44
+ * @param filePath - The path to check
45
+ */
46
+ export async function pathExists(filePath) {
47
+ return existsSync(filePath);
48
+ }
49
+ /**
50
+ * Synchronous version of pathExists
51
+ */
52
+ export { existsSync };
53
+ /**
54
+ * Read a JSON file and parse it
55
+ * @param filePath - The JSON file path
56
+ */
57
+ export async function readJson(filePath) {
58
+ const content = await fsPromises.readFile(filePath, 'utf-8');
59
+ return JSON.parse(content);
60
+ }
61
+ /**
62
+ * Synchronous version of readJson
63
+ */
64
+ export function readJsonSync(filePath) {
65
+ const content = readFileSync(filePath, 'utf-8');
66
+ return JSON.parse(content);
67
+ }
68
+ /**
69
+ * Write a JSON file with formatting
70
+ * @param filePath - The JSON file path
71
+ * @param data - The data to write
72
+ * @param options - Options (spaces for indentation)
73
+ */
74
+ export async function writeJson(filePath, data, options) {
75
+ const spaces = options?.spaces ?? 2;
76
+ const content = JSON.stringify(data, null, spaces);
77
+ await fsPromises.writeFile(filePath, content, 'utf-8');
78
+ }
79
+ /**
80
+ * Synchronous version of writeJson
81
+ */
82
+ export function writeJsonSync(filePath, data, options) {
83
+ const spaces = options?.spaces ?? 2;
84
+ const content = JSON.stringify(data, null, spaces);
85
+ writeFileSync(filePath, content, 'utf-8');
86
+ }
87
+ /**
88
+ * Remove a file or directory (recursively)
89
+ * @param targetPath - The path to remove
90
+ */
91
+ export async function remove(targetPath) {
92
+ if (existsSync(targetPath)) {
93
+ await fsPromises.rm(targetPath, { recursive: true, force: true });
94
+ }
95
+ }
96
+ /**
97
+ * Synchronous version of remove
98
+ */
99
+ export function removeSync(targetPath) {
100
+ if (existsSync(targetPath)) {
101
+ rmSync(targetPath, { recursive: true, force: true });
102
+ }
103
+ }
104
+ /**
105
+ * Copy a file or directory
106
+ * @param src - Source path
107
+ * @param dest - Destination path
108
+ * @param options - Copy options
109
+ */
110
+ export async function copy(src, dest, options) {
111
+ const srcStat = await fsPromises.stat(src);
112
+ if (srcStat.isFile()) {
113
+ // Copy single file
114
+ await fsPromises.mkdir(path.dirname(dest), { recursive: true });
115
+ await fsPromises.copyFile(src, dest);
116
+ }
117
+ else if (srcStat.isDirectory()) {
118
+ // Copy directory recursively
119
+ await fsPromises.mkdir(dest, { recursive: true });
120
+ const entries = await fsPromises.readdir(src, { withFileTypes: true });
121
+ for (const entry of entries) {
122
+ const srcPath = path.join(src, entry.name);
123
+ const destPath = path.join(dest, entry.name);
124
+ // Apply filter if provided
125
+ if (options?.filter && !options.filter(srcPath)) {
126
+ continue;
127
+ }
128
+ if (entry.isDirectory()) {
129
+ await copy(srcPath, destPath, options);
130
+ }
131
+ else {
132
+ await fsPromises.copyFile(srcPath, destPath);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * Synchronous version of copy
139
+ */
140
+ export function copySync(src, dest, options) {
141
+ const srcStat = statSync(src);
142
+ if (srcStat.isFile()) {
143
+ // Copy single file
144
+ mkdirSync(path.dirname(dest), { recursive: true });
145
+ copyFileSync(src, dest);
146
+ }
147
+ else if (srcStat.isDirectory()) {
148
+ // Copy directory recursively
149
+ mkdirSync(dest, { recursive: true });
150
+ const entries = readdirSync(src, { withFileTypes: true });
151
+ for (const entry of entries) {
152
+ const srcPath = path.join(src, entry.name);
153
+ const destPath = path.join(dest, entry.name);
154
+ // Apply filter if provided
155
+ if (options?.filter && !options.filter(srcPath)) {
156
+ continue;
157
+ }
158
+ if (entry.isDirectory()) {
159
+ copySync(srcPath, destPath, options);
160
+ }
161
+ else {
162
+ copyFileSync(srcPath, destPath);
163
+ }
164
+ }
165
+ }
166
+ }
167
+ /**
168
+ * Ensure a file exists (create if it doesn't)
169
+ * @param filePath - The file path
170
+ */
171
+ export async function ensureFile(filePath) {
172
+ if (!existsSync(filePath)) {
173
+ await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
174
+ await fsPromises.writeFile(filePath, '', 'utf-8');
175
+ }
176
+ }
177
+ /**
178
+ * Synchronous version of ensureFile
179
+ */
180
+ export function ensureFileSync(filePath) {
181
+ if (!existsSync(filePath)) {
182
+ mkdirSync(path.dirname(filePath), { recursive: true });
183
+ writeFileSync(filePath, '', 'utf-8');
184
+ }
185
+ }
186
+ // Re-export common fs/promises methods for convenience
187
+ export const { readFile, writeFile, appendFile, stat, readdir, access, unlink, rmdir, rename, chmod, } = fsPromises;
188
+ // Re-export common synchronous methods
189
+ export { readFileSync, writeFileSync, statSync, readdirSync, unlinkSync, mkdirSync, rmSync, };
190
+ // Default export for convenience
191
+ export default {
192
+ // Async methods
193
+ ensureDir,
194
+ pathExists,
195
+ readJson,
196
+ writeJson,
197
+ remove,
198
+ copy,
199
+ ensureFile,
200
+ readFile,
201
+ writeFile,
202
+ appendFile,
203
+ stat,
204
+ readdir,
205
+ access,
206
+ unlink,
207
+ // Sync methods
208
+ ensureDirSync,
209
+ mkdirpSync,
210
+ existsSync,
211
+ readJsonSync,
212
+ writeJsonSync,
213
+ removeSync,
214
+ copySync,
215
+ ensureFileSync,
216
+ readFileSync,
217
+ writeFileSync,
218
+ statSync,
219
+ readdirSync,
220
+ unlinkSync,
221
+ mkdirSync,
222
+ rmSync,
223
+ };
224
+ //# sourceMappingURL=fs-native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-native.js","sourceRoot":"","sources":["../../../../../plugins/specweave/lib/utils/fs-native.ts"],"names":[],"mappings":"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"}
@@ -258,7 +258,7 @@ export class GitHubClientV2 {
258
258
  '--json',
259
259
  'number,title,state,url,labels',
260
260
  '--limit',
261
- '1',
261
+ '50', // ✅ FIX: Increased from 1 to 50 to catch duplicates (Issue #0047)
262
262
  ]);
263
263
  if (result.exitCode !== 0) {
264
264
  // Search failed, return null (treat as not found)