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,181 @@
|
|
|
1
|
+
const simpleGit = require('simple-git');
|
|
2
|
+
const GitError = require('./errors/git-error');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GitOperations - Low-level Git operations wrapper around simple-git
|
|
6
|
+
*
|
|
7
|
+
* Provides a clean interface for Git operations with consistent error handling
|
|
8
|
+
* and cross-platform compatibility.
|
|
9
|
+
*/
|
|
10
|
+
class GitOperations {
|
|
11
|
+
/**
|
|
12
|
+
* Create a simple-git instance for a repository
|
|
13
|
+
* @param {string} repoPath - Path to the Git repository
|
|
14
|
+
* @returns {SimpleGit} Configured simple-git instance
|
|
15
|
+
*/
|
|
16
|
+
createGitInstance(repoPath) {
|
|
17
|
+
return simpleGit(repoPath);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if path is a valid Git repository
|
|
22
|
+
* @param {string} path - Path to check
|
|
23
|
+
* @returns {Promise<boolean>} True if path contains a valid Git repository
|
|
24
|
+
*/
|
|
25
|
+
async isGitRepo(path) {
|
|
26
|
+
try {
|
|
27
|
+
const fs = require('fs').promises;
|
|
28
|
+
const pathModule = require('path');
|
|
29
|
+
|
|
30
|
+
// Check if .git directory exists
|
|
31
|
+
const gitDir = pathModule.join(path, '.git');
|
|
32
|
+
const stats = await fs.stat(gitDir);
|
|
33
|
+
|
|
34
|
+
// Verify it's a directory (not a file, which can occur in Git worktrees)
|
|
35
|
+
if (!stats.isDirectory()) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Optional: Verify with git command for additional validation
|
|
40
|
+
// This ensures the .git directory is valid and not corrupted
|
|
41
|
+
try {
|
|
42
|
+
const git = this.createGitInstance(path);
|
|
43
|
+
await git.revparse(['--git-dir']);
|
|
44
|
+
} catch (gitError) {
|
|
45
|
+
// .git directory exists but git command failed
|
|
46
|
+
// Still treat as repository since .git directory is the authoritative indicator
|
|
47
|
+
// This handles cases like corrupted repos that still have .git structure
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
// If we can't access .git directory (ENOENT, EACCES, etc.), it's not a repo
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get repository status
|
|
59
|
+
* @param {string} repoPath - Path to the Git repository
|
|
60
|
+
* @returns {Promise<StatusSummary>} Status summary from simple-git
|
|
61
|
+
* @throws {GitError} If status retrieval fails
|
|
62
|
+
*/
|
|
63
|
+
async getStatus(repoPath) {
|
|
64
|
+
try {
|
|
65
|
+
const git = this.createGitInstance(repoPath);
|
|
66
|
+
return await git.status();
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new GitError(
|
|
69
|
+
`Failed to get status for repository at ${repoPath}`,
|
|
70
|
+
'status',
|
|
71
|
+
null,
|
|
72
|
+
error.message
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get current branch name
|
|
79
|
+
* @param {string} repoPath - Path to the Git repository
|
|
80
|
+
* @returns {Promise<string>} Current branch name
|
|
81
|
+
* @throws {GitError} If branch retrieval fails
|
|
82
|
+
*/
|
|
83
|
+
async getCurrentBranch(repoPath) {
|
|
84
|
+
try {
|
|
85
|
+
const git = this.createGitInstance(repoPath);
|
|
86
|
+
const status = await git.status();
|
|
87
|
+
return status.current;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new GitError(
|
|
90
|
+
`Failed to get current branch for repository at ${repoPath}`,
|
|
91
|
+
'branch',
|
|
92
|
+
null,
|
|
93
|
+
error.message
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get remote URL
|
|
100
|
+
* @param {string} repoPath - Path to the Git repository
|
|
101
|
+
* @param {string} remoteName - Name of the remote (default: 'origin')
|
|
102
|
+
* @returns {Promise<string|null>} Remote URL or null if remote doesn't exist
|
|
103
|
+
* @throws {GitError} If remote retrieval fails
|
|
104
|
+
*/
|
|
105
|
+
async getRemoteUrl(repoPath, remoteName = 'origin') {
|
|
106
|
+
try {
|
|
107
|
+
const git = this.createGitInstance(repoPath);
|
|
108
|
+
const remotes = await git.getRemotes(true);
|
|
109
|
+
const remote = remotes.find(r => r.name === remoteName);
|
|
110
|
+
return remote ? remote.refs.fetch : null;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new GitError(
|
|
113
|
+
`Failed to get remote URL for ${remoteName} in repository at ${repoPath}`,
|
|
114
|
+
'remote',
|
|
115
|
+
null,
|
|
116
|
+
error.message
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get all remotes
|
|
123
|
+
* @param {string} repoPath - Path to the Git repository
|
|
124
|
+
* @returns {Promise<Array<{name: string, refs: {fetch: string, push: string}}>>} Array of remotes
|
|
125
|
+
* @throws {GitError} If remotes retrieval fails
|
|
126
|
+
*/
|
|
127
|
+
async getRemotes(repoPath) {
|
|
128
|
+
try {
|
|
129
|
+
const git = this.createGitInstance(repoPath);
|
|
130
|
+
return await git.getRemotes(true);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
throw new GitError(
|
|
133
|
+
`Failed to get remotes for repository at ${repoPath}`,
|
|
134
|
+
'remote',
|
|
135
|
+
null,
|
|
136
|
+
error.message
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if remote is reachable
|
|
143
|
+
* @param {string} repoPath - Path to the Git repository
|
|
144
|
+
* @param {string} remoteName - Name of the remote (default: 'origin')
|
|
145
|
+
* @returns {Promise<boolean>} True if remote is reachable
|
|
146
|
+
*/
|
|
147
|
+
async isRemoteReachable(repoPath, remoteName = 'origin') {
|
|
148
|
+
try {
|
|
149
|
+
const git = this.createGitInstance(repoPath);
|
|
150
|
+
// Use ls-remote to check if remote is reachable
|
|
151
|
+
await git.listRemote([remoteName]);
|
|
152
|
+
return true;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Execute raw Git command
|
|
160
|
+
* @param {string} repoPath - Path to the Git repository
|
|
161
|
+
* @param {string[]} args - Git command arguments
|
|
162
|
+
* @returns {Promise<string>} Command output
|
|
163
|
+
* @throws {GitError} If command execution fails
|
|
164
|
+
*/
|
|
165
|
+
async execRaw(repoPath, args) {
|
|
166
|
+
try {
|
|
167
|
+
const git = this.createGitInstance(repoPath);
|
|
168
|
+
const result = await git.raw(args);
|
|
169
|
+
return result;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
throw new GitError(
|
|
172
|
+
`Failed to execute git command: git ${args.join(' ')}`,
|
|
173
|
+
`git ${args.join(' ')}`,
|
|
174
|
+
error.exitCode || null,
|
|
175
|
+
error.message
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = GitOperations;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Placeholder for command handlers
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const RepoManager = require('../repo-manager');
|
|
2
|
+
const ConfigManager = require('../config-manager');
|
|
3
|
+
const OutputFormatter = require('../output-formatter');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ExecHandler - Handles batch command execution across repositories
|
|
7
|
+
*
|
|
8
|
+
* Executes Git commands across all configured repositories with
|
|
9
|
+
* support for dry-run mode and detailed result reporting.
|
|
10
|
+
*/
|
|
11
|
+
class ExecHandler {
|
|
12
|
+
/**
|
|
13
|
+
* Create a new ExecHandler
|
|
14
|
+
* @param {string} projectRoot - The project root directory
|
|
15
|
+
*/
|
|
16
|
+
constructor(projectRoot) {
|
|
17
|
+
this.projectRoot = projectRoot;
|
|
18
|
+
this.repoManager = new RepoManager(projectRoot);
|
|
19
|
+
this.configManager = new ConfigManager(projectRoot);
|
|
20
|
+
this.formatter = new OutputFormatter();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Execute command across repositories
|
|
25
|
+
* @param {string} command - Git command to execute (without 'git' prefix)
|
|
26
|
+
* @param {Object} options - Execution options
|
|
27
|
+
* @param {boolean} options.dryRun - Show commands without executing
|
|
28
|
+
* @returns {Promise<void>}
|
|
29
|
+
*/
|
|
30
|
+
async execute(command, options = {}) {
|
|
31
|
+
const { dryRun = false } = options;
|
|
32
|
+
|
|
33
|
+
if (!command || command.trim().length === 0) {
|
|
34
|
+
console.log(this.formatter.error('Command is required'));
|
|
35
|
+
throw new Error('Command is required');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Load configuration
|
|
39
|
+
let config;
|
|
40
|
+
try {
|
|
41
|
+
config = await this.configManager.loadConfig();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.log(this.formatter.error(error.message));
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (config.repositories.length === 0) {
|
|
48
|
+
console.log(this.formatter.warning('No repositories configured'));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Dry-run mode: just display what would be executed
|
|
53
|
+
if (dryRun) {
|
|
54
|
+
console.log(this.formatter.info('Dry-run mode: Commands that would be executed:'));
|
|
55
|
+
console.log('');
|
|
56
|
+
config.repositories.forEach(repo => {
|
|
57
|
+
console.log(`${this.formatter.info('Repository:')} ${repo.name} (${repo.path})`);
|
|
58
|
+
console.log(` Command: git ${command}`);
|
|
59
|
+
console.log('');
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Execute command in all repositories
|
|
65
|
+
const progress = this.formatter.createProgress('Executing command...');
|
|
66
|
+
progress.start();
|
|
67
|
+
|
|
68
|
+
let results;
|
|
69
|
+
try {
|
|
70
|
+
results = await this.repoManager.execInAllRepos(config.repositories, command);
|
|
71
|
+
progress.stop();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
progress.fail('Failed to execute command');
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Display results
|
|
78
|
+
this.displayResults(results);
|
|
79
|
+
|
|
80
|
+
// Display summary
|
|
81
|
+
this.displaySummary(results);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Display execution results
|
|
86
|
+
* @param {Array<Object>} results - Array of execution results
|
|
87
|
+
*/
|
|
88
|
+
displayResults(results) {
|
|
89
|
+
console.log('\n' + this.formatter.info('Execution Results'));
|
|
90
|
+
console.log('='.repeat(80));
|
|
91
|
+
|
|
92
|
+
results.forEach((result, index) => {
|
|
93
|
+
if (index > 0) {
|
|
94
|
+
console.log('\n' + '-'.repeat(80));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`\n${this.formatter.info('Repository:')} ${result.name} (${result.path})`);
|
|
98
|
+
console.log(`${this.formatter.info('Command:')} git ${result.command}`);
|
|
99
|
+
|
|
100
|
+
if (result.success) {
|
|
101
|
+
console.log(this.formatter.success('Status: Success'));
|
|
102
|
+
if (result.output && result.output.length > 0) {
|
|
103
|
+
console.log('\nOutput:');
|
|
104
|
+
console.log(result.output);
|
|
105
|
+
} else {
|
|
106
|
+
console.log(this.formatter.info('(No output)'));
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
console.log(this.formatter.error(`Status: Failed (exit code: ${result.exitCode})`));
|
|
110
|
+
if (result.error) {
|
|
111
|
+
console.log('\nError:');
|
|
112
|
+
console.log(this.formatter.error(result.error));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
console.log('\n' + '='.repeat(80));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Display execution summary
|
|
122
|
+
* @param {Array<Object>} results - Array of execution results
|
|
123
|
+
*/
|
|
124
|
+
displaySummary(results) {
|
|
125
|
+
const successCount = results.filter(r => r.success).length;
|
|
126
|
+
const failureCount = results.filter(r => !r.success).length;
|
|
127
|
+
const totalCount = results.length;
|
|
128
|
+
|
|
129
|
+
console.log('\n' + this.formatter.info('Summary'));
|
|
130
|
+
console.log(` Total repositories: ${totalCount}`);
|
|
131
|
+
console.log(` ${this.formatter.success(`Successful: ${successCount}`)}`);
|
|
132
|
+
|
|
133
|
+
if (failureCount > 0) {
|
|
134
|
+
console.log(` ${this.formatter.error(`Failed: ${failureCount}`)}`);
|
|
135
|
+
|
|
136
|
+
// List failed repositories
|
|
137
|
+
const failedRepos = results.filter(r => !r.success);
|
|
138
|
+
console.log('\nFailed repositories:');
|
|
139
|
+
failedRepos.forEach(result => {
|
|
140
|
+
console.log(` - ${result.name} (${result.path}): ${result.error}`);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Display exit codes for failed commands
|
|
145
|
+
const failedWithExitCodes = results.filter(r => !r.success && r.exitCode !== 0);
|
|
146
|
+
if (failedWithExitCodes.length > 0) {
|
|
147
|
+
console.log('\nExit codes:');
|
|
148
|
+
failedWithExitCodes.forEach(result => {
|
|
149
|
+
console.log(` - ${result.name}: ${result.exitCode}`);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = ExecHandler;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const RepoManager = require('../repo-manager');
|
|
2
|
+
const ConfigManager = require('../config-manager');
|
|
3
|
+
const OutputFormatter = require('../output-formatter');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* HealthHandler - Handles repository health check command
|
|
7
|
+
*
|
|
8
|
+
* Performs comprehensive health checks on all configured repositories
|
|
9
|
+
* including path validation, Git repository verification, remote
|
|
10
|
+
* reachability, and branch existence.
|
|
11
|
+
*/
|
|
12
|
+
class HealthHandler {
|
|
13
|
+
/**
|
|
14
|
+
* Create a new HealthHandler
|
|
15
|
+
* @param {string} projectRoot - The project root directory
|
|
16
|
+
*/
|
|
17
|
+
constructor(projectRoot) {
|
|
18
|
+
this.projectRoot = projectRoot;
|
|
19
|
+
this.repoManager = new RepoManager(projectRoot);
|
|
20
|
+
this.configManager = new ConfigManager(projectRoot);
|
|
21
|
+
this.formatter = new OutputFormatter();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Execute health check
|
|
26
|
+
* @param {Object} options - Health check options
|
|
27
|
+
* @returns {Promise<void>}
|
|
28
|
+
*/
|
|
29
|
+
async execute(options = {}) {
|
|
30
|
+
// Load configuration
|
|
31
|
+
let config;
|
|
32
|
+
try {
|
|
33
|
+
config = await this.configManager.loadConfig();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.log(this.formatter.error(error.message));
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (config.repositories.length === 0) {
|
|
40
|
+
console.log(this.formatter.warning('No repositories configured'));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Perform health checks
|
|
45
|
+
const progress = this.formatter.createProgress('Performing health checks...');
|
|
46
|
+
progress.start();
|
|
47
|
+
|
|
48
|
+
let results;
|
|
49
|
+
try {
|
|
50
|
+
results = await this.repoManager.checkAllReposHealth(config.repositories);
|
|
51
|
+
progress.stop();
|
|
52
|
+
} catch (error) {
|
|
53
|
+
progress.fail('Failed to perform health checks');
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Display results
|
|
58
|
+
this.displayResults(results);
|
|
59
|
+
|
|
60
|
+
// Display summary
|
|
61
|
+
this.displaySummary(results);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Display health check results
|
|
66
|
+
* @param {Array<Object>} results - Array of health check results
|
|
67
|
+
*/
|
|
68
|
+
displayResults(results) {
|
|
69
|
+
console.log('\n' + this.formatter.info('Health Check Results'));
|
|
70
|
+
console.log('='.repeat(80));
|
|
71
|
+
|
|
72
|
+
results.forEach((result, index) => {
|
|
73
|
+
if (index > 0) {
|
|
74
|
+
console.log('\n' + '-'.repeat(80));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(`\n${this.formatter.info('Repository:')} ${result.name} (${result.path})`);
|
|
78
|
+
|
|
79
|
+
// Overall health status
|
|
80
|
+
if (result.healthy) {
|
|
81
|
+
console.log(this.formatter.success('Status: Healthy ✓'));
|
|
82
|
+
} else {
|
|
83
|
+
console.log(this.formatter.error('Status: Unhealthy ✗'));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Display individual checks
|
|
87
|
+
console.log('\nChecks:');
|
|
88
|
+
console.log(` Path exists: ${this._formatCheckResult(result.checks.pathExists)}`);
|
|
89
|
+
console.log(` Is Git repository: ${this._formatCheckResult(result.checks.isGitRepo)}`);
|
|
90
|
+
|
|
91
|
+
if (result.checks.remoteReachable !== null) {
|
|
92
|
+
console.log(` Remote reachable: ${this._formatCheckResult(result.checks.remoteReachable)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (result.checks.branchExists !== null) {
|
|
96
|
+
console.log(` Branch exists: ${this._formatCheckResult(result.checks.branchExists)}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Display errors
|
|
100
|
+
if (result.errors.length > 0) {
|
|
101
|
+
console.log('\n' + this.formatter.error('Errors:'));
|
|
102
|
+
result.errors.forEach(error => {
|
|
103
|
+
console.log(` ${this.formatter.error('•')} ${error}`);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Display warnings
|
|
108
|
+
if (result.warnings.length > 0) {
|
|
109
|
+
console.log('\n' + this.formatter.warning('Warnings:'));
|
|
110
|
+
result.warnings.forEach(warning => {
|
|
111
|
+
console.log(` ${this.formatter.warning('•')} ${warning}`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
console.log('\n' + '='.repeat(80));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Display health summary
|
|
121
|
+
* @param {Array<Object>} results - Array of health check results
|
|
122
|
+
*/
|
|
123
|
+
displaySummary(results) {
|
|
124
|
+
const healthyCount = results.filter(r => r.healthy).length;
|
|
125
|
+
const unhealthyCount = results.filter(r => !r.healthy).length;
|
|
126
|
+
const totalCount = results.length;
|
|
127
|
+
|
|
128
|
+
console.log('\n' + this.formatter.info('Overall Health Summary'));
|
|
129
|
+
console.log(` Total repositories: ${totalCount}`);
|
|
130
|
+
console.log(` ${this.formatter.success(`Healthy: ${healthyCount}`)}`);
|
|
131
|
+
|
|
132
|
+
if (unhealthyCount > 0) {
|
|
133
|
+
console.log(` ${this.formatter.error(`Unhealthy: ${unhealthyCount}`)}`);
|
|
134
|
+
|
|
135
|
+
// List unhealthy repositories
|
|
136
|
+
const unhealthyRepos = results.filter(r => !r.healthy);
|
|
137
|
+
console.log('\nUnhealthy repositories:');
|
|
138
|
+
unhealthyRepos.forEach(result => {
|
|
139
|
+
const mainError = result.errors.length > 0 ? result.errors[0] : 'Unknown error';
|
|
140
|
+
console.log(` - ${result.name} (${result.path}): ${mainError}`);
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
console.log('\n' + this.formatter.success('All repositories are healthy! 🎉'));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Display warning summary
|
|
147
|
+
const reposWithWarnings = results.filter(r => r.warnings.length > 0);
|
|
148
|
+
if (reposWithWarnings.length > 0) {
|
|
149
|
+
console.log('\n' + this.formatter.warning(`${reposWithWarnings.length} ${reposWithWarnings.length === 1 ? 'repository has' : 'repositories have'} warnings:`));
|
|
150
|
+
reposWithWarnings.forEach(result => {
|
|
151
|
+
console.log(` - ${result.name}: ${result.warnings.length} ${result.warnings.length === 1 ? 'warning' : 'warnings'}`);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Format check result as colored string
|
|
158
|
+
* @private
|
|
159
|
+
* @param {boolean} passed - Whether the check passed
|
|
160
|
+
* @returns {string} Formatted check result
|
|
161
|
+
*/
|
|
162
|
+
_formatCheckResult(passed) {
|
|
163
|
+
return passed
|
|
164
|
+
? this.formatter.success('✓ Pass')
|
|
165
|
+
: this.formatter.error('✗ Fail');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = HealthHandler;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const RepoManager = require('../repo-manager');
|
|
3
|
+
const ConfigManager = require('../config-manager');
|
|
4
|
+
const OutputFormatter = require('../output-formatter');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* InitHandler - Handles repository initialization command
|
|
8
|
+
*
|
|
9
|
+
* Scans the project directory for Git repositories and creates
|
|
10
|
+
* the project-repos.json configuration file.
|
|
11
|
+
*/
|
|
12
|
+
class InitHandler {
|
|
13
|
+
/**
|
|
14
|
+
* Create a new InitHandler
|
|
15
|
+
* @param {string} projectRoot - The project root directory
|
|
16
|
+
*/
|
|
17
|
+
constructor(projectRoot) {
|
|
18
|
+
this.projectRoot = projectRoot;
|
|
19
|
+
this.repoManager = new RepoManager(projectRoot);
|
|
20
|
+
this.configManager = new ConfigManager(projectRoot);
|
|
21
|
+
this.formatter = new OutputFormatter();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Execute repository initialization
|
|
26
|
+
* @param {Object} options - Initialization options
|
|
27
|
+
* @param {boolean} options.yes - Skip confirmation prompts
|
|
28
|
+
* @param {number} options.maxDepth - Maximum scan depth
|
|
29
|
+
* @param {string[]} options.exclude - Directories to exclude
|
|
30
|
+
* @param {boolean} options.nested - Enable nested repository scanning (default: true)
|
|
31
|
+
* @returns {Promise<Object>} Initialization result
|
|
32
|
+
*/
|
|
33
|
+
async execute(options = {}) {
|
|
34
|
+
const { yes = false, maxDepth = 3, exclude = ['.kiro'], nested = true } = options;
|
|
35
|
+
|
|
36
|
+
// Check if configuration already exists
|
|
37
|
+
const configExists = await this.configManager.configExists();
|
|
38
|
+
|
|
39
|
+
if (configExists && !yes) {
|
|
40
|
+
const shouldOverwrite = await this.confirmOverwrite();
|
|
41
|
+
if (!shouldOverwrite) {
|
|
42
|
+
console.log(this.formatter.info('Initialization cancelled'));
|
|
43
|
+
return { cancelled: true, discovered: [], nestedMode: nested };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Scan for repositories
|
|
48
|
+
const scanMode = nested ? 'nested' : 'non-nested';
|
|
49
|
+
const progress = this.formatter.createProgress(`Scanning for Git repositories (${scanMode} mode)...`);
|
|
50
|
+
progress.start();
|
|
51
|
+
|
|
52
|
+
let discovered;
|
|
53
|
+
try {
|
|
54
|
+
discovered = await this.repoManager.discoverRepositories(this.projectRoot, {
|
|
55
|
+
maxDepth,
|
|
56
|
+
exclude,
|
|
57
|
+
nested
|
|
58
|
+
});
|
|
59
|
+
progress.succeed(`Found ${discovered.length} Git ${discovered.length === 1 ? 'repository' : 'repositories'} (${scanMode} scanning)`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
progress.fail('Failed to scan for repositories');
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (discovered.length === 0) {
|
|
66
|
+
console.log(this.formatter.warning('No Git repositories found in the project directory'));
|
|
67
|
+
return { cancelled: false, discovered: [], nestedMode: nested };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create configuration
|
|
71
|
+
const config = {
|
|
72
|
+
version: '1.0',
|
|
73
|
+
repositories: discovered.map(repo => ({
|
|
74
|
+
name: repo.name,
|
|
75
|
+
path: repo.path,
|
|
76
|
+
remote: repo.remote,
|
|
77
|
+
defaultBranch: repo.branch,
|
|
78
|
+
parent: repo.parent || null // Include parent field
|
|
79
|
+
})),
|
|
80
|
+
settings: {
|
|
81
|
+
nestedMode: nested // Store nested mode setting
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Save configuration
|
|
86
|
+
try {
|
|
87
|
+
await this.configManager.saveConfig(config);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.log(this.formatter.error(`Failed to save configuration: ${error.message}`));
|
|
90
|
+
|
|
91
|
+
// Display detailed validation errors if available
|
|
92
|
+
if (error.details && error.details.errors && Array.isArray(error.details.errors)) {
|
|
93
|
+
console.log(this.formatter.error('\nValidation errors:'));
|
|
94
|
+
error.details.errors.forEach((err, index) => {
|
|
95
|
+
console.log(this.formatter.error(` ${index + 1}. ${err}`));
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Display summary
|
|
103
|
+
this.displaySummary({ discovered, configPath: this.configManager.getConfigPath(), nestedMode: nested });
|
|
104
|
+
|
|
105
|
+
return { cancelled: false, discovered, nestedMode: nested };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Prompt user for confirmation if config exists
|
|
110
|
+
* @returns {Promise<boolean>} True if user confirms overwrite
|
|
111
|
+
*/
|
|
112
|
+
async confirmOverwrite() {
|
|
113
|
+
const rl = readline.createInterface({
|
|
114
|
+
input: process.stdin,
|
|
115
|
+
output: process.stdout
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
rl.question(
|
|
120
|
+
this.formatter.warning('Configuration file already exists. Overwrite? (y/N): '),
|
|
121
|
+
(answer) => {
|
|
122
|
+
rl.close();
|
|
123
|
+
const normalized = answer.trim().toLowerCase();
|
|
124
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Display initialization summary
|
|
132
|
+
* @param {Object} result - Initialization result
|
|
133
|
+
* @param {Array} result.discovered - Discovered repositories
|
|
134
|
+
* @param {string} result.configPath - Configuration file path
|
|
135
|
+
* @param {boolean} result.nestedMode - Whether nested scanning was enabled
|
|
136
|
+
*/
|
|
137
|
+
displaySummary(result) {
|
|
138
|
+
const { discovered, configPath, nestedMode } = result;
|
|
139
|
+
|
|
140
|
+
console.log('\n' + this.formatter.success('Repository configuration initialized'));
|
|
141
|
+
console.log(this.formatter.info(`Configuration saved to: ${configPath}`));
|
|
142
|
+
console.log(this.formatter.info(`Scan mode: ${nestedMode ? 'nested' : 'non-nested'}`));
|
|
143
|
+
console.log('\nDiscovered repositories:');
|
|
144
|
+
|
|
145
|
+
// Display table of discovered repositories with parent column
|
|
146
|
+
const hasNestedRepos = discovered.some(r => r.parent);
|
|
147
|
+
const headers = hasNestedRepos
|
|
148
|
+
? ['Name', 'Path', 'Branch', 'Has Remote', 'Parent']
|
|
149
|
+
: ['Name', 'Path', 'Branch', 'Has Remote'];
|
|
150
|
+
|
|
151
|
+
const tableData = discovered.map(repo => {
|
|
152
|
+
const row = [
|
|
153
|
+
repo.name,
|
|
154
|
+
repo.path,
|
|
155
|
+
repo.branch,
|
|
156
|
+
repo.hasRemote ? '✓' : '✗'
|
|
157
|
+
];
|
|
158
|
+
if (hasNestedRepos) {
|
|
159
|
+
row.push(repo.parent || '');
|
|
160
|
+
}
|
|
161
|
+
return row;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const table = this.formatter.formatTable([], {
|
|
165
|
+
head: headers,
|
|
166
|
+
rows: tableData
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
console.log(table);
|
|
170
|
+
|
|
171
|
+
// Display warnings for repos without remotes
|
|
172
|
+
const reposWithoutRemotes = discovered.filter(r => !r.hasRemote);
|
|
173
|
+
if (reposWithoutRemotes.length > 0) {
|
|
174
|
+
console.log('\n' + this.formatter.warning(
|
|
175
|
+
`${reposWithoutRemotes.length} ${reposWithoutRemotes.length === 1 ? 'repository' : 'repositories'} without remote URLs:`
|
|
176
|
+
));
|
|
177
|
+
reposWithoutRemotes.forEach(repo => {
|
|
178
|
+
console.log(` - ${repo.name} (${repo.path})`);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Display nested repository info
|
|
183
|
+
if (hasNestedRepos) {
|
|
184
|
+
const nestedRepos = discovered.filter(r => r.parent);
|
|
185
|
+
console.log('\n' + this.formatter.info(
|
|
186
|
+
`Found ${nestedRepos.length} nested ${nestedRepos.length === 1 ? 'repository' : 'repositories'}`
|
|
187
|
+
));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log('\nNext steps:');
|
|
191
|
+
console.log(' • Run "kse repo status" to view repository status');
|
|
192
|
+
console.log(' • Run "kse repo health" to check repository health');
|
|
193
|
+
console.log(' • Run "kse repo exec <command>" to execute commands across all repos');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = InitHandler;
|