@planu/cli 0.28.0 → 0.30.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/model-routing-rules.json +98 -0
- package/dist/engine/ai-cost-estimator/core.d.ts.map +1 -1
- package/dist/engine/ai-cost-estimator/core.js +2 -202
- package/dist/engine/ai-cost-estimator/core.js.map +1 -1
- package/dist/engine/ai-cost-estimator/recommender.d.ts +8 -0
- package/dist/engine/ai-cost-estimator/recommender.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/recommender.js +94 -0
- package/dist/engine/ai-cost-estimator/recommender.js.map +1 -0
- package/dist/engine/ai-cost-estimator/spec-loader.d.ts +13 -0
- package/dist/engine/ai-cost-estimator/spec-loader.d.ts.map +1 -0
- package/dist/engine/ai-cost-estimator/spec-loader.js +102 -0
- package/dist/engine/ai-cost-estimator/spec-loader.js.map +1 -0
- package/dist/engine/ci-generator/planu-config.d.ts +7 -0
- package/dist/engine/ci-generator/planu-config.d.ts.map +1 -0
- package/dist/engine/ci-generator/planu-config.js +40 -0
- package/dist/engine/ci-generator/planu-config.js.map +1 -0
- package/dist/engine/ci-generator/planu-steps.d.ts +11 -0
- package/dist/engine/ci-generator/planu-steps.d.ts.map +1 -0
- package/dist/engine/ci-generator/planu-steps.js +292 -0
- package/dist/engine/ci-generator/planu-steps.js.map +1 -0
- package/dist/engine/code-transforms/transform-engine.d.ts +7 -0
- package/dist/engine/code-transforms/transform-engine.d.ts.map +1 -0
- package/dist/engine/code-transforms/transform-engine.js +67 -0
- package/dist/engine/code-transforms/transform-engine.js.map +1 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.d.ts +6 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.js +92 -0
- package/dist/engine/code-transforms/typescript/add-error-handling.js.map +1 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.d.ts +6 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.js +83 -0
- package/dist/engine/code-transforms/typescript/add-jsdoc.js.map +1 -0
- package/dist/engine/code-transforms/typescript/add-types.d.ts +3 -0
- package/dist/engine/code-transforms/typescript/add-types.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/add-types.js +182 -0
- package/dist/engine/code-transforms/typescript/add-types.js.map +1 -0
- package/dist/engine/code-transforms/typescript/ast-utils.d.ts +38 -0
- package/dist/engine/code-transforms/typescript/ast-utils.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/ast-utils.js +90 -0
- package/dist/engine/code-transforms/typescript/ast-utils.js.map +1 -0
- package/dist/engine/code-transforms/typescript/extract-interface.d.ts +6 -0
- package/dist/engine/code-transforms/typescript/extract-interface.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/extract-interface.js +103 -0
- package/dist/engine/code-transforms/typescript/extract-interface.js.map +1 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.d.ts +3 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.js +213 -0
- package/dist/engine/code-transforms/typescript/modernize-syntax.js.map +1 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.d.ts +8 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.d.ts.map +1 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.js +40 -0
- package/dist/engine/code-transforms/typescript/rename-symbol.js.map +1 -0
- package/dist/engine/mermaid/core.d.ts +18 -0
- package/dist/engine/mermaid/core.d.ts.map +1 -0
- package/dist/engine/mermaid/core.js +88 -0
- package/dist/engine/mermaid/core.js.map +1 -0
- package/dist/engine/mermaid/diagram-generators.d.ts +22 -0
- package/dist/engine/mermaid/diagram-generators.d.ts.map +1 -0
- package/dist/engine/mermaid/diagram-generators.js +139 -0
- package/dist/engine/mermaid/diagram-generators.js.map +1 -0
- package/dist/engine/mermaid/helpers.d.ts +8 -0
- package/dist/engine/mermaid/helpers.d.ts.map +1 -0
- package/dist/engine/mermaid/helpers.js +61 -0
- package/dist/engine/mermaid/helpers.js.map +1 -0
- package/dist/engine/mermaid-generator.d.ts +2 -37
- package/dist/engine/mermaid-generator.d.ts.map +1 -1
- package/dist/engine/mermaid-generator.js +4 -276
- package/dist/engine/mermaid-generator.js.map +1 -1
- package/dist/engine/model-router/complexity-analyzer.d.ts +26 -0
- package/dist/engine/model-router/complexity-analyzer.d.ts.map +1 -0
- package/dist/engine/model-router/complexity-analyzer.js +182 -0
- package/dist/engine/model-router/complexity-analyzer.js.map +1 -0
- package/dist/engine/model-router/cost-estimator.d.ts +6 -0
- package/dist/engine/model-router/cost-estimator.d.ts.map +1 -0
- package/dist/engine/model-router/cost-estimator.js +60 -0
- package/dist/engine/model-router/cost-estimator.js.map +1 -0
- package/dist/engine/model-router/historical-learner.d.ts +26 -0
- package/dist/engine/model-router/historical-learner.d.ts.map +1 -0
- package/dist/engine/model-router/historical-learner.js +91 -0
- package/dist/engine/model-router/historical-learner.js.map +1 -0
- package/dist/engine/model-router/rules-engine.d.ts +13 -0
- package/dist/engine/model-router/rules-engine.d.ts.map +1 -0
- package/dist/engine/model-router/rules-engine.js +142 -0
- package/dist/engine/model-router/rules-engine.js.map +1 -0
- package/dist/engine/spec-coverage/criteria-mapper.d.ts +1 -2
- package/dist/engine/spec-coverage/criteria-mapper.d.ts.map +1 -1
- package/dist/engine/spec-coverage/criteria-mapper.js +4 -203
- package/dist/engine/spec-coverage/criteria-mapper.js.map +1 -1
- package/dist/engine/spec-coverage/keyword-extractor.d.ts +10 -0
- package/dist/engine/spec-coverage/keyword-extractor.d.ts.map +1 -0
- package/dist/engine/spec-coverage/keyword-extractor.js +147 -0
- package/dist/engine/spec-coverage/keyword-extractor.js.map +1 -0
- package/dist/engine/spec-coverage/test-matchers.d.ts +9 -0
- package/dist/engine/spec-coverage/test-matchers.d.ts.map +1 -0
- package/dist/engine/spec-coverage/test-matchers.js +59 -0
- package/dist/engine/spec-coverage/test-matchers.js.map +1 -0
- package/dist/engine/spec-templates/catalog-extra.d.ts +1 -1
- package/dist/engine/spec-templates/catalog-extra.d.ts.map +1 -1
- package/dist/engine/spec-templates/catalog-extra.js +8 -363
- package/dist/engine/spec-templates/catalog-extra.js.map +1 -1
- package/dist/engine/spec-templates/catalog-industry.d.ts +4 -0
- package/dist/engine/spec-templates/catalog-industry.d.ts.map +1 -0
- package/dist/engine/spec-templates/catalog-industry.js +19 -0
- package/dist/engine/spec-templates/catalog-industry.js.map +1 -0
- package/dist/engine/spec-templates/catalog.d.ts +1 -1
- package/dist/engine/spec-templates/catalog.d.ts.map +1 -1
- package/dist/engine/spec-templates/catalog.js +12 -381
- package/dist/engine/spec-templates/catalog.js.map +1 -1
- package/dist/engine/spec-templates/custom-loader.d.ts +12 -0
- package/dist/engine/spec-templates/custom-loader.d.ts.map +1 -0
- package/dist/engine/spec-templates/custom-loader.js +99 -0
- package/dist/engine/spec-templates/custom-loader.js.map +1 -0
- package/dist/engine/spec-templates/index.d.ts +4 -2
- package/dist/engine/spec-templates/index.d.ts.map +1 -1
- package/dist/engine/spec-templates/index.js +4 -2
- package/dist/engine/spec-templates/index.js.map +1 -1
- package/dist/engine/spec-templates/query.d.ts +12 -4
- package/dist/engine/spec-templates/query.d.ts.map +1 -1
- package/dist/engine/spec-templates/query.js +29 -7
- package/dist/engine/spec-templates/query.js.map +1 -1
- package/dist/engine/spec-templates/renderer.d.ts +27 -2
- package/dist/engine/spec-templates/renderer.d.ts.map +1 -1
- package/dist/engine/spec-templates/renderer.js +119 -4
- package/dist/engine/spec-templates/renderer.js.map +1 -1
- package/dist/engine/spec-templates/templates-api-ui.d.ts +6 -0
- package/dist/engine/spec-templates/templates-api-ui.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-api-ui.js +215 -0
- package/dist/engine/spec-templates/templates-api-ui.js.map +1 -0
- package/dist/engine/spec-templates/templates-auth-crud.d.ts +6 -0
- package/dist/engine/spec-templates/templates-auth-crud.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-auth-crud.js +225 -0
- package/dist/engine/spec-templates/templates-auth-crud.js.map +1 -0
- package/dist/engine/spec-templates/templates-data-security.d.ts +6 -0
- package/dist/engine/spec-templates/templates-data-security.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-data-security.js +198 -0
- package/dist/engine/spec-templates/templates-data-security.js.map +1 -0
- package/dist/engine/spec-templates/templates-industry-ecom.d.ts +6 -0
- package/dist/engine/spec-templates/templates-industry-ecom.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-industry-ecom.js +209 -0
- package/dist/engine/spec-templates/templates-industry-ecom.js.map +1 -0
- package/dist/engine/spec-templates/templates-industry-fintech.d.ts +8 -0
- package/dist/engine/spec-templates/templates-industry-fintech.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-industry-fintech.js +350 -0
- package/dist/engine/spec-templates/templates-industry-fintech.js.map +1 -0
- package/dist/engine/spec-templates/templates-industry-health-ecom.d.ts +3 -0
- package/dist/engine/spec-templates/templates-industry-health-ecom.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-industry-health-ecom.js +5 -0
- package/dist/engine/spec-templates/templates-industry-health-ecom.js.map +1 -0
- package/dist/engine/spec-templates/templates-industry-health.d.ts +6 -0
- package/dist/engine/spec-templates/templates-industry-health.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-industry-health.js +208 -0
- package/dist/engine/spec-templates/templates-industry-health.js.map +1 -0
- package/dist/engine/spec-templates/templates-industry-saas.d.ts +8 -0
- package/dist/engine/spec-templates/templates-industry-saas.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-industry-saas.js +323 -0
- package/dist/engine/spec-templates/templates-industry-saas.js.map +1 -0
- package/dist/engine/spec-templates/templates-perf-integration.d.ts +6 -0
- package/dist/engine/spec-templates/templates-perf-integration.d.ts.map +1 -0
- package/dist/engine/spec-templates/templates-perf-integration.js +226 -0
- package/dist/engine/spec-templates/templates-perf-integration.js.map +1 -0
- package/dist/engine/vector-store/hnsw.d.ts +37 -0
- package/dist/engine/vector-store/hnsw.d.ts.map +1 -0
- package/dist/engine/vector-store/hnsw.js +294 -0
- package/dist/engine/vector-store/hnsw.js.map +1 -0
- package/dist/engine/vector-store/similarity.d.ts +21 -0
- package/dist/engine/vector-store/similarity.d.ts.map +1 -0
- package/dist/engine/vector-store/similarity.js +86 -0
- package/dist/engine/vector-store/similarity.js.map +1 -0
- package/dist/engine/vector-store/tfidf.d.ts +35 -0
- package/dist/engine/vector-store/tfidf.d.ts.map +1 -0
- package/dist/engine/vector-store/tfidf.js +255 -0
- package/dist/engine/vector-store/tfidf.js.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/vector-store/backend-factory.d.ts +9 -0
- package/dist/storage/vector-store/backend-factory.d.ts.map +1 -0
- package/dist/storage/vector-store/backend-factory.js +33 -0
- package/dist/storage/vector-store/backend-factory.js.map +1 -0
- package/dist/storage/vector-store/json-fallback.d.ts +21 -0
- package/dist/storage/vector-store/json-fallback.d.ts.map +1 -0
- package/dist/storage/vector-store/json-fallback.js +85 -0
- package/dist/storage/vector-store/json-fallback.js.map +1 -0
- package/dist/storage/vector-store/migrator.d.ts +10 -0
- package/dist/storage/vector-store/migrator.d.ts.map +1 -0
- package/dist/storage/vector-store/migrator.js +139 -0
- package/dist/storage/vector-store/migrator.js.map +1 -0
- package/dist/storage/vector-store/sqlite-adapter.d.ts +28 -0
- package/dist/storage/vector-store/sqlite-adapter.d.ts.map +1 -0
- package/dist/storage/vector-store/sqlite-adapter.js +142 -0
- package/dist/storage/vector-store/sqlite-adapter.js.map +1 -0
- package/dist/tools/ci-planu-handler.d.ts +8 -0
- package/dist/tools/ci-planu-handler.d.ts.map +1 -0
- package/dist/tools/ci-planu-handler.js +44 -0
- package/dist/tools/ci-planu-handler.js.map +1 -0
- package/dist/tools/create-spec/constitution-validator.d.ts +4 -0
- package/dist/tools/create-spec/constitution-validator.d.ts.map +1 -0
- package/dist/tools/create-spec/constitution-validator.js +37 -0
- package/dist/tools/create-spec/constitution-validator.js.map +1 -0
- package/dist/tools/create-spec/post-creation.d.ts +11 -0
- package/dist/tools/create-spec/post-creation.d.ts.map +1 -0
- package/dist/tools/create-spec/post-creation.js +48 -0
- package/dist/tools/create-spec/post-creation.js.map +1 -0
- package/dist/tools/create-spec/spec-builder.d.ts +14 -0
- package/dist/tools/create-spec/spec-builder.d.ts.map +1 -0
- package/dist/tools/create-spec/spec-builder.js +131 -0
- package/dist/tools/create-spec/spec-builder.js.map +1 -0
- package/dist/tools/create-spec.d.ts.map +1 -1
- package/dist/tools/create-spec.js +42 -172
- package/dist/tools/create-spec.js.map +1 -1
- package/dist/tools/init-project/handler.d.ts.map +1 -1
- package/dist/tools/init-project/handler.js +24 -159
- package/dist/tools/init-project/handler.js.map +1 -1
- package/dist/tools/init-project/result-builder.d.ts +4 -0
- package/dist/tools/init-project/result-builder.d.ts.map +1 -0
- package/dist/tools/init-project/result-builder.js +150 -0
- package/dist/tools/init-project/result-builder.js.map +1 -0
- package/dist/tools/learn.d.ts.map +1 -1
- package/dist/tools/learn.js +67 -14
- package/dist/tools/learn.js.map +1 -1
- package/dist/tools/recommend-model-handler.d.ts +8 -0
- package/dist/tools/recommend-model-handler.d.ts.map +1 -0
- package/dist/tools/recommend-model-handler.js +65 -0
- package/dist/tools/recommend-model-handler.js.map +1 -0
- package/dist/tools/register-ci-tools.d.ts.map +1 -1
- package/dist/tools/register-ci-tools.js +36 -0
- package/dist/tools/register-ci-tools.js.map +1 -1
- package/dist/tools/register-model-tools.d.ts +3 -0
- package/dist/tools/register-model-tools.d.ts.map +1 -0
- package/dist/tools/register-model-tools.js +50 -0
- package/dist/tools/register-model-tools.js.map +1 -0
- package/dist/tools/register-search-tools.d.ts +7 -0
- package/dist/tools/register-search-tools.d.ts.map +1 -0
- package/dist/tools/register-search-tools.js +34 -0
- package/dist/tools/register-search-tools.js.map +1 -0
- package/dist/tools/register-template-tools.d.ts.map +1 -1
- package/dist/tools/register-template-tools.js +21 -8
- package/dist/tools/register-template-tools.js.map +1 -1
- package/dist/tools/register-transform-tools.d.ts +3 -0
- package/dist/tools/register-transform-tools.d.ts.map +1 -0
- package/dist/tools/register-transform-tools.js +29 -0
- package/dist/tools/register-transform-tools.js.map +1 -0
- package/dist/tools/semantic-search-handler.d.ts +7 -0
- package/dist/tools/semantic-search-handler.d.ts.map +1 -0
- package/dist/tools/semantic-search-handler.js +72 -0
- package/dist/tools/semantic-search-handler.js.map +1 -0
- package/dist/tools/spec-templates.d.ts +1 -1
- package/dist/tools/spec-templates.d.ts.map +1 -1
- package/dist/tools/spec-templates.js +20 -6
- package/dist/tools/spec-templates.js.map +1 -1
- package/dist/tools/transform-code-handler.d.ts +7 -0
- package/dist/tools/transform-code-handler.d.ts.map +1 -0
- package/dist/tools/transform-code-handler.js +58 -0
- package/dist/tools/transform-code-handler.js.map +1 -0
- package/dist/types/advanced-framework.d.ts +47 -0
- package/dist/types/advanced-framework.d.ts.map +1 -0
- package/dist/types/advanced-framework.js +3 -0
- package/dist/types/advanced-framework.js.map +1 -0
- package/dist/types/ci.d.ts +51 -0
- package/dist/types/ci.d.ts.map +1 -1
- package/dist/types/code-transforms.d.ts +114 -0
- package/dist/types/code-transforms.d.ts.map +1 -0
- package/dist/types/code-transforms.js +11 -0
- package/dist/types/code-transforms.js.map +1 -0
- package/dist/types/css-framework.d.ts +110 -0
- package/dist/types/css-framework.d.ts.map +1 -0
- package/dist/types/css-framework.js +3 -0
- package/dist/types/css-framework.js.map +1 -0
- package/dist/types/dashboard.d.ts +77 -0
- package/dist/types/dashboard.d.ts.map +1 -0
- package/dist/types/dashboard.js +2 -0
- package/dist/types/dashboard.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/model-routing.d.ts +127 -0
- package/dist/types/model-routing.d.ts.map +1 -0
- package/dist/types/model-routing.js +3 -0
- package/dist/types/model-routing.js.map +1 -0
- package/dist/types/project/core.d.ts +46 -5
- package/dist/types/project/core.d.ts.map +1 -1
- package/dist/types/spec/core.d.ts +28 -1
- package/dist/types/spec/core.d.ts.map +1 -1
- package/dist/types/spec/inputs.d.ts +1 -6
- package/dist/types/spec/inputs.d.ts.map +1 -1
- package/dist/types/spec-templates.d.ts +51 -1
- package/dist/types/spec-templates.d.ts.map +1 -1
- package/dist/types/ui.d.ts +3 -231
- package/dist/types/ui.d.ts.map +1 -1
- package/dist/types/ui.js +7 -1
- package/dist/types/ui.js.map +1 -1
- package/dist/types/vector-store.d.ts +144 -0
- package/dist/types/vector-store.d.ts.map +1 -0
- package/dist/types/vector-store.js +3 -0
- package/dist/types/vector-store.js.map +1 -0
- package/package.json +1 -1
- package/src/config/model-routing-rules.json +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-estimator.d.ts","sourceRoot":"","sources":["../../../src/engine/model-router/cost-estimator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,SAAS,EAEV,MAAM,sBAAsB,CAAC;AAkD9B,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,YAAY,CAYvF;AAED,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAMjG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// engine/model-router/cost-estimator.ts — Token and USD cost estimation per model tier.
|
|
2
|
+
// SPEC-076 AC-04
|
|
3
|
+
import pricingJson from '../../config/ai-model-pricing.json' with { type: 'json' };
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Pricing lookup
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const MODEL_IDS = {
|
|
8
|
+
haiku: 'claude-haiku-3-5',
|
|
9
|
+
sonnet: 'claude-sonnet-4',
|
|
10
|
+
opus: 'claude-opus-4',
|
|
11
|
+
};
|
|
12
|
+
function getPricing(tier) {
|
|
13
|
+
const modelId = MODEL_IDS[tier];
|
|
14
|
+
const entry = pricingJson.models.find((m) => m.id === modelId);
|
|
15
|
+
/* c8 ignore next 3 */
|
|
16
|
+
if (!entry) {
|
|
17
|
+
return { id: modelId, inputPricePerMillion: 3, outputPricePerMillion: 15 };
|
|
18
|
+
}
|
|
19
|
+
return entry;
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Token estimation heuristic
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/** Base token estimates per complexity range (overall score brackets). */
|
|
25
|
+
function estimateTokens(signals) {
|
|
26
|
+
const score = signals.overallScore;
|
|
27
|
+
// Read-only tasks consume less output
|
|
28
|
+
const outputMultiplier = signals.isReadOnly ? 0.3 : 1;
|
|
29
|
+
if (score < 0.3) {
|
|
30
|
+
return { input: 2_000, output: Math.round(1_000 * outputMultiplier) };
|
|
31
|
+
}
|
|
32
|
+
if (score < 0.6) {
|
|
33
|
+
return { input: 8_000, output: Math.round(5_000 * outputMultiplier) };
|
|
34
|
+
}
|
|
35
|
+
return { input: 20_000, output: Math.round(15_000 * outputMultiplier) };
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Public API
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/** Calculate the cost estimate for a given model tier and complexity signals. */
|
|
41
|
+
export function calculateCost(tier, signals) {
|
|
42
|
+
const pricing = getPricing(tier);
|
|
43
|
+
const tokens = estimateTokens(signals);
|
|
44
|
+
const inputCost = (tokens.input / 1_000_000) * pricing.inputPricePerMillion;
|
|
45
|
+
const outputCost = (tokens.output / 1_000_000) * pricing.outputPricePerMillion;
|
|
46
|
+
return {
|
|
47
|
+
estimatedTokens: tokens.input + tokens.output,
|
|
48
|
+
estimatedUSD: Math.round((inputCost + outputCost) * 10_000) / 10_000,
|
|
49
|
+
breakdown: { input: tokens.input, output: tokens.output },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Calculate costs for all three tiers so the user can compare. */
|
|
53
|
+
export function calculateAllTierCosts(signals) {
|
|
54
|
+
return {
|
|
55
|
+
haiku: calculateCost('haiku', signals),
|
|
56
|
+
sonnet: calculateCost('sonnet', signals),
|
|
57
|
+
opus: calculateCost('opus', signals),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=cost-estimator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-estimator.js","sourceRoot":"","sources":["../../../src/engine/model-router/cost-estimator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HistoricalAdjustment, ModelOutcome, ModelTier } from '../../types/index.js';
|
|
2
|
+
/** Record a model outcome for future learning. */
|
|
3
|
+
export declare function recordOutcome(outcome: ModelOutcome): void;
|
|
4
|
+
/** Get all recorded outcomes (for testing / export). */
|
|
5
|
+
export declare function getOutcomes(): readonly ModelOutcome[];
|
|
6
|
+
/** Clear all outcomes (for testing). */
|
|
7
|
+
export declare function clearOutcomes(): void;
|
|
8
|
+
/** Load outcomes from an external source (e.g. JSON file). */
|
|
9
|
+
export declare function loadOutcomes(loaded: ModelOutcome[]): void;
|
|
10
|
+
/**
|
|
11
|
+
* Find outcomes whose taskSignature matches the given signature.
|
|
12
|
+
* Uses exact match for now; vector similarity can be added later (SPEC-075).
|
|
13
|
+
*/
|
|
14
|
+
export declare function findSimilarOutcomes(taskSignature: string): ModelOutcome[];
|
|
15
|
+
/**
|
|
16
|
+
* Compute a task signature from a description or spec ID.
|
|
17
|
+
* For now this is a simple normalisation; in the future it can use embeddings.
|
|
18
|
+
*/
|
|
19
|
+
export declare function computeTaskSignature(text: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Given the rules engine recommendation and a task signature,
|
|
22
|
+
* adjust the tier based on historical outcomes.
|
|
23
|
+
* Maximum adjustment: plus or minus 1 tier.
|
|
24
|
+
*/
|
|
25
|
+
export declare function adjustFromHistory(recommendedModel: ModelTier, taskSignature: string): HistoricalAdjustment;
|
|
26
|
+
//# sourceMappingURL=historical-learner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historical-learner.d.ts","sourceRoot":"","sources":["../../../src/engine/model-router/historical-learner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAuB1F,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEzD;AAED,wDAAwD;AACxD,wBAAgB,WAAW,IAAI,SAAS,YAAY,EAAE,CAErD;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,CAEzD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,YAAY,EAAE,CAEzE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,gBAAgB,EAAE,SAAS,EAC3B,aAAa,EAAE,MAAM,GACpB,oBAAoB,CA6CtB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// engine/model-router/historical-learner.ts — Learn from past model outcomes.
|
|
2
|
+
// SPEC-076 AC-03
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// In-memory outcome store (JSON persistence is left to the storage layer)
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
let outcomes = [];
|
|
7
|
+
const TIER_ORDER = ['haiku', 'sonnet', 'opus'];
|
|
8
|
+
function tierIndex(tier) {
|
|
9
|
+
return TIER_ORDER.indexOf(tier);
|
|
10
|
+
}
|
|
11
|
+
function tierByIndex(index) {
|
|
12
|
+
const clamped = Math.max(0, Math.min(index, TIER_ORDER.length - 1));
|
|
13
|
+
return TIER_ORDER[clamped] ?? 'sonnet';
|
|
14
|
+
}
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Public API
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/** Record a model outcome for future learning. */
|
|
19
|
+
export function recordOutcome(outcome) {
|
|
20
|
+
outcomes.push(outcome);
|
|
21
|
+
}
|
|
22
|
+
/** Get all recorded outcomes (for testing / export). */
|
|
23
|
+
export function getOutcomes() {
|
|
24
|
+
return outcomes;
|
|
25
|
+
}
|
|
26
|
+
/** Clear all outcomes (for testing). */
|
|
27
|
+
export function clearOutcomes() {
|
|
28
|
+
outcomes = [];
|
|
29
|
+
}
|
|
30
|
+
/** Load outcomes from an external source (e.g. JSON file). */
|
|
31
|
+
export function loadOutcomes(loaded) {
|
|
32
|
+
outcomes = [...loaded];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Find outcomes whose taskSignature matches the given signature.
|
|
36
|
+
* Uses exact match for now; vector similarity can be added later (SPEC-075).
|
|
37
|
+
*/
|
|
38
|
+
export function findSimilarOutcomes(taskSignature) {
|
|
39
|
+
return outcomes.filter((o) => o.taskSignature === taskSignature);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Compute a task signature from a description or spec ID.
|
|
43
|
+
* For now this is a simple normalisation; in the future it can use embeddings.
|
|
44
|
+
*/
|
|
45
|
+
export function computeTaskSignature(text) {
|
|
46
|
+
return text.trim().toLowerCase().replace(/\s+/g, '-').slice(0, 80);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Given the rules engine recommendation and a task signature,
|
|
50
|
+
* adjust the tier based on historical outcomes.
|
|
51
|
+
* Maximum adjustment: plus or minus 1 tier.
|
|
52
|
+
*/
|
|
53
|
+
export function adjustFromHistory(recommendedModel, taskSignature) {
|
|
54
|
+
const similar = findSimilarOutcomes(taskSignature);
|
|
55
|
+
if (similar.length === 0) {
|
|
56
|
+
return {
|
|
57
|
+
adjustedModel: recommendedModel,
|
|
58
|
+
reason: 'No historical data available.',
|
|
59
|
+
adjusted: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const successCount = similar.filter((o) => o.success).length;
|
|
63
|
+
const failCount = similar.length - successCount;
|
|
64
|
+
const successRate = successCount / similar.length;
|
|
65
|
+
// Check if a lighter model was consistently successful
|
|
66
|
+
const lighterSuccesses = similar.filter((o) => o.success && tierIndex(o.modelUsed) < tierIndex(recommendedModel));
|
|
67
|
+
if (lighterSuccesses.length >= 2 && successRate > 0.8) {
|
|
68
|
+
const lighterTier = tierByIndex(tierIndex(recommendedModel) - 1);
|
|
69
|
+
return {
|
|
70
|
+
adjustedModel: lighterTier,
|
|
71
|
+
reason: `Historical data shows a lighter model (${lighterTier}) succeeded ${String(lighterSuccesses.length)} times for similar tasks.`,
|
|
72
|
+
adjusted: true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Check if the recommended model or lighter models frequently failed
|
|
76
|
+
const recFailures = similar.filter((o) => !o.success && tierIndex(o.modelUsed) <= tierIndex(recommendedModel));
|
|
77
|
+
if (recFailures.length >= 2 && failCount > successCount) {
|
|
78
|
+
const heavierTier = tierByIndex(tierIndex(recommendedModel) + 1);
|
|
79
|
+
return {
|
|
80
|
+
adjustedModel: heavierTier,
|
|
81
|
+
reason: `Historical data shows ${String(recFailures.length)} failures at tier ${recommendedModel} or below — upgrading to ${heavierTier}.`,
|
|
82
|
+
adjusted: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
adjustedModel: recommendedModel,
|
|
87
|
+
reason: 'Historical data supports the current recommendation.',
|
|
88
|
+
adjusted: false,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=historical-learner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historical-learner.js","sourceRoot":"","sources":["../../../src/engine/model-router/historical-learner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComplexitySignals, RoutingRule, RuleCondition, RuleMatch, RulesEngineResult } from '../../types/index.js';
|
|
2
|
+
/** Evaluate a single condition against a signal value. */
|
|
3
|
+
export declare function evaluateCondition(signals: ComplexitySignals, condition: RuleCondition): boolean;
|
|
4
|
+
/** Evaluate a single rule against the given signals. */
|
|
5
|
+
export declare function evaluateRule(signals: ComplexitySignals, rule: RoutingRule): RuleMatch;
|
|
6
|
+
/** Load rules from the built-in config. Custom rules support is left as a hook. */
|
|
7
|
+
export declare function loadRules(customRules?: RoutingRule[]): RoutingRule[];
|
|
8
|
+
/**
|
|
9
|
+
* Evaluate all rules against the given signals and return the best model
|
|
10
|
+
* recommendation with alternatives.
|
|
11
|
+
*/
|
|
12
|
+
export declare function evaluateRules(signals: ComplexitySignals, customRules?: RoutingRule[]): RulesEngineResult;
|
|
13
|
+
//# sourceMappingURL=rules-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-engine.d.ts","sourceRoot":"","sources":["../../../src/engine/model-router/rules-engine.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAEjB,WAAW,EAEX,aAAa,EACb,SAAS,EACT,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAyB9B,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CA6B/F;AAMD,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,CAWrF;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAQpE;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,EAAE,GAC1B,iBAAiB,CAmDnB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// engine/model-router/rules-engine.ts — Deterministic rules evaluation for model routing.
|
|
2
|
+
// SPEC-076 AC-02
|
|
3
|
+
import defaultRulesJson from '../../config/model-routing-rules.json' with { type: 'json' };
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Model tier ordering (for tie-breaking: higher index = more capable)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const TIER_ORDER = ['haiku', 'sonnet', 'opus'];
|
|
8
|
+
function tierIndex(tier) {
|
|
9
|
+
return TIER_ORDER.indexOf(tier);
|
|
10
|
+
}
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Condition evaluation
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
function getSignalValue(signals, signal) {
|
|
15
|
+
return signals[signal];
|
|
16
|
+
}
|
|
17
|
+
/** Evaluate a single condition against a signal value. */
|
|
18
|
+
export function evaluateCondition(signals, condition) {
|
|
19
|
+
const raw = getSignalValue(signals, condition.signal);
|
|
20
|
+
if (raw === undefined) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const { operator, value } = condition;
|
|
24
|
+
switch (operator) {
|
|
25
|
+
case 'eq':
|
|
26
|
+
return raw === value;
|
|
27
|
+
case 'gt':
|
|
28
|
+
return typeof raw === 'number' && typeof value === 'number' && raw > value;
|
|
29
|
+
case 'gte':
|
|
30
|
+
return typeof raw === 'number' && typeof value === 'number' && raw >= value;
|
|
31
|
+
case 'lt':
|
|
32
|
+
return typeof raw === 'number' && typeof value === 'number' && raw < value;
|
|
33
|
+
case 'lte':
|
|
34
|
+
return typeof raw === 'number' && typeof value === 'number' && raw <= value;
|
|
35
|
+
case 'between': {
|
|
36
|
+
if (typeof raw !== 'number' || !Array.isArray(value)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const [lo, hi] = value;
|
|
40
|
+
return raw >= lo && raw <= hi;
|
|
41
|
+
}
|
|
42
|
+
default:
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Rule evaluation
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Evaluate a single rule against the given signals. */
|
|
50
|
+
export function evaluateRule(signals, rule) {
|
|
51
|
+
// An empty conditions list means "always match" (catch-all)
|
|
52
|
+
if (rule.conditions.length === 0) {
|
|
53
|
+
return { rule, matchRatio: 1, confidence: rule.weight };
|
|
54
|
+
}
|
|
55
|
+
const matches = rule.conditions.filter((c) => evaluateCondition(signals, c));
|
|
56
|
+
const matchRatio = matches.length / rule.conditions.length;
|
|
57
|
+
const confidence = matchRatio * rule.weight;
|
|
58
|
+
return { rule, matchRatio, confidence };
|
|
59
|
+
}
|
|
60
|
+
/** Load rules from the built-in config. Custom rules support is left as a hook. */
|
|
61
|
+
export function loadRules(customRules) {
|
|
62
|
+
const config = defaultRulesJson;
|
|
63
|
+
const base = config.rules;
|
|
64
|
+
if (!customRules || customRules.length === 0) {
|
|
65
|
+
return base;
|
|
66
|
+
}
|
|
67
|
+
// Custom rules have higher priority: prepend them
|
|
68
|
+
return [...customRules, ...base];
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Main evaluation
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Evaluate all rules against the given signals and return the best model
|
|
75
|
+
* recommendation with alternatives.
|
|
76
|
+
*/
|
|
77
|
+
export function evaluateRules(signals, customRules) {
|
|
78
|
+
const rules = loadRules(customRules);
|
|
79
|
+
const matches = rules.map((r) => evaluateRule(signals, r));
|
|
80
|
+
// Only keep matches where at least all conditions matched (matchRatio === 1)
|
|
81
|
+
// unless no rule fully matches, then relax to best partial match
|
|
82
|
+
let fullMatches = matches.filter((m) => m.matchRatio === 1 && m.confidence > 0);
|
|
83
|
+
if (fullMatches.length === 0) {
|
|
84
|
+
fullMatches = matches.filter((m) => m.confidence > 0);
|
|
85
|
+
}
|
|
86
|
+
// Sort by confidence desc, then by tier capability desc (tie-break: more capable wins)
|
|
87
|
+
fullMatches.sort((a, b) => {
|
|
88
|
+
const confDiff = b.confidence - a.confidence;
|
|
89
|
+
if (Math.abs(confDiff) > 0.001) {
|
|
90
|
+
return confDiff;
|
|
91
|
+
}
|
|
92
|
+
return tierIndex(b.rule.result) - tierIndex(a.rule.result);
|
|
93
|
+
});
|
|
94
|
+
const best = fullMatches[0];
|
|
95
|
+
/* c8 ignore next 5 */
|
|
96
|
+
if (!best) {
|
|
97
|
+
// Absolute fallback — should never happen because of the catch-all sonnet rule
|
|
98
|
+
return {
|
|
99
|
+
model: 'sonnet',
|
|
100
|
+
confidence: 0.5,
|
|
101
|
+
reasoning: ['Default: sonnet (no rule matched)'],
|
|
102
|
+
alternatives: [],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const reasoning = buildReasoning(best, signals);
|
|
106
|
+
// Collect alternatives (different model tiers from other matches)
|
|
107
|
+
const seen = new Set([best.rule.result]);
|
|
108
|
+
const alternatives = [];
|
|
109
|
+
for (const m of fullMatches) {
|
|
110
|
+
if (!seen.has(m.rule.result)) {
|
|
111
|
+
seen.add(m.rule.result);
|
|
112
|
+
alternatives.push({ model: m.rule.result, confidence: m.confidence });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
model: best.rule.result,
|
|
117
|
+
confidence: best.confidence,
|
|
118
|
+
reasoning,
|
|
119
|
+
alternatives,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Reasoning builder
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
function buildReasoning(match, signals) {
|
|
126
|
+
const reasons = [];
|
|
127
|
+
reasons.push(`Rule "${match.rule.name}" matched with confidence ${match.confidence.toFixed(2)}.`);
|
|
128
|
+
if (signals.criteriaCount > 15) {
|
|
129
|
+
reasons.push(`High criteria count (${String(signals.criteriaCount)}) suggests a complex task.`);
|
|
130
|
+
}
|
|
131
|
+
if (signals.crossLayerCount > 2) {
|
|
132
|
+
reasons.push(`Cross-layer changes (${String(signals.crossLayerCount)} layers) increase coordination complexity.`);
|
|
133
|
+
}
|
|
134
|
+
if (signals.isReadOnly) {
|
|
135
|
+
reasons.push('Read-only task — a lighter model can handle this efficiently.');
|
|
136
|
+
}
|
|
137
|
+
if (signals.hasSecurityImplications) {
|
|
138
|
+
reasons.push('Security implications detected — a more capable model is recommended.');
|
|
139
|
+
}
|
|
140
|
+
return reasons;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=rules-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-engine.js","sourceRoot":"","sources":["../../../src/engine/model-router/rules-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CriterionCoverage, CoverageTestFile, RawCriterion } from '../../types/index.js';
|
|
2
|
+
export { extractKeywords } from './keyword-extractor.js';
|
|
2
3
|
/**
|
|
3
4
|
* Parses HU.md content and returns a list of acceptance criteria with their
|
|
4
5
|
* identifiers and descriptions. Supports flexible AC formats:
|
|
@@ -13,6 +14,4 @@ export declare function extractCriteria(huContent: string): readonly RawCriterio
|
|
|
13
14
|
* defined in SPEC-068. Returns the full CriterionCoverage for that criterion.
|
|
14
15
|
*/
|
|
15
16
|
export declare function mapTestsToCriterion(criterion: RawCriterion, testFiles: readonly CoverageTestFile[], testContents: ReadonlyMap<string, string>, specId: string): CriterionCoverage;
|
|
16
|
-
/** Extracts 3–5 meaningful keywords from an AC description text. */
|
|
17
|
-
export declare function extractKeywords(text: string): readonly string[];
|
|
18
17
|
//# sourceMappingURL=criteria-mapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"criteria-mapper.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-coverage/criteria-mapper.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAIjB,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"criteria-mapper.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-coverage/criteria-mapper.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAIjB,gBAAgB,EAChB,YAAY,EACb,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAMzD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,YAAY,EAAE,CAmC1E;AAMD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,SAAS,gBAAgB,EAAE,EACtC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,MAAM,EAAE,MAAM,GACb,iBAAiB,CAsCnB"}
|
|
@@ -1,78 +1,9 @@
|
|
|
1
1
|
// engine/spec-coverage/criteria-mapper.ts — Extracts acceptance criteria from
|
|
2
2
|
// HU.md and maps test files to each criterion using 3 strategies.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
'the',
|
|
8
|
-
'a',
|
|
9
|
-
'an',
|
|
10
|
-
'is',
|
|
11
|
-
'are',
|
|
12
|
-
'was',
|
|
13
|
-
'were',
|
|
14
|
-
'be',
|
|
15
|
-
'been',
|
|
16
|
-
'being',
|
|
17
|
-
'have',
|
|
18
|
-
'has',
|
|
19
|
-
'had',
|
|
20
|
-
'do',
|
|
21
|
-
'does',
|
|
22
|
-
'did',
|
|
23
|
-
'will',
|
|
24
|
-
'would',
|
|
25
|
-
'could',
|
|
26
|
-
'should',
|
|
27
|
-
'may',
|
|
28
|
-
'might',
|
|
29
|
-
'shall',
|
|
30
|
-
'must',
|
|
31
|
-
'can',
|
|
32
|
-
'to',
|
|
33
|
-
'of',
|
|
34
|
-
'in',
|
|
35
|
-
'on',
|
|
36
|
-
'at',
|
|
37
|
-
'by',
|
|
38
|
-
'for',
|
|
39
|
-
'with',
|
|
40
|
-
'from',
|
|
41
|
-
'as',
|
|
42
|
-
'and',
|
|
43
|
-
'or',
|
|
44
|
-
'but',
|
|
45
|
-
'not',
|
|
46
|
-
'no',
|
|
47
|
-
'if',
|
|
48
|
-
'when',
|
|
49
|
-
'where',
|
|
50
|
-
'that',
|
|
51
|
-
'which',
|
|
52
|
-
'this',
|
|
53
|
-
'it',
|
|
54
|
-
'its',
|
|
55
|
-
'el',
|
|
56
|
-
'la',
|
|
57
|
-
'los',
|
|
58
|
-
'las',
|
|
59
|
-
'un',
|
|
60
|
-
'una',
|
|
61
|
-
'de',
|
|
62
|
-
'del',
|
|
63
|
-
'en',
|
|
64
|
-
'que',
|
|
65
|
-
'se',
|
|
66
|
-
'por',
|
|
67
|
-
'para',
|
|
68
|
-
'con',
|
|
69
|
-
'al',
|
|
70
|
-
'lo',
|
|
71
|
-
'le',
|
|
72
|
-
'les',
|
|
73
|
-
'y',
|
|
74
|
-
'o',
|
|
75
|
-
]);
|
|
3
|
+
import { findDirectReferences, findSpecReferences, findKeywordMatches } from './test-matchers.js';
|
|
4
|
+
import { extractKeywords } from './keyword-extractor.js';
|
|
5
|
+
// Re-export for backward compatibility
|
|
6
|
+
export { extractKeywords } from './keyword-extractor.js';
|
|
76
7
|
// ---------------------------------------------------------------------------
|
|
77
8
|
// AC extraction
|
|
78
9
|
// ---------------------------------------------------------------------------
|
|
@@ -152,136 +83,6 @@ export function mapTestsToCriterion(criterion, testFiles, testContents, specId)
|
|
|
152
83
|
};
|
|
153
84
|
}
|
|
154
85
|
// ---------------------------------------------------------------------------
|
|
155
|
-
// Strategy implementations
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
/** Strategy 1: look for AC-ID or AC ID (with space or no dash) in test content. */
|
|
158
|
-
function findDirectReferences(acId, testFiles, testContents) {
|
|
159
|
-
const matches = [];
|
|
160
|
-
// Build regex variants: AC-01, AC01, #AC-01, AC 01
|
|
161
|
-
const acNum = acId.replace('AC-', '');
|
|
162
|
-
const pattern = new RegExp(`\\b(?:AC[-\\s]?${acNum}|#AC-?${acNum})\\b`, 'i');
|
|
163
|
-
for (const tf of testFiles) {
|
|
164
|
-
const content = testContents.get(tf.file) ?? '';
|
|
165
|
-
if (!pattern.test(content)) {
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
// Extract the test name from the first matching line
|
|
169
|
-
const testName = extractTestName(content, pattern);
|
|
170
|
-
matches.push({ file: tf.file, testName, strategy: 'direct-reference' });
|
|
171
|
-
}
|
|
172
|
-
return matches;
|
|
173
|
-
}
|
|
174
|
-
/** Strategy 2: look for SPEC-ID in file path or content. */
|
|
175
|
-
function findSpecReferences(specId, testFiles, testContents) {
|
|
176
|
-
const matches = [];
|
|
177
|
-
const specPattern = new RegExp(`\\b${specId.replace('-', '[-_]?')}\\b`, 'i');
|
|
178
|
-
for (const tf of testFiles) {
|
|
179
|
-
const inPath = specPattern.test(tf.file);
|
|
180
|
-
const content = testContents.get(tf.file) ?? '';
|
|
181
|
-
const inContent = specPattern.test(content);
|
|
182
|
-
if (!inPath && !inContent) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
const testName = inContent ? extractTestName(content, specPattern) : tf.file;
|
|
186
|
-
matches.push({ file: tf.file, testName, strategy: 'spec-reference' });
|
|
187
|
-
}
|
|
188
|
-
return matches;
|
|
189
|
-
}
|
|
190
|
-
/** Strategy 3: match keywords from criterion text against test names in files. */
|
|
191
|
-
function findKeywordMatches(keywords, testFiles, testContents) {
|
|
192
|
-
const matches = [];
|
|
193
|
-
for (const tf of testFiles) {
|
|
194
|
-
const content = testContents.get(tf.file) ?? '';
|
|
195
|
-
// Extract all describe/it/test block names
|
|
196
|
-
const testNames = extractAllTestNames(content);
|
|
197
|
-
const combinedText = [...testNames, tf.file].join(' ').toLowerCase();
|
|
198
|
-
const matchCount = keywords.filter((kw) => combinedText.includes(kw.toLowerCase())).length;
|
|
199
|
-
if (matchCount >= 2) {
|
|
200
|
-
matches.push({
|
|
201
|
-
file: tf.file,
|
|
202
|
-
/* v8 ignore next 1 */
|
|
203
|
-
testName: testNames[0] ?? tf.file,
|
|
204
|
-
strategy: 'heuristic',
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return matches;
|
|
209
|
-
}
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
// Keyword extraction
|
|
212
|
-
// ---------------------------------------------------------------------------
|
|
213
|
-
/** Extracts 3–5 meaningful keywords from an AC description text. */
|
|
214
|
-
export function extractKeywords(text) {
|
|
215
|
-
const words = text
|
|
216
|
-
.toLowerCase()
|
|
217
|
-
.replace(/[^a-zA-Z0-9\s_àáéíóúñüÀÁÉÍÓÚÑÜ]/g, ' ')
|
|
218
|
-
.split(/\s+/)
|
|
219
|
-
.filter((w) => w.length >= 3 && !STOP_WORDS.has(w));
|
|
220
|
-
// De-duplicate preserving order
|
|
221
|
-
const seen = new Set();
|
|
222
|
-
const unique = [];
|
|
223
|
-
for (const w of words) {
|
|
224
|
-
if (!seen.has(w)) {
|
|
225
|
-
seen.add(w);
|
|
226
|
-
unique.push(w);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return unique.slice(0, 5);
|
|
230
|
-
}
|
|
231
|
-
// ---------------------------------------------------------------------------
|
|
232
|
-
// Test name extraction helpers
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
|
-
/** Patterns for extracting test block names from source code. */
|
|
235
|
-
const TEST_NAME_PATTERNS = [
|
|
236
|
-
/(?:describe|it|test|context|suite|feature|scenario)\s*\(\s*['"`](.*?)['"`]/g,
|
|
237
|
-
/(?:func\s+Test\w+)\s*\(/g, // Go: func TestXxx(
|
|
238
|
-
/(?:def\s+test_\w+)\s*\(/g, // Python: def test_xxx(
|
|
239
|
-
/#\[test\]\s*(?:fn\s+(\w+))/g, // Rust: #[test] fn name
|
|
240
|
-
/(?:func|fun)\s+(test\w+)\s*\(/gi, // Swift/Kotlin
|
|
241
|
-
];
|
|
242
|
-
/** Extracts the first matching test name from source code. */
|
|
243
|
-
function extractTestName(content, nearPattern) {
|
|
244
|
-
// Find the line with the pattern match, then look for a nearby test name
|
|
245
|
-
const lines = content.split('\n');
|
|
246
|
-
for (let i = 0; i < lines.length; i++) {
|
|
247
|
-
/* v8 ignore next 1 */
|
|
248
|
-
const line = lines[i] ?? '';
|
|
249
|
-
if (!nearPattern.test(line)) {
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
// Look in ±5 lines for a test name
|
|
253
|
-
const start = Math.max(0, i - 5);
|
|
254
|
-
const end = Math.min(lines.length - 1, i + 5);
|
|
255
|
-
for (let j = start; j <= end; j++) {
|
|
256
|
-
/* v8 ignore next 1 */
|
|
257
|
-
const nearby = lines[j] ?? '';
|
|
258
|
-
const nameMatch = /(?:describe|it|test|context)\s*\(\s*['"`](.*?)['"`]/.exec(nearby);
|
|
259
|
-
if (nameMatch?.[1]) {
|
|
260
|
-
return nameMatch[1];
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return line.trim().slice(0, 80);
|
|
264
|
-
}
|
|
265
|
-
return '';
|
|
266
|
-
}
|
|
267
|
-
/** Extracts all test/describe block names from source code. */
|
|
268
|
-
function extractAllTestNames(content) {
|
|
269
|
-
const names = [];
|
|
270
|
-
for (const pattern of TEST_NAME_PATTERNS) {
|
|
271
|
-
pattern.lastIndex = 0;
|
|
272
|
-
let match;
|
|
273
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
274
|
-
/* v8 ignore next 1 */
|
|
275
|
-
const name = match[1] ?? match[0];
|
|
276
|
-
/* v8 ignore next 1 */
|
|
277
|
-
if (name) {
|
|
278
|
-
names.push(name);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return names;
|
|
283
|
-
}
|
|
284
|
-
// ---------------------------------------------------------------------------
|
|
285
86
|
// Result builder
|
|
286
87
|
// ---------------------------------------------------------------------------
|
|
287
88
|
function buildResult(acId, description, status, strategy, rawMatches, testFiles) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"criteria-mapper.js","sourceRoot":"","sources":["../../../src/engine/spec-coverage/criteria-mapper.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kEAAkE;
|
|
1
|
+
{"version":3,"file":"criteria-mapper.js","sourceRoot":"","sources":["../../../src/engine/spec-coverage/criteria-mapper.ts"],"names":[],"mappings":"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"}
|