scene-capability-engine 3.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 (336) hide show
  1. package/CHANGELOG.md +2513 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/README.zh.md +630 -0
  5. package/bin/kiro-spec-engine.js +796 -0
  6. package/bin/kse.js +3 -0
  7. package/bin/sce.js +3 -0
  8. package/bin/sco.js +3 -0
  9. package/docs/331-poc-adaptation-roadmap.md +156 -0
  10. package/docs/331-poc-dual-track-integration-guide.md +120 -0
  11. package/docs/331-poc-weekly-delivery-checklist.md +52 -0
  12. package/docs/OFFLINE_INSTALL.md +96 -0
  13. package/docs/README.md +279 -0
  14. package/docs/adopt-migration-guide.md +599 -0
  15. package/docs/adoption-guide.md +616 -0
  16. package/docs/agent-hooks-analysis.md +815 -0
  17. package/docs/architecture.md +733 -0
  18. package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
  19. package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
  20. package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
  21. package/docs/autonomous-control-guide.md +851 -0
  22. package/docs/command-reference.md +1368 -0
  23. package/docs/community.md +115 -0
  24. package/docs/cross-tool-guide.md +555 -0
  25. package/docs/developer-guide.md +619 -0
  26. package/docs/document-governance.md +865 -0
  27. package/docs/environment-management-guide.md +526 -0
  28. package/docs/examples/add-export-command/design.md +194 -0
  29. package/docs/examples/add-export-command/requirements.md +110 -0
  30. package/docs/examples/add-export-command/tasks.md +88 -0
  31. package/docs/examples/add-rest-api/design.md +855 -0
  32. package/docs/examples/add-rest-api/requirements.md +323 -0
  33. package/docs/examples/add-rest-api/tasks.md +355 -0
  34. package/docs/examples/add-user-dashboard/design.md +192 -0
  35. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  36. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  37. package/docs/faq.md +697 -0
  38. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
  39. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
  40. package/docs/images/wechat-qr.png +0 -0
  41. package/docs/integration-modes.md +529 -0
  42. package/docs/integration-philosophy.md +313 -0
  43. package/docs/knowledge-management-guide.md +263 -0
  44. package/docs/manual-workflows-guide.md +418 -0
  45. package/docs/moqui-capability-matrix.md +73 -0
  46. package/docs/moqui-template-core-library-playbook.md +109 -0
  47. package/docs/multi-agent-coordination-guide.md +553 -0
  48. package/docs/multi-repo-management-guide.md +1344 -0
  49. package/docs/quick-start-with-ai-tools.md +375 -0
  50. package/docs/quick-start.md +146 -0
  51. package/docs/release-checklist.md +121 -0
  52. package/docs/releases/README.md +13 -0
  53. package/docs/releases/v1.46.2-validation.md +45 -0
  54. package/docs/releases/v1.46.2.md +50 -0
  55. package/docs/scene-runtime-guide.md +347 -0
  56. package/docs/spec-collaboration-guide.md +369 -0
  57. package/docs/spec-locking-guide.md +225 -0
  58. package/docs/spec-numbering-guide.md +348 -0
  59. package/docs/spec-workflow.md +519 -0
  60. package/docs/steering-strategy-guide.md +196 -0
  61. package/docs/team-collaboration-guide.md +465 -0
  62. package/docs/testing-strategy.md +272 -0
  63. package/docs/tools/claude-guide.md +654 -0
  64. package/docs/tools/cursor-guide.md +706 -0
  65. package/docs/tools/generic-guide.md +446 -0
  66. package/docs/tools/kiro-guide.md +308 -0
  67. package/docs/tools/vscode-guide.md +445 -0
  68. package/docs/tools/windsurf-guide.md +391 -0
  69. package/docs/troubleshooting.md +1135 -0
  70. package/docs/upgrade-guide.md +639 -0
  71. package/docs/value-observability-guide.md +127 -0
  72. package/docs/zh/README.md +341 -0
  73. package/docs/zh/quick-start.md +764 -0
  74. package/docs/zh/release-checklist.md +121 -0
  75. package/docs/zh/releases/README.md +13 -0
  76. package/docs/zh/releases/v1.46.2-validation.md +45 -0
  77. package/docs/zh/releases/v1.46.2.md +50 -0
  78. package/docs/zh/spec-numbering-guide.md +348 -0
  79. package/docs/zh/tools/claude-guide.md +349 -0
  80. package/docs/zh/tools/cursor-guide.md +281 -0
  81. package/docs/zh/tools/generic-guide.md +499 -0
  82. package/docs/zh/tools/kiro-guide.md +342 -0
  83. package/docs/zh/tools/vscode-guide.md +449 -0
  84. package/docs/zh/tools/windsurf-guide.md +378 -0
  85. package/docs/zh/value-observability-guide.md +127 -0
  86. package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
  87. package/lib/adoption/adoption-logger.js +487 -0
  88. package/lib/adoption/adoption-strategy.js +538 -0
  89. package/lib/adoption/backup-manager.js +420 -0
  90. package/lib/adoption/conflict-resolver.js +410 -0
  91. package/lib/adoption/detection-engine.js +275 -0
  92. package/lib/adoption/diff-viewer.js +226 -0
  93. package/lib/adoption/error-formatter.js +509 -0
  94. package/lib/adoption/file-classifier.js +385 -0
  95. package/lib/adoption/progress-reporter.js +534 -0
  96. package/lib/adoption/smart-orchestrator.js +470 -0
  97. package/lib/adoption/strategy-selector.js +218 -0
  98. package/lib/adoption/summary-generator.js +493 -0
  99. package/lib/adoption/template-sync.js +605 -0
  100. package/lib/auto/autonomous-engine.js +485 -0
  101. package/lib/auto/checkpoint-manager.js +300 -0
  102. package/lib/auto/close-loop-runner.js +2476 -0
  103. package/lib/auto/config-schema.js +176 -0
  104. package/lib/auto/decision-engine.js +344 -0
  105. package/lib/auto/error-recovery-manager.js +580 -0
  106. package/lib/auto/goal-decomposer.js +278 -0
  107. package/lib/auto/progress-tracker.js +502 -0
  108. package/lib/auto/safety-manager.js +186 -0
  109. package/lib/auto/semantic-decomposer.js +137 -0
  110. package/lib/auto/state-manager.js +126 -0
  111. package/lib/auto/task-queue-manager.js +340 -0
  112. package/lib/backup/backup-system.js +372 -0
  113. package/lib/backup/selective-backup.js +207 -0
  114. package/lib/collab/agent-registry.js +240 -0
  115. package/lib/collab/collab-manager.js +285 -0
  116. package/lib/collab/contract-manager.js +320 -0
  117. package/lib/collab/coordinator.js +370 -0
  118. package/lib/collab/dependency-manager.js +280 -0
  119. package/lib/collab/index.js +20 -0
  120. package/lib/collab/integration-manager.js +202 -0
  121. package/lib/collab/merge-coordinator.js +252 -0
  122. package/lib/collab/metadata-manager.js +233 -0
  123. package/lib/collab/multi-agent-config.js +120 -0
  124. package/lib/collab/spec-lifecycle-manager.js +304 -0
  125. package/lib/collab/sync-barrier.js +88 -0
  126. package/lib/collab/visualizer.js +208 -0
  127. package/lib/commands/adopt.js +749 -0
  128. package/lib/commands/auto.js +19559 -0
  129. package/lib/commands/collab.js +275 -0
  130. package/lib/commands/context.js +99 -0
  131. package/lib/commands/docs.js +808 -0
  132. package/lib/commands/doctor.js +273 -0
  133. package/lib/commands/env.js +420 -0
  134. package/lib/commands/knowledge.js +309 -0
  135. package/lib/commands/lock.js +235 -0
  136. package/lib/commands/ops.js +409 -0
  137. package/lib/commands/orchestrate.js +446 -0
  138. package/lib/commands/prompt.js +105 -0
  139. package/lib/commands/repo.js +118 -0
  140. package/lib/commands/rollback.js +219 -0
  141. package/lib/commands/scene.js +15549 -0
  142. package/lib/commands/spec-bootstrap.js +147 -0
  143. package/lib/commands/spec-gate.js +157 -0
  144. package/lib/commands/spec-pipeline.js +205 -0
  145. package/lib/commands/status.js +321 -0
  146. package/lib/commands/task.js +199 -0
  147. package/lib/commands/templates.js +654 -0
  148. package/lib/commands/upgrade.js +231 -0
  149. package/lib/commands/value.js +569 -0
  150. package/lib/commands/watch.js +684 -0
  151. package/lib/commands/workflows.js +240 -0
  152. package/lib/commands/workspace-multi.js +325 -0
  153. package/lib/commands/workspace.js +189 -0
  154. package/lib/context/context-exporter.js +378 -0
  155. package/lib/context/prompt-generator.js +482 -0
  156. package/lib/data/moqui-capability-lexicon.json +45 -0
  157. package/lib/environment/backup-system.js +189 -0
  158. package/lib/environment/environment-manager.js +379 -0
  159. package/lib/environment/environment-registry.js +168 -0
  160. package/lib/gitignore/gitignore-backup.js +229 -0
  161. package/lib/gitignore/gitignore-detector.js +239 -0
  162. package/lib/gitignore/gitignore-integration.js +267 -0
  163. package/lib/gitignore/gitignore-transformer.js +193 -0
  164. package/lib/gitignore/layered-rules-template.js +42 -0
  165. package/lib/governance/archive-tool.js +284 -0
  166. package/lib/governance/cleanup-tool.js +237 -0
  167. package/lib/governance/config-manager.js +186 -0
  168. package/lib/governance/diagnostic-engine.js +271 -0
  169. package/lib/governance/doc-reference-checker.js +200 -0
  170. package/lib/governance/execution-logger.js +243 -0
  171. package/lib/governance/file-scanner.js +285 -0
  172. package/lib/governance/hooks-manager.js +333 -0
  173. package/lib/governance/reporter.js +337 -0
  174. package/lib/governance/validation-engine.js +181 -0
  175. package/lib/i18n.js +79 -0
  176. package/lib/knowledge/entry-manager.js +208 -0
  177. package/lib/knowledge/index-manager.js +261 -0
  178. package/lib/knowledge/knowledge-manager.js +273 -0
  179. package/lib/knowledge/template-manager.js +191 -0
  180. package/lib/lock/index.js +21 -0
  181. package/lib/lock/lock-file.js +192 -0
  182. package/lib/lock/lock-manager.js +321 -0
  183. package/lib/lock/machine-identifier.js +135 -0
  184. package/lib/lock/steering-file-lock.js +207 -0
  185. package/lib/lock/task-lock-manager.js +345 -0
  186. package/lib/operations/audit-logger.js +293 -0
  187. package/lib/operations/feedback-manager.js +1147 -0
  188. package/lib/operations/index.js +23 -0
  189. package/lib/operations/models/index.js +170 -0
  190. package/lib/operations/operations-manager.js +151 -0
  191. package/lib/operations/operations-validator.js +280 -0
  192. package/lib/operations/permission-manager.js +354 -0
  193. package/lib/operations/template-loader.js +143 -0
  194. package/lib/orchestrator/agent-spawner.js +629 -0
  195. package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
  196. package/lib/orchestrator/index.js +19 -0
  197. package/lib/orchestrator/orchestration-engine.js +1270 -0
  198. package/lib/orchestrator/orchestrator-config.js +173 -0
  199. package/lib/orchestrator/status-monitor.js +591 -0
  200. package/lib/python-checker.js +209 -0
  201. package/lib/repo/config-manager.js +580 -0
  202. package/lib/repo/errors/config-error.js +13 -0
  203. package/lib/repo/errors/git-error.js +15 -0
  204. package/lib/repo/errors/repo-error.js +14 -0
  205. package/lib/repo/git-operations.js +181 -0
  206. package/lib/repo/handlers/.gitkeep +1 -0
  207. package/lib/repo/handlers/exec-handler.js +155 -0
  208. package/lib/repo/handlers/health-handler.js +169 -0
  209. package/lib/repo/handlers/init-handler.js +197 -0
  210. package/lib/repo/handlers/status-handler.js +176 -0
  211. package/lib/repo/output-formatter.js +184 -0
  212. package/lib/repo/path-resolver.js +178 -0
  213. package/lib/repo/repo-manager.js +514 -0
  214. package/lib/scene-runtime/audit-emitter.js +59 -0
  215. package/lib/scene-runtime/binding-plugin-loader.js +351 -0
  216. package/lib/scene-runtime/binding-registry.js +349 -0
  217. package/lib/scene-runtime/eval-bridge.js +44 -0
  218. package/lib/scene-runtime/index.js +19 -0
  219. package/lib/scene-runtime/moqui-adapter.js +620 -0
  220. package/lib/scene-runtime/moqui-client.js +606 -0
  221. package/lib/scene-runtime/moqui-extractor.js +2029 -0
  222. package/lib/scene-runtime/plan-compiler.js +208 -0
  223. package/lib/scene-runtime/policy-gate.js +58 -0
  224. package/lib/scene-runtime/runtime-executor.js +358 -0
  225. package/lib/scene-runtime/scene-loader.js +96 -0
  226. package/lib/scene-runtime/scene-ontology.js +959 -0
  227. package/lib/scene-runtime/scene-template-linter.js +852 -0
  228. package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
  229. package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
  230. package/lib/spec/bootstrap/context-collector.js +48 -0
  231. package/lib/spec/bootstrap/draft-generator.js +158 -0
  232. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  233. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  234. package/lib/spec/multi-spec-orchestrate.js +93 -0
  235. package/lib/spec/pipeline/constants.js +6 -0
  236. package/lib/spec/pipeline/stage-adapters.js +118 -0
  237. package/lib/spec/pipeline/stage-runner.js +146 -0
  238. package/lib/spec/pipeline/state-store.js +119 -0
  239. package/lib/spec-gate/engine/gate-engine.js +165 -0
  240. package/lib/spec-gate/policy/default-policy.js +22 -0
  241. package/lib/spec-gate/policy/policy-loader.js +103 -0
  242. package/lib/spec-gate/result-emitter.js +81 -0
  243. package/lib/spec-gate/rules/default-rules.js +156 -0
  244. package/lib/spec-gate/rules/rule-registry.js +51 -0
  245. package/lib/steering/adoption-config.js +164 -0
  246. package/lib/steering/compliance-auto-fixer.js +204 -0
  247. package/lib/steering/compliance-cache.js +99 -0
  248. package/lib/steering/compliance-error-reporter.js +70 -0
  249. package/lib/steering/context-sync-manager.js +273 -0
  250. package/lib/steering/index.js +92 -0
  251. package/lib/steering/spec-steering.js +230 -0
  252. package/lib/steering/steering-compliance-checker.js +73 -0
  253. package/lib/steering/steering-loader.js +144 -0
  254. package/lib/steering/steering-manager.js +289 -0
  255. package/lib/task/index.js +12 -0
  256. package/lib/task/task-claimer.js +489 -0
  257. package/lib/task/task-status-store.js +418 -0
  258. package/lib/templates/cache-manager.js +440 -0
  259. package/lib/templates/content-generalizer.js +247 -0
  260. package/lib/templates/frontmatter-generator.js +128 -0
  261. package/lib/templates/git-handler.js +471 -0
  262. package/lib/templates/metadata-collector.js +328 -0
  263. package/lib/templates/path-utils.js +144 -0
  264. package/lib/templates/registry-parser.js +505 -0
  265. package/lib/templates/spec-reader.js +216 -0
  266. package/lib/templates/template-applicator.js +249 -0
  267. package/lib/templates/template-creator.js +256 -0
  268. package/lib/templates/template-error.js +143 -0
  269. package/lib/templates/template-exporter.js +502 -0
  270. package/lib/templates/template-manager.js +782 -0
  271. package/lib/templates/template-validator.js +361 -0
  272. package/lib/upgrade/migration-engine.js +382 -0
  273. package/lib/upgrade/migrations/.gitkeep +52 -0
  274. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
  275. package/lib/utils/file-diff.js +177 -0
  276. package/lib/utils/fs-utils.js +274 -0
  277. package/lib/utils/tool-detector.js +383 -0
  278. package/lib/utils/validation.js +324 -0
  279. package/lib/value/gate-summary-emitter.js +99 -0
  280. package/lib/value/metric-contract-loader.js +210 -0
  281. package/lib/value/risk-evaluator.js +117 -0
  282. package/lib/value/weekly-snapshot-builder.js +61 -0
  283. package/lib/version/version-checker.js +156 -0
  284. package/lib/version/version-manager.js +327 -0
  285. package/lib/watch/action-executor.js +458 -0
  286. package/lib/watch/event-debouncer.js +323 -0
  287. package/lib/watch/execution-logger.js +550 -0
  288. package/lib/watch/file-watcher.js +499 -0
  289. package/lib/watch/presets.js +266 -0
  290. package/lib/watch/watch-manager.js +533 -0
  291. package/lib/workspace/multi/global-config.js +150 -0
  292. package/lib/workspace/multi/index.js +22 -0
  293. package/lib/workspace/multi/path-utils.js +173 -0
  294. package/lib/workspace/multi/workspace-context-resolver.js +244 -0
  295. package/lib/workspace/multi/workspace-registry.js +196 -0
  296. package/lib/workspace/multi/workspace-state-manager.js +537 -0
  297. package/lib/workspace/multi/workspace.js +90 -0
  298. package/lib/workspace/workspace-manager.js +370 -0
  299. package/lib/workspace/workspace-sync.js +356 -0
  300. package/locales/en.json +114 -0
  301. package/locales/zh.json +114 -0
  302. package/package.json +102 -0
  303. package/template/.kiro/README.md +247 -0
  304. package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
  305. package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
  306. package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
  307. package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
  308. package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
  309. package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
  310. package/template/.kiro/steering/ENVIRONMENT.md +35 -0
  311. package/template/.kiro/steering/RULES_GUIDE.md +46 -0
  312. package/template/.kiro/templates/operations/default/change-impact.md +112 -0
  313. package/template/.kiro/templates/operations/default/deployment.md +91 -0
  314. package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
  315. package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
  316. package/template/.kiro/templates/operations/default/monitoring.md +135 -0
  317. package/template/.kiro/templates/operations/default/operations.md +135 -0
  318. package/template/.kiro/templates/operations/default/rollback.md +143 -0
  319. package/template/.kiro/templates/operations/default/tools.yaml +364 -0
  320. package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
  321. package/template/.kiro/tools/backup_manager.py +295 -0
  322. package/template/.kiro/tools/configuration_manager.py +218 -0
  323. package/template/.kiro/tools/document_evaluator.py +550 -0
  324. package/template/.kiro/tools/enhancement_logger.py +168 -0
  325. package/template/.kiro/tools/error_handler.py +335 -0
  326. package/template/.kiro/tools/improvement_identifier.py +444 -0
  327. package/template/.kiro/tools/modification_applicator.py +737 -0
  328. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  329. package/template/.kiro/tools/quality_scorer.py +305 -0
  330. package/template/.kiro/tools/report_generator.py +154 -0
  331. package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
  332. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  333. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  334. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  335. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
  336. package/template/README.md +111 -0
