specweave 0.24.8 → 0.24.9

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 (130) hide show
  1. package/dist/src/cli/commands/init.d.ts.map +1 -1
  2. package/dist/src/cli/commands/init.js +3 -1
  3. package/dist/src/cli/commands/init.js.map +1 -1
  4. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  5. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +18 -2
  6. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  7. package/dist/src/core/repo-structure/git-error-handler.d.ts +1 -1
  8. package/dist/src/core/repo-structure/git-error-handler.d.ts.map +1 -1
  9. package/dist/src/core/repo-structure/git-provider.d.ts +1 -1
  10. package/dist/src/core/repo-structure/git-provider.d.ts.map +1 -1
  11. package/dist/src/core/repo-structure/platform-registry.d.ts.map +1 -1
  12. package/dist/src/core/repo-structure/platform-registry.js +20 -9
  13. package/dist/src/core/repo-structure/platform-registry.js.map +1 -1
  14. package/dist/src/core/repo-structure/prompt-consolidator.d.ts +13 -1
  15. package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
  16. package/dist/src/core/repo-structure/prompt-consolidator.js +38 -5
  17. package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
  18. package/dist/src/core/repo-structure/providers/azure-devops-provider.d.ts +64 -0
  19. package/dist/src/core/repo-structure/providers/azure-devops-provider.d.ts.map +1 -0
  20. package/dist/src/core/repo-structure/providers/azure-devops-provider.js +263 -0
  21. package/dist/src/core/repo-structure/providers/azure-devops-provider.js.map +1 -0
  22. package/dist/src/core/repo-structure/providers/bitbucket-provider.d.ts +12 -11
  23. package/dist/src/core/repo-structure/providers/bitbucket-provider.d.ts.map +1 -1
  24. package/dist/src/core/repo-structure/providers/bitbucket-provider.js +164 -30
  25. package/dist/src/core/repo-structure/providers/bitbucket-provider.js.map +1 -1
  26. package/dist/src/core/repo-structure/providers/gitlab-provider.d.ts +10 -9
  27. package/dist/src/core/repo-structure/providers/gitlab-provider.d.ts.map +1 -1
  28. package/dist/src/core/repo-structure/providers/gitlab-provider.js +182 -28
  29. package/dist/src/core/repo-structure/providers/gitlab-provider.js.map +1 -1
  30. package/dist/src/core/repo-structure/providers/index.d.ts +3 -1
  31. package/dist/src/core/repo-structure/providers/index.d.ts.map +1 -1
  32. package/dist/src/core/repo-structure/providers/index.js +10 -2
  33. package/dist/src/core/repo-structure/providers/index.js.map +1 -1
  34. package/dist/src/core/repo-structure/providers/local-provider.d.ts +61 -0
  35. package/dist/src/core/repo-structure/providers/local-provider.d.ts.map +1 -0
  36. package/dist/src/core/repo-structure/providers/local-provider.js +148 -0
  37. package/dist/src/core/repo-structure/providers/local-provider.js.map +1 -0
  38. package/dist/src/core/repo-structure/repo-structure-manager.d.ts +11 -1
  39. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  40. package/dist/src/core/repo-structure/repo-structure-manager.js +268 -84
  41. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  42. package/package.json +1 -1
  43. package/plugins/specweave/agents/pm/AGENT.md.bak +1893 -0
  44. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  45. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  46. package/plugins/specweave/hooks/lib/migrate-increment-work.sh.bak +245 -0
  47. package/plugins/specweave/hooks/lib/sync-spec-content.sh.bak +149 -0
  48. package/plugins/specweave/hooks/lib/validate-spec-status.sh.bak +163 -0
  49. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  50. package/plugins/specweave/hooks/post-first-increment.sh.bak +61 -0
  51. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  52. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  53. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  54. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  55. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  56. package/plugins/specweave/hooks/post-spec-update.sh.bak +158 -0
  57. package/plugins/specweave/hooks/post-task-completion.sh +69 -175
  58. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  59. package/plugins/specweave/hooks/post-user-story-complete.sh.bak +179 -0
  60. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  61. package/plugins/specweave/hooks/pre-command-deduplication.sh.bak +83 -0
  62. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  63. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  64. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  65. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  66. package/plugins/specweave/hooks/user-prompt-submit.sh.bak +386 -0
  67. package/plugins/specweave/lib/hooks/auto-transition.js.bak +50 -0
  68. package/plugins/specweave/lib/hooks/auto-transition.ts.bak +84 -0
  69. package/plugins/specweave/lib/hooks/consolidated-sync.js +183 -0
  70. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
  71. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +89 -0
  72. package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +142 -0
  73. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +269 -0
  74. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
  75. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +60 -0
  76. package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +155 -0
  77. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +264 -0
  78. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
  79. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +42 -0
  80. package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +110 -0
  81. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +178 -0
  82. package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
  83. package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +45 -0
  84. package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +92 -0
  85. package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +156 -0
  86. package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
  87. package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +33 -0
  88. package/plugins/specweave/lib/hooks/reflection-parser.js.bak +301 -0
  89. package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +484 -0
  90. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
  91. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +56 -0
  92. package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +182 -0
  93. package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +306 -0
  94. package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
  95. package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +64 -0
  96. package/plugins/specweave/lib/hooks/reflection-storage.js.bak +231 -0
  97. package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +369 -0
  98. package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
  99. package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +43 -0
  100. package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +132 -0
  101. package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +258 -0
  102. package/plugins/specweave/lib/hooks/sync-cache.js.bak +294 -0
  103. package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +1 -0
  104. package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +27 -0
  105. package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +339 -0
  106. package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +476 -0
  107. package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
  108. package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +59 -0
  109. package/plugins/specweave/lib/hooks/translate-file.js.bak +289 -0
  110. package/plugins/specweave/lib/hooks/translate-file.ts.bak +428 -0
  111. package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
  112. package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +13 -0
  113. package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +119 -0
  114. package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +224 -0
  115. package/plugins/specweave/lib/hooks/update-ac-status.js.bak +51 -0
  116. package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +103 -0
  117. package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +1 -0
  118. package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +29 -0
  119. package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +296 -0
  120. package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +489 -0
  121. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  122. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  123. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
  124. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  125. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +424 -0
  126. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  127. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  128. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  129. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +540 -0
  130. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
