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,514 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const GitOperations = require('./git-operations');
|
|
4
|
+
const PathResolver = require('./path-resolver');
|
|
5
|
+
const RepoError = require('./errors/repo-error');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* RepoManager - Orchestrates operations across multiple repositories
|
|
9
|
+
*
|
|
10
|
+
* Provides high-level operations for repository discovery, status checking,
|
|
11
|
+
* command execution, and health diagnostics across multiple Git repositories.
|
|
12
|
+
*/
|
|
13
|
+
class RepoManager {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new RepoManager
|
|
16
|
+
* @param {string} projectRoot - The project root directory
|
|
17
|
+
*/
|
|
18
|
+
constructor(projectRoot) {
|
|
19
|
+
if (!projectRoot) {
|
|
20
|
+
throw new Error('Project root is required');
|
|
21
|
+
}
|
|
22
|
+
this.projectRoot = projectRoot;
|
|
23
|
+
this.gitOps = new GitOperations();
|
|
24
|
+
this.pathResolver = new PathResolver();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Scan directory for Git repositories
|
|
29
|
+
* @param {string} rootPath - Root path to scan
|
|
30
|
+
* @param {Object} options - Scan options
|
|
31
|
+
* @param {number} options.maxDepth - Maximum depth to scan (default: 3)
|
|
32
|
+
* @param {string[]} options.exclude - Directories to exclude (default: ['.kiro'])
|
|
33
|
+
* @param {boolean} options.nested - Enable nested repository scanning (default: true)
|
|
34
|
+
* @returns {Promise<Array<{path: string, name: string, remote: string|null, branch: string, hasRemote: boolean, parent: string|null}>>}
|
|
35
|
+
*/
|
|
36
|
+
async discoverRepositories(rootPath, options = {}) {
|
|
37
|
+
const maxDepth = options.maxDepth || 3;
|
|
38
|
+
const exclude = options.exclude || ['.kiro'];
|
|
39
|
+
const nested = options.nested !== false; // Default to true
|
|
40
|
+
|
|
41
|
+
const discovered = [];
|
|
42
|
+
const visitedPaths = new Set();
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
await this._scanDirectory(rootPath, rootPath, 0, maxDepth, exclude, discovered, nested, null, visitedPaths);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw new RepoError(
|
|
48
|
+
`Failed to scan directory: ${error.message}`,
|
|
49
|
+
null,
|
|
50
|
+
{ rootPath, error: error.message }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return discovered;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if directory should be skipped during scanning
|
|
59
|
+
* @private
|
|
60
|
+
* @param {string} dirName - Directory name
|
|
61
|
+
* @param {string[]} exclude - User-specified exclusions
|
|
62
|
+
* @returns {boolean} True if directory should be skipped
|
|
63
|
+
*/
|
|
64
|
+
_shouldSkipDirectory(dirName, exclude) {
|
|
65
|
+
// Common non-repository directories to skip
|
|
66
|
+
const commonExclusions = [
|
|
67
|
+
'node_modules',
|
|
68
|
+
'.git',
|
|
69
|
+
'build',
|
|
70
|
+
'dist',
|
|
71
|
+
'target',
|
|
72
|
+
'out',
|
|
73
|
+
'.next',
|
|
74
|
+
'.nuxt',
|
|
75
|
+
'vendor',
|
|
76
|
+
'.idea',
|
|
77
|
+
'.vscode',
|
|
78
|
+
'__pycache__',
|
|
79
|
+
'.pytest_cache',
|
|
80
|
+
'coverage'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// Skip if in common exclusions
|
|
84
|
+
if (commonExclusions.includes(dirName)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Skip if in user-specified exclusions
|
|
89
|
+
if (exclude.includes(dirName)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Recursively scan directory for Git repositories
|
|
98
|
+
* @private
|
|
99
|
+
* @param {string} currentPath - Current directory path
|
|
100
|
+
* @param {string} rootPath - Root path for relative path calculation
|
|
101
|
+
* @param {number} depth - Current depth
|
|
102
|
+
* @param {number} maxDepth - Maximum depth
|
|
103
|
+
* @param {string[]} exclude - Directories to exclude
|
|
104
|
+
* @param {Array} discovered - Array to store discovered repositories
|
|
105
|
+
* @param {boolean} nested - Enable nested repository scanning
|
|
106
|
+
* @param {string|null} parentPath - Parent repository path (for nested repos)
|
|
107
|
+
* @param {Set<string>} visitedPaths - Set of visited paths (for symlink detection)
|
|
108
|
+
*/
|
|
109
|
+
async _scanDirectory(currentPath, rootPath, depth, maxDepth, exclude, discovered, nested, parentPath, visitedPaths) {
|
|
110
|
+
// Check depth limit
|
|
111
|
+
if (depth > maxDepth) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Resolve symlinks and check for circular references
|
|
116
|
+
try {
|
|
117
|
+
const realPath = await fs.realpath(currentPath);
|
|
118
|
+
if (visitedPaths.has(realPath)) {
|
|
119
|
+
// Skip circular symlink
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
visitedPaths.add(realPath);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
// If realpath fails (e.g., in tests with mock fs), use currentPath
|
|
125
|
+
if (visitedPaths.has(currentPath)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
visitedPaths.add(currentPath);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check if current directory should be excluded
|
|
132
|
+
const dirName = path.basename(currentPath);
|
|
133
|
+
if (this._shouldSkipDirectory(dirName, exclude)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check if current directory is a Git repository
|
|
138
|
+
const isRepo = await this.gitOps.isGitRepo(currentPath);
|
|
139
|
+
|
|
140
|
+
if (isRepo) {
|
|
141
|
+
// Extract repository information
|
|
142
|
+
try {
|
|
143
|
+
const branch = await this.gitOps.getCurrentBranch(currentPath);
|
|
144
|
+
const remotes = await this.gitOps.getRemotes(currentPath);
|
|
145
|
+
|
|
146
|
+
// Select remote: prefer 'origin', otherwise use first available
|
|
147
|
+
let remoteUrl = null;
|
|
148
|
+
let hasRemote = false;
|
|
149
|
+
|
|
150
|
+
if (remotes.length > 0) {
|
|
151
|
+
hasRemote = true;
|
|
152
|
+
const originRemote = remotes.find(r => r.name === 'origin');
|
|
153
|
+
if (originRemote) {
|
|
154
|
+
remoteUrl = originRemote.refs.fetch;
|
|
155
|
+
} else {
|
|
156
|
+
remoteUrl = remotes[0].refs.fetch;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Generate repository name from path
|
|
161
|
+
const relativePath = this.pathResolver.toRelative(currentPath, rootPath);
|
|
162
|
+
// Handle empty path (root directory) or '.' (current directory)
|
|
163
|
+
const normalizedPath = (!relativePath || relativePath === '.') ? '.' : relativePath;
|
|
164
|
+
const repoName = normalizedPath === '.' ? path.basename(rootPath) : normalizedPath.replace(/\//g, '-');
|
|
165
|
+
|
|
166
|
+
discovered.push({
|
|
167
|
+
path: normalizedPath,
|
|
168
|
+
name: repoName,
|
|
169
|
+
remote: remoteUrl,
|
|
170
|
+
branch: branch,
|
|
171
|
+
hasRemote: hasRemote,
|
|
172
|
+
parent: parentPath // Track parent repository
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// If nested scanning is disabled, don't scan subdirectories
|
|
176
|
+
if (!nested) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// If nested scanning is enabled, continue scanning subdirectories
|
|
181
|
+
// Update parentPath to current repository for nested repos
|
|
182
|
+
parentPath = relativePath;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
// If we can't get repo info, skip this directory
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Scan subdirectories
|
|
190
|
+
try {
|
|
191
|
+
const entries = await fs.readdir(currentPath, { withFileTypes: true });
|
|
192
|
+
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
if (entry.isDirectory()) {
|
|
195
|
+
const subPath = path.join(currentPath, entry.name);
|
|
196
|
+
await this._scanDirectory(subPath, rootPath, depth + 1, maxDepth, exclude, discovered, nested, parentPath, visitedPaths);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
// If we can't read the directory, skip it
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get status for a single repository
|
|
207
|
+
* @param {Object} repo - Repository configuration
|
|
208
|
+
* @param {string} repo.name - Repository name
|
|
209
|
+
* @param {string} repo.path - Repository path
|
|
210
|
+
* @returns {Promise<Object>} Repository status
|
|
211
|
+
*/
|
|
212
|
+
async getRepoStatus(repo) {
|
|
213
|
+
const repoPath = this.pathResolver.resolvePath(repo.path, this.projectRoot);
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
// Check if path exists
|
|
217
|
+
try {
|
|
218
|
+
await fs.access(repoPath);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
return {
|
|
221
|
+
name: repo.name,
|
|
222
|
+
path: repo.path,
|
|
223
|
+
branch: null,
|
|
224
|
+
isClean: false,
|
|
225
|
+
modified: 0,
|
|
226
|
+
added: 0,
|
|
227
|
+
deleted: 0,
|
|
228
|
+
ahead: 0,
|
|
229
|
+
behind: 0,
|
|
230
|
+
error: 'Path not found or inaccessible'
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check if it's a Git repository
|
|
235
|
+
const isRepo = await this.gitOps.isGitRepo(repoPath);
|
|
236
|
+
if (!isRepo) {
|
|
237
|
+
return {
|
|
238
|
+
name: repo.name,
|
|
239
|
+
path: repo.path,
|
|
240
|
+
branch: null,
|
|
241
|
+
isClean: false,
|
|
242
|
+
modified: 0,
|
|
243
|
+
added: 0,
|
|
244
|
+
deleted: 0,
|
|
245
|
+
ahead: 0,
|
|
246
|
+
behind: 0,
|
|
247
|
+
error: 'Not a Git repository'
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Get status
|
|
252
|
+
const status = await this.gitOps.getStatus(repoPath);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
name: repo.name,
|
|
256
|
+
path: repo.path,
|
|
257
|
+
branch: status.current,
|
|
258
|
+
isClean: status.isClean(),
|
|
259
|
+
modified: status.modified.length,
|
|
260
|
+
added: status.created.length,
|
|
261
|
+
deleted: status.deleted.length,
|
|
262
|
+
ahead: status.ahead,
|
|
263
|
+
behind: status.behind,
|
|
264
|
+
error: null
|
|
265
|
+
};
|
|
266
|
+
} catch (error) {
|
|
267
|
+
return {
|
|
268
|
+
name: repo.name,
|
|
269
|
+
path: repo.path,
|
|
270
|
+
branch: null,
|
|
271
|
+
isClean: false,
|
|
272
|
+
modified: 0,
|
|
273
|
+
added: 0,
|
|
274
|
+
deleted: 0,
|
|
275
|
+
ahead: 0,
|
|
276
|
+
behind: 0,
|
|
277
|
+
error: error.message
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get status for all repositories
|
|
284
|
+
* @param {Array<Object>} repos - Array of repository configurations
|
|
285
|
+
* @returns {Promise<Array<Object>>} Array of repository statuses
|
|
286
|
+
*/
|
|
287
|
+
async getAllRepoStatuses(repos) {
|
|
288
|
+
const statuses = [];
|
|
289
|
+
|
|
290
|
+
for (const repo of repos) {
|
|
291
|
+
const status = await this.getRepoStatus(repo);
|
|
292
|
+
statuses.push(status);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return statuses;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Execute command in a repository
|
|
300
|
+
* @param {Object} repo - Repository configuration
|
|
301
|
+
* @param {string} command - Git command to execute (without 'git' prefix)
|
|
302
|
+
* @returns {Promise<Object>} Execution result
|
|
303
|
+
*/
|
|
304
|
+
async execInRepo(repo, command) {
|
|
305
|
+
const repoPath = this.pathResolver.resolvePath(repo.path, this.projectRoot);
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
// Check if path exists and is a Git repository
|
|
309
|
+
try {
|
|
310
|
+
await fs.access(repoPath);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return {
|
|
313
|
+
name: repo.name,
|
|
314
|
+
path: repo.path,
|
|
315
|
+
command: command,
|
|
316
|
+
success: false,
|
|
317
|
+
output: '',
|
|
318
|
+
error: 'Path not found or inaccessible',
|
|
319
|
+
exitCode: 1
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const isRepo = await this.gitOps.isGitRepo(repoPath);
|
|
324
|
+
if (!isRepo) {
|
|
325
|
+
return {
|
|
326
|
+
name: repo.name,
|
|
327
|
+
path: repo.path,
|
|
328
|
+
command: command,
|
|
329
|
+
success: false,
|
|
330
|
+
output: '',
|
|
331
|
+
error: 'Not a Git repository',
|
|
332
|
+
exitCode: 1
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Parse command into arguments
|
|
337
|
+
// Check if command already starts with "git" to avoid duplication
|
|
338
|
+
const trimmedCommand = command.trim();
|
|
339
|
+
const args = trimmedCommand.startsWith('git ')
|
|
340
|
+
? trimmedCommand.substring(4).trim().split(/\s+/)
|
|
341
|
+
: trimmedCommand.split(/\s+/);
|
|
342
|
+
|
|
343
|
+
// Execute command
|
|
344
|
+
const output = await this.gitOps.execRaw(repoPath, args);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
name: repo.name,
|
|
348
|
+
path: repo.path,
|
|
349
|
+
command: command,
|
|
350
|
+
success: true,
|
|
351
|
+
output: output.trim(),
|
|
352
|
+
error: null,
|
|
353
|
+
exitCode: 0
|
|
354
|
+
};
|
|
355
|
+
} catch (error) {
|
|
356
|
+
return {
|
|
357
|
+
name: repo.name,
|
|
358
|
+
path: repo.path,
|
|
359
|
+
command: command,
|
|
360
|
+
success: false,
|
|
361
|
+
output: '',
|
|
362
|
+
error: error.message,
|
|
363
|
+
exitCode: error.exitCode || 1
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Execute command in all repositories
|
|
370
|
+
* @param {Array<Object>} repos - Array of repository configurations
|
|
371
|
+
* @param {string} command - Git command to execute
|
|
372
|
+
* @returns {Promise<Array<Object>>} Array of execution results
|
|
373
|
+
*/
|
|
374
|
+
async execInAllRepos(repos, command) {
|
|
375
|
+
const results = [];
|
|
376
|
+
|
|
377
|
+
for (const repo of repos) {
|
|
378
|
+
const result = await this.execInRepo(repo, command);
|
|
379
|
+
results.push(result);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return results;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Perform health check on a repository
|
|
387
|
+
* @param {Object} repo - Repository configuration
|
|
388
|
+
* @param {string} repo.name - Repository name
|
|
389
|
+
* @param {string} repo.path - Repository path
|
|
390
|
+
* @param {string} [repo.remote] - Remote URL
|
|
391
|
+
* @param {string} [repo.defaultBranch] - Default branch name
|
|
392
|
+
* @returns {Promise<Object>} Health check result
|
|
393
|
+
*/
|
|
394
|
+
async checkRepoHealth(repo) {
|
|
395
|
+
const repoPath = this.pathResolver.resolvePath(repo.path, this.projectRoot);
|
|
396
|
+
const checks = {
|
|
397
|
+
pathExists: false,
|
|
398
|
+
isGitRepo: false,
|
|
399
|
+
remoteReachable: null,
|
|
400
|
+
branchExists: null
|
|
401
|
+
};
|
|
402
|
+
const errors = [];
|
|
403
|
+
const warnings = [];
|
|
404
|
+
|
|
405
|
+
// Check 1: Path exists
|
|
406
|
+
try {
|
|
407
|
+
await fs.access(repoPath);
|
|
408
|
+
checks.pathExists = true;
|
|
409
|
+
} catch (error) {
|
|
410
|
+
errors.push(`Path does not exist or is not accessible: ${repo.path}`);
|
|
411
|
+
return {
|
|
412
|
+
name: repo.name,
|
|
413
|
+
path: repo.path,
|
|
414
|
+
checks,
|
|
415
|
+
errors,
|
|
416
|
+
warnings,
|
|
417
|
+
healthy: false
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Check 2: Is Git repository
|
|
422
|
+
try {
|
|
423
|
+
checks.isGitRepo = await this.gitOps.isGitRepo(repoPath);
|
|
424
|
+
if (!checks.isGitRepo) {
|
|
425
|
+
errors.push(`Path is not a valid Git repository: ${repo.path}`);
|
|
426
|
+
return {
|
|
427
|
+
name: repo.name,
|
|
428
|
+
path: repo.path,
|
|
429
|
+
checks,
|
|
430
|
+
errors,
|
|
431
|
+
warnings,
|
|
432
|
+
healthy: false
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
errors.push(`Failed to verify Git repository: ${error.message}`);
|
|
437
|
+
return {
|
|
438
|
+
name: repo.name,
|
|
439
|
+
path: repo.path,
|
|
440
|
+
checks,
|
|
441
|
+
errors,
|
|
442
|
+
warnings,
|
|
443
|
+
healthy: false
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Check 3: Remote reachable (if remote is configured)
|
|
448
|
+
if (repo.remote) {
|
|
449
|
+
try {
|
|
450
|
+
const remotes = await this.gitOps.getRemotes(repoPath);
|
|
451
|
+
const hasOrigin = remotes.some(r => r.name === 'origin');
|
|
452
|
+
|
|
453
|
+
if (hasOrigin) {
|
|
454
|
+
checks.remoteReachable = await this.gitOps.isRemoteReachable(repoPath, 'origin');
|
|
455
|
+
if (!checks.remoteReachable) {
|
|
456
|
+
warnings.push('Remote "origin" is not reachable (network issue or invalid URL)');
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
checks.remoteReachable = false;
|
|
460
|
+
warnings.push('Remote "origin" is not configured');
|
|
461
|
+
}
|
|
462
|
+
} catch (error) {
|
|
463
|
+
checks.remoteReachable = false;
|
|
464
|
+
warnings.push(`Failed to check remote: ${error.message}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Check 4: Default branch exists (if configured)
|
|
469
|
+
if (repo.defaultBranch) {
|
|
470
|
+
try {
|
|
471
|
+
const currentBranch = await this.gitOps.getCurrentBranch(repoPath);
|
|
472
|
+
checks.branchExists = currentBranch === repo.defaultBranch;
|
|
473
|
+
|
|
474
|
+
if (!checks.branchExists) {
|
|
475
|
+
warnings.push(
|
|
476
|
+
`Current branch "${currentBranch}" differs from configured default branch "${repo.defaultBranch}"`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
} catch (error) {
|
|
480
|
+
checks.branchExists = false;
|
|
481
|
+
warnings.push(`Failed to check branch: ${error.message}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const healthy = errors.length === 0;
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
name: repo.name,
|
|
489
|
+
path: repo.path,
|
|
490
|
+
checks,
|
|
491
|
+
errors,
|
|
492
|
+
warnings,
|
|
493
|
+
healthy
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Perform health check on all repositories
|
|
499
|
+
* @param {Array<Object>} repos - Array of repository configurations
|
|
500
|
+
* @returns {Promise<Array<Object>>} Array of health check results
|
|
501
|
+
*/
|
|
502
|
+
async checkAllReposHealth(repos) {
|
|
503
|
+
const results = [];
|
|
504
|
+
|
|
505
|
+
for (const repo of repos) {
|
|
506
|
+
const result = await this.checkRepoHealth(repo);
|
|
507
|
+
results.push(result);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return results;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
module.exports = RepoManager;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function createId(prefix) {
|
|
6
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class AuditEmitter {
|
|
10
|
+
constructor(projectRoot = process.cwd(), options = {}) {
|
|
11
|
+
this.projectRoot = projectRoot;
|
|
12
|
+
this.auditFile = options.auditFile || path.join(projectRoot, '.kiro', 'audit', 'scene-runtime-events.jsonl');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async emit(eventType, payload = {}) {
|
|
16
|
+
const event = {
|
|
17
|
+
event_id: createId('srevt'),
|
|
18
|
+
event_type: eventType,
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
trace_id: payload.trace_id || payload.traceId || createId('trace'),
|
|
21
|
+
scene_ref: payload.scene_ref || payload.sceneRef || null,
|
|
22
|
+
scene_version: payload.scene_version || payload.sceneVersion || null,
|
|
23
|
+
run_mode: payload.run_mode || payload.runMode || null,
|
|
24
|
+
actor: payload.actor || 'kse.scene-runtime',
|
|
25
|
+
payload: payload.payload || payload
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
event.checksum = this.calculateChecksum(event);
|
|
29
|
+
|
|
30
|
+
await fs.ensureDir(path.dirname(this.auditFile));
|
|
31
|
+
await fs.appendFile(this.auditFile, `${JSON.stringify(event)}\n`, 'utf8');
|
|
32
|
+
|
|
33
|
+
return event;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
calculateChecksum(event) {
|
|
37
|
+
const { checksum, ...rest } = event;
|
|
38
|
+
return crypto.createHash('sha256').update(JSON.stringify(rest)).digest('hex');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
verifyEvent(event) {
|
|
42
|
+
return this.calculateChecksum(event) === event.checksum;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async readAll() {
|
|
46
|
+
if (!await fs.pathExists(this.auditFile)) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const content = await fs.readFile(this.auditFile, 'utf8');
|
|
51
|
+
return content
|
|
52
|
+
.split('\n')
|
|
53
|
+
.map((line) => line.trim())
|
|
54
|
+
.filter(Boolean)
|
|
55
|
+
.map((line) => JSON.parse(line));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = AuditEmitter;
|