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,351 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+
4
+ const DEFAULT_BINDING_PLUGIN_DIRS = [
5
+ '.kiro/plugins/scene-bindings',
6
+ '.kiro/config/scene-bindings'
7
+ ];
8
+ const DEFAULT_BINDING_PLUGIN_MANIFEST = '.kiro/config/scene-binding-plugins.json';
9
+
10
+ function isPlainObject(value) {
11
+ return value && typeof value === 'object' && !Array.isArray(value);
12
+ }
13
+
14
+ function normalizePath(projectRoot, targetPath) {
15
+ if (!targetPath) {
16
+ return null;
17
+ }
18
+
19
+ if (path.isAbsolute(targetPath)) {
20
+ return targetPath;
21
+ }
22
+
23
+ return path.join(projectRoot, targetPath);
24
+ }
25
+
26
+ function normalizePluginFileToken(value) {
27
+ const normalized = String(value || '').trim().replace(/\\/g, '/');
28
+ if (!normalized) {
29
+ return null;
30
+ }
31
+
32
+ return path.posix.basename(normalized);
33
+ }
34
+
35
+ function extractPluginDefinitions(exportedValue, filePath, warnings = []) {
36
+ let value = exportedValue;
37
+
38
+ if (value && isPlainObject(value) && value.__esModule && value.default) {
39
+ value = value.default;
40
+ }
41
+
42
+ if (typeof value === 'function') {
43
+ try {
44
+ value = value();
45
+ } catch (error) {
46
+ warnings.push(`binding plugin factory failed: ${filePath} (${error.message})`);
47
+ return [];
48
+ }
49
+ }
50
+
51
+ if (Array.isArray(value)) {
52
+ return value;
53
+ }
54
+
55
+ if (isPlainObject(value)) {
56
+ return [value];
57
+ }
58
+
59
+ warnings.push(`binding plugin export must be object/array/function: ${filePath}`);
60
+ return [];
61
+ }
62
+
63
+ function normalizePluginHandler(handler, fallbackId) {
64
+ if (!isPlainObject(handler) || typeof handler.execute !== 'function') {
65
+ return null;
66
+ }
67
+
68
+ return {
69
+ ...handler,
70
+ id: handler.id || fallbackId
71
+ };
72
+ }
73
+
74
+ function normalizePluginManifest(rawManifest, manifestPath, warnings = []) {
75
+ if (!isPlainObject(rawManifest)) {
76
+ warnings.push(`binding plugin manifest must be a JSON object: ${manifestPath}`);
77
+ return null;
78
+ }
79
+
80
+ const strict = rawManifest.strict === true;
81
+ const enabledByDefault = rawManifest.enabled_by_default !== false;
82
+ const defaultPriority = Number.isFinite(rawManifest.default_priority) ? Number(rawManifest.default_priority) : 1000;
83
+
84
+ const allowedFiles = new Set(
85
+ (Array.isArray(rawManifest.allowed_files) ? rawManifest.allowed_files : [])
86
+ .map(normalizePluginFileToken)
87
+ .filter(Boolean)
88
+ );
89
+
90
+ const blockedFiles = new Set(
91
+ (Array.isArray(rawManifest.blocked_files) ? rawManifest.blocked_files : [])
92
+ .map(normalizePluginFileToken)
93
+ .filter(Boolean)
94
+ );
95
+
96
+ const pluginEntries = new Map();
97
+
98
+ if (Array.isArray(rawManifest.plugins)) {
99
+ let order = 0;
100
+
101
+ for (const entry of rawManifest.plugins) {
102
+ order += 1;
103
+
104
+ if (!isPlainObject(entry)) {
105
+ warnings.push(`invalid plugin manifest entry ignored at index ${order}: ${manifestPath}`);
106
+ continue;
107
+ }
108
+
109
+ const fileToken = normalizePluginFileToken(entry.file);
110
+ if (!fileToken) {
111
+ warnings.push(`plugin manifest entry missing file at index ${order}: ${manifestPath}`);
112
+ continue;
113
+ }
114
+
115
+ pluginEntries.set(fileToken, {
116
+ enabled: entry.enabled !== false,
117
+ priority: Number.isFinite(entry.priority) ? Number(entry.priority) : defaultPriority,
118
+ order
119
+ });
120
+ }
121
+ }
122
+
123
+ return {
124
+ strict,
125
+ enabledByDefault,
126
+ defaultPriority,
127
+ allowedFiles,
128
+ blockedFiles,
129
+ pluginEntries,
130
+ manifestPath
131
+ };
132
+ }
133
+
134
+ function loadPluginManifest(projectRoot, options = {}, warnings = []) {
135
+ const manifestEnabled = options.manifestEnabled !== false;
136
+ if (!manifestEnabled) {
137
+ return {
138
+ manifest: null,
139
+ manifestPath: null,
140
+ manifestLoaded: false
141
+ };
142
+ }
143
+
144
+ const explicitManifestPath = typeof options.manifestPath === 'string' && options.manifestPath.trim().length > 0;
145
+ const manifestPath = explicitManifestPath
146
+ ? normalizePath(projectRoot, options.manifestPath)
147
+ : path.join(projectRoot, DEFAULT_BINDING_PLUGIN_MANIFEST);
148
+
149
+ if (!manifestPath || !fs.pathExistsSync(manifestPath)) {
150
+ if (explicitManifestPath) {
151
+ warnings.push(`binding plugin manifest not found: ${manifestPath}`);
152
+ }
153
+
154
+ return {
155
+ manifest: null,
156
+ manifestPath: explicitManifestPath ? manifestPath : null,
157
+ manifestLoaded: false
158
+ };
159
+ }
160
+
161
+ try {
162
+ const rawManifest = fs.readJsonSync(manifestPath);
163
+ const manifest = normalizePluginManifest(rawManifest, manifestPath, warnings);
164
+
165
+ if (!manifest) {
166
+ return {
167
+ manifest: null,
168
+ manifestPath,
169
+ manifestLoaded: false
170
+ };
171
+ }
172
+
173
+ return {
174
+ manifest,
175
+ manifestPath,
176
+ manifestLoaded: true
177
+ };
178
+ } catch (error) {
179
+ warnings.push(`failed to read binding plugin manifest: ${manifestPath} (${error.message})`);
180
+
181
+ return {
182
+ manifest: null,
183
+ manifestPath,
184
+ manifestLoaded: false
185
+ };
186
+ }
187
+ }
188
+
189
+ function resolvePluginFilePlan(discoveredFiles = [], manifest = null, candidateDir = '', warnings = []) {
190
+ const orderedFiles = [];
191
+ const discoveredSet = new Set(discoveredFiles);
192
+
193
+ if (!manifest) {
194
+ for (const fileName of discoveredFiles) {
195
+ orderedFiles.push({ fileName, priority: 1000, order: Number.MAX_SAFE_INTEGER });
196
+ }
197
+
198
+ return orderedFiles
199
+ .sort((left, right) => left.fileName.localeCompare(right.fileName))
200
+ .map((item) => item.fileName);
201
+ }
202
+
203
+ for (const fileName of discoveredFiles) {
204
+ const entry = manifest.pluginEntries.get(fileName);
205
+
206
+ if (manifest.blockedFiles.has(fileName)) {
207
+ warnings.push(`binding plugin blocked by manifest: ${path.join(candidateDir, fileName)}`);
208
+ continue;
209
+ }
210
+
211
+ if (manifest.allowedFiles.size > 0 && !manifest.allowedFiles.has(fileName)) {
212
+ warnings.push(`binding plugin skipped (not allowed): ${path.join(candidateDir, fileName)}`);
213
+ continue;
214
+ }
215
+
216
+ if (entry && entry.enabled === false) {
217
+ continue;
218
+ }
219
+
220
+ if (!entry && (manifest.strict || !manifest.enabledByDefault)) {
221
+ warnings.push(`binding plugin skipped by manifest policy: ${path.join(candidateDir, fileName)}`);
222
+ continue;
223
+ }
224
+
225
+ orderedFiles.push({
226
+ fileName,
227
+ priority: entry ? entry.priority : manifest.defaultPriority,
228
+ order: entry ? entry.order : Number.MAX_SAFE_INTEGER
229
+ });
230
+ }
231
+
232
+ for (const declaredFile of manifest.pluginEntries.keys()) {
233
+ if (!discoveredSet.has(declaredFile)) {
234
+ warnings.push(`binding plugin declared but missing: ${path.join(candidateDir, declaredFile)}`);
235
+ }
236
+ }
237
+
238
+ return orderedFiles
239
+ .sort((left, right) => {
240
+ if (left.priority !== right.priority) {
241
+ return left.priority - right.priority;
242
+ }
243
+
244
+ if (left.order !== right.order) {
245
+ return left.order - right.order;
246
+ }
247
+
248
+ return left.fileName.localeCompare(right.fileName);
249
+ })
250
+ .map((item) => item.fileName);
251
+ }
252
+
253
+ function loadBindingPlugins(options = {}) {
254
+ const projectRoot = options.projectRoot || process.cwd();
255
+ const pluginDir = options.pluginDir || null;
256
+ const autoDiscovery = options.autoDiscovery !== false;
257
+
258
+ const warnings = [];
259
+ const pluginDirs = [];
260
+ const pluginFiles = [];
261
+ const handlers = [];
262
+
263
+ const manifestResolution = loadPluginManifest(projectRoot, {
264
+ manifestPath: options.manifestPath,
265
+ manifestEnabled: options.manifestEnabled !== false
266
+ }, warnings);
267
+
268
+ const candidateDirs = [];
269
+
270
+ if (pluginDir) {
271
+ const resolved = normalizePath(projectRoot, pluginDir);
272
+ if (resolved) {
273
+ candidateDirs.push(resolved);
274
+ }
275
+ } else if (autoDiscovery) {
276
+ for (const relativeDir of DEFAULT_BINDING_PLUGIN_DIRS) {
277
+ candidateDirs.push(path.join(projectRoot, relativeDir));
278
+ }
279
+ }
280
+
281
+ for (const candidateDir of candidateDirs) {
282
+ if (!candidateDir || !fs.pathExistsSync(candidateDir)) {
283
+ continue;
284
+ }
285
+
286
+ pluginDirs.push(candidateDir);
287
+
288
+ let entries = [];
289
+ try {
290
+ entries = fs.readdirSync(candidateDir, { withFileTypes: true });
291
+ } catch (error) {
292
+ warnings.push(`failed to list binding plugin dir: ${candidateDir} (${error.message})`);
293
+ continue;
294
+ }
295
+
296
+ const discoveredFiles = entries
297
+ .filter((entry) => entry && typeof entry.isFile === 'function' && entry.isFile() && /\.(js|cjs)$/i.test(entry.name || ''))
298
+ .map((entry) => normalizePluginFileToken(entry.name))
299
+ .filter(Boolean)
300
+ .sort((left, right) => left.localeCompare(right));
301
+
302
+ const filesToLoad = resolvePluginFilePlan(
303
+ discoveredFiles,
304
+ manifestResolution.manifest,
305
+ candidateDir,
306
+ warnings
307
+ );
308
+
309
+ for (const fileName of filesToLoad) {
310
+ const absolutePath = path.join(candidateDir, fileName);
311
+
312
+ try {
313
+ const resolvedPath = require.resolve(absolutePath);
314
+ delete require.cache[resolvedPath];
315
+ const pluginExport = require(resolvedPath);
316
+ const rawHandlers = extractPluginDefinitions(pluginExport, absolutePath, warnings);
317
+
318
+ let localIndex = 0;
319
+ for (const rawHandler of rawHandlers) {
320
+ localIndex += 1;
321
+ const handler = normalizePluginHandler(rawHandler, `${path.basename(fileName, path.extname(fileName))}-${localIndex}`);
322
+ if (!handler) {
323
+ warnings.push(`invalid binding handler in plugin: ${absolutePath}`);
324
+ continue;
325
+ }
326
+
327
+ handlers.push(handler);
328
+ }
329
+
330
+ pluginFiles.push(absolutePath);
331
+ } catch (error) {
332
+ warnings.push(`failed to load binding plugin: ${absolutePath} (${error.message})`);
333
+ }
334
+ }
335
+ }
336
+
337
+ return {
338
+ handlers,
339
+ warnings,
340
+ plugin_dirs: pluginDirs,
341
+ plugin_files: pluginFiles,
342
+ manifest_path: manifestResolution.manifestPath,
343
+ manifest_loaded: manifestResolution.manifestLoaded
344
+ };
345
+ }
346
+
347
+ module.exports = {
348
+ DEFAULT_BINDING_PLUGIN_DIRS,
349
+ DEFAULT_BINDING_PLUGIN_MANIFEST,
350
+ loadBindingPlugins
351
+ };
@@ -0,0 +1,349 @@
1
+ const { createMoquiAdapterHandler } = require('./moqui-adapter');
2
+
3
+ const DEFAULT_READINESS_SUCCESS = {
4
+ passed: true,
5
+ reason: 'default-ready'
6
+ };
7
+
8
+ function isPlainObject(value) {
9
+ return value && typeof value === 'object' && !Array.isArray(value);
10
+ }
11
+
12
+ function normalizeNodeType(value) {
13
+ const normalized = String(value || '').trim().toLowerCase();
14
+
15
+ if (!normalized) {
16
+ return null;
17
+ }
18
+
19
+ return normalized;
20
+ }
21
+
22
+ function normalizeRef(value) {
23
+ return String(value || '').trim();
24
+ }
25
+
26
+ function normalizePatternList(value) {
27
+ if (!value) {
28
+ return [];
29
+ }
30
+
31
+ if (Array.isArray(value)) {
32
+ return value;
33
+ }
34
+
35
+ return [value];
36
+ }
37
+
38
+ function createMatcher(match = null) {
39
+ if (typeof match === 'function') {
40
+ return match;
41
+ }
42
+
43
+ const normalizedMatch = isPlainObject(match)
44
+ ? match
45
+ : (typeof match === 'string' ? { refPrefix: match } : {});
46
+
47
+ const nodeTypes = normalizePatternList(normalizedMatch.nodeType || normalizedMatch.nodeTypes)
48
+ .map(normalizeNodeType)
49
+ .filter(Boolean);
50
+ const refPrefixes = normalizePatternList(normalizedMatch.refPrefix || normalizedMatch.refPrefixes)
51
+ .map((item) => normalizeRef(item).toLowerCase())
52
+ .filter(Boolean);
53
+
54
+ const refRegexes = [];
55
+ for (const item of normalizePatternList(normalizedMatch.refPattern || normalizedMatch.refPatterns)) {
56
+ if (!item) {
57
+ continue;
58
+ }
59
+
60
+ if (item instanceof RegExp) {
61
+ refRegexes.push(item);
62
+ continue;
63
+ }
64
+
65
+ const pattern = String(item).trim();
66
+ if (!pattern) {
67
+ continue;
68
+ }
69
+
70
+ try {
71
+ refRegexes.push(new RegExp(pattern, 'i'));
72
+ } catch (error) {
73
+ continue;
74
+ }
75
+ }
76
+
77
+ if (nodeTypes.length === 0 && refPrefixes.length === 0 && refRegexes.length === 0) {
78
+ return () => true;
79
+ }
80
+
81
+ return (node = {}) => {
82
+ const nodeType = normalizeNodeType(node.node_type || node.type);
83
+ const bindingRef = normalizeRef(node.binding_ref || node.ref);
84
+ const bindingRefLower = bindingRef.toLowerCase();
85
+
86
+ if (nodeTypes.length > 0 && !nodeTypes.includes(nodeType)) {
87
+ return false;
88
+ }
89
+
90
+ if (refPrefixes.length > 0) {
91
+ const matchedPrefix = refPrefixes.some((prefix) => bindingRefLower.startsWith(prefix));
92
+ if (!matchedPrefix) {
93
+ return false;
94
+ }
95
+ }
96
+
97
+ if (refRegexes.length > 0) {
98
+ const matchedRegex = refRegexes.some((regex) => regex.test(bindingRef));
99
+ if (!matchedRegex) {
100
+ return false;
101
+ }
102
+ }
103
+
104
+ return true;
105
+ };
106
+ }
107
+
108
+ function normalizeReadinessResult(rawResult, fallbackName) {
109
+ if (rawResult === true) {
110
+ return { ...DEFAULT_READINESS_SUCCESS, name: fallbackName };
111
+ }
112
+
113
+ if (rawResult === false) {
114
+ return {
115
+ passed: false,
116
+ reason: 'readiness-failed',
117
+ name: fallbackName
118
+ };
119
+ }
120
+
121
+ if (!isPlainObject(rawResult)) {
122
+ return { ...DEFAULT_READINESS_SUCCESS, name: fallbackName };
123
+ }
124
+
125
+ return {
126
+ name: rawResult.name || fallbackName,
127
+ passed: rawResult.passed !== false,
128
+ reason: rawResult.reason || (rawResult.passed === false ? 'readiness-failed' : 'ready'),
129
+ detail: rawResult.detail
130
+ };
131
+ }
132
+
133
+ class BindingRegistry {
134
+ constructor(options = {}) {
135
+ this.projectRoot = options.projectRoot || process.cwd();
136
+ this.moquiConfigPath = typeof options.moquiConfigPath === 'string' && options.moquiConfigPath.trim().length > 0
137
+ ? options.moquiConfigPath.trim()
138
+ : undefined;
139
+ this.useMoquiAdapter = options.useMoquiAdapter !== false;
140
+
141
+ this.handlers = [];
142
+ this.fallbackHandler = {
143
+ id: 'builtin.default',
144
+ matcher: () => true,
145
+ execute: async (node) => ({
146
+ status: 'success',
147
+ binding_ref: node.binding_ref,
148
+ provider: 'default'
149
+ }),
150
+ readiness: async () => ({ ...DEFAULT_READINESS_SUCCESS })
151
+ };
152
+
153
+ if (options.useDefaultHandlers !== false) {
154
+ this.registerDefaultHandlers();
155
+ }
156
+
157
+ if (Array.isArray(options.handlers)) {
158
+ for (const handler of options.handlers) {
159
+ this.register(handler);
160
+ }
161
+ }
162
+
163
+ if (options.fallbackHandler) {
164
+ this.setFallbackHandler(options.fallbackHandler);
165
+ }
166
+ }
167
+
168
+ registerDefaultHandlers() {
169
+ if (this.useMoquiAdapter) {
170
+ this.register(createMoquiAdapterHandler({
171
+ projectRoot: this.projectRoot,
172
+ configPath: this.moquiConfigPath,
173
+ allowSpecErpFallback: true
174
+ }));
175
+ }
176
+
177
+ this.register({
178
+ id: 'builtin.erp-sim',
179
+ match: { refPrefix: 'spec.erp.' },
180
+ execute: async (node) => ({
181
+ status: 'success',
182
+ provider: 'erp-sim',
183
+ binding_ref: node.binding_ref
184
+ })
185
+ });
186
+
187
+ this.register({
188
+ id: 'builtin.robot-sim',
189
+ match: { refPrefix: 'spec.robot.' },
190
+ execute: async (node) => ({
191
+ status: 'success',
192
+ provider: 'robot-sim',
193
+ binding_ref: node.binding_ref
194
+ }),
195
+ readiness: async (node, payload = {}) => {
196
+ const context = payload.context || {};
197
+ const safetyChecks = context.safetyChecks || {};
198
+
199
+ if (normalizeNodeType(node.node_type || node.type) !== 'adapter') {
200
+ return {
201
+ passed: true,
202
+ reason: 'not-adapter'
203
+ };
204
+ }
205
+
206
+ if (!safetyChecks.preflight) {
207
+ return {
208
+ passed: false,
209
+ reason: 'missing-preflight'
210
+ };
211
+ }
212
+
213
+ if (!safetyChecks.stopChannel) {
214
+ return {
215
+ passed: false,
216
+ reason: 'missing-stop-channel'
217
+ };
218
+ }
219
+
220
+ return {
221
+ passed: true,
222
+ reason: 'robot-safety-ready'
223
+ };
224
+ }
225
+ });
226
+
227
+ this.register({
228
+ id: 'builtin.adapter-sim',
229
+ match: { nodeType: 'adapter' },
230
+ execute: async (node) => ({
231
+ status: 'success',
232
+ provider: 'adapter-sim',
233
+ binding_ref: node.binding_ref
234
+ }),
235
+ readiness: async () => ({
236
+ passed: true,
237
+ reason: 'adapter-ready'
238
+ })
239
+ });
240
+ }
241
+
242
+ register(handler = {}) {
243
+ if (!isPlainObject(handler)) {
244
+ throw new Error('binding handler must be an object');
245
+ }
246
+
247
+ if (typeof handler.execute !== 'function') {
248
+ throw new Error('binding handler requires execute function');
249
+ }
250
+
251
+ const id = normalizeRef(handler.id) || `handler-${this.handlers.length + 1}`;
252
+ const matcher = createMatcher(handler.matcher || handler.match || null);
253
+
254
+ this.handlers.push({
255
+ ...handler,
256
+ id,
257
+ matcher
258
+ });
259
+
260
+ return id;
261
+ }
262
+
263
+ setFallbackHandler(handler = {}) {
264
+ if (!isPlainObject(handler) || typeof handler.execute !== 'function') {
265
+ throw new Error('fallback handler requires execute function');
266
+ }
267
+
268
+ this.fallbackHandler = {
269
+ ...handler,
270
+ id: normalizeRef(handler.id) || 'fallback',
271
+ matcher: () => true
272
+ };
273
+ }
274
+
275
+ resolve(node = {}, payload = {}) {
276
+ for (const handler of this.handlers) {
277
+ try {
278
+ if (handler.matcher(node, payload)) {
279
+ return handler;
280
+ }
281
+ } catch (error) {
282
+ continue;
283
+ }
284
+ }
285
+
286
+ return this.fallbackHandler;
287
+ }
288
+
289
+ async execute(node = {}, payload = {}) {
290
+ const handler = this.resolve(node, payload);
291
+ const rawResult = await handler.execute(node, payload);
292
+
293
+ if (!isPlainObject(rawResult)) {
294
+ return {
295
+ status: 'success',
296
+ output: rawResult,
297
+ handler_id: handler.id
298
+ };
299
+ }
300
+
301
+ return {
302
+ status: rawResult.status === 'failed' ? 'failed' : 'success',
303
+ ...rawResult,
304
+ handler_id: rawResult.handler_id || handler.id
305
+ };
306
+ }
307
+
308
+ async checkReadiness(sceneManifest = {}, payload = {}) {
309
+ const bindings = ((((sceneManifest || {}).spec || {}).capability_contract || {}).bindings) || [];
310
+ const checks = [];
311
+
312
+ for (const binding of bindings) {
313
+ const nodeType = normalizeNodeType(binding.type);
314
+ if (nodeType !== 'adapter') {
315
+ continue;
316
+ }
317
+
318
+ const node = {
319
+ node_type: nodeType,
320
+ binding_ref: binding.ref
321
+ };
322
+ const handler = this.resolve(node, payload);
323
+
324
+ if (typeof handler.readiness !== 'function') {
325
+ checks.push({
326
+ name: `adapter:${node.binding_ref}`,
327
+ passed: true,
328
+ reason: 'no-readiness-hook',
329
+ handler_id: handler.id
330
+ });
331
+ continue;
332
+ }
333
+
334
+ const readinessRaw = await handler.readiness(node, payload);
335
+ const readiness = normalizeReadinessResult(readinessRaw, `adapter:${node.binding_ref}`);
336
+ checks.push({
337
+ ...readiness,
338
+ handler_id: handler.id
339
+ });
340
+ }
341
+
342
+ return {
343
+ ready: checks.every((item) => item.passed !== false),
344
+ checks
345
+ };
346
+ }
347
+ }
348
+
349
+ module.exports = BindingRegistry;