@@ -0,0 +1,132 @@
1
+ import { loadAndValidateReflectionConfig } from "./reflection-config-loader";
2
+ import { getModifiedFiles, getModifiedFilesSummary } from "./git-diff-analyzer";
3
+ import { buildReflectionPrompt, estimatePromptTokens } from "./reflection-prompt-builder";
4
+ import { parseReflectionMarkdown, validateReflectionResult } from "./reflection-parser";
5
+ import { saveReflection } from "./reflection-storage";
6
+ import {
7
+ ReflectionMode,
8
+ ReflectionModel
9
+ } from "./types/reflection-types";
10
+ function shouldRunReflection(context) {
11
+ const { config, modifiedFiles } = context;
12
+ if (!config.enabled) {
13
+ return false;
14
+ }
15
+ if (config.mode === ReflectionMode.DISABLED) {
16
+ return false;
17
+ }
18
+ if (config.mode === ReflectionMode.MANUAL) {
19
+ return false;
20
+ }
21
+ if (modifiedFiles.length === 0) {
22
+ return false;
23
+ }
24
+ return true;
25
+ }
26
+ function estimateReflectionCost(model, promptTokens, responseTokens = 2e3) {
27
+ const pricing = {
28
+ [ReflectionModel.HAIKU]: { input: 0.8, output: 4 },
29
+ [ReflectionModel.SONNET]: { input: 3, output: 15 },
30
+ [ReflectionModel.OPUS]: { input: 15, output: 75 }
31
+ };
32
+ const modelPricing = pricing[model];
33
+ const inputCost = promptTokens / 1e6 * modelPricing.input;
34
+ const outputCost = responseTokens / 1e6 * modelPricing.output;
35
+ return inputCost + outputCost;
36
+ }
37
+ async function invokeReflectiveReviewerAgent(prompt, model) {
38
+ throw new Error(
39
+ "Agent invocation not implemented yet. This function will be implemented in hook integration (T-008). Call this from the post-task-completion hook using the Task tool."
40
+ );
41
+ }
42
+ async function runSelfReflection(context) {
43
+ const { incrementId, taskId, modifiedFiles, config } = context;
44
+ const startTime = Date.now();
45
+ try {
46
+ if (!shouldRunReflection(context)) {
47
+ return {
48
+ success: true,
49
+ result: void 0,
50
+ error: void 0
51
+ };
52
+ }
53
+ const prompt = buildReflectionPrompt({
54
+ taskId,
55
+ taskName: `Task ${taskId}`,
56
+ // Will be enhanced with actual task name in hook
57
+ modifiedFiles,
58
+ config,
59
+ incrementId,
60
+ includeFullDiff: false
61
+ // Use simplified prompt for token efficiency
62
+ });
63
+ const promptTokens = estimatePromptTokens(prompt);
64
+ const estimatedCost = estimateReflectionCost(config.model, promptTokens);
65
+ const agentResponse = await invokeReflectiveReviewerAgent(prompt, config.model);
66
+ const endTime = Date.now();
67
+ const reflectionTime = Math.round((endTime - startTime) / 1e3);
68
+ const reflectionResult = parseReflectionMarkdown(
69
+ agentResponse,
70
+ `Task ${taskId}`,
71
+ config.model,
72
+ reflectionTime,
73
+ estimatedCost
74
+ );
75
+ const fileStats = getModifiedFilesSummary(modifiedFiles);
76
+ reflectionResult.filesModified = {
77
+ count: fileStats.count,
78
+ linesAdded: fileStats.linesAdded,
79
+ linesRemoved: fileStats.linesRemoved
80
+ };
81
+ const validation = validateReflectionResult(reflectionResult);
82
+ if (!validation.valid) {
83
+ return {
84
+ success: false,
85
+ error: {
86
+ message: "Reflection validation failed",
87
+ code: "VALIDATION_ERROR",
88
+ details: validation.errors
89
+ }
90
+ };
91
+ }
92
+ if (config.storeReflections) {
93
+ try {
94
+ saveReflection(reflectionResult, incrementId, taskId);
95
+ } catch (storageError) {
96
+ console.warn(`Failed to store reflection: ${storageError.message}`);
97
+ }
98
+ }
99
+ return {
100
+ success: true,
101
+ result: reflectionResult
102
+ };
103
+ } catch (error) {
104
+ return {
105
+ success: false,
106
+ error: {
107
+ message: error.message || "Unknown error during reflection",
108
+ code: "REFLECTION_ERROR",
109
+ details: error.stack
110
+ }
111
+ };
112
+ }
113
+ }
114
+ function createReflectionContext(incrementId, taskId, projectRoot) {
115
+ const config = loadAndValidateReflectionConfig(projectRoot);
116
+ const modifiedFiles = getModifiedFiles(projectRoot);
117
+ return {
118
+ incrementId,
119
+ taskId,
120
+ modifiedFiles,
121
+ config
122
+ };
123
+ }
124
+ async function runReflectionAuto(incrementId, taskId, projectRoot) {
125
+ const context = createReflectionContext(incrementId, taskId, projectRoot);
126
+ return runSelfReflection(context);
127
+ }
128
+ export {
129
+ createReflectionContext,
130
+ runReflectionAuto,
131
+ runSelfReflection
132
+ };
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Core Reflection Engine
3
+ *
4
+ * Orchestrates the self-reflection process:
5
+ * 1. Load configuration
6
+ * 2. Get modified files
7
+ * 3. Build prompt
8
+ * 4. Invoke reflective-reviewer agent
9
+ * 5. Parse response
10
+ * 6. Store reflection
11
+ *
12
+ * @module run-self-reflection
13
+ */
14
+
15
+ import { loadAndValidateReflectionConfig } from './reflection-config-loader';
16
+ import { getModifiedFiles, getModifiedFilesSummary } from './git-diff-analyzer';
17
+ import { buildReflectionPrompt, estimatePromptTokens } from './reflection-prompt-builder';
18
+ import { parseReflectionMarkdown, validateReflectionResult } from './reflection-parser';
19
+ import { saveReflection } from './reflection-storage';
20
+ import {
21
+ ReflectionContext,
22
+ ReflectionExecutionResult,
23
+ ReflectionMode,
24
+ ReflectionModel
25
+ } from './types/reflection-types';
26
+
27
+ /**
28
+ * Check if reflection should run for the current context
29
+ *
30
+ * @param context Reflection context with config and modified files
31
+ * @returns True if reflection should run
32
+ */
33
+ function shouldRunReflection(context: ReflectionContext): boolean {
34
+ const { config, modifiedFiles } = context;
35
+
36
+ // Check if reflection is enabled
37
+ if (!config.enabled) {
38
+ return false;
39
+ }
40
+
41
+ // Check mode
42
+ if (config.mode === ReflectionMode.DISABLED) {
43
+ return false;
44
+ }
45
+
46
+ if (config.mode === ReflectionMode.MANUAL) {
47
+ // Manual mode requires explicit invocation
48
+ // This function is only called in auto mode, so skip
49
+ return false;
50
+ }
51
+
52
+ // Check if there are modified files to analyze
53
+ if (modifiedFiles.length === 0) {
54
+ return false;
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ /**
61
+ * Estimate reflection cost based on model and prompt size
62
+ *
63
+ * @param model Reflection model (haiku, sonnet, opus)
64
+ * @param promptTokens Estimated prompt token count
65
+ * @param responseTokens Estimated response token count (default: 2000)
66
+ * @returns Estimated cost in USD
67
+ */
68
+ function estimateReflectionCost(
69
+ model: ReflectionModel,
70
+ promptTokens: number,
71
+ responseTokens: number = 2000
72
+ ): number {
73
+ // Pricing per 1M tokens (as of Nov 2025)
74
+ const pricing = {
75
+ [ReflectionModel.HAIKU]: { input: 0.80, output: 4.00 },
76
+ [ReflectionModel.SONNET]: { input: 3.00, output: 15.00 },
77
+ [ReflectionModel.OPUS]: { input: 15.00, output: 75.00 }
78
+ };
79
+
80
+ const modelPricing = pricing[model];
81
+ const inputCost = (promptTokens / 1_000_000) * modelPricing.input;
82
+ const outputCost = (responseTokens / 1_000_000) * modelPricing.output;
83
+
84
+ return inputCost + outputCost;
85
+ }
86
+
87
+ /**
88
+ * Invoke reflective-reviewer agent to analyze code
89
+ *
90
+ * @param prompt Reflection prompt
91
+ * @param model Model to use (haiku, sonnet, opus)
92
+ * @returns Agent response (markdown reflection)
93
+ */
94
+ async function invokeReflectiveReviewerAgent(
95
+ prompt: string,
96
+ model: ReflectionModel
97
+ ): Promise<string> {
98
+ // NOTE: This is a placeholder for agent invocation
99
+ // In the real implementation, this would use Claude Code's Task tool
100
+ // to invoke the specweave:reflective-reviewer:reflective-reviewer agent
101
+ //
102
+ // For now, we'll return a mock response to enable testing
103
+ // The actual implementation will be in the hook integration (T-008)
104
+
105
+ throw new Error(
106
+ 'Agent invocation not implemented yet. ' +
107
+ 'This function will be implemented in hook integration (T-008). ' +
108
+ 'Call this from the post-task-completion hook using the Task tool.'
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Run self-reflection analysis
114
+ * Main entry point for the reflection system
115
+ *
116
+ * @param context Reflection context with increment, task, and files
117
+ * @returns Reflection execution result (success or error)
118
+ */
119
+ export async function runSelfReflection(
120
+ context: ReflectionContext
121
+ ): Promise<ReflectionExecutionResult> {
122
+ const { incrementId, taskId, modifiedFiles, config } = context;
123
+ const startTime = Date.now();
124
+
125
+ try {
126
+ // Step 1: Check if reflection should run
127
+ if (!shouldRunReflection(context)) {
128
+ return {
129
+ success: true,
130
+ result: undefined,
131
+ error: undefined
132
+ };
133
+ }
134
+
135
+ // Step 2: Build reflection prompt
136
+ const prompt = buildReflectionPrompt({
137
+ taskId,
138
+ taskName: `Task ${taskId}`, // Will be enhanced with actual task name in hook
139
+ modifiedFiles,
140
+ config,
141
+ incrementId,
142
+ includeFullDiff: false // Use simplified prompt for token efficiency
143
+ });
144
+
145
+ // Step 3: Estimate cost
146
+ const promptTokens = estimatePromptTokens(prompt);
147
+ const estimatedCost = estimateReflectionCost(config.model, promptTokens);
148
+
149
+ // Step 4: Invoke reflective-reviewer agent
150
+ const agentResponse = await invokeReflectiveReviewerAgent(prompt, config.model);
151
+
152
+ // Step 5: Parse response
153
+ const endTime = Date.now();
154
+ const reflectionTime = Math.round((endTime - startTime) / 1000); // seconds
155
+
156
+ const reflectionResult = parseReflectionMarkdown(
157
+ agentResponse,
158
+ `Task ${taskId}`,
159
+ config.model,
160
+ reflectionTime,
161
+ estimatedCost
162
+ );
163
+
164
+ // Step 6: Add file modification stats
165
+ const fileStats = getModifiedFilesSummary(modifiedFiles);
166
+ reflectionResult.filesModified = {
167
+ count: fileStats.count,
168
+ linesAdded: fileStats.linesAdded,
169
+ linesRemoved: fileStats.linesRemoved
170
+ };
171
+
172
+ // Step 7: Validate reflection result
173
+ const validation = validateReflectionResult(reflectionResult);
174
+ if (!validation.valid) {
175
+ return {
176
+ success: false,
177
+ error: {
178
+ message: 'Reflection validation failed',
179
+ code: 'VALIDATION_ERROR',
180
+ details: validation.errors
181
+ }
182
+ };
183
+ }
184
+
185
+ // Step 8: Store reflection (if enabled)
186
+ if (config.storeReflections) {
187
+ try {
188
+ saveReflection(reflectionResult, incrementId, taskId);
189
+ } catch (storageError: any) {
190
+ // Storage failure shouldn't fail the entire reflection
191
+ console.warn(`Failed to store reflection: ${storageError.message}`);
192
+ }
193
+ }
194
+
195
+ // Step 9: Return success
196
+ return {
197
+ success: true,
198
+ result: reflectionResult
199
+ };
200
+
201
+ } catch (error: any) {
202
+ // Graceful degradation: Return error but don't throw
203
+ return {
204
+ success: false,
205
+ error: {
206
+ message: error.message || 'Unknown error during reflection',
207
+ code: 'REFLECTION_ERROR',
208
+ details: error.stack
209
+ }
210
+ };
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Create reflection context from hook environment
216
+ * Helper function for hook integration
217
+ *
218
+ * @param incrementId Increment identifier
219
+ * @param taskId Task identifier
220
+ * @param projectRoot Project root directory (optional, auto-detected)
221
+ * @returns Reflection context ready for runSelfReflection
222
+ */
223
+ export function createReflectionContext(
224
+ incrementId: string,
225
+ taskId: string,
226
+ projectRoot?: string
227
+ ): ReflectionContext {
228
+ // Load and validate configuration
229
+ const config = loadAndValidateReflectionConfig(projectRoot);
230
+
231
+ // Get modified files from git
232
+ const modifiedFiles = getModifiedFiles(projectRoot);
233
+
234
+ return {
235
+ incrementId,
236
+ taskId,
237
+ modifiedFiles,
238
+ config
239
+ };
240
+ }
241
+
242
+ /**
243
+ * Run reflection with automatic context creation
244
+ * Convenience function for hook integration
245
+ *
246
+ * @param incrementId Increment identifier
247
+ * @param taskId Task identifier
248
+ * @param projectRoot Project root directory (optional)
249
+ * @returns Reflection execution result
250
+ */
251
+ export async function runReflectionAuto(
252
+ incrementId: string,
253
+ taskId: string,
254
+ projectRoot?: string
255
+ ): Promise<ReflectionExecutionResult> {
256
+ const context = createReflectionContext(incrementId, taskId, projectRoot);
257
+ return runSelfReflection(context);
258
+ }
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sync Performance Cache Module
4
+ *
5
+ * Provides caching layer for living docs sync to meet <500ms target.
6
+ * Caches:
7
+ * - Parsed tasks.md content
8
+ * - File modification timestamps
9
+ * - US-Task mappings
10
+ *
11
+ * Part of increment 0047-us-task-linkage (T-012).
12
+ */
13
+
14
+ import fs from 'fs-extra';
15
+ import path from 'path';
16
+ import crypto from 'crypto';
17
+
18
+ /**
19
+ * In-memory cache with TTL
20
+ */
21
+ class SyncCache {
22
+ constructor() {
23
+ this.cache = new Map();
24
+ this.ttl = 60000; // 60 seconds default TTL
25
+ }
26
+
27
+ /**
28
+ * Get cached value
29
+ *
30
+ * @param {string} key - Cache key
31
+ * @returns {any|null} Cached value or null if expired/missing
32
+ */
33
+ get(key) {
34
+ const entry = this.cache.get(key);
35
+
36
+ if (!entry) {
37
+ return null;
38
+ }
39
+
40
+ // Check if expired
41
+ if (Date.now() > entry.expiry) {
42
+ this.cache.delete(key);
43
+ return null;
44
+ }
45
+
46
+ return entry.value;
47
+ }
48
+
49
+ /**
50
+ * Set cached value
51
+ *
52
+ * @param {string} key - Cache key
53
+ * @param {any} value - Value to cache
54
+ * @param {number} ttl - Time to live in milliseconds (optional)
55
+ */
56
+ set(key, value, ttl = this.ttl) {
57
+ this.cache.set(key, {
58
+ value,
59
+ expiry: Date.now() + ttl
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Invalidate cache entry
65
+ *
66
+ * @param {string} key - Cache key
67
+ */
68
+ invalidate(key) {
69
+ this.cache.delete(key);
70
+ }
71
+
72
+ /**
73
+ * Clear all cache
74
+ */
75
+ clear() {
76
+ this.cache.clear();
77
+ }
78
+
79
+ /**
80
+ * Get cache size
81
+ *
82
+ * @returns {number} Number of cached entries
83
+ */
84
+ size() {
85
+ return this.cache.size;
86
+ }
87
+ }
88
+
89
+ // Global cache instance
90
+ const globalCache = new SyncCache();
91
+
92
+ /**
93
+ * Get file hash for change detection
94
+ *
95
+ * @param {string} filePath - Path to file
96
+ * @returns {string} SHA256 hash of file content
97
+ */
98
+ export function getFileHash(filePath) {
99
+ try {
100
+ const content = fs.readFileSync(filePath, 'utf-8');
101
+ return crypto.createHash('sha256').update(content).digest('hex');
102
+ } catch (error) {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Get cached parsed tasks.md
109
+ *
110
+ * @param {string} tasksPath - Path to tasks.md
111
+ * @param {Function} parser - Parser function to call if cache miss
112
+ * @returns {any} Parsed tasks (from cache or fresh)
113
+ */
114
+ export function getCachedTasks(tasksPath, parser) {
115
+ // Generate cache key from file path + hash
116
+ const fileHash = getFileHash(tasksPath);
117
+
118
+ if (!fileHash) {
119
+ // File doesn't exist, call parser directly
120
+ return parser(tasksPath);
121
+ }
122
+
123
+ const cacheKey = `tasks:${tasksPath}:${fileHash}`;
124
+
125
+ // Try to get from cache
126
+ const cached = globalCache.get(cacheKey);
127
+
128
+ if (cached) {
129
+ // Cache hit
130
+ return cached;
131
+ }
132
+
133
+ // Cache miss - parse and cache
134
+ const parsed = parser(tasksPath);
135
+ globalCache.set(cacheKey, parsed);
136
+
137
+ return parsed;
138
+ }
139
+
140
+ /**
141
+ * Get cached US file metadata
142
+ *
143
+ * @param {string} usFilePath - Path to US file
144
+ * @returns {object|null} Metadata object or null if file doesn't exist
145
+ */
146
+ export function getCachedUSMetadata(usFilePath) {
147
+ try {
148
+ const stats = fs.statSync(usFilePath);
149
+ const cacheKey = `us-metadata:${usFilePath}`;
150
+
151
+ const cached = globalCache.get(cacheKey);
152
+
153
+ if (cached && cached.mtime === stats.mtimeMs) {
154
+ // File hasn't changed, return cached metadata
155
+ return cached.metadata;
156
+ }
157
+
158
+ // File changed or not in cache - read and cache
159
+ const content = fs.readFileSync(usFilePath, 'utf-8');
160
+ const metadata = {
161
+ mtime: stats.mtimeMs,
162
+ metadata: {
163
+ path: usFilePath,
164
+ size: content.length,
165
+ tasksSectionExists: content.includes('## Tasks'),
166
+ acCount: (content.match(/- \[[x ]\] \*\*AC-US\d+-\d{2}\*\*/g) || []).length
167
+ }
168
+ };
169
+
170
+ globalCache.set(cacheKey, metadata);
171
+
172
+ return metadata.metadata;
173
+ } catch (error) {
174
+ return null;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Batch file operations to reduce I/O
180
+ *
181
+ * @param {Array<{path: string, content: string}>} updates - Files to update
182
+ * @returns {Promise<void>}
183
+ */
184
+ export async function batchFileUpdates(updates) {
185
+ // Group updates by directory to optimize disk I/O
186
+ const updatesByDir = new Map();
187
+
188
+ updates.forEach(({ path: filePath, content }) => {
189
+ const dir = path.dirname(filePath);
190
+
191
+ if (!updatesByDir.has(dir)) {
192
+ updatesByDir.set(dir, []);
193
+ }
194
+
195
+ updatesByDir.get(dir).push({ path: filePath, content });
196
+ });
197
+
198
+ // Write files sequentially within same directory (better disk I/O)
199
+ for (const [dir, fileUpdates] of updatesByDir.entries()) {
200
+ // Ensure directory exists once per directory
201
+ await fs.ensureDir(dir);
202
+
203
+ // Write all files in this directory
204
+ await Promise.all(
205
+ fileUpdates.map(({ path: filePath, content }) =>
206
+ fs.writeFile(filePath, content, 'utf-8')
207
+ )
208
+ );
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Check if sync is needed for a US file
214
+ *
215
+ * @param {string} usFilePath - Path to US file
216
+ * @param {Array} tasks - Tasks for this US
217
+ * @param {string} tasksPath - Path to tasks.md
218
+ * @returns {boolean} True if sync is needed
219
+ */
220
+ export function needsSync(usFilePath, tasks, tasksPath) {
221
+ try {
222
+ // Check if US file exists
223
+ if (!fs.existsSync(usFilePath)) {
224
+ return false; // File doesn't exist, can't sync
225
+ }
226
+
227
+ // Get file modification times
228
+ const usStats = fs.statSync(usFilePath);
229
+ const tasksStats = fs.statSync(tasksPath);
230
+
231
+ // If tasks.md is newer than US file, sync is needed
232
+ if (tasksStats.mtimeMs > usStats.mtimeMs) {
233
+ return true;
234
+ }
235
+
236
+ // Check cache for last sync result
237
+ const cacheKey = `sync-result:${usFilePath}`;
238
+ const cached = globalCache.get(cacheKey);
239
+
240
+ if (cached) {
241
+ // Compare task list with cached
242
+ const currentTaskIds = tasks.map(t => t.id).sort().join(',');
243
+ const cachedTaskIds = cached.taskIds;
244
+
245
+ if (currentTaskIds === cachedTaskIds) {
246
+ // Task list unchanged, no sync needed
247
+ return false;
248
+ }
249
+ }
250
+
251
+ // Default: sync is needed
252
+ return true;
253
+ } catch (error) {
254
+ // If error checking, assume sync is needed
255
+ return true;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Record sync result for incremental sync
261
+ *
262
+ * @param {string} usFilePath - Path to US file
263
+ * @param {Array} tasks - Tasks that were synced
264
+ */
265
+ export function recordSync(usFilePath, tasks) {
266
+ const cacheKey = `sync-result:${usFilePath}`;
267
+ const taskIds = tasks.map(t => t.id).sort().join(',');
268
+
269
+ globalCache.set(cacheKey, {
270
+ taskIds,
271
+ timestamp: Date.now()
272
+ });
273
+ }
274
+
275
+ /**
276
+ * Get cache statistics
277
+ *
278
+ * @returns {object} Cache stats
279
+ */
280
+ export function getCacheStats() {
281
+ return {
282
+ size: globalCache.size(),
283
+ entries: Array.from(globalCache.cache.keys())
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Clear cache (for testing)
289
+ */
290
+ export function clearCache() {
291
+ globalCache.clear();
292
+ }
293
+
294
+ export default globalCache;
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SpecWeave Living Docs Auto-Sync
4
+ *
5
+ * Automatically syncs living documentation after task completion.
6
+ *
7
+ * Usage:
8
+ * node dist/hooks/lib/sync-living-docs.js <incrementId>
9
+ *
10
+ * Example:
11
+ * node dist/hooks/lib/sync-living-docs.js 0006-llm-native-i18n
12
+ *
13
+ * What it does:
14
+ * 1. Checks if sync_living_docs enabled in config
15
+ * 2. Detects changed docs via git diff
16
+ * 3. Invokes /sync-docs update command (future implementation)
17
+ * 4. Logs sync actions
18
+ *
19
+ * @author SpecWeave Team
20
+ * @version 1.0.0
21
+ */
22
+ /**
23
+ * Main function - sync living docs for given increment
24
+ */
25
+ declare function syncLivingDocs(incrementId: string): Promise<void>;
26
+ export { syncLivingDocs };
27
+ //# sourceMappingURL=sync-living-docs.d.ts.map