@@ -0,0 +1,1147 @@
1
+ /**
2
+ * Feedback Manager
3
+ *
4
+ * Manages user feedback collection, classification, routing, and resolution tracking.
5
+ * Integrates with operations specs to improve operational procedures based on feedback.
6
+ */
7
+
8
+ const path = require('path');
9
+ const crypto = require('crypto');
10
+ const {
11
+ FeedbackChannel,
12
+ FeedbackType,
13
+ FeedbackSeverity,
14
+ FeedbackStatus
15
+ } = require('./models');
16
+ const {
17
+ pathExists,
18
+ ensureDirectory,
19
+ readJSON,
20
+ writeJSON
21
+ } = require('../utils/fs-utils');
22
+
23
+ /**
24
+ * Generate UUID v4
25
+ * @returns {string} UUID
26
+ */
27
+ function generateUUID() {
28
+ return crypto.randomUUID();
29
+ }
30
+
31
+ /**
32
+ * FeedbackManager class
33
+ */
34
+ class FeedbackManager {
35
+ constructor(projectPath) {
36
+ this.projectPath = projectPath;
37
+ this.feedbackDir = path.join(projectPath, '.kiro/feedback');
38
+ this.feedbackFile = path.join(this.feedbackDir, 'feedback.json');
39
+ }
40
+
41
+ /**
42
+ * Receive feedback from a channel
43
+ *
44
+ * @param {string} channel - Feedback channel (from FeedbackChannel enum)
45
+ * @param {Object} content - Feedback content
46
+ * @param {string} content.title - Feedback title
47
+ * @param {string} content.description - Detailed description
48
+ * @param {string} content.project - Project name
49
+ * @param {string} content.version - Project version
50
+ * @param {Object} content.metadata - Additional metadata
51
+ * @returns {Promise<Object>} Created feedback object
52
+ */
53
+ async receiveFeedback(channel, content) {
54
+ // Validate channel
55
+ if (!Object.values(FeedbackChannel).includes(channel)) {
56
+ throw new Error(`Invalid feedback channel: ${channel}`);
57
+ }
58
+
59
+ // Validate required content fields
60
+ if (!content.title || !content.description) {
61
+ throw new Error('Feedback must include title and description');
62
+ }
63
+
64
+ if (!content.project) {
65
+ throw new Error('Feedback must include project name');
66
+ }
67
+
68
+ // Create feedback object
69
+ const feedback = {
70
+ id: generateUUID(),
71
+ channel,
72
+ type: null, // Will be classified
73
+ severity: null, // Will be classified
74
+ status: FeedbackStatus.ACKNOWLEDGED,
75
+ project: content.project,
76
+ version: content.version || 'unknown',
77
+ content: {
78
+ title: content.title,
79
+ description: content.description,
80
+ metadata: content.metadata || {}
81
+ },
82
+ classification: null, // Will be set by classifier
83
+ resolution: null,
84
+ createdAt: new Date().toISOString(),
85
+ updatedAt: new Date().toISOString()
86
+ };
87
+
88
+ // Classify feedback
89
+ const classification = this.classifyFeedback(feedback);
90
+ feedback.type = classification.type;
91
+ feedback.severity = classification.severity;
92
+ feedback.classification = classification;
93
+
94
+ // Save feedback
95
+ await this.saveFeedback(feedback);
96
+
97
+ // Route feedback based on severity
98
+ await this.routeFeedback(feedback);
99
+
100
+ return feedback;
101
+ }
102
+
103
+ /**
104
+ * Classify feedback by type and severity
105
+ *
106
+ * @param {Object} feedback - Feedback object
107
+ * @returns {Object} Classification result
108
+ */
109
+ classifyFeedback(feedback) {
110
+ const content = feedback.content;
111
+ const text = `${content.title} ${content.description}`.toLowerCase();
112
+
113
+ // Classify type based on keywords
114
+ let type = FeedbackType.OPERATIONAL_CONCERN; // Default
115
+ let confidence = 0.5;
116
+
117
+ if (text.includes('bug') || text.includes('error') || text.includes('crash') || text.includes('fail')) {
118
+ type = FeedbackType.BUG_REPORT;
119
+ confidence = 0.9;
120
+ } else if (text.includes('slow') || text.includes('performance') || text.includes('timeout') || text.includes('latency')) {
121
+ type = FeedbackType.PERFORMANCE_ISSUE;
122
+ confidence = 0.85;
123
+ } else if (text.includes('feature') || text.includes('enhancement') || text.includes('request') || text.includes('add')) {
124
+ type = FeedbackType.FEATURE_REQUEST;
125
+ confidence = 0.8;
126
+ }
127
+
128
+ // Classify severity based on keywords and channel
129
+ let severity = FeedbackSeverity.MEDIUM; // Default
130
+
131
+ if (text.includes('down') || text.includes('critical') || text.includes('urgent') || text.includes('production')) {
132
+ severity = FeedbackSeverity.CRITICAL;
133
+ } else if (text.includes('high priority') || text.includes('important') || text.includes('degraded')) {
134
+ severity = FeedbackSeverity.HIGH;
135
+ } else if (text.includes('minor') || text.includes('low priority') || text.includes('enhancement')) {
136
+ severity = FeedbackSeverity.LOW;
137
+ }
138
+
139
+ // Monitoring alerts are typically high severity
140
+ if (feedback.channel === FeedbackChannel.MONITORING_ALERT) {
141
+ if (severity === FeedbackSeverity.MEDIUM) {
142
+ severity = FeedbackSeverity.HIGH;
143
+ }
144
+ }
145
+
146
+ return {
147
+ type,
148
+ severity,
149
+ confidence,
150
+ classifiedAt: new Date().toISOString()
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Route feedback to appropriate handler
156
+ *
157
+ * @param {Object} feedback - Feedback object
158
+ * @returns {Promise<void>}
159
+ */
160
+ async routeFeedback(feedback) {
161
+ // Critical feedback triggers immediate response
162
+ if (feedback.severity === FeedbackSeverity.CRITICAL) {
163
+ await this.triggerCriticalResponse(feedback);
164
+ }
165
+
166
+ // Log routing action
167
+ const routingLog = {
168
+ feedbackId: feedback.id,
169
+ severity: feedback.severity,
170
+ type: feedback.type,
171
+ action: feedback.severity === FeedbackSeverity.CRITICAL ? 'triggered_critical_response' : 'queued_for_review',
172
+ timestamp: new Date().toISOString()
173
+ };
174
+
175
+ await this.logRoutingAction(routingLog);
176
+ }
177
+
178
+ /**
179
+ * Trigger critical feedback response
180
+ *
181
+ * @param {Object} feedback - Critical feedback object
182
+ * @returns {Promise<void>}
183
+ */
184
+ async triggerCriticalResponse(feedback) {
185
+ // Create response record
186
+ const response = {
187
+ feedbackId: feedback.id,
188
+ type: 'critical_response',
189
+ actions: [
190
+ 'Escalated to on-call team',
191
+ 'Triggered troubleshooting procedure',
192
+ 'Notified stakeholders'
193
+ ],
194
+ triggeredAt: new Date().toISOString()
195
+ };
196
+
197
+ // Save response
198
+ await this.saveCriticalResponse(response);
199
+
200
+ // Update feedback status (only if feedback is persisted)
201
+ try {
202
+ const existingFeedback = await this.getFeedback(feedback.id);
203
+ if (existingFeedback) {
204
+ await this.trackResolution(feedback.id, FeedbackStatus.INVESTIGATING);
205
+ }
206
+ } catch (error) {
207
+ // Feedback not yet persisted, skip status update
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Track feedback resolution
213
+ *
214
+ * @param {string} feedbackId - Feedback ID
215
+ * @param {string} status - New status (from FeedbackStatus enum)
216
+ * @param {string} resolution - Resolution description (optional)
217
+ * @returns {Promise<void>}
218
+ */
219
+ async trackResolution(feedbackId, status, resolution = null) {
220
+ // Validate status
221
+ if (!Object.values(FeedbackStatus).includes(status)) {
222
+ throw new Error(`Invalid feedback status: ${status}`);
223
+ }
224
+
225
+ // Load feedback
226
+ const feedbacks = await this.loadFeedbacks();
227
+ const feedback = feedbacks.find(f => f.id === feedbackId);
228
+
229
+ if (!feedback) {
230
+ throw new Error(`Feedback not found: ${feedbackId}`);
231
+ }
232
+
233
+ // Validate state transition
234
+ this.validateStateTransition(feedback.status, status);
235
+
236
+ // Update feedback
237
+ feedback.status = status;
238
+ feedback.updatedAt = new Date().toISOString();
239
+
240
+ if (resolution) {
241
+ feedback.resolution = {
242
+ description: resolution,
243
+ resolvedAt: new Date().toISOString()
244
+ };
245
+ }
246
+
247
+ // Save updated feedback
248
+ await this.saveFeedbacks(feedbacks);
249
+ }
250
+
251
+ /**
252
+ * Validate feedback state transition
253
+ *
254
+ * @param {string} currentStatus - Current status
255
+ * @param {string} newStatus - New status
256
+ * @throws {Error} If transition is invalid
257
+ */
258
+ validateStateTransition(currentStatus, newStatus) {
259
+ const validTransitions = {
260
+ [FeedbackStatus.ACKNOWLEDGED]: [FeedbackStatus.INVESTIGATING, FeedbackStatus.RESOLVED],
261
+ [FeedbackStatus.INVESTIGATING]: [FeedbackStatus.RESOLVED, FeedbackStatus.ACKNOWLEDGED],
262
+ [FeedbackStatus.RESOLVED]: [FeedbackStatus.VERIFIED, FeedbackStatus.INVESTIGATING],
263
+ [FeedbackStatus.VERIFIED]: [] // Terminal state
264
+ };
265
+
266
+ const allowed = validTransitions[currentStatus] || [];
267
+
268
+ if (!allowed.includes(newStatus) && currentStatus !== newStatus) {
269
+ throw new Error(`Invalid state transition: ${currentStatus} -> ${newStatus}`);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Get feedback by ID
275
+ *
276
+ * @param {string} feedbackId - Feedback ID
277
+ * @returns {Promise<Object|null>} Feedback object or null
278
+ */
279
+ async getFeedback(feedbackId) {
280
+ const feedbacks = await this.loadFeedbacks();
281
+ return feedbacks.find(f => f.id === feedbackId) || null;
282
+ }
283
+
284
+ /**
285
+ * List feedbacks with optional filters
286
+ *
287
+ * @param {Object} filters - Filter criteria
288
+ * @param {string} filters.project - Filter by project
289
+ * @param {string} filters.severity - Filter by severity
290
+ * @param {string} filters.status - Filter by status
291
+ * @param {string} filters.type - Filter by type
292
+ * @returns {Promise<Array>} Array of feedback objects
293
+ */
294
+ async listFeedbacks(filters = {}) {
295
+ let feedbacks = await this.loadFeedbacks();
296
+
297
+ // Apply filters
298
+ if (filters.project) {
299
+ feedbacks = feedbacks.filter(f => f.project === filters.project);
300
+ }
301
+
302
+ if (filters.severity) {
303
+ feedbacks = feedbacks.filter(f => f.severity === filters.severity);
304
+ }
305
+
306
+ if (filters.status) {
307
+ feedbacks = feedbacks.filter(f => f.status === filters.status);
308
+ }
309
+
310
+ if (filters.type) {
311
+ feedbacks = feedbacks.filter(f => f.type === filters.type);
312
+ }
313
+
314
+ return feedbacks;
315
+ }
316
+
317
+ /**
318
+ * Link feedback to project version
319
+ *
320
+ * @param {string} feedbackId - Feedback ID
321
+ * @param {string} version - Project version
322
+ * @returns {Promise<void>}
323
+ */
324
+ async linkToVersion(feedbackId, version) {
325
+ const feedbacks = await this.loadFeedbacks();
326
+ const feedback = feedbacks.find(f => f.id === feedbackId);
327
+
328
+ if (!feedback) {
329
+ throw new Error(`Feedback not found: ${feedbackId}`);
330
+ }
331
+
332
+ feedback.version = version;
333
+ feedback.updatedAt = new Date().toISOString();
334
+
335
+ await this.saveFeedbacks(feedbacks);
336
+ }
337
+
338
+ /**
339
+ * Save feedback to storage
340
+ *
341
+ * @param {Object} feedback - Feedback object
342
+ * @returns {Promise<void>}
343
+ */
344
+ async saveFeedback(feedback) {
345
+ const feedbacks = await this.loadFeedbacks();
346
+ feedbacks.push(feedback);
347
+ await this.saveFeedbacks(feedbacks);
348
+ }
349
+
350
+ /**
351
+ * Load all feedbacks from storage
352
+ *
353
+ * @returns {Promise<Array>} Array of feedback objects
354
+ */
355
+ async loadFeedbacks() {
356
+ await ensureDirectory(this.feedbackDir);
357
+
358
+ const exists = await pathExists(this.feedbackFile);
359
+ if (!exists) {
360
+ return [];
361
+ }
362
+
363
+ try {
364
+ const data = await readJSON(this.feedbackFile);
365
+ return data.feedbacks || [];
366
+ } catch (error) {
367
+ console.warn(`Warning: Could not load feedbacks: ${error.message}`);
368
+ return [];
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Save feedbacks to storage
374
+ *
375
+ * @param {Array} feedbacks - Array of feedback objects
376
+ * @returns {Promise<void>}
377
+ */
378
+ async saveFeedbacks(feedbacks) {
379
+ await ensureDirectory(this.feedbackDir);
380
+
381
+ const data = {
382
+ feedbacks,
383
+ lastUpdated: new Date().toISOString()
384
+ };
385
+
386
+ await writeJSON(this.feedbackFile, data);
387
+ }
388
+
389
+ /**
390
+ * Log routing action
391
+ *
392
+ * @param {Object} routingLog - Routing log entry
393
+ * @returns {Promise<void>}
394
+ */
395
+ async logRoutingAction(routingLog) {
396
+ const logFile = path.join(this.feedbackDir, 'routing-log.json');
397
+ await ensureDirectory(this.feedbackDir);
398
+
399
+ let logs = [];
400
+ const exists = await pathExists(logFile);
401
+ if (exists) {
402
+ try {
403
+ const data = await readJSON(logFile);
404
+ logs = data.logs || [];
405
+ } catch (error) {
406
+ // Ignore errors, start fresh
407
+ }
408
+ }
409
+
410
+ logs.push(routingLog);
411
+
412
+ await writeJSON(logFile, { logs, lastUpdated: new Date().toISOString() });
413
+ }
414
+
415
+ /**
416
+ * Save critical response
417
+ *
418
+ * @param {Object} response - Critical response object
419
+ * @returns {Promise<void>}
420
+ */
421
+ async saveCriticalResponse(response) {
422
+ const responseFile = path.join(this.feedbackDir, 'critical-responses.json');
423
+ await ensureDirectory(this.feedbackDir);
424
+
425
+ let responses = [];
426
+ const exists = await pathExists(responseFile);
427
+ if (exists) {
428
+ try {
429
+ const data = await readJSON(responseFile);
430
+ responses = data.responses || [];
431
+ } catch (error) {
432
+ // Ignore errors, start fresh
433
+ }
434
+ }
435
+
436
+ responses.push(response);
437
+
438
+ await writeJSON(responseFile, { responses, lastUpdated: new Date().toISOString() });
439
+ }
440
+
441
+ /**
442
+ * Generate feedback analytics
443
+ *
444
+ * @param {string} project - Project name
445
+ * @param {Object} timeRange - Time range for analytics
446
+ * @param {string} timeRange.from - Start date (ISO)
447
+ * @param {string} timeRange.to - End date (ISO)
448
+ * @returns {Promise<Object>} FeedbackAnalytics object
449
+ */
450
+ async generateAnalytics(project, timeRange) {
451
+ // Load all feedbacks for the project
452
+ const allFeedbacks = await this.listFeedbacks({ project });
453
+
454
+ // Filter by time range
455
+ const fromDate = new Date(timeRange.from);
456
+ const toDate = new Date(timeRange.to);
457
+
458
+ const feedbacks = allFeedbacks.filter(f => {
459
+ const createdAt = new Date(f.createdAt);
460
+ return createdAt >= fromDate && createdAt <= toDate;
461
+ });
462
+
463
+ // Generate analytics components
464
+ const commonIssues = this._analyzeCommonIssues(feedbacks);
465
+ const resolutionTimes = this._calculateResolutionTimes(feedbacks);
466
+ const satisfactionTrends = this._trackSatisfactionTrends(feedbacks, fromDate, toDate);
467
+ const versionSpecificIssues = this._identifyVersionIssues(feedbacks);
468
+
469
+ // Calculate status distribution
470
+ const statusDistribution = {};
471
+ Object.values(FeedbackStatus).forEach(status => {
472
+ statusDistribution[status] = feedbacks.filter(f => f.status === status).length;
473
+ });
474
+
475
+ return {
476
+ project,
477
+ generatedAt: new Date().toISOString(),
478
+ timeRange: {
479
+ from: timeRange.from,
480
+ to: timeRange.to
481
+ },
482
+ commonIssues,
483
+ resolutionTimes,
484
+ satisfactionTrends,
485
+ versionSpecificIssues,
486
+ totalFeedback: feedbacks.length,
487
+ statusDistribution
488
+ };
489
+ }
490
+
491
+ /**
492
+ * Analyze common issue patterns
493
+ *
494
+ * @param {Array} feedbacks - Array of feedback objects
495
+ * @returns {Array} Array of IssuePattern objects
496
+ * @private
497
+ */
498
+ _analyzeCommonIssues(feedbacks) {
499
+ // Group feedbacks by type and severity
500
+ const patterns = new Map();
501
+
502
+ feedbacks.forEach(feedback => {
503
+ const key = `${feedback.type}_${feedback.severity}`;
504
+
505
+ if (!patterns.has(key)) {
506
+ patterns.set(key, {
507
+ pattern: `${feedback.type} with ${feedback.severity} severity`,
508
+ occurrences: 0,
509
+ type: feedback.type,
510
+ severity: feedback.severity,
511
+ affectedVersions: new Set(),
512
+ firstSeen: feedback.createdAt,
513
+ lastSeen: feedback.createdAt
514
+ });
515
+ }
516
+
517
+ const pattern = patterns.get(key);
518
+ pattern.occurrences++;
519
+ pattern.affectedVersions.add(feedback.version);
520
+
521
+ // Update first/last seen
522
+ if (new Date(feedback.createdAt) < new Date(pattern.firstSeen)) {
523
+ pattern.firstSeen = feedback.createdAt;
524
+ }
525
+ if (new Date(feedback.createdAt) > new Date(pattern.lastSeen)) {
526
+ pattern.lastSeen = feedback.createdAt;
527
+ }
528
+ });
529
+
530
+ // Convert to array and sort by occurrences
531
+ const issuePatterns = Array.from(patterns.values())
532
+ .map(p => ({
533
+ ...p,
534
+ affectedVersions: Array.from(p.affectedVersions)
535
+ }))
536
+ .sort((a, b) => b.occurrences - a.occurrences)
537
+ .slice(0, 10); // Top 10 issues
538
+
539
+ return issuePatterns;
540
+ }
541
+
542
+ /**
543
+ * Calculate resolution time statistics
544
+ *
545
+ * @param {Array} feedbacks - Array of feedback objects
546
+ * @returns {Object} ResolutionTimeStats object
547
+ * @private
548
+ */
549
+ _calculateResolutionTimes(feedbacks) {
550
+ // Filter resolved feedbacks
551
+ const resolvedFeedbacks = feedbacks.filter(f =>
552
+ f.resolution && f.resolution.resolvedAt
553
+ );
554
+
555
+ if (resolvedFeedbacks.length === 0) {
556
+ return {
557
+ average: 0,
558
+ median: 0,
559
+ min: 0,
560
+ max: 0,
561
+ bySeverity: {},
562
+ byType: {}
563
+ };
564
+ }
565
+
566
+ // Calculate resolution times in hours
567
+ const resolutionTimes = resolvedFeedbacks.map(f => {
568
+ const created = new Date(f.createdAt);
569
+ const resolved = new Date(f.resolution.resolvedAt);
570
+ return (resolved - created) / (1000 * 60 * 60); // Convert to hours
571
+ });
572
+
573
+ // Calculate statistics
574
+ const sorted = resolutionTimes.sort((a, b) => a - b);
575
+ const average = resolutionTimes.reduce((sum, t) => sum + t, 0) / resolutionTimes.length;
576
+ const median = sorted[Math.floor(sorted.length / 2)];
577
+ const min = sorted[0];
578
+ const max = sorted[sorted.length - 1];
579
+
580
+ // Calculate by severity
581
+ const bySeverity = {};
582
+ Object.values(FeedbackSeverity).forEach(severity => {
583
+ const severityFeedbacks = resolvedFeedbacks.filter(f => f.severity === severity);
584
+ if (severityFeedbacks.length > 0) {
585
+ const times = severityFeedbacks.map(f => {
586
+ const created = new Date(f.createdAt);
587
+ const resolved = new Date(f.resolution.resolvedAt);
588
+ return (resolved - created) / (1000 * 60 * 60);
589
+ });
590
+ bySeverity[severity] = times.reduce((sum, t) => sum + t, 0) / times.length;
591
+ }
592
+ });
593
+
594
+ // Calculate by type
595
+ const byType = {};
596
+ Object.values(FeedbackType).forEach(type => {
597
+ const typeFeedbacks = resolvedFeedbacks.filter(f => f.type === type);
598
+ if (typeFeedbacks.length > 0) {
599
+ const times = typeFeedbacks.map(f => {
600
+ const created = new Date(f.createdAt);
601
+ const resolved = new Date(f.resolution.resolvedAt);
602
+ return (resolved - created) / (1000 * 60 * 60);
603
+ });
604
+ byType[type] = times.reduce((sum, t) => sum + t, 0) / times.length;
605
+ }
606
+ });
607
+
608
+ return {
609
+ average,
610
+ median,
611
+ min,
612
+ max,
613
+ bySeverity,
614
+ byType
615
+ };
616
+ }
617
+
618
+ /**
619
+ * Track satisfaction trends over time
620
+ *
621
+ * @param {Array} feedbacks - Array of feedback objects
622
+ * @param {Date} fromDate - Start date
623
+ * @param {Date} toDate - End date
624
+ * @returns {Array} Array of SatisfactionTrend objects
625
+ * @private
626
+ */
627
+ _trackSatisfactionTrends(feedbacks, fromDate, toDate) {
628
+ // Group feedbacks by month
629
+ const monthlyData = new Map();
630
+
631
+ feedbacks.forEach(feedback => {
632
+ const date = new Date(feedback.createdAt);
633
+ const period = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
634
+
635
+ if (!monthlyData.has(period)) {
636
+ monthlyData.set(period, {
637
+ period,
638
+ feedbacks: [],
639
+ severityDistribution: {}
640
+ });
641
+ }
642
+
643
+ monthlyData.get(period).feedbacks.push(feedback);
644
+ });
645
+
646
+ // Calculate trends for each period
647
+ const trends = Array.from(monthlyData.values()).map(data => {
648
+ const totalFeedback = data.feedbacks.length;
649
+ const resolvedCount = data.feedbacks.filter(f =>
650
+ f.status === FeedbackStatus.RESOLVED || f.status === FeedbackStatus.VERIFIED
651
+ ).length;
652
+ const resolutionRate = totalFeedback > 0 ? (resolvedCount / totalFeedback) * 100 : 0;
653
+
654
+ // Calculate average resolution time for this period
655
+ const resolvedFeedbacks = data.feedbacks.filter(f =>
656
+ f.resolution && f.resolution.resolvedAt
657
+ );
658
+ let averageResolutionTime = 0;
659
+ if (resolvedFeedbacks.length > 0) {
660
+ const times = resolvedFeedbacks.map(f => {
661
+ const created = new Date(f.createdAt);
662
+ const resolved = new Date(f.resolution.resolvedAt);
663
+ return (resolved - created) / (1000 * 60 * 60);
664
+ });
665
+ averageResolutionTime = times.reduce((sum, t) => sum + t, 0) / times.length;
666
+ }
667
+
668
+ // Calculate severity distribution
669
+ const severityDistribution = {};
670
+ Object.values(FeedbackSeverity).forEach(severity => {
671
+ severityDistribution[severity] = data.feedbacks.filter(f => f.severity === severity).length;
672
+ });
673
+
674
+ return {
675
+ period: data.period,
676
+ totalFeedback,
677
+ resolvedCount,
678
+ resolutionRate,
679
+ averageResolutionTime,
680
+ severityDistribution
681
+ };
682
+ });
683
+
684
+ // Sort by period
685
+ return trends.sort((a, b) => a.period.localeCompare(b.period));
686
+ }
687
+
688
+ /**
689
+ * Identify version-specific issues
690
+ *
691
+ * @param {Array} feedbacks - Array of feedback objects
692
+ * @returns {Array} Array of VersionIssue objects
693
+ * @private
694
+ */
695
+ _identifyVersionIssues(feedbacks) {
696
+ // Group feedbacks by version
697
+ const versionData = new Map();
698
+
699
+ feedbacks.forEach(feedback => {
700
+ const version = feedback.version || 'unknown';
701
+
702
+ if (!versionData.has(version)) {
703
+ versionData.set(version, {
704
+ version,
705
+ feedbacks: []
706
+ });
707
+ }
708
+
709
+ versionData.get(version).feedbacks.push(feedback);
710
+ });
711
+
712
+ // Analyze each version
713
+ const versionIssues = Array.from(versionData.values()).map(data => {
714
+ const feedbackCount = data.feedbacks.length;
715
+ const criticalCount = data.feedbacks.filter(f => f.severity === FeedbackSeverity.CRITICAL).length;
716
+
717
+ // Get top issues for this version
718
+ const topIssues = this._analyzeCommonIssues(data.feedbacks).slice(0, 5);
719
+
720
+ // Calculate average resolution time
721
+ const resolvedFeedbacks = data.feedbacks.filter(f =>
722
+ f.resolution && f.resolution.resolvedAt
723
+ );
724
+ let averageResolutionTime = 0;
725
+ if (resolvedFeedbacks.length > 0) {
726
+ const times = resolvedFeedbacks.map(f => {
727
+ const created = new Date(f.createdAt);
728
+ const resolved = new Date(f.resolution.resolvedAt);
729
+ return (resolved - created) / (1000 * 60 * 60);
730
+ });
731
+ averageResolutionTime = times.reduce((sum, t) => sum + t, 0) / times.length;
732
+ }
733
+
734
+ return {
735
+ version: data.version,
736
+ feedbackCount,
737
+ topIssues,
738
+ criticalCount,
739
+ averageResolutionTime
740
+ };
741
+ });
742
+
743
+ // Sort by feedback count (descending)
744
+ return versionIssues.sort((a, b) => b.feedbackCount - a.feedbackCount);
745
+ }
746
+
747
+ /**
748
+ * Generate automated response for feedback
749
+ *
750
+ * @param {string} feedbackId - Feedback ID
751
+ * @param {string} project - Project name
752
+ * @param {string} environment - Security environment
753
+ * @param {Object} permissionManager - PermissionManager instance (optional)
754
+ * @returns {Promise<Object>} Response result
755
+ */
756
+ async generateAutomatedResponse(feedbackId, project, environment, permissionManager = null) {
757
+ // Load feedback
758
+ const feedback = await this.getFeedback(feedbackId);
759
+ if (!feedback) {
760
+ throw new Error(`Feedback not found: ${feedbackId}`);
761
+ }
762
+
763
+ // Check if automated response is authorized
764
+ if (permissionManager) {
765
+ const takeoverLevel = await permissionManager.getTakeoverLevel(project, environment);
766
+ const authorized = this._isAutomatedResponseAuthorized(feedback, takeoverLevel);
767
+
768
+ if (!authorized) {
769
+ return {
770
+ success: false,
771
+ reason: 'Automated response not authorized for current takeover level',
772
+ takeoverLevel,
773
+ requiresHumanReview: true
774
+ };
775
+ }
776
+ }
777
+
778
+ // Generate response based on feedback pattern
779
+ const response = this._generateResponseContent(feedback);
780
+
781
+ // Save automated response
782
+ await this.saveAutomatedResponse({
783
+ feedbackId,
784
+ response,
785
+ generatedAt: new Date().toISOString(),
786
+ authorized: true,
787
+ takeoverLevel: permissionManager ? await permissionManager.getTakeoverLevel(project, environment) : 'unknown'
788
+ });
789
+
790
+ return {
791
+ success: true,
792
+ response,
793
+ requiresHumanReview: false
794
+ };
795
+ }
796
+
797
+ /**
798
+ * Check if automated response is authorized
799
+ *
800
+ * @param {Object} feedback - Feedback object
801
+ * @param {string} takeoverLevel - Current takeover level
802
+ * @returns {boolean} True if authorized
803
+ * @private
804
+ */
805
+ _isAutomatedResponseAuthorized(feedback, takeoverLevel) {
806
+ const { TakeoverLevel } = require('./models');
807
+
808
+ // Critical feedback always requires human review
809
+ if (feedback.severity === FeedbackSeverity.CRITICAL) {
810
+ return false;
811
+ }
812
+
813
+ // L1 and L2: No automation
814
+ if (takeoverLevel === TakeoverLevel.L1_OBSERVATION ||
815
+ takeoverLevel === TakeoverLevel.L2_SUGGESTION) {
816
+ return false;
817
+ }
818
+
819
+ // L3+: Can automate non-critical feedback
820
+ return true;
821
+ }
822
+
823
+ /**
824
+ * Generate response content based on feedback pattern
825
+ *
826
+ * @param {Object} feedback - Feedback object
827
+ * @returns {Object} Response content
828
+ * @private
829
+ */
830
+ _generateResponseContent(feedback) {
831
+ // Simple pattern-based response generation
832
+ const responses = {
833
+ [FeedbackType.BUG_REPORT]: {
834
+ message: 'Thank you for reporting this issue. We have logged it and will investigate.',
835
+ actions: ['Issue logged', 'Assigned to engineering team']
836
+ },
837
+ [FeedbackType.PERFORMANCE_ISSUE]: {
838
+ message: 'We have received your performance report and are analyzing the metrics.',
839
+ actions: ['Performance metrics collected', 'Analysis in progress']
840
+ },
841
+ [FeedbackType.FEATURE_REQUEST]: {
842
+ message: 'Thank you for your feature suggestion. We have added it to our backlog.',
843
+ actions: ['Feature request logged', 'Added to product backlog']
844
+ },
845
+ [FeedbackType.OPERATIONAL_CONCERN]: {
846
+ message: 'Your operational concern has been noted and forwarded to the operations team.',
847
+ actions: ['Concern logged', 'Operations team notified']
848
+ }
849
+ };
850
+
851
+ const template = responses[feedback.type] || responses[FeedbackType.OPERATIONAL_CONCERN];
852
+
853
+ return {
854
+ message: template.message,
855
+ actions: template.actions,
856
+ feedbackId: feedback.id,
857
+ type: 'automated'
858
+ };
859
+ }
860
+
861
+ /**
862
+ * Generate change proposal from feedback
863
+ *
864
+ * @param {string} feedbackId - Feedback ID
865
+ * @returns {Promise<Object>} Change proposal
866
+ */
867
+ async generateChangeProposal(feedbackId) {
868
+ const feedback = await this.getFeedback(feedbackId);
869
+ if (!feedback) {
870
+ throw new Error(`Feedback not found: ${feedbackId}`);
871
+ }
872
+
873
+ // Determine if feedback requires operational changes
874
+ const requiresChange = this._requiresOperationalChange(feedback);
875
+
876
+ if (!requiresChange) {
877
+ return {
878
+ required: false,
879
+ reason: 'Feedback does not indicate need for operational changes'
880
+ };
881
+ }
882
+
883
+ // Generate change proposal
884
+ const proposal = {
885
+ feedbackId: feedback.id,
886
+ proposalType: this._determineChangeType(feedback),
887
+ description: `Operational change proposed based on feedback: ${feedback.content.title}`,
888
+ impactAssessment: this._assessChangeImpact(feedback),
889
+ recommendedActions: this._recommendActions(feedback),
890
+ priority: feedback.severity,
891
+ createdAt: new Date().toISOString()
892
+ };
893
+
894
+ // Save proposal
895
+ await this.saveChangeProposal(proposal);
896
+
897
+ return {
898
+ required: true,
899
+ proposal
900
+ };
901
+ }
902
+
903
+ /**
904
+ * Check if feedback requires operational change
905
+ *
906
+ * @param {Object} feedback - Feedback object
907
+ * @returns {boolean} True if change required
908
+ * @private
909
+ */
910
+ _requiresOperationalChange(feedback) {
911
+ // Recurring issues or high severity issues typically require changes
912
+ return feedback.severity === FeedbackSeverity.CRITICAL ||
913
+ feedback.severity === FeedbackSeverity.HIGH;
914
+ }
915
+
916
+ /**
917
+ * Determine type of operational change needed
918
+ *
919
+ * @param {Object} feedback - Feedback object
920
+ * @returns {string} Change type
921
+ * @private
922
+ */
923
+ _determineChangeType(feedback) {
924
+ if (feedback.type === FeedbackType.PERFORMANCE_ISSUE) {
925
+ return 'performance_optimization';
926
+ } else if (feedback.type === FeedbackType.BUG_REPORT) {
927
+ return 'bug_fix';
928
+ } else {
929
+ return 'operational_improvement';
930
+ }
931
+ }
932
+
933
+ /**
934
+ * Assess impact of proposed change
935
+ *
936
+ * @param {Object} feedback - Feedback object
937
+ * @returns {Object} Impact assessment
938
+ * @private
939
+ */
940
+ _assessChangeImpact(feedback) {
941
+ return {
942
+ severity: feedback.severity,
943
+ affectedComponents: ['operations'],
944
+ estimatedEffort: feedback.severity === FeedbackSeverity.CRITICAL ? 'high' : 'medium',
945
+ riskLevel: feedback.severity === FeedbackSeverity.CRITICAL ? 'high' : 'low'
946
+ };
947
+ }
948
+
949
+ /**
950
+ * Recommend actions for feedback
951
+ *
952
+ * @param {Object} feedback - Feedback object
953
+ * @returns {Array} Recommended actions
954
+ * @private
955
+ */
956
+ _recommendActions(feedback) {
957
+ const actions = [];
958
+
959
+ if (feedback.type === FeedbackType.PERFORMANCE_ISSUE) {
960
+ actions.push('Review performance metrics');
961
+ actions.push('Optimize slow operations');
962
+ actions.push('Update monitoring thresholds');
963
+ } else if (feedback.type === FeedbackType.BUG_REPORT) {
964
+ actions.push('Investigate root cause');
965
+ actions.push('Implement fix');
966
+ actions.push('Add regression test');
967
+ } else {
968
+ actions.push('Review operational procedures');
969
+ actions.push('Update documentation');
970
+ }
971
+
972
+ return actions;
973
+ }
974
+
975
+ /**
976
+ * Notify stakeholders about feedback
977
+ *
978
+ * @param {string} feedbackId - Feedback ID
979
+ * @param {Array} stakeholders - List of stakeholder emails
980
+ * @returns {Promise<Object>} Notification result
981
+ */
982
+ async notifyStakeholders(feedbackId, stakeholders = []) {
983
+ const feedback = await this.getFeedback(feedbackId);
984
+ if (!feedback) {
985
+ throw new Error(`Feedback not found: ${feedbackId}`);
986
+ }
987
+
988
+ // Determine if notification is required
989
+ const requiresNotification = this._requiresStakeholderNotification(feedback);
990
+
991
+ if (!requiresNotification) {
992
+ return {
993
+ sent: false,
994
+ reason: 'Feedback does not require stakeholder notification'
995
+ };
996
+ }
997
+
998
+ // Generate notification
999
+ const notification = {
1000
+ feedbackId: feedback.id,
1001
+ subject: `[${feedback.severity.toUpperCase()}] Feedback requires attention: ${feedback.content.title}`,
1002
+ body: this._generateNotificationBody(feedback),
1003
+ recipients: stakeholders.length > 0 ? stakeholders : this._getDefaultStakeholders(feedback),
1004
+ sentAt: new Date().toISOString()
1005
+ };
1006
+
1007
+ // Save notification record
1008
+ await this.saveNotification(notification);
1009
+
1010
+ return {
1011
+ sent: true,
1012
+ notification
1013
+ };
1014
+ }
1015
+
1016
+ /**
1017
+ * Check if feedback requires stakeholder notification
1018
+ *
1019
+ * @param {Object} feedback - Feedback object
1020
+ * @returns {boolean} True if notification required
1021
+ * @private
1022
+ */
1023
+ _requiresStakeholderNotification(feedback) {
1024
+ // Critical and high severity feedback requires notification
1025
+ return feedback.severity === FeedbackSeverity.CRITICAL ||
1026
+ feedback.severity === FeedbackSeverity.HIGH;
1027
+ }
1028
+
1029
+ /**
1030
+ * Generate notification body
1031
+ *
1032
+ * @param {Object} feedback - Feedback object
1033
+ * @returns {string} Notification body
1034
+ * @private
1035
+ */
1036
+ _generateNotificationBody(feedback) {
1037
+ return `
1038
+ Feedback ID: ${feedback.id}
1039
+ Severity: ${feedback.severity}
1040
+ Type: ${feedback.type}
1041
+ Project: ${feedback.project}
1042
+ Version: ${feedback.version}
1043
+
1044
+ Title: ${feedback.content.title}
1045
+ Description: ${feedback.content.description}
1046
+
1047
+ Status: ${feedback.status}
1048
+ Created: ${feedback.createdAt}
1049
+
1050
+ This feedback requires human attention. Please review and take appropriate action.
1051
+ `.trim();
1052
+ }
1053
+
1054
+ /**
1055
+ * Get default stakeholders for feedback
1056
+ *
1057
+ * @param {Object} feedback - Feedback object
1058
+ * @returns {Array} List of stakeholder emails
1059
+ * @private
1060
+ */
1061
+ _getDefaultStakeholders(feedback) {
1062
+ // In a real system, this would query a stakeholder registry
1063
+ // For now, return placeholder
1064
+ return ['operations-team@example.com'];
1065
+ }
1066
+
1067
+ /**
1068
+ * Save automated response
1069
+ *
1070
+ * @param {Object} response - Automated response object
1071
+ * @returns {Promise<void>}
1072
+ */
1073
+ async saveAutomatedResponse(response) {
1074
+ const responseFile = path.join(this.feedbackDir, 'automated-responses.json');
1075
+ await ensureDirectory(this.feedbackDir);
1076
+
1077
+ let responses = [];
1078
+ const exists = await pathExists(responseFile);
1079
+ if (exists) {
1080
+ try {
1081
+ const data = await readJSON(responseFile);
1082
+ responses = data.responses || [];
1083
+ } catch (error) {
1084
+ // Ignore errors, start fresh
1085
+ }
1086
+ }
1087
+
1088
+ responses.push(response);
1089
+
1090
+ await writeJSON(responseFile, { responses, lastUpdated: new Date().toISOString() });
1091
+ }
1092
+
1093
+ /**
1094
+ * Save change proposal
1095
+ *
1096
+ * @param {Object} proposal - Change proposal object
1097
+ * @returns {Promise<void>}
1098
+ */
1099
+ async saveChangeProposal(proposal) {
1100
+ const proposalFile = path.join(this.feedbackDir, 'change-proposals.json');
1101
+ await ensureDirectory(this.feedbackDir);
1102
+
1103
+ let proposals = [];
1104
+ const exists = await pathExists(proposalFile);
1105
+ if (exists) {
1106
+ try {
1107
+ const data = await readJSON(proposalFile);
1108
+ proposals = data.proposals || [];
1109
+ } catch (error) {
1110
+ // Ignore errors, start fresh
1111
+ }
1112
+ }
1113
+
1114
+ proposals.push(proposal);
1115
+
1116
+ await writeJSON(proposalFile, { proposals, lastUpdated: new Date().toISOString() });
1117
+ }
1118
+
1119
+ /**
1120
+ * Save notification record
1121
+ *
1122
+ * @param {Object} notification - Notification object
1123
+ * @returns {Promise<void>}
1124
+ */
1125
+ async saveNotification(notification) {
1126
+ const notificationFile = path.join(this.feedbackDir, 'notifications.json');
1127
+ await ensureDirectory(this.feedbackDir);
1128
+
1129
+ let notifications = [];
1130
+ const exists = await pathExists(notificationFile);
1131
+ if (exists) {
1132
+ try {
1133
+ const data = await readJSON(notificationFile);
1134
+ notifications = data.notifications || [];
1135
+ } catch (error) {
1136
+ // Ignore errors, start fresh
1137
+ }
1138
+ }
1139
+
1140
+ notifications.push(notification);
1141
+
1142
+ await writeJSON(notificationFile, { notifications, lastUpdated: new Date().toISOString() });
1143
+ }
1144
+ }
1145
+
1146
+ module.exports = FeedbackManager;
1147
+