pull-request-split-advisor 3.1.2
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/LICENSE +52 -0
- package/README.md +168 -0
- package/dist/ai/config-wizard.js +282 -0
- package/dist/ai/enricher.js +290 -0
- package/dist/ai/prompts.js +231 -0
- package/dist/ai/provider.js +265 -0
- package/dist/cli.js +442 -0
- package/dist/config/config.js +315 -0
- package/dist/config/default-config.js +223 -0
- package/dist/core/blocks.js +145 -0
- package/dist/core/commit-planner.js +273 -0
- package/dist/core/dependency.js +284 -0
- package/dist/core/file-stats.js +341 -0
- package/dist/core/history.js +72 -0
- package/dist/core/planner.js +25 -0
- package/dist/core/scoring.js +166 -0
- package/dist/core/strategy.js +486 -0
- package/dist/git/branch-naming.js +120 -0
- package/dist/git/executor.js +378 -0
- package/dist/git/git.js +239 -0
- package/dist/output/report-styles.generated.js +10 -0
- package/dist/output/report.js +726 -0
- package/dist/output/ui.js +417 -0
- package/dist/shared/constants.js +59 -0
- package/dist/shared/types.js +7 -0
- package/dist/shared/utils.js +73 -0
- package/node_modules/@colors/colors/LICENSE +26 -0
- package/node_modules/@colors/colors/README.md +219 -0
- package/node_modules/@colors/colors/examples/normal-usage.js +83 -0
- package/node_modules/@colors/colors/examples/safe-string.js +80 -0
- package/node_modules/@colors/colors/index.d.ts +136 -0
- package/node_modules/@colors/colors/lib/colors.js +211 -0
- package/node_modules/@colors/colors/lib/custom/trap.js +46 -0
- package/node_modules/@colors/colors/lib/custom/zalgo.js +110 -0
- package/node_modules/@colors/colors/lib/extendStringPrototype.js +110 -0
- package/node_modules/@colors/colors/lib/index.js +13 -0
- package/node_modules/@colors/colors/lib/maps/america.js +10 -0
- package/node_modules/@colors/colors/lib/maps/rainbow.js +12 -0
- package/node_modules/@colors/colors/lib/maps/random.js +11 -0
- package/node_modules/@colors/colors/lib/maps/zebra.js +5 -0
- package/node_modules/@colors/colors/lib/styles.js +95 -0
- package/node_modules/@colors/colors/lib/system/has-flag.js +35 -0
- package/node_modules/@colors/colors/lib/system/supports-colors.js +151 -0
- package/node_modules/@colors/colors/package.json +45 -0
- package/node_modules/@colors/colors/safe.d.ts +48 -0
- package/node_modules/@colors/colors/safe.js +10 -0
- package/node_modules/@colors/colors/themes/generic-logging.js +12 -0
- package/node_modules/ansi-align/LICENSE +13 -0
- package/node_modules/ansi-align/README.md +80 -0
- package/node_modules/ansi-align/index.js +61 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/index.d.ts +37 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/index.js +10 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/license +9 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/package.json +55 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/readme.md +78 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/README.md +73 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/es2015/index.js +6 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/es2015/text.js +6 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/index.d.ts +23 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/index.js +6 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/package.json +50 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/text.js +6 -0
- package/node_modules/ansi-align/node_modules/string-width/index.d.ts +29 -0
- package/node_modules/ansi-align/node_modules/string-width/index.js +47 -0
- package/node_modules/ansi-align/node_modules/string-width/license +9 -0
- package/node_modules/ansi-align/node_modules/string-width/package.json +56 -0
- package/node_modules/ansi-align/node_modules/string-width/readme.md +50 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/index.d.ts +17 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/index.js +4 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/license +9 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/package.json +54 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/readme.md +46 -0
- package/node_modules/ansi-align/package.json +43 -0
- package/node_modules/ansi-regex/index.d.ts +33 -0
- package/node_modules/ansi-regex/index.js +14 -0
- package/node_modules/ansi-regex/license +9 -0
- package/node_modules/ansi-regex/package.json +61 -0
- package/node_modules/ansi-regex/readme.md +66 -0
- package/node_modules/ansi-styles/index.d.ts +236 -0
- package/node_modules/ansi-styles/index.js +223 -0
- package/node_modules/ansi-styles/license +9 -0
- package/node_modules/ansi-styles/package.json +54 -0
- package/node_modules/ansi-styles/readme.md +173 -0
- package/node_modules/boxen/index.d.ts +267 -0
- package/node_modules/boxen/index.js +376 -0
- package/node_modules/boxen/license +9 -0
- package/node_modules/boxen/package.json +69 -0
- package/node_modules/boxen/readme.md +300 -0
- package/node_modules/camelcase/index.d.ts +102 -0
- package/node_modules/camelcase/index.js +110 -0
- package/node_modules/camelcase/license +9 -0
- package/node_modules/camelcase/package.json +47 -0
- package/node_modules/camelcase/readme.md +135 -0
- package/node_modules/chalk/license +9 -0
- package/node_modules/chalk/package.json +83 -0
- package/node_modules/chalk/readme.md +297 -0
- package/node_modules/chalk/source/index.d.ts +325 -0
- package/node_modules/chalk/source/index.js +225 -0
- package/node_modules/chalk/source/utilities.js +33 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/node_modules/cli-boxes/boxes.json +82 -0
- package/node_modules/cli-boxes/index.d.ts +127 -0
- package/node_modules/cli-boxes/index.js +6 -0
- package/node_modules/cli-boxes/license +9 -0
- package/node_modules/cli-boxes/package.json +42 -0
- package/node_modules/cli-boxes/readme.md +115 -0
- package/node_modules/cli-table3/LICENSE +21 -0
- package/node_modules/cli-table3/README.md +236 -0
- package/node_modules/cli-table3/index.d.ts +96 -0
- package/node_modules/cli-table3/index.js +1 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/index.d.ts +37 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/index.js +10 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/license +9 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/package.json +55 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/readme.md +78 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/README.md +73 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/es2015/index.js +6 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/es2015/text.js +6 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/index.d.ts +23 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/index.js +6 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/package.json +50 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/text.js +6 -0
- package/node_modules/cli-table3/node_modules/string-width/index.d.ts +29 -0
- package/node_modules/cli-table3/node_modules/string-width/index.js +47 -0
- package/node_modules/cli-table3/node_modules/string-width/license +9 -0
- package/node_modules/cli-table3/node_modules/string-width/package.json +56 -0
- package/node_modules/cli-table3/node_modules/string-width/readme.md +50 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/index.d.ts +17 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/index.js +4 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/license +9 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/package.json +54 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/readme.md +46 -0
- package/node_modules/cli-table3/package.json +100 -0
- package/node_modules/cli-table3/src/cell.js +409 -0
- package/node_modules/cli-table3/src/debug.js +28 -0
- package/node_modules/cli-table3/src/layout-manager.js +254 -0
- package/node_modules/cli-table3/src/table.js +106 -0
- package/node_modules/cli-table3/src/utils.js +344 -0
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1157 -0
- package/node_modules/commander/esm.mjs +16 -0
- package/node_modules/commander/index.js +24 -0
- package/node_modules/commander/lib/argument.js +149 -0
- package/node_modules/commander/lib/command.js +2509 -0
- package/node_modules/commander/lib/error.js +39 -0
- package/node_modules/commander/lib/help.js +520 -0
- package/node_modules/commander/lib/option.js +330 -0
- package/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/node_modules/commander/package-support.json +16 -0
- package/node_modules/commander/package.json +84 -0
- package/node_modules/commander/typings/esm.d.mts +3 -0
- package/node_modules/commander/typings/index.d.ts +969 -0
- package/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/emoji-regex/README.md +107 -0
- package/node_modules/emoji-regex/index.d.ts +3 -0
- package/node_modules/emoji-regex/index.js +4 -0
- package/node_modules/emoji-regex/index.mjs +4 -0
- package/node_modules/emoji-regex/package.json +45 -0
- package/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/node_modules/get-east-asian-width/index.js +30 -0
- package/node_modules/get-east-asian-width/license +9 -0
- package/node_modules/get-east-asian-width/lookup-data.js +18 -0
- package/node_modules/get-east-asian-width/lookup.js +135 -0
- package/node_modules/get-east-asian-width/package.json +71 -0
- package/node_modules/get-east-asian-width/readme.md +65 -0
- package/node_modules/get-east-asian-width/utilities.js +24 -0
- package/node_modules/is-fullwidth-code-point/index.d.ts +17 -0
- package/node_modules/is-fullwidth-code-point/index.js +50 -0
- package/node_modules/is-fullwidth-code-point/license +9 -0
- package/node_modules/is-fullwidth-code-point/package.json +42 -0
- package/node_modules/is-fullwidth-code-point/readme.md +39 -0
- package/node_modules/isbinaryfile/LICENSE.txt +22 -0
- package/node_modules/isbinaryfile/README.md +70 -0
- package/node_modules/isbinaryfile/lib/index.d.ts +3 -0
- package/node_modules/isbinaryfile/lib/index.js +256 -0
- package/node_modules/isbinaryfile/package.json +64 -0
- package/node_modules/string-width/index.d.ts +39 -0
- package/node_modules/string-width/index.js +82 -0
- package/node_modules/string-width/license +9 -0
- package/node_modules/string-width/package.json +64 -0
- package/node_modules/string-width/readme.md +66 -0
- package/node_modules/strip-ansi/index.d.ts +15 -0
- package/node_modules/strip-ansi/index.js +19 -0
- package/node_modules/strip-ansi/license +9 -0
- package/node_modules/strip-ansi/package.json +59 -0
- package/node_modules/strip-ansi/readme.md +37 -0
- package/node_modules/type-fest/index.d.ts +178 -0
- package/node_modules/type-fest/license-cc0 +121 -0
- package/node_modules/type-fest/license-mit +9 -0
- package/node_modules/type-fest/package.json +91 -0
- package/node_modules/type-fest/readme.md +1060 -0
- package/node_modules/type-fest/source/all-union-fields.d.ts +88 -0
- package/node_modules/type-fest/source/and.d.ts +25 -0
- package/node_modules/type-fest/source/array-indices.d.ts +23 -0
- package/node_modules/type-fest/source/array-slice.d.ts +109 -0
- package/node_modules/type-fest/source/array-splice.d.ts +99 -0
- package/node_modules/type-fest/source/array-tail.d.ts +76 -0
- package/node_modules/type-fest/source/array-values.d.ts +22 -0
- package/node_modules/type-fest/source/arrayable.d.ts +29 -0
- package/node_modules/type-fest/source/async-return-type.d.ts +23 -0
- package/node_modules/type-fest/source/asyncify.d.ts +32 -0
- package/node_modules/type-fest/source/basic.d.ts +68 -0
- package/node_modules/type-fest/source/camel-case.d.ts +89 -0
- package/node_modules/type-fest/source/camel-cased-properties-deep.d.ts +97 -0
- package/node_modules/type-fest/source/camel-cased-properties.d.ts +43 -0
- package/node_modules/type-fest/source/conditional-except.d.ts +45 -0
- package/node_modules/type-fest/source/conditional-keys.d.ts +47 -0
- package/node_modules/type-fest/source/conditional-pick-deep.d.ts +118 -0
- package/node_modules/type-fest/source/conditional-pick.d.ts +44 -0
- package/node_modules/type-fest/source/conditional-simplify.d.ts +32 -0
- package/node_modules/type-fest/source/delimiter-case.d.ts +78 -0
- package/node_modules/type-fest/source/delimiter-cased-properties-deep.d.ts +106 -0
- package/node_modules/type-fest/source/delimiter-cased-properties.d.ts +46 -0
- package/node_modules/type-fest/source/distributed-omit.d.ts +89 -0
- package/node_modules/type-fest/source/distributed-pick.d.ts +85 -0
- package/node_modules/type-fest/source/empty-object.d.ts +46 -0
- package/node_modules/type-fest/source/enforce-optional.d.ts +47 -0
- package/node_modules/type-fest/source/entries.d.ts +62 -0
- package/node_modules/type-fest/source/entry.d.ts +65 -0
- package/node_modules/type-fest/source/exact.d.ts +68 -0
- package/node_modules/type-fest/source/except.d.ts +108 -0
- package/node_modules/type-fest/source/find-global-type.d.ts +64 -0
- package/node_modules/type-fest/source/fixed-length-array.d.ts +43 -0
- package/node_modules/type-fest/source/get.d.ts +219 -0
- package/node_modules/type-fest/source/global-this.d.ts +21 -0
- package/node_modules/type-fest/source/greater-than-or-equal.d.ts +22 -0
- package/node_modules/type-fest/source/greater-than.d.ts +56 -0
- package/node_modules/type-fest/source/has-optional-keys.d.ts +21 -0
- package/node_modules/type-fest/source/has-readonly-keys.d.ts +21 -0
- package/node_modules/type-fest/source/has-required-keys.d.ts +59 -0
- package/node_modules/type-fest/source/has-writable-keys.d.ts +21 -0
- package/node_modules/type-fest/source/if-any.d.ts +24 -0
- package/node_modules/type-fest/source/if-empty-object.d.ts +26 -0
- package/node_modules/type-fest/source/if-never.d.ts +24 -0
- package/node_modules/type-fest/source/if-null.d.ts +24 -0
- package/node_modules/type-fest/source/if-unknown.d.ts +24 -0
- package/node_modules/type-fest/source/includes.d.ts +22 -0
- package/node_modules/type-fest/source/int-closed-range.d.ts +35 -0
- package/node_modules/type-fest/source/int-range.d.ts +55 -0
- package/node_modules/type-fest/source/internal/array.d.ts +126 -0
- package/node_modules/type-fest/source/internal/characters.d.ts +67 -0
- package/node_modules/type-fest/source/internal/index.d.ts +8 -0
- package/node_modules/type-fest/source/internal/keys.d.ts +97 -0
- package/node_modules/type-fest/source/internal/numeric.d.ts +118 -0
- package/node_modules/type-fest/source/internal/object.d.ts +236 -0
- package/node_modules/type-fest/source/internal/string.d.ts +210 -0
- package/node_modules/type-fest/source/internal/tuple.d.ts +90 -0
- package/node_modules/type-fest/source/internal/type.d.ts +139 -0
- package/node_modules/type-fest/source/invariant-of.d.ts +76 -0
- package/node_modules/type-fest/source/is-any.d.ts +33 -0
- package/node_modules/type-fest/source/is-equal.d.ts +31 -0
- package/node_modules/type-fest/source/is-float.d.ts +41 -0
- package/node_modules/type-fest/source/is-integer.d.ts +58 -0
- package/node_modules/type-fest/source/is-literal.d.ts +296 -0
- package/node_modules/type-fest/source/is-never.d.ts +42 -0
- package/node_modules/type-fest/source/is-null.d.ts +20 -0
- package/node_modules/type-fest/source/is-tuple.d.ts +89 -0
- package/node_modules/type-fest/source/is-unknown.d.ts +52 -0
- package/node_modules/type-fest/source/iterable-element.d.ts +64 -0
- package/node_modules/type-fest/source/join.d.ts +68 -0
- package/node_modules/type-fest/source/jsonifiable.d.ts +37 -0
- package/node_modules/type-fest/source/jsonify.d.ts +122 -0
- package/node_modules/type-fest/source/kebab-case.d.ts +44 -0
- package/node_modules/type-fest/source/kebab-cased-properties-deep.d.ts +63 -0
- package/node_modules/type-fest/source/kebab-cased-properties.d.ts +40 -0
- package/node_modules/type-fest/source/keys-of-union.d.ts +42 -0
- package/node_modules/type-fest/source/last-array-element.d.ts +38 -0
- package/node_modules/type-fest/source/less-than-or-equal.d.ts +22 -0
- package/node_modules/type-fest/source/less-than.d.ts +26 -0
- package/node_modules/type-fest/source/literal-to-primitive-deep.d.ts +36 -0
- package/node_modules/type-fest/source/literal-to-primitive.d.ts +36 -0
- package/node_modules/type-fest/source/literal-union.d.ts +37 -0
- package/node_modules/type-fest/source/merge-deep.d.ts +486 -0
- package/node_modules/type-fest/source/merge-exclusive.d.ts +41 -0
- package/node_modules/type-fest/source/merge.d.ts +48 -0
- package/node_modules/type-fest/source/multidimensional-array.d.ts +44 -0
- package/node_modules/type-fest/source/multidimensional-readonly-array.d.ts +48 -0
- package/node_modules/type-fest/source/non-empty-object.d.ts +35 -0
- package/node_modules/type-fest/source/non-empty-string.d.ts +28 -0
- package/node_modules/type-fest/source/non-empty-tuple.d.ts +21 -0
- package/node_modules/type-fest/source/numeric.d.ts +222 -0
- package/node_modules/type-fest/source/observable-like.d.ts +63 -0
- package/node_modules/type-fest/source/omit-deep.d.ts +167 -0
- package/node_modules/type-fest/source/omit-index-signature.d.ts +95 -0
- package/node_modules/type-fest/source/opaque.d.ts +1 -0
- package/node_modules/type-fest/source/optional-keys-of.d.ts +39 -0
- package/node_modules/type-fest/source/or.d.ts +25 -0
- package/node_modules/type-fest/source/override-properties.d.ts +36 -0
- package/node_modules/type-fest/source/package-json.d.ts +676 -0
- package/node_modules/type-fest/source/partial-deep.d.ts +151 -0
- package/node_modules/type-fest/source/partial-on-undefined-deep.d.ts +78 -0
- package/node_modules/type-fest/source/pascal-case.d.ts +42 -0
- package/node_modules/type-fest/source/pascal-cased-properties-deep.d.ts +62 -0
- package/node_modules/type-fest/source/pascal-cased-properties.d.ts +36 -0
- package/node_modules/type-fest/source/paths.d.ts +262 -0
- package/node_modules/type-fest/source/pick-deep.d.ts +149 -0
- package/node_modules/type-fest/source/pick-index-signature.d.ts +50 -0
- package/node_modules/type-fest/source/primitive.d.ts +13 -0
- package/node_modules/type-fest/source/promisable.d.ts +25 -0
- package/node_modules/type-fest/source/readonly-deep.d.ts +81 -0
- package/node_modules/type-fest/source/readonly-keys-of.d.ts +30 -0
- package/node_modules/type-fest/source/readonly-tuple.d.ts +41 -0
- package/node_modules/type-fest/source/replace.d.ts +85 -0
- package/node_modules/type-fest/source/require-all-or-none.d.ts +51 -0
- package/node_modules/type-fest/source/require-at-least-one.d.ts +47 -0
- package/node_modules/type-fest/source/require-exactly-one.d.ts +45 -0
- package/node_modules/type-fest/source/require-one-or-none.d.ts +46 -0
- package/node_modules/type-fest/source/required-deep.d.ts +78 -0
- package/node_modules/type-fest/source/required-keys-of.d.ts +30 -0
- package/node_modules/type-fest/source/schema.d.ts +114 -0
- package/node_modules/type-fest/source/screaming-snake-case.d.ts +28 -0
- package/node_modules/type-fest/source/set-field-type.d.ts +65 -0
- package/node_modules/type-fest/source/set-non-nullable-deep.d.ts +83 -0
- package/node_modules/type-fest/source/set-non-nullable.d.ts +39 -0
- package/node_modules/type-fest/source/set-optional.d.ts +38 -0
- package/node_modules/type-fest/source/set-parameter-type.d.ts +117 -0
- package/node_modules/type-fest/source/set-readonly.d.ts +39 -0
- package/node_modules/type-fest/source/set-required-deep.d.ts +68 -0
- package/node_modules/type-fest/source/set-required.d.ts +70 -0
- package/node_modules/type-fest/source/set-return-type.d.ts +29 -0
- package/node_modules/type-fest/source/shared-union-fields-deep.d.ts +178 -0
- package/node_modules/type-fest/source/shared-union-fields.d.ts +76 -0
- package/node_modules/type-fest/source/simplify-deep.d.ts +115 -0
- package/node_modules/type-fest/source/simplify.d.ts +58 -0
- package/node_modules/type-fest/source/single-key-object.d.ts +29 -0
- package/node_modules/type-fest/source/snake-case.d.ts +45 -0
- package/node_modules/type-fest/source/snake-cased-properties-deep.d.ts +63 -0
- package/node_modules/type-fest/source/snake-cased-properties.d.ts +40 -0
- package/node_modules/type-fest/source/split.d.ts +88 -0
- package/node_modules/type-fest/source/spread.d.ts +84 -0
- package/node_modules/type-fest/source/string-key-of.d.ts +25 -0
- package/node_modules/type-fest/source/string-repeat.d.ts +47 -0
- package/node_modules/type-fest/source/string-slice.d.ts +37 -0
- package/node_modules/type-fest/source/stringified.d.ts +23 -0
- package/node_modules/type-fest/source/structured-cloneable.d.ts +92 -0
- package/node_modules/type-fest/source/subtract.d.ts +83 -0
- package/node_modules/type-fest/source/sum.d.ts +78 -0
- package/node_modules/type-fest/source/tagged-union.d.ts +51 -0
- package/node_modules/type-fest/source/tagged.d.ts +256 -0
- package/node_modules/type-fest/source/trim.d.ts +27 -0
- package/node_modules/type-fest/source/tsconfig-json.d.ts +1294 -0
- package/node_modules/type-fest/source/tuple-to-object.d.ts +42 -0
- package/node_modules/type-fest/source/tuple-to-union.d.ts +51 -0
- package/node_modules/type-fest/source/typed-array.d.ts +17 -0
- package/node_modules/type-fest/source/undefined-on-partial-deep.d.ts +80 -0
- package/node_modules/type-fest/source/union-to-intersection.d.ts +61 -0
- package/node_modules/type-fest/source/union-to-tuple.d.ts +56 -0
- package/node_modules/type-fest/source/unknown-array.d.ts +25 -0
- package/node_modules/type-fest/source/unknown-map.d.ts +24 -0
- package/node_modules/type-fest/source/unknown-record.d.ts +31 -0
- package/node_modules/type-fest/source/unknown-set.d.ts +24 -0
- package/node_modules/type-fest/source/value-of.d.ts +42 -0
- package/node_modules/type-fest/source/words.d.ts +118 -0
- package/node_modules/type-fest/source/writable-deep.d.ts +83 -0
- package/node_modules/type-fest/source/writable-keys-of.d.ts +33 -0
- package/node_modules/type-fest/source/writable.d.ts +68 -0
- package/node_modules/widest-line/index.d.ts +12 -0
- package/node_modules/widest-line/index.js +11 -0
- package/node_modules/widest-line/license +9 -0
- package/node_modules/widest-line/package.json +60 -0
- package/node_modules/widest-line/readme.md +26 -0
- package/node_modules/wrap-ansi/index.d.ts +41 -0
- package/node_modules/wrap-ansi/index.js +222 -0
- package/node_modules/wrap-ansi/license +9 -0
- package/node_modules/wrap-ansi/package.json +69 -0
- package/node_modules/wrap-ansi/readme.md +75 -0
- package/package.json +78 -0
- package/scripts/postinstall.cjs +122 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* file-stats.ts — Recolección y enriquecimiento de estadísticas de archivos.
|
|
3
|
+
*
|
|
4
|
+
* Expone dos funciones públicas que forman los primeros dos pasos del pipeline de análisis:
|
|
5
|
+
*
|
|
6
|
+
* 1. **`gatherChangedFiles`**: obtiene la lista de archivos con cambios
|
|
7
|
+
* (tracked modificados + untracked) filtrando lockfiles, binarios y
|
|
8
|
+
* carpetas de build. Cuando se pasa `baseBranch` usa `git diff <base>`
|
|
9
|
+
* en lugar del diff del working tree.
|
|
10
|
+
*
|
|
11
|
+
* 2. **`getFileStats`**: para cada archivo de la lista calcula líneas,
|
|
12
|
+
* adiciones, eliminaciones, tipo de cambio y prioridad arquitectónica.
|
|
13
|
+
* Usa `git diff --numstat` y `git diff --name-status` para minimizar
|
|
14
|
+
* el número de subprocesos.
|
|
15
|
+
*
|
|
16
|
+
* ### Prioridades arquitectónicas
|
|
17
|
+
* Los archivos se clasifican en 5 niveles para que `distributeBlocks` y
|
|
18
|
+
* `buildCommitPlan` procesen primero los cambios de mayor impacto:
|
|
19
|
+
* - 1: config / types / schemas / migrations
|
|
20
|
+
* - 2: servicios / core / dominio (fallback general)
|
|
21
|
+
* - 3: infra / adapters / rutas
|
|
22
|
+
* - 4: tests
|
|
23
|
+
* - 5: docs
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
26
|
+
import { basename } from "node:path";
|
|
27
|
+
import { isBinaryFileSync } from "isbinaryfile";
|
|
28
|
+
import { shSafe, q } from "../git/git.js";
|
|
29
|
+
import { normalizePathValue, isTrackedFile } from "../shared/utils.js";
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Detección de archivos binarios (por contenido, no por extensión)
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Detecta si un archivo es binario leyendo sus primeros bytes con `isbinaryfile`.
|
|
35
|
+
*
|
|
36
|
+
* Usar detección por contenido (no por extensión) captura archivos como `.wasm`,
|
|
37
|
+
* imágenes renombradas, o cualquier archivo sin extensión que sea binario.
|
|
38
|
+
* Devuelve `false` si el archivo no existe (puede haber sido eliminado).
|
|
39
|
+
*/
|
|
40
|
+
function isBinary(file) {
|
|
41
|
+
if (!existsSync(file))
|
|
42
|
+
return false;
|
|
43
|
+
try {
|
|
44
|
+
return isBinaryFileSync(file);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Exclusión de archivos del análisis
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/**
|
|
54
|
+
* Determina si un archivo debe excluirse del análisis.
|
|
55
|
+
*
|
|
56
|
+
* Se excluyen archivos que:
|
|
57
|
+
* - Están en carpetas de dependencias / artefactos de build:
|
|
58
|
+
* `node_modules/`, `dist/`, `build/`, `coverage/`, `.next/`, `target/`, `vendor/`
|
|
59
|
+
* - Son lockfiles (cuando `config.excludeLockfiles` es `true`):
|
|
60
|
+
* `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `poetry.lock`, `pipfile.lock`
|
|
61
|
+
* - Son archivos generados por la propia herramienta (JSON / HTML de salida),
|
|
62
|
+
* incluyendo los configurados en `runtimeExcludedFiles`.
|
|
63
|
+
* - Coinciden con patrones genéricos de reportes: `pr-split-*`, `reporte*`, `plan*`.
|
|
64
|
+
*
|
|
65
|
+
* @param path - Ruta relativa del archivo a evaluar.
|
|
66
|
+
* @param config - Configuración activa con `excludeLockfiles`, `jsonOutputFile`, etc.
|
|
67
|
+
*/
|
|
68
|
+
function shouldExcludeFile(path, config) {
|
|
69
|
+
const normalizedPath = normalizePathValue(path);
|
|
70
|
+
const fileName = basename(normalizedPath).toLowerCase();
|
|
71
|
+
const excludedPrefixes = [
|
|
72
|
+
"node_modules/",
|
|
73
|
+
"dist/",
|
|
74
|
+
"build/",
|
|
75
|
+
"coverage/",
|
|
76
|
+
".next/",
|
|
77
|
+
"target/",
|
|
78
|
+
"vendor/"
|
|
79
|
+
];
|
|
80
|
+
if (excludedPrefixes.some((p) => normalizedPath.startsWith(p)))
|
|
81
|
+
return true;
|
|
82
|
+
if (config.excludeLockfiles) {
|
|
83
|
+
const lockfiles = [
|
|
84
|
+
"package-lock.json",
|
|
85
|
+
"yarn.lock",
|
|
86
|
+
"pnpm-lock.yaml",
|
|
87
|
+
"poetry.lock",
|
|
88
|
+
"pipfile.lock"
|
|
89
|
+
];
|
|
90
|
+
if (lockfiles.includes(fileName))
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
// Comparar por ruta completa normalizada (no solo basename) para evitar
|
|
94
|
+
// excluir archivos legítimos que compartan nombre con archivos generados.
|
|
95
|
+
// jsonOutputFile ya está incluido en runtimeExcludedFiles (vía cli.ts).
|
|
96
|
+
// Ambas partes se normalizan a minúsculas para que la comparación sea
|
|
97
|
+
// insensible a mayúsculas en sistemas con filesystem case-sensitive (Linux).
|
|
98
|
+
const configuredGeneratedFiles = (config.runtimeExcludedFiles ?? [])
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.map((value) => normalizePathValue(value).toLowerCase());
|
|
101
|
+
if (configuredGeneratedFiles.includes(normalizedPath.toLowerCase()))
|
|
102
|
+
return true;
|
|
103
|
+
// Patrones de archivos generados comunes (independiente de la config)
|
|
104
|
+
const generatedPatterns = [
|
|
105
|
+
/^pr-split-.*\.(json|html)$/i,
|
|
106
|
+
/^reporte.*\.(json|html)$/i,
|
|
107
|
+
/^report.*\.(json|html)$/i,
|
|
108
|
+
/^plan.*\.(json|html)$/i
|
|
109
|
+
];
|
|
110
|
+
if (generatedPatterns.some((pattern) => pattern.test(fileName)))
|
|
111
|
+
return true;
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Gather
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
/**
|
|
118
|
+
* Devuelve la lista de archivos con cambios no commiteados en el working tree.
|
|
119
|
+
*
|
|
120
|
+
* Combina `git diff --name-only` (tracked) con
|
|
121
|
+
* `git ls-files --others --exclude-standard` (untracked), elimina duplicados,
|
|
122
|
+
* excluye archivos de sistema (lockfiles, binaries, carpetas de build) y
|
|
123
|
+
* los archivos generados por la propia herramienta.
|
|
124
|
+
*
|
|
125
|
+
* @param config - Configuración activa del proyecto.
|
|
126
|
+
* @param baseBranch - Rama base opcional. Si se proporciona, usa `git diff <baseBranch>` para
|
|
127
|
+
* capturar todos los cambios del PR respecto a la rama base en lugar
|
|
128
|
+
* de comparar solo cambios no staged del working tree.
|
|
129
|
+
* @returns Lista de rutas relativas ordenadas alfabéticamente.
|
|
130
|
+
*/
|
|
131
|
+
export function gatherChangedFiles(config, baseBranch) {
|
|
132
|
+
// Archivos que difieren entre el working tree y la rama base.
|
|
133
|
+
// `git diff <branch>` compara el working tree contra <branch>, NO HEAD contra <branch>:
|
|
134
|
+
// captura tanto los cambios sin commitear como los commits propios de la rama.
|
|
135
|
+
// Consecuencia esperada: archivos ya commiteados en la rama aparecen en el análisis
|
|
136
|
+
// (la advertencia de aheadCount en cli.ts informa al usuario de este comportamiento).
|
|
137
|
+
const vsBase = baseBranch
|
|
138
|
+
? shSafe(`git diff ${q(baseBranch)} --name-only`)
|
|
139
|
+
.split("\n")
|
|
140
|
+
.map((s) => s.trim())
|
|
141
|
+
.filter(Boolean)
|
|
142
|
+
: [];
|
|
143
|
+
// Archivos tracked modificados en el working tree (aún no commiteados).
|
|
144
|
+
const workingTree = shSafe("git diff --name-only")
|
|
145
|
+
.split("\n")
|
|
146
|
+
.map((s) => s.trim())
|
|
147
|
+
.filter(Boolean);
|
|
148
|
+
// Archivos nuevos sin seguimiento git (untracked): creados pero nunca añadidos al índice.
|
|
149
|
+
// Se incluyen siempre porque forman parte del trabajo pendiente del developer,
|
|
150
|
+
// independientemente de si se usa --base o no.
|
|
151
|
+
const untracked = shSafe("git ls-files --others --exclude-standard")
|
|
152
|
+
.split("\n")
|
|
153
|
+
.map((s) => s.trim())
|
|
154
|
+
.filter(Boolean);
|
|
155
|
+
return [...new Set([...vsBase, ...workingTree, ...untracked])]
|
|
156
|
+
.filter((f) => !shouldExcludeFile(f, config))
|
|
157
|
+
.sort();
|
|
158
|
+
}
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// Prioridad
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
/**
|
|
163
|
+
* Asigna una prioridad arquitectónica al archivo (1 = más importante, 5 = menos).
|
|
164
|
+
*
|
|
165
|
+
* La prioridad se usa para:
|
|
166
|
+
* - Ordenar bloques en `buildCommitPlan` (los más prioritarios se commitean primero).
|
|
167
|
+
* - Influir en la distribución greedy de `distributeBlocks`.
|
|
168
|
+
*
|
|
169
|
+
* Heurística basada en patrones de ruta comunes en proyectos backend/fullstack.
|
|
170
|
+
* Si ninguna regla coincide, el nivel por defecto es 2 (servicios/core).
|
|
171
|
+
*
|
|
172
|
+
* @param file - Ruta relativa del archivo.
|
|
173
|
+
* @returns Número de prioridad entre 1 y 5.
|
|
174
|
+
*/
|
|
175
|
+
function getPriority(file) {
|
|
176
|
+
const f = file.toLowerCase();
|
|
177
|
+
if (/(config|configs|configuration|settings|env|schema|schemas|type|types|interface|interfaces|model|models|entity|entities|dto|dtos|migration|migrations|constant|constants)/.test(f)) {
|
|
178
|
+
return 1;
|
|
179
|
+
}
|
|
180
|
+
if (/(core|domain|service|services|usecase|usecases|controller|controllers|application|manager|handler)/.test(f)) {
|
|
181
|
+
return 2;
|
|
182
|
+
}
|
|
183
|
+
if (/(infra|infrastructure|repository|repositories|adapter|adapters|route|routes|gateway|gateways|client|clients)/.test(f)) {
|
|
184
|
+
return 3;
|
|
185
|
+
}
|
|
186
|
+
if (/(^|\/)(__tests__|tests|test)(\/|$)|(\.spec\.|\.test\.)/.test(f)) {
|
|
187
|
+
return 4;
|
|
188
|
+
}
|
|
189
|
+
if (/(^|\/)(docs|doc)(\/|$)|(^|\/)readme(\.[^/]*)?$|(^|\/)changelog(\.[^/]*)?$|(^|\/)license(\.[^/]*)?$|\.md$|\.adoc$|\.rst$/.test(f)) {
|
|
190
|
+
return 5;
|
|
191
|
+
}
|
|
192
|
+
return 2;
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Line counting
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
/**
|
|
198
|
+
* Cuenta las líneas de un archivo nuevo (untracked) leyéndolo del disco.
|
|
199
|
+
*
|
|
200
|
+
* Solo se llama como fallback cuando `numstat` no tiene información del archivo
|
|
201
|
+
* (lo que ocurre con archivos nuevos que aún no han sido añadidos con `git add`).
|
|
202
|
+
* Para archivos binarios devuelve 0.
|
|
203
|
+
*
|
|
204
|
+
* @param file - Ruta relativa del archivo a leer.
|
|
205
|
+
* @returns Número de líneas del archivo (0 si es binario o no se puede leer).
|
|
206
|
+
*/
|
|
207
|
+
function getUntrackedFileLines(file) {
|
|
208
|
+
if (isBinary(file))
|
|
209
|
+
return 0;
|
|
210
|
+
try {
|
|
211
|
+
const content = readFileSync(file, "utf8");
|
|
212
|
+
// split("\n") produce un elemento vacío final en archivos con trailing newline
|
|
213
|
+
// (la gran mayoría de archivos de texto), lo que sobrecontaría en 1 línea.
|
|
214
|
+
const lines = content.split("\n");
|
|
215
|
+
return lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// Parsers de salida de git diff
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
/**
|
|
225
|
+
* Parsea la salida de `git diff --name-status` y devuelve un mapa
|
|
226
|
+
* `ruta → tipo-de-cambio` (A, M, D, R).
|
|
227
|
+
*
|
|
228
|
+
* Los archivos renombrados (tipo `R<porcentaje>\told\tnew`) se registran
|
|
229
|
+
* con la ruta nueva (`parts[2]`) como tipo `"R"`.
|
|
230
|
+
*
|
|
231
|
+
* @param baseBranch - Si se provee, usa `git diff <base>`. Si no, diff del working tree.
|
|
232
|
+
*/
|
|
233
|
+
function parseDiffNameStatus(baseBranch) {
|
|
234
|
+
const out = baseBranch
|
|
235
|
+
? shSafe(`git diff ${q(baseBranch)} --name-status`)
|
|
236
|
+
: shSafe("git diff --name-status");
|
|
237
|
+
const map = new Map();
|
|
238
|
+
for (const line of out.split("\n").map((s) => s.trim()).filter(Boolean)) {
|
|
239
|
+
const parts = line.split("\t");
|
|
240
|
+
const rawType = parts[0];
|
|
241
|
+
if (rawType.startsWith("R")) {
|
|
242
|
+
const newPath = parts[2];
|
|
243
|
+
if (newPath)
|
|
244
|
+
map.set(newPath, "R");
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const file = parts[1];
|
|
248
|
+
if (!file)
|
|
249
|
+
continue;
|
|
250
|
+
if (rawType === "A" || rawType === "M" || rawType === "D") {
|
|
251
|
+
map.set(file, rawType);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return map;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Parsea la salida de `git diff --numstat` y devuelve un mapa
|
|
258
|
+
* `ruta → { additions, deletions }`.
|
|
259
|
+
*
|
|
260
|
+
* Para archivos binarios git imprime `"-"` en lugar de un número; en ese caso
|
|
261
|
+
* se usa 0. Si la línea tiene menos de 3 columnas se ignora.
|
|
262
|
+
*
|
|
263
|
+
* @param baseBranch - Si se provee, usa `git diff <base>`. Si no, diff del working tree.
|
|
264
|
+
*/
|
|
265
|
+
function parseDiffNumstat(baseBranch) {
|
|
266
|
+
const out = baseBranch
|
|
267
|
+
? shSafe(`git diff ${q(baseBranch)} --numstat`)
|
|
268
|
+
: shSafe("git diff --numstat");
|
|
269
|
+
const map = new Map();
|
|
270
|
+
for (const line of out.split("\n").map((s) => s.trim()).filter(Boolean)) {
|
|
271
|
+
const parts = line.split("\t");
|
|
272
|
+
if (parts.length < 3)
|
|
273
|
+
continue;
|
|
274
|
+
const addRaw = parts[0];
|
|
275
|
+
const delRaw = parts[1];
|
|
276
|
+
const file = parts.slice(2).join("\t");
|
|
277
|
+
// Manejar notación de renombrado: "old => new" o "src/{old => new}/file.ts"
|
|
278
|
+
let fileKey = file;
|
|
279
|
+
const braceMatch = file.match(/^(.*)\{(.+?) => (.+?)\}(.*)$/);
|
|
280
|
+
if (braceMatch) {
|
|
281
|
+
fileKey = braceMatch[1] + braceMatch[3] + braceMatch[4];
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
const arrowMatch = file.match(/^.+ => (.+)$/);
|
|
285
|
+
if (arrowMatch)
|
|
286
|
+
fileKey = arrowMatch[1];
|
|
287
|
+
}
|
|
288
|
+
const additions = addRaw === "-" ? 0 : Number(addRaw || 0);
|
|
289
|
+
const deletions = delRaw === "-" ? 0 : Number(delRaw || 0);
|
|
290
|
+
map.set(fileKey, { additions, deletions });
|
|
291
|
+
}
|
|
292
|
+
return map;
|
|
293
|
+
}
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
// Public
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
/**
|
|
298
|
+
* Calcula estadísticas detalladas (líneas, adiciones, eliminaciones, tipo de
|
|
299
|
+
* cambio, prioridad) para cada archivo cambiado.
|
|
300
|
+
*
|
|
301
|
+
* La prioridad se asigna según el rol del archivo en la arquitectura:
|
|
302
|
+
* config/types → 1, servicios/core → 2, infra → 3, tests → 4, docs → 5.
|
|
303
|
+
*
|
|
304
|
+
* @param files - Rutas relativas de los archivos a analizar.
|
|
305
|
+
* @returns Array de {@link FileStat} en el mismo orden que la entrada.
|
|
306
|
+
*/
|
|
307
|
+
export function getFileStats(files, baseBranch) {
|
|
308
|
+
const changeTypeMap = parseDiffNameStatus(baseBranch);
|
|
309
|
+
const numstatMap = parseDiffNumstat(baseBranch);
|
|
310
|
+
return files.map((file) => {
|
|
311
|
+
const tracked = isTrackedFile(file);
|
|
312
|
+
const binary = isBinary(file);
|
|
313
|
+
const numstat = numstatMap.get(file);
|
|
314
|
+
// Preferir numstat cuando está disponible: ya tiene el diff correcto
|
|
315
|
+
// (respecto a baseBranch si se usó --base). El fallback a getUntrackedFileLines
|
|
316
|
+
// solo aplica para archivos nuevos locales (changeType U) que no aparecen en el diff.
|
|
317
|
+
const lineCount = binary
|
|
318
|
+
? 0
|
|
319
|
+
: numstat
|
|
320
|
+
? numstat.additions + numstat.deletions
|
|
321
|
+
: getUntrackedFileLines(file);
|
|
322
|
+
const additions = numstat?.additions ?? lineCount;
|
|
323
|
+
const deletions = numstat?.deletions ?? 0;
|
|
324
|
+
let changeType = "M";
|
|
325
|
+
if (!tracked) {
|
|
326
|
+
changeType = "U";
|
|
327
|
+
}
|
|
328
|
+
else if (changeTypeMap.has(file)) {
|
|
329
|
+
changeType = changeTypeMap.get(file);
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
path: file,
|
|
333
|
+
lines: lineCount,
|
|
334
|
+
additions,
|
|
335
|
+
deletions,
|
|
336
|
+
changeType,
|
|
337
|
+
status: tracked ? "tracked" : "untracked",
|
|
338
|
+
priority: getPriority(file)
|
|
339
|
+
};
|
|
340
|
+
});
|
|
341
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* history.ts — Historial de scores por ejecución.
|
|
3
|
+
*
|
|
4
|
+
* Cada vez que el CLI analiza un repositorio guarda una entrada en el archivo
|
|
5
|
+
* `.advisor-history.json` (configurable con `historyFile`). El flag `--stats`
|
|
6
|
+
* lee ese archivo y muestra las últimas N entradas con su evolución de score.
|
|
7
|
+
*
|
|
8
|
+
* ### Estructura del archivo JSON
|
|
9
|
+
* ```json
|
|
10
|
+
* [
|
|
11
|
+
* {
|
|
12
|
+
* "timestamp": "2026-04-03T12:00:00.000Z",
|
|
13
|
+
* "branch": "feature/ABC-123-add-login",
|
|
14
|
+
* "baseBranch": "main",
|
|
15
|
+
* "avgScore": 4.35,
|
|
16
|
+
* "totalBranches": 2,
|
|
17
|
+
* "totalFiles": 12,
|
|
18
|
+
* "totalLines": 480
|
|
19
|
+
* }
|
|
20
|
+
* ]
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
/**
|
|
26
|
+
* Lee el historial existente desde el archivo configurado con `config.historyFile`.
|
|
27
|
+
* Devuelve un array vacío si el archivo no existe o está corrupto.
|
|
28
|
+
*/
|
|
29
|
+
export function readHistory(config) {
|
|
30
|
+
const fullPath = path.resolve(process.cwd(), config.historyFile);
|
|
31
|
+
if (!existsSync(fullPath))
|
|
32
|
+
return [];
|
|
33
|
+
try {
|
|
34
|
+
const raw = readFileSync(fullPath, "utf8");
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Agrega una nueva entrada al historial y la persiste en disco.
|
|
44
|
+
* Si el archivo ya existe, concatena la entrada al array existente.
|
|
45
|
+
* Limita el historial a las últimas 100 entradas para evitar archivos enormes.
|
|
46
|
+
*
|
|
47
|
+
* @param plans - Plan de ramas producido por `findBestPlan`.
|
|
48
|
+
* @param branch - Nombre de la rama de trabajo actual.
|
|
49
|
+
* @param baseBranch - Nombre de la rama base del PR.
|
|
50
|
+
* @param fileStats - Lista de estadísticas de archivos.
|
|
51
|
+
* @param config - Configuración activa (contiene `historyFile`).
|
|
52
|
+
*/
|
|
53
|
+
export function appendHistory(plans, branch, baseBranch, fileStats, config) {
|
|
54
|
+
const avgScore = plans.length > 0
|
|
55
|
+
? Math.round((plans.reduce((s, p) => s + p.score, 0) / plans.length) * 100) / 100
|
|
56
|
+
: 0;
|
|
57
|
+
const entry = {
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
branch,
|
|
60
|
+
baseBranch,
|
|
61
|
+
avgScore,
|
|
62
|
+
totalBranches: plans.length,
|
|
63
|
+
totalFiles: fileStats.length,
|
|
64
|
+
totalLines: fileStats.reduce((s, f) => s + f.lines, 0)
|
|
65
|
+
};
|
|
66
|
+
const history = readHistory(config);
|
|
67
|
+
history.push(entry);
|
|
68
|
+
// Conservar solo las últimas 100 entradas
|
|
69
|
+
const trimmed = history.slice(-100);
|
|
70
|
+
const fullPath = path.resolve(process.cwd(), config.historyFile);
|
|
71
|
+
writeFileSync(fullPath, JSON.stringify(trimmed, null, 2), "utf8");
|
|
72
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* planner.ts — Barrel público del pipeline de análisis.
|
|
3
|
+
*
|
|
4
|
+
* Re-exporta las cuatro funciones públicas del pipeline en el orden en que
|
|
5
|
+
* deben invocarse:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* gatherChangedFiles(config, baseBranch)
|
|
9
|
+
* ↓
|
|
10
|
+
* getFileStats(files, baseBranch)
|
|
11
|
+
* ↓
|
|
12
|
+
* buildDependencyEdges(files)
|
|
13
|
+
* ↓
|
|
14
|
+
* buildBlocks(fileStats, config, deps)
|
|
15
|
+
* ↓
|
|
16
|
+
* findBestPlan(blocks, currentBranch, config, deps)
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Importar desde este barrel en lugar de los módulos individuales reduce el
|
|
20
|
+
* acoplamiento entre `cli.ts` y la estructura interna del directorio `core/`.
|
|
21
|
+
*/
|
|
22
|
+
export { gatherChangedFiles, getFileStats } from "./file-stats.js";
|
|
23
|
+
export { buildDependencyEdges } from "./dependency.js";
|
|
24
|
+
export { buildBlocks } from "./blocks.js";
|
|
25
|
+
export { findBestPlan } from "./strategy.js";
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scoring.ts — Cálculo del score compuesto de un Pull Request.
|
|
3
|
+
*
|
|
4
|
+
* El score es un número entre 0 y 5 que representa la «calidad de revisión»
|
|
5
|
+
* de un PR: cuánto esfuerzo cognitivo se espera del revisor. Un score de 5
|
|
6
|
+
* indica un PR pequeño, bien estructurado y fácil de revisar.
|
|
7
|
+
*
|
|
8
|
+
* ### Fórmula
|
|
9
|
+
* ```
|
|
10
|
+
* score = Σ(points(metric) × weight(metric))
|
|
11
|
+
* ```
|
|
12
|
+
* Donde `points(metric)` es 1–5 según las reglas de scoring de la métrica.
|
|
13
|
+
*
|
|
14
|
+
* ### Métricas (orden posicional requerido en la configuración)
|
|
15
|
+
* El motor asigna valores brutos **posicionalmente** según el orden declarado
|
|
16
|
+
* en `config.metrics`. Los 4 valores brutos, en orden obligatorio, son:
|
|
17
|
+
*
|
|
18
|
+
* 1. Cantidad de commits del PR
|
|
19
|
+
* 2. Promedio de archivos por commit
|
|
20
|
+
* 3. Promedio de líneas por commit
|
|
21
|
+
* 4. Total de líneas cambiadas
|
|
22
|
+
*
|
|
23
|
+
* Los códigos de cada métrica son libres («sólo etiquetas») y cada equipo
|
|
24
|
+
* puede definir los suyos. Ver `MetricCode` en `shared/types.ts`.
|
|
25
|
+
*
|
|
26
|
+
* ### Diseño: comparador compartido
|
|
27
|
+
* `checkComparators` es reutilizado por `matchesRule` (scoring) y
|
|
28
|
+
* `matchesRecommendation` (mensajes de recomendación), garantizando que la
|
|
29
|
+
* lógica de rangos numéricos no se duplique.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Evalúa si un valor numérico satisface todos los comparadores especificados.
|
|
33
|
+
*
|
|
34
|
+
* Los comparadores son aditivos: todos los presentes deben cumplirse (AND lógico).
|
|
35
|
+
* Si un comparador no está definido (`undefined`), no restringe el rango.
|
|
36
|
+
*
|
|
37
|
+
* @param value - Valor de la métrica a evaluar.
|
|
38
|
+
* @param c - Objeto con los comparadores opcionales (lt, lte, gt, gte, eq).
|
|
39
|
+
* @returns `true` si el valor satisface todos los comparadores presentes.
|
|
40
|
+
*/
|
|
41
|
+
function checkComparators(value, c) {
|
|
42
|
+
if (c.eq !== undefined && value !== c.eq)
|
|
43
|
+
return false;
|
|
44
|
+
if (c.lt !== undefined && !(value < c.lt))
|
|
45
|
+
return false;
|
|
46
|
+
if (c.lte !== undefined && !(value <= c.lte))
|
|
47
|
+
return false;
|
|
48
|
+
if (c.gt !== undefined && !(value > c.gt))
|
|
49
|
+
return false;
|
|
50
|
+
if (c.gte !== undefined && !(value >= c.gte))
|
|
51
|
+
return false;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Decide si una regla de scoring aplica a un valor dado.
|
|
56
|
+
*
|
|
57
|
+
* - Si la regla no tiene ningún comparador explícito, solo aplica si es `default: true`.
|
|
58
|
+
* - Si tiene comparadores, delega en `checkComparators`.
|
|
59
|
+
*
|
|
60
|
+
* @param value - Valor crudo de la métrica.
|
|
61
|
+
* @param rule - Regla de scoring a evaluar.
|
|
62
|
+
*/
|
|
63
|
+
function matchesRule(value, rule) {
|
|
64
|
+
const hasComparator = rule.eq !== undefined ||
|
|
65
|
+
rule.lt !== undefined ||
|
|
66
|
+
rule.lte !== undefined ||
|
|
67
|
+
rule.gt !== undefined ||
|
|
68
|
+
rule.gte !== undefined;
|
|
69
|
+
// Sin comparador explícito la regla sólo aplica si está marcada como default.
|
|
70
|
+
if (!hasComparator)
|
|
71
|
+
return rule.default === true;
|
|
72
|
+
return checkComparators(value, rule);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Recorre las reglas de scoring de una métrica y devuelve los puntos de la primera
|
|
76
|
+
* regla que coincida con el valor. Si ninguna coincide (configuración incompleta,
|
|
77
|
+
* falta regla `default: true`), emite un aviso por stderr y devuelve 0.
|
|
78
|
+
*
|
|
79
|
+
* @param value - Valor crudo de la métrica.
|
|
80
|
+
* @param metric - Definición de la métrica con su lista de reglas.
|
|
81
|
+
*/
|
|
82
|
+
function resolvePoints(value, metric) {
|
|
83
|
+
for (const rule of metric.scoring) {
|
|
84
|
+
if (matchesRule(value, rule)) {
|
|
85
|
+
return rule.points;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Sin regla aplicable: indica una configuración incompleta (falta regla default).
|
|
89
|
+
// Emitimos el aviso por stderr con el mismo formato que el resto del proyecto.
|
|
90
|
+
process.stderr.write(`[scoring warn] Sin regla para valor ${value} en "${metric.label}". Revisa que la métrica tenga una regla { default: true }. Devolviendo 0 puntos.\n`);
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
/** Reutiliza `checkComparators` para evaluar condiciones de recomendación. La lógica de rangos es idéntica a `matchesRule`. */
|
|
94
|
+
function matchesRecommendation(value, condition) {
|
|
95
|
+
return checkComparators(value, condition);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Devuelve el mensaje de recomendación para un score compuesto evaluando las
|
|
99
|
+
* reglas en orden. Siempre debe haber una regla fallback `{ gte: 0 }` para
|
|
100
|
+
* scores fuera del rango esperado (pesos mal configurados).
|
|
101
|
+
*
|
|
102
|
+
* @param complexity - Score compuesto del PR (0–5).
|
|
103
|
+
* @param rules - Lista de reglas de recomendación de la configuración.
|
|
104
|
+
*/
|
|
105
|
+
function resolveRecommendation(complexity, rules) {
|
|
106
|
+
for (const rule of rules) {
|
|
107
|
+
if (matchesRecommendation(complexity, rule.when)) {
|
|
108
|
+
return rule.message;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return "Sin recomendación disponible para este score";
|
|
112
|
+
}
|
|
113
|
+
/** Redondea un número a 2 decimales usando representación de string para evitar problemas de punto flotante. */
|
|
114
|
+
function round2(value) {
|
|
115
|
+
return Number(value.toFixed(2));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Calcula el score compuesto de un Pull Request a partir de sus métricas brutas.
|
|
119
|
+
*
|
|
120
|
+
* Las métricas M1.3, M1.4, M1.5 y M3.2 se evalúan contra las reglas definidas
|
|
121
|
+
* en `config.metrics`. Cada métrica produce una puntuación de 1-5 que se
|
|
122
|
+
* multiplica por su peso para obtener el score ponderado.
|
|
123
|
+
*
|
|
124
|
+
* @param raw - Valores brutos del PR (commits, archivos/commit, líneas/commit, total líneas).
|
|
125
|
+
* @param config - Configuración que contiene las reglas de scoring y recomendaciones.
|
|
126
|
+
* @returns Objeto con métricas detalladas, score compuesto (`complexity`) y recomendación.
|
|
127
|
+
*/
|
|
128
|
+
export function scorePullRequest(raw, config) {
|
|
129
|
+
const defs = config.metrics;
|
|
130
|
+
// Mapeo posicional: el orden declarado en config.metrics determina qué valor
|
|
131
|
+
// bruto recibe cada métrica. Convención invariante (ver MetricCode en types.ts):
|
|
132
|
+
// posición 0 → commitCount
|
|
133
|
+
// posición 1 → filesPerCommit
|
|
134
|
+
// posición 2 → avgLinesPerCommit
|
|
135
|
+
// posición 3 → totalLinesChanged
|
|
136
|
+
const rawValues = [
|
|
137
|
+
raw.commitCount,
|
|
138
|
+
raw.filesPerCommit,
|
|
139
|
+
raw.avgLinesPerCommit,
|
|
140
|
+
raw.totalLinesChanged
|
|
141
|
+
];
|
|
142
|
+
const metrics = {};
|
|
143
|
+
// Mapeo posicional documentado (ver cabecera del módulo).
|
|
144
|
+
// deepMerge preserva siempre el orden de inserción de defaultConfig,
|
|
145
|
+
// por lo que el índice 0 → 3 es estable. El fallback a 0 protege ante
|
|
146
|
+
// configuraciones con menos de 4 métricas (ya rechazadas por loadConfig).
|
|
147
|
+
Object.entries(defs).forEach(([code, def], i) => {
|
|
148
|
+
const rawValue = i < rawValues.length ? rawValues[i] : 0;
|
|
149
|
+
const points = resolvePoints(rawValue, def);
|
|
150
|
+
metrics[code] = {
|
|
151
|
+
code,
|
|
152
|
+
label: def.label,
|
|
153
|
+
rawValue,
|
|
154
|
+
points,
|
|
155
|
+
weight: def.weight,
|
|
156
|
+
weightedScore: round2(points * def.weight)
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
const complexity = round2(Object.values(metrics).reduce((sum, m) => sum + m.weightedScore, 0));
|
|
160
|
+
const recommendation = resolveRecommendation(complexity, config.recommendationRules);
|
|
161
|
+
return {
|
|
162
|
+
metrics,
|
|
163
|
+
complexity,
|
|
164
|
+
recommendation
|
|
165
|
+
};
|
|
166
|
+
}
|