specweave 0.23.2 → 0.23.5

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 (106) hide show
  1. package/CLAUDE.md +367 -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/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -1
  12. package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +24 -0
  13. package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -1
  14. package/dist/src/cli/helpers/init/initial-increment-generator.d.ts.map +1 -1
  15. package/dist/src/cli/helpers/init/initial-increment-generator.js +2 -1
  16. package/dist/src/cli/helpers/init/initial-increment-generator.js.map +1 -1
  17. package/dist/src/core/ac-test-validator-cli.d.ts +16 -0
  18. package/dist/src/core/ac-test-validator-cli.d.ts.map +1 -0
  19. package/dist/src/core/ac-test-validator-cli.js +118 -0
  20. package/dist/src/core/ac-test-validator-cli.js.map +1 -0
  21. package/dist/src/core/ac-test-validator.d.ts +111 -0
  22. package/dist/src/core/ac-test-validator.d.ts.map +1 -0
  23. package/dist/src/core/ac-test-validator.js +292 -0
  24. package/dist/src/core/ac-test-validator.js.map +1 -0
  25. package/dist/src/core/increment/desync-detector.d.ts +142 -0
  26. package/dist/src/core/increment/desync-detector.d.ts.map +1 -0
  27. package/dist/src/core/increment/desync-detector.js +270 -0
  28. package/dist/src/core/increment/desync-detector.js.map +1 -0
  29. package/dist/src/core/increment/metadata-manager.d.ts +8 -4
  30. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  31. package/dist/src/core/increment/metadata-manager.js +45 -21
  32. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  33. package/dist/src/core/qa/qa-runner.js +9 -2
  34. package/dist/src/core/qa/qa-runner.js.map +1 -1
  35. package/dist/src/sync/sync-coordinator.d.ts +1 -1
  36. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  37. package/dist/src/sync/sync-coordinator.js +40 -2
  38. package/dist/src/sync/sync-coordinator.js.map +1 -1
  39. package/dist/src/utils/fs-native.d.ts +133 -0
  40. package/dist/src/utils/fs-native.d.ts.map +1 -0
  41. package/dist/src/utils/fs-native.js +224 -0
  42. package/dist/src/utils/fs-native.js.map +1 -0
  43. package/package.json +1 -1
  44. package/plugins/specweave/.claude-plugin/plugin.json +12 -0
  45. package/plugins/specweave/agents/AGENTS-INDEX.md +216 -0
  46. package/plugins/specweave/agents/architect/AGENT.md +17 -0
  47. package/plugins/specweave/agents/code-standards-detective/AGENT.md +16 -0
  48. package/plugins/specweave/agents/docs-writer/AGENT.md +16 -0
  49. package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +704 -0
  50. package/plugins/specweave/agents/infrastructure/AGENT.md +16 -0
  51. package/plugins/specweave/agents/performance/AGENT.md +16 -0
  52. package/plugins/specweave/agents/pm/AGENT.md +17 -0
  53. package/plugins/specweave/agents/qa-lead/AGENT.md +15 -0
  54. package/plugins/specweave/agents/reflective-reviewer/AGENT.md +16 -0
  55. package/plugins/specweave/agents/security/AGENT.md +16 -0
  56. package/plugins/specweave/agents/tdd-orchestrator/AGENT.md +16 -0
  57. package/plugins/specweave/agents/tech-lead/AGENT.md +16 -0
  58. package/plugins/specweave/agents/test-aware-planner/AGENT.md +16 -0
  59. package/plugins/specweave/agents/translator/AGENT.md +13 -0
  60. package/plugins/specweave/commands/specweave-done.md +14 -0
  61. package/plugins/specweave/commands/specweave-qa.md +11 -1
  62. package/plugins/specweave/commands/specweave-sync-status.md +356 -0
  63. package/plugins/specweave/commands/specweave-validate.md +10 -1
  64. package/plugins/specweave/hooks/pre-task-completion.sh +196 -0
  65. package/plugins/specweave/lib/hooks/git-diff-analyzer.js +3 -3
  66. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts +3 -3
  67. package/plugins/specweave/lib/hooks/invoke-translator-skill.js +3 -2
  68. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +3 -2
  69. package/plugins/specweave/lib/hooks/prepare-reflection-context.js +3 -3
  70. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts +3 -3
  71. package/plugins/specweave/lib/hooks/reflection-config-loader.js +4 -4
  72. package/plugins/specweave/lib/hooks/reflection-config-loader.ts +4 -4
  73. package/plugins/specweave/lib/hooks/reflection-storage.js +9 -9
  74. package/plugins/specweave/lib/hooks/reflection-storage.ts +9 -9
  75. package/plugins/specweave/lib/hooks/sync-cache.js +9 -8
  76. package/plugins/specweave/lib/hooks/sync-living-docs.js +57 -6
  77. package/plugins/specweave/lib/hooks/sync-us-tasks.js +6 -6
  78. package/plugins/specweave/lib/hooks/translate-file.js +3 -2
  79. package/plugins/specweave/lib/hooks/translate-file.ts +3 -2
  80. package/plugins/specweave/lib/hooks/translate-living-docs.js +4 -3
  81. package/plugins/specweave/lib/hooks/translate-living-docs.ts +4 -3
  82. package/plugins/specweave/lib/hooks/update-tasks-md.js +3 -3
  83. package/plugins/specweave/lib/hooks/update-tasks-md.ts +3 -3
  84. package/plugins/specweave/lib/utils/fs-native.js +182 -0
  85. package/plugins/specweave/lib/utils/fs-native.ts +283 -0
  86. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +8 -4
  87. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +45 -21
  88. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  89. package/plugins/specweave/skills/SKILLS-INDEX.md +26 -2
  90. package/plugins/specweave/skills/increment-planner/SKILL.md +2 -2
  91. package/plugins/specweave-ado/commands/specweave-ado-close-workitem.md +1 -1
  92. package/plugins/specweave-ado/commands/specweave-ado-create-workitem.md +1 -1
  93. package/plugins/specweave-ado/commands/specweave-ado-status.md +1 -1
  94. package/plugins/specweave-ado/commands/specweave-ado-sync.md +1 -1
  95. package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +1 -1
  96. package/plugins/specweave-diagrams/skills/diagrams-generator/SKILL.md +4 -4
  97. package/plugins/specweave-github/lib/github-client-v2.js +2 -1
  98. package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
  99. package/plugins/specweave-github/lib/github-feature-sync.js +30 -17
  100. package/plugins/specweave-github/lib/github-feature-sync.ts +54 -24
  101. package/plugins/specweave-github/lib/user-story-issue-builder.js +24 -0
  102. package/plugins/specweave-github/lib/user-story-issue-builder.ts +33 -0
  103. package/plugins/specweave-mobile/README.md +1 -1
  104. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +72 -0
  105. package/src/templates/CLAUDE.md.template +13 -0
  106. package/plugins/specweave/skills/task-builder/README.md +0 -84
