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.
- package/CLAUDE.md +367 -0
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts +133 -0
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/utils/fs-native.js +224 -0
- package/dist/plugins/specweave/lib/utils/fs-native.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +52 -20
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +24 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js +2 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js.map +1 -1
- package/dist/src/core/ac-test-validator-cli.d.ts +16 -0
- package/dist/src/core/ac-test-validator-cli.d.ts.map +1 -0
- package/dist/src/core/ac-test-validator-cli.js +118 -0
- package/dist/src/core/ac-test-validator-cli.js.map +1 -0
- package/dist/src/core/ac-test-validator.d.ts +111 -0
- package/dist/src/core/ac-test-validator.d.ts.map +1 -0
- package/dist/src/core/ac-test-validator.js +292 -0
- package/dist/src/core/ac-test-validator.js.map +1 -0
- package/dist/src/core/increment/desync-detector.d.ts +142 -0
- package/dist/src/core/increment/desync-detector.d.ts.map +1 -0
- package/dist/src/core/increment/desync-detector.js +270 -0
- package/dist/src/core/increment/desync-detector.js.map +1 -0
- package/dist/src/core/increment/metadata-manager.d.ts +8 -4
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +45 -21
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/qa/qa-runner.js +9 -2
- package/dist/src/core/qa/qa-runner.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts +1 -1
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +40 -2
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/fs-native.d.ts +133 -0
- package/dist/src/utils/fs-native.d.ts.map +1 -0
- package/dist/src/utils/fs-native.js +224 -0
- package/dist/src/utils/fs-native.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +12 -0
- package/plugins/specweave/agents/AGENTS-INDEX.md +216 -0
- package/plugins/specweave/agents/architect/AGENT.md +17 -0
- package/plugins/specweave/agents/code-standards-detective/AGENT.md +16 -0
- package/plugins/specweave/agents/docs-writer/AGENT.md +16 -0
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +704 -0
- package/plugins/specweave/agents/infrastructure/AGENT.md +16 -0
- package/plugins/specweave/agents/performance/AGENT.md +16 -0
- package/plugins/specweave/agents/pm/AGENT.md +17 -0
- package/plugins/specweave/agents/qa-lead/AGENT.md +15 -0
- package/plugins/specweave/agents/reflective-reviewer/AGENT.md +16 -0
- package/plugins/specweave/agents/security/AGENT.md +16 -0
- package/plugins/specweave/agents/tdd-orchestrator/AGENT.md +16 -0
- package/plugins/specweave/agents/tech-lead/AGENT.md +16 -0
- package/plugins/specweave/agents/test-aware-planner/AGENT.md +16 -0
- package/plugins/specweave/agents/translator/AGENT.md +13 -0
- package/plugins/specweave/commands/specweave-done.md +14 -0
- package/plugins/specweave/commands/specweave-qa.md +11 -1
- package/plugins/specweave/commands/specweave-sync-status.md +356 -0
- package/plugins/specweave/commands/specweave-validate.md +10 -1
- package/plugins/specweave/hooks/pre-task-completion.sh +196 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.js +3 -3
- package/plugins/specweave/lib/hooks/git-diff-analyzer.ts +3 -3
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js +3 -2
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +3 -2
- package/plugins/specweave/lib/hooks/prepare-reflection-context.js +3 -3
- package/plugins/specweave/lib/hooks/prepare-reflection-context.ts +3 -3
- package/plugins/specweave/lib/hooks/reflection-config-loader.js +4 -4
- package/plugins/specweave/lib/hooks/reflection-config-loader.ts +4 -4
- package/plugins/specweave/lib/hooks/reflection-storage.js +9 -9
- package/plugins/specweave/lib/hooks/reflection-storage.ts +9 -9
- package/plugins/specweave/lib/hooks/sync-cache.js +9 -8
- package/plugins/specweave/lib/hooks/sync-living-docs.js +57 -6
- package/plugins/specweave/lib/hooks/sync-us-tasks.js +6 -6
- package/plugins/specweave/lib/hooks/translate-file.js +3 -2
- package/plugins/specweave/lib/hooks/translate-file.ts +3 -2
- package/plugins/specweave/lib/hooks/translate-living-docs.js +4 -3
- package/plugins/specweave/lib/hooks/translate-living-docs.ts +4 -3
- package/plugins/specweave/lib/hooks/update-tasks-md.js +3 -3
- package/plugins/specweave/lib/hooks/update-tasks-md.ts +3 -3
- package/plugins/specweave/lib/utils/fs-native.js +182 -0
- package/plugins/specweave/lib/utils/fs-native.ts +283 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +8 -4
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +45 -21
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/skills/SKILLS-INDEX.md +26 -2
- package/plugins/specweave/skills/increment-planner/SKILL.md +2 -2
- package/plugins/specweave-ado/commands/specweave-ado-close-workitem.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-create-workitem.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-status.md +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-sync.md +1 -1
- package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +1 -1
- package/plugins/specweave-diagrams/skills/diagrams-generator/SKILL.md +4 -4
- package/plugins/specweave-github/lib/github-client-v2.js +2 -1
- package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
- package/plugins/specweave-github/lib/github-feature-sync.js +30 -17
- package/plugins/specweave-github/lib/github-feature-sync.ts +54 -24
- package/plugins/specweave-github/lib/user-story-issue-builder.js +24 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.ts +33 -0
- package/plugins/specweave-mobile/README.md +1 -1
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +72 -0
- package/src/templates/CLAUDE.md.template +13 -0
- 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
|
|
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 (!
|
|
68
|
+
if (!existsSync(absolutePath)) {
|
|
69
69
|
return "";
|
|
70
70
|
}
|
|
71
|
-
return
|
|
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
|
|
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 (!
|
|
139
|
+
if (!existsSync(absolutePath)) {
|
|
140
140
|
return '';
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
return
|
|
143
|
+
return readFileSync(absolutePath, 'utf-8');
|
|
144
144
|
} catch {
|
|
145
145
|
return '';
|
|
146
146
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
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 (!
|
|
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
|
|
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 (!
|
|
190
|
+
if (!existsSync(filePath)) {
|
|
190
191
|
throw new Error(`File not found: ${filePath}`);
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
65
|
+
writeJsonSync(contextFile, contextData, { spaces: 2 });
|
|
66
66
|
|
|
67
67
|
return contextFile;
|
|
68
68
|
} catch (error: any) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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 (
|
|
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 (!
|
|
22
|
+
if (!existsSync(configPath)) {
|
|
23
23
|
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
|
-
const configContent =
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
199
|
+
if (!existsSync(logDir)) {
|
|
200
200
|
return [];
|
|
201
201
|
}
|
|
202
|
-
const files =
|
|
203
|
-
const statA =
|
|
204
|
-
const statB =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
327
|
+
if (!existsSync(logDir)) {
|
|
328
328
|
return [];
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
const files =
|
|
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 =
|
|
337
|
-
const statB =
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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.
|
|
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 (!
|
|
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 =
|
|
229
|
-
const tasksStats =
|
|
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) {
|