specweave 0.23.0 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +0 -88
- package/CLAUDE.md +100 -0
- package/bin/fix-marketplace-errors.sh +8 -8
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +5 -17
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/core/repo-structure/repo-id-generator.d.ts +20 -0
- package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-id-generator.js +44 -0
- package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +5 -2
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave/commands/specweave-archive.md +51 -15
- package/plugins/specweave/hooks/post-edit-spec.sh +62 -9
- package/plugins/specweave/hooks/post-metadata-change.sh +160 -0
- package/plugins/specweave/hooks/post-write-spec.sh +62 -8
- package/plugins/specweave/lib/hooks/auto-transition.js.bak +50 -0
- package/plugins/specweave/lib/hooks/auto-transition.ts.bak +84 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +89 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +142 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +269 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +60 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +155 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +264 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +42 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +110 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +178 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +45 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +92 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +156 -0
- package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +33 -0
- package/plugins/specweave/lib/hooks/reflection-parser.js.bak +301 -0
- package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +484 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +56 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +182 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +306 -0
- package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +64 -0
- package/plugins/specweave/lib/hooks/reflection-storage.js.bak +231 -0
- package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +369 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +43 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +132 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +258 -0
- package/plugins/specweave/lib/hooks/sync-cache.js.bak +294 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +1 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +27 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +339 -0
- package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +476 -0
- package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +59 -0
- package/plugins/specweave/lib/hooks/translate-file.js.bak +289 -0
- package/plugins/specweave/lib/hooks/translate-file.ts.bak +428 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +13 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +119 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +224 -0
- package/plugins/specweave/lib/hooks/update-ac-status.js.bak +51 -0
- package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +103 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +1 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +29 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +296 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +489 -0
- package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +6225 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prepare Reflection Context
|
|
3
|
+
*
|
|
4
|
+
* Prepares context for reflection and saves to temp file
|
|
5
|
+
* Used by post-task-completion hook to prepare for reflection invocation
|
|
6
|
+
*
|
|
7
|
+
* @module prepare-reflection-context
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { createReflectionContext } from './run-self-reflection';
|
|
13
|
+
import { getModifiedFilesSummary } from './git-diff-analyzer';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Prepare reflection context and save to temp file
|
|
17
|
+
* This allows the hook to prepare data without actually invoking the agent
|
|
18
|
+
*
|
|
19
|
+
* @param incrementId Increment identifier
|
|
20
|
+
* @param taskId Task identifier
|
|
21
|
+
* @param projectRoot Project root directory (optional)
|
|
22
|
+
* @returns Path to saved context file or null if reflection should be skipped
|
|
23
|
+
*/
|
|
24
|
+
export function prepareReflectionContext(
|
|
25
|
+
incrementId: string,
|
|
26
|
+
taskId: string,
|
|
27
|
+
projectRoot?: string
|
|
28
|
+
): string | null {
|
|
29
|
+
try {
|
|
30
|
+
const context = createReflectionContext(incrementId, taskId, projectRoot);
|
|
31
|
+
|
|
32
|
+
// Check if reflection should run
|
|
33
|
+
if (!context.config.enabled || context.modifiedFiles.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create temp directory for reflection context
|
|
38
|
+
const rootDir = projectRoot || process.cwd();
|
|
39
|
+
const tempDir = path.join(rootDir, '.specweave', 'increments', incrementId, 'logs', 'reflections', '.temp');
|
|
40
|
+
fs.mkdirpSync(tempDir);
|
|
41
|
+
|
|
42
|
+
// Save context to temp file
|
|
43
|
+
const contextFile = path.join(tempDir, 'reflection-context.json');
|
|
44
|
+
|
|
45
|
+
const fileStats = getModifiedFilesSummary(context.modifiedFiles);
|
|
46
|
+
|
|
47
|
+
const contextData = {
|
|
48
|
+
incrementId: context.incrementId,
|
|
49
|
+
taskId: context.taskId,
|
|
50
|
+
modifiedFiles: context.modifiedFiles.map(f => ({
|
|
51
|
+
file: f.file,
|
|
52
|
+
linesAdded: f.linesAdded,
|
|
53
|
+
linesRemoved: f.linesRemoved
|
|
54
|
+
// Exclude diff content to save space
|
|
55
|
+
})),
|
|
56
|
+
fileSummary: {
|
|
57
|
+
count: fileStats.count,
|
|
58
|
+
linesAdded: fileStats.linesAdded,
|
|
59
|
+
linesRemoved: fileStats.linesRemoved
|
|
60
|
+
},
|
|
61
|
+
config: context.config,
|
|
62
|
+
timestamp: new Date().toISOString()
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
fs.writeJsonSync(contextFile, contextData, { spaces: 2 });
|
|
66
|
+
|
|
67
|
+
return contextFile;
|
|
68
|
+
} catch (error: any) {
|
|
69
|
+
console.error(`Failed to prepare reflection context: ${error.message}`);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if reflection context exists for an increment
|
|
76
|
+
*
|
|
77
|
+
* @param incrementId Increment identifier
|
|
78
|
+
* @param projectRoot Project root directory (optional)
|
|
79
|
+
* @returns True if context file exists
|
|
80
|
+
*/
|
|
81
|
+
export function hasReflectionContext(
|
|
82
|
+
incrementId: string,
|
|
83
|
+
projectRoot?: string
|
|
84
|
+
): boolean {
|
|
85
|
+
const rootDir = projectRoot || process.cwd();
|
|
86
|
+
const contextFile = path.join(
|
|
87
|
+
rootDir,
|
|
88
|
+
'.specweave',
|
|
89
|
+
'increments',
|
|
90
|
+
incrementId,
|
|
91
|
+
'logs',
|
|
92
|
+
'reflections',
|
|
93
|
+
'.temp',
|
|
94
|
+
'reflection-context.json'
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return fs.existsSync(contextFile);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Read reflection context from file
|
|
102
|
+
*
|
|
103
|
+
* @param incrementId Increment identifier
|
|
104
|
+
* @param projectRoot Project root directory (optional)
|
|
105
|
+
* @returns Context data or null if not found
|
|
106
|
+
*/
|
|
107
|
+
export function readReflectionContext(
|
|
108
|
+
incrementId: string,
|
|
109
|
+
projectRoot?: string
|
|
110
|
+
): any | null {
|
|
111
|
+
const rootDir = projectRoot || process.cwd();
|
|
112
|
+
const contextFile = path.join(
|
|
113
|
+
rootDir,
|
|
114
|
+
'.specweave',
|
|
115
|
+
'increments',
|
|
116
|
+
incrementId,
|
|
117
|
+
'logs',
|
|
118
|
+
'reflections',
|
|
119
|
+
'.temp',
|
|
120
|
+
'reflection-context.json'
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!fs.existsSync(contextFile)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
return fs.readJsonSync(contextFile);
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Clear reflection context after reflection completes
|
|
136
|
+
*
|
|
137
|
+
* @param incrementId Increment identifier
|
|
138
|
+
* @param projectRoot Project root directory (optional)
|
|
139
|
+
*/
|
|
140
|
+
export function clearReflectionContext(
|
|
141
|
+
incrementId: string,
|
|
142
|
+
projectRoot?: string
|
|
143
|
+
): void {
|
|
144
|
+
const rootDir = projectRoot || process.cwd();
|
|
145
|
+
const tempDir = path.join(
|
|
146
|
+
rootDir,
|
|
147
|
+
'.specweave',
|
|
148
|
+
'increments',
|
|
149
|
+
incrementId,
|
|
150
|
+
'logs',
|
|
151
|
+
'reflections',
|
|
152
|
+
'.temp'
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (fs.existsSync(tempDir)) {
|
|
156
|
+
fs.removeSync(tempDir);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// CLI entry point for hook integration
|
|
161
|
+
if (require.main === module) {
|
|
162
|
+
const incrementId = process.argv[2];
|
|
163
|
+
const taskId = process.argv[3];
|
|
164
|
+
|
|
165
|
+
if (!incrementId || !taskId) {
|
|
166
|
+
console.error('Usage: node prepare-reflection-context.js <increment-id> <task-id>');
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const contextFile = prepareReflectionContext(incrementId, taskId);
|
|
171
|
+
|
|
172
|
+
if (contextFile) {
|
|
173
|
+
console.log(`Reflection context prepared: ${contextFile}`);
|
|
174
|
+
console.log('✨ Reflection ready. Run /specweave:reflect to analyze your work.');
|
|
175
|
+
} else {
|
|
176
|
+
console.log('Reflection skipped (disabled or no changes).');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reflection Configuration Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads and validates reflection configuration from .specweave/config.json
|
|
5
|
+
* Merges user config with defaults, validates against schema
|
|
6
|
+
*
|
|
7
|
+
* @module reflection-config-loader
|
|
8
|
+
*/
|
|
9
|
+
import { ReflectionConfig } from './types/reflection-types';
|
|
10
|
+
/**
|
|
11
|
+
* Find .specweave directory by traversing up from current directory
|
|
12
|
+
* @param startDir Starting directory (defaults to cwd)
|
|
13
|
+
* @returns Path to .specweave directory or null if not found
|
|
14
|
+
*/
|
|
15
|
+
export declare function findSpecweaveRoot(startDir?: string): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Load reflection configuration from .specweave/config.json
|
|
18
|
+
* Falls back to defaults if config file doesn't exist or reflection section is missing
|
|
19
|
+
*
|
|
20
|
+
* @param projectRoot Path to project root (optional, auto-detected if not provided)
|
|
21
|
+
* @returns Merged reflection configuration
|
|
22
|
+
* @throws Error if config file exists but has invalid JSON
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadReflectionConfig(projectRoot?: string): ReflectionConfig;
|
|
25
|
+
/**
|
|
26
|
+
* Validate reflection configuration against constraints
|
|
27
|
+
* Checks for logical inconsistencies (e.g., all categories disabled)
|
|
28
|
+
*
|
|
29
|
+
* @param config Reflection configuration to validate
|
|
30
|
+
* @returns Validation result with errors array
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateReflectionConfig(config: ReflectionConfig): {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
errors: string[];
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Load and validate reflection configuration
|
|
38
|
+
* Throws error if configuration is invalid
|
|
39
|
+
*
|
|
40
|
+
* @param projectRoot Path to project root (optional, auto-detected if not provided)
|
|
41
|
+
* @returns Valid reflection configuration
|
|
42
|
+
* @throws Error if configuration is invalid
|
|
43
|
+
*/
|
|
44
|
+
export declare function loadAndValidateReflectionConfig(projectRoot?: string): ReflectionConfig;
|
|
45
|
+
//# sourceMappingURL=reflection-config-loader.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { DEFAULT_REFLECTION_CONFIG } from "./types/reflection-types";
|
|
4
|
+
function findSpecweaveRoot(startDir = process.cwd()) {
|
|
5
|
+
let currentDir = startDir;
|
|
6
|
+
const root = path.parse(currentDir).root;
|
|
7
|
+
while (currentDir !== root) {
|
|
8
|
+
const specweavePath = path.join(currentDir, ".specweave");
|
|
9
|
+
if (fs.existsSync(specweavePath)) {
|
|
10
|
+
return currentDir;
|
|
11
|
+
}
|
|
12
|
+
currentDir = path.dirname(currentDir);
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
function loadReflectionConfig(projectRoot) {
|
|
17
|
+
const rootDir = projectRoot || findSpecweaveRoot();
|
|
18
|
+
if (!rootDir) {
|
|
19
|
+
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
20
|
+
}
|
|
21
|
+
const configPath = path.join(rootDir, ".specweave", "config.json");
|
|
22
|
+
if (!fs.existsSync(configPath)) {
|
|
23
|
+
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const configContent = fs.readFileSync(configPath, "utf-8");
|
|
27
|
+
const config = JSON.parse(configContent);
|
|
28
|
+
const userReflectionConfig = config.reflection || {};
|
|
29
|
+
const mergedConfig = {
|
|
30
|
+
enabled: userReflectionConfig.enabled ?? DEFAULT_REFLECTION_CONFIG.enabled,
|
|
31
|
+
mode: userReflectionConfig.mode ?? DEFAULT_REFLECTION_CONFIG.mode,
|
|
32
|
+
depth: userReflectionConfig.depth ?? DEFAULT_REFLECTION_CONFIG.depth,
|
|
33
|
+
model: userReflectionConfig.model ?? DEFAULT_REFLECTION_CONFIG.model,
|
|
34
|
+
categories: {
|
|
35
|
+
security: userReflectionConfig.categories?.security ?? DEFAULT_REFLECTION_CONFIG.categories.security,
|
|
36
|
+
quality: userReflectionConfig.categories?.quality ?? DEFAULT_REFLECTION_CONFIG.categories.quality,
|
|
37
|
+
testing: userReflectionConfig.categories?.testing ?? DEFAULT_REFLECTION_CONFIG.categories.testing,
|
|
38
|
+
performance: userReflectionConfig.categories?.performance ?? DEFAULT_REFLECTION_CONFIG.categories.performance,
|
|
39
|
+
technicalDebt: userReflectionConfig.categories?.technicalDebt ?? DEFAULT_REFLECTION_CONFIG.categories.technicalDebt
|
|
40
|
+
},
|
|
41
|
+
criticalThreshold: userReflectionConfig.criticalThreshold ?? DEFAULT_REFLECTION_CONFIG.criticalThreshold,
|
|
42
|
+
storeReflections: userReflectionConfig.storeReflections ?? DEFAULT_REFLECTION_CONFIG.storeReflections,
|
|
43
|
+
autoCreateFollowUpTasks: userReflectionConfig.autoCreateFollowUpTasks ?? DEFAULT_REFLECTION_CONFIG.autoCreateFollowUpTasks,
|
|
44
|
+
soundNotifications: userReflectionConfig.soundNotifications ?? DEFAULT_REFLECTION_CONFIG.soundNotifications
|
|
45
|
+
};
|
|
46
|
+
return mergedConfig;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (error instanceof SyntaxError) {
|
|
49
|
+
throw new Error(`Invalid JSON in config file: ${configPath}. ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function validateReflectionConfig(config) {
|
|
55
|
+
const errors = [];
|
|
56
|
+
if (config.enabled && config.mode !== "disabled") {
|
|
57
|
+
const hasEnabledCategory = Object.values(config.categories).some((enabled) => enabled);
|
|
58
|
+
if (!hasEnabledCategory) {
|
|
59
|
+
errors.push("At least one analysis category must be enabled when reflection is active");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!config.enabled && config.mode === "auto") {
|
|
63
|
+
errors.push('Reflection mode cannot be "auto" when reflection is disabled');
|
|
64
|
+
}
|
|
65
|
+
if (config.soundNotifications && !config.enabled) {
|
|
66
|
+
errors.push("Sound notifications require reflection to be enabled");
|
|
67
|
+
}
|
|
68
|
+
if (config.autoCreateFollowUpTasks && !config.storeReflections) {
|
|
69
|
+
errors.push("Auto-create follow-up tasks requires storeReflections to be enabled");
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
valid: errors.length === 0,
|
|
73
|
+
errors
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function loadAndValidateReflectionConfig(projectRoot) {
|
|
77
|
+
const config = loadReflectionConfig(projectRoot);
|
|
78
|
+
const validation = validateReflectionConfig(config);
|
|
79
|
+
if (!validation.valid) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Invalid reflection configuration:
|
|
82
|
+
${validation.errors.map((e) => ` - ${e}`).join("\n")}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
findSpecweaveRoot,
|
|
89
|
+
loadAndValidateReflectionConfig,
|
|
90
|
+
loadReflectionConfig,
|
|
91
|
+
validateReflectionConfig
|
|
92
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reflection Configuration Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads and validates reflection configuration from .specweave/config.json
|
|
5
|
+
* Merges user config with defaults, validates against schema
|
|
6
|
+
*
|
|
7
|
+
* @module reflection-config-loader
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { DEFAULT_REFLECTION_CONFIG, ReflectionConfig } from './types/reflection-types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Find .specweave directory by traversing up from current directory
|
|
16
|
+
* @param startDir Starting directory (defaults to cwd)
|
|
17
|
+
* @returns Path to .specweave directory or null if not found
|
|
18
|
+
*/
|
|
19
|
+
export function findSpecweaveRoot(startDir: string = process.cwd()): string | null {
|
|
20
|
+
let currentDir = startDir;
|
|
21
|
+
const root = path.parse(currentDir).root;
|
|
22
|
+
|
|
23
|
+
while (currentDir !== root) {
|
|
24
|
+
const specweavePath = path.join(currentDir, '.specweave');
|
|
25
|
+
if (fs.existsSync(specweavePath)) {
|
|
26
|
+
return currentDir;
|
|
27
|
+
}
|
|
28
|
+
currentDir = path.dirname(currentDir);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load reflection configuration from .specweave/config.json
|
|
36
|
+
* Falls back to defaults if config file doesn't exist or reflection section is missing
|
|
37
|
+
*
|
|
38
|
+
* @param projectRoot Path to project root (optional, auto-detected if not provided)
|
|
39
|
+
* @returns Merged reflection configuration
|
|
40
|
+
* @throws Error if config file exists but has invalid JSON
|
|
41
|
+
*/
|
|
42
|
+
export function loadReflectionConfig(projectRoot?: string): ReflectionConfig {
|
|
43
|
+
// Auto-detect project root if not provided
|
|
44
|
+
const rootDir = projectRoot || findSpecweaveRoot();
|
|
45
|
+
|
|
46
|
+
if (!rootDir) {
|
|
47
|
+
// No .specweave directory found, return defaults
|
|
48
|
+
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const configPath = path.join(rootDir, '.specweave', 'config.json');
|
|
52
|
+
|
|
53
|
+
// Config file doesn't exist, return defaults
|
|
54
|
+
if (!fs.existsSync(configPath)) {
|
|
55
|
+
return { ...DEFAULT_REFLECTION_CONFIG };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Read and parse config file
|
|
60
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
61
|
+
const config = JSON.parse(configContent);
|
|
62
|
+
|
|
63
|
+
// Extract reflection section (may be undefined)
|
|
64
|
+
const userReflectionConfig = config.reflection || {};
|
|
65
|
+
|
|
66
|
+
// Deep merge with defaults (user config overrides defaults)
|
|
67
|
+
const mergedConfig: ReflectionConfig = {
|
|
68
|
+
enabled: userReflectionConfig.enabled ?? DEFAULT_REFLECTION_CONFIG.enabled,
|
|
69
|
+
mode: userReflectionConfig.mode ?? DEFAULT_REFLECTION_CONFIG.mode,
|
|
70
|
+
depth: userReflectionConfig.depth ?? DEFAULT_REFLECTION_CONFIG.depth,
|
|
71
|
+
model: userReflectionConfig.model ?? DEFAULT_REFLECTION_CONFIG.model,
|
|
72
|
+
categories: {
|
|
73
|
+
security: userReflectionConfig.categories?.security ?? DEFAULT_REFLECTION_CONFIG.categories.security,
|
|
74
|
+
quality: userReflectionConfig.categories?.quality ?? DEFAULT_REFLECTION_CONFIG.categories.quality,
|
|
75
|
+
testing: userReflectionConfig.categories?.testing ?? DEFAULT_REFLECTION_CONFIG.categories.testing,
|
|
76
|
+
performance: userReflectionConfig.categories?.performance ?? DEFAULT_REFLECTION_CONFIG.categories.performance,
|
|
77
|
+
technicalDebt: userReflectionConfig.categories?.technicalDebt ?? DEFAULT_REFLECTION_CONFIG.categories.technicalDebt,
|
|
78
|
+
},
|
|
79
|
+
criticalThreshold: userReflectionConfig.criticalThreshold ?? DEFAULT_REFLECTION_CONFIG.criticalThreshold,
|
|
80
|
+
storeReflections: userReflectionConfig.storeReflections ?? DEFAULT_REFLECTION_CONFIG.storeReflections,
|
|
81
|
+
autoCreateFollowUpTasks: userReflectionConfig.autoCreateFollowUpTasks ?? DEFAULT_REFLECTION_CONFIG.autoCreateFollowUpTasks,
|
|
82
|
+
soundNotifications: userReflectionConfig.soundNotifications ?? DEFAULT_REFLECTION_CONFIG.soundNotifications,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return mergedConfig;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Invalid JSON or other error
|
|
88
|
+
if (error instanceof SyntaxError) {
|
|
89
|
+
throw new Error(`Invalid JSON in config file: ${configPath}. ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Validate reflection configuration against constraints
|
|
97
|
+
* Checks for logical inconsistencies (e.g., all categories disabled)
|
|
98
|
+
*
|
|
99
|
+
* @param config Reflection configuration to validate
|
|
100
|
+
* @returns Validation result with errors array
|
|
101
|
+
*/
|
|
102
|
+
export function validateReflectionConfig(config: ReflectionConfig): {
|
|
103
|
+
valid: boolean;
|
|
104
|
+
errors: string[];
|
|
105
|
+
} {
|
|
106
|
+
const errors: string[] = [];
|
|
107
|
+
|
|
108
|
+
// Check: If reflection enabled, at least one category must be enabled
|
|
109
|
+
if (config.enabled && config.mode !== 'disabled') {
|
|
110
|
+
const hasEnabledCategory = Object.values(config.categories).some(enabled => enabled);
|
|
111
|
+
if (!hasEnabledCategory) {
|
|
112
|
+
errors.push('At least one analysis category must be enabled when reflection is active');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check: Mode cannot be 'auto' if reflection is disabled
|
|
117
|
+
if (!config.enabled && config.mode === 'auto') {
|
|
118
|
+
errors.push('Reflection mode cannot be "auto" when reflection is disabled');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check: Sound notifications require reflection to be enabled
|
|
122
|
+
if (config.soundNotifications && !config.enabled) {
|
|
123
|
+
errors.push('Sound notifications require reflection to be enabled');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check: Auto-create follow-up tasks requires storeReflections
|
|
127
|
+
if (config.autoCreateFollowUpTasks && !config.storeReflections) {
|
|
128
|
+
errors.push('Auto-create follow-up tasks requires storeReflections to be enabled');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
valid: errors.length === 0,
|
|
133
|
+
errors
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Load and validate reflection configuration
|
|
139
|
+
* Throws error if configuration is invalid
|
|
140
|
+
*
|
|
141
|
+
* @param projectRoot Path to project root (optional, auto-detected if not provided)
|
|
142
|
+
* @returns Valid reflection configuration
|
|
143
|
+
* @throws Error if configuration is invalid
|
|
144
|
+
*/
|
|
145
|
+
export function loadAndValidateReflectionConfig(projectRoot?: string): ReflectionConfig {
|
|
146
|
+
const config = loadReflectionConfig(projectRoot);
|
|
147
|
+
const validation = validateReflectionConfig(config);
|
|
148
|
+
|
|
149
|
+
if (!validation.valid) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Invalid reflection configuration:\n${validation.errors.map(e => ` - ${e}`).join('\n')}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return config;
|
|
156
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reflection Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses markdown output from reflective-reviewer agent
|
|
5
|
+
* Extracts structured data (issues, metrics, lessons learned, etc.)
|
|
6
|
+
*
|
|
7
|
+
* @module reflection-parser
|
|
8
|
+
*/
|
|
9
|
+
import { ReflectionResult, ReflectionModel } from './types/reflection-types';
|
|
10
|
+
/**
|
|
11
|
+
* Parse complete reflection result from markdown
|
|
12
|
+
* Main parsing function
|
|
13
|
+
*
|
|
14
|
+
* @param markdown Reflection markdown from reflective-reviewer agent
|
|
15
|
+
* @param taskName Task name for result
|
|
16
|
+
* @param model Model used for reflection
|
|
17
|
+
* @param reflectionTime Time taken in seconds
|
|
18
|
+
* @param estimatedCost Estimated cost in USD
|
|
19
|
+
* @returns Complete ReflectionResult object
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseReflectionMarkdown(markdown: string, taskName: string, model?: ReflectionModel, reflectionTime?: number, estimatedCost?: number): ReflectionResult;
|
|
22
|
+
/**
|
|
23
|
+
* Validate parsed reflection result
|
|
24
|
+
* Checks for required fields and data quality
|
|
25
|
+
*
|
|
26
|
+
* @param result Parsed reflection result
|
|
27
|
+
* @returns Validation result with errors
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateReflectionResult(result: ReflectionResult): {
|
|
30
|
+
valid: boolean;
|
|
31
|
+
errors: string[];
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=reflection-parser.d.ts.map
|