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,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter
|
|
3
|
+
*
|
|
4
|
+
* Formats and displays governance operation results
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class Reporter {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.chalk = chalk;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Display diagnostic report
|
|
17
|
+
*
|
|
18
|
+
* @param {DiagnosticReport} report - Diagnostic report
|
|
19
|
+
*/
|
|
20
|
+
displayDiagnostic(report) {
|
|
21
|
+
console.log(this.chalk.bold.cyan('\n📋 Document Governance Diagnostic\n'));
|
|
22
|
+
|
|
23
|
+
if (report.compliant) {
|
|
24
|
+
console.log(this.chalk.green('✅ Project is compliant'));
|
|
25
|
+
console.log('All documents follow the lifecycle management rules.\n');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(this.chalk.yellow(`⚠️ Found ${report.violations.length} violation(s)\n`));
|
|
30
|
+
|
|
31
|
+
// Group violations by type
|
|
32
|
+
const byType = this.groupBy(report.violations, 'type');
|
|
33
|
+
|
|
34
|
+
for (const [type, violations] of Object.entries(byType)) {
|
|
35
|
+
console.log(this.chalk.bold.blue(`${this.formatType(type)} (${violations.length})`));
|
|
36
|
+
|
|
37
|
+
for (const violation of violations) {
|
|
38
|
+
const icon = violation.severity === 'error' ? '❌' : '⚠️';
|
|
39
|
+
console.log(` ${icon} ${this.chalk.gray(violation.path)}`);
|
|
40
|
+
console.log(` ${violation.description}`);
|
|
41
|
+
console.log(` ${this.chalk.cyan('→ ' + violation.recommendation)}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Display recommendations
|
|
48
|
+
if (report.recommendations && report.recommendations.length > 0) {
|
|
49
|
+
console.log(this.chalk.bold.blue('💡 Recommended Actions'));
|
|
50
|
+
report.recommendations.forEach(rec => {
|
|
51
|
+
console.log(` • ${rec}`);
|
|
52
|
+
});
|
|
53
|
+
console.log();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Display summary
|
|
57
|
+
if (report.summary) {
|
|
58
|
+
console.log(this.chalk.bold('Summary:'));
|
|
59
|
+
console.log(` Total violations: ${report.summary.totalViolations}`);
|
|
60
|
+
if (report.summary.bySeverity) {
|
|
61
|
+
console.log(` Errors: ${report.summary.bySeverity.error || 0}`);
|
|
62
|
+
console.log(` Warnings: ${report.summary.bySeverity.warning || 0}`);
|
|
63
|
+
console.log(` Info: ${report.summary.bySeverity.info || 0}`);
|
|
64
|
+
}
|
|
65
|
+
console.log();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Display cleanup report
|
|
71
|
+
*
|
|
72
|
+
* @param {CleanupReport} report - Cleanup report
|
|
73
|
+
* @param {boolean} dryRun - Whether this was a dry run
|
|
74
|
+
*/
|
|
75
|
+
displayCleanup(report, dryRun = false) {
|
|
76
|
+
const title = dryRun ? 'Cleanup Preview (Dry Run)' : 'Cleanup Complete';
|
|
77
|
+
console.log(this.chalk.bold.cyan(`\n🧹 ${title}\n`));
|
|
78
|
+
|
|
79
|
+
if (report.deletedFiles.length === 0) {
|
|
80
|
+
console.log(this.chalk.green('✅ No files to clean\n'));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const verb = dryRun ? 'Would delete' : 'Deleted';
|
|
85
|
+
console.log(this.chalk.yellow(`${verb} ${report.deletedFiles.length} file(s):\n`));
|
|
86
|
+
|
|
87
|
+
report.deletedFiles.forEach(file => {
|
|
88
|
+
console.log(` 🗑️ ${this.chalk.gray(file)}`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (report.errors && report.errors.length > 0) {
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(this.chalk.red(`❌ ${report.errors.length} error(s):`));
|
|
94
|
+
report.errors.forEach(err => {
|
|
95
|
+
console.log(` • ${this.chalk.gray(err.path)}: ${err.error}`);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log();
|
|
100
|
+
|
|
101
|
+
if (dryRun) {
|
|
102
|
+
console.log(this.chalk.cyan('💡 Run without --dry-run to actually delete these files\n'));
|
|
103
|
+
} else if (report.success) {
|
|
104
|
+
console.log(this.chalk.green('✅ Cleanup completed successfully\n'));
|
|
105
|
+
} else {
|
|
106
|
+
console.log(this.chalk.yellow('⚠️ Cleanup completed with errors\n'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Display validation report
|
|
112
|
+
*
|
|
113
|
+
* @param {ValidationReport} report - Validation report
|
|
114
|
+
*/
|
|
115
|
+
displayValidation(report) {
|
|
116
|
+
console.log(this.chalk.bold.cyan('\n✓ Document Structure Validation\n'));
|
|
117
|
+
|
|
118
|
+
// Check if there are any errors or warnings
|
|
119
|
+
const hasErrors = report.errors && report.errors.length > 0;
|
|
120
|
+
const hasWarnings = report.warnings && report.warnings.length > 0;
|
|
121
|
+
|
|
122
|
+
if (report.valid && !hasWarnings) {
|
|
123
|
+
console.log(this.chalk.green('✅ Validation passed'));
|
|
124
|
+
console.log('All document structures are compliant.\n');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (hasErrors) {
|
|
129
|
+
console.log(this.chalk.red(`❌ ${report.errors.length} error(s):\n`));
|
|
130
|
+
|
|
131
|
+
report.errors.forEach(err => {
|
|
132
|
+
console.log(` ❌ ${this.chalk.gray(err.path)}`);
|
|
133
|
+
console.log(` ${err.message}`);
|
|
134
|
+
console.log(` ${this.chalk.cyan('→ ' + err.recommendation)}`);
|
|
135
|
+
console.log();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (hasWarnings) {
|
|
140
|
+
console.log(this.chalk.yellow(`⚠️ ${report.warnings.length} warning(s):\n`));
|
|
141
|
+
|
|
142
|
+
report.warnings.forEach(warn => {
|
|
143
|
+
console.log(` ⚠️ ${this.chalk.gray(warn.path)}`);
|
|
144
|
+
console.log(` ${warn.message}`);
|
|
145
|
+
console.log(` ${this.chalk.cyan('→ ' + warn.recommendation)}`);
|
|
146
|
+
console.log();
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Display summary
|
|
151
|
+
if (report.summary) {
|
|
152
|
+
console.log(this.chalk.bold('Summary:'));
|
|
153
|
+
console.log(` Errors: ${report.summary.totalErrors}`);
|
|
154
|
+
console.log(` Warnings: ${report.summary.totalWarnings}`);
|
|
155
|
+
console.log();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Display archive report
|
|
161
|
+
*
|
|
162
|
+
* @param {ArchiveReport} report - Archive report
|
|
163
|
+
* @param {boolean} dryRun - Whether this was a dry run
|
|
164
|
+
*/
|
|
165
|
+
displayArchive(report, dryRun = false) {
|
|
166
|
+
const title = dryRun ? 'Archive Preview (Dry Run)' : 'Archive Complete';
|
|
167
|
+
console.log(this.chalk.bold.cyan(`\n📦 ${title}\n`));
|
|
168
|
+
|
|
169
|
+
// Check if there are errors even with no moved files
|
|
170
|
+
const hasErrors = report.errors && report.errors.length > 0;
|
|
171
|
+
|
|
172
|
+
if (report.movedFiles.length === 0 && !hasErrors) {
|
|
173
|
+
console.log(this.chalk.green('✅ No files to archive\n'));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (report.movedFiles.length > 0) {
|
|
178
|
+
const verb = dryRun ? 'Would move' : 'Moved';
|
|
179
|
+
console.log(this.chalk.yellow(`${verb} ${report.movedFiles.length} file(s):\n`));
|
|
180
|
+
|
|
181
|
+
report.movedFiles.forEach(move => {
|
|
182
|
+
const fromBasename = path.basename(move.from);
|
|
183
|
+
const toRelative = this.chalk.gray(move.to);
|
|
184
|
+
console.log(` 📦 ${fromBasename}`);
|
|
185
|
+
console.log(` ${this.chalk.gray('→')} ${toRelative}`);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
console.log();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (hasErrors) {
|
|
192
|
+
console.log(this.chalk.red(`❌ ${report.errors.length} error(s):`));
|
|
193
|
+
report.errors.forEach(err => {
|
|
194
|
+
console.log(` • ${this.chalk.gray(err.path)}: ${err.error}`);
|
|
195
|
+
});
|
|
196
|
+
console.log();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (dryRun && report.movedFiles.length > 0) {
|
|
200
|
+
console.log(this.chalk.cyan('💡 Run without --dry-run to actually move these files\n'));
|
|
201
|
+
} else if (report.success && report.movedFiles.length > 0) {
|
|
202
|
+
console.log(this.chalk.green('✅ Archive completed successfully\n'));
|
|
203
|
+
} else if (!report.success) {
|
|
204
|
+
console.log(this.chalk.yellow('⚠️ Archive completed with errors\n'));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Display error message
|
|
210
|
+
*
|
|
211
|
+
* @param {string} message - Error message
|
|
212
|
+
*/
|
|
213
|
+
displayError(message) {
|
|
214
|
+
console.log(this.chalk.red(`\n❌ Error: ${message}\n`));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Display statistics
|
|
219
|
+
*
|
|
220
|
+
* @param {Object} stats - Statistics object
|
|
221
|
+
*/
|
|
222
|
+
displayStats(stats) {
|
|
223
|
+
console.log(this.chalk.bold.cyan('\n📊 Document Compliance Statistics\n'));
|
|
224
|
+
|
|
225
|
+
// Overall Summary
|
|
226
|
+
console.log(this.chalk.bold('Overall Summary:'));
|
|
227
|
+
console.log(` Total Executions: ${stats.totalExecutions}`);
|
|
228
|
+
console.log(` Total Violations Found: ${stats.totalViolations}`);
|
|
229
|
+
console.log(` Total Cleanup Actions: ${stats.totalCleanupActions}`);
|
|
230
|
+
console.log(` Total Archive Actions: ${stats.totalArchiveActions}`);
|
|
231
|
+
console.log(` Total Errors: ${stats.totalErrors}`);
|
|
232
|
+
console.log();
|
|
233
|
+
|
|
234
|
+
// Time Range
|
|
235
|
+
if (stats.firstExecution && stats.lastExecution) {
|
|
236
|
+
console.log(this.chalk.bold('Time Range:'));
|
|
237
|
+
console.log(` First Execution: ${new Date(stats.firstExecution).toLocaleString()}`);
|
|
238
|
+
console.log(` Last Execution: ${new Date(stats.lastExecution).toLocaleString()}`);
|
|
239
|
+
console.log();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Executions by Tool
|
|
243
|
+
if (Object.keys(stats.executionsByTool).length > 0) {
|
|
244
|
+
console.log(this.chalk.bold('Executions by Tool:'));
|
|
245
|
+
Object.entries(stats.executionsByTool)
|
|
246
|
+
.sort((a, b) => b[1] - a[1])
|
|
247
|
+
.forEach(([tool, count]) => {
|
|
248
|
+
console.log(` ${tool}: ${count}`);
|
|
249
|
+
});
|
|
250
|
+
console.log();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Violations by Type
|
|
254
|
+
if (Object.keys(stats.violationsByType).length > 0) {
|
|
255
|
+
console.log(this.chalk.bold('Violations by Type:'));
|
|
256
|
+
Object.entries(stats.violationsByType)
|
|
257
|
+
.sort((a, b) => b[1] - a[1])
|
|
258
|
+
.forEach(([type, count]) => {
|
|
259
|
+
console.log(` ${this.formatType(type)}: ${count}`);
|
|
260
|
+
});
|
|
261
|
+
console.log();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Violations Over Time (last 5)
|
|
265
|
+
if (stats.violationsOverTime.length > 0) {
|
|
266
|
+
console.log(this.chalk.bold('Recent Violations:'));
|
|
267
|
+
const recent = stats.violationsOverTime.slice(-5);
|
|
268
|
+
recent.forEach(entry => {
|
|
269
|
+
const date = new Date(entry.timestamp).toLocaleString();
|
|
270
|
+
console.log(` ${date}: ${entry.count} violation(s)`);
|
|
271
|
+
});
|
|
272
|
+
console.log();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Cleanup Actions Over Time (last 5)
|
|
276
|
+
if (stats.cleanupActionsOverTime.length > 0) {
|
|
277
|
+
console.log(this.chalk.bold('Recent Cleanup Actions:'));
|
|
278
|
+
const recent = stats.cleanupActionsOverTime.slice(-5);
|
|
279
|
+
recent.forEach(entry => {
|
|
280
|
+
const date = new Date(entry.timestamp).toLocaleString();
|
|
281
|
+
console.log(` ${date}: ${entry.count} file(s) deleted`);
|
|
282
|
+
});
|
|
283
|
+
console.log();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Recommendations
|
|
287
|
+
console.log(this.chalk.cyan('💡 Run "kse docs report" to generate a detailed compliance report\n'));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Display success message
|
|
292
|
+
*
|
|
293
|
+
* @param {string} message - Success message
|
|
294
|
+
*/
|
|
295
|
+
displaySuccess(message) {
|
|
296
|
+
console.log(this.chalk.green(`\n✅ ${message}\n`));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Display info message
|
|
301
|
+
*
|
|
302
|
+
* @param {string} message - Info message
|
|
303
|
+
*/
|
|
304
|
+
displayInfo(message) {
|
|
305
|
+
console.log(this.chalk.cyan(`\nℹ️ ${message}\n`));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Helper: Group array by property
|
|
310
|
+
*
|
|
311
|
+
* @param {Array} array - Array to group
|
|
312
|
+
* @param {string} property - Property to group by
|
|
313
|
+
* @returns {Object} - Grouped object
|
|
314
|
+
*/
|
|
315
|
+
groupBy(array, property) {
|
|
316
|
+
return array.reduce((acc, item) => {
|
|
317
|
+
const key = item[property];
|
|
318
|
+
if (!acc[key]) acc[key] = [];
|
|
319
|
+
acc[key].push(item);
|
|
320
|
+
return acc;
|
|
321
|
+
}, {});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Helper: Format violation type
|
|
326
|
+
*
|
|
327
|
+
* @param {string} type - Violation type
|
|
328
|
+
* @returns {string} - Formatted type
|
|
329
|
+
*/
|
|
330
|
+
formatType(type) {
|
|
331
|
+
return type.split('_').map(word =>
|
|
332
|
+
word.charAt(0).toUpperCase() + word.slice(1)
|
|
333
|
+
).join(' ');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
module.exports = Reporter;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Engine
|
|
3
|
+
*
|
|
4
|
+
* Validates project documentation structure
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const FileScanner = require('./file-scanner');
|
|
9
|
+
|
|
10
|
+
class ValidationEngine {
|
|
11
|
+
constructor(projectPath, config) {
|
|
12
|
+
this.projectPath = projectPath;
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.scanner = new FileScanner(projectPath);
|
|
15
|
+
this.validationErrors = [];
|
|
16
|
+
this.validationWarnings = [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validate project structure
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} options - Validation options
|
|
23
|
+
* @param {string} options.spec - Specific Spec to validate
|
|
24
|
+
* @param {boolean} options.all - Validate all Specs
|
|
25
|
+
* @returns {Promise<ValidationReport>}
|
|
26
|
+
*/
|
|
27
|
+
async validate(options = {}) {
|
|
28
|
+
this.validationErrors = []; // Reset
|
|
29
|
+
this.validationWarnings = []; // Reset
|
|
30
|
+
|
|
31
|
+
await this.validateRootDirectory();
|
|
32
|
+
|
|
33
|
+
if (options.spec) {
|
|
34
|
+
await this.validateSpec(options.spec);
|
|
35
|
+
} else if (options.all) {
|
|
36
|
+
await this.validateAllSpecs();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this.generateReport();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validate root directory
|
|
44
|
+
*
|
|
45
|
+
* @returns {Promise<void>}
|
|
46
|
+
*/
|
|
47
|
+
async validateRootDirectory() {
|
|
48
|
+
const mdFiles = await this.scanner.findMarkdownFiles(this.projectPath);
|
|
49
|
+
const allowedFiles = this.config.rootAllowedFiles || [];
|
|
50
|
+
|
|
51
|
+
for (const filePath of mdFiles) {
|
|
52
|
+
const basename = path.basename(filePath);
|
|
53
|
+
|
|
54
|
+
if (!allowedFiles.includes(basename)) {
|
|
55
|
+
this.validationErrors.push({
|
|
56
|
+
type: 'root_violation',
|
|
57
|
+
path: filePath,
|
|
58
|
+
message: `Unexpected markdown file in root: ${basename}`,
|
|
59
|
+
recommendation: 'Move to appropriate location or delete if temporary'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validate all Spec directories
|
|
67
|
+
*
|
|
68
|
+
* @returns {Promise<void>}
|
|
69
|
+
*/
|
|
70
|
+
async validateAllSpecs() {
|
|
71
|
+
const specDirs = await this.scanner.findSpecDirectories();
|
|
72
|
+
|
|
73
|
+
for (const specDir of specDirs) {
|
|
74
|
+
const specName = path.basename(specDir);
|
|
75
|
+
await this.validateSpec(specName);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Validate a Spec directory
|
|
81
|
+
*
|
|
82
|
+
* @param {string} specName - Spec name
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
async validateSpec(specName) {
|
|
86
|
+
const specPath = this.scanner.getSpecDirectory(specName);
|
|
87
|
+
|
|
88
|
+
// Check if Spec directory exists
|
|
89
|
+
if (!await this.scanner.exists(specPath)) {
|
|
90
|
+
this.validationErrors.push({
|
|
91
|
+
type: 'missing_spec',
|
|
92
|
+
path: specPath,
|
|
93
|
+
message: `Spec directory does not exist: ${specName}`,
|
|
94
|
+
recommendation: `Create Spec directory at ${specPath}`
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check required files
|
|
100
|
+
const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
|
|
101
|
+
for (const file of requiredFiles) {
|
|
102
|
+
const filePath = path.join(specPath, file);
|
|
103
|
+
if (!await this.scanner.exists(filePath)) {
|
|
104
|
+
this.validationErrors.push({
|
|
105
|
+
type: 'missing_required_file',
|
|
106
|
+
path: filePath,
|
|
107
|
+
message: `Missing required file: ${file}`,
|
|
108
|
+
recommendation: `Create ${file} in ${specName}`
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check for misplaced artifacts
|
|
114
|
+
const files = await this.scanner.getFiles(specPath);
|
|
115
|
+
const allowedSubdirs = this.config.specSubdirs || [];
|
|
116
|
+
|
|
117
|
+
for (const filePath of files) {
|
|
118
|
+
const basename = path.basename(filePath);
|
|
119
|
+
|
|
120
|
+
// Skip required files
|
|
121
|
+
if (requiredFiles.includes(basename)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.validationWarnings.push({
|
|
126
|
+
type: 'misplaced_artifact',
|
|
127
|
+
path: filePath,
|
|
128
|
+
message: `Artifact not in subdirectory: ${basename}`,
|
|
129
|
+
recommendation: `Move to appropriate subdirectory (${allowedSubdirs.join(', ')})`
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check subdirectory naming
|
|
134
|
+
const subdirs = await this.scanner.getSubdirectories(specPath);
|
|
135
|
+
|
|
136
|
+
for (const subdirPath of subdirs) {
|
|
137
|
+
const subdirName = path.basename(subdirPath);
|
|
138
|
+
|
|
139
|
+
// Skip hidden directories
|
|
140
|
+
if (subdirName.startsWith('.')) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!allowedSubdirs.includes(subdirName)) {
|
|
145
|
+
this.validationWarnings.push({
|
|
146
|
+
type: 'invalid_subdirectory',
|
|
147
|
+
path: subdirPath,
|
|
148
|
+
message: `Non-standard subdirectory: ${subdirName}`,
|
|
149
|
+
recommendation: `Rename to one of: ${allowedSubdirs.join(', ')}, or remove if not needed`
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Generate validation report
|
|
157
|
+
*
|
|
158
|
+
* @returns {ValidationReport}
|
|
159
|
+
*/
|
|
160
|
+
generateReport() {
|
|
161
|
+
return {
|
|
162
|
+
valid: this.validationErrors.length === 0,
|
|
163
|
+
errors: this.validationErrors,
|
|
164
|
+
warnings: this.validationWarnings,
|
|
165
|
+
summary: {
|
|
166
|
+
totalErrors: this.validationErrors.length,
|
|
167
|
+
totalWarnings: this.validationWarnings.length
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @typedef {Object} ValidationReport
|
|
175
|
+
* @property {boolean} valid - Whether validation passed
|
|
176
|
+
* @property {Object[]} errors - Validation errors
|
|
177
|
+
* @property {Object[]} warnings - Validation warnings
|
|
178
|
+
* @property {Object} summary - Summary statistics
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
module.exports = ValidationEngine;
|
package/lib/i18n.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class I18n {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.locale = this.detectLocale();
|
|
7
|
+
this.messages = this.loadMessages(this.locale);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
detectLocale() {
|
|
11
|
+
// 优先级: 环境变量 > 系统语言 > 默认英文
|
|
12
|
+
const envLocale = process.env.KIRO_LANG || process.env.LANG;
|
|
13
|
+
|
|
14
|
+
if (envLocale) {
|
|
15
|
+
if (envLocale.startsWith('zh')) return 'zh';
|
|
16
|
+
if (envLocale.startsWith('en')) return 'en';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 检测系统语言
|
|
20
|
+
const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
21
|
+
if (systemLocale.startsWith('zh')) return 'zh';
|
|
22
|
+
|
|
23
|
+
return 'en'; // 默认英文
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
loadMessages(locale) {
|
|
27
|
+
try {
|
|
28
|
+
const messagesPath = path.join(__dirname, '../locales', `${locale}.json`);
|
|
29
|
+
return JSON.parse(fs.readFileSync(messagesPath, 'utf8'));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
// 如果加载失败,回退到英文
|
|
32
|
+
const fallbackPath = path.join(__dirname, '../locales', 'en.json');
|
|
33
|
+
return JSON.parse(fs.readFileSync(fallbackPath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
t(key, params = {}) {
|
|
38
|
+
const keys = key.split('.');
|
|
39
|
+
let value = this.messages;
|
|
40
|
+
|
|
41
|
+
for (const k of keys) {
|
|
42
|
+
if (value && typeof value === 'object') {
|
|
43
|
+
value = value[k];
|
|
44
|
+
} else {
|
|
45
|
+
return key; // 如果找不到,返回 key 本身
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 替换参数
|
|
50
|
+
if (typeof value === 'string') {
|
|
51
|
+
return value.replace(/\{(\w+)\}/g, (match, param) => {
|
|
52
|
+
return params[param] !== undefined ? params[param] : match;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return value || key;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setLocale(locale) {
|
|
60
|
+
this.locale = locale;
|
|
61
|
+
this.messages = this.loadMessages(locale);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getLocale() {
|
|
65
|
+
return this.locale;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 单例模式
|
|
70
|
+
let instance = null;
|
|
71
|
+
|
|
72
|
+
function getI18n() {
|
|
73
|
+
if (!instance) {
|
|
74
|
+
instance = new I18n();
|
|
75
|
+
}
|
|
76
|
+
return instance;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { getI18n, I18n };
|