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,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CacheManager - Manages local template cache
|
|
3
|
+
*
|
|
4
|
+
* Handles cache directory structure, metadata storage, and cache operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const { FileSystemError } = require('./template-error');
|
|
11
|
+
|
|
12
|
+
class CacheManager {
|
|
13
|
+
constructor(cacheDir = null) {
|
|
14
|
+
// Default cache directory: ~/.kse/templates/
|
|
15
|
+
this.cacheDir = cacheDir || path.join(os.homedir(), '.kse', 'templates');
|
|
16
|
+
this.metadataFile = path.join(this.cacheDir, '.cache-metadata.json');
|
|
17
|
+
this.sourcesFile = path.join(this.cacheDir, '.sources.json');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Gets the cache directory path
|
|
22
|
+
*
|
|
23
|
+
* @returns {string} Cache directory path
|
|
24
|
+
*/
|
|
25
|
+
getCacheDir() {
|
|
26
|
+
return this.cacheDir;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Gets the cache path for a specific source
|
|
31
|
+
*
|
|
32
|
+
* @param {string} sourceName - Source name (e.g., 'official')
|
|
33
|
+
* @returns {string} Source cache path
|
|
34
|
+
*/
|
|
35
|
+
getSourceCachePath(sourceName) {
|
|
36
|
+
return path.join(this.cacheDir, sourceName);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Ensures cache directory exists
|
|
41
|
+
*
|
|
42
|
+
* @returns {Promise<void>}
|
|
43
|
+
*/
|
|
44
|
+
async ensureCacheDir() {
|
|
45
|
+
try {
|
|
46
|
+
await fs.ensureDir(this.cacheDir);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new FileSystemError(
|
|
49
|
+
'Failed to create cache directory',
|
|
50
|
+
{
|
|
51
|
+
path: this.cacheDir,
|
|
52
|
+
error: error.message
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if cache exists for a source
|
|
60
|
+
*
|
|
61
|
+
* @param {string} sourceName - Source name
|
|
62
|
+
* @returns {Promise<boolean>}
|
|
63
|
+
*/
|
|
64
|
+
async cacheExists(sourceName) {
|
|
65
|
+
const sourcePath = this.getSourceCachePath(sourceName);
|
|
66
|
+
return await fs.pathExists(sourcePath);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets cache metadata
|
|
71
|
+
*
|
|
72
|
+
* @returns {Promise<Object>} Cache metadata
|
|
73
|
+
*/
|
|
74
|
+
async getMetadata() {
|
|
75
|
+
if (!await fs.pathExists(this.metadataFile)) {
|
|
76
|
+
return {
|
|
77
|
+
sources: {},
|
|
78
|
+
last_check: null
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const metadata = await fs.readJson(this.metadataFile);
|
|
84
|
+
return metadata;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// If metadata is corrupted, return empty metadata
|
|
87
|
+
return {
|
|
88
|
+
sources: {},
|
|
89
|
+
last_check: null
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Updates cache metadata for a source
|
|
96
|
+
*
|
|
97
|
+
* @param {string} sourceName - Source name
|
|
98
|
+
* @param {Object} sourceMetadata - Source metadata
|
|
99
|
+
* @returns {Promise<void>}
|
|
100
|
+
*/
|
|
101
|
+
async updateMetadata(sourceName, sourceMetadata) {
|
|
102
|
+
await this.ensureCacheDir();
|
|
103
|
+
|
|
104
|
+
const metadata = await this.getMetadata();
|
|
105
|
+
|
|
106
|
+
metadata.sources[sourceName] = {
|
|
107
|
+
...sourceMetadata,
|
|
108
|
+
last_updated: new Date().toISOString()
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
metadata.last_check = new Date().toISOString();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
await fs.writeJson(this.metadataFile, metadata, { spaces: 2 });
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw new FileSystemError(
|
|
117
|
+
'Failed to update cache metadata',
|
|
118
|
+
{
|
|
119
|
+
path: this.metadataFile,
|
|
120
|
+
error: error.message
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Gets metadata for a specific source
|
|
128
|
+
*
|
|
129
|
+
* @param {string} sourceName - Source name
|
|
130
|
+
* @returns {Promise<Object|null>} Source metadata or null
|
|
131
|
+
*/
|
|
132
|
+
async getSourceMetadata(sourceName) {
|
|
133
|
+
const metadata = await this.getMetadata();
|
|
134
|
+
return metadata.sources[sourceName] || null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Calculates cache size for a source
|
|
139
|
+
*
|
|
140
|
+
* @param {string} sourceName - Source name
|
|
141
|
+
* @returns {Promise<number>} Size in bytes
|
|
142
|
+
*/
|
|
143
|
+
async getCacheSize(sourceName) {
|
|
144
|
+
const sourcePath = this.getSourceCachePath(sourceName);
|
|
145
|
+
|
|
146
|
+
if (!await fs.pathExists(sourcePath)) {
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return await this._calculateDirectorySize(sourcePath);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Calculates total cache size
|
|
155
|
+
*
|
|
156
|
+
* @returns {Promise<number>} Total size in bytes
|
|
157
|
+
*/
|
|
158
|
+
async getTotalCacheSize() {
|
|
159
|
+
if (!await fs.pathExists(this.cacheDir)) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return await this._calculateDirectorySize(this.cacheDir);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Helper: Recursively calculates directory size
|
|
168
|
+
*
|
|
169
|
+
* @param {string} dirPath - Directory path
|
|
170
|
+
* @returns {Promise<number>} Size in bytes
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
async _calculateDirectorySize(dirPath) {
|
|
174
|
+
let totalSize = 0;
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const items = await fs.readdir(dirPath);
|
|
178
|
+
|
|
179
|
+
for (const item of items) {
|
|
180
|
+
const itemPath = path.join(dirPath, item);
|
|
181
|
+
const stats = await fs.stat(itemPath);
|
|
182
|
+
|
|
183
|
+
if (stats.isDirectory()) {
|
|
184
|
+
totalSize += await this._calculateDirectorySize(itemPath);
|
|
185
|
+
} else {
|
|
186
|
+
totalSize += stats.size;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
// Ignore errors for individual files
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return totalSize;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Gets cache information
|
|
198
|
+
*
|
|
199
|
+
* @param {string} sourceName - Source name (optional)
|
|
200
|
+
* @returns {Promise<Object>} Cache info
|
|
201
|
+
*/
|
|
202
|
+
async getCacheInfo(sourceName = null) {
|
|
203
|
+
if (sourceName) {
|
|
204
|
+
// Get info for specific source
|
|
205
|
+
const exists = await this.cacheExists(sourceName);
|
|
206
|
+
const metadata = await this.getSourceMetadata(sourceName);
|
|
207
|
+
const size = await this.getCacheSize(sourceName);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
source: sourceName,
|
|
211
|
+
exists,
|
|
212
|
+
size,
|
|
213
|
+
metadata
|
|
214
|
+
};
|
|
215
|
+
} else {
|
|
216
|
+
// Get info for all sources
|
|
217
|
+
const metadata = await this.getMetadata();
|
|
218
|
+
const totalSize = await getTotalCacheSize();
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
cacheDir: this.cacheDir,
|
|
222
|
+
totalSize,
|
|
223
|
+
sources: metadata.sources,
|
|
224
|
+
lastCheck: metadata.last_check
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Formats size in human-readable format
|
|
231
|
+
*
|
|
232
|
+
* @param {number} bytes - Size in bytes
|
|
233
|
+
* @returns {string} Formatted size
|
|
234
|
+
*/
|
|
235
|
+
formatSize(bytes) {
|
|
236
|
+
if (bytes === 0) return '0 B';
|
|
237
|
+
|
|
238
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
239
|
+
const k = 1024;
|
|
240
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
241
|
+
|
|
242
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Checks if cache is stale
|
|
247
|
+
*
|
|
248
|
+
* @param {string} sourceName - Source name
|
|
249
|
+
* @param {number} maxAge - Maximum age in milliseconds (default: 7 days)
|
|
250
|
+
* @returns {Promise<boolean>} True if cache is stale
|
|
251
|
+
*/
|
|
252
|
+
async isStale(sourceName, maxAge = 7 * 24 * 60 * 60 * 1000) {
|
|
253
|
+
const metadata = await this.getSourceMetadata(sourceName);
|
|
254
|
+
|
|
255
|
+
if (!metadata || !metadata.last_updated) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const lastUpdated = new Date(metadata.last_updated);
|
|
260
|
+
const now = new Date();
|
|
261
|
+
const age = now - lastUpdated;
|
|
262
|
+
|
|
263
|
+
return age > maxAge;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Validates cache integrity
|
|
268
|
+
*
|
|
269
|
+
* @param {string} sourceName - Source name
|
|
270
|
+
* @returns {Promise<Object>} Validation result
|
|
271
|
+
*/
|
|
272
|
+
async validateCache(sourceName) {
|
|
273
|
+
const result = {
|
|
274
|
+
valid: true,
|
|
275
|
+
errors: [],
|
|
276
|
+
warnings: []
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Check if cache exists
|
|
280
|
+
if (!await this.cacheExists(sourceName)) {
|
|
281
|
+
result.valid = false;
|
|
282
|
+
result.errors.push('Cache does not exist');
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const sourcePath = this.getSourceCachePath(sourceName);
|
|
287
|
+
|
|
288
|
+
// Check if template-registry.json exists
|
|
289
|
+
const registryPath = path.join(sourcePath, 'template-registry.json');
|
|
290
|
+
if (!await fs.pathExists(registryPath)) {
|
|
291
|
+
result.valid = false;
|
|
292
|
+
result.errors.push('Missing template-registry.json');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Check if .git directory exists (for Git-based sources)
|
|
296
|
+
const gitPath = path.join(sourcePath, '.git');
|
|
297
|
+
if (!await fs.pathExists(gitPath)) {
|
|
298
|
+
result.warnings.push('Not a Git repository (may be a manual cache)');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Check metadata consistency
|
|
302
|
+
const metadata = await this.getSourceMetadata(sourceName);
|
|
303
|
+
if (!metadata) {
|
|
304
|
+
result.warnings.push('Missing cache metadata');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Clears cache for a specific source
|
|
312
|
+
*
|
|
313
|
+
* @param {string} sourceName - Source name
|
|
314
|
+
* @returns {Promise<void>}
|
|
315
|
+
*/
|
|
316
|
+
async clearCache(sourceName) {
|
|
317
|
+
const sourcePath = this.getSourceCachePath(sourceName);
|
|
318
|
+
|
|
319
|
+
if (!await fs.pathExists(sourcePath)) {
|
|
320
|
+
return; // Already cleared
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
await fs.remove(sourcePath);
|
|
325
|
+
} catch (error) {
|
|
326
|
+
throw new FileSystemError(
|
|
327
|
+
'Failed to clear cache',
|
|
328
|
+
{
|
|
329
|
+
source: sourceName,
|
|
330
|
+
path: sourcePath,
|
|
331
|
+
error: error.message
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Remove from metadata
|
|
337
|
+
const metadata = await this.getMetadata();
|
|
338
|
+
if (metadata.sources[sourceName]) {
|
|
339
|
+
delete metadata.sources[sourceName];
|
|
340
|
+
await fs.writeJson(this.metadataFile, metadata, { spaces: 2 });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Clears all cache
|
|
346
|
+
*
|
|
347
|
+
* @returns {Promise<void>}
|
|
348
|
+
*/
|
|
349
|
+
async clearAllCache() {
|
|
350
|
+
if (!await fs.pathExists(this.cacheDir)) {
|
|
351
|
+
return; // Already cleared
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
await fs.remove(this.cacheDir);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
throw new FileSystemError(
|
|
358
|
+
'Failed to clear all cache',
|
|
359
|
+
{
|
|
360
|
+
path: this.cacheDir,
|
|
361
|
+
error: error.message
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Lists all cached sources
|
|
369
|
+
*
|
|
370
|
+
* @returns {Promise<string[]>} Array of source names
|
|
371
|
+
*/
|
|
372
|
+
async listCachedSources() {
|
|
373
|
+
if (!await fs.pathExists(this.cacheDir)) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const items = await fs.readdir(this.cacheDir);
|
|
379
|
+
const sources = [];
|
|
380
|
+
|
|
381
|
+
for (const item of items) {
|
|
382
|
+
// Skip metadata files
|
|
383
|
+
if (item.startsWith('.')) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const itemPath = path.join(this.cacheDir, item);
|
|
388
|
+
const stats = await fs.stat(itemPath);
|
|
389
|
+
|
|
390
|
+
if (stats.isDirectory()) {
|
|
391
|
+
sources.push(item);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return sources;
|
|
396
|
+
} catch (error) {
|
|
397
|
+
throw new FileSystemError(
|
|
398
|
+
'Failed to list cached sources',
|
|
399
|
+
{
|
|
400
|
+
path: this.cacheDir,
|
|
401
|
+
error: error.message
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Gets cache statistics
|
|
409
|
+
*
|
|
410
|
+
* @returns {Promise<Object>} Cache statistics
|
|
411
|
+
*/
|
|
412
|
+
async getStatistics() {
|
|
413
|
+
const sources = await this.listCachedSources();
|
|
414
|
+
const metadata = await this.getMetadata();
|
|
415
|
+
const totalSize = await this.getTotalCacheSize();
|
|
416
|
+
|
|
417
|
+
const stats = {
|
|
418
|
+
totalSources: sources.length,
|
|
419
|
+
totalSize,
|
|
420
|
+
totalSizeFormatted: this.formatSize(totalSize),
|
|
421
|
+
sources: {}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
for (const source of sources) {
|
|
425
|
+
const size = await this.getCacheSize(source);
|
|
426
|
+
const sourceMeta = metadata.sources[source];
|
|
427
|
+
|
|
428
|
+
stats.sources[source] = {
|
|
429
|
+
size,
|
|
430
|
+
sizeFormatted: this.formatSize(size),
|
|
431
|
+
lastUpdated: sourceMeta ? sourceMeta.last_updated : null,
|
|
432
|
+
templateCount: sourceMeta ? sourceMeta.template_count : null
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return stats;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
module.exports = CacheManager;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContentGeneralizer - Replaces project-specific content with template variables
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
class ContentGeneralizer {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.patterns = this.definePatterns();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Defines generalization patterns
|
|
12
|
+
* @returns {Object} Pattern definitions
|
|
13
|
+
*/
|
|
14
|
+
definePatterns() {
|
|
15
|
+
return {
|
|
16
|
+
// Spec name patterns (will be customized per Spec)
|
|
17
|
+
SPEC_NAME: {
|
|
18
|
+
variable: '{{SPEC_NAME}}',
|
|
19
|
+
priority: 1
|
|
20
|
+
},
|
|
21
|
+
SPEC_NAME_TITLE: {
|
|
22
|
+
variable: '{{SPEC_NAME_TITLE}}',
|
|
23
|
+
priority: 2
|
|
24
|
+
},
|
|
25
|
+
// Date patterns
|
|
26
|
+
DATE: {
|
|
27
|
+
pattern: /\b\d{4}-\d{2}-\d{2}\b/g,
|
|
28
|
+
variable: '{{DATE}}',
|
|
29
|
+
priority: 3
|
|
30
|
+
},
|
|
31
|
+
// Version patterns (in context)
|
|
32
|
+
VERSION: {
|
|
33
|
+
pattern: /\bv?\d+\.\d+\.\d+\b/g,
|
|
34
|
+
variable: '{{VERSION}}',
|
|
35
|
+
priority: 4,
|
|
36
|
+
contextKeywords: ['version', 'release', 'v']
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generalizes Spec content
|
|
43
|
+
* @param {Object} fileContents - Original file contents
|
|
44
|
+
* @param {Object} specMetadata - Spec metadata
|
|
45
|
+
* @returns {Object} Generalized content and flags
|
|
46
|
+
*/
|
|
47
|
+
generalize(fileContents, specMetadata) {
|
|
48
|
+
const replacements = this.buildReplacementMap(specMetadata);
|
|
49
|
+
const result = {
|
|
50
|
+
files: {},
|
|
51
|
+
summary: {
|
|
52
|
+
totalReplacements: 0,
|
|
53
|
+
totalFlags: 0
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
for (const [filename, content] of Object.entries(fileContents)) {
|
|
58
|
+
if (!content) {
|
|
59
|
+
result.files[filename] = {
|
|
60
|
+
original: content,
|
|
61
|
+
generalized: content,
|
|
62
|
+
replacements: [],
|
|
63
|
+
flags: []
|
|
64
|
+
};
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { generalized, replacementList } = this.applyPatterns(content, replacements);
|
|
69
|
+
const flags = this.detectAmbiguousContent(generalized);
|
|
70
|
+
|
|
71
|
+
result.files[filename] = {
|
|
72
|
+
original: content,
|
|
73
|
+
generalized,
|
|
74
|
+
replacements: replacementList,
|
|
75
|
+
flags
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
result.summary.totalReplacements += replacementList.reduce((sum, r) => sum + r.count, 0);
|
|
79
|
+
result.summary.totalFlags += flags.length;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Builds replacement map from Spec metadata
|
|
87
|
+
* @param {Object} specMetadata - Spec metadata
|
|
88
|
+
* @returns {Array} Replacement patterns
|
|
89
|
+
*/
|
|
90
|
+
buildReplacementMap(specMetadata) {
|
|
91
|
+
const replacements = [];
|
|
92
|
+
|
|
93
|
+
// Spec name replacements (most specific first)
|
|
94
|
+
if (specMetadata.fullDirName) {
|
|
95
|
+
replacements.push({
|
|
96
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.fullDirName), 'g'),
|
|
97
|
+
variable: '{{SPEC_NAME}}',
|
|
98
|
+
priority: 1,
|
|
99
|
+
description: 'Full directory name'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (specMetadata.specName) {
|
|
104
|
+
replacements.push({
|
|
105
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.specName), 'g'),
|
|
106
|
+
variable: '{{SPEC_NAME}}',
|
|
107
|
+
priority: 1,
|
|
108
|
+
description: 'Kebab-case name'
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (specMetadata.specNameTitle) {
|
|
113
|
+
replacements.push({
|
|
114
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.specNameTitle), 'g'),
|
|
115
|
+
variable: '{{SPEC_NAME_TITLE}}',
|
|
116
|
+
priority: 2,
|
|
117
|
+
description: 'Title case name'
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Date replacements
|
|
122
|
+
if (specMetadata.dates) {
|
|
123
|
+
if (specMetadata.dates.created) {
|
|
124
|
+
replacements.push({
|
|
125
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.dates.created), 'g'),
|
|
126
|
+
variable: '{{DATE}}',
|
|
127
|
+
priority: 3,
|
|
128
|
+
description: 'Creation date'
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (specMetadata.dates.modified && specMetadata.dates.modified !== specMetadata.dates.created) {
|
|
132
|
+
replacements.push({
|
|
133
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.dates.modified), 'g'),
|
|
134
|
+
variable: '{{DATE}}',
|
|
135
|
+
priority: 3,
|
|
136
|
+
description: 'Modified date'
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Author replacement
|
|
142
|
+
if (specMetadata.author && specMetadata.author !== 'Unknown') {
|
|
143
|
+
replacements.push({
|
|
144
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.author), 'g'),
|
|
145
|
+
variable: '{{AUTHOR}}',
|
|
146
|
+
priority: 4,
|
|
147
|
+
description: 'Author name'
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Path replacements
|
|
152
|
+
if (specMetadata.specPath) {
|
|
153
|
+
replacements.push({
|
|
154
|
+
pattern: new RegExp(this.escapeRegex(specMetadata.specPath), 'g'),
|
|
155
|
+
variable: '.kiro/specs/{{SPEC_NAME}}',
|
|
156
|
+
priority: 5,
|
|
157
|
+
description: 'Spec path'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Sort by priority
|
|
162
|
+
return replacements.sort((a, b) => a.priority - b.priority);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Applies generalization patterns to text
|
|
167
|
+
* @param {string} content - Original content
|
|
168
|
+
* @param {Array} replacements - Replacement patterns
|
|
169
|
+
* @returns {Object} Generalized content and matches
|
|
170
|
+
*/
|
|
171
|
+
applyPatterns(content, replacements) {
|
|
172
|
+
let generalized = content;
|
|
173
|
+
const replacementList = [];
|
|
174
|
+
|
|
175
|
+
for (const replacement of replacements) {
|
|
176
|
+
const matches = content.match(replacement.pattern);
|
|
177
|
+
if (matches && matches.length > 0) {
|
|
178
|
+
generalized = generalized.replace(replacement.pattern, replacement.variable);
|
|
179
|
+
replacementList.push({
|
|
180
|
+
pattern: replacement.description || replacement.pattern.toString(),
|
|
181
|
+
variable: replacement.variable,
|
|
182
|
+
count: matches.length
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return { generalized, replacementList };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Detects ambiguous content that needs review
|
|
192
|
+
* @param {string} content - Content to analyze
|
|
193
|
+
* @returns {Array} Flagged items
|
|
194
|
+
*/
|
|
195
|
+
detectAmbiguousContent(content) {
|
|
196
|
+
const flags = [];
|
|
197
|
+
const lines = content.split('\n');
|
|
198
|
+
|
|
199
|
+
// Patterns for ambiguous content
|
|
200
|
+
const ambiguousPatterns = [
|
|
201
|
+
{
|
|
202
|
+
pattern: /\b[A-Z][a-z]+\s+(?:Corp|Inc|LLC|Ltd|Company)\b/g,
|
|
203
|
+
severity: 'warning',
|
|
204
|
+
message: 'Possible company name detected'
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
pattern: /https?:\/\/(?!github\.com|npmjs\.com)[^\s]+/g,
|
|
208
|
+
severity: 'info',
|
|
209
|
+
message: 'Specific URL detected (not GitHub/npm)'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
pattern: /\b(?:API_KEY|SECRET|PASSWORD|TOKEN)\s*[:=]\s*['"][^'"]+['"]/g,
|
|
213
|
+
severity: 'warning',
|
|
214
|
+
message: 'Hardcoded credential detected'
|
|
215
|
+
}
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
lines.forEach((line, index) => {
|
|
219
|
+
ambiguousPatterns.forEach(({ pattern, severity, message }) => {
|
|
220
|
+
const matches = line.match(pattern);
|
|
221
|
+
if (matches) {
|
|
222
|
+
matches.forEach(match => {
|
|
223
|
+
flags.push({
|
|
224
|
+
line: index + 1,
|
|
225
|
+
content: match,
|
|
226
|
+
severity,
|
|
227
|
+
message
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return flags;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Escapes special regex characters
|
|
239
|
+
* @param {string} str - String to escape
|
|
240
|
+
* @returns {string} Escaped string
|
|
241
|
+
*/
|
|
242
|
+
escapeRegex(str) {
|
|
243
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = ContentGeneralizer;
|