@@ -0,0 +1,196 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave Pre-Task-Completion Hook
4
+ # CRITICAL QUALITY GATE: Validates AC tests before allowing task completion
5
+ #
6
+ # Runs automatically BEFORE any task is marked complete via TodoWrite
7
+ #
8
+ # WORKFLOW:
9
+ # =========
10
+ # 1. TodoWrite called with status="completed"
11
+ # 2. This hook fires (pre-completion validation)
12
+ # 3. Extract task ID from TodoWrite input
13
+ # 4. Find task in tasks.md
14
+ # 5. Run AC test validator
15
+ # 6. If tests PASS → Allow completion (continue: true)
16
+ # 7. If tests FAIL → Block completion (continue: false, show error)
17
+ #
18
+ # ENFORCEMENT:
19
+ # ============
20
+ # This is the ONLY way to mark tasks complete in SpecWeave.
21
+ # Manual edits to tasks.md are detected and flagged by pre-commit hooks.
22
+
23
+ set -e
24
+
25
+ # Find project root
26
+ find_project_root() {
27
+ local dir="$1"
28
+ while [ "$dir" != "/" ]; do
29
+ if [ -d "$dir/.specweave" ]; then
30
+ echo "$dir"
31
+ return 0
32
+ fi
33
+ dir="$(dirname "$dir")"
34
+ done
35
+ pwd
36
+ }
37
+
38
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
39
+ cd "$PROJECT_ROOT" 2>/dev/null || true
40
+
41
+ # ============================================================================
42
+ # CONFIGURATION
43
+ # ============================================================================
44
+
45
+ LOGS_DIR=".specweave/logs"
46
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
47
+
48
+ mkdir -p "$LOGS_DIR" 2>/dev/null || true
49
+
50
+ echo "[$(date)] 🔒 Pre-task-completion hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
51
+
52
+ # ============================================================================
53
+ # CAPTURE INPUT
54
+ # ============================================================================
55
+
56
+ STDIN_DATA=$(mktemp)
57
+ cat > "$STDIN_DATA"
58
+
59
+ echo "[$(date)] Input JSON:" >> "$DEBUG_LOG" 2>/dev/null || true
60
+ cat "$STDIN_DATA" >> "$DEBUG_LOG" 2>/dev/null || true
61
+
62
+ # ============================================================================
63
+ # CHECK FOR TASK COMPLETION
64
+ # ============================================================================
65
+
66
+ # Only validate if a task is being marked complete
67
+ COMPLETING_TASK=false
68
+
69
+ if command -v jq >/dev/null 2>&1; then
70
+ # Check if any task is transitioning to "completed" status
71
+ COMPLETED_COUNT=$(jq -r '.tool_input.todos // [] | map(select(.status == "completed")) | length' "$STDIN_DATA" 2>/dev/null || echo "0")
72
+
73
+ if [ "$COMPLETED_COUNT" != "0" ]; then
74
+ COMPLETING_TASK=true
75
+ echo "[$(date)] ✓ Detected task completion (${COMPLETED_COUNT} tasks)" >> "$DEBUG_LOG" 2>/dev/null || true
76
+ fi
77
+ fi
78
+
79
+ # If no tasks being completed, allow without validation
80
+ if [ "$COMPLETING_TASK" = "false" ]; then
81
+ echo "[$(date)] ⏭️ No tasks being completed, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
82
+ rm -f "$STDIN_DATA"
83
+ cat <<EOF
84
+ {
85
+ "continue": true
86
+ }
87
+ EOF
88
+ exit 0
89
+ fi
90
+
91
+ # ============================================================================
92
+ # DETECT CURRENT INCREMENT
93
+ # ============================================================================
94
+
95
+ CURRENT_INCREMENT=$(ls -t .specweave/increments/ 2>/dev/null | grep -v "_backlog" | grep -v "_archive" | head -1)
96
+
97
+ if [ -z "$CURRENT_INCREMENT" ]; then
98
+ echo "[$(date)] ⚠️ No active increment found, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
99
+ rm -f "$STDIN_DATA"
100
+ cat <<EOF
101
+ {
102
+ "continue": true,
103
+ "systemMessage": "⚠️ Warning: No active increment found. Task completion validation skipped."
104
+ }
105
+ EOF
106
+ exit 0
107
+ fi
108
+
109
+ TASKS_MD=".specweave/increments/$CURRENT_INCREMENT/tasks.md"
110
+
111
+ if [ ! -f "$TASKS_MD" ]; then
112
+ echo "[$(date)] ⚠️ tasks.md not found for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
113
+ rm -f "$STDIN_DATA"
114
+ cat <<EOF
115
+ {
116
+ "continue": true,
117
+ "systemMessage": "⚠️ Warning: tasks.md not found. Task completion validation skipped."
118
+ }
119
+ EOF
120
+ exit 0
121
+ fi
122
+
123
+ # ============================================================================
124
+ # RUN AC TEST VALIDATION
125
+ # ============================================================================
126
+
127
+ echo "[$(date)] 🧪 Running AC test validation for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
128
+
129
+ # Determine which validation script to use
130
+ VALIDATOR_SCRIPT=""
131
+ if [ -f "$PROJECT_ROOT/dist/src/core/ac-test-validator-cli.js" ]; then
132
+ VALIDATOR_SCRIPT="$PROJECT_ROOT/dist/src/core/ac-test-validator-cli.js"
133
+ elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/src/core/ac-test-validator-cli.js" ]; then
134
+ VALIDATOR_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/src/core/ac-test-validator-cli.js"
135
+ elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/dist/src/core/ac-test-validator-cli.js" ]; then
136
+ VALIDATOR_SCRIPT="${CLAUDE_PLUGIN_ROOT}/dist/src/core/ac-test-validator-cli.js"
137
+ fi
138
+
139
+ if [ -z "$VALIDATOR_SCRIPT" ] || ! command -v node &> /dev/null; then
140
+ echo "[$(date)] ⚠️ AC test validator not found or Node.js missing" >> "$DEBUG_LOG" 2>/dev/null || true
141
+ rm -f "$STDIN_DATA"
142
+ cat <<EOF
143
+ {
144
+ "continue": true,
145
+ "systemMessage": "⚠️ Warning: AC test validator not available. Task completion validation skipped. Install Node.js and rebuild SpecWeave to enable validation."
146
+ }
147
+ EOF
148
+ exit 0
149
+ fi
150
+
151
+ # Run validator (captures exit code)
152
+ VALIDATION_OUTPUT=$(mktemp)
153
+ VALIDATION_EXIT_CODE=0
154
+
155
+ (cd "$PROJECT_ROOT" && node "$VALIDATOR_SCRIPT" "$CURRENT_INCREMENT") > "$VALIDATION_OUTPUT" 2>&1 || VALIDATION_EXIT_CODE=$?
156
+
157
+ echo "[$(date)] Validator exit code: $VALIDATION_EXIT_CODE" >> "$DEBUG_LOG" 2>/dev/null || true
158
+ cat "$VALIDATION_OUTPUT" >> "$DEBUG_LOG" 2>/dev/null || true
159
+
160
+ rm -f "$STDIN_DATA"
161
+
162
+ # ============================================================================
163
+ # DECISION LOGIC
164
+ # ============================================================================
165
+
166
+ if [ "$VALIDATION_EXIT_CODE" = "0" ]; then
167
+ # Validation passed - allow completion
168
+ echo "[$(date)] ✅ AC test validation passed" >> "$DEBUG_LOG" 2>/dev/null || true
169
+
170
+ VALIDATION_SUMMARY=$(cat "$VALIDATION_OUTPUT" | tail -5 | tr '\n' ' ')
171
+
172
+ rm -f "$VALIDATION_OUTPUT"
173
+
174
+ cat <<EOF
175
+ {
176
+ "continue": true,
177
+ "systemMessage": "✅ AC Test Validation Passed: All acceptance criteria have passing tests. Task completion allowed. ${VALIDATION_SUMMARY}"
178
+ }
179
+ EOF
180
+ else
181
+ # Validation failed - block completion
182
+ echo "[$(date)] ❌ AC test validation failed" >> "$DEBUG_LOG" 2>/dev/null || true
183
+
184
+ VALIDATION_ERROR=$(cat "$VALIDATION_OUTPUT" | grep -A 10 "VALIDATION FAILED" | tr '\n' ' ' | cut -c 1-300)
185
+
186
+ rm -f "$VALIDATION_OUTPUT"
187
+
188
+ cat <<EOF
189
+ {
190
+ "continue": false,
191
+ "systemMessage": "❌ AC TEST VALIDATION FAILED: Cannot mark task as complete until all acceptance criteria have passing tests. ${VALIDATION_ERROR}
192
+
193
+ Fix the failing tests and try again. Run tests manually: npm test"
194
+ }
195
+ EOF
196
+ fi
@@ -1,5 +1,5 @@
1
1
  import { execSync } from "child_process";
