wogiflow 1.0.0

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 (221) hide show
  1. package/.workflow/agents/reviewer.md +81 -0
  2. package/.workflow/agents/security.md +94 -0
  3. package/.workflow/agents/story-writer.md +58 -0
  4. package/.workflow/bridges/base-bridge.js +395 -0
  5. package/.workflow/bridges/claude-bridge.js +434 -0
  6. package/.workflow/bridges/index.js +130 -0
  7. package/.workflow/lib/assumption-detector.js +481 -0
  8. package/.workflow/lib/config-substitution.js +371 -0
  9. package/.workflow/lib/failure-categories.js +478 -0
  10. package/.workflow/state/app-map.md.template +15 -0
  11. package/.workflow/state/architecture.md.template +24 -0
  12. package/.workflow/state/component-index.json.template +5 -0
  13. package/.workflow/state/decisions.md.template +15 -0
  14. package/.workflow/state/feedback-patterns.md.template +9 -0
  15. package/.workflow/state/knowledge-sync.json.template +6 -0
  16. package/.workflow/state/progress.md.template +14 -0
  17. package/.workflow/state/ready.json.template +7 -0
  18. package/.workflow/state/request-log.md.template +14 -0
  19. package/.workflow/state/session-state.json.template +11 -0
  20. package/.workflow/state/stack.md.template +33 -0
  21. package/.workflow/state/testing.md.template +36 -0
  22. package/.workflow/templates/claude-md.hbs +257 -0
  23. package/.workflow/templates/correction-report.md +67 -0
  24. package/.workflow/templates/gemini-md.hbs +52 -0
  25. package/README.md +1802 -0
  26. package/bin/flow +205 -0
  27. package/lib/index.js +33 -0
  28. package/lib/installer.js +467 -0
  29. package/lib/release-channel.js +269 -0
  30. package/lib/skill-registry.js +526 -0
  31. package/lib/upgrader.js +401 -0
  32. package/lib/utils.js +305 -0
  33. package/package.json +64 -0
  34. package/scripts/flow +985 -0
  35. package/scripts/flow-adaptive-learning.js +1259 -0
  36. package/scripts/flow-aggregate.js +488 -0
  37. package/scripts/flow-archive +133 -0
  38. package/scripts/flow-auto-context.js +1015 -0
  39. package/scripts/flow-auto-learn.js +615 -0
  40. package/scripts/flow-bridge.js +223 -0
  41. package/scripts/flow-browser-suggest.js +316 -0
  42. package/scripts/flow-bug.js +247 -0
  43. package/scripts/flow-cascade.js +711 -0
  44. package/scripts/flow-changelog +85 -0
  45. package/scripts/flow-checkpoint.js +483 -0
  46. package/scripts/flow-cli.js +403 -0
  47. package/scripts/flow-code-intelligence.js +760 -0
  48. package/scripts/flow-complexity.js +502 -0
  49. package/scripts/flow-config-set.js +152 -0
  50. package/scripts/flow-constants.js +157 -0
  51. package/scripts/flow-context +152 -0
  52. package/scripts/flow-context-init.js +482 -0
  53. package/scripts/flow-context-monitor.js +384 -0
  54. package/scripts/flow-context-scoring.js +886 -0
  55. package/scripts/flow-correct.js +458 -0
  56. package/scripts/flow-damage-control.js +985 -0
  57. package/scripts/flow-deps +101 -0
  58. package/scripts/flow-diff.js +700 -0
  59. package/scripts/flow-done +151 -0
  60. package/scripts/flow-done.js +489 -0
  61. package/scripts/flow-durable-session.js +1541 -0
  62. package/scripts/flow-entropy-monitor.js +345 -0
  63. package/scripts/flow-export-profile +349 -0
  64. package/scripts/flow-export-scanner.js +1046 -0
  65. package/scripts/flow-figma-confirm.js +400 -0
  66. package/scripts/flow-figma-extract.js +496 -0
  67. package/scripts/flow-figma-generate.js +683 -0
  68. package/scripts/flow-figma-index.js +909 -0
  69. package/scripts/flow-figma-match.js +617 -0
  70. package/scripts/flow-figma-mcp-server.js +518 -0
  71. package/scripts/flow-figma-pipeline.js +414 -0
  72. package/scripts/flow-file-ops.js +301 -0
  73. package/scripts/flow-gate-confidence.js +825 -0
  74. package/scripts/flow-guided-edit.js +659 -0
  75. package/scripts/flow-health +185 -0
  76. package/scripts/flow-health.js +413 -0
  77. package/scripts/flow-hooks.js +556 -0
  78. package/scripts/flow-http-client.js +249 -0
  79. package/scripts/flow-hybrid-detect.js +167 -0
  80. package/scripts/flow-hybrid-interactive.js +591 -0
  81. package/scripts/flow-hybrid-test.js +152 -0
  82. package/scripts/flow-import-profile +439 -0
  83. package/scripts/flow-init +253 -0
  84. package/scripts/flow-instruction-richness.js +827 -0
  85. package/scripts/flow-jira-integration.js +579 -0
  86. package/scripts/flow-knowledge-router.js +522 -0
  87. package/scripts/flow-knowledge-sync.js +589 -0
  88. package/scripts/flow-linear-integration.js +631 -0
  89. package/scripts/flow-links.js +774 -0
  90. package/scripts/flow-log-manager.js +559 -0
  91. package/scripts/flow-loop-enforcer.js +1246 -0
  92. package/scripts/flow-loop-retry-learning.js +630 -0
  93. package/scripts/flow-lsp.js +923 -0
  94. package/scripts/flow-map-index +348 -0
  95. package/scripts/flow-map-sync +201 -0
  96. package/scripts/flow-memory-blocks.js +668 -0
  97. package/scripts/flow-memory-compactor.js +350 -0
  98. package/scripts/flow-memory-db.js +1110 -0
  99. package/scripts/flow-memory-sync.js +484 -0
  100. package/scripts/flow-metrics.js +353 -0
  101. package/scripts/flow-migrate-ids.js +370 -0
  102. package/scripts/flow-model-adapter.js +802 -0
  103. package/scripts/flow-model-router.js +884 -0
  104. package/scripts/flow-models.js +1231 -0
  105. package/scripts/flow-morning.js +517 -0
  106. package/scripts/flow-multi-approach.js +660 -0
  107. package/scripts/flow-new-feature +86 -0
  108. package/scripts/flow-onboard +1042 -0
  109. package/scripts/flow-orchestrate-llm.js +459 -0
  110. package/scripts/flow-orchestrate.js +3592 -0
  111. package/scripts/flow-output.js +123 -0
  112. package/scripts/flow-parallel-detector.js +399 -0
  113. package/scripts/flow-parallel-dispatch.js +987 -0
  114. package/scripts/flow-parallel.js +428 -0
  115. package/scripts/flow-pattern-enforcer.js +600 -0
  116. package/scripts/flow-prd-manager.js +282 -0
  117. package/scripts/flow-progress.js +323 -0
  118. package/scripts/flow-project-analyzer.js +975 -0
  119. package/scripts/flow-prompt-composer.js +487 -0
  120. package/scripts/flow-providers.js +1381 -0
  121. package/scripts/flow-queue.js +308 -0
  122. package/scripts/flow-ready +82 -0
  123. package/scripts/flow-ready.js +189 -0
  124. package/scripts/flow-regression.js +396 -0
  125. package/scripts/flow-response-parser.js +450 -0
  126. package/scripts/flow-resume.js +284 -0
  127. package/scripts/flow-rules-sync.js +439 -0
  128. package/scripts/flow-run-trace.js +718 -0
  129. package/scripts/flow-safety.js +587 -0
  130. package/scripts/flow-search +104 -0
  131. package/scripts/flow-security.js +481 -0
  132. package/scripts/flow-session-end +106 -0
  133. package/scripts/flow-session-end.js +437 -0
  134. package/scripts/flow-session-state.js +671 -0
  135. package/scripts/flow-setup-hooks +216 -0
  136. package/scripts/flow-setup-hooks.js +377 -0
  137. package/scripts/flow-skill-create.js +329 -0
  138. package/scripts/flow-skill-creator.js +572 -0
  139. package/scripts/flow-skill-generator.js +1046 -0
  140. package/scripts/flow-skill-learn.js +880 -0
  141. package/scripts/flow-skill-matcher.js +578 -0
  142. package/scripts/flow-spec-generator.js +820 -0
  143. package/scripts/flow-stack-wizard.js +895 -0
  144. package/scripts/flow-standup +162 -0
  145. package/scripts/flow-start +74 -0
  146. package/scripts/flow-start.js +235 -0
  147. package/scripts/flow-status +110 -0
  148. package/scripts/flow-status.js +301 -0
  149. package/scripts/flow-step-browser.js +83 -0
  150. package/scripts/flow-step-changelog.js +217 -0
  151. package/scripts/flow-step-comments.js +306 -0
  152. package/scripts/flow-step-complexity.js +234 -0
  153. package/scripts/flow-step-coverage.js +218 -0
  154. package/scripts/flow-step-knowledge.js +193 -0
  155. package/scripts/flow-step-pr-tests.js +364 -0
  156. package/scripts/flow-step-regression.js +89 -0
  157. package/scripts/flow-step-review.js +516 -0
  158. package/scripts/flow-step-security.js +162 -0
  159. package/scripts/flow-step-silent-failures.js +290 -0
  160. package/scripts/flow-step-simplifier.js +346 -0
  161. package/scripts/flow-story +105 -0
  162. package/scripts/flow-story.js +500 -0
  163. package/scripts/flow-suspend.js +252 -0
  164. package/scripts/flow-sync-daemon.js +654 -0
  165. package/scripts/flow-task-analyzer.js +606 -0
  166. package/scripts/flow-team-dashboard.js +748 -0
  167. package/scripts/flow-team-sync.js +752 -0
  168. package/scripts/flow-team.js +977 -0
  169. package/scripts/flow-tech-options.js +528 -0
  170. package/scripts/flow-templates.js +812 -0
  171. package/scripts/flow-tiered-learning.js +728 -0
  172. package/scripts/flow-trace +204 -0
  173. package/scripts/flow-transcript-chunking.js +1106 -0
  174. package/scripts/flow-transcript-digest.js +7918 -0
  175. package/scripts/flow-transcript-language.js +465 -0
  176. package/scripts/flow-transcript-parsing.js +1085 -0
  177. package/scripts/flow-transcript-stories.js +2194 -0
  178. package/scripts/flow-update-map +224 -0
  179. package/scripts/flow-utils.js +2242 -0
  180. package/scripts/flow-verification.js +644 -0
  181. package/scripts/flow-verify.js +1177 -0
  182. package/scripts/flow-voice-input.js +638 -0
  183. package/scripts/flow-watch +168 -0
  184. package/scripts/flow-workflow-steps.js +521 -0
  185. package/scripts/flow-workflow.js +1029 -0
  186. package/scripts/flow-worktree.js +489 -0
  187. package/scripts/hooks/adapters/base-adapter.js +102 -0
  188. package/scripts/hooks/adapters/claude-code.js +359 -0
  189. package/scripts/hooks/adapters/index.js +79 -0
  190. package/scripts/hooks/core/component-check.js +341 -0
  191. package/scripts/hooks/core/index.js +35 -0
  192. package/scripts/hooks/core/loop-check.js +241 -0
  193. package/scripts/hooks/core/session-context.js +294 -0
  194. package/scripts/hooks/core/task-gate.js +177 -0
  195. package/scripts/hooks/core/validation.js +230 -0
  196. package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
  197. package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
  198. package/scripts/hooks/entry/claude-code/session-end.js +87 -0
  199. package/scripts/hooks/entry/claude-code/session-start.js +46 -0
  200. package/scripts/hooks/entry/claude-code/stop.js +43 -0
  201. package/scripts/postinstall.js +139 -0
  202. package/templates/browser-test-flow.json +56 -0
  203. package/templates/bug-report.md +43 -0
  204. package/templates/component-detail.md +42 -0
  205. package/templates/component.stories.tsx +49 -0
  206. package/templates/context/constraints.md +83 -0
  207. package/templates/context/conventions.md +177 -0
  208. package/templates/context/stack.md +60 -0
  209. package/templates/correction-report.md +90 -0
  210. package/templates/feature-proposal.md +35 -0
  211. package/templates/hybrid/_base.md +254 -0
  212. package/templates/hybrid/_patterns.md +45 -0
  213. package/templates/hybrid/create-component.md +127 -0
  214. package/templates/hybrid/create-file.md +56 -0
  215. package/templates/hybrid/create-hook.md +145 -0
  216. package/templates/hybrid/create-service.md +70 -0
  217. package/templates/hybrid/fix-bug.md +33 -0
  218. package/templates/hybrid/modify-file.md +55 -0
  219. package/templates/story.md +68 -0
  220. package/templates/task.json +56 -0
  221. package/templates/trace.md +69 -0
