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,782 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateManager - Main template management class
|
|
3
|
+
*
|
|
4
|
+
* Coordinates all template operations: sources, discovery, download, caching, and application.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const GitHandler = require('./git-handler');
|
|
11
|
+
const CacheManager = require('./cache-manager');
|
|
12
|
+
const RegistryParser = require('./registry-parser');
|
|
13
|
+
const TemplateValidator = require('./template-validator');
|
|
14
|
+
const TemplateApplicator = require('./template-applicator');
|
|
15
|
+
const { ValidationError, NetworkError } = require('./template-error');
|
|
16
|
+
|
|
17
|
+
class TemplateManager {
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
this.cacheDir = options.cacheDir || path.join(os.homedir(), '.kse', 'templates');
|
|
20
|
+
|
|
21
|
+
// Initialize components
|
|
22
|
+
this.gitHandler = new GitHandler();
|
|
23
|
+
this.cacheManager = new CacheManager(this.cacheDir);
|
|
24
|
+
this.registryParser = new RegistryParser();
|
|
25
|
+
this.validator = new TemplateValidator();
|
|
26
|
+
this.applicator = new TemplateApplicator();
|
|
27
|
+
|
|
28
|
+
// Sources configuration file
|
|
29
|
+
this.sourcesFile = path.join(this.cacheDir, '.sources.json');
|
|
30
|
+
|
|
31
|
+
// Default official source
|
|
32
|
+
this.officialSource = {
|
|
33
|
+
name: 'official',
|
|
34
|
+
type: 'official',
|
|
35
|
+
url: 'https://github.com/heguangyong/scene-capability-engine-templates.git',
|
|
36
|
+
branch: 'main',
|
|
37
|
+
enabled: true
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets all configured sources
|
|
43
|
+
*
|
|
44
|
+
* @returns {Promise<Object[]>} Array of source configurations
|
|
45
|
+
*/
|
|
46
|
+
async getSources() {
|
|
47
|
+
if (!await fs.pathExists(this.sourcesFile)) {
|
|
48
|
+
// Return default official source
|
|
49
|
+
return [this.officialSource];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const sources = await fs.readJson(this.sourcesFile);
|
|
54
|
+
return sources.sources || [this.officialSource];
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return [this.officialSource];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Saves sources configuration
|
|
62
|
+
*
|
|
63
|
+
* @param {Object[]} sources - Array of source configurations
|
|
64
|
+
* @returns {Promise<void>}
|
|
65
|
+
*/
|
|
66
|
+
async saveSources(sources) {
|
|
67
|
+
await this.cacheManager.ensureCacheDir();
|
|
68
|
+
await fs.writeJson(this.sourcesFile, { sources }, { spaces: 2 });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Adds a custom template source
|
|
73
|
+
*
|
|
74
|
+
* @param {string} name - Source name
|
|
75
|
+
* @param {string} gitUrl - Git repository URL
|
|
76
|
+
* @param {Object} options - Source options
|
|
77
|
+
* @returns {Promise<Object>} Result
|
|
78
|
+
*/
|
|
79
|
+
async addSource(name, gitUrl, options = {}) {
|
|
80
|
+
const { branch = 'main' } = options;
|
|
81
|
+
|
|
82
|
+
// Validate name
|
|
83
|
+
if (!name || !/^[a-z0-9-]+$/.test(name)) {
|
|
84
|
+
throw new ValidationError(
|
|
85
|
+
'Invalid source name. Use lowercase letters, numbers, and hyphens only.',
|
|
86
|
+
{ name }
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check if source already exists
|
|
91
|
+
const sources = await this.getSources();
|
|
92
|
+
if (sources.some(s => s.name === name)) {
|
|
93
|
+
throw new ValidationError(
|
|
94
|
+
'Source already exists',
|
|
95
|
+
{ name }
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add new source
|
|
100
|
+
const newSource = {
|
|
101
|
+
name,
|
|
102
|
+
type: 'custom',
|
|
103
|
+
url: gitUrl,
|
|
104
|
+
branch,
|
|
105
|
+
enabled: true
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
sources.push(newSource);
|
|
109
|
+
await this.saveSources(sources);
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
source: newSource,
|
|
114
|
+
message: `Source "${name}" added successfully`
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Removes a template source
|
|
120
|
+
*
|
|
121
|
+
* @param {string} name - Source name
|
|
122
|
+
* @param {Object} options - Options
|
|
123
|
+
* @param {boolean} options.clearCache - Clear cached templates
|
|
124
|
+
* @returns {Promise<Object>} Result
|
|
125
|
+
*/
|
|
126
|
+
async removeSource(name, options = {}) {
|
|
127
|
+
const { clearCache = false } = options;
|
|
128
|
+
|
|
129
|
+
// Cannot remove official source
|
|
130
|
+
if (name === 'official') {
|
|
131
|
+
throw new ValidationError(
|
|
132
|
+
'Cannot remove official source',
|
|
133
|
+
{ name }
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const sources = await this.getSources();
|
|
138
|
+
const index = sources.findIndex(s => s.name === name);
|
|
139
|
+
|
|
140
|
+
if (index === -1) {
|
|
141
|
+
throw new ValidationError(
|
|
142
|
+
'Source not found',
|
|
143
|
+
{ name }
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Remove source
|
|
148
|
+
sources.splice(index, 1);
|
|
149
|
+
await this.saveSources(sources);
|
|
150
|
+
|
|
151
|
+
// Clear cache if requested
|
|
152
|
+
if (clearCache) {
|
|
153
|
+
await this.cacheManager.clearCache(name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
success: true,
|
|
158
|
+
message: `Source "${name}" removed successfully`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Lists all template sources
|
|
164
|
+
*
|
|
165
|
+
* @returns {Promise<Object[]>} Array of sources with status
|
|
166
|
+
*/
|
|
167
|
+
async listSources() {
|
|
168
|
+
const sources = await this.getSources();
|
|
169
|
+
const result = [];
|
|
170
|
+
|
|
171
|
+
for (const source of sources) {
|
|
172
|
+
const cached = await this.cacheManager.cacheExists(source.name);
|
|
173
|
+
const metadata = await this.cacheManager.getSourceMetadata(source.name);
|
|
174
|
+
|
|
175
|
+
result.push({
|
|
176
|
+
...source,
|
|
177
|
+
cached,
|
|
178
|
+
lastUpdated: metadata ? metadata.last_updated : null,
|
|
179
|
+
templateCount: metadata ? metadata.template_count : null
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Enables or disables a source
|
|
188
|
+
*
|
|
189
|
+
* @param {string} name - Source name
|
|
190
|
+
* @param {boolean} enabled - Enable or disable
|
|
191
|
+
* @returns {Promise<void>}
|
|
192
|
+
*/
|
|
193
|
+
async setSourceEnabled(name, enabled) {
|
|
194
|
+
const sources = await this.getSources();
|
|
195
|
+
const source = sources.find(s => s.name === name);
|
|
196
|
+
|
|
197
|
+
if (!source) {
|
|
198
|
+
throw new ValidationError(
|
|
199
|
+
'Source not found',
|
|
200
|
+
{ name }
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
source.enabled = enabled;
|
|
205
|
+
await this.saveSources(sources);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Gets a specific source by name
|
|
210
|
+
*
|
|
211
|
+
* @param {string} name - Source name
|
|
212
|
+
* @returns {Promise<Object|null>} Source or null
|
|
213
|
+
*/
|
|
214
|
+
async getSource(name) {
|
|
215
|
+
const sources = await this.getSources();
|
|
216
|
+
return sources.find(s => s.name === name) || null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Lists all templates from all enabled sources
|
|
221
|
+
*
|
|
222
|
+
* @param {Object} options - List options
|
|
223
|
+
* @param {string} options.category - Filter by category
|
|
224
|
+
* @param {string} options.source - Filter by source
|
|
225
|
+
* @returns {Promise<Object[]>} Array of templates
|
|
226
|
+
*/
|
|
227
|
+
async listTemplates(options = {}) {
|
|
228
|
+
const { category = null, source = null } = options;
|
|
229
|
+
|
|
230
|
+
const sources = await this.getSources();
|
|
231
|
+
const enabledSources = sources.filter(s => s.enabled);
|
|
232
|
+
|
|
233
|
+
// Filter by source if specified
|
|
234
|
+
const targetSources = source
|
|
235
|
+
? enabledSources.filter(s => s.name === source)
|
|
236
|
+
: enabledSources;
|
|
237
|
+
|
|
238
|
+
const allTemplates = [];
|
|
239
|
+
|
|
240
|
+
for (const src of targetSources) {
|
|
241
|
+
// Check if source is cached
|
|
242
|
+
if (!await this.cacheManager.cacheExists(src.name)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Parse registry
|
|
247
|
+
const registryPath = path.join(
|
|
248
|
+
this.cacheManager.getSourceCachePath(src.name),
|
|
249
|
+
'template-registry.json'
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
if (!await fs.pathExists(registryPath)) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const registry = await this.registryParser.parseRegistry(registryPath);
|
|
258
|
+
const index = this.registryParser.buildIndex(registry);
|
|
259
|
+
|
|
260
|
+
// Get templates
|
|
261
|
+
let templates = category
|
|
262
|
+
? this.registryParser.getTemplatesByCategory(index, category)
|
|
263
|
+
: index.all;
|
|
264
|
+
|
|
265
|
+
// Add source information
|
|
266
|
+
templates = templates.map(t => ({
|
|
267
|
+
...t,
|
|
268
|
+
source: src.name
|
|
269
|
+
}));
|
|
270
|
+
|
|
271
|
+
allTemplates.push(...templates);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
// Skip sources with invalid registries
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return allTemplates;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Searches templates across all enabled sources
|
|
283
|
+
*
|
|
284
|
+
* @param {string} keyword - Search keyword
|
|
285
|
+
* @param {Object} filters - Search filters
|
|
286
|
+
* @returns {Promise<Object[]>} Array of matching templates
|
|
287
|
+
*/
|
|
288
|
+
async searchTemplates(keyword, filters = {}) {
|
|
289
|
+
const sources = await this.getSources();
|
|
290
|
+
const enabledSources = sources.filter(s => s.enabled);
|
|
291
|
+
|
|
292
|
+
const allResults = [];
|
|
293
|
+
|
|
294
|
+
for (const src of enabledSources) {
|
|
295
|
+
if (!await this.cacheManager.cacheExists(src.name)) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const registryPath = path.join(
|
|
300
|
+
this.cacheManager.getSourceCachePath(src.name),
|
|
301
|
+
'template-registry.json'
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
if (!await fs.pathExists(registryPath)) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const registry = await this.registryParser.parseRegistry(registryPath);
|
|
310
|
+
const index = this.registryParser.buildIndex(registry);
|
|
311
|
+
|
|
312
|
+
const results = this.registryParser.searchTemplates(index, keyword, filters);
|
|
313
|
+
|
|
314
|
+
// Add source information
|
|
315
|
+
const resultsWithSource = results.map(t => ({
|
|
316
|
+
...t,
|
|
317
|
+
source: src.name
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
allResults.push(...resultsWithSource);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return allResults;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Shows detailed information about a template
|
|
331
|
+
*
|
|
332
|
+
* @param {string} templatePath - Template path (e.g., 'web-features/rest-api' or 'official:web-features/rest-api')
|
|
333
|
+
* @returns {Promise<Object>} Template details
|
|
334
|
+
*/
|
|
335
|
+
async showTemplate(templatePath) {
|
|
336
|
+
// Parse template path
|
|
337
|
+
const { sourceName, templateId } = this._parseTemplatePath(templatePath);
|
|
338
|
+
|
|
339
|
+
// Get source
|
|
340
|
+
const source = await this.getSource(sourceName);
|
|
341
|
+
if (!source) {
|
|
342
|
+
throw new ValidationError(
|
|
343
|
+
'Source not found',
|
|
344
|
+
{ source: sourceName }
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check cache
|
|
349
|
+
if (!await this.cacheManager.cacheExists(sourceName)) {
|
|
350
|
+
throw new ValidationError(
|
|
351
|
+
'Source not cached. Run update first.',
|
|
352
|
+
{ source: sourceName }
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Parse registry
|
|
357
|
+
const registryPath = path.join(
|
|
358
|
+
this.cacheManager.getSourceCachePath(sourceName),
|
|
359
|
+
'template-registry.json'
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
const registry = await this.registryParser.parseRegistry(registryPath);
|
|
363
|
+
const index = this.registryParser.buildIndex(registry);
|
|
364
|
+
|
|
365
|
+
// Find template
|
|
366
|
+
const template = this.registryParser.getTemplateById(index, templateId);
|
|
367
|
+
|
|
368
|
+
if (!template) {
|
|
369
|
+
throw new ValidationError(
|
|
370
|
+
'Template not found',
|
|
371
|
+
{ templateId, source: sourceName }
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
...template,
|
|
377
|
+
source: sourceName
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Gets all categories from all enabled sources
|
|
383
|
+
*
|
|
384
|
+
* @returns {Promise<string[]>} Array of unique categories
|
|
385
|
+
*/
|
|
386
|
+
async getCategories() {
|
|
387
|
+
const sources = await this.getSources();
|
|
388
|
+
const enabledSources = sources.filter(s => s.enabled);
|
|
389
|
+
|
|
390
|
+
const allCategories = new Set();
|
|
391
|
+
|
|
392
|
+
for (const src of enabledSources) {
|
|
393
|
+
if (!await this.cacheManager.cacheExists(src.name)) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const registryPath = path.join(
|
|
398
|
+
this.cacheManager.getSourceCachePath(src.name),
|
|
399
|
+
'template-registry.json'
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
if (!await fs.pathExists(registryPath)) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const registry = await this.registryParser.parseRegistry(registryPath);
|
|
408
|
+
const index = this.registryParser.buildIndex(registry);
|
|
409
|
+
const categories = this.registryParser.getCategories(index);
|
|
410
|
+
|
|
411
|
+
categories.forEach(cat => allCategories.add(cat));
|
|
412
|
+
} catch (error) {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return Array.from(allCategories).sort();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Parses template path to extract source and template ID
|
|
422
|
+
*
|
|
423
|
+
* @param {string} templatePath - Template path
|
|
424
|
+
* @returns {Object} Parsed path
|
|
425
|
+
* @private
|
|
426
|
+
*/
|
|
427
|
+
_parseTemplatePath(templatePath) {
|
|
428
|
+
// Format: 'source:category/template' or 'category/template'
|
|
429
|
+
if (templatePath.includes(':')) {
|
|
430
|
+
const [sourceName, templateId] = templatePath.split(':', 2);
|
|
431
|
+
return { sourceName, templateId };
|
|
432
|
+
} else {
|
|
433
|
+
return { sourceName: 'official', templateId: templatePath };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Downloads templates from a source
|
|
439
|
+
*
|
|
440
|
+
* @param {string} sourceName - Source name (default: 'official')
|
|
441
|
+
* @returns {Promise<Object>} Download result
|
|
442
|
+
*/
|
|
443
|
+
async downloadTemplates(sourceName = 'official') {
|
|
444
|
+
const source = await this.getSource(sourceName);
|
|
445
|
+
|
|
446
|
+
if (!source) {
|
|
447
|
+
throw new ValidationError(
|
|
448
|
+
'Source not found',
|
|
449
|
+
{ source: sourceName }
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const targetPath = this.cacheManager.getSourceCachePath(sourceName);
|
|
454
|
+
|
|
455
|
+
// Check if already cached
|
|
456
|
+
if (await this.cacheManager.cacheExists(sourceName)) {
|
|
457
|
+
throw new ValidationError(
|
|
458
|
+
'Templates already cached. Use update to refresh.',
|
|
459
|
+
{ source: sourceName }
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Clone repository
|
|
464
|
+
try {
|
|
465
|
+
await this.gitHandler.cloneRepository(source.url, targetPath, {
|
|
466
|
+
shallow: true,
|
|
467
|
+
branch: source.branch
|
|
468
|
+
});
|
|
469
|
+
} catch (error) {
|
|
470
|
+
throw new NetworkError(
|
|
471
|
+
'Failed to download templates',
|
|
472
|
+
{
|
|
473
|
+
source: sourceName,
|
|
474
|
+
url: source.url,
|
|
475
|
+
error: error.message
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Validate repository structure
|
|
481
|
+
const validation = await this.gitHandler.validateRepository(targetPath);
|
|
482
|
+
if (!validation.valid) {
|
|
483
|
+
// Clean up invalid download
|
|
484
|
+
await fs.remove(targetPath);
|
|
485
|
+
|
|
486
|
+
throw new ValidationError(
|
|
487
|
+
'Downloaded repository is invalid',
|
|
488
|
+
{
|
|
489
|
+
source: sourceName,
|
|
490
|
+
errors: validation.errors
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Count templates
|
|
496
|
+
const registryPath = path.join(targetPath, 'template-registry.json');
|
|
497
|
+
const registry = await this.registryParser.parseRegistry(registryPath);
|
|
498
|
+
const templateCount = registry.templates.length;
|
|
499
|
+
|
|
500
|
+
// Update cache metadata
|
|
501
|
+
await this.cacheManager.updateMetadata(sourceName, {
|
|
502
|
+
version: await this.gitHandler.getCurrentCommit(targetPath),
|
|
503
|
+
template_count: templateCount,
|
|
504
|
+
size_bytes: await this.cacheManager.getCacheSize(sourceName)
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
return {
|
|
508
|
+
success: true,
|
|
509
|
+
source: sourceName,
|
|
510
|
+
templateCount,
|
|
511
|
+
message: `Downloaded ${templateCount} templates from ${sourceName}`
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Updates templates from a source
|
|
517
|
+
*
|
|
518
|
+
* @param {string} sourceName - Source name (null = all sources)
|
|
519
|
+
* @param {string} version - Specific version to checkout (optional)
|
|
520
|
+
* @returns {Promise<Object>} Update result
|
|
521
|
+
*/
|
|
522
|
+
async updateTemplates(sourceName = null, version = null) {
|
|
523
|
+
if (sourceName) {
|
|
524
|
+
return await this._updateSingleSource(sourceName, version);
|
|
525
|
+
} else {
|
|
526
|
+
return await this._updateAllSources();
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Updates a single source
|
|
532
|
+
*
|
|
533
|
+
* @param {string} sourceName - Source name
|
|
534
|
+
* @param {string} version - Specific version (optional)
|
|
535
|
+
* @returns {Promise<Object>} Update result
|
|
536
|
+
* @private
|
|
537
|
+
*/
|
|
538
|
+
async _updateSingleSource(sourceName, version = null) {
|
|
539
|
+
const source = await this.getSource(sourceName);
|
|
540
|
+
|
|
541
|
+
if (!source) {
|
|
542
|
+
throw new ValidationError(
|
|
543
|
+
'Source not found',
|
|
544
|
+
{ source: sourceName }
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const targetPath = this.cacheManager.getSourceCachePath(sourceName);
|
|
549
|
+
|
|
550
|
+
// If not cached, download instead
|
|
551
|
+
if (!await this.cacheManager.cacheExists(sourceName)) {
|
|
552
|
+
return await this.downloadTemplates(sourceName);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Get old template list
|
|
556
|
+
const oldRegistryPath = path.join(targetPath, 'template-registry.json');
|
|
557
|
+
const oldRegistry = await this.registryParser.parseRegistry(oldRegistryPath);
|
|
558
|
+
const oldTemplates = new Map(
|
|
559
|
+
oldRegistry.templates.map(t => [t.id, t])
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
// Pull updates
|
|
563
|
+
try {
|
|
564
|
+
await this.gitHandler.pullUpdates(targetPath);
|
|
565
|
+
|
|
566
|
+
// Checkout specific version if requested
|
|
567
|
+
if (version) {
|
|
568
|
+
await this.gitHandler.checkoutVersion(targetPath, version);
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
throw new NetworkError(
|
|
572
|
+
'Failed to update templates',
|
|
573
|
+
{
|
|
574
|
+
source: sourceName,
|
|
575
|
+
error: error.message
|
|
576
|
+
}
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Get new template list
|
|
581
|
+
const newRegistry = await this.registryParser.parseRegistry(oldRegistryPath);
|
|
582
|
+
const newTemplates = new Map(
|
|
583
|
+
newRegistry.templates.map(t => [t.id, t])
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
// Detect changes
|
|
587
|
+
const changes = {
|
|
588
|
+
added: 0,
|
|
589
|
+
modified: 0,
|
|
590
|
+
deleted: 0,
|
|
591
|
+
details: {
|
|
592
|
+
added: [],
|
|
593
|
+
modified: [],
|
|
594
|
+
deleted: []
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// Find added and modified templates
|
|
599
|
+
for (const [id, newTemplate] of newTemplates) {
|
|
600
|
+
if (!oldTemplates.has(id)) {
|
|
601
|
+
changes.added++;
|
|
602
|
+
changes.details.added.push(id);
|
|
603
|
+
} else {
|
|
604
|
+
const oldTemplate = oldTemplates.get(id);
|
|
605
|
+
// Check if template was modified (compare updated_at or version)
|
|
606
|
+
if (oldTemplate.updated_at !== newTemplate.updated_at ||
|
|
607
|
+
oldTemplate.version !== newTemplate.version) {
|
|
608
|
+
changes.modified++;
|
|
609
|
+
changes.details.modified.push(id);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Find deleted templates
|
|
615
|
+
for (const [id] of oldTemplates) {
|
|
616
|
+
if (!newTemplates.has(id)) {
|
|
617
|
+
changes.deleted++;
|
|
618
|
+
changes.details.deleted.push(id);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Update cache metadata
|
|
623
|
+
await this.cacheManager.updateMetadata(sourceName, {
|
|
624
|
+
version: await this.gitHandler.getCurrentCommit(targetPath),
|
|
625
|
+
template_count: newTemplates.size,
|
|
626
|
+
size_bytes: await this.cacheManager.getCacheSize(sourceName)
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
success: true,
|
|
631
|
+
source: sourceName,
|
|
632
|
+
changes,
|
|
633
|
+
message: `Updated ${sourceName}: ${changes.added} added, ${changes.modified} modified, ${changes.deleted} deleted`
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Updates all enabled sources
|
|
639
|
+
*
|
|
640
|
+
* @returns {Promise<Object>} Update result
|
|
641
|
+
* @private
|
|
642
|
+
*/
|
|
643
|
+
async _updateAllSources() {
|
|
644
|
+
const sources = await this.getSources();
|
|
645
|
+
const enabledSources = sources.filter(s => s.enabled);
|
|
646
|
+
|
|
647
|
+
const results = [];
|
|
648
|
+
const errors = [];
|
|
649
|
+
|
|
650
|
+
for (const source of enabledSources) {
|
|
651
|
+
try {
|
|
652
|
+
const result = await this._updateSingleSource(source.name);
|
|
653
|
+
results.push(result);
|
|
654
|
+
} catch (error) {
|
|
655
|
+
errors.push({
|
|
656
|
+
source: source.name,
|
|
657
|
+
error: error.message
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return {
|
|
663
|
+
success: errors.length === 0,
|
|
664
|
+
results,
|
|
665
|
+
errors,
|
|
666
|
+
message: `Updated ${results.length} source(s), ${errors.length} error(s)`
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Ensures templates are cached (downloads if needed)
|
|
672
|
+
*
|
|
673
|
+
* @param {string} sourceName - Source name
|
|
674
|
+
* @returns {Promise<void>}
|
|
675
|
+
*/
|
|
676
|
+
async ensureCached(sourceName) {
|
|
677
|
+
if (!await this.cacheManager.cacheExists(sourceName)) {
|
|
678
|
+
await this.downloadTemplates(sourceName);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Applies a template to create a new Spec
|
|
684
|
+
*
|
|
685
|
+
* @param {string} specName - Spec name
|
|
686
|
+
* @param {string} templatePath - Template path
|
|
687
|
+
* @param {Object} options - Application options
|
|
688
|
+
* @returns {Promise<Object>} Application result
|
|
689
|
+
*/
|
|
690
|
+
async applyTemplate(specName, templatePath, options = {}) {
|
|
691
|
+
const { force = false, variables = {} } = options;
|
|
692
|
+
|
|
693
|
+
// Parse template path
|
|
694
|
+
const { sourceName, templateId } = this._parseTemplatePath(templatePath);
|
|
695
|
+
|
|
696
|
+
// Ensure source is cached
|
|
697
|
+
await this.ensureCached(sourceName);
|
|
698
|
+
|
|
699
|
+
// Get template details
|
|
700
|
+
const template = await this.showTemplate(templatePath);
|
|
701
|
+
|
|
702
|
+
// Get template directory path
|
|
703
|
+
const sourcePath = this.cacheManager.getSourceCachePath(sourceName);
|
|
704
|
+
const templateDir = path.join(sourcePath, templateId);
|
|
705
|
+
|
|
706
|
+
// Validate template
|
|
707
|
+
const validation = await this.validator.validateTemplate(templateDir);
|
|
708
|
+
if (!validation.valid) {
|
|
709
|
+
throw new ValidationError(
|
|
710
|
+
'Template validation failed',
|
|
711
|
+
{
|
|
712
|
+
template: templateId,
|
|
713
|
+
errors: validation.errors
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Determine target directory
|
|
719
|
+
const targetDir = path.join(process.cwd(), '.kiro', 'specs', specName);
|
|
720
|
+
|
|
721
|
+
// Apply template
|
|
722
|
+
const result = await this.applicator.applyTemplate(
|
|
723
|
+
specName,
|
|
724
|
+
templateDir,
|
|
725
|
+
targetDir,
|
|
726
|
+
{ force, variables }
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
return {
|
|
730
|
+
...result,
|
|
731
|
+
template: template.name,
|
|
732
|
+
source: sourceName,
|
|
733
|
+
specPath: targetDir
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Gets cache status
|
|
739
|
+
*
|
|
740
|
+
* @returns {Promise<Object>} Cache status
|
|
741
|
+
*/
|
|
742
|
+
async getCacheStatus() {
|
|
743
|
+
const stats = await this.cacheManager.getStatistics();
|
|
744
|
+
const sources = await this.listSources();
|
|
745
|
+
|
|
746
|
+
return {
|
|
747
|
+
...stats,
|
|
748
|
+
sources: sources.map(s => ({
|
|
749
|
+
name: s.name,
|
|
750
|
+
type: s.type,
|
|
751
|
+
enabled: s.enabled,
|
|
752
|
+
cached: s.cached,
|
|
753
|
+
lastUpdated: s.lastUpdated,
|
|
754
|
+
templateCount: s.templateCount
|
|
755
|
+
}))
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Clears cache
|
|
761
|
+
*
|
|
762
|
+
* @param {string} sourceName - Source name (null = all)
|
|
763
|
+
* @returns {Promise<Object>} Result
|
|
764
|
+
*/
|
|
765
|
+
async clearCache(sourceName = null) {
|
|
766
|
+
if (sourceName) {
|
|
767
|
+
await this.cacheManager.clearCache(sourceName);
|
|
768
|
+
return {
|
|
769
|
+
success: true,
|
|
770
|
+
message: `Cache cleared for ${sourceName}`
|
|
771
|
+
};
|
|
772
|
+
} else {
|
|
773
|
+
await this.cacheManager.clearAllCache();
|
|
774
|
+
return {
|
|
775
|
+
success: true,
|
|
776
|
+
message: 'All cache cleared'
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
module.exports = TemplateManager;
|