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.
- package/CHANGELOG.md +2513 -0
- package/LICENSE +21 -0
- package/README.md +765 -0
- package/README.zh.md +630 -0
- package/bin/kiro-spec-engine.js +796 -0
- package/bin/kse.js +3 -0
- package/bin/sce.js +3 -0
- package/bin/sco.js +3 -0
- package/docs/331-poc-adaptation-roadmap.md +156 -0
- package/docs/331-poc-dual-track-integration-guide.md +120 -0
- package/docs/331-poc-weekly-delivery-checklist.md +52 -0
- package/docs/OFFLINE_INSTALL.md +96 -0
- package/docs/README.md +279 -0
- package/docs/adopt-migration-guide.md +599 -0
- package/docs/adoption-guide.md +616 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/architecture.md +733 -0
- package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
- package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
- package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
- package/docs/autonomous-control-guide.md +851 -0
- package/docs/command-reference.md +1368 -0
- package/docs/community.md +115 -0
- package/docs/cross-tool-guide.md +555 -0
- package/docs/developer-guide.md +619 -0
- package/docs/document-governance.md +865 -0
- package/docs/environment-management-guide.md +526 -0
- package/docs/examples/add-export-command/design.md +194 -0
- package/docs/examples/add-export-command/requirements.md +110 -0
- package/docs/examples/add-export-command/tasks.md +88 -0
- package/docs/examples/add-rest-api/design.md +855 -0
- package/docs/examples/add-rest-api/requirements.md +323 -0
- package/docs/examples/add-rest-api/tasks.md +355 -0
- package/docs/examples/add-user-dashboard/design.md +192 -0
- package/docs/examples/add-user-dashboard/requirements.md +143 -0
- package/docs/examples/add-user-dashboard/tasks.md +91 -0
- package/docs/faq.md +697 -0
- package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
- package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
- package/docs/images/wechat-qr.png +0 -0
- package/docs/integration-modes.md +529 -0
- package/docs/integration-philosophy.md +313 -0
- package/docs/knowledge-management-guide.md +263 -0
- package/docs/manual-workflows-guide.md +418 -0
- package/docs/moqui-capability-matrix.md +73 -0
- package/docs/moqui-template-core-library-playbook.md +109 -0
- package/docs/multi-agent-coordination-guide.md +553 -0
- package/docs/multi-repo-management-guide.md +1344 -0
- package/docs/quick-start-with-ai-tools.md +375 -0
- package/docs/quick-start.md +146 -0
- package/docs/release-checklist.md +121 -0
- package/docs/releases/README.md +13 -0
- package/docs/releases/v1.46.2-validation.md +45 -0
- package/docs/releases/v1.46.2.md +50 -0
- package/docs/scene-runtime-guide.md +347 -0
- package/docs/spec-collaboration-guide.md +369 -0
- package/docs/spec-locking-guide.md +225 -0
- package/docs/spec-numbering-guide.md +348 -0
- package/docs/spec-workflow.md +519 -0
- package/docs/steering-strategy-guide.md +196 -0
- package/docs/team-collaboration-guide.md +465 -0
- package/docs/testing-strategy.md +272 -0
- package/docs/tools/claude-guide.md +654 -0
- package/docs/tools/cursor-guide.md +706 -0
- package/docs/tools/generic-guide.md +446 -0
- package/docs/tools/kiro-guide.md +308 -0
- package/docs/tools/vscode-guide.md +445 -0
- package/docs/tools/windsurf-guide.md +391 -0
- package/docs/troubleshooting.md +1135 -0
- package/docs/upgrade-guide.md +639 -0
- package/docs/value-observability-guide.md +127 -0
- package/docs/zh/README.md +341 -0
- package/docs/zh/quick-start.md +764 -0
- package/docs/zh/release-checklist.md +121 -0
- package/docs/zh/releases/README.md +13 -0
- package/docs/zh/releases/v1.46.2-validation.md +45 -0
- package/docs/zh/releases/v1.46.2.md +50 -0
- package/docs/zh/spec-numbering-guide.md +348 -0
- package/docs/zh/tools/claude-guide.md +349 -0
- package/docs/zh/tools/cursor-guide.md +281 -0
- package/docs/zh/tools/generic-guide.md +499 -0
- package/docs/zh/tools/kiro-guide.md +342 -0
- package/docs/zh/tools/vscode-guide.md +449 -0
- package/docs/zh/tools/windsurf-guide.md +378 -0
- package/docs/zh/value-observability-guide.md +127 -0
- package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
- package/lib/adoption/adoption-logger.js +487 -0
- package/lib/adoption/adoption-strategy.js +538 -0
- package/lib/adoption/backup-manager.js +420 -0
- package/lib/adoption/conflict-resolver.js +410 -0
- package/lib/adoption/detection-engine.js +275 -0
- package/lib/adoption/diff-viewer.js +226 -0
- package/lib/adoption/error-formatter.js +509 -0
- package/lib/adoption/file-classifier.js +385 -0
- package/lib/adoption/progress-reporter.js +534 -0
- package/lib/adoption/smart-orchestrator.js +470 -0
- package/lib/adoption/strategy-selector.js +218 -0
- package/lib/adoption/summary-generator.js +493 -0
- package/lib/adoption/template-sync.js +605 -0
- package/lib/auto/autonomous-engine.js +485 -0
- package/lib/auto/checkpoint-manager.js +300 -0
- package/lib/auto/close-loop-runner.js +2476 -0
- package/lib/auto/config-schema.js +176 -0
- package/lib/auto/decision-engine.js +344 -0
- package/lib/auto/error-recovery-manager.js +580 -0
- package/lib/auto/goal-decomposer.js +278 -0
- package/lib/auto/progress-tracker.js +502 -0
- package/lib/auto/safety-manager.js +186 -0
- package/lib/auto/semantic-decomposer.js +137 -0
- package/lib/auto/state-manager.js +126 -0
- package/lib/auto/task-queue-manager.js +340 -0
- package/lib/backup/backup-system.js +372 -0
- package/lib/backup/selective-backup.js +207 -0
- package/lib/collab/agent-registry.js +240 -0
- package/lib/collab/collab-manager.js +285 -0
- package/lib/collab/contract-manager.js +320 -0
- package/lib/collab/coordinator.js +370 -0
- package/lib/collab/dependency-manager.js +280 -0
- package/lib/collab/index.js +20 -0
- package/lib/collab/integration-manager.js +202 -0
- package/lib/collab/merge-coordinator.js +252 -0
- package/lib/collab/metadata-manager.js +233 -0
- package/lib/collab/multi-agent-config.js +120 -0
- package/lib/collab/spec-lifecycle-manager.js +304 -0
- package/lib/collab/sync-barrier.js +88 -0
- package/lib/collab/visualizer.js +208 -0
- package/lib/commands/adopt.js +749 -0
- package/lib/commands/auto.js +19559 -0
- package/lib/commands/collab.js +275 -0
- package/lib/commands/context.js +99 -0
- package/lib/commands/docs.js +808 -0
- package/lib/commands/doctor.js +273 -0
- package/lib/commands/env.js +420 -0
- package/lib/commands/knowledge.js +309 -0
- package/lib/commands/lock.js +235 -0
- package/lib/commands/ops.js +409 -0
- package/lib/commands/orchestrate.js +446 -0
- package/lib/commands/prompt.js +105 -0
- package/lib/commands/repo.js +118 -0
- package/lib/commands/rollback.js +219 -0
- package/lib/commands/scene.js +15549 -0
- package/lib/commands/spec-bootstrap.js +147 -0
- package/lib/commands/spec-gate.js +157 -0
- package/lib/commands/spec-pipeline.js +205 -0
- package/lib/commands/status.js +321 -0
- package/lib/commands/task.js +199 -0
- package/lib/commands/templates.js +654 -0
- package/lib/commands/upgrade.js +231 -0
- package/lib/commands/value.js +569 -0
- package/lib/commands/watch.js +684 -0
- package/lib/commands/workflows.js +240 -0
- package/lib/commands/workspace-multi.js +325 -0
- package/lib/commands/workspace.js +189 -0
- package/lib/context/context-exporter.js +378 -0
- package/lib/context/prompt-generator.js +482 -0
- package/lib/data/moqui-capability-lexicon.json +45 -0
- package/lib/environment/backup-system.js +189 -0
- package/lib/environment/environment-manager.js +379 -0
- package/lib/environment/environment-registry.js +168 -0
- package/lib/gitignore/gitignore-backup.js +229 -0
- package/lib/gitignore/gitignore-detector.js +239 -0
- package/lib/gitignore/gitignore-integration.js +267 -0
- package/lib/gitignore/gitignore-transformer.js +193 -0
- package/lib/gitignore/layered-rules-template.js +42 -0
- package/lib/governance/archive-tool.js +284 -0
- package/lib/governance/cleanup-tool.js +237 -0
- package/lib/governance/config-manager.js +186 -0
- package/lib/governance/diagnostic-engine.js +271 -0
- package/lib/governance/doc-reference-checker.js +200 -0
- package/lib/governance/execution-logger.js +243 -0
- package/lib/governance/file-scanner.js +285 -0
- package/lib/governance/hooks-manager.js +333 -0
- package/lib/governance/reporter.js +337 -0
- package/lib/governance/validation-engine.js +181 -0
- package/lib/i18n.js +79 -0
- package/lib/knowledge/entry-manager.js +208 -0
- package/lib/knowledge/index-manager.js +261 -0
- package/lib/knowledge/knowledge-manager.js +273 -0
- package/lib/knowledge/template-manager.js +191 -0
- package/lib/lock/index.js +21 -0
- package/lib/lock/lock-file.js +192 -0
- package/lib/lock/lock-manager.js +321 -0
- package/lib/lock/machine-identifier.js +135 -0
- package/lib/lock/steering-file-lock.js +207 -0
- package/lib/lock/task-lock-manager.js +345 -0
- package/lib/operations/audit-logger.js +293 -0
- package/lib/operations/feedback-manager.js +1147 -0
- package/lib/operations/index.js +23 -0
- package/lib/operations/models/index.js +170 -0
- package/lib/operations/operations-manager.js +151 -0
- package/lib/operations/operations-validator.js +280 -0
- package/lib/operations/permission-manager.js +354 -0
- package/lib/operations/template-loader.js +143 -0
- package/lib/orchestrator/agent-spawner.js +629 -0
- package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
- package/lib/orchestrator/index.js +19 -0
- package/lib/orchestrator/orchestration-engine.js +1270 -0
- package/lib/orchestrator/orchestrator-config.js +173 -0
- package/lib/orchestrator/status-monitor.js +591 -0
- package/lib/python-checker.js +209 -0
- package/lib/repo/config-manager.js +580 -0
- package/lib/repo/errors/config-error.js +13 -0
- package/lib/repo/errors/git-error.js +15 -0
- package/lib/repo/errors/repo-error.js +14 -0
- package/lib/repo/git-operations.js +181 -0
- package/lib/repo/handlers/.gitkeep +1 -0
- package/lib/repo/handlers/exec-handler.js +155 -0
- package/lib/repo/handlers/health-handler.js +169 -0
- package/lib/repo/handlers/init-handler.js +197 -0
- package/lib/repo/handlers/status-handler.js +176 -0
- package/lib/repo/output-formatter.js +184 -0
- package/lib/repo/path-resolver.js +178 -0
- package/lib/repo/repo-manager.js +514 -0
- package/lib/scene-runtime/audit-emitter.js +59 -0
- package/lib/scene-runtime/binding-plugin-loader.js +351 -0
- package/lib/scene-runtime/binding-registry.js +349 -0
- package/lib/scene-runtime/eval-bridge.js +44 -0
- package/lib/scene-runtime/index.js +19 -0
- package/lib/scene-runtime/moqui-adapter.js +620 -0
- package/lib/scene-runtime/moqui-client.js +606 -0
- package/lib/scene-runtime/moqui-extractor.js +2029 -0
- package/lib/scene-runtime/plan-compiler.js +208 -0
- package/lib/scene-runtime/policy-gate.js +58 -0
- package/lib/scene-runtime/runtime-executor.js +358 -0
- package/lib/scene-runtime/scene-loader.js +96 -0
- package/lib/scene-runtime/scene-ontology.js +959 -0
- package/lib/scene-runtime/scene-template-linter.js +852 -0
- package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
- package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
- package/lib/spec/bootstrap/context-collector.js +48 -0
- package/lib/spec/bootstrap/draft-generator.js +158 -0
- package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
- package/lib/spec/bootstrap/trace-emitter.js +59 -0
- package/lib/spec/multi-spec-orchestrate.js +93 -0
- package/lib/spec/pipeline/constants.js +6 -0
- package/lib/spec/pipeline/stage-adapters.js +118 -0
- package/lib/spec/pipeline/stage-runner.js +146 -0
- package/lib/spec/pipeline/state-store.js +119 -0
- package/lib/spec-gate/engine/gate-engine.js +165 -0
- package/lib/spec-gate/policy/default-policy.js +22 -0
- package/lib/spec-gate/policy/policy-loader.js +103 -0
- package/lib/spec-gate/result-emitter.js +81 -0
- package/lib/spec-gate/rules/default-rules.js +156 -0
- package/lib/spec-gate/rules/rule-registry.js +51 -0
- package/lib/steering/adoption-config.js +164 -0
- package/lib/steering/compliance-auto-fixer.js +204 -0
- package/lib/steering/compliance-cache.js +99 -0
- package/lib/steering/compliance-error-reporter.js +70 -0
- package/lib/steering/context-sync-manager.js +273 -0
- package/lib/steering/index.js +92 -0
- package/lib/steering/spec-steering.js +230 -0
- package/lib/steering/steering-compliance-checker.js +73 -0
- package/lib/steering/steering-loader.js +144 -0
- package/lib/steering/steering-manager.js +289 -0
- package/lib/task/index.js +12 -0
- package/lib/task/task-claimer.js +489 -0
- package/lib/task/task-status-store.js +418 -0
- package/lib/templates/cache-manager.js +440 -0
- package/lib/templates/content-generalizer.js +247 -0
- package/lib/templates/frontmatter-generator.js +128 -0
- package/lib/templates/git-handler.js +471 -0
- package/lib/templates/metadata-collector.js +328 -0
- package/lib/templates/path-utils.js +144 -0
- package/lib/templates/registry-parser.js +505 -0
- package/lib/templates/spec-reader.js +216 -0
- package/lib/templates/template-applicator.js +249 -0
- package/lib/templates/template-creator.js +256 -0
- package/lib/templates/template-error.js +143 -0
- package/lib/templates/template-exporter.js +502 -0
- package/lib/templates/template-manager.js +782 -0
- package/lib/templates/template-validator.js +361 -0
- package/lib/upgrade/migration-engine.js +382 -0
- package/lib/upgrade/migrations/.gitkeep +52 -0
- package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
- package/lib/utils/file-diff.js +177 -0
- package/lib/utils/fs-utils.js +274 -0
- package/lib/utils/tool-detector.js +383 -0
- package/lib/utils/validation.js +324 -0
- package/lib/value/gate-summary-emitter.js +99 -0
- package/lib/value/metric-contract-loader.js +210 -0
- package/lib/value/risk-evaluator.js +117 -0
- package/lib/value/weekly-snapshot-builder.js +61 -0
- package/lib/version/version-checker.js +156 -0
- package/lib/version/version-manager.js +327 -0
- package/lib/watch/action-executor.js +458 -0
- package/lib/watch/event-debouncer.js +323 -0
- package/lib/watch/execution-logger.js +550 -0
- package/lib/watch/file-watcher.js +499 -0
- package/lib/watch/presets.js +266 -0
- package/lib/watch/watch-manager.js +533 -0
- package/lib/workspace/multi/global-config.js +150 -0
- package/lib/workspace/multi/index.js +22 -0
- package/lib/workspace/multi/path-utils.js +173 -0
- package/lib/workspace/multi/workspace-context-resolver.js +244 -0
- package/lib/workspace/multi/workspace-registry.js +196 -0
- package/lib/workspace/multi/workspace-state-manager.js +537 -0
- package/lib/workspace/multi/workspace.js +90 -0
- package/lib/workspace/workspace-manager.js +370 -0
- package/lib/workspace/workspace-sync.js +356 -0
- package/locales/en.json +114 -0
- package/locales/zh.json +114 -0
- package/package.json +102 -0
- package/template/.kiro/README.md +247 -0
- package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
- package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
- package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
- package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
- package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
- package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
- package/template/.kiro/steering/ENVIRONMENT.md +35 -0
- package/template/.kiro/steering/RULES_GUIDE.md +46 -0
- package/template/.kiro/templates/operations/default/change-impact.md +112 -0
- package/template/.kiro/templates/operations/default/deployment.md +91 -0
- package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
- package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
- package/template/.kiro/templates/operations/default/monitoring.md +135 -0
- package/template/.kiro/templates/operations/default/operations.md +135 -0
- package/template/.kiro/templates/operations/default/rollback.md +143 -0
- package/template/.kiro/templates/operations/default/tools.yaml +364 -0
- package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
- package/template/.kiro/tools/backup_manager.py +295 -0
- package/template/.kiro/tools/configuration_manager.py +218 -0
- package/template/.kiro/tools/document_evaluator.py +550 -0
- package/template/.kiro/tools/enhancement_logger.py +168 -0
- package/template/.kiro/tools/error_handler.py +335 -0
- package/template/.kiro/tools/improvement_identifier.py +444 -0
- package/template/.kiro/tools/modification_applicator.py +737 -0
- package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
- package/template/.kiro/tools/quality_scorer.py +305 -0
- package/template/.kiro/tools/report_generator.py +154 -0
- package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
- package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
- package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
- package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
- package/template/.kiro/tools/workflow_quality_gate.py +100 -0
- package/template/README.md +111 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides validation functions for project structure, version files,
|
|
5
|
+
* and dependencies. Used for post-operation validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { pathExists, readJSON } = require('./fs-utils');
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validates project structure
|
|
14
|
+
* Checks if all required files and directories exist
|
|
15
|
+
*
|
|
16
|
+
* @param {string} projectPath - Absolute path to project root
|
|
17
|
+
* @returns {Promise<ValidationResult>}
|
|
18
|
+
*/
|
|
19
|
+
async function validateProjectStructure(projectPath) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const warnings = [];
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const kiroPath = path.join(projectPath, '.kiro');
|
|
25
|
+
|
|
26
|
+
// Check if .kiro/ directory exists
|
|
27
|
+
const kiroExists = await pathExists(kiroPath);
|
|
28
|
+
if (!kiroExists) {
|
|
29
|
+
errors.push('.kiro/ directory not found');
|
|
30
|
+
return { success: false, errors, warnings };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check required directories
|
|
34
|
+
const requiredDirs = [
|
|
35
|
+
{ path: 'specs', required: false },
|
|
36
|
+
{ path: 'steering', required: true },
|
|
37
|
+
{ path: 'tools', required: true },
|
|
38
|
+
{ path: 'backups', required: false }
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const dir of requiredDirs) {
|
|
42
|
+
const dirPath = path.join(kiroPath, dir.path);
|
|
43
|
+
const exists = await pathExists(dirPath);
|
|
44
|
+
|
|
45
|
+
if (!exists) {
|
|
46
|
+
if (dir.required) {
|
|
47
|
+
errors.push(`Required directory not found: ${dir.path}/`);
|
|
48
|
+
} else {
|
|
49
|
+
warnings.push(`Optional directory not found: ${dir.path}/`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check required steering files
|
|
55
|
+
const requiredSteeringFiles = [
|
|
56
|
+
{ path: 'steering/CORE_PRINCIPLES.md', required: true },
|
|
57
|
+
{ path: 'steering/ENVIRONMENT.md', required: true },
|
|
58
|
+
{ path: 'steering/CURRENT_CONTEXT.md', required: true },
|
|
59
|
+
{ path: 'steering/RULES_GUIDE.md', required: true }
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
for (const file of requiredSteeringFiles) {
|
|
63
|
+
const filePath = path.join(kiroPath, file.path);
|
|
64
|
+
const exists = await pathExists(filePath);
|
|
65
|
+
|
|
66
|
+
if (!exists) {
|
|
67
|
+
if (file.required) {
|
|
68
|
+
errors.push(`Required file not found: ${file.path}`);
|
|
69
|
+
} else {
|
|
70
|
+
warnings.push(`Optional file not found: ${file.path}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check required tool files
|
|
76
|
+
const requiredToolFiles = [
|
|
77
|
+
{ path: 'tools/ultrawork_enhancer.py', required: true }
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
for (const file of requiredToolFiles) {
|
|
81
|
+
const filePath = path.join(kiroPath, file.path);
|
|
82
|
+
const exists = await pathExists(filePath);
|
|
83
|
+
|
|
84
|
+
if (!exists) {
|
|
85
|
+
if (file.required) {
|
|
86
|
+
warnings.push(`Tool file not found: ${file.path}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: errors.length === 0,
|
|
93
|
+
errors,
|
|
94
|
+
warnings
|
|
95
|
+
};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
errors.push(`Validation failed: ${error.message}`);
|
|
98
|
+
return { success: false, errors, warnings };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Validates version.json file structure
|
|
104
|
+
*
|
|
105
|
+
* @param {string} projectPath - Absolute path to project root
|
|
106
|
+
* @returns {Promise<ValidationResult>}
|
|
107
|
+
*/
|
|
108
|
+
async function validateVersionFile(projectPath) {
|
|
109
|
+
const errors = [];
|
|
110
|
+
const warnings = [];
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const versionPath = path.join(projectPath, '.kiro', 'version.json');
|
|
114
|
+
|
|
115
|
+
// Check if version.json exists
|
|
116
|
+
const exists = await pathExists(versionPath);
|
|
117
|
+
if (!exists) {
|
|
118
|
+
errors.push('version.json not found');
|
|
119
|
+
return { success: false, errors, warnings };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Read and validate structure
|
|
123
|
+
const versionInfo = await readJSON(versionPath);
|
|
124
|
+
|
|
125
|
+
// Check required fields
|
|
126
|
+
const requiredFields = [
|
|
127
|
+
'kse-version',
|
|
128
|
+
'template-version',
|
|
129
|
+
'created',
|
|
130
|
+
'last-upgraded',
|
|
131
|
+
'upgrade-history'
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
for (const field of requiredFields) {
|
|
135
|
+
if (!(field in versionInfo)) {
|
|
136
|
+
errors.push(`Missing required field in version.json: ${field}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Validate field types
|
|
141
|
+
if (versionInfo['kse-version'] && typeof versionInfo['kse-version'] !== 'string') {
|
|
142
|
+
errors.push('kse-version must be a string');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (versionInfo['template-version'] && typeof versionInfo['template-version'] !== 'string') {
|
|
146
|
+
errors.push('template-version must be a string');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (versionInfo['upgrade-history'] && !Array.isArray(versionInfo['upgrade-history'])) {
|
|
150
|
+
errors.push('upgrade-history must be an array');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Validate upgrade history entries
|
|
154
|
+
if (Array.isArray(versionInfo['upgrade-history'])) {
|
|
155
|
+
versionInfo['upgrade-history'].forEach((entry, index) => {
|
|
156
|
+
if (!entry.from || !entry.to || !entry.date || typeof entry.success !== 'boolean') {
|
|
157
|
+
warnings.push(`Invalid upgrade history entry at index ${index}`);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
success: errors.length === 0,
|
|
164
|
+
errors,
|
|
165
|
+
warnings
|
|
166
|
+
};
|
|
167
|
+
} catch (error) {
|
|
168
|
+
errors.push(`Failed to validate version.json: ${error.message}`);
|
|
169
|
+
return { success: false, errors, warnings };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Validates dependencies (Node.js and Python versions)
|
|
175
|
+
*
|
|
176
|
+
* @param {string} projectPath - Absolute path to project root
|
|
177
|
+
* @returns {Promise<ValidationResult>}
|
|
178
|
+
*/
|
|
179
|
+
async function validateDependencies(projectPath) {
|
|
180
|
+
const errors = [];
|
|
181
|
+
const warnings = [];
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
// Check Node.js version
|
|
185
|
+
const nodeVersion = process.version;
|
|
186
|
+
const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
187
|
+
|
|
188
|
+
if (nodeMajor < 16) {
|
|
189
|
+
errors.push(`Node.js version ${nodeVersion} is not supported. Requires Node.js 16+`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check Python version (if Ultrawork tools are present)
|
|
193
|
+
const toolPath = path.join(projectPath, '.kiro/tools/ultrawork_enhancer.py');
|
|
194
|
+
const toolExists = await pathExists(toolPath);
|
|
195
|
+
|
|
196
|
+
if (toolExists) {
|
|
197
|
+
try {
|
|
198
|
+
const pythonVersion = await checkPythonVersion();
|
|
199
|
+
|
|
200
|
+
if (!pythonVersion) {
|
|
201
|
+
warnings.push('Python not found. Ultrawork tools require Python 3.8+');
|
|
202
|
+
} else {
|
|
203
|
+
const [major, minor] = pythonVersion.split('.').map(Number);
|
|
204
|
+
|
|
205
|
+
if (major < 3 || (major === 3 && minor < 8)) {
|
|
206
|
+
warnings.push(`Python ${pythonVersion} found. Ultrawork tools require Python 3.8+`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
warnings.push('Could not check Python version');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
success: errors.length === 0,
|
|
216
|
+
errors,
|
|
217
|
+
warnings
|
|
218
|
+
};
|
|
219
|
+
} catch (error) {
|
|
220
|
+
errors.push(`Failed to validate dependencies: ${error.message}`);
|
|
221
|
+
return { success: false, errors, warnings };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Checks Python version
|
|
227
|
+
*
|
|
228
|
+
* @returns {Promise<string|null>} - Python version string or null if not found
|
|
229
|
+
*/
|
|
230
|
+
function checkPythonVersion() {
|
|
231
|
+
return new Promise((resolve) => {
|
|
232
|
+
const python = spawn('python', ['--version']);
|
|
233
|
+
let output = '';
|
|
234
|
+
let settled = false;
|
|
235
|
+
let timeoutId = null;
|
|
236
|
+
|
|
237
|
+
const finish = (value) => {
|
|
238
|
+
if (settled) return;
|
|
239
|
+
settled = true;
|
|
240
|
+
if (timeoutId) {
|
|
241
|
+
clearTimeout(timeoutId);
|
|
242
|
+
}
|
|
243
|
+
resolve(value);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
python.stdout.on('data', (data) => {
|
|
247
|
+
output += data.toString();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
python.stderr.on('data', (data) => {
|
|
251
|
+
output += data.toString();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
python.on('close', (code) => {
|
|
255
|
+
if (code === 0 && output) {
|
|
256
|
+
// Extract version number from "Python 3.x.x"
|
|
257
|
+
const match = output.match(/Python (\d+\.\d+\.\d+)/);
|
|
258
|
+
if (match) {
|
|
259
|
+
finish(match[1]);
|
|
260
|
+
} else {
|
|
261
|
+
finish(null);
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
finish(null);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
python.on('error', () => {
|
|
269
|
+
finish(null);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Timeout after 5 seconds
|
|
273
|
+
timeoutId = setTimeout(() => {
|
|
274
|
+
try {
|
|
275
|
+
python.kill();
|
|
276
|
+
} catch (_err) {
|
|
277
|
+
// Process may already be gone.
|
|
278
|
+
}
|
|
279
|
+
finish(null);
|
|
280
|
+
}, 5000);
|
|
281
|
+
if (typeof timeoutId.unref === 'function') {
|
|
282
|
+
timeoutId.unref();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Validates complete project (structure + version + dependencies)
|
|
289
|
+
*
|
|
290
|
+
* @param {string} projectPath - Absolute path to project root
|
|
291
|
+
* @returns {Promise<ValidationResult>}
|
|
292
|
+
*/
|
|
293
|
+
async function validateProject(projectPath) {
|
|
294
|
+
const allErrors = [];
|
|
295
|
+
const allWarnings = [];
|
|
296
|
+
|
|
297
|
+
// Run all validations
|
|
298
|
+
const structureResult = await validateProjectStructure(projectPath);
|
|
299
|
+
const versionResult = await validateVersionFile(projectPath);
|
|
300
|
+
const depsResult = await validateDependencies(projectPath);
|
|
301
|
+
|
|
302
|
+
// Combine results
|
|
303
|
+
allErrors.push(...structureResult.errors);
|
|
304
|
+
allErrors.push(...versionResult.errors);
|
|
305
|
+
allErrors.push(...depsResult.errors);
|
|
306
|
+
|
|
307
|
+
allWarnings.push(...structureResult.warnings);
|
|
308
|
+
allWarnings.push(...versionResult.warnings);
|
|
309
|
+
allWarnings.push(...depsResult.warnings);
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
success: allErrors.length === 0,
|
|
313
|
+
errors: allErrors,
|
|
314
|
+
warnings: allWarnings
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
module.exports = {
|
|
319
|
+
validateProjectStructure,
|
|
320
|
+
validateVersionFile,
|
|
321
|
+
validateDependencies,
|
|
322
|
+
validateProject,
|
|
323
|
+
checkPythonVersion
|
|
324
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const { parseTargetExpression } = require('./metric-contract-loader');
|
|
2
|
+
|
|
3
|
+
function evaluateTarget(value, targetRule) {
|
|
4
|
+
const numericValue = Number(value);
|
|
5
|
+
if (!Number.isFinite(numericValue)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
switch (targetRule.operator) {
|
|
10
|
+
case '<=':
|
|
11
|
+
return numericValue <= targetRule.value;
|
|
12
|
+
case '>=':
|
|
13
|
+
return numericValue >= targetRule.value;
|
|
14
|
+
case '<':
|
|
15
|
+
return numericValue < targetRule.value;
|
|
16
|
+
case '>':
|
|
17
|
+
return numericValue > targetRule.value;
|
|
18
|
+
case '=':
|
|
19
|
+
return numericValue === targetRule.value;
|
|
20
|
+
default:
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class GateSummaryEmitter {
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
this.defaultThresholdRatio = options.defaultThresholdRatio || 0.75;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
build(options = {}) {
|
|
31
|
+
const {
|
|
32
|
+
checkpoint = 'day-30',
|
|
33
|
+
snapshot,
|
|
34
|
+
contract,
|
|
35
|
+
evidence = []
|
|
36
|
+
} = options;
|
|
37
|
+
|
|
38
|
+
if (!snapshot || typeof snapshot !== 'object') {
|
|
39
|
+
throw new Error('snapshot is required');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!contract || !Array.isArray(contract.metrics)) {
|
|
43
|
+
throw new Error('metric contract is required');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const checks = contract.metrics.map(metric => {
|
|
47
|
+
const targetRule = metric.target_rule || parseTargetExpression(metric.target);
|
|
48
|
+
const value = Number(snapshot[metric.id]);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
metric_id: metric.id,
|
|
52
|
+
value,
|
|
53
|
+
target: targetRule.raw,
|
|
54
|
+
operator: targetRule.operator,
|
|
55
|
+
target_value: targetRule.value,
|
|
56
|
+
passed: evaluateTarget(value, targetRule)
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const passedMetrics = checks.filter(item => item.passed).length;
|
|
61
|
+
const totalMetrics = checks.length;
|
|
62
|
+
const requiredPassedMetrics = this.resolveRequiredPassCount(checkpoint, totalMetrics, contract);
|
|
63
|
+
const decision = passedMetrics >= requiredPassedMetrics ? 'go' : 'no-go';
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
checkpoint,
|
|
67
|
+
passed_metrics: passedMetrics,
|
|
68
|
+
total_metrics: totalMetrics,
|
|
69
|
+
required_passed_metrics: requiredPassedMetrics,
|
|
70
|
+
decision,
|
|
71
|
+
checks,
|
|
72
|
+
evidence
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resolveRequiredPassCount(checkpoint, totalMetrics, contract) {
|
|
77
|
+
const goNoGo = contract
|
|
78
|
+
&& contract.threshold_policy
|
|
79
|
+
&& contract.threshold_policy.go_no_go
|
|
80
|
+
? contract.threshold_policy.go_no_go
|
|
81
|
+
: {};
|
|
82
|
+
|
|
83
|
+
let configuredThreshold;
|
|
84
|
+
if (checkpoint === 'day-60') {
|
|
85
|
+
configuredThreshold = Number(goNoGo.day_60_min_passed_metrics);
|
|
86
|
+
} else if (checkpoint === 'day-30') {
|
|
87
|
+
configuredThreshold = Number(goNoGo.day_30_min_passed_metrics);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (Number.isFinite(configuredThreshold) && configuredThreshold > 0) {
|
|
91
|
+
return Math.min(totalMetrics, Math.floor(configuredThreshold));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return Math.max(1, Math.ceil(totalMetrics * this.defaultThresholdRatio));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = GateSummaryEmitter;
|
|
99
|
+
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
|
|
5
|
+
const REQUIRED_METRIC_IDS = [
|
|
6
|
+
'ttfv_minutes',
|
|
7
|
+
'batch_success_rate',
|
|
8
|
+
'cycle_reduction_rate',
|
|
9
|
+
'manual_takeover_rate'
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const METRIC_DIRECTION_HINTS = {
|
|
13
|
+
ttfv_minutes: 'lower',
|
|
14
|
+
batch_success_rate: 'higher',
|
|
15
|
+
cycle_reduction_rate: 'higher',
|
|
16
|
+
manual_takeover_rate: 'lower'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function parseTargetExpression(target) {
|
|
20
|
+
if (typeof target === 'number' && Number.isFinite(target)) {
|
|
21
|
+
return {
|
|
22
|
+
operator: '=',
|
|
23
|
+
value: target,
|
|
24
|
+
raw: String(target)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof target !== 'string') {
|
|
29
|
+
throw new Error(`Invalid target expression type: ${typeof target}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const match = /^(<=|>=|<|>|=)\s*(-?\d+(?:\.\d+)?)$/.exec(target.trim());
|
|
33
|
+
if (!match) {
|
|
34
|
+
throw new Error(`Invalid target expression: ${target}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
operator: match[1],
|
|
39
|
+
value: Number(match[2]),
|
|
40
|
+
raw: target
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class MetricContractLoader {
|
|
45
|
+
constructor(projectPath = process.cwd()) {
|
|
46
|
+
this.projectPath = projectPath;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getDefaultPath() {
|
|
50
|
+
return path.join(
|
|
51
|
+
this.projectPath,
|
|
52
|
+
'.kiro',
|
|
53
|
+
'specs',
|
|
54
|
+
'112-00-spec-value-realization-program',
|
|
55
|
+
'custom',
|
|
56
|
+
'metric-definition.yaml'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
resolvePath(contractPath) {
|
|
61
|
+
if (!contractPath) {
|
|
62
|
+
return this.getDefaultPath();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return path.isAbsolute(contractPath)
|
|
66
|
+
? contractPath
|
|
67
|
+
: path.join(this.projectPath, contractPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async load(options = {}) {
|
|
71
|
+
const contractPath = this.resolvePath(options.path);
|
|
72
|
+
|
|
73
|
+
if (!await fs.pathExists(contractPath)) {
|
|
74
|
+
throw new Error(`Metric contract not found: ${contractPath}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const raw = await fs.readFile(contractPath, 'utf8');
|
|
78
|
+
const extension = path.extname(contractPath).toLowerCase();
|
|
79
|
+
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = extension === '.json' ? JSON.parse(raw) : yaml.load(raw);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(`Failed to parse metric contract: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const normalized = this.normalize(parsed);
|
|
88
|
+
const validation = this.validate(normalized);
|
|
89
|
+
|
|
90
|
+
if (!validation.valid) {
|
|
91
|
+
throw new Error(`Invalid metric contract: ${validation.errors.join('; ')}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
contract: normalized,
|
|
96
|
+
contractPath
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
normalize(contract = {}) {
|
|
101
|
+
const metrics = Array.isArray(contract.metrics) ? contract.metrics : [];
|
|
102
|
+
|
|
103
|
+
const normalizedMetrics = metrics.map(metric => {
|
|
104
|
+
const id = typeof metric.id === 'string' ? metric.id.trim() : '';
|
|
105
|
+
const targetRule = parseTargetExpression(metric.target);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
...metric,
|
|
109
|
+
id,
|
|
110
|
+
unit: typeof metric.unit === 'string' ? metric.unit.trim().toLowerCase() : metric.unit,
|
|
111
|
+
better_direction: this.inferBetterDirection(id, metric, targetRule),
|
|
112
|
+
target_rule: targetRule
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const metricMap = {};
|
|
117
|
+
normalizedMetrics.forEach(metric => {
|
|
118
|
+
metricMap[metric.id] = metric;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
...contract,
|
|
123
|
+
metrics: normalizedMetrics,
|
|
124
|
+
metric_map: metricMap
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
inferBetterDirection(metricId, metric, targetRule) {
|
|
129
|
+
if (metric && typeof metric.better_direction === 'string') {
|
|
130
|
+
return metric.better_direction.trim().toLowerCase();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (METRIC_DIRECTION_HINTS[metricId]) {
|
|
134
|
+
return METRIC_DIRECTION_HINTS[metricId];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (targetRule.operator === '<=' || targetRule.operator === '<') {
|
|
138
|
+
return 'lower';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return 'higher';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
validate(contract = {}) {
|
|
145
|
+
const errors = [];
|
|
146
|
+
|
|
147
|
+
if (!contract || typeof contract !== 'object') {
|
|
148
|
+
return {
|
|
149
|
+
valid: false,
|
|
150
|
+
errors: ['Contract must be an object']
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!Array.isArray(contract.metrics) || contract.metrics.length === 0) {
|
|
155
|
+
errors.push('metrics must be a non-empty array');
|
|
156
|
+
return { valid: false, errors };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const presentMetricIds = new Set();
|
|
160
|
+
|
|
161
|
+
contract.metrics.forEach((metric, index) => {
|
|
162
|
+
if (!metric.id || typeof metric.id !== 'string') {
|
|
163
|
+
errors.push(`metrics[${index}].id is required`);
|
|
164
|
+
} else {
|
|
165
|
+
presentMetricIds.add(metric.id);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!metric.name || typeof metric.name !== 'string') {
|
|
169
|
+
errors.push(`metrics[${index}].name is required`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!metric.unit || typeof metric.unit !== 'string') {
|
|
173
|
+
errors.push(`metrics[${index}].unit is required`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!metric.target_rule) {
|
|
177
|
+
errors.push(`metrics[${index}].target is required`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!Number.isFinite(metric.target_rule.value)) {
|
|
182
|
+
errors.push(`metrics[${index}].target value must be numeric`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (metric.unit === 'ratio' && (metric.target_rule.value < 0 || metric.target_rule.value > 1)) {
|
|
186
|
+
errors.push(`metrics[${index}].target ratio must be between 0 and 1`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (metric.id === 'ttfv_minutes' && metric.target_rule.value < 0) {
|
|
190
|
+
errors.push('ttfv_minutes target must be >= 0');
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
REQUIRED_METRIC_IDS.forEach(metricId => {
|
|
195
|
+
if (!presentMetricIds.has(metricId)) {
|
|
196
|
+
errors.push(`required metric is missing: ${metricId}`);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
valid: errors.length === 0,
|
|
202
|
+
errors
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = MetricContractLoader;
|
|
208
|
+
module.exports.REQUIRED_METRIC_IDS = REQUIRED_METRIC_IDS;
|
|
209
|
+
module.exports.parseTargetExpression = parseTargetExpression;
|
|
210
|
+
|