@@ -0,0 +1,559 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Request Log Manager
5
+ *
6
+ * Implements summary buffer pattern for request-log.md:
7
+ * - Keeps recent entries in main log
8
+ * - Archives older entries to monthly files
9
+ * - Maintains summary of archived content
10
+ *
11
+ * Inspired by LangChain's SummaryBufferMemory pattern.
12
+ *
13
+ * Part of v1.7.0 Context Memory Management
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const {
19
+ getConfig,
20
+ PATHS,
21
+ WORKFLOW_DIR,
22
+ STATE_DIR,
23
+ colors,
24
+ color,
25
+ warn,
26
+ success,
27
+ error,
28
+ readFile,
29
+ writeFile,
30
+ fileExists,
31
+ dirExists,
32
+ countRequestLogEntries,
33
+ printHeader
34
+ } = require('./flow-utils');
35
+
36
+ // ============================================================
37
+ // Constants
38
+ // ============================================================
39
+
40
+ const LOG_PATH = PATHS.requestLog;
41
+ const ARCHIVE_DIR = path.join(WORKFLOW_DIR, 'archive');
42
+ const SUMMARY_PATH = path.join(STATE_DIR, 'request-log-summary.md');
43
+
44
+ // Default configuration
45
+ const DEFAULTS = {
46
+ enabled: true,
47
+ autoArchive: true,
48
+ maxRecentEntries: 50,
49
+ keepRecent: 30,
50
+ createSummary: true
51
+ };
52
+
53
+ // ============================================================
54
+ // Configuration
55
+ // ============================================================
56
+
57
+ /**
58
+ * Get log manager configuration
59
+ */
60
+ function getLogManagerConfig() {
61
+ const config = getConfig();
62
+ return {
63
+ ...DEFAULTS,
64
+ ...(config.requestLog || {})
65
+ };
66
+ }
67
+
68
+ // ============================================================
69
+ // Entry Parsing
70
+ // ============================================================
71
+
72
+ /**
73
+ * Parse entries from request-log content
74
+ * Returns array of { id, raw, type, tags, request, result, date }
75
+ */
76
+ function parseEntries(content) {
77
+ const entries = [];
78
+
79
+ // Match entry blocks starting with ### R-NNN
80
+ const entryRegex = /### (R-\d+)\s*\|\s*([^\n]+)\n([\s\S]*?)(?=### R-|\Z|$)/g;
81
+ let match;
82
+
83
+ while ((match = entryRegex.exec(content)) !== null) {
84
+ const id = match[1];
85
+ const dateStr = match[2].trim();
86
+ const body = match[3];
87
+
88
+ entries.push({
89
+ id,
90
+ date: dateStr,
91
+ raw: match[0].trim(),
92
+ type: extractField(body, 'Type'),
93
+ tags: extractField(body, 'Tags'),
94
+ request: extractField(body, 'Request'),
95
+ result: extractField(body, 'Result'),
96
+ files: extractField(body, 'Files')
97
+ });
98
+ }
99
+
100
+ return entries;
101
+ }
102
+
103
+ /**
104
+ * Extract a field value from entry body
105
+ */
106
+ function extractField(text, field) {
107
+ const match = text.match(new RegExp(`\\*\\*${field}\\*\\*:\\s*(.+)`, 'i'));
108
+ return match ? match[1].trim() : null;
109
+ }
110
+
111
+ /**
112
+ * Get the header portion of the log (before entries)
113
+ */
114
+ function getLogHeader(content) {
115
+ // Find where entries start (first ### R-)
116
+ const entryStart = content.indexOf('### R-');
117
+ if (entryStart === -1) {
118
+ return content;
119
+ }
120
+ return content.slice(0, entryStart);
121
+ }
122
+
123
+ // ============================================================
124
+ // Archive Operations
125
+ // ============================================================
126
+
127
+ /**
128
+ * Check if archival is needed based on entry count
129
+ */
130
+ function shouldArchive() {
131
+ const config = getLogManagerConfig();
132
+ if (!config.autoArchive) return false;
133
+
134
+ const currentCount = countRequestLogEntries();
135
+ return currentCount > config.maxRecentEntries;
136
+ }
137
+
138
+ /**
139
+ * Archive old entries and optionally create summary
140
+ * Returns { archived, remaining, archivePath }
141
+ */
142
+ function archiveOldEntries() {
143
+ const config = getLogManagerConfig();
144
+
145
+ if (!fileExists(LOG_PATH)) {
146
+ return { archived: 0, remaining: 0, archivePath: null };
147
+ }
148
+
149
+ const content = readFile(LOG_PATH, '');
150
+ const entries = parseEntries(content);
151
+
152
+ if (entries.length <= config.keepRecent) {
153
+ return { archived: 0, remaining: entries.length, archivePath: null };
154
+ }
155
+
156
+ // Split into archive and keep
157
+ const toArchive = entries.slice(0, entries.length - config.keepRecent);
158
+ const toKeep = entries.slice(-config.keepRecent);
159
+
160
+ // Ensure archive directory exists
161
+ if (!dirExists(ARCHIVE_DIR)) {
162
+ fs.mkdirSync(ARCHIVE_DIR, { recursive: true });
163
+ }
164
+
165
+ // Create archive file (monthly grouping)
166
+ const archiveDate = new Date().toISOString().slice(0, 7); // YYYY-MM
167
+ const archivePath = path.join(ARCHIVE_DIR, `request-log-${archiveDate}.md`);
168
+
169
+ // Prepare archive content
170
+ const archiveContent = toArchive.map(e => e.raw).join('\n\n');
171
+
172
+ // Write to archive first - only update main log if archive succeeds
173
+ try {
174
+ if (fileExists(archivePath)) {
175
+ // Append to existing archive
176
+ const existing = readFile(archivePath, '');
177
+ writeFile(archivePath, existing + '\n\n' + archiveContent);
178
+ } else {
179
+ // Create new archive with header
180
+ const archiveHeader = `# Request Log Archive - ${archiveDate}
181
+
182
+ Archived entries from request-log.md.
183
+
184
+ ---
185
+
186
+ `;
187
+ writeFile(archivePath, archiveHeader + archiveContent);
188
+ }
189
+ } catch (err) {
190
+ // Archive write failed - don't update main log to prevent data loss
191
+ console.error(`Failed to write archive: ${err.message}`);
192
+ return {
193
+ archived: 0,
194
+ remaining: entries.length,
195
+ error: `Archive write failed: ${err.message}`
196
+ };
197
+ }
198
+
199
+ // Update summary if enabled
200
+ if (config.createSummary) {
201
+ updateSummary(toArchive, archiveDate);
202
+ }
203
+
204
+ // Rewrite main log with only recent entries (only after archive succeeded)
205
+ const header = getLogHeader(content);
206
+ const recentContent = header + toKeep.map(e => e.raw).join('\n\n') + '\n';
207
+ writeFile(LOG_PATH, recentContent);
208
+
209
+ return {
210
+ archived: toArchive.length,
211
+ remaining: toKeep.length,
212
+ archivePath
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Update running summary of archived entries
218
+ */
219
+ function updateSummary(archivedEntries, archiveDate) {
220
+ let summary;
221
+
222
+ if (fileExists(SUMMARY_PATH)) {
223
+ summary = readFile(SUMMARY_PATH, '');
224
+ } else {
225
+ summary = `# Request Log Summary
226
+
227
+ Compressed history of archived entries.
228
+ Search archives in \`.workflow/archive/\` for full details.
229
+
230
+ ---
231
+
232
+ ## Archive Summary
233
+
234
+ `;
235
+ }
236
+
237
+ // Group archived entries by type
238
+ const byType = {};
239
+ for (const entry of archivedEntries) {
240
+ const type = entry.type || 'other';
241
+ if (!byType[type]) byType[type] = [];
242
+
243
+ // Extract brief description from request
244
+ let brief = entry.request || entry.id;
245
+ brief = brief.replace(/^["']|["']$/g, ''); // Remove quotes
246
+ if (brief.length > 50) {
247
+ brief = brief.slice(0, 47) + '...';
248
+ }
249
+ byType[type].push(brief);
250
+ }
251
+
252
+ // Create summary section for this archive batch
253
+ const date = new Date().toISOString().split('T')[0];
254
+ let newSummary = `\n### Archived ${date} (${archivedEntries.length} entries)\n`;
255
+
256
+ for (const [type, requests] of Object.entries(byType)) {
257
+ const displayItems = requests.slice(0, 3);
258
+ const remaining = requests.length - displayItems.length;
259
+ const suffix = remaining > 0 ? ` (+${remaining} more)` : '';
260
+ newSummary += `- **${type}**: ${displayItems.join('; ')}${suffix}\n`;
261
+ }
262
+
263
+ writeFile(SUMMARY_PATH, summary + newSummary);
264
+ }
265
+
266
+ // ============================================================
267
+ // Auto-Archive Hook
268
+ // ============================================================
269
+
270
+ /**
271
+ * Automatically archive if needed
272
+ * Safe to call frequently - only archives when threshold exceeded
273
+ * Returns result object or null if no action taken
274
+ */
275
+ function autoArchiveIfNeeded() {
276
+ const config = getLogManagerConfig();
277
+
278
+ if (!config.autoArchive) {
279
+ return null;
280
+ }
281
+
282
+ if (!shouldArchive()) {
283
+ return null;
284
+ }
285
+
286
+ return archiveOldEntries();
287
+ }
288
+
289
+ // ============================================================
290
+ // Search Operations
291
+ // ============================================================
292
+
293
+ /**
294
+ * Search entries in both current log and archives
295
+ * @param {string} query - Search term (tag, type, or text)
296
+ * @param {object} options - { searchArchives, maxResults }
297
+ */
298
+ function searchEntries(query, options = {}) {
299
+ const {
300
+ searchArchives = true,
301
+ maxResults = 20
302
+ } = options;
303
+
304
+ const results = [];
305
+ const queryLower = query.toLowerCase();
306
+
307
+ // Search current log
308
+ if (fileExists(LOG_PATH)) {
309
+ const content = readFile(LOG_PATH, '');
310
+ const entries = parseEntries(content);
311
+
312
+ for (const entry of entries) {
313
+ if (matchesQuery(entry, queryLower)) {
314
+ results.push({ ...entry, source: 'current' });
315
+ }
316
+ }
317
+ }
318
+
319
+ // Search archives
320
+ if (searchArchives && dirExists(ARCHIVE_DIR)) {
321
+ try {
322
+ const archiveFiles = fs.readdirSync(ARCHIVE_DIR)
323
+ .filter(f => f.startsWith('request-log-') && f.endsWith('.md'))
324
+ .sort()
325
+ .reverse(); // Most recent first
326
+
327
+ for (const file of archiveFiles) {
328
+ if (results.length >= maxResults) break;
329
+
330
+ const archivePath = path.join(ARCHIVE_DIR, file);
331
+ const content = readFile(archivePath, '');
332
+ const entries = parseEntries(content);
333
+
334
+ for (const entry of entries) {
335
+ if (results.length >= maxResults) break;
336
+ if (matchesQuery(entry, queryLower)) {
337
+ results.push({ ...entry, source: file });
338
+ }
339
+ }
340
+ }
341
+ } catch {
342
+ // Ignore errors reading archives
343
+ }
344
+ }
345
+
346
+ return results.slice(0, maxResults);
347
+ }
348
+
349
+ /**
350
+ * Check if entry matches search query
351
+ */
352
+ function matchesQuery(entry, queryLower) {
353
+ const searchable = [
354
+ entry.tags,
355
+ entry.type,
356
+ entry.request,
357
+ entry.result,
358
+ entry.raw
359
+ ].filter(Boolean).join(' ').toLowerCase();
360
+
361
+ return searchable.includes(queryLower);
362
+ }
363
+
364
+ // ============================================================
365
+ // Statistics
366
+ // ============================================================
367
+
368
+ /**
369
+ * Get log statistics
370
+ */
371
+ function getLogStats() {
372
+ const stats = {
373
+ currentEntries: countRequestLogEntries(),
374
+ archiveFiles: 0,
375
+ archivedEntries: 0,
376
+ hasSummary: fileExists(SUMMARY_PATH)
377
+ };
378
+
379
+ // Count archive entries
380
+ if (dirExists(ARCHIVE_DIR)) {
381
+ try {
382
+ const archiveFiles = fs.readdirSync(ARCHIVE_DIR)
383
+ .filter(f => f.startsWith('request-log-') && f.endsWith('.md'));
384
+
385
+ stats.archiveFiles = archiveFiles.length;
386
+
387
+ for (const file of archiveFiles) {
388
+ const content = readFile(path.join(ARCHIVE_DIR, file), '');
389
+ const entries = parseEntries(content);
390
+ stats.archivedEntries += entries.length;
391
+ }
392
+ } catch {
393
+ // Ignore errors
394
+ }
395
+ }
396
+
397
+ stats.totalEntries = stats.currentEntries + stats.archivedEntries;
398
+ return stats;
399
+ }
400
+
401
+ // ============================================================
402
+ // CLI Interface
403
+ // ============================================================
404
+
405
+ function printUsage() {
406
+ console.log(`
407
+ Usage: flow-log-manager.js [command] [args]
408
+
409
+ Commands:
410
+ status Show log statistics
411
+ check Check if archiving is needed
412
+ archive Force archive old entries
413
+ search <query> Search entries (current + archives)
414
+ list-archives List archive files
415
+ --help Show this help
416
+
417
+ Examples:
418
+ node scripts/flow-log-manager.js status
419
+ node scripts/flow-log-manager.js search "#component:Button"
420
+ node scripts/flow-log-manager.js archive
421
+ `);
422
+ }
423
+
424
+ // Main CLI handler
425
+ if (require.main === module) {
426
+ const args = process.argv.slice(2);
427
+ const command = args[0];
428
+
429
+ switch (command) {
430
+ case 'status': {
431
+ const stats = getLogStats();
432
+ const config = getLogManagerConfig();
433
+
434
+ printHeader('Request Log Status');
435
+ console.log(`Current entries: ${stats.currentEntries}`);
436
+ console.log(`Archive files: ${stats.archiveFiles}`);
437
+ console.log(`Archived entries: ${stats.archivedEntries}`);
438
+ console.log(`Total entries: ${stats.totalEntries}`);
439
+ console.log(`Summary exists: ${stats.hasSummary ? 'yes' : 'no'}`);
440
+ console.log('');
441
+ console.log(color('dim', `Config: autoArchive=${config.autoArchive}, maxRecent=${config.maxRecentEntries}, keep=${config.keepRecent}`));
442
+ break;
443
+ }
444
+
445
+ case 'check': {
446
+ if (shouldArchive()) {
447
+ const stats = getLogStats();
448
+ const config = getLogManagerConfig();
449
+ warn(`Archive recommended: ${stats.currentEntries} entries (threshold: ${config.maxRecentEntries})`);
450
+ } else {
451
+ success('No archiving needed');
452
+ }
453
+ break;
454
+ }
455
+
456
+ case 'archive': {
457
+ const result = archiveOldEntries();
458
+ if (result.archived > 0) {
459
+ success(`Archived ${result.archived} entries to ${result.archivePath}`);
460
+ console.log(`Remaining in log: ${result.remaining} entries`);
461
+ } else {
462
+ console.log('No entries needed archiving');
463
+ }
464
+ break;
465
+ }
466
+
467
+ case 'search': {
468
+ const query = args.slice(1).join(' ');
469
+ if (!query) {
470
+ error('Please provide a search query');
471
+ process.exit(1);
472
+ }
473
+
474
+ const results = searchEntries(query);
475
+ if (results.length === 0) {
476
+ console.log(`No entries found for: ${query}`);
477
+ } else {
478
+ printHeader(`Search Results: "${query}"`);
479
+ for (const entry of results) {
480
+ console.log(`\n${color('cyan', entry.id)} (${entry.source})`);
481
+ if (entry.type) console.log(` Type: ${entry.type}`);
482
+ if (entry.request) console.log(` Request: ${entry.request}`);
483
+ if (entry.tags) console.log(` Tags: ${entry.tags}`);
484
+ }
485
+ console.log(`\n${results.length} result(s) found`);
486
+ }
487
+ break;
488
+ }
489
+
490
+ case 'list-archives': {
491
+ if (!dirExists(ARCHIVE_DIR)) {
492
+ console.log('No archive directory');
493
+ break;
494
+ }
495
+
496
+ const files = fs.readdirSync(ARCHIVE_DIR)
497
+ .filter(f => f.startsWith('request-log-') && f.endsWith('.md'))
498
+ .sort()
499
+ .reverse();
500
+
501
+ if (files.length === 0) {
502
+ console.log('No archive files');
503
+ } else {
504
+ printHeader('Archive Files');
505
+ for (const file of files) {
506
+ const stat = fs.statSync(path.join(ARCHIVE_DIR, file));
507
+ const size = Math.round(stat.size / 1024);
508
+ console.log(` ${file} (${size}KB)`);
509
+ }
510
+ }
511
+ break;
512
+ }
513
+
514
+ case '--help':
515
+ case '-h':
516
+ printUsage();
517
+ break;
518
+
519
+ default:
520
+ if (command) {
521
+ error(`Unknown command: ${command}`);
522
+ }
523
+ printUsage();
524
+ process.exit(command ? 1 : 0);
525
+ }
526
+ }
527
+
528
+ // ============================================================
529
+ // Exports
530
+ // ============================================================
531
+
532
+ module.exports = {
533
+ // Configuration
534
+ getLogManagerConfig,
535
+ DEFAULTS,
536
+
537
+ // Parsing
538
+ parseEntries,
539
+ extractField,
540
+ getLogHeader,
541
+
542
+ // Archive operations
543
+ shouldArchive,
544
+ archiveOldEntries,
545
+ autoArchiveIfNeeded,
546
+ updateSummary,
547
+
548
+ // Search
549
+ searchEntries,
550
+ matchesQuery,
551
+
552
+ // Statistics
553
+ getLogStats,
554
+
555
+ // Paths
556
+ LOG_PATH,
557
+ ARCHIVE_DIR,
558
+ SUMMARY_PATH
559
+ };