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,569 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
const MetricContractLoader = require('../value/metric-contract-loader');
|
|
6
|
+
const WeeklySnapshotBuilder = require('../value/weekly-snapshot-builder');
|
|
7
|
+
const RiskEvaluator = require('../value/risk-evaluator');
|
|
8
|
+
const GateSummaryEmitter = require('../value/gate-summary-emitter');
|
|
9
|
+
const { parsePeriodKey } = require('../value/risk-evaluator');
|
|
10
|
+
|
|
11
|
+
function toProjectPath(projectPath, maybePath) {
|
|
12
|
+
if (!maybePath) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return path.isAbsolute(maybePath)
|
|
17
|
+
? maybePath
|
|
18
|
+
: path.join(projectPath, maybePath);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function toPortablePath(projectPath, filePath) {
|
|
22
|
+
return path.relative(projectPath, filePath).replace(/\\/g, '/');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function loadJson(filePath) {
|
|
26
|
+
try {
|
|
27
|
+
return await fs.readJson(filePath);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new Error(`Failed to read JSON file (${filePath}): ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function loadHistorySnapshots(historyDir) {
|
|
34
|
+
if (!await fs.pathExists(historyDir)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const entries = await fs.readdir(historyDir);
|
|
39
|
+
const snapshots = [];
|
|
40
|
+
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (!entry.endsWith('.json')) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (entry.startsWith('gate-summary.') || entry.includes('risk-evaluation')) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const filePath = path.join(historyDir, entry);
|
|
51
|
+
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = await fs.readJson(filePath);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (parsed && typeof parsed.period === 'string') {
|
|
60
|
+
snapshots.push(parsed);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return snapshots;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sortSnapshotsByPeriod(snapshots = []) {
|
|
68
|
+
return [...snapshots].sort((left, right) => parsePeriodKey(left.period) - parsePeriodKey(right.period));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parsePositiveInteger(input, fallbackValue) {
|
|
72
|
+
const parsed = Number.parseInt(input, 10);
|
|
73
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
74
|
+
return parsed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return fallbackValue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function inferMetricDirection(metric = {}) {
|
|
81
|
+
if (typeof metric.better_direction === 'string' && metric.better_direction.trim()) {
|
|
82
|
+
return metric.better_direction.trim().toLowerCase();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const targetRule = metric.target_rule;
|
|
86
|
+
if (!targetRule || typeof targetRule.operator !== 'string') {
|
|
87
|
+
return 'higher';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (targetRule.operator.includes('<')) {
|
|
91
|
+
return 'lower';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return 'higher';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function evaluateTarget(value, targetRule) {
|
|
98
|
+
const numericValue = Number(value);
|
|
99
|
+
if (!Number.isFinite(numericValue) || !targetRule) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
switch (targetRule.operator) {
|
|
104
|
+
case '<=':
|
|
105
|
+
return numericValue <= targetRule.value;
|
|
106
|
+
case '>=':
|
|
107
|
+
return numericValue >= targetRule.value;
|
|
108
|
+
case '<':
|
|
109
|
+
return numericValue < targetRule.value;
|
|
110
|
+
case '>':
|
|
111
|
+
return numericValue > targetRule.value;
|
|
112
|
+
case '=':
|
|
113
|
+
return numericValue === targetRule.value;
|
|
114
|
+
default:
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getImprovement(delta, direction) {
|
|
120
|
+
if (!Number.isFinite(delta) || delta === 0) {
|
|
121
|
+
return 'flat';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (direction === 'lower') {
|
|
125
|
+
return delta < 0 ? 'improved' : 'degraded';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return delta > 0 ? 'improved' : 'degraded';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resolveMetricInput(inputPayload) {
|
|
132
|
+
if (inputPayload && typeof inputPayload.metrics === 'object' && inputPayload.metrics !== null) {
|
|
133
|
+
return inputPayload.metrics;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return inputPayload;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function defaultHistoryDir(projectPath) {
|
|
140
|
+
return path.join(
|
|
141
|
+
projectPath,
|
|
142
|
+
'.kiro',
|
|
143
|
+
'specs',
|
|
144
|
+
'114-00-kpi-automation-and-observability',
|
|
145
|
+
'custom',
|
|
146
|
+
'weekly-metrics'
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getIsoWeekPeriod(date = new Date()) {
|
|
151
|
+
const utcDate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
152
|
+
const day = utcDate.getUTCDay() || 7;
|
|
153
|
+
utcDate.setUTCDate(utcDate.getUTCDate() + 4 - day);
|
|
154
|
+
|
|
155
|
+
const yearStart = new Date(Date.UTC(utcDate.getUTCFullYear(), 0, 1));
|
|
156
|
+
const week = Math.ceil((((utcDate - yearStart) / 86400000) + 1) / 7);
|
|
157
|
+
|
|
158
|
+
return `${utcDate.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function createSampleMetricsInput(period) {
|
|
162
|
+
return {
|
|
163
|
+
period,
|
|
164
|
+
metrics: {
|
|
165
|
+
ttfv_minutes: 25,
|
|
166
|
+
batch_success_rate: 0.86,
|
|
167
|
+
cycle_reduction_rate: 0.34,
|
|
168
|
+
manual_takeover_rate: 0.16
|
|
169
|
+
},
|
|
170
|
+
notes: 'sample metrics input generated by kse value metrics sample'
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function runValueMetricsSample(options = {}, dependencies = {}) {
|
|
175
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
176
|
+
const period = options.period || getIsoWeekPeriod();
|
|
177
|
+
const sampleInput = createSampleMetricsInput(period);
|
|
178
|
+
|
|
179
|
+
const outputPath = toProjectPath(projectPath, options.out) || path.join(projectPath, 'kpi-input.json');
|
|
180
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
181
|
+
await fs.writeJson(outputPath, sampleInput, { spaces: 2 });
|
|
182
|
+
|
|
183
|
+
const result = {
|
|
184
|
+
success: true,
|
|
185
|
+
period,
|
|
186
|
+
sample_path: toPortablePath(projectPath, outputPath),
|
|
187
|
+
generated_at: new Date().toISOString()
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
if (options.json) {
|
|
191
|
+
console.log(JSON.stringify(result, null, 2));
|
|
192
|
+
} else if (!options.silent) {
|
|
193
|
+
console.log(chalk.green('✅ KPI sample input generated'));
|
|
194
|
+
console.log(chalk.gray(` period: ${result.period}`));
|
|
195
|
+
console.log(chalk.gray(` sample: ${result.sample_path}`));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function runValueMetricsSnapshot(options = {}, dependencies = {}) {
|
|
202
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
203
|
+
|
|
204
|
+
if (!options.input) {
|
|
205
|
+
throw new Error('--input <path> is required for snapshot generation. Tip: run "kse value metrics sample --out ./kpi-input.json" first.');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const loader = dependencies.metricContractLoader || new MetricContractLoader(projectPath);
|
|
209
|
+
const builder = dependencies.weeklySnapshotBuilder || new WeeklySnapshotBuilder();
|
|
210
|
+
const evaluator = dependencies.riskEvaluator || new RiskEvaluator();
|
|
211
|
+
const gateEmitter = dependencies.gateSummaryEmitter || new GateSummaryEmitter();
|
|
212
|
+
|
|
213
|
+
const { contract, contractPath } = await loader.load({
|
|
214
|
+
path: options.definitions
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const inputPath = toProjectPath(projectPath, options.input);
|
|
218
|
+
const inputPayload = await loadJson(inputPath);
|
|
219
|
+
|
|
220
|
+
const period = options.period || inputPayload.period;
|
|
221
|
+
const notes = options.notes || inputPayload.notes || '';
|
|
222
|
+
|
|
223
|
+
const snapshot = builder.build({
|
|
224
|
+
period,
|
|
225
|
+
metrics: resolveMetricInput(inputPayload),
|
|
226
|
+
notes,
|
|
227
|
+
contract
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const historyDir = toProjectPath(projectPath, options.historyDir) || defaultHistoryDir(projectPath);
|
|
231
|
+
await fs.ensureDir(historyDir);
|
|
232
|
+
|
|
233
|
+
const historySnapshots = await loadHistorySnapshots(historyDir);
|
|
234
|
+
const riskResult = evaluator.evaluate({
|
|
235
|
+
historySnapshots,
|
|
236
|
+
currentSnapshot: snapshot
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
snapshot.risk_level = riskResult.risk_level;
|
|
240
|
+
snapshot.reasons = riskResult.reasons;
|
|
241
|
+
|
|
242
|
+
const snapshotPath = toProjectPath(projectPath, options.out)
|
|
243
|
+
|| path.join(historyDir, `${snapshot.period}.json`);
|
|
244
|
+
|
|
245
|
+
await fs.ensureDir(path.dirname(snapshotPath));
|
|
246
|
+
await fs.writeJson(snapshotPath, snapshot, { spaces: 2 });
|
|
247
|
+
|
|
248
|
+
const checkpoint = options.checkpoint || 'day-30';
|
|
249
|
+
const gateSummary = gateEmitter.build({
|
|
250
|
+
checkpoint,
|
|
251
|
+
snapshot,
|
|
252
|
+
contract,
|
|
253
|
+
evidence: [toPortablePath(projectPath, snapshotPath)]
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const gateSummaryPath = toProjectPath(projectPath, options.gateOut)
|
|
257
|
+
|| path.join(path.dirname(snapshotPath), `gate-summary.${snapshot.period}.${checkpoint}.json`);
|
|
258
|
+
await fs.writeJson(gateSummaryPath, gateSummary, { spaces: 2 });
|
|
259
|
+
|
|
260
|
+
const result = {
|
|
261
|
+
success: true,
|
|
262
|
+
period: snapshot.period,
|
|
263
|
+
risk_level: snapshot.risk_level,
|
|
264
|
+
triggered_metrics: riskResult.triggered_metrics,
|
|
265
|
+
snapshot_path: toPortablePath(projectPath, snapshotPath),
|
|
266
|
+
gate_summary_path: toPortablePath(projectPath, gateSummaryPath),
|
|
267
|
+
contract_path: toPortablePath(projectPath, contractPath)
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
if (options.json) {
|
|
271
|
+
console.log(JSON.stringify(result, null, 2));
|
|
272
|
+
} else if (!options.silent) {
|
|
273
|
+
console.log(chalk.green('✅ KPI snapshot generated'));
|
|
274
|
+
console.log(chalk.gray(` period: ${result.period}`));
|
|
275
|
+
console.log(chalk.gray(` risk: ${result.risk_level}`));
|
|
276
|
+
console.log(chalk.gray(` snapshot: ${result.snapshot_path}`));
|
|
277
|
+
console.log(chalk.gray(` gate summary: ${result.gate_summary_path}`));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function runValueMetricsBaseline(options = {}, dependencies = {}) {
|
|
284
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
285
|
+
const loader = dependencies.metricContractLoader || new MetricContractLoader(projectPath);
|
|
286
|
+
const builder = dependencies.weeklySnapshotBuilder || new WeeklySnapshotBuilder();
|
|
287
|
+
|
|
288
|
+
const { contract, contractPath } = await loader.load({
|
|
289
|
+
path: options.definitions
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const historyDir = toProjectPath(projectPath, options.historyDir) || defaultHistoryDir(projectPath);
|
|
293
|
+
await fs.ensureDir(historyDir);
|
|
294
|
+
|
|
295
|
+
let period;
|
|
296
|
+
let notes;
|
|
297
|
+
let metricInput;
|
|
298
|
+
let baselineSource;
|
|
299
|
+
|
|
300
|
+
if (options.input) {
|
|
301
|
+
const inputPath = toProjectPath(projectPath, options.input);
|
|
302
|
+
const inputPayload = await loadJson(inputPath);
|
|
303
|
+
|
|
304
|
+
period = options.period || inputPayload.period;
|
|
305
|
+
notes = options.notes || inputPayload.notes || '';
|
|
306
|
+
metricInput = resolveMetricInput(inputPayload);
|
|
307
|
+
baselineSource = `input:${toPortablePath(projectPath, inputPath)}`;
|
|
308
|
+
} else {
|
|
309
|
+
const allSnapshots = sortSnapshotsByPeriod(await loadHistorySnapshots(historyDir));
|
|
310
|
+
if (allSnapshots.length === 0) {
|
|
311
|
+
throw new Error('--input is required when history directory has no snapshots. Tip: run "kse value metrics sample --out ./kpi-input.json" and then "kse value metrics snapshot --input ./kpi-input.json".');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const fromHistory = parsePositiveInteger(options.fromHistory, Math.min(3, allSnapshots.length));
|
|
315
|
+
const selected = allSnapshots.slice(0, Math.min(fromHistory, allSnapshots.length));
|
|
316
|
+
|
|
317
|
+
metricInput = {};
|
|
318
|
+
for (const metric of contract.metrics) {
|
|
319
|
+
const values = selected
|
|
320
|
+
.map(snapshot => Number(snapshot[metric.id]))
|
|
321
|
+
.filter(value => Number.isFinite(value));
|
|
322
|
+
|
|
323
|
+
if (values.length === 0) {
|
|
324
|
+
throw new Error(`Unable to derive baseline metric from history: ${metric.id}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const average = values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
328
|
+
metricInput[metric.id] = Number(average.toFixed(6));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const startPeriod = selected[0].period;
|
|
332
|
+
const endPeriod = selected[selected.length - 1].period;
|
|
333
|
+
period = options.period || endPeriod;
|
|
334
|
+
notes = options.notes || `Baseline derived from ${selected.length} snapshots (${startPeriod} -> ${endPeriod})`;
|
|
335
|
+
baselineSource = `history:${startPeriod}-${endPeriod}`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const baselineSnapshot = builder.build({
|
|
339
|
+
period,
|
|
340
|
+
metrics: metricInput,
|
|
341
|
+
notes,
|
|
342
|
+
contract
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
baselineSnapshot.is_baseline = true;
|
|
346
|
+
baselineSnapshot.baseline_source = baselineSource;
|
|
347
|
+
|
|
348
|
+
const baselinePath = toProjectPath(projectPath, options.out)
|
|
349
|
+
|| path.join(historyDir, `${baselineSnapshot.period}.baseline.json`);
|
|
350
|
+
await fs.ensureDir(path.dirname(baselinePath));
|
|
351
|
+
await fs.writeJson(baselinePath, baselineSnapshot, { spaces: 2 });
|
|
352
|
+
|
|
353
|
+
const result = {
|
|
354
|
+
success: true,
|
|
355
|
+
period: baselineSnapshot.period,
|
|
356
|
+
baseline_source: baselineSource,
|
|
357
|
+
baseline_path: toPortablePath(projectPath, baselinePath),
|
|
358
|
+
contract_path: toPortablePath(projectPath, contractPath)
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
if (options.json) {
|
|
362
|
+
console.log(JSON.stringify(result, null, 2));
|
|
363
|
+
} else if (!options.silent) {
|
|
364
|
+
console.log(chalk.green('✅ KPI baseline generated'));
|
|
365
|
+
console.log(chalk.gray(` period: ${result.period}`));
|
|
366
|
+
console.log(chalk.gray(` source: ${result.baseline_source}`));
|
|
367
|
+
console.log(chalk.gray(` baseline: ${result.baseline_path}`));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function runValueMetricsTrend(options = {}, dependencies = {}) {
|
|
374
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
375
|
+
const loader = dependencies.metricContractLoader || new MetricContractLoader(projectPath);
|
|
376
|
+
const evaluator = dependencies.riskEvaluator || new RiskEvaluator();
|
|
377
|
+
|
|
378
|
+
const { contract, contractPath } = await loader.load({
|
|
379
|
+
path: options.definitions
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const historyDir = toProjectPath(projectPath, options.historyDir) || defaultHistoryDir(projectPath);
|
|
383
|
+
const allSnapshots = sortSnapshotsByPeriod(await loadHistorySnapshots(historyDir));
|
|
384
|
+
|
|
385
|
+
if (allSnapshots.length < 2) {
|
|
386
|
+
throw new Error('At least 2 snapshots are required to calculate trends. Tip: generate snapshots first with "kse value metrics snapshot --input ./kpi-input.json".');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const window = parsePositiveInteger(options.window, allSnapshots.length);
|
|
390
|
+
const selected = allSnapshots.slice(-Math.min(window, allSnapshots.length));
|
|
391
|
+
const latest = selected[selected.length - 1];
|
|
392
|
+
const previous = selected[selected.length - 2];
|
|
393
|
+
|
|
394
|
+
const metricTrends = contract.metrics.map(metric => {
|
|
395
|
+
const latestValue = Number(latest[metric.id]);
|
|
396
|
+
const previousValue = Number(previous[metric.id]);
|
|
397
|
+
|
|
398
|
+
const delta = Number.isFinite(latestValue) && Number.isFinite(previousValue)
|
|
399
|
+
? Number((latestValue - previousValue).toFixed(6))
|
|
400
|
+
: null;
|
|
401
|
+
|
|
402
|
+
const direction = inferMetricDirection(metric);
|
|
403
|
+
const trend = !Number.isFinite(delta) || delta === 0
|
|
404
|
+
? 'flat'
|
|
405
|
+
: (delta > 0 ? 'up' : 'down');
|
|
406
|
+
|
|
407
|
+
const targetPassed = evaluateTarget(latestValue, metric.target_rule);
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
metric_id: metric.id,
|
|
411
|
+
latest: latestValue,
|
|
412
|
+
previous: previousValue,
|
|
413
|
+
delta,
|
|
414
|
+
trend,
|
|
415
|
+
better_direction: direction,
|
|
416
|
+
status: getImprovement(delta, direction),
|
|
417
|
+
target: metric.target_rule ? metric.target_rule.raw : metric.target,
|
|
418
|
+
target_passed: targetPassed
|
|
419
|
+
};
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const riskResult = evaluator.evaluate({
|
|
423
|
+
historySnapshots: selected.slice(0, -1),
|
|
424
|
+
currentSnapshot: latest
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const result = {
|
|
428
|
+
success: true,
|
|
429
|
+
period: latest.period,
|
|
430
|
+
window_size: selected.length,
|
|
431
|
+
range: {
|
|
432
|
+
from: selected[0].period,
|
|
433
|
+
to: latest.period
|
|
434
|
+
},
|
|
435
|
+
risk_level: riskResult.risk_level,
|
|
436
|
+
triggered_metrics: riskResult.triggered_metrics,
|
|
437
|
+
reasons: riskResult.reasons,
|
|
438
|
+
metrics: metricTrends,
|
|
439
|
+
contract_path: toPortablePath(projectPath, contractPath)
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const outputPath = toProjectPath(projectPath, options.out)
|
|
443
|
+
|| path.join(historyDir, 'trend.latest.json');
|
|
444
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
445
|
+
await fs.writeJson(outputPath, result, { spaces: 2 });
|
|
446
|
+
result.trend_path = toPortablePath(projectPath, outputPath);
|
|
447
|
+
|
|
448
|
+
if (options.json) {
|
|
449
|
+
console.log(JSON.stringify(result, null, 2));
|
|
450
|
+
} else if (!options.silent) {
|
|
451
|
+
console.log(chalk.green('✅ KPI trend calculated'));
|
|
452
|
+
console.log(chalk.gray(` period: ${result.period}`));
|
|
453
|
+
console.log(chalk.gray(` range: ${result.range.from} -> ${result.range.to}`));
|
|
454
|
+
console.log(chalk.gray(` risk: ${result.risk_level}`));
|
|
455
|
+
console.log(chalk.gray(` trend: ${result.trend_path}`));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return result;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function registerValueCommands(program) {
|
|
462
|
+
const value = program
|
|
463
|
+
.command('value')
|
|
464
|
+
.description('Spec value realization utilities');
|
|
465
|
+
|
|
466
|
+
const metrics = value
|
|
467
|
+
.command('metrics')
|
|
468
|
+
.description('KPI metrics and observability commands');
|
|
469
|
+
|
|
470
|
+
metrics
|
|
471
|
+
.command('sample')
|
|
472
|
+
.description('Generate sample KPI input JSON')
|
|
473
|
+
.option('--period <YYYY-WNN>', 'Week period, e.g. 2026-W10')
|
|
474
|
+
.option('--out <path>', 'Output path for sample input JSON', 'kpi-input.json')
|
|
475
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
476
|
+
.action(async options => {
|
|
477
|
+
try {
|
|
478
|
+
await runValueMetricsSample(options);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
if (options.json) {
|
|
481
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
482
|
+
} else {
|
|
483
|
+
console.error(chalk.red('❌ Value metrics sample failed:'), error.message);
|
|
484
|
+
}
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
metrics
|
|
490
|
+
.command('snapshot')
|
|
491
|
+
.description('Generate weekly KPI snapshot and gate summary')
|
|
492
|
+
.requiredOption('--input <path>', 'Input JSON with metric values')
|
|
493
|
+
.option('--period <YYYY-WNN>', 'Week period, e.g. 2026-W08')
|
|
494
|
+
.option('--definitions <path>', 'Metric definition YAML/JSON path')
|
|
495
|
+
.option('--history-dir <path>', 'History snapshots directory')
|
|
496
|
+
.option('--out <path>', 'Output path for snapshot JSON')
|
|
497
|
+
.option('--gate-out <path>', 'Output path for gate summary JSON')
|
|
498
|
+
.option('--checkpoint <name>', 'Gate checkpoint (day-30/day-60)', 'day-30')
|
|
499
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
500
|
+
.action(async options => {
|
|
501
|
+
try {
|
|
502
|
+
await runValueMetricsSnapshot(options);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
if (options.json) {
|
|
505
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
506
|
+
} else {
|
|
507
|
+
console.error(chalk.red('❌ Value metrics snapshot failed:'), error.message);
|
|
508
|
+
}
|
|
509
|
+
process.exit(1);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
metrics
|
|
514
|
+
.command('baseline')
|
|
515
|
+
.description('Generate baseline snapshot from input or history')
|
|
516
|
+
.option('--input <path>', 'Input JSON with metric values')
|
|
517
|
+
.option('--from-history <n>', 'Derive baseline from earliest N history snapshots', parseInt)
|
|
518
|
+
.option('--period <YYYY-WNN>', 'Baseline period, e.g. 2026-W07')
|
|
519
|
+
.option('--definitions <path>', 'Metric definition YAML/JSON path')
|
|
520
|
+
.option('--history-dir <path>', 'History snapshots directory')
|
|
521
|
+
.option('--out <path>', 'Output path for baseline JSON')
|
|
522
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
523
|
+
.action(async options => {
|
|
524
|
+
try {
|
|
525
|
+
await runValueMetricsBaseline(options);
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (options.json) {
|
|
528
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
529
|
+
} else {
|
|
530
|
+
console.error(chalk.red('❌ Value metrics baseline failed:'), error.message);
|
|
531
|
+
}
|
|
532
|
+
process.exit(1);
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
metrics
|
|
537
|
+
.command('trend')
|
|
538
|
+
.description('Analyze KPI trends from historical snapshots')
|
|
539
|
+
.option('--definitions <path>', 'Metric definition YAML/JSON path')
|
|
540
|
+
.option('--history-dir <path>', 'History snapshots directory')
|
|
541
|
+
.option('--window <n>', 'Use latest N snapshots for trend analysis', parseInt)
|
|
542
|
+
.option('--out <path>', 'Output path for trend JSON')
|
|
543
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
544
|
+
.action(async options => {
|
|
545
|
+
try {
|
|
546
|
+
await runValueMetricsTrend(options);
|
|
547
|
+
} catch (error) {
|
|
548
|
+
if (options.json) {
|
|
549
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
550
|
+
} else {
|
|
551
|
+
console.error(chalk.red('❌ Value metrics trend failed:'), error.message);
|
|
552
|
+
}
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
module.exports = {
|
|
559
|
+
registerValueCommands,
|
|
560
|
+
runValueMetricsSample,
|
|
561
|
+
runValueMetricsSnapshot,
|
|
562
|
+
runValueMetricsBaseline,
|
|
563
|
+
runValueMetricsTrend,
|
|
564
|
+
_loadHistorySnapshots: loadHistorySnapshots,
|
|
565
|
+
_resolveMetricInput: resolveMetricInput,
|
|
566
|
+
_sortSnapshotsByPeriod: sortSnapshotsByPeriod,
|
|
567
|
+
_getIsoWeekPeriod: getIsoWeekPeriod,
|
|
568
|
+
_createSampleMetricsInput: createSampleMetricsInput
|
|
569
|
+
};
|