@planu/cli 0.24.0 → 0.26.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/dist/config/ai-model-pricing.json +81 -0
- package/dist/config/framework-registry/csharp-aspnet.json +67 -0
- package/dist/config/framework-registry/dart-flutter.json +61 -0
- package/dist/config/framework-registry/go-gin.json +62 -0
- package/dist/config/framework-registry/index.json +164 -0
- package/dist/config/framework-registry/java-springboot.json +67 -0
- package/dist/config/framework-registry/php-laravel.json +67 -0
- package/dist/config/framework-registry/python-django.json +72 -0
- package/dist/config/framework-registry/python-fastapi.json +72 -0
- package/dist/config/framework-registry/ruby-rails.json +67 -0
- package/dist/config/framework-registry/rust-actix.json +61 -0
- package/dist/config/framework-registry/swift-swiftui.json +67 -0
- package/dist/config/framework-registry/typescript-angular.json +67 -0
- package/dist/config/framework-registry/typescript-astro.json +61 -0
- package/dist/config/framework-registry/typescript-express.json +67 -0
- package/dist/config/framework-registry/typescript-nestjs.json +62 -0
- package/dist/config/framework-registry/typescript-nextjs.json +82 -0
- package/dist/config/framework-registry/typescript-nuxt.json +67 -0
- package/dist/config/framework-registry/typescript-react.json +77 -0
- package/dist/config/framework-registry/typescript-remix.json +61 -0
- package/dist/config/framework-registry/typescript-sveltekit.json +67 -0
- package/dist/config/framework-registry/typescript-vue.json +72 -0
- package/dist/config/license-plans.json +23 -3
- package/dist/engine/agile-config-builder.d.ts.map +1 -1
- package/dist/engine/agile-config-builder.js +3 -0
- package/dist/engine/agile-config-builder.js.map +1 -1
- package/dist/engine/ai-cost-estimator/core.d.ts +8 -0
- package/dist/engine/ai-cost-estimator/core.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/core.js +257 -0
- package/dist/engine/ai-cost-estimator/core.js.map +1 -0
- package/dist/engine/ai-cost-estimator/index.d.ts +4 -0
- package/dist/engine/ai-cost-estimator/index.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/index.js +5 -0
- package/dist/engine/ai-cost-estimator/index.js.map +1 -0
- package/dist/engine/ai-cost-estimator/model-pricing.d.ts +31 -0
- package/dist/engine/ai-cost-estimator/model-pricing.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/model-pricing.js +96 -0
- package/dist/engine/ai-cost-estimator/model-pricing.js.map +1 -0
- package/dist/engine/ai-cost-estimator/token-estimator.d.ts +37 -0
- package/dist/engine/ai-cost-estimator/token-estimator.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/token-estimator.js +194 -0
- package/dist/engine/ai-cost-estimator/token-estimator.js.map +1 -0
- package/dist/engine/ci-generator/context-builders.d.ts +4 -0
- package/dist/engine/ci-generator/context-builders.d.ts.map +1 -0
- package/dist/engine/ci-generator/context-builders.js +129 -0
- package/dist/engine/ci-generator/context-builders.js.map +1 -0
- package/dist/engine/ci-generator/index.d.ts +7 -0
- package/dist/engine/ci-generator/index.d.ts.map +1 -0
- package/dist/engine/ci-generator/index.js +44 -0
- package/dist/engine/ci-generator/index.js.map +1 -0
- package/dist/engine/ci-generator/stack-detector.d.ts +21 -0
- package/dist/engine/ci-generator/stack-detector.d.ts.map +1 -0
- package/dist/engine/ci-generator/stack-detector.js +203 -0
- package/dist/engine/ci-generator/stack-detector.js.map +1 -0
- package/dist/engine/ci-generator/yaml-builder.d.ts +8 -0
- package/dist/engine/ci-generator/yaml-builder.d.ts.map +1 -0
- package/dist/engine/ci-generator/yaml-builder.js +285 -0
- package/dist/engine/ci-generator/yaml-builder.js.map +1 -0
- package/dist/engine/dashboard/data-loader.d.ts +21 -0
- package/dist/engine/dashboard/data-loader.d.ts.map +1 -0
- package/dist/engine/dashboard/data-loader.js +196 -0
- package/dist/engine/dashboard/data-loader.js.map +1 -0
- package/dist/engine/dashboard/index.d.ts +5 -0
- package/dist/engine/dashboard/index.d.ts.map +1 -0
- package/dist/engine/dashboard/index.js +6 -0
- package/dist/engine/dashboard/index.js.map +1 -0
- package/dist/engine/dashboard/routes.d.ts +4 -0
- package/dist/engine/dashboard/routes.d.ts.map +1 -0
- package/dist/engine/dashboard/routes.js +120 -0
- package/dist/engine/dashboard/routes.js.map +1 -0
- package/dist/engine/dashboard/server.d.ts +14 -0
- package/dist/engine/dashboard/server.d.ts.map +1 -0
- package/dist/engine/dashboard/server.js +88 -0
- package/dist/engine/dashboard/server.js.map +1 -0
- package/dist/engine/dashboard/templates-layout.d.ts +11 -0
- package/dist/engine/dashboard/templates-layout.d.ts.map +1 -0
- package/dist/engine/dashboard/templates-layout.js +305 -0
- package/dist/engine/dashboard/templates-layout.js.map +1 -0
- package/dist/engine/dashboard/templates-project.d.ts +10 -0
- package/dist/engine/dashboard/templates-project.d.ts.map +1 -0
- package/dist/engine/dashboard/templates-project.js +163 -0
- package/dist/engine/dashboard/templates-project.js.map +1 -0
- package/dist/engine/dashboard/templates.d.ts +3 -0
- package/dist/engine/dashboard/templates.d.ts.map +1 -0
- package/dist/engine/dashboard/templates.js +4 -0
- package/dist/engine/dashboard/templates.js.map +1 -0
- package/dist/engine/focus-tracker.d.ts.map +1 -1
- package/dist/engine/focus-tracker.js +1 -0
- package/dist/engine/focus-tracker.js.map +1 -1
- package/dist/engine/hooks/core.d.ts +26 -0
- package/dist/engine/hooks/core.d.ts.map +1 -0
- package/dist/engine/hooks/core.js +164 -0
- package/dist/engine/hooks/core.js.map +1 -0
- package/dist/engine/hooks/git-hook-generator.d.ts +14 -0
- package/dist/engine/hooks/git-hook-generator.d.ts.map +1 -0
- package/dist/engine/hooks/git-hook-generator.js +119 -0
- package/dist/engine/hooks/git-hook-generator.js.map +1 -0
- package/dist/engine/hooks/index.d.ts +5 -0
- package/dist/engine/hooks/index.d.ts.map +1 -0
- package/dist/engine/hooks/index.js +6 -0
- package/dist/engine/hooks/index.js.map +1 -0
- package/dist/engine/hooks/templates.d.ts +14 -0
- package/dist/engine/hooks/templates.d.ts.map +1 -0
- package/dist/engine/hooks/templates.js +138 -0
- package/dist/engine/hooks/templates.js.map +1 -0
- package/dist/engine/hooks/triggers.d.ts +27 -0
- package/dist/engine/hooks/triggers.d.ts.map +1 -0
- package/dist/engine/hooks/triggers.js +104 -0
- package/dist/engine/hooks/triggers.js.map +1 -0
- package/dist/engine/registry/core.d.ts +32 -0
- package/dist/engine/registry/core.d.ts.map +1 -0
- package/dist/engine/registry/core.js +85 -0
- package/dist/engine/registry/core.js.map +1 -0
- package/dist/engine/registry/index.d.ts +6 -0
- package/dist/engine/registry/index.d.ts.map +1 -0
- package/dist/engine/registry/index.js +8 -0
- package/dist/engine/registry/index.js.map +1 -0
- package/dist/engine/registry/loader.d.ts +18 -0
- package/dist/engine/registry/loader.d.ts.map +1 -0
- package/dist/engine/registry/loader.js +81 -0
- package/dist/engine/registry/loader.js.map +1 -0
- package/dist/engine/registry/matcher.d.ts +19 -0
- package/dist/engine/registry/matcher.d.ts.map +1 -0
- package/dist/engine/registry/matcher.js +150 -0
- package/dist/engine/registry/matcher.js.map +1 -0
- package/dist/engine/registry/reporter.d.ts +10 -0
- package/dist/engine/registry/reporter.d.ts.map +1 -0
- package/dist/engine/registry/reporter.js +132 -0
- package/dist/engine/registry/reporter.js.map +1 -0
- package/dist/engine/registry/validator.d.ts +17 -0
- package/dist/engine/registry/validator.d.ts.map +1 -0
- package/dist/engine/registry/validator.js +103 -0
- package/dist/engine/registry/validator.js.map +1 -0
- package/dist/engine/spec-changelog/core.d.ts +16 -0
- package/dist/engine/spec-changelog/core.d.ts.map +1 -0
- package/dist/engine/spec-changelog/core.js +175 -0
- package/dist/engine/spec-changelog/core.js.map +1 -0
- package/dist/engine/spec-changelog/diff.d.ts +18 -0
- package/dist/engine/spec-changelog/diff.d.ts.map +1 -0
- package/dist/engine/spec-changelog/diff.js +116 -0
- package/dist/engine/spec-changelog/diff.js.map +1 -0
- package/dist/engine/spec-changelog/index.d.ts +3 -0
- package/dist/engine/spec-changelog/index.d.ts.map +1 -0
- package/dist/engine/spec-changelog/index.js +4 -0
- package/dist/engine/spec-changelog/index.js.map +1 -0
- package/dist/engine/spec-coverage/core.d.ts +14 -0
- package/dist/engine/spec-coverage/core.d.ts.map +1 -0
- package/dist/engine/spec-coverage/core.js +132 -0
- package/dist/engine/spec-coverage/core.js.map +1 -0
- package/dist/engine/spec-coverage/criteria-mapper.d.ts +18 -0
- package/dist/engine/spec-coverage/criteria-mapper.d.ts.map +1 -0
- package/dist/engine/spec-coverage/criteria-mapper.js +306 -0
- package/dist/engine/spec-coverage/criteria-mapper.js.map +1 -0
- package/dist/engine/spec-coverage/index.d.ts +4 -0
- package/dist/engine/spec-coverage/index.d.ts.map +1 -0
- package/dist/engine/spec-coverage/index.js +5 -0
- package/dist/engine/spec-coverage/index.js.map +1 -0
- package/dist/engine/spec-coverage/test-finder.d.ts +17 -0
- package/dist/engine/spec-coverage/test-finder.d.ts.map +1 -0
- package/dist/engine/spec-coverage/test-finder.js +250 -0
- package/dist/engine/spec-coverage/test-finder.js.map +1 -0
- package/dist/engine/spec-templates/catalog-extra.d.ts +4 -0
- package/dist/engine/spec-templates/catalog-extra.d.ts.map +1 -0
- package/dist/engine/spec-templates/catalog-extra.js +367 -0
- package/dist/engine/spec-templates/catalog-extra.js.map +1 -0
- package/dist/engine/spec-templates/catalog.d.ts +4 -0
- package/dist/engine/spec-templates/catalog.d.ts.map +1 -0
- package/dist/engine/spec-templates/catalog.js +386 -0
- package/dist/engine/spec-templates/catalog.js.map +1 -0
- package/dist/engine/spec-templates/index.d.ts +4 -0
- package/dist/engine/spec-templates/index.d.ts.map +1 -0
- package/dist/engine/spec-templates/index.js +5 -0
- package/dist/engine/spec-templates/index.js.map +1 -0
- package/dist/engine/spec-templates/query.d.ts +14 -0
- package/dist/engine/spec-templates/query.d.ts.map +1 -0
- package/dist/engine/spec-templates/query.js +56 -0
- package/dist/engine/spec-templates/query.js.map +1 -0
- package/dist/engine/spec-templates/renderer.d.ts +19 -0
- package/dist/engine/spec-templates/renderer.d.ts.map +1 -0
- package/dist/engine/spec-templates/renderer.js +45 -0
- package/dist/engine/spec-templates/renderer.js.map +1 -0
- package/dist/engine/usage-tracker/trial.d.ts +4 -4
- package/dist/engine/usage-tracker/trial.d.ts.map +1 -1
- package/dist/engine/usage-tracker/trial.js +4 -4
- package/dist/engine/usage-tracker/trial.js.map +1 -1
- package/dist/engine/webhook/event-handlers.d.ts +25 -0
- package/dist/engine/webhook/event-handlers.d.ts.map +1 -0
- package/dist/engine/webhook/event-handlers.js +89 -0
- package/dist/engine/webhook/event-handlers.js.map +1 -0
- package/dist/engine/webhook/index.d.ts +4 -0
- package/dist/engine/webhook/index.d.ts.map +1 -0
- package/dist/engine/webhook/index.js +5 -0
- package/dist/engine/webhook/index.js.map +1 -0
- package/dist/engine/webhook/server.d.ts +18 -0
- package/dist/engine/webhook/server.d.ts.map +1 -0
- package/dist/engine/webhook/server.js +187 -0
- package/dist/engine/webhook/server.js.map +1 -0
- package/dist/engine/webhook/signature.d.ts +16 -0
- package/dist/engine/webhook/signature.d.ts.map +1 -0
- package/dist/engine/webhook/signature.js +49 -0
- package/dist/engine/webhook/signature.js.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/hooks-store.d.ts +34 -0
- package/dist/storage/hooks-store.d.ts.map +1 -0
- package/dist/storage/hooks-store.js +128 -0
- package/dist/storage/hooks-store.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/check-spec-accuracy.d.ts +7 -0
- package/dist/tools/check-spec-accuracy.d.ts.map +1 -0
- package/dist/tools/check-spec-accuracy.js +28 -0
- package/dist/tools/check-spec-accuracy.js.map +1 -0
- package/dist/tools/ci-generator.d.ts +7 -0
- package/dist/tools/ci-generator.d.ts.map +1 -0
- package/dist/tools/ci-generator.js +49 -0
- package/dist/tools/ci-generator.js.map +1 -0
- package/dist/tools/create-spec.d.ts.map +1 -1
- package/dist/tools/create-spec.js +4 -0
- package/dist/tools/create-spec.js.map +1 -1
- package/dist/tools/dashboard.d.ts +6 -0
- package/dist/tools/dashboard.d.ts.map +1 -0
- package/dist/tools/dashboard.js +48 -0
- package/dist/tools/dashboard.js.map +1 -0
- package/dist/tools/detect-drift.d.ts.map +1 -1
- package/dist/tools/detect-drift.js +6 -0
- package/dist/tools/detect-drift.js.map +1 -1
- package/dist/tools/estimate-ai-cost.d.ts +8 -0
- package/dist/tools/estimate-ai-cost.d.ts.map +1 -0
- package/dist/tools/estimate-ai-cost.js +29 -0
- package/dist/tools/estimate-ai-cost.js.map +1 -0
- package/dist/tools/list-specs.d.ts.map +1 -1
- package/dist/tools/list-specs.js +15 -0
- package/dist/tools/list-specs.js.map +1 -1
- package/dist/tools/manage-hooks.d.ts +5 -0
- package/dist/tools/manage-hooks.d.ts.map +1 -0
- package/dist/tools/manage-hooks.js +247 -0
- package/dist/tools/manage-hooks.js.map +1 -0
- package/dist/tools/reconcile-spec.d.ts.map +1 -1
- package/dist/tools/reconcile-spec.js +24 -0
- package/dist/tools/reconcile-spec.js.map +1 -1
- package/dist/tools/register-ai-cost-tools.d.ts +3 -0
- package/dist/tools/register-ai-cost-tools.d.ts.map +1 -0
- package/dist/tools/register-ai-cost-tools.js +25 -0
- package/dist/tools/register-ai-cost-tools.js.map +1 -0
- package/dist/tools/register-changelog-tools.d.ts +3 -0
- package/dist/tools/register-changelog-tools.d.ts.map +1 -0
- package/dist/tools/register-changelog-tools.js +23 -0
- package/dist/tools/register-changelog-tools.js.map +1 -0
- package/dist/tools/register-ci-tools.d.ts +3 -0
- package/dist/tools/register-ci-tools.d.ts.map +1 -0
- package/dist/tools/register-ci-tools.js +50 -0
- package/dist/tools/register-ci-tools.js.map +1 -0
- package/dist/tools/register-coverage-tools.d.ts +7 -0
- package/dist/tools/register-coverage-tools.d.ts.map +1 -0
- package/dist/tools/register-coverage-tools.js +27 -0
- package/dist/tools/register-coverage-tools.js.map +1 -0
- package/dist/tools/register-dashboard-tools.d.ts +3 -0
- package/dist/tools/register-dashboard-tools.d.ts.map +1 -0
- package/dist/tools/register-dashboard-tools.js +29 -0
- package/dist/tools/register-dashboard-tools.js.map +1 -0
- package/dist/tools/register-hooks-tools.d.ts +3 -0
- package/dist/tools/register-hooks-tools.d.ts.map +1 -0
- package/dist/tools/register-hooks-tools.js +107 -0
- package/dist/tools/register-hooks-tools.js.map +1 -0
- package/dist/tools/register-registry-tools.d.ts +3 -0
- package/dist/tools/register-registry-tools.d.ts.map +1 -0
- package/dist/tools/register-registry-tools.js +43 -0
- package/dist/tools/register-registry-tools.js.map +1 -0
- package/dist/tools/register-template-tools.d.ts +8 -0
- package/dist/tools/register-template-tools.d.ts.map +1 -0
- package/dist/tools/register-template-tools.js +71 -0
- package/dist/tools/register-template-tools.js.map +1 -0
- package/dist/tools/register-webhook-tools.d.ts +3 -0
- package/dist/tools/register-webhook-tools.d.ts.map +1 -0
- package/dist/tools/register-webhook-tools.js +49 -0
- package/dist/tools/register-webhook-tools.js.map +1 -0
- package/dist/tools/schemas/index.d.ts +1 -0
- package/dist/tools/schemas/index.d.ts.map +1 -1
- package/dist/tools/schemas/index.js +1 -0
- package/dist/tools/schemas/index.js.map +1 -1
- package/dist/tools/schemas/registry.d.ts +41 -0
- package/dist/tools/schemas/registry.d.ts.map +1 -0
- package/dist/tools/schemas/registry.js +62 -0
- package/dist/tools/schemas/registry.js.map +1 -0
- package/dist/tools/spec-coverage.d.ts +3 -0
- package/dist/tools/spec-coverage.d.ts.map +1 -0
- package/dist/tools/spec-coverage.js +176 -0
- package/dist/tools/spec-coverage.js.map +1 -0
- package/dist/tools/spec-history.d.ts +3 -0
- package/dist/tools/spec-history.d.ts.map +1 -0
- package/dist/tools/spec-history.js +67 -0
- package/dist/tools/spec-history.js.map +1 -0
- package/dist/tools/spec-templates.d.ts +10 -0
- package/dist/tools/spec-templates.d.ts.map +1 -0
- package/dist/tools/spec-templates.js +155 -0
- package/dist/tools/spec-templates.js.map +1 -0
- package/dist/tools/update-registry.d.ts +8 -0
- package/dist/tools/update-registry.d.ts.map +1 -0
- package/dist/tools/update-registry.js +53 -0
- package/dist/tools/update-registry.js.map +1 -0
- package/dist/tools/update-status.d.ts.map +1 -1
- package/dist/tools/update-status.js +24 -0
- package/dist/tools/update-status.js.map +1 -1
- package/dist/tools/webhook.d.ts +6 -0
- package/dist/tools/webhook.d.ts.map +1 -0
- package/dist/tools/webhook.js +150 -0
- package/dist/tools/webhook.js.map +1 -0
- package/dist/types/ai-cost.d.ts +98 -0
- package/dist/types/ai-cost.d.ts.map +1 -0
- package/dist/types/ai-cost.js +4 -0
- package/dist/types/ai-cost.js.map +1 -0
- package/dist/types/analysis.d.ts +64 -0
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/changelog.d.ts +49 -0
- package/dist/types/changelog.d.ts.map +1 -0
- package/dist/types/changelog.js +3 -0
- package/dist/types/changelog.js.map +1 -0
- package/dist/types/ci.d.ts +105 -0
- package/dist/types/ci.d.ts.map +1 -0
- package/dist/types/ci.js +3 -0
- package/dist/types/ci.js.map +1 -0
- package/dist/types/coverage.d.ts +95 -0
- package/dist/types/coverage.d.ts.map +1 -0
- package/dist/types/coverage.js +4 -0
- package/dist/types/coverage.js.map +1 -0
- package/dist/types/hooks.d.ts +139 -0
- package/dist/types/hooks.d.ts.map +1 -0
- package/dist/types/hooks.js +3 -0
- package/dist/types/hooks.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/registry.d.ts +90 -0
- package/dist/types/registry.d.ts.map +1 -0
- package/dist/types/registry.js +5 -0
- package/dist/types/registry.js.map +1 -0
- package/dist/types/spec-templates.d.ts +87 -0
- package/dist/types/spec-templates.d.ts.map +1 -0
- package/dist/types/spec-templates.js +3 -0
- package/dist/types/spec-templates.js.map +1 -0
- package/dist/types/ui.d.ts +76 -0
- package/dist/types/ui.d.ts.map +1 -1
- package/dist/types/webhook.d.ts +62 -0
- package/dist/types/webhook.d.ts.map +1 -0
- package/dist/types/webhook.js +4 -0
- package/dist/types/webhook.js.map +1 -0
- package/package.json +1 -1
- package/src/config/ai-model-pricing.json +81 -0
- package/src/config/framework-registry/csharp-aspnet.json +67 -0
- package/src/config/framework-registry/dart-flutter.json +61 -0
- package/src/config/framework-registry/go-gin.json +62 -0
- package/src/config/framework-registry/index.json +164 -0
- package/src/config/framework-registry/java-springboot.json +67 -0
- package/src/config/framework-registry/php-laravel.json +67 -0
- package/src/config/framework-registry/python-django.json +72 -0
- package/src/config/framework-registry/python-fastapi.json +72 -0
- package/src/config/framework-registry/ruby-rails.json +67 -0
- package/src/config/framework-registry/rust-actix.json +61 -0
- package/src/config/framework-registry/swift-swiftui.json +67 -0
- package/src/config/framework-registry/typescript-angular.json +67 -0
- package/src/config/framework-registry/typescript-astro.json +61 -0
- package/src/config/framework-registry/typescript-express.json +67 -0
- package/src/config/framework-registry/typescript-nestjs.json +62 -0
- package/src/config/framework-registry/typescript-nextjs.json +82 -0
- package/src/config/framework-registry/typescript-nuxt.json +67 -0
- package/src/config/framework-registry/typescript-react.json +77 -0
- package/src/config/framework-registry/typescript-remix.json +61 -0
- package/src/config/framework-registry/typescript-sveltekit.json +67 -0
- package/src/config/framework-registry/typescript-vue.json +72 -0
- package/src/config/license-plans.json +23 -3
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// engine/registry/matcher.ts — Detects framework mentions and API references in spec text
|
|
2
|
+
// Uses registry-aware keywords to avoid false positives on plain English words.
|
|
3
|
+
/** Mapping of framework ID → detection keywords (aliases / common spellings). */
|
|
4
|
+
const FRAMEWORK_ALIASES = {
|
|
5
|
+
nextjs: ['next.js', 'nextjs', 'next js', 'next/navigation', 'next/router', 'app router'],
|
|
6
|
+
react: ['react', 'react dom', 'reactdom', 'jsx', 'useState', 'useEffect', 'useCallback'],
|
|
7
|
+
vue: ['vue', 'vue 3', 'vue.js', 'vuejs', 'composition api', 'script setup'],
|
|
8
|
+
nuxt: ['nuxt', 'nuxt 3', 'nuxt.js', 'usefetch', 'definepageeta'],
|
|
9
|
+
angular: ['angular', 'angular 17', '@angular', 'standalone component', 'angularjs'],
|
|
10
|
+
sveltekit: ['sveltekit', 'svelte kit', 'svelte', '+page.server.ts', 'use:enhance'],
|
|
11
|
+
astro: ['astro', 'astro.js', 'astro 4', 'client:load', 'getcollection'],
|
|
12
|
+
remix: ['remix', 'remix 2', '@remix-run', 'useloaderdata', 'useactiondata'],
|
|
13
|
+
express: ['express', 'express.js', 'expressjs', 'express.router', 'app.get(', 'app.use('],
|
|
14
|
+
nestjs: ['nestjs', 'nest.js', '@nestjs', '@injectable', '@controller', '@module'],
|
|
15
|
+
django: ['django', 'django 5', 'django orm', 'models.model', 'views.py', 'urls.py'],
|
|
16
|
+
fastapi: [
|
|
17
|
+
'fastapi',
|
|
18
|
+
'fast api',
|
|
19
|
+
'fastapi route',
|
|
20
|
+
'@app.get',
|
|
21
|
+
'@app.post',
|
|
22
|
+
'pydantic v2',
|
|
23
|
+
'basemodel',
|
|
24
|
+
],
|
|
25
|
+
springboot: [
|
|
26
|
+
'spring boot',
|
|
27
|
+
'springboot',
|
|
28
|
+
'@springbootapplication',
|
|
29
|
+
'@restcontroller',
|
|
30
|
+
'jparepository',
|
|
31
|
+
],
|
|
32
|
+
rails: ['rails', 'ruby on rails', 'activerecord', 'activesupport', 'hotwire', 'turbo'],
|
|
33
|
+
laravel: ['laravel', 'laravel 11', 'eloquent', 'artisan', 'blade template'],
|
|
34
|
+
gin: ['gin framework', 'gin.new()', 'gin.default()', 'c.json(', 'gin.h{', 'c.shouldbindjson'],
|
|
35
|
+
actix: ['actix-web', 'actix web', 'actix 4', 'web::data', 'httpresponse', 'web::path'],
|
|
36
|
+
aspnet: ['asp.net', 'aspnet', '.net core', 'dotnet', '@controller', 'iactionresult', 'blazor'],
|
|
37
|
+
flutter: [
|
|
38
|
+
'flutter',
|
|
39
|
+
'dart flutter',
|
|
40
|
+
'statefulwidget',
|
|
41
|
+
'statelesswidget',
|
|
42
|
+
'buildcontext',
|
|
43
|
+
'scaffold(',
|
|
44
|
+
],
|
|
45
|
+
swiftui: ['swiftui', 'swift ui', '@state', '@observedobject', '@stateobject', 'navigationstack'],
|
|
46
|
+
};
|
|
47
|
+
/** Regex patterns to detect API/pattern references in text. */
|
|
48
|
+
const API_PATTERNS = [
|
|
49
|
+
// Function calls: functionName() or method.call()
|
|
50
|
+
/\b([a-zA-Z$_][a-zA-Z0-9$_.]{2,})\s*\(/g,
|
|
51
|
+
// Import statements
|
|
52
|
+
/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g,
|
|
53
|
+
// Config keys: key.subkey = value
|
|
54
|
+
/\b([a-z][a-z0-9-]{2,}\.[a-z][a-z0-9-.]{2,})\s*=/g,
|
|
55
|
+
// Decorator-like patterns: @SomeName
|
|
56
|
+
/@([A-Z][a-zA-Z0-9]+)/g,
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Detect which frameworks from the registry are mentioned in the spec text.
|
|
60
|
+
* A framework is considered mentioned if one of its aliases appears in the text
|
|
61
|
+
* (case-insensitive). Avoids single-word generic terms without API context.
|
|
62
|
+
*/
|
|
63
|
+
export function detectFrameworks(specText, registry) {
|
|
64
|
+
const lower = specText.toLowerCase();
|
|
65
|
+
const detected = [];
|
|
66
|
+
for (const [frameworkId, aliases] of Object.entries(FRAMEWORK_ALIASES)) {
|
|
67
|
+
if (!registry.has(frameworkId)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const matched = aliases.some((alias) => lower.includes(alias.toLowerCase()));
|
|
71
|
+
if (matched) {
|
|
72
|
+
detected.push(frameworkId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...new Set(detected)];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Extract potential API/pattern references from spec text for specific frameworks.
|
|
79
|
+
* Returns deduplicated references with framework attribution.
|
|
80
|
+
*/
|
|
81
|
+
export function extractReferences(specText, frameworkIds, registry) {
|
|
82
|
+
const references = [];
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
for (const frameworkId of frameworkIds) {
|
|
85
|
+
const entry = registry.get(frameworkId);
|
|
86
|
+
if (!entry) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// Extract all pattern-like tokens from the text
|
|
90
|
+
for (const regex of API_PATTERNS) {
|
|
91
|
+
const cloned = new RegExp(regex.source, regex.flags);
|
|
92
|
+
let match;
|
|
93
|
+
while ((match = cloned.exec(specText)) !== null) {
|
|
94
|
+
const token = (match[1] ?? match[0]).trim();
|
|
95
|
+
const key = `${frameworkId}::${token}`;
|
|
96
|
+
if (seen.has(key) || token.length < 3) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
seen.add(key);
|
|
100
|
+
const kind = determineKind(match[0]);
|
|
101
|
+
references.push({ framework: frameworkId, text: token, kind });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return references;
|
|
106
|
+
}
|
|
107
|
+
/** Determine the kind of reference from the matched text. */
|
|
108
|
+
function determineKind(rawMatch) {
|
|
109
|
+
if (rawMatch.startsWith('import')) {
|
|
110
|
+
return 'import';
|
|
111
|
+
}
|
|
112
|
+
if (rawMatch.startsWith('@')) {
|
|
113
|
+
return 'api-call';
|
|
114
|
+
}
|
|
115
|
+
if (/=\s*$/.test(rawMatch)) {
|
|
116
|
+
return 'config-key';
|
|
117
|
+
}
|
|
118
|
+
return 'pattern-mention';
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Detect any framework names mentioned in the spec that are NOT in the registry.
|
|
122
|
+
* Scans for common framework keywords in text that don't map to known entries.
|
|
123
|
+
*/
|
|
124
|
+
export function detectUnknownFrameworks(specText, registry) {
|
|
125
|
+
const lower = specText.toLowerCase();
|
|
126
|
+
const knownFrameworkNames = new Set([...registry.values()].map((e) => e.displayName.toLowerCase()));
|
|
127
|
+
// Broad keyword scan for frameworks not in registry
|
|
128
|
+
const frameworkMentionRegex = /\b((?:[a-z][a-z0-9-]{1,}\.?(?:js|ts|py|rb|php|go|rs))|(?:fastify|hono|elysia|fiber|axum|rocket|gin|echo|koa|hapi|oak|deno|bun))\b/gi;
|
|
129
|
+
const unknown = [];
|
|
130
|
+
const seen = new Set();
|
|
131
|
+
let m;
|
|
132
|
+
while ((m = frameworkMentionRegex.exec(lower)) !== null) {
|
|
133
|
+
const name = (m[1] ?? '').toLowerCase();
|
|
134
|
+
if (!seen.has(name) && !knownFrameworkNames.has(name) && !isInRegistry(name, registry)) {
|
|
135
|
+
seen.add(name);
|
|
136
|
+
unknown.push(m[1] ?? name);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return unknown;
|
|
140
|
+
}
|
|
141
|
+
/** Check if a name matches any alias in the registry. */
|
|
142
|
+
function isInRegistry(name, registry) {
|
|
143
|
+
for (const aliases of Object.values(FRAMEWORK_ALIASES)) {
|
|
144
|
+
if (aliases.some((a) => a.toLowerCase() === name.toLowerCase())) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return registry.has(name);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../../src/engine/registry/matcher.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,gFAAgF;AAMhF,iFAAiF;AACjF,MAAM,iBAAiB,GAA6B;IAClD,MAAM,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,CAAC;IACxF,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,CAAC;IACxF,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,cAAc,CAAC;IAC3E,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,CAAC;IAChE,OAAO,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,WAAW,CAAC;IACnF,SAAS,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,CAAC;IAClF,KAAK,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,CAAC;IACvE,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,CAAC;IAC3E,OAAO,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,UAAU,CAAC;IACzF,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,CAAC;IACjF,MAAM,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,CAAC;IACnF,OAAO,EAAE;QACP,SAAS;QACT,UAAU;QACV,eAAe;QACf,UAAU;QACV,WAAW;QACX,aAAa;QACb,WAAW;KACZ;IACD,UAAU,EAAE;QACV,aAAa;QACb,YAAY;QACZ,wBAAwB;QACxB,iBAAiB;QACjB,eAAe;KAChB;IACD,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,CAAC;IACtF,OAAO,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,CAAC;IAC3E,GAAG,EAAE,CAAC,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAC7F,KAAK,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,CAAC;IACtF,MAAM,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC;IAC9F,OAAO,EAAE;QACP,SAAS;QACT,cAAc;QACd,gBAAgB;QAChB,iBAAiB;QACjB,cAAc;QACd,WAAW;KACZ;IACD,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,EAAE,cAAc,EAAE,iBAAiB,CAAC;CACjG,CAAC;AAEF,+DAA+D;AAC/D,MAAM,YAAY,GAAG;IACnB,kDAAkD;IAClD,wCAAwC;IACxC,oBAAoB;IACpB,yCAAyC;IACzC,kCAAkC;IAClC,kDAAkD;IAClD,qCAAqC;IACrC,uBAAuB;CACxB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,QAAoC;IACrF,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC7E,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAgB,EAChB,YAAsB,EACtB,QAAoC;IAEpC,MAAM,UAAU,GAAyB,EAAE,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,SAAS;QACX,CAAC;QAED,gDAAgD;QAChD,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YACrD,IAAI,KAA6B,CAAC;YAClC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChD,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC5C,MAAM,GAAG,GAAG,GAAG,WAAW,KAAK,KAAK,EAAE,CAAC;gBACvC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtC,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAEd,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrC,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,6DAA6D;AAC7D,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAgB,EAChB,QAAoC;IAEpC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACjC,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAC/D,CAAC;IAEF,oDAAoD;IACpD,MAAM,qBAAqB,GACzB,qIAAqI,CAAC;IAExI,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,IAAI,CAAyB,CAAC;IAE9B,OAAO,CAAC,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;YACvF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,yDAAyD;AACzD,SAAS,YAAY,CAAC,IAAY,EAAE,QAAoC;IACtE,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvD,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAChE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SpecAccuracyReport, RegistryEntry, ValidationResult } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build the full SpecAccuracyReport from validation results and metadata.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildReport(results: ValidationResult[], frameworksDetected: string[], frameworksNotInRegistry: string[], registry: Map<string, RegistryEntry>): SpecAccuracyReport;
|
|
6
|
+
/**
|
|
7
|
+
* Format a SpecAccuracyReport as a Markdown string for display in MCP response.
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatReport(report: SpecAccuracyReport): string;
|
|
10
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../../src/engine/registry/reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAElB,aAAa,EACb,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAkD9B;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,kBAAkB,EAAE,MAAM,EAAE,EAC5B,uBAAuB,EAAE,MAAM,EAAE,EACjC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACnC,kBAAkB,CAepB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAkF/D"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// engine/registry/reporter.ts — Builds the SpecAccuracyReport from validation results
|
|
2
|
+
/** Threshold (days) beyond which a registry entry is considered stale. */
|
|
3
|
+
const STALE_DAYS = 180;
|
|
4
|
+
const MS_PER_DAY = 86_400_000;
|
|
5
|
+
/** Calculate score as percentage of references that were validated (not unknown). */
|
|
6
|
+
function calculateScore(results) {
|
|
7
|
+
if (results.length === 0) {
|
|
8
|
+
return 100;
|
|
9
|
+
}
|
|
10
|
+
const validated = results.filter((r) => r.status !== 'unknown').length;
|
|
11
|
+
return Math.round((validated / results.length) * 100);
|
|
12
|
+
}
|
|
13
|
+
/** Extract unique issues from validation results (skip valid ones). */
|
|
14
|
+
function collectIssues(results) {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const issues = [];
|
|
17
|
+
for (const result of results) {
|
|
18
|
+
if (result.status === 'valid' || !result.issue) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const key = `${result.issue.type}::${result.issue.framework}::${result.issue.reference}`;
|
|
22
|
+
if (!seen.has(key)) {
|
|
23
|
+
seen.add(key);
|
|
24
|
+
issues.push(result.issue);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return issues;
|
|
28
|
+
}
|
|
29
|
+
/** Find registry entries that haven't been updated in more than 180 days. */
|
|
30
|
+
function detectStaleEntries(frameworkIds, registry) {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
return frameworkIds.filter((id) => {
|
|
33
|
+
const entry = registry.get(id);
|
|
34
|
+
/* v8 ignore next 2 — defensive guard; callers pass IDs from detectFrameworks() which already filters by registry */
|
|
35
|
+
if (!entry) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const updated = new Date(entry.lastUpdated).getTime();
|
|
39
|
+
return !isNaN(updated) && now - updated > STALE_DAYS * MS_PER_DAY;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the full SpecAccuracyReport from validation results and metadata.
|
|
44
|
+
*/
|
|
45
|
+
export function buildReport(results, frameworksDetected, frameworksNotInRegistry, registry) {
|
|
46
|
+
const issues = collectIssues(results);
|
|
47
|
+
const score = calculateScore(results);
|
|
48
|
+
const validatedReferences = results.filter((r) => r.status !== 'unknown').length;
|
|
49
|
+
const staleRegistryEntries = detectStaleEntries(frameworksDetected, registry);
|
|
50
|
+
return {
|
|
51
|
+
score,
|
|
52
|
+
issues,
|
|
53
|
+
frameworksDetected,
|
|
54
|
+
frameworksNotInRegistry,
|
|
55
|
+
totalReferencesChecked: results.length,
|
|
56
|
+
validatedReferences,
|
|
57
|
+
staleRegistryEntries,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Format a SpecAccuracyReport as a Markdown string for display in MCP response.
|
|
62
|
+
*/
|
|
63
|
+
export function formatReport(report) {
|
|
64
|
+
const lines = [];
|
|
65
|
+
lines.push('## Spec Accuracy Report');
|
|
66
|
+
lines.push('');
|
|
67
|
+
lines.push(`**Score de confianza:** ${report.score}/100`);
|
|
68
|
+
lines.push(`**Referencias analizadas:** ${report.totalReferencesChecked}`);
|
|
69
|
+
lines.push(`**Referencias validadas:** ${report.validatedReferences}`);
|
|
70
|
+
lines.push('');
|
|
71
|
+
if (report.frameworksDetected.length > 0) {
|
|
72
|
+
lines.push(`**Frameworks detectados:** ${report.frameworksDetected.join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
if (report.frameworksNotInRegistry.length > 0) {
|
|
75
|
+
lines.push('');
|
|
76
|
+
lines.push('### Advertencias — Frameworks no en registry');
|
|
77
|
+
lines.push('');
|
|
78
|
+
for (const fw of report.frameworksNotInRegistry) {
|
|
79
|
+
lines.push(`- Framework '${fw}' no encontrado en el registry — las referencias a su API no pudieron ser validadas`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (report.staleRegistryEntries.length > 0) {
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push('### Advertencias — Entradas desactualizadas');
|
|
85
|
+
lines.push('');
|
|
86
|
+
for (const fw of report.staleRegistryEntries) {
|
|
87
|
+
lines.push(`- La entrada de '${fw}' tiene mas de 180 dias sin actualizar — verificar si los patrones siguen vigentes`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const errors = report.issues.filter((i) => i.severity === 'error');
|
|
91
|
+
const warnings = report.issues.filter((i) => i.severity === 'warning');
|
|
92
|
+
const infos = report.issues.filter((i) => i.severity === 'info');
|
|
93
|
+
if (errors.length > 0) {
|
|
94
|
+
lines.push('');
|
|
95
|
+
lines.push('### Errores detectados');
|
|
96
|
+
lines.push('');
|
|
97
|
+
for (const issue of errors) {
|
|
98
|
+
lines.push(`- **[${issue.framework}]** \`${issue.reference}\``);
|
|
99
|
+
if (issue.suggestion) {
|
|
100
|
+
lines.push(` - Sugerencia: ${issue.suggestion}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (warnings.length > 0) {
|
|
105
|
+
lines.push('');
|
|
106
|
+
lines.push('### Advertencias de patrones');
|
|
107
|
+
lines.push('');
|
|
108
|
+
for (const issue of warnings) {
|
|
109
|
+
lines.push(`- **[${issue.framework}]** \`${issue.reference}\``);
|
|
110
|
+
if (issue.suggestion) {
|
|
111
|
+
lines.push(` - Sugerencia: ${issue.suggestion}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (infos.length > 0) {
|
|
116
|
+
lines.push('');
|
|
117
|
+
lines.push('### Informacion adicional');
|
|
118
|
+
lines.push('');
|
|
119
|
+
for (const issue of infos) {
|
|
120
|
+
lines.push(`- **[${issue.framework}]** ${issue.reference}`);
|
|
121
|
+
if (issue.suggestion) {
|
|
122
|
+
lines.push(` - ${issue.suggestion}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (report.issues.length === 0) {
|
|
127
|
+
lines.push('');
|
|
128
|
+
lines.push('No se detectaron problemas en las referencias analizadas.');
|
|
129
|
+
}
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../../src/engine/registry/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RegistryEntry, FrameworkReference, ValidationResult } from '../../types/index.js';
|
|
2
|
+
export type { ValidationResult };
|
|
3
|
+
/**
|
|
4
|
+
* Validate a single reference against its framework registry entry.
|
|
5
|
+
* Precedence: mistake > deprecated > correct > unknown
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateReference(ref: FrameworkReference, registry: Map<string, RegistryEntry>): ValidationResult;
|
|
8
|
+
/**
|
|
9
|
+
* Validate all references and return the classification results.
|
|
10
|
+
* References for frameworks not in the registry are skipped (handled separately).
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateReferences(references: FrameworkReference[], registry: Map<string, RegistryEntry>): ValidationResult[];
|
|
13
|
+
/**
|
|
14
|
+
* Detect registry entries that have not been updated in more than 180 days.
|
|
15
|
+
*/
|
|
16
|
+
export declare function detectStaleEntries(registry: Map<string, RegistryEntry>): string[];
|
|
17
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/engine/registry/validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EAEb,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAsCjC;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,kBAAkB,EACvB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACnC,gBAAgB,CAiDlB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,kBAAkB,EAAE,EAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACnC,gBAAgB,EAAE,CAEpB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,EAAE,CAajF"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// engine/registry/validator.ts — Compares extracted references against registry entries
|
|
2
|
+
// Classifies each reference as: valid, deprecated, mistake, or unknown
|
|
3
|
+
/** Search through all patterns in a registry entry to find a match. */
|
|
4
|
+
function findInPatterns(token, patterns) {
|
|
5
|
+
const lowerToken = token.toLowerCase();
|
|
6
|
+
return patterns.some((p) => {
|
|
7
|
+
const lowerPattern = p.pattern.toLowerCase();
|
|
8
|
+
const lowerExample = (p.example ?? '').toLowerCase();
|
|
9
|
+
return lowerPattern.includes(lowerToken) || lowerExample.includes(lowerToken);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/** Find a matching deprecated pattern and return its replacement. */
|
|
13
|
+
function findDeprecated(token, patterns) {
|
|
14
|
+
const lowerToken = token.toLowerCase();
|
|
15
|
+
return patterns.find((p) => {
|
|
16
|
+
const lowerPattern = p.pattern.toLowerCase();
|
|
17
|
+
const lowerExample = (p.example ?? '').toLowerCase();
|
|
18
|
+
return lowerPattern.includes(lowerToken) || lowerExample.includes(lowerToken);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Find a matching common mistake and return it. */
|
|
22
|
+
function findMistake(token, patterns) {
|
|
23
|
+
const lowerToken = token.toLowerCase();
|
|
24
|
+
return patterns.find((p) => {
|
|
25
|
+
const lowerPattern = p.pattern.toLowerCase();
|
|
26
|
+
const lowerExample = (p.example ?? '').toLowerCase();
|
|
27
|
+
return lowerPattern.includes(lowerToken) || lowerExample.includes(lowerToken);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate a single reference against its framework registry entry.
|
|
32
|
+
* Precedence: mistake > deprecated > correct > unknown
|
|
33
|
+
*/
|
|
34
|
+
export function validateReference(ref, registry) {
|
|
35
|
+
const entry = registry.get(ref.framework);
|
|
36
|
+
if (!entry) {
|
|
37
|
+
return {
|
|
38
|
+
reference: ref,
|
|
39
|
+
status: 'unknown',
|
|
40
|
+
issue: {
|
|
41
|
+
type: 'unknown-framework',
|
|
42
|
+
framework: ref.framework,
|
|
43
|
+
reference: ref.text,
|
|
44
|
+
suggestion: `Framework '${ref.framework}' no encontrado en el registry`,
|
|
45
|
+
severity: 'warning',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Check common mistakes first (highest priority — explicit errors)
|
|
50
|
+
const mistake = findMistake(ref.text, entry.commonMistakes);
|
|
51
|
+
if (mistake) {
|
|
52
|
+
const issue = {
|
|
53
|
+
type: 'invalid-api',
|
|
54
|
+
framework: ref.framework,
|
|
55
|
+
reference: ref.text,
|
|
56
|
+
suggestion: mistake.replacedBy ? `Usar en su lugar: ${mistake.replacedBy}` : mistake.pattern,
|
|
57
|
+
severity: mistake.severity ?? 'warning',
|
|
58
|
+
};
|
|
59
|
+
return { reference: ref, status: 'mistake', issue };
|
|
60
|
+
}
|
|
61
|
+
// Check deprecated patterns
|
|
62
|
+
const deprecated = findDeprecated(ref.text, entry.deprecatedPatterns);
|
|
63
|
+
if (deprecated) {
|
|
64
|
+
const issue = {
|
|
65
|
+
type: 'deprecated-pattern',
|
|
66
|
+
framework: ref.framework,
|
|
67
|
+
reference: ref.text,
|
|
68
|
+
suggestion: `Patron obsoleto. Reemplazar con: ${deprecated.replacedBy}`,
|
|
69
|
+
severity: deprecated.severity ?? 'warning',
|
|
70
|
+
};
|
|
71
|
+
return { reference: ref, status: 'deprecated', issue };
|
|
72
|
+
}
|
|
73
|
+
// Check correct patterns
|
|
74
|
+
if (findInPatterns(ref.text, entry.correctPatterns)) {
|
|
75
|
+
return { reference: ref, status: 'valid' };
|
|
76
|
+
}
|
|
77
|
+
// Not found in any pattern list — unknown but not an error
|
|
78
|
+
return { reference: ref, status: 'unknown' };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Validate all references and return the classification results.
|
|
82
|
+
* References for frameworks not in the registry are skipped (handled separately).
|
|
83
|
+
*/
|
|
84
|
+
export function validateReferences(references, registry) {
|
|
85
|
+
return references.map((ref) => validateReference(ref, registry));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Detect registry entries that have not been updated in more than 180 days.
|
|
89
|
+
*/
|
|
90
|
+
export function detectStaleEntries(registry) {
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const MS_PER_DAY = 86_400_000;
|
|
93
|
+
const STALE_DAYS = 180;
|
|
94
|
+
const stale = [];
|
|
95
|
+
for (const entry of registry.values()) {
|
|
96
|
+
const updated = new Date(entry.lastUpdated).getTime();
|
|
97
|
+
if (!isNaN(updated) && now - updated > STALE_DAYS * MS_PER_DAY) {
|
|
98
|
+
stale.push(entry.framework);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return stale;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/engine/registry/validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SpecChangelogEntry, SpecChangelogData, AppendChangelogOptions, ReadChangelogOptions } from '../../types/index.js';
|
|
2
|
+
/** Format a single changelog entry as Markdown. */
|
|
3
|
+
export declare function formatEntry(entry: SpecChangelogEntry, specId: string): string;
|
|
4
|
+
/** Parse the timestamp from the first entry in a CHANGELOG.md content. */
|
|
5
|
+
export declare function parseFirstEntryTimestamp(content: string): string | null;
|
|
6
|
+
/** Parse changelog entries from a CHANGELOG.md content string. */
|
|
7
|
+
export declare function parseChangelogEntries(content: string, specId: string): SpecChangelogEntry[];
|
|
8
|
+
/** Read the CHANGELOG.md for a spec directory. Returns null if not found. */
|
|
9
|
+
export declare function readChangelog(options: ReadChangelogOptions): Promise<SpecChangelogData | null>;
|
|
10
|
+
/** Read only the timestamp of the last (most recent) changelog entry. Optimized for list_specs. */
|
|
11
|
+
export declare function readLastModifiedTimestamp(specDir: string): Promise<string | null>;
|
|
12
|
+
/** Append a new entry to the CHANGELOG.md for a spec. Creates the file if it doesn't exist. */
|
|
13
|
+
export declare function appendChangelogEntry(options: AppendChangelogOptions): Promise<boolean>;
|
|
14
|
+
/** Format a relative date string (e.g. "hace 2 dias") for a timestamp. */
|
|
15
|
+
export declare function formatRelativeDate(timestamp: string): string;
|
|
16
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-changelog/core.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAElB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAK9B,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAmD7E;AAQD,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIvE;AAED,kEAAkE;AAClE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAqC3F;AAED,6EAA6E;AAC7E,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAWnC;AAED,mGAAmG;AACnG,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASvF;AAED,+FAA+F;AAC/F,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwC5F;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAkB5D"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// engine/spec-changelog/core.ts — Changelog entry creation and persistence
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const CHANGELOG_FILENAME = 'CHANGELOG.md';
|
|
5
|
+
const CHANGELOG_VERSION_COMMENT = '<!-- changelog-version: 1 -->';
|
|
6
|
+
/** Format a single changelog entry as Markdown. */
|
|
7
|
+
export function formatEntry(entry, specId) {
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push(`## ${entry.timestamp} — ${entry.type}`);
|
|
10
|
+
lines.push('');
|
|
11
|
+
lines.push(`**Tool**: ${entry.triggeredBy}`);
|
|
12
|
+
lines.push(`**Descripcion**: ${entry.description}`);
|
|
13
|
+
lines.push('');
|
|
14
|
+
if (entry.before !== undefined && entry.after !== undefined) {
|
|
15
|
+
if (entry.type === 'status_changed') {
|
|
16
|
+
lines.push(`**Antes**: ${entry.before.status} | **Despues**: ${entry.after.status}`);
|
|
17
|
+
lines.push(`**Progreso**: ${entry.before.completedCount}/${entry.before.criteriaCount} → ${entry.after.completedCount}/${entry.after.criteriaCount}`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
lines.push(`**Criterios antes**: ${entry.before.criteriaCount} | **Criterios despues**: ${entry.after.criteriaCount}`);
|
|
21
|
+
if (entry.type === 'progress_updated') {
|
|
22
|
+
lines.push(`**Progreso**: ${entry.before.completedCount}/${entry.before.criteriaCount} → ${entry.after.completedCount}/${entry.after.criteriaCount}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
lines.push('');
|
|
26
|
+
}
|
|
27
|
+
if (entry.diff !== undefined) {
|
|
28
|
+
const { added, removed, modified } = entry.diff;
|
|
29
|
+
const hasCriteriaChanges = added.length > 0 || removed.length > 0 || modified.length > 0;
|
|
30
|
+
if (hasCriteriaChanges) {
|
|
31
|
+
lines.push('### Criterios modificados');
|
|
32
|
+
for (const item of added) {
|
|
33
|
+
lines.push(`\u2795 ${item}`);
|
|
34
|
+
}
|
|
35
|
+
for (const item of removed) {
|
|
36
|
+
lines.push(`\u2796 ${item}`);
|
|
37
|
+
}
|
|
38
|
+
for (const item of modified) {
|
|
39
|
+
lines.push(`\u270f\ufe0f ${item.after} (antes: ${item.before})`);
|
|
40
|
+
}
|
|
41
|
+
lines.push('');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
lines.push('---');
|
|
45
|
+
lines.push('');
|
|
46
|
+
void specId; // referenced in header externally
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
/** Build the full CHANGELOG.md header for a spec. */
|
|
50
|
+
function buildHeader(specId, specTitle) {
|
|
51
|
+
const title = specTitle ? `${specId}: ${specTitle}` : specId;
|
|
52
|
+
return [CHANGELOG_VERSION_COMMENT, '', `# Changelog — ${title}`, '', ''].join('\n');
|
|
53
|
+
}
|
|
54
|
+
/** Parse the timestamp from the first entry in a CHANGELOG.md content. */
|
|
55
|
+
export function parseFirstEntryTimestamp(content) {
|
|
56
|
+
// Matches lines like: ## 2026-03-04T14:23:00Z — reconciled
|
|
57
|
+
const match = /^## (\d{4}-\d{2}-\d{2}T[\d:.]+Z) —/m.exec(content);
|
|
58
|
+
return match?.[1] ?? null;
|
|
59
|
+
}
|
|
60
|
+
/** Parse changelog entries from a CHANGELOG.md content string. */
|
|
61
|
+
export function parseChangelogEntries(content, specId) {
|
|
62
|
+
const entries = [];
|
|
63
|
+
// Split on section headers: ## <timestamp> — <type>
|
|
64
|
+
const sectionRegex = /^## (\d{4}-\d{2}-\d{2}T[\d:.]+Z) — (\w+)/gm;
|
|
65
|
+
const matches = [...content.matchAll(sectionRegex)];
|
|
66
|
+
for (const match of matches) {
|
|
67
|
+
const timestamp = match[1];
|
|
68
|
+
const type = match[2];
|
|
69
|
+
if (timestamp === undefined || type === undefined) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const sectionStart = match.index;
|
|
73
|
+
const nextMatch = matches[matches.indexOf(match) + 1];
|
|
74
|
+
const sectionEnd = nextMatch?.index ?? content.length;
|
|
75
|
+
const sectionContent = content.slice(sectionStart, sectionEnd);
|
|
76
|
+
// Extract tool name
|
|
77
|
+
const toolMatch = /\*\*Tool\*\*: (.+)/.exec(sectionContent);
|
|
78
|
+
const triggeredBy = toolMatch?.[1]?.trim() ?? 'unknown';
|
|
79
|
+
// Extract description
|
|
80
|
+
const descMatch = /\*\*Descripcion\*\*: (.+)/.exec(sectionContent);
|
|
81
|
+
const description = descMatch?.[1]?.trim() ?? '';
|
|
82
|
+
entries.push({
|
|
83
|
+
timestamp,
|
|
84
|
+
type: type,
|
|
85
|
+
description,
|
|
86
|
+
triggeredBy,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
void specId;
|
|
90
|
+
return entries;
|
|
91
|
+
}
|
|
92
|
+
/** Read the CHANGELOG.md for a spec directory. Returns null if not found. */
|
|
93
|
+
export async function readChangelog(options) {
|
|
94
|
+
const { specDir, specId } = options;
|
|
95
|
+
const filePath = join(specDir, CHANGELOG_FILENAME);
|
|
96
|
+
try {
|
|
97
|
+
const content = await readFile(filePath, 'utf-8');
|
|
98
|
+
const entries = parseChangelogEntries(content, specId);
|
|
99
|
+
return { specId, entries };
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Read only the timestamp of the last (most recent) changelog entry. Optimized for list_specs. */
|
|
106
|
+
export async function readLastModifiedTimestamp(specDir) {
|
|
107
|
+
const filePath = join(specDir, CHANGELOG_FILENAME);
|
|
108
|
+
try {
|
|
109
|
+
const content = await readFile(filePath, 'utf-8');
|
|
110
|
+
return parseFirstEntryTimestamp(content);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Append a new entry to the CHANGELOG.md for a spec. Creates the file if it doesn't exist. */
|
|
117
|
+
export async function appendChangelogEntry(options) {
|
|
118
|
+
const { specDir, specId, entry } = options;
|
|
119
|
+
const filePath = join(specDir, CHANGELOG_FILENAME);
|
|
120
|
+
try {
|
|
121
|
+
let existingContent = '';
|
|
122
|
+
let hasHeader = false;
|
|
123
|
+
try {
|
|
124
|
+
existingContent = await readFile(filePath, 'utf-8');
|
|
125
|
+
hasHeader = existingContent.includes(CHANGELOG_VERSION_COMMENT);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// File doesn't exist yet — will create fresh
|
|
129
|
+
}
|
|
130
|
+
const entryMarkdown = formatEntry(entry, specId);
|
|
131
|
+
let newContent;
|
|
132
|
+
if (!hasHeader) {
|
|
133
|
+
newContent = buildHeader(specId) + entryMarkdown + existingContent;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Insert after the header block (after the # Changelog line)
|
|
137
|
+
const insertAfterPattern = /^# Changelog[^\n]*\n\n/m;
|
|
138
|
+
const match = insertAfterPattern.exec(existingContent);
|
|
139
|
+
if (match) {
|
|
140
|
+
const insertAt = match.index + match[0].length;
|
|
141
|
+
newContent =
|
|
142
|
+
existingContent.slice(0, insertAt) + entryMarkdown + existingContent.slice(insertAt);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Fallback: append after existing content
|
|
146
|
+
newContent = existingContent + entryMarkdown;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
await writeFile(filePath, newContent, 'utf-8');
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error(`[spec-changelog] Failed to write CHANGELOG.md for ${specId}:`, error);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Format a relative date string (e.g. "hace 2 dias") for a timestamp. */
|
|
158
|
+
export function formatRelativeDate(timestamp) {
|
|
159
|
+
const date = new Date(timestamp);
|
|
160
|
+
const now = new Date();
|
|
161
|
+
const diffMs = now.getTime() - date.getTime();
|
|
162
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
163
|
+
if (diffDays === 0) {
|
|
164
|
+
return 'hoy';
|
|
165
|
+
}
|
|
166
|
+
if (diffDays === 1) {
|
|
167
|
+
return 'hace 1 dia';
|
|
168
|
+
}
|
|
169
|
+
if (diffDays <= 30) {
|
|
170
|
+
return `hace ${diffDays} dias`;
|
|
171
|
+
}
|
|
172
|
+
// Older than 30 days: return ISO date portion
|
|
173
|
+
return timestamp.slice(0, 10);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=core.js.map
|