@webpieces/rules-config 0.3.357 → 0.3.359
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/package.json +3 -3
- package/src/branch-mutation-log.d.ts +13 -5
- package/src/branch-mutation-log.js +86 -76
- package/src/branch-mutation-log.js.map +1 -1
- package/src/diff-scope.d.ts +28 -45
- package/src/diff-scope.js +193 -180
- package/src/diff-scope.js.map +1 -1
- package/src/index.d.ts +3 -3
- package/src/index.js +7 -2
- package/src/index.js.map +1 -1
- package/src/review-json.d.ts +13 -5
- package/src/review-json.js +106 -87
- package/src/review-json.js.map +1 -1
- package/src/rules-config-design.d.ts +7 -1
- package/src/rules-config-design.js +14 -2
- package/src/rules-config-design.js.map +1 -1
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":";;;;;AAAA,iCAA0E;AAAjE,uGAAA,cAAc,OAAA;AAAE,2GAAA,kBAAkB,OAAA;AAC3C,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,mDAAgD;AAAvC,8GAAA,YAAY,OAAA;AACrB,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,6CAA4E;AAAnE,8GAAA,eAAe,OAAA;AAAE,2GAAA,YAAY,OAAA;AAAE,2GAAA,YAAY,OAAA;AACpD,6CAA4E;AAAnE,6GAAA,cAAc,OAAA;AAAE,8GAAA,eAAe,OAAA;AAAE,yGAAA,UAAU,OAAA;AACpD,yCAA8D;AAArD,2GAAA,cAAc,OAAA;AAAE,4GAAA,eAAe,OAAA;AACxC,6DAA0D;AAAjD,wHAAA,iBAAiB,OAAA;AAC1B,2DAAoD;AAA3C,kHAAA,YAAY,OAAA;AACrB,iDAAiD;AAAxC,+GAAA,cAAc,OAAA;AACvB,iDAAgE;AAAvD,6GAAA,YAAY,OAAA;AAAE,gHAAA,eAAe,OAAA;AACtC,iDAAsG;AAA7F,6GAAA,YAAY,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AAAE,8GAAA,aAAa,OAAA;AAAE,+GAAA,cAAc,OAAA;AAC5E,qDAAqM;AAA5L,0HAAA,uBAAuB,OAAA;AAAE,wHAAA,qBAAqB,OAAA;AAAE,2HAAA,wBAAwB,OAAA;AAAE,0HAAA,uBAAuB,OAAA;AAAE,uHAAA,oBAAoB,OAAA;AAAE,4HAAA,yBAAyB,OAAA;AAAE,+GAAA,YAAY,OAAA;AACzK,2DAQ8B;AAP1B,qHAAA,eAAe,OAAA;AACf,wHAAA,kBAAkB,OAAA;AAClB,6HAAA,uBAAuB,OAAA;AACvB,4HAAA,sBAAsB,OAAA;AACtB,8HAAA,wBAAwB,OAAA;AACxB,4HAAA,sBAAsB,OAAA;AACtB,yHAAA,mBAAmB,OAAA;AAGvB,uCAA2E;AAAlE,4GAAA,gBAAgB,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AACtD,yCAAuC;AAA9B,qGAAA,QAAQ,OAAA;AAEjB,yCAA+D;AAAtD,2GAAA,cAAc,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAEzC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":";;;;;AAAA,iCAA0E;AAAjE,uGAAA,cAAc,OAAA;AAAE,2GAAA,kBAAkB,OAAA;AAC3C,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,mDAAgD;AAAvC,8GAAA,YAAY,OAAA;AACrB,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,6CAA4E;AAAnE,8GAAA,eAAe,OAAA;AAAE,2GAAA,YAAY,OAAA;AAAE,2GAAA,YAAY,OAAA;AACpD,6CAA4E;AAAnE,6GAAA,cAAc,OAAA;AAAE,8GAAA,eAAe,OAAA;AAAE,yGAAA,UAAU,OAAA;AACpD,yCAA8D;AAArD,2GAAA,cAAc,OAAA;AAAE,4GAAA,eAAe,OAAA;AACxC,6DAA0D;AAAjD,wHAAA,iBAAiB,OAAA;AAC1B,2DAAoD;AAA3C,kHAAA,YAAY,OAAA;AACrB,iDAAiD;AAAxC,+GAAA,cAAc,OAAA;AACvB,iDAAgE;AAAvD,6GAAA,YAAY,OAAA;AAAE,gHAAA,eAAe,OAAA;AACtC,iDAAsG;AAA7F,6GAAA,YAAY,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AAAE,8GAAA,aAAa,OAAA;AAAE,+GAAA,cAAc,OAAA;AAC5E,qDAAqM;AAA5L,0HAAA,uBAAuB,OAAA;AAAE,wHAAA,qBAAqB,OAAA;AAAE,2HAAA,wBAAwB,OAAA;AAAE,0HAAA,uBAAuB,OAAA;AAAE,uHAAA,oBAAoB,OAAA;AAAE,4HAAA,yBAAyB,OAAA;AAAE,+GAAA,YAAY,OAAA;AACzK,2DAQ8B;AAP1B,qHAAA,eAAe,OAAA;AACf,wHAAA,kBAAkB,OAAA;AAClB,6HAAA,uBAAuB,OAAA;AACvB,4HAAA,sBAAsB,OAAA;AACtB,8HAAA,wBAAwB,OAAA;AACxB,4HAAA,sBAAsB,OAAA;AACtB,yHAAA,mBAAmB,OAAA;AAGvB,uCAA2E;AAAlE,4GAAA,gBAAgB,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AACtD,yCAAuC;AAA9B,qGAAA,QAAQ,OAAA;AAEjB,yCAA+D;AAAtD,2GAAA,cAAc,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAEzC,2CAYsB;AAXlB,wGAAA,UAAU,OAAA;AACV,yGAAA,WAAW,OAAA;AACX,6GAAA,eAAe,OAAA;AACf,yGAAA,WAAW,OAAA;AACX,mHAAA,qBAAqB,OAAA;AACrB,2HAAA,6BAA6B,OAAA;AAC7B,+GAAA,iBAAiB,OAAA;AACjB,6GAAA,eAAe,OAAA;AACf,uGAAA,SAAS,OAAA;AACT,uGAAA,SAAS,OAAA;AACT,iHAAA,mBAAmB,OAAA;AAEvB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,yCASqB;AARjB,8GAAA,iBAAiB,OAAA;AACjB,uGAAA,UAAU,OAAA;AACV,uGAAA,UAAU,OAAA;AACV,8GAAA,iBAAiB,OAAA;AACjB,2GAAA,cAAc,OAAA;AACd,0GAAA,aAAa,OAAA;AACb,mHAAA,sBAAsB,OAAA;AACtB,mHAAA,sBAAsB,OAAA;AAE1B,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,+CAiCwB;AAhCpB,oHAAA,oBAAoB,OAAA;AACpB,kHAAA,kBAAkB,OAAA;AAClB,uHAAA,uBAAuB,OAAA;AACvB,0HAAA,0BAA0B,OAAA;AAC1B,kHAAA,kBAAkB,OAAA;AAClB,mHAAA,mBAAmB,OAAA;AACnB,wHAAA,wBAAwB,OAAA;AACxB,qHAAA,qBAAqB,OAAA;AACrB,mHAAA,mBAAmB,OAAA;AACnB,2HAAA,2BAA2B,OAAA;AAC3B,uHAAA,uBAAuB,OAAA;AACvB,wHAAA,wBAAwB,OAAA;AACxB,kIAAA,kCAAkC,OAAA;AAClC,sHAAA,sBAAsB,OAAA;AACtB,8HAAA,8BAA8B,OAAA;AAC9B,4HAAA,4BAA4B,OAAA;AAC5B,+IAAA,+CAA+C,OAAA;AAC/C,kHAAA,kBAAkB,OAAA;AAClB,6GAAA,aAAa,OAAA;AACb,yHAAA,yBAAyB,OAAA;AACzB,2HAAA,2BAA2B,OAAA;AAC3B,0HAAA,0BAA0B,OAAA;AAC1B,kHAAA,kBAAkB,OAAA;AAClB,4HAAA,4BAA4B,OAAA;AAC5B,wHAAA,wBAAwB,OAAA;AACxB,wHAAA,wBAAwB,OAAA;AACxB,yHAAA,yBAAyB,OAAA;AACzB,8GAAA,cAAc,OAAA;AACd,6GAAA,aAAa,OAAA;AACb,+GAAA,eAAe,OAAA;AACf,qHAAA,qBAAqB,OAAA;AACrB,8GAAA,cAAc,OAAA;AAElB,wFAAwF;AACxF,+CAcwB;AAbpB,kHAAA,kBAAkB,OAAA;AAClB,gHAAA,gBAAgB,OAAA;AAChB,iHAAA,iBAAiB,OAAA;AACjB,iHAAA,iBAAiB,OAAA;AACjB,mHAAA,mBAAmB,OAAA;AACnB,6GAAA,aAAa,OAAA;AACb,iHAAA,iBAAiB,OAAA;AACjB,sHAAA,sBAAsB,OAAA;AACtB,yHAAA,yBAAyB,OAAA;AACzB,iHAAA,iBAAiB,OAAA;AACjB,4GAAA,YAAY,OAAA;AACZ,gHAAA,gBAAgB,OAAA;AAChB,iHAAA,iBAAiB,OAAA;AAiBrB,mDAM0B;AALtB,gHAAA,cAAc,OAAA;AACd,8GAAA,YAAY,OAAA;AACZ,8GAAA,YAAY,OAAA;AACZ,qHAAA,mBAAmB,OAAA;AACnB,mHAAA,iBAAiB,OAAA;AAErB,6CAOuB;AANnB,yGAAA,UAAU,OAAA;AACV,gHAAA,iBAAiB,OAAA;AACjB,6GAAA,cAAc,OAAA;AACd,uGAAA,QAAQ,OAAA;AACR,6GAAA,cAAc,OAAA;AACd,mHAAA,oBAAoB,OAAA;AAExB,uDAiB4B;AAhBxB,kHAAA,cAAc,OAAA;AACd,gHAAA,YAAY,OAAA;AACZ,gIAAA,4BAA4B,OAAA;AAC5B,sHAAA,kBAAkB,OAAA;AAClB,oHAAA,gBAAgB,OAAA;AAChB,sHAAA,kBAAkB,OAAA;AAClB,uHAAA,mBAAmB,OAAA;AACnB,oHAAA,gBAAgB,OAAA;AAChB,qHAAA,iBAAiB,OAAA;AACjB,+GAAA,WAAW,OAAA;AACX,uHAAA,mBAAmB,OAAA;AACnB,iHAAA,aAAa,OAAA;AACb,gHAAA,YAAY,OAAA;AACZ,yHAAA,qBAAqB,OAAA;AACrB,4HAAA,wBAAwB,OAAA;AACxB,uHAAA,mBAAmB,OAAA;AAGvB,6DAK+B;AAJ3B,0HAAA,mBAAmB,OAAA;AACnB,wHAAA,iBAAiB,OAAA;AACjB,4HAAA,qBAAqB,OAAA;AACrB,wHAAA,iBAAiB,OAAA;AAErB,qDAK2B;AAJvB,iHAAA,cAAc,OAAA;AACd,sHAAA,mBAAmB,OAAA;AACnB,4HAAA,yBAAyB,OAAA;AACzB,iIAAA,8BAA8B,OAAA","sourcesContent":["export { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nexport { InformAiError } from './inform-ai-error';\nexport { RuleFailError } from './rule-fail-error';\nexport { CliExitError } from './cli-exit-error';\nexport { runMain } from './run-main';\nexport { toError } from './to-error';\nexport { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';\nexport { findConfigFile, CONFIG_FILENAME, ConfigFile } from './config-file';\nexport { RepoRootFinder, INSTRUCT_AI_DIR } from './repo-root';\nexport { RulesConfigDesign } from './rules-config-design';\nexport { ExcludePaths } from './exclude-hook-paths';\nexport { isPathExcluded } from './exclude-paths';\nexport { defaultRules, defaultRulesDir } from './default-rules';\nexport { loadTemplate, writeTemplateIfMissing, writeTemplate, TemplateWriter } from './load-template';\nexport { validateWebpiecesConfig, validatePrGateSection, validateSectionPlacement, validateCommandsSection, validateExcludePaths, validateMatchRulesSection, allRuleNames } from './validate-config';\nexport {\n MatchRuleConfig,\n MatchRuleViolation,\n findMatchRuleViolations,\n isMatchRuleAllowedPath,\n compileMatchRulePatterns,\n renderMatchRuleMessage,\n DEFAULT_MATCH_RULES,\n} from './match-rules-config';\nexport type { ConfigSection } from './sections';\nexport { HOOK_GUARD_NAMES, isHookGuard, sectionForRule } from './sections';\nexport { FieldDef } from './field-def';\nexport type { SchemaShape } from './field-def';\nexport { shouldSkipRule, getCurrentBranch } from './skip-rule';\nexport type { SkipRuleResult } from './skip-rule';\nexport {\n detectBase,\n resolveBase,\n getChangedFiles,\n getFileDiff,\n getChangedLineNumbers,\n findNewMethodSignaturesInDiff,\n hasChangesInRange,\n isNewOrModified,\n DiffScope,\n DiffRange,\n ChangedFilesOptions,\n} from './diff-scope';\nexport { AbstractRule } from './abstract-rule';\nexport {\n WEBPIECES_DISABLE,\n RULE_NAMES,\n hasDisable,\n WEBPIECES_TMP_DIR,\n MERGE_INFO_DIR,\n PR_REVIEW_DIR,\n MERGE_IN_PROGRESS_FILE,\n MERGE_EXPLANATION_FILE,\n} from './constants';\nexport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\nexport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n FeatureBranchGuardConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n BaseRuleConfig,\n} from './rule-configs';\n// Mode unions + their value arrays — the single source of truth shared with code-rules.\nexport {\n METHOD_LIMIT_MODES,\n FILE_LIMIT_MODES,\n RETURN_TYPE_MODES,\n INLINE_TYPE_MODES,\n MODIFIED_CODE_MODES,\n PROJECT_MODES,\n PRISMA_DTOS_MODES,\n PRISMA_CONVERTER_MODES,\n DIRECT_API_RESOLVER_MODES,\n THROW_CAUSE_MODES,\n ON_OFF_MODES,\n STRUCTURAL_MODES,\n VALIDATE_TS_MODES,\n} from './rule-configs';\nexport type {\n MethodLimitMode,\n FileLimitMode,\n ReturnTypeMode,\n InlineTypeMode,\n ModifiedCodeMode,\n ProjectMode,\n PrismaValidateDtosMode,\n PrismaConverterMode,\n DirectApiResolverMode,\n ThrowCauseMode,\n OnOffMode,\n StructuralMode,\n ValidateTsMode,\n} from './rule-configs';\nexport {\n GateDefinition,\n PrGateConfig,\n defaultGates,\n defaultPrGateConfig,\n buildPrGateConfig,\n} from './pr-gate-config';\nexport {\n ReviewJson,\n ReviewJsonService,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n reviewJsonSchemaHint,\n} from './review-json';\nexport {\n MainSyncStatus,\n MainSyncLock,\n DEFAULT_HANG_TIMEOUT_MINUTES,\n mainSyncStatusPath,\n mainSyncLockPath,\n readMainSyncStatus,\n writeMainSyncStatus,\n readMainSyncLock,\n writeMainSyncLock,\n isLockStale,\n isRefreshInProgress,\n inProcessLock,\n finishedLock,\n computeMainSyncStatus,\n stampCleanMainSyncStatus,\n squashRecoverySteps,\n} from './main-sync-status';\nexport type { MutationVerb, MutationPhase } from './branch-mutation-log';\nexport {\n BranchMutationEvent,\n BranchMutationLog,\n branchMutationLogPath,\n logBranchMutation,\n} from './branch-mutation-log';\nexport {\n CommandsConfig,\n buildCommandsConfig,\n DEFAULT_UPSERT_PR_COMMAND,\n DEFAULT_MERGE_COMPLETE_COMMAND,\n} from './commands-config';\n"]}
|
package/src/review-json.d.ts
CHANGED
|
@@ -9,12 +9,20 @@ export declare class ReviewJson {
|
|
|
9
9
|
filesToReview: string[];
|
|
10
10
|
constructor(title: string, riskScore: number, riskLevel: string, riskEmoji: string, summary: string, violations: string[], risks: string[], filesToReview: string[]);
|
|
11
11
|
}
|
|
12
|
+
/** Locates + loads/validates the AI-authored review.json. `@provideSingleton` so it's drawn in the design. */
|
|
13
|
+
export declare class ReviewJsonService {
|
|
14
|
+
prDirFor(repoRoot: string, featureName: string): string;
|
|
15
|
+
reviewJsonPath(repoRoot: string, featureName: string): string;
|
|
16
|
+
reviewJsonSchemaHint(filePath: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,
|
|
19
|
+
* unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.
|
|
20
|
+
*/
|
|
21
|
+
loadReviewJson(filePath: string): ReviewJson;
|
|
22
|
+
private asStringArray;
|
|
23
|
+
private parseReviewJson;
|
|
24
|
+
}
|
|
12
25
|
export declare function prDirFor(repoRoot: string, featureName: string): string;
|
|
13
26
|
export declare function reviewJsonPath(repoRoot: string, featureName: string): string;
|
|
14
27
|
export declare function reviewJsonSchemaHint(filePath: string): string;
|
|
15
|
-
/**
|
|
16
|
-
* Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when the file
|
|
17
|
-
* is missing, unparseable, or structurally wrong — the message is written straight back to the AI so
|
|
18
|
-
* it can fix the file and re-run. Returns a fully-populated ReviewJson on success.
|
|
19
|
-
*/
|
|
20
28
|
export declare function loadReviewJson(filePath: string): ReviewJson;
|
package/src/review-json.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ReviewJson = void 0;
|
|
3
|
+
exports.ReviewJsonService = exports.ReviewJson = void 0;
|
|
4
4
|
exports.prDirFor = prDirFor;
|
|
5
5
|
exports.reviewJsonPath = reviewJsonPath;
|
|
6
6
|
exports.reviewJsonSchemaHint = reviewJsonSchemaHint;
|
|
@@ -8,13 +8,13 @@ exports.loadReviewJson = loadReviewJson;
|
|
|
8
8
|
const tslib_1 = require("tslib");
|
|
9
9
|
const fs = tslib_1.__importStar(require("fs"));
|
|
10
10
|
const path = tslib_1.__importStar(require("path"));
|
|
11
|
+
const core_context_1 = require("@webpieces/core-context");
|
|
12
|
+
const inversify_1 = require("inversify");
|
|
11
13
|
const constants_1 = require("./constants");
|
|
12
14
|
const inform_ai_error_1 = require("./inform-ai-error");
|
|
13
15
|
const to_error_1 = require("./to-error");
|
|
14
|
-
// The AI-authored review for a PR.
|
|
15
|
-
//
|
|
16
|
-
// schema + instructions) and `wp-finish-upsert-pr` (which reads it to render the RISK section and
|
|
17
|
-
// post the PR). Data-only (per CLAUDE.md, classes for data).
|
|
16
|
+
// The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which
|
|
17
|
+
// prints the schema) and `wp-finish-upsert-pr` (which reads it). Data-only (per CLAUDE.md).
|
|
18
18
|
class ReviewJson {
|
|
19
19
|
title; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)
|
|
20
20
|
riskScore; // 0–100, drives the risk bar
|
|
@@ -38,94 +38,113 @@ class ReviewJson {
|
|
|
38
38
|
exports.ReviewJson = ReviewJson;
|
|
39
39
|
const RISK_LEVELS = ['green', 'yellow', 'red'];
|
|
40
40
|
const EMOJI_FOR_LEVEL = { green: '🟢', yellow: '🟡', red: '🔴' };
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//
|
|
41
|
+
/** Locates + loads/validates the AI-authored review.json. `@provideSingleton` so it's drawn in the design. */
|
|
42
|
+
let ReviewJsonService = class ReviewJsonService {
|
|
43
|
+
// The per-feature PR working dir: `.webpieces/pr-review/<feature>`.
|
|
44
|
+
prDirFor(repoRoot, featureName) {
|
|
45
|
+
return path.join(repoRoot, constants_1.WEBPIECES_TMP_DIR, constants_1.PR_REVIEW_DIR, featureName);
|
|
46
|
+
}
|
|
47
|
+
// Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name.
|
|
48
|
+
reviewJsonPath(repoRoot, featureName) {
|
|
49
|
+
return path.join(this.prDirFor(repoRoot, featureName), 'review.json');
|
|
50
|
+
}
|
|
51
|
+
// Copy-paste schema both commands print (write it / fix it).
|
|
52
|
+
reviewJsonSchemaHint(filePath) {
|
|
53
|
+
return (`Write your PR review to:\n ${filePath}\n\n` +
|
|
54
|
+
`with this exact JSON shape (riskEmoji optional — derived from riskLevel):\n\n` +
|
|
55
|
+
`{\n` +
|
|
56
|
+
` "title": "concise PR title describing the change (imperative, no branch names)",\n` +
|
|
57
|
+
` "riskScore": 0, // integer 0–100 (higher = riskier)\n` +
|
|
58
|
+
` "riskLevel": "green | yellow | red",\n` +
|
|
59
|
+
` "summary": "5–10 sentence review summary",\n` +
|
|
60
|
+
` "violations": ["pattern/architecture violations you found (empty array if none)"],\n` +
|
|
61
|
+
` "risks": ["notable risks (empty array if none)"],\n` +
|
|
62
|
+
` "filesToReview": ["paths a human should look at (empty array if none)"]\n` +
|
|
63
|
+
`}`);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,
|
|
67
|
+
* unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.
|
|
68
|
+
*/
|
|
69
|
+
// webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields
|
|
70
|
+
loadReviewJson(filePath) {
|
|
71
|
+
if (!fs.existsSync(filePath)) {
|
|
72
|
+
throw new inform_ai_error_1.InformAiError(`Required review.json not found.\n\n${this.reviewJsonSchemaHint(filePath)}\n\n` +
|
|
73
|
+
`Then re-run: pnpm wp-finish-upsert-pr`);
|
|
74
|
+
}
|
|
75
|
+
const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);
|
|
76
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
77
|
+
throw new inform_ai_error_1.InformAiError(`review.json must be a JSON object.\n\n${this.reviewJsonSchemaHint(filePath)}`);
|
|
78
|
+
}
|
|
79
|
+
const errors = [];
|
|
80
|
+
const riskScore = raw['riskScore'];
|
|
81
|
+
if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {
|
|
82
|
+
errors.push(`"riskScore" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);
|
|
83
|
+
}
|
|
84
|
+
const riskLevel = raw['riskLevel'];
|
|
85
|
+
if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel)) {
|
|
86
|
+
errors.push(`"riskLevel" must be one of: ${RISK_LEVELS.join(', ')}.`);
|
|
87
|
+
}
|
|
88
|
+
const title = typeof raw['title'] === 'string' ? raw['title'].trim() : '';
|
|
89
|
+
if (title === '') {
|
|
90
|
+
errors.push('"title" must be a non-empty, imperative PR title describing the change (no branch names).');
|
|
91
|
+
}
|
|
92
|
+
if (errors.length > 0) {
|
|
93
|
+
throw new inform_ai_error_1.InformAiError(`review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\n\n` +
|
|
94
|
+
errors.map((e) => ` • ${e}`).join('\n') +
|
|
95
|
+
`\n\n${this.reviewJsonSchemaHint(filePath)}`);
|
|
96
|
+
}
|
|
97
|
+
const level = riskLevel;
|
|
98
|
+
const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''
|
|
99
|
+
? raw['riskEmoji']
|
|
100
|
+
: (EMOJI_FOR_LEVEL[level] ?? '🟡');
|
|
101
|
+
const summary = typeof raw['summary'] === 'string' ? raw['summary'] : '';
|
|
102
|
+
return new ReviewJson(title, riskScore, level, emoji, summary, this.asStringArray(raw['violations']), this.asStringArray(raw['risks']), this.asStringArray(raw['filesToReview']));
|
|
103
|
+
}
|
|
104
|
+
// webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here
|
|
105
|
+
asStringArray(value) {
|
|
106
|
+
if (!Array.isArray(value))
|
|
107
|
+
return [];
|
|
108
|
+
// webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard
|
|
109
|
+
return value.filter((v) => typeof v === 'string');
|
|
110
|
+
}
|
|
111
|
+
// Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.
|
|
112
|
+
// webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field
|
|
113
|
+
parseReviewJson(raw, filePath) {
|
|
114
|
+
// webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI
|
|
115
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
116
|
+
try {
|
|
117
|
+
// webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller
|
|
118
|
+
return JSON.parse(raw);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const error = (0, to_error_1.toError)(err);
|
|
122
|
+
throw new inform_ai_error_1.InformAiError(`review.json is not valid JSON (${error.message}).\n\n${this.reviewJsonSchemaHint(filePath)}\n\n` +
|
|
123
|
+
`Then re-run: pnpm wp-finish-upsert-pr`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
exports.ReviewJsonService = ReviewJsonService;
|
|
128
|
+
exports.ReviewJsonService = ReviewJsonService = tslib_1.__decorate([
|
|
129
|
+
(0, core_context_1.provideSingleton)(),
|
|
130
|
+
(0, inversify_1.injectable)()
|
|
131
|
+
], ReviewJsonService);
|
|
132
|
+
// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.
|
|
133
|
+
const reviewJsonSvc = new ReviewJsonService();
|
|
134
|
+
// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it
|
|
44
135
|
function prDirFor(repoRoot, featureName) {
|
|
45
|
-
return
|
|
136
|
+
return reviewJsonSvc.prDirFor(repoRoot, featureName);
|
|
46
137
|
}
|
|
47
|
-
//
|
|
48
|
-
// AI and the finish command agree on the location without passing it around.
|
|
138
|
+
// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it
|
|
49
139
|
function reviewJsonPath(repoRoot, featureName) {
|
|
50
|
-
return
|
|
140
|
+
return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);
|
|
51
141
|
}
|
|
52
|
-
//
|
|
53
|
-
// wp-finish-upsert-pr to instruct the AI to FIX it when missing/invalid.
|
|
142
|
+
// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it
|
|
54
143
|
function reviewJsonSchemaHint(filePath) {
|
|
55
|
-
return (
|
|
56
|
-
`with this exact JSON shape (riskEmoji optional — derived from riskLevel):\n\n` +
|
|
57
|
-
`{\n` +
|
|
58
|
-
` "title": "concise PR title describing the change (imperative, no branch names)",\n` +
|
|
59
|
-
` "riskScore": 0, // integer 0–100 (higher = riskier)\n` +
|
|
60
|
-
` "riskLevel": "green | yellow | red",\n` +
|
|
61
|
-
` "summary": "5–10 sentence review summary",\n` +
|
|
62
|
-
` "violations": ["pattern/architecture violations you found (empty array if none)"],\n` +
|
|
63
|
-
` "risks": ["notable risks (empty array if none)"],\n` +
|
|
64
|
-
` "filesToReview": ["paths a human should look at (empty array if none)"]\n` +
|
|
65
|
-
`}`);
|
|
66
|
-
}
|
|
67
|
-
// webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here
|
|
68
|
-
function asStringArray(value) {
|
|
69
|
-
if (!Array.isArray(value))
|
|
70
|
-
return [];
|
|
71
|
-
// webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard
|
|
72
|
-
return value.filter((v) => typeof v === 'string');
|
|
73
|
-
}
|
|
74
|
-
// Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError the AI can
|
|
75
|
-
// act on (mirrors readRawConfig in config-file.ts — the established JSON-parse chokepoint).
|
|
76
|
-
// webpieces-disable no-any-unknown -- returns the opaque parsed object; the caller narrows each field
|
|
77
|
-
function parseReviewJson(raw, filePath) {
|
|
78
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI
|
|
79
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
80
|
-
try {
|
|
81
|
-
// webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller
|
|
82
|
-
return JSON.parse(raw);
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
const error = (0, to_error_1.toError)(err);
|
|
86
|
-
throw new inform_ai_error_1.InformAiError(`review.json is not valid JSON (${error.message}).\n\n${reviewJsonSchemaHint(filePath)}\n\n` +
|
|
87
|
-
`Then re-run: pnpm wp-finish-upsert-pr`);
|
|
88
|
-
}
|
|
144
|
+
return reviewJsonSvc.reviewJsonSchemaHint(filePath);
|
|
89
145
|
}
|
|
90
|
-
|
|
91
|
-
* Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when the file
|
|
92
|
-
* is missing, unparseable, or structurally wrong — the message is written straight back to the AI so
|
|
93
|
-
* it can fix the file and re-run. Returns a fully-populated ReviewJson on success.
|
|
94
|
-
*/
|
|
146
|
+
// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it
|
|
95
147
|
function loadReviewJson(filePath) {
|
|
96
|
-
|
|
97
|
-
throw new inform_ai_error_1.InformAiError(`Required review.json not found.\n\n${reviewJsonSchemaHint(filePath)}\n\n` +
|
|
98
|
-
`Then re-run: pnpm wp-finish-upsert-pr`);
|
|
99
|
-
}
|
|
100
|
-
const raw = parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);
|
|
101
|
-
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
102
|
-
throw new inform_ai_error_1.InformAiError(`review.json must be a JSON object.\n\n${reviewJsonSchemaHint(filePath)}`);
|
|
103
|
-
}
|
|
104
|
-
const errors = [];
|
|
105
|
-
const riskScore = raw['riskScore'];
|
|
106
|
-
if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {
|
|
107
|
-
errors.push(`"riskScore" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);
|
|
108
|
-
}
|
|
109
|
-
const riskLevel = raw['riskLevel'];
|
|
110
|
-
if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel)) {
|
|
111
|
-
errors.push(`"riskLevel" must be one of: ${RISK_LEVELS.join(', ')}.`);
|
|
112
|
-
}
|
|
113
|
-
// Title is REQUIRED (hard-reject): the AI must author a real PR title. We no longer silently fall
|
|
114
|
-
// back to the feature name — an empty title means the AI skipped the field, which is a review gap.
|
|
115
|
-
const title = typeof raw['title'] === 'string' ? raw['title'].trim() : '';
|
|
116
|
-
if (title === '') {
|
|
117
|
-
errors.push('"title" must be a non-empty, imperative PR title describing the change (no branch names).');
|
|
118
|
-
}
|
|
119
|
-
if (errors.length > 0) {
|
|
120
|
-
throw new inform_ai_error_1.InformAiError(`review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\n\n` +
|
|
121
|
-
errors.map((e) => ` • ${e}`).join('\n') +
|
|
122
|
-
`\n\n${reviewJsonSchemaHint(filePath)}`);
|
|
123
|
-
}
|
|
124
|
-
const level = riskLevel;
|
|
125
|
-
const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''
|
|
126
|
-
? raw['riskEmoji']
|
|
127
|
-
: (EMOJI_FOR_LEVEL[level] ?? '🟡');
|
|
128
|
-
const summary = typeof raw['summary'] === 'string' ? raw['summary'] : '';
|
|
129
|
-
return new ReviewJson(title, riskScore, level, emoji, summary, asStringArray(raw['violations']), asStringArray(raw['risks']), asStringArray(raw['filesToReview']));
|
|
148
|
+
return reviewJsonSvc.loadReviewJson(filePath);
|
|
130
149
|
}
|
|
131
150
|
//# sourceMappingURL=review-json.js.map
|
package/src/review-json.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-json.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { WEBPIECES_TMP_DIR, PR_REVIEW_DIR } from './constants';\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\n// The AI-authored review for a PR. webpieces is AI-first, so unlike trytami (where a human command\n// calls Claude), the AI writes this file itself between `wp-start-upsert-pr` (which prints the\n// schema + instructions) and `wp-finish-upsert-pr` (which reads it to render the RISK section and\n// post the PR). Data-only (per CLAUDE.md, classes for data).\nexport class ReviewJson {\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n\n constructor(\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n ) {\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n }\n}\n\nconst RISK_LEVELS = ['green', 'yellow', 'red'] as const;\nconst EMOJI_FOR_LEVEL: Record<string, string> = { green: '🟢', yellow: '🟡', red: '🔴' };\n\n// The per-feature PR working dir: `.webpieces/pr-review/<feature>`. Holds pr-body.md (rendered\n// dashboard) and review.json (AI-authored review). Nested under pr-review/ to keep `.webpieces/`\n// top level quiet. Shared so the start/finish commands and the AI agree on one location.\nexport function prDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, PR_REVIEW_DIR, featureName);\n}\n\n// Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name so the\n// AI and the finish command agree on the location without passing it around.\nexport function reviewJsonPath(repoRoot: string, featureName: string): string {\n return path.join(prDirFor(repoRoot, featureName), 'review.json');\n}\n\n// Copy-paste schema both commands print: wp-start-upsert-pr to instruct the AI to WRITE it,\n// wp-finish-upsert-pr to instruct the AI to FIX it when missing/invalid.\nexport function reviewJsonSchemaHint(filePath: string): string {\n return (\n `Write your PR review to:\\n ${filePath}\\n\\n` +\n `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\\n\\n` +\n `{\\n` +\n ` \"title\": \"concise PR title describing the change (imperative, no branch names)\",\\n` +\n ` \"riskScore\": 0, // integer 0–100 (higher = riskier)\\n` +\n ` \"riskLevel\": \"green | yellow | red\",\\n` +\n ` \"summary\": \"5–10 sentence review summary\",\\n` +\n ` \"violations\": [\"pattern/architecture violations you found (empty array if none)\"],\\n` +\n ` \"risks\": [\"notable risks (empty array if none)\"],\\n` +\n ` \"filesToReview\": [\"paths a human should look at (empty array if none)\"]\\n` +\n `}`\n );\n}\n\n// webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\nfunction asStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard\n return value.filter((v: unknown): v is string => typeof v === 'string');\n}\n\n// Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError the AI can\n// act on (mirrors readRawConfig in config-file.ts — the established JSON-parse chokepoint).\n// webpieces-disable no-any-unknown -- returns the opaque parsed object; the caller narrows each field\nfunction parseReviewJson(raw: string, filePath: string): Record<string, unknown> {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `review.json is not valid JSON (${error.message}).\\n\\n${reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n}\n\n/**\n * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when the file\n * is missing, unparseable, or structurally wrong — the message is written straight back to the AI so\n * it can fix the file and re-run. Returns a fully-populated ReviewJson on success.\n */\nexport function loadReviewJson(filePath: string): ReviewJson {\n if (!fs.existsSync(filePath)) {\n throw new InformAiError(\n `Required review.json not found.\\n\\n${reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n\n const raw = parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new InformAiError(`review.json must be a JSON object.\\n\\n${reviewJsonSchemaHint(filePath)}`);\n }\n\n const errors: string[] = [];\n\n const riskScore = raw['riskScore'];\n if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {\n errors.push(`\"riskScore\" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);\n }\n\n const riskLevel = raw['riskLevel'];\n if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel as typeof RISK_LEVELS[number])) {\n errors.push(`\"riskLevel\" must be one of: ${RISK_LEVELS.join(', ')}.`);\n }\n\n // Title is REQUIRED (hard-reject): the AI must author a real PR title. We no longer silently fall\n // back to the feature name — an empty title means the AI skipped the field, which is a review gap.\n const title = typeof raw['title'] === 'string' ? (raw['title'] as string).trim() : '';\n if (title === '') {\n errors.push('\"title\" must be a non-empty, imperative PR title describing the change (no branch names).');\n }\n\n if (errors.length > 0) {\n throw new InformAiError(\n `review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\n${reviewJsonSchemaHint(filePath)}`,\n );\n }\n\n const level = riskLevel as string;\n const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''\n ? (raw['riskEmoji'] as string)\n : (EMOJI_FOR_LEVEL[level] ?? '🟡');\n const summary = typeof raw['summary'] === 'string' ? (raw['summary'] as string) : '';\n\n return new ReviewJson(\n title,\n riskScore as number,\n level,\n emoji,\n summary,\n asStringArray(raw['violations']),\n asStringArray(raw['risks']),\n asStringArray(raw['filesToReview']),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"review-json.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\nimport { WEBPIECES_TMP_DIR, PR_REVIEW_DIR } from './constants';\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\n// The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it). Data-only (per CLAUDE.md).\nexport class ReviewJson {\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n\n constructor(\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n ) {\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n }\n}\n\nconst RISK_LEVELS = ['green', 'yellow', 'red'] as const;\nconst EMOJI_FOR_LEVEL: Record<string, string> = { green: '🟢', yellow: '🟡', red: '🔴' };\n\n/** Locates + loads/validates the AI-authored review.json. `@provideSingleton` so it's drawn in the design. */\n@provideSingleton()\n@injectable()\nexport class ReviewJsonService {\n // The per-feature PR working dir: `.webpieces/pr-review/<feature>`.\n prDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, PR_REVIEW_DIR, featureName);\n }\n\n // Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name.\n reviewJsonPath(repoRoot: string, featureName: string): string {\n return path.join(this.prDirFor(repoRoot, featureName), 'review.json');\n }\n\n // Copy-paste schema both commands print (write it / fix it).\n reviewJsonSchemaHint(filePath: string): string {\n return (\n `Write your PR review to:\\n ${filePath}\\n\\n` +\n `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\\n\\n` +\n `{\\n` +\n ` \"title\": \"concise PR title describing the change (imperative, no branch names)\",\\n` +\n ` \"riskScore\": 0, // integer 0–100 (higher = riskier)\\n` +\n ` \"riskLevel\": \"green | yellow | red\",\\n` +\n ` \"summary\": \"5–10 sentence review summary\",\\n` +\n ` \"violations\": [\"pattern/architecture violations you found (empty array if none)\"],\\n` +\n ` \"risks\": [\"notable risks (empty array if none)\"],\\n` +\n ` \"filesToReview\": [\"paths a human should look at (empty array if none)\"]\\n` +\n `}`\n );\n }\n\n /**\n * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,\n * unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields\n loadReviewJson(filePath: string): ReviewJson {\n if (!fs.existsSync(filePath)) {\n throw new InformAiError(\n `Required review.json not found.\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n\n const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new InformAiError(`review.json must be a JSON object.\\n\\n${this.reviewJsonSchemaHint(filePath)}`);\n }\n\n const errors: string[] = [];\n\n const riskScore = raw['riskScore'];\n if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {\n errors.push(`\"riskScore\" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);\n }\n\n const riskLevel = raw['riskLevel'];\n if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel as typeof RISK_LEVELS[number])) {\n errors.push(`\"riskLevel\" must be one of: ${RISK_LEVELS.join(', ')}.`);\n }\n\n const title = typeof raw['title'] === 'string' ? (raw['title'] as string).trim() : '';\n if (title === '') {\n errors.push('\"title\" must be a non-empty, imperative PR title describing the change (no branch names).');\n }\n\n if (errors.length > 0) {\n throw new InformAiError(\n `review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\n${this.reviewJsonSchemaHint(filePath)}`,\n );\n }\n\n const level = riskLevel as string;\n const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''\n ? (raw['riskEmoji'] as string)\n : (EMOJI_FOR_LEVEL[level] ?? '🟡');\n const summary = typeof raw['summary'] === 'string' ? (raw['summary'] as string) : '';\n\n return new ReviewJson(\n title,\n riskScore as number,\n level,\n emoji,\n summary,\n this.asStringArray(raw['violations']),\n this.asStringArray(raw['risks']),\n this.asStringArray(raw['filesToReview']),\n );\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\n private asStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard\n return value.filter((v: unknown): v is string => typeof v === 'string');\n }\n\n // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.\n // webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field\n private parseReviewJson(raw: string, filePath: string): Record<string, unknown> {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `review.json is not valid JSON (${error.message}).\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n }\n}\n\n// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.\nconst reviewJsonSvc = new ReviewJsonService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function prDirFor(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.prDirFor(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonPath(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonSchemaHint(filePath: string): string {\n return reviewJsonSvc.reviewJsonSchemaHint(filePath);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function loadReviewJson(filePath: string): ReviewJson {\n return reviewJsonSvc.loadReviewJson(filePath);\n}\n"]}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { RepoRootFinder } from './repo-root';
|
|
2
2
|
import { ConfigLoader } from './load-config';
|
|
3
3
|
import { TemplateWriter } from './load-template';
|
|
4
|
+
import { DiffScope } from './diff-scope';
|
|
5
|
+
import { BranchMutationLog } from './branch-mutation-log';
|
|
6
|
+
import { ReviewJsonService } from './review-json';
|
|
4
7
|
/**
|
|
5
8
|
* DI-design root for @webpieces/rules-config (role:designed-lib).
|
|
6
9
|
*
|
|
@@ -14,5 +17,8 @@ export declare class RulesConfigDesign {
|
|
|
14
17
|
private readonly repoRootFinder;
|
|
15
18
|
private readonly configLoader;
|
|
16
19
|
private readonly templateWriter;
|
|
17
|
-
|
|
20
|
+
private readonly diffScope;
|
|
21
|
+
private readonly branchMutationLog;
|
|
22
|
+
private readonly reviewJson;
|
|
23
|
+
constructor(repoRootFinder: RepoRootFinder, configLoader: ConfigLoader, templateWriter: TemplateWriter, diffScope: DiffScope, branchMutationLog: BranchMutationLog, reviewJson: ReviewJsonService);
|
|
18
24
|
}
|
|
@@ -8,6 +8,9 @@ const inversify_1 = require("inversify");
|
|
|
8
8
|
const repo_root_1 = require("./repo-root");
|
|
9
9
|
const load_config_1 = require("./load-config");
|
|
10
10
|
const load_template_1 = require("./load-template");
|
|
11
|
+
const diff_scope_1 = require("./diff-scope");
|
|
12
|
+
const branch_mutation_log_1 = require("./branch-mutation-log");
|
|
13
|
+
const review_json_1 = require("./review-json");
|
|
11
14
|
/**
|
|
12
15
|
* DI-design root for @webpieces/rules-config (role:designed-lib).
|
|
13
16
|
*
|
|
@@ -21,10 +24,16 @@ let RulesConfigDesign = class RulesConfigDesign {
|
|
|
21
24
|
repoRootFinder;
|
|
22
25
|
configLoader;
|
|
23
26
|
templateWriter;
|
|
24
|
-
|
|
27
|
+
diffScope;
|
|
28
|
+
branchMutationLog;
|
|
29
|
+
reviewJson;
|
|
30
|
+
constructor(repoRootFinder, configLoader, templateWriter, diffScope, branchMutationLog, reviewJson) {
|
|
25
31
|
this.repoRootFinder = repoRootFinder;
|
|
26
32
|
this.configLoader = configLoader;
|
|
27
33
|
this.templateWriter = templateWriter;
|
|
34
|
+
this.diffScope = diffScope;
|
|
35
|
+
this.branchMutationLog = branchMutationLog;
|
|
36
|
+
this.reviewJson = reviewJson;
|
|
28
37
|
}
|
|
29
38
|
};
|
|
30
39
|
exports.RulesConfigDesign = RulesConfigDesign;
|
|
@@ -34,6 +43,9 @@ exports.RulesConfigDesign = RulesConfigDesign = tslib_1.__decorate([
|
|
|
34
43
|
(0, inversify_1.injectable)(),
|
|
35
44
|
tslib_1.__metadata("design:paramtypes", [repo_root_1.RepoRootFinder,
|
|
36
45
|
load_config_1.ConfigLoader,
|
|
37
|
-
load_template_1.TemplateWriter
|
|
46
|
+
load_template_1.TemplateWriter,
|
|
47
|
+
diff_scope_1.DiffScope,
|
|
48
|
+
branch_mutation_log_1.BranchMutationLog,
|
|
49
|
+
review_json_1.ReviewJsonService])
|
|
38
50
|
], RulesConfigDesign);
|
|
39
51
|
//# sourceMappingURL=rules-config-design.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules-config-design.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/rules-config-design.ts"],"names":[],"mappings":";;;;AAAA,oDAAsD;AACtD,0DAA2D;AAC3D,yCAAuC;AAEvC,2CAA6C;AAC7C,+CAA6C;AAC7C,mDAAiD;
|
|
1
|
+
{"version":3,"file":"rules-config-design.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/rules-config-design.ts"],"names":[],"mappings":";;;;AAAA,oDAAsD;AACtD,0DAA2D;AAC3D,yCAAuC;AAEvC,2CAA6C;AAC7C,+CAA6C;AAC7C,mDAAiD;AACjD,6CAAyC;AACzC,+DAA0D;AAC1D,+CAAkD;AAElD;;;;;;;;GAQG;AAII,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAEL;IACA;IACA;IACA;IACA;IACA;IANrB,YACqB,cAA8B,EAC9B,YAA0B,EAC1B,cAA8B,EAC9B,SAAoB,EACpB,iBAAoC,EACpC,UAA6B;QAL7B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,iBAAY,GAAZ,YAAY,CAAc;QAC1B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,cAAS,GAAT,SAAS,CAAW;QACpB,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,eAAU,GAAV,UAAU,CAAmB;IAC/C,CAAC;CACP,CAAA;AATY,8CAAiB;4BAAjB,iBAAiB;IAH7B,IAAA,0BAAc,GAAE;IAChB,IAAA,+BAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;6CAG4B,0BAAc;QAChB,0BAAY;QACV,8BAAc;QACnB,sBAAS;QACD,uCAAiB;QACxB,+BAAiB;GAPzC,iBAAiB,CAS7B","sourcesContent":["import { DocumentDesign } from '@webpieces/core-util';\nimport { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\n\nimport { RepoRootFinder } from './repo-root';\nimport { ConfigLoader } from './load-config';\nimport { TemplateWriter } from './load-template';\nimport { DiffScope } from './diff-scope';\nimport { BranchMutationLog } from './branch-mutation-log';\nimport { ReviewJsonService } from './review-json';\n\n/**\n * DI-design root for @webpieces/rules-config (role:designed-lib).\n *\n * `@DocumentDesign` marks the top of the DAG the DI-design analyzer roots on, so the library's design\n * (design.json / design.md / design.html) is generated. rules-config is the shared foundation whose\n * utilities are being migrated from free functions to injected `@provideSingleton` service classes; as\n * each service class lands (config loader, template writer, diff/git services, …) it is injected HERE\n * so it appears in the drawn design.\n */\n@DocumentDesign()\n@provideSingleton()\n@injectable()\nexport class RulesConfigDesign {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly configLoader: ConfigLoader,\n private readonly templateWriter: TemplateWriter,\n private readonly diffScope: DiffScope,\n private readonly branchMutationLog: BranchMutationLog,\n private readonly reviewJson: ReviewJsonService,\n ) {}\n}\n"]}
|