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,379 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const EnvironmentRegistry = require('./environment-registry');
|
|
4
|
+
const BackupSystem = require('./backup-system');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Environment Manager
|
|
8
|
+
*
|
|
9
|
+
* Core logic for environment operations including registration,
|
|
10
|
+
* switching, listing, and managing active environment.
|
|
11
|
+
*/
|
|
12
|
+
class EnvironmentManager {
|
|
13
|
+
/**
|
|
14
|
+
* Create a new Environment Manager
|
|
15
|
+
* @param {string} projectRoot - Project root directory
|
|
16
|
+
* @param {string} [workspaceContext] - Optional workspace context
|
|
17
|
+
*/
|
|
18
|
+
constructor(projectRoot, workspaceContext = null) {
|
|
19
|
+
this.projectRoot = projectRoot;
|
|
20
|
+
this.workspaceContext = workspaceContext;
|
|
21
|
+
this.registryPath = this.resolveRegistryPath();
|
|
22
|
+
this.backupSystem = new BackupSystem(projectRoot);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve registry path based on workspace context
|
|
27
|
+
* @returns {string} Path to environments.json
|
|
28
|
+
*/
|
|
29
|
+
resolveRegistryPath() {
|
|
30
|
+
if (this.workspaceContext) {
|
|
31
|
+
return path.join(
|
|
32
|
+
this.projectRoot,
|
|
33
|
+
'.kiro',
|
|
34
|
+
'workspaces',
|
|
35
|
+
this.workspaceContext,
|
|
36
|
+
'environments.json'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return path.join(this.projectRoot, '.kiro', 'environments.json');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Register new environment
|
|
44
|
+
* @param {Object} environmentConfig - Environment configuration
|
|
45
|
+
* @returns {Object} Registration result
|
|
46
|
+
* @throws {Error} If registration fails
|
|
47
|
+
*/
|
|
48
|
+
async registerEnvironment(environmentConfig) {
|
|
49
|
+
// Validate environment configuration
|
|
50
|
+
EnvironmentRegistry.validateEnvironment(environmentConfig);
|
|
51
|
+
|
|
52
|
+
// Load existing registry
|
|
53
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
54
|
+
|
|
55
|
+
// Check for duplicate name
|
|
56
|
+
const exists = registry.environments.some(
|
|
57
|
+
env => env.name === environmentConfig.name
|
|
58
|
+
);
|
|
59
|
+
if (exists) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Environment "${environmentConfig.name}" already exists`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Validate source files exist
|
|
66
|
+
for (const mapping of environmentConfig.config_files) {
|
|
67
|
+
const sourcePath = path.join(this.projectRoot, mapping.source);
|
|
68
|
+
if (!await fs.pathExists(sourcePath)) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Source file does not exist: ${mapping.source}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Add environment to registry
|
|
76
|
+
registry.environments.push(environmentConfig);
|
|
77
|
+
|
|
78
|
+
// Save registry
|
|
79
|
+
await EnvironmentRegistry.save(this.registryPath, registry);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
environment: environmentConfig.name,
|
|
84
|
+
message: `Environment "${environmentConfig.name}" registered successfully`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* List all registered environments
|
|
90
|
+
* @returns {Array} Array of environment objects with metadata
|
|
91
|
+
*/
|
|
92
|
+
async listEnvironments() {
|
|
93
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
94
|
+
return registry.environments.map(env => ({
|
|
95
|
+
name: env.name,
|
|
96
|
+
description: env.description,
|
|
97
|
+
isActive: env.name === registry.active_environment,
|
|
98
|
+
configFilesCount: env.config_files.length,
|
|
99
|
+
hasVerification: !!env.verification
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get active environment details
|
|
105
|
+
* @returns {Object} Active environment configuration
|
|
106
|
+
* @throws {Error} If no environment is active
|
|
107
|
+
*/
|
|
108
|
+
async getActiveEnvironment() {
|
|
109
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
110
|
+
|
|
111
|
+
if (!registry.active_environment) {
|
|
112
|
+
throw new Error('No active environment. Use "kse env switch <name>" to activate one.');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const env = registry.environments.find(
|
|
116
|
+
e => e.name === registry.active_environment
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (!env) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Active environment "${registry.active_environment}" not found in registry`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return env;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Set active environment
|
|
130
|
+
* @param {string} environmentName - Name of environment to activate
|
|
131
|
+
* @throws {Error} If environment doesn't exist
|
|
132
|
+
*/
|
|
133
|
+
async setActiveEnvironment(environmentName) {
|
|
134
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
135
|
+
|
|
136
|
+
const env = registry.environments.find(e => e.name === environmentName);
|
|
137
|
+
if (!env) {
|
|
138
|
+
throw new Error(`Environment "${environmentName}" not found`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
registry.active_environment = environmentName;
|
|
142
|
+
await EnvironmentRegistry.save(this.registryPath, registry);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Switch to specified environment (basic implementation)
|
|
147
|
+
* @param {string} environmentName - Name of environment to switch to
|
|
148
|
+
* @param {Object} options - Switch options
|
|
149
|
+
* @returns {Object} Switch result with status and details
|
|
150
|
+
* @throws {Error} If switch operation fails
|
|
151
|
+
*/
|
|
152
|
+
async switchEnvironment(environmentName, options = {}) {
|
|
153
|
+
try {
|
|
154
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
155
|
+
|
|
156
|
+
// Find environment
|
|
157
|
+
const env = registry.environments.find(e => e.name === environmentName);
|
|
158
|
+
if (!env) {
|
|
159
|
+
throw new Error(`Environment "${environmentName}" not found`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const previousEnvironment = registry.active_environment;
|
|
163
|
+
const copiedFiles = [];
|
|
164
|
+
let backupMetadata = null;
|
|
165
|
+
|
|
166
|
+
// Create backup unless skipBackup option is set
|
|
167
|
+
if (!options.skipBackup) {
|
|
168
|
+
const targetFiles = env.config_files.map(m => m.target);
|
|
169
|
+
backupMetadata = await this.backupSystem.createBackup(targetFiles, environmentName);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Copy each config file
|
|
173
|
+
for (const mapping of env.config_files) {
|
|
174
|
+
const sourcePath = path.join(this.projectRoot, mapping.source);
|
|
175
|
+
const targetPath = path.join(this.projectRoot, mapping.target);
|
|
176
|
+
|
|
177
|
+
// Validate source exists
|
|
178
|
+
if (!await fs.pathExists(sourcePath)) {
|
|
179
|
+
throw new Error(`Source file does not exist: ${mapping.source}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Create target directory if needed
|
|
183
|
+
const targetDir = path.dirname(targetPath);
|
|
184
|
+
await fs.ensureDir(targetDir);
|
|
185
|
+
|
|
186
|
+
// Copy file
|
|
187
|
+
await fs.copy(sourcePath, targetPath, { overwrite: true });
|
|
188
|
+
copiedFiles.push(mapping.target);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Update active environment
|
|
192
|
+
registry.active_environment = environmentName;
|
|
193
|
+
await EnvironmentRegistry.save(this.registryPath, registry);
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
success: true,
|
|
197
|
+
previous_environment: previousEnvironment,
|
|
198
|
+
new_environment: environmentName,
|
|
199
|
+
files_copied: copiedFiles.length,
|
|
200
|
+
backup_created: !!backupMetadata,
|
|
201
|
+
backup_location: backupMetadata ? backupMetadata.backup_directory : null,
|
|
202
|
+
errors: []
|
|
203
|
+
};
|
|
204
|
+
} catch (error) {
|
|
205
|
+
// If error occurs, system remains in previous state
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
previous_environment: null,
|
|
209
|
+
new_environment: null,
|
|
210
|
+
files_copied: 0,
|
|
211
|
+
backup_created: false,
|
|
212
|
+
backup_location: null,
|
|
213
|
+
errors: [error.message]
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Rollback to previous environment using backup
|
|
220
|
+
* @returns {Object} Rollback result
|
|
221
|
+
* @throws {Error} If rollback fails
|
|
222
|
+
*/
|
|
223
|
+
async rollbackEnvironment() {
|
|
224
|
+
const result = await this.backupSystem.restoreBackup();
|
|
225
|
+
|
|
226
|
+
// Update active environment to match restored backup
|
|
227
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
228
|
+
registry.active_environment = result.environment_name;
|
|
229
|
+
await EnvironmentRegistry.save(this.registryPath, registry);
|
|
230
|
+
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Unregister environment
|
|
236
|
+
* @param {string} environmentName - Name of environment to remove
|
|
237
|
+
* @throws {Error} If environment is active or doesn't exist
|
|
238
|
+
*/
|
|
239
|
+
async unregisterEnvironment(environmentName) {
|
|
240
|
+
const registry = await EnvironmentRegistry.load(this.registryPath);
|
|
241
|
+
|
|
242
|
+
// Check if environment exists
|
|
243
|
+
const envIndex = registry.environments.findIndex(
|
|
244
|
+
e => e.name === environmentName
|
|
245
|
+
);
|
|
246
|
+
if (envIndex === -1) {
|
|
247
|
+
throw new Error(`Environment "${environmentName}" not found`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Prevent unregistering active environment
|
|
251
|
+
if (registry.active_environment === environmentName) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`Cannot unregister active environment "${environmentName}". ` +
|
|
254
|
+
'Switch to another environment first.'
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Remove environment
|
|
259
|
+
registry.environments.splice(envIndex, 1);
|
|
260
|
+
await EnvironmentRegistry.save(this.registryPath, registry);
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
success: true,
|
|
264
|
+
message: `Environment "${environmentName}" unregistered successfully`
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Verify current environment configuration
|
|
270
|
+
* @returns {Object} Verification result with status and output
|
|
271
|
+
* @throws {Error} If verification command fails
|
|
272
|
+
*/
|
|
273
|
+
async verifyEnvironment() {
|
|
274
|
+
const env = await this.getActiveEnvironment();
|
|
275
|
+
|
|
276
|
+
// If no verification rules, return success
|
|
277
|
+
if (!env.verification) {
|
|
278
|
+
return {
|
|
279
|
+
success: true,
|
|
280
|
+
environment_name: env.name,
|
|
281
|
+
command: null,
|
|
282
|
+
expected_output: null,
|
|
283
|
+
actual_output: null,
|
|
284
|
+
exit_code: 0,
|
|
285
|
+
error: null
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const { command, expected_output } = env.verification;
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
// Execute verification command
|
|
293
|
+
const { execSync } = require('child_process');
|
|
294
|
+
const actualOutput = execSync(command, {
|
|
295
|
+
cwd: this.projectRoot,
|
|
296
|
+
encoding: 'utf8',
|
|
297
|
+
timeout: 30000 // 30 second timeout
|
|
298
|
+
}).trim();
|
|
299
|
+
|
|
300
|
+
// Check if output matches expected pattern
|
|
301
|
+
const success = actualOutput.includes(expected_output);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
success,
|
|
305
|
+
environment_name: env.name,
|
|
306
|
+
command,
|
|
307
|
+
expected_output,
|
|
308
|
+
actual_output: actualOutput,
|
|
309
|
+
exit_code: 0,
|
|
310
|
+
error: success ? null : 'Output does not match expected pattern'
|
|
311
|
+
};
|
|
312
|
+
} catch (error) {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
environment_name: env.name,
|
|
316
|
+
command,
|
|
317
|
+
expected_output,
|
|
318
|
+
actual_output: error.stdout ? error.stdout.toString().trim() : '',
|
|
319
|
+
exit_code: error.status || 1,
|
|
320
|
+
error: error.message
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Run command in environment context
|
|
327
|
+
* @param {string} command - Command to execute
|
|
328
|
+
* @param {string} environmentName - Optional environment name (defaults to active)
|
|
329
|
+
* @returns {Object} Command execution result
|
|
330
|
+
* @throws {Error} If command execution fails
|
|
331
|
+
*/
|
|
332
|
+
async runInEnvironment(command, environmentName = null) {
|
|
333
|
+
// If environment name specified, ensure it's active
|
|
334
|
+
if (environmentName) {
|
|
335
|
+
const currentEnv = await this.getActiveEnvironment();
|
|
336
|
+
if (currentEnv.name !== environmentName) {
|
|
337
|
+
// Switch to specified environment
|
|
338
|
+
const switchResult = await this.switchEnvironment(environmentName);
|
|
339
|
+
if (!switchResult.success) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`Failed to switch to environment "${environmentName}": ${switchResult.errors.join(', ')}`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const env = await this.getActiveEnvironment();
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
// Execute command
|
|
351
|
+
const { execSync } = require('child_process');
|
|
352
|
+
const output = execSync(command, {
|
|
353
|
+
cwd: this.projectRoot,
|
|
354
|
+
encoding: 'utf8',
|
|
355
|
+
stdio: 'pipe'
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
success: true,
|
|
360
|
+
environment_name: env.name,
|
|
361
|
+
command,
|
|
362
|
+
output: output.trim(),
|
|
363
|
+
exit_code: 0,
|
|
364
|
+
error: null
|
|
365
|
+
};
|
|
366
|
+
} catch (error) {
|
|
367
|
+
return {
|
|
368
|
+
success: false,
|
|
369
|
+
environment_name: env.name,
|
|
370
|
+
command,
|
|
371
|
+
output: error.stdout ? error.stdout.toString().trim() : '',
|
|
372
|
+
exit_code: error.status || 1,
|
|
373
|
+
error: error.message
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
module.exports = EnvironmentManager;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Environment Registry
|
|
6
|
+
*
|
|
7
|
+
* Manages persistent storage of environment configurations.
|
|
8
|
+
* Handles loading, saving, and validating the environments.json file.
|
|
9
|
+
*/
|
|
10
|
+
class EnvironmentRegistry {
|
|
11
|
+
/**
|
|
12
|
+
* Load registry from disk
|
|
13
|
+
* @param {string} registryPath - Path to environments.json
|
|
14
|
+
* @returns {Object} Registry data
|
|
15
|
+
* @throws {Error} If registry is corrupted or invalid
|
|
16
|
+
*/
|
|
17
|
+
static async load(registryPath) {
|
|
18
|
+
try {
|
|
19
|
+
if (!await fs.pathExists(registryPath)) {
|
|
20
|
+
return this.initialize();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const content = await fs.readFile(registryPath, 'utf8');
|
|
24
|
+
const data = JSON.parse(content);
|
|
25
|
+
|
|
26
|
+
this.validate(data);
|
|
27
|
+
return data;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error.name === 'SyntaxError') {
|
|
30
|
+
throw new Error(`Registry file is corrupted: ${error.message}`);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Save registry to disk
|
|
38
|
+
* @param {string} registryPath - Path to environments.json
|
|
39
|
+
* @param {Object} registryData - Registry data to save
|
|
40
|
+
* @throws {Error} If save operation fails
|
|
41
|
+
*/
|
|
42
|
+
static async save(registryPath, registryData) {
|
|
43
|
+
this.validate(registryData);
|
|
44
|
+
|
|
45
|
+
const dir = path.dirname(registryPath);
|
|
46
|
+
await fs.ensureDir(dir);
|
|
47
|
+
|
|
48
|
+
await fs.writeFile(
|
|
49
|
+
registryPath,
|
|
50
|
+
JSON.stringify(registryData, null, 2),
|
|
51
|
+
'utf8'
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Validate registry structure
|
|
57
|
+
* @param {Object} registryData - Registry data to validate
|
|
58
|
+
* @returns {boolean} True if valid
|
|
59
|
+
* @throws {Error} If validation fails with details
|
|
60
|
+
*/
|
|
61
|
+
static validate(registryData) {
|
|
62
|
+
if (!registryData || typeof registryData !== 'object') {
|
|
63
|
+
throw new Error('Registry data must be an object');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!registryData.version) {
|
|
67
|
+
throw new Error('Registry must have a version field');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!Array.isArray(registryData.environments)) {
|
|
71
|
+
throw new Error('Registry must have an environments array');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Validate each environment
|
|
75
|
+
const names = new Set();
|
|
76
|
+
for (const env of registryData.environments) {
|
|
77
|
+
this.validateEnvironment(env);
|
|
78
|
+
|
|
79
|
+
if (names.has(env.name)) {
|
|
80
|
+
throw new Error(`Duplicate environment name: ${env.name}`);
|
|
81
|
+
}
|
|
82
|
+
names.add(env.name);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Validate active_environment if present
|
|
86
|
+
if (registryData.active_environment) {
|
|
87
|
+
const activeExists = registryData.environments.some(
|
|
88
|
+
env => env.name === registryData.active_environment
|
|
89
|
+
);
|
|
90
|
+
if (!activeExists) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Active environment "${registryData.active_environment}" not found in registry`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Validate a single environment configuration
|
|
102
|
+
* @param {Object} env - Environment configuration
|
|
103
|
+
* @throws {Error} If validation fails
|
|
104
|
+
*/
|
|
105
|
+
static validateEnvironment(env) {
|
|
106
|
+
if (!env.name || typeof env.name !== 'string') {
|
|
107
|
+
throw new Error('Environment must have a name (string)');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!/^[a-z0-9-]+$/.test(env.name)) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Environment name must be kebab-case (lowercase, numbers, hyphens): ${env.name}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!env.description || typeof env.description !== 'string') {
|
|
117
|
+
throw new Error(`Environment "${env.name}" must have a description`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!Array.isArray(env.config_files) || env.config_files.length === 0) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Environment "${env.name}" must have at least one config file mapping`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Validate config file mappings
|
|
127
|
+
for (const mapping of env.config_files) {
|
|
128
|
+
if (!mapping.source || typeof mapping.source !== 'string') {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Environment "${env.name}" has invalid config file mapping: missing source`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
if (!mapping.target || typeof mapping.target !== 'string') {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Environment "${env.name}" has invalid config file mapping: missing target`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Validate verification rules if present
|
|
141
|
+
if (env.verification) {
|
|
142
|
+
if (!env.verification.command || typeof env.verification.command !== 'string') {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Environment "${env.name}" has invalid verification: missing command`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (!env.verification.expected_output || typeof env.verification.expected_output !== 'string') {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Environment "${env.name}" has invalid verification: missing expected_output`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Initialize empty registry
|
|
157
|
+
* @returns {Object} Empty registry structure
|
|
158
|
+
*/
|
|
159
|
+
static initialize() {
|
|
160
|
+
return {
|
|
161
|
+
version: '1.0',
|
|
162
|
+
environments: [],
|
|
163
|
+
active_environment: null
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = EnvironmentRegistry;
|