2
- import fs from "fs-extra";
2
+ import { existsSync, readFileSync } from "fs";
3
3
  import path from "path";
4
4
  function executeGitCommand(command, cwd) {
5
5
  try {
@@ -65,10 +65,10 @@ function getFileContent(file, cwd) {
65
65
  try {
66
66
  const workingDir = cwd || process.cwd();
67
67
  const absolutePath = path.isAbsolute(file) ? file : path.join(workingDir, file);
68
- if (!fs.existsSync(absolutePath)) {
68
+ if (!existsSync(absolutePath)) {
69
69
  return "";
70
70
  }
71
- return fs.readFileSync(absolutePath, "utf-8");
71
+ return readFileSync(absolutePath, "utf-8");
72
72
  } catch {
73
73
  return "";
74
74
  }
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { execSync } from 'child_process';
11
- import fs from 'fs-extra';
11
+ import { existsSync, readFileSync } from 'fs';
12
12
  import path from 'path';
13
13
  import { GitDiffInfo } from './types/reflection-types';
14
14
 
@@ -136,11 +136,11 @@ export function getFileContent(file: string, cwd?: string): string {
136
136
  const workingDir = cwd || process.cwd();
137
137
  const absolutePath = path.isAbsolute(file) ? file : path.join(workingDir, file);
138
138
 
139
- if (!fs.existsSync(absolutePath)) {
139
+ if (!existsSync(absolutePath)) {
140
140
  return '';
141
141
  }
142
142
 
143
- return fs.readFileSync(absolutePath, 'utf-8');
143
+ return readFileSync(absolutePath, 'utf-8');
144
144
  } catch {
145
145
  return '';
146
146
  }
@@ -1,4 +1,5 @@
1
- import fs from "fs-extra";
1
+ import { existsSync } from "fs";
2
+ import { promises as fs } from "fs";
2
3
  import {
3
4
  detectLanguage,
4
5
  prepareTranslation,
@@ -94,7 +95,7 @@ ${contentToTranslate}`,
94
95
  }
95
96
  }
96
97
  async function translateFile(filePath, targetLang = "en") {
97
- if (!await fs.pathExists(filePath)) {
98
+ if (!existsSync(filePath)) {
98
99
  throw new Error(`File not found: ${filePath}`);
99
100
  }
100
101
  const content = await fs.readFile(filePath, "utf-8");
@@ -8,7 +8,8 @@
8
8
  * @see plugins/specweave/commands/translate.md
9
9
  */
10
10
 
11
- import fs from 'fs-extra';
11
+ import { existsSync } from 'fs';
12
+ import { promises as fs } from 'fs';
12
13
  import path from 'path';
13
14
  import {
14
15
  detectLanguage,
@@ -186,7 +187,7 @@ export async function translateFile(
186
187
  targetLang: SupportedLanguage = 'en'
187
188
  ): Promise<TranslationResult & { filePath: string }> {
188
189
  // Read file
189
- if (!await fs.pathExists(filePath)) {
190
+ if (!existsSync(filePath)) {
190
191
  throw new Error(`File not found: ${filePath}`);
191
192
  }
192
193
 
@@ -1,4 +1,4 @@
1
- import fs from "fs-extra";
1
+ import { mkdirpSync, writeJsonSync } from "../utils/fs-native.js";
2
2
  import path from "path";
3
3
  import { createReflectionContext } from "./run-self-reflection";
4
4
  import { getModifiedFilesSummary } from "./git-diff-analyzer";
@@ -10,7 +10,7 @@ function prepareReflectionContext(incrementId, taskId, projectRoot) {
10
10
  }
11
11
  const rootDir = projectRoot || process.cwd();
12
12
  const tempDir = path.join(rootDir, ".specweave", "increments", incrementId, "logs", "reflections", ".temp");
13
- fs.mkdirpSync(tempDir);
13
+ mkdirpSync(tempDir);
14
14
  const contextFile = path.join(tempDir, "reflection-context.json");
15
15
  const fileStats = getModifiedFilesSummary(context.modifiedFiles);
16
16
  const contextData = {
@@ -30,7 +30,7 @@ function prepareReflectionContext(incrementId, taskId, projectRoot) {
30
30
  config: context.config,
31
31
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
32
32
  };
33
- fs.writeJsonSync(contextFile, contextData, { spaces: 2 });
33
+ writeJsonSync(contextFile, contextData, { spaces: 2 });
34
34
  return contextFile;
35
35
  } catch (error) {
36
36
  console.error(`Failed to prepare reflection context: ${error.message}`);
@@ -7,7 +7,7 @@
7
7
  * @module prepare-reflection-context
8
8
  */
9
9
 
10
- import fs from 'fs-extra';
10
+ import { mkdirpSync, writeJsonSync } from '../utils/fs-native.js';
11
11
  import path from 'path';
12
12
  import { createReflectionContext } from './run-self-reflection';
13
13
  import { getModifiedFilesSummary } from './git-diff-analyzer';
@@ -37,7 +37,7 @@ export function prepareReflectionContext(
37
37
  // Create temp directory for reflection context
38
38
  const rootDir = projectRoot || process.cwd();
39
39
  const tempDir = path.join(rootDir, '.specweave', 'increments', incrementId, 'logs', 'reflections', '.temp');
40
- fs.mkdirpSync(tempDir);
40
+ mkdirpSync(tempDir);
41
41
 
42
42
  // Save context to temp file
43
43
  const contextFile = path.join(tempDir, 'reflection-context.json');
@@ -62,7 +62,7 @@ export function prepareReflectionContext(
62
62
  timestamp: new Date().toISOString()
63
63
  };
64
64
 
65
- fs.writeJsonSync(contextFile, contextData, { spaces: 2 });
65
+ writeJsonSync(contextFile, contextData, { spaces: 2 });
66
66
 
67
67
  return contextFile;
68
68
  } catch (error: any) {
@@ -1,4 +1,4 @@
1
- import fs from "fs-extra";
1
+ import { existsSync, readFileSync } from "fs";
2
2
  import path from "path";
3
3
  import { DEFAULT_REFLECTION_CONFIG } from "./types/reflection-types";
4
4
  function findSpecweaveRoot(startDir = process.cwd()) {
@@ -6,7 +6,7 @@ function findSpecweaveRoot(startDir = process.cwd()) {
6
6
  const root = path.parse(currentDir).root;
7
7
  while (currentDir !== root) {
8
8
  const specweavePath = path.join(currentDir, ".specweave");
9
- if (fs.existsSync(specweavePath)) {
9
+ if (existsSync(specweavePath)) {
10
10
  return currentDir;
11
11
  }
12
12
  currentDir = path.dirname(currentDir);
@@ -19,11 +19,11 @@ function loadReflectionConfig(projectRoot) {
19
19
  return { ...DEFAULT_REFLECTION_CONFIG };
20
20
  }
21
21
  const configPath = path.join(rootDir, ".specweave", "config.json");
22
- if (!fs.existsSync(configPath)) {
22
+ if (!existsSync(configPath)) {
23
23
  return { ...DEFAULT_REFLECTION_CONFIG };
24
24
  }
25
25
  try {
26
- const configContent = fs.readFileSync(configPath, "utf-8");
26
+ const configContent = readFileSync(configPath, "utf-8");
27
27
  const config = JSON.parse(configContent);
28
28
  const userReflectionConfig = config.reflection || {};
29
29
  const mergedConfig = {
@@ -7,7 +7,7 @@
7
7
  * @module reflection-config-loader
8
8
  */
9
9
 
10
- import fs from 'fs-extra';
10
+ import { existsSync, readFileSync } from 'fs';
11
11
  import path from 'path';
12
12
  import { DEFAULT_REFLECTION_CONFIG, ReflectionConfig } from './types/reflection-types';
13
13
 
@@ -22,7 +22,7 @@ export function findSpecweaveRoot(startDir: string = process.cwd()): string | nu
22
22
 
23
23
  while (currentDir !== root) {
24
24
  const specweavePath = path.join(currentDir, '.specweave');
25
- if (fs.existsSync(specweavePath)) {
25
+ if (existsSync(specweavePath)) {
26
26
  return currentDir;
27
27
  }
28
28
  currentDir = path.dirname(currentDir);
@@ -51,13 +51,13 @@ export function loadReflectionConfig(projectRoot?: string): ReflectionConfig {
51
51
  const configPath = path.join(rootDir, '.specweave', 'config.json');
52
52
 
53
53
  // Config file doesn't exist, return defaults
54
- if (!fs.existsSync(configPath)) {
54
+ if (!existsSync(configPath)) {
55
55
  return { ...DEFAULT_REFLECTION_CONFIG };
56
56
  }
57
57
 
58
58
  try {
59
59
  // Read and parse config file
60
- const configContent = fs.readFileSync(configPath, 'utf-8');
60
+ const configContent = readFileSync(configPath, 'utf-8');
61
61
  const config = JSON.parse(configContent);
62
62
 
63
63
  // Extract reflection section (may be undefined)
@@ -1,4 +1,4 @@
1
- import fs from "fs-extra";
1
+ import { mkdirpSync, writeFileSync, existsSync, readdirSync, statSync, readFileSync, removeSync } from "../utils/fs-native.js";
2
2
  import path from "path";
3
3
  import { IssueSeverity } from "./types/reflection-types";
4
4
  function generateReflectionMarkdown(result) {
@@ -183,12 +183,12 @@ function getReflectionFilename(taskId, timestamp) {
183
183
  }
184
184
  function saveReflection(result, incrementId, taskId, projectRoot) {
185
185
  const logDir = getReflectionLogDir(incrementId, projectRoot);
186
- fs.mkdirpSync(logDir);
186
+ mkdirpSync(logDir);
187
187
  const filename = getReflectionFilename(taskId);
188
188
  const filepath = path.join(logDir, filename);
189
189
  const markdown = generateReflectionMarkdown(result);
190
190
  try {
191
- fs.writeFileSync(filepath, markdown, "utf-8");
191
+ writeFileSync(filepath, markdown, "utf-8");
192
192
  return filepath;
193
193
  } catch (error) {
194
194
  throw new Error(`Failed to save reflection: ${error.message}`);
@@ -196,26 +196,26 @@ function saveReflection(result, incrementId, taskId, projectRoot) {
196
196
  }
197
197
  function listReflections(incrementId, projectRoot) {
198
198
  const logDir = getReflectionLogDir(incrementId, projectRoot);
199
- if (!fs.existsSync(logDir)) {
199
+ if (!existsSync(logDir)) {
200
200
  return [];
201
201
  }
202
- const files = fs.readdirSync(logDir).filter((file) => file.endsWith(".md")).map((file) => path.join(logDir, file)).sort((a, b) => {
203
- const statA = fs.statSync(a);
204
- const statB = fs.statSync(b);
202
+ const files = readdirSync(logDir).filter((file) => file.endsWith(".md")).map((file) => path.join(logDir, file)).sort((a, b) => {
203
+ const statA = statSync(a);
204
+ const statB = statSync(b);
205
205
  return statB.mtime.getTime() - statA.mtime.getTime();
206
206
  });
207
207
  return files;
208
208
  }
209
209
  function readReflection(filepath) {
210
210
  try {
211
- return fs.readFileSync(filepath, "utf-8");
211
+ return readFileSync(filepath, "utf-8");
212
212
  } catch (error) {
213
213
  throw new Error(`Failed to read reflection: ${error.message}`);
214
214
  }
215
215
  }
216
216
  function deleteReflection(filepath) {
217
217
  try {
218
- fs.removeSync(filepath);
218
+ removeSync(filepath);
219
219
  } catch (error) {
220
220
  throw new Error(`Failed to delete reflection: ${error.message}`);
221
221
  }
@@ -7,7 +7,7 @@
7
7
  * @module reflection-storage
8
8
  */
9
9
 
10
- import fs from 'fs-extra';
10
+ import { mkdirpSync, writeFileSync, existsSync, readdirSync, statSync, readFileSync, removeSync } from '../utils/fs-native.js';
11
11
  import path from 'path';
12
12
  import { ReflectionResult, IssueSeverity } from './types/reflection-types';
13
13
 
@@ -294,7 +294,7 @@ export function saveReflection(
294
294
  ): string {
295
295
  // Ensure reflection logs directory exists
296
296
  const logDir = getReflectionLogDir(incrementId, projectRoot);
297
- fs.mkdirpSync(logDir);
297
+ mkdirpSync(logDir);
298
298
 
299
299
  // Generate filename
300
300
  const filename = getReflectionFilename(taskId);
@@ -305,7 +305,7 @@ export function saveReflection(
305
305
 
306
306
  // Write to file
307
307
  try {
308
- fs.writeFileSync(filepath, markdown, 'utf-8');
308
+ writeFileSync(filepath, markdown, 'utf-8');
309
309
  return filepath;
310
310
  } catch (error: any) {
311
311
  throw new Error(`Failed to save reflection: ${error.message}`);
@@ -324,17 +324,17 @@ export function listReflections(
324
324
  ): string[] {
325
325
  const logDir = getReflectionLogDir(incrementId, projectRoot);
326
326
 
327
- if (!fs.existsSync(logDir)) {
327
+ if (!existsSync(logDir)) {
328
328
  return [];
329
329
  }
330
330
 
331
- const files = fs.readdirSync(logDir)
331
+ const files = readdirSync(logDir)
332
332
  .filter(file => file.endsWith('.md'))
333
333
  .map(file => path.join(logDir, file))
334
334
  .sort((a, b) => {
335
335
  // Sort by modification time (newest first)
336
- const statA = fs.statSync(a);
337
- const statB = fs.statSync(b);
336
+ const statA = statSync(a);
337
+ const statB = statSync(b);
338
338
  return statB.mtime.getTime() - statA.mtime.getTime();
339
339
  });
340
340
 
@@ -349,7 +349,7 @@ export function listReflections(
349
349
  */
350
350
  export function readReflection(filepath: string): string {
351
351
  try {
352
- return fs.readFileSync(filepath, 'utf-8');
352
+ return readFileSync(filepath, 'utf-8');
353
353
  } catch (error: any) {
354
354
  throw new Error(`Failed to read reflection: ${error.message}`);
355
355
  }
@@ -362,7 +362,7 @@ export function readReflection(filepath: string): string {
362
362
  */
363
363
  export function deleteReflection(filepath: string): void {
364
364
  try {
365
- fs.removeSync(filepath);
365
+ removeSync(filepath);
366
366
  } catch (error: any) {
367
367
  throw new Error(`Failed to delete reflection: ${error.message}`);
368
368
  }
@@ -11,9 +11,10 @@
11
11
  * Part of increment 0047-us-task-linkage (T-012).
12
12
  */
13
13
 
14
- import fs from 'fs-extra';
14
+ import { readFileSync, statSync, existsSync, promises as fs } from 'fs';
15
15
  import path from 'path';
16
16
  import crypto from 'crypto';
17
+ import { mkdirpSync } from '../utils/fs-native.js';
17
18
 
18
19
  /**
19
20
  * In-memory cache with TTL
@@ -97,7 +98,7 @@ const globalCache = new SyncCache();
97
98
  */
98
99
  export function getFileHash(filePath) {
99
100
  try {
100
- const content = fs.readFileSync(filePath, 'utf-8');
101
+ const content = readFileSync(filePath, 'utf-8');
101
102
  return crypto.createHash('sha256').update(content).digest('hex');
102
103
  } catch (error) {
103
104
  return null;
@@ -145,7 +146,7 @@ export function getCachedTasks(tasksPath, parser) {
145
146
  */
146
147
  export function getCachedUSMetadata(usFilePath) {
147
148
  try {
148
- const stats = fs.statSync(usFilePath);
149
+ const stats = statSync(usFilePath);
149
150
  const cacheKey = `us-metadata:${usFilePath}`;
150
151
 
151
152
  const cached = globalCache.get(cacheKey);
@@ -156,7 +157,7 @@ export function getCachedUSMetadata(usFilePath) {
156
157
  }
157
158
 
158
159
  // File changed or not in cache - read and cache
159
- const content = fs.readFileSync(usFilePath, 'utf-8');
160
+ const content = readFileSync(usFilePath, 'utf-8');
160
161
  const metadata = {
161
162
  mtime: stats.mtimeMs,
162
163
  metadata: {
@@ -198,7 +199,7 @@ export async function batchFileUpdates(updates) {
198
199
  // Write files sequentially within same directory (better disk I/O)
199
200
  for (const [dir, fileUpdates] of updatesByDir.entries()) {
200
201
  // Ensure directory exists once per directory
201
- await fs.ensureDir(dir);
202
+ await fs.mkdir(dir, { recursive: true });
202
203
 
203
204
  // Write all files in this directory
204
205
  await Promise.all(
@@ -220,13 +221,13 @@ export async function batchFileUpdates(updates) {
220
221
  export function needsSync(usFilePath, tasks, tasksPath) {
221
222
  try {
222
223
  // Check if US file exists
223
- if (!fs.existsSync(usFilePath)) {
224
+ if (!existsSync(usFilePath)) {
224
225
  return false; // File doesn't exist, can't sync
225
226
  }
226
227
 
227
228
  // Get file modification times
228
- const usStats = fs.statSync(usFilePath);
229
- const tasksStats = fs.statSync(tasksPath);
229
+ const usStats = statSync(usFilePath);
230
+ const tasksStats = statSync(tasksPath);
230
231
 
231
232
  // If tasks.md is newer than US file, sync is needed
232
233
  if (tasksStats.mtimeMs > usStats.mtimeMs) {