@webpieces/rules-config 0.3.310 → 0.3.311
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 +1 -1
- package/src/constants.d.ts +1 -1
- package/src/constants.js +6 -3
- package/src/constants.js.map +1 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +2 -2
- package/src/index.js.map +1 -1
- package/src/review-json.js +3 -3
- package/src/review-json.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/rules-config",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.311",
|
|
4
4
|
"description": "Shared webpieces.config.json loader. Single source of truth for validation rule configuration consumed by @webpieces/ai-hook-rules, @webpieces/code-rules, and @webpieces/nx-webpieces-rules.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/constants.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare const RULE_NAMES: {
|
|
|
20
20
|
};
|
|
21
21
|
export declare const WEBPIECES_TMP_DIR = ".webpieces";
|
|
22
22
|
export declare const MERGE_INFO_DIR = "merge-info";
|
|
23
|
-
export declare const
|
|
23
|
+
export declare const PR_REVIEW_DIR = "pr-review";
|
|
24
24
|
export declare const MERGE_IN_PROGRESS_FILE = "merge-in-progress.json";
|
|
25
25
|
export declare const MERGE_EXPLANATION_FILE = "merge-explanation.md";
|
|
26
26
|
/**
|
package/src/constants.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// The legacy `ai-hook-disable` alias and the `-file`/`-next`/`-all` variants and the
|
|
7
7
|
// `*`/bare (no-rule) wildcard have been removed — every disable MUST name a rule.
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.MERGE_EXPLANATION_FILE = exports.MERGE_IN_PROGRESS_FILE = exports.
|
|
9
|
+
exports.MERGE_EXPLANATION_FILE = exports.MERGE_IN_PROGRESS_FILE = exports.PR_REVIEW_DIR = exports.MERGE_INFO_DIR = exports.WEBPIECES_TMP_DIR = exports.RULE_NAMES = exports.WEBPIECES_DISABLE = void 0;
|
|
10
10
|
exports.hasDisable = hasDisable;
|
|
11
11
|
exports.WEBPIECES_DISABLE = 'webpieces-disable';
|
|
12
12
|
// Rule-name tokens as they appear AFTER `webpieces-disable` in a disable comment.
|
|
@@ -40,12 +40,15 @@ exports.RULE_NAMES = {
|
|
|
40
40
|
// `.webpieces/` is the single working dir for all webpieces tooling: ai-hook-rules
|
|
41
41
|
// bootstrap/cache, the instruct-ai docs, and the workflow state. To keep the top level
|
|
42
42
|
// quiet, per-feature workflow dirs are nested one level down under `merge-info/<feature>`
|
|
43
|
-
// and `pr-
|
|
43
|
+
// and `pr-review/<feature>` rather than scattered as top-level `merge-<feature>`/`pr-<feature>`.
|
|
44
44
|
// `.webpieces/` is gitignored; only the per-feature subdirs under those two homes are subject
|
|
45
45
|
// to 30-day cleanup (the homes themselves, like hooks/ and instruct-ai/, are permanent).
|
|
46
46
|
exports.WEBPIECES_TMP_DIR = '.webpieces';
|
|
47
47
|
exports.MERGE_INFO_DIR = 'merge-info';
|
|
48
|
-
|
|
48
|
+
// The PR working home. Renamed from the legacy `pr-info` to `pr-review` for clarity (it holds the
|
|
49
|
+
// AI's PR review + rendered body). Old `pr-info/` dirs are gitignored local state and self-clear via
|
|
50
|
+
// cleanTmp's legacy `pr-` sweep.
|
|
51
|
+
exports.PR_REVIEW_DIR = 'pr-review';
|
|
49
52
|
exports.MERGE_IN_PROGRESS_FILE = 'merge-in-progress.json';
|
|
50
53
|
// Proof-of-work the AI must produce for every conflicted file it resolves during a 3-point
|
|
51
54
|
// merge: a short explanation written NEXT TO that file's 3-point context (the same
|
package/src/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/constants.ts"],"names":[],"mappings":";AAAA,iFAAiF;AACjF,sEAAsE;AACtE,EAAE;AACF,yFAAyF;AACzF,qFAAqF;AACrF,kFAAkF;;;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/constants.ts"],"names":[],"mappings":";AAAA,iFAAiF;AACjF,sEAAsE;AACtE,EAAE;AACF,yFAAyF;AACzF,qFAAqF;AACrF,kFAAkF;;;AA8DlF,gCAEC;AA9DY,QAAA,iBAAiB,GAAG,mBAAmB,CAAC;AAErD,kFAAkF;AAClF,uFAAuF;AACvF,uEAAuE;AACvE,sFAAsF;AACzE,QAAA,UAAU,GAAG;IACtB,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,cAAc,EAAE,gBAAgB;IAChC,uBAAuB,EAAE,yBAAyB;IAClD,mBAAmB,EAAE,qBAAqB;IAC1C,oBAAoB,EAAE,sBAAsB;IAC5C,mBAAmB,EAAE,qBAAqB;IAC1C,mBAAmB,EAAE,qBAAqB;IAC1C,4BAA4B,EAAE,8BAA8B;IAC5D,aAAa,EAAE,eAAe;IAC9B,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,iBAAiB;IAClC,sBAAsB,EAAE,wBAAwB;IAChD,gBAAgB,EAAE,kBAAkB;IACpC,qBAAqB,EAAE,uBAAuB;IAC9C,wBAAwB,EAAE,0BAA0B;IACpD,kBAAkB,EAAE,oBAAoB;CAClC,CAAC;AAEX,wFAAwF;AACxF,0FAA0F;AAC1F,sFAAsF;AACtF,0DAA0D;AAC1D,EAAE;AACF,mFAAmF;AACnF,uFAAuF;AACvF,0FAA0F;AAC1F,iGAAiG;AACjG,8FAA8F;AAC9F,yFAAyF;AAC5E,QAAA,iBAAiB,GAAG,YAAY,CAAC;AACjC,QAAA,cAAc,GAAG,YAAY,CAAC;AAC3C,kGAAkG;AAClG,qGAAqG;AACrG,iCAAiC;AACpB,QAAA,aAAa,GAAG,WAAW,CAAC;AAC5B,QAAA,sBAAsB,GAAG,wBAAwB,CAAC;AAE/D,2FAA2F;AAC3F,mFAAmF;AACnF,uFAAuF;AACvF,uGAAuG;AACvG,kGAAkG;AAClG,kGAAkG;AAClG,iDAAiD;AACpC,QAAA,sBAAsB,GAAG,sBAAsB,CAAC;AAE7D;;;;;GAKG;AACH,SAAgB,UAAU,CAAC,IAAY,EAAE,QAAgB;IACrD,OAAO,IAAI,CAAC,QAAQ,CAAC,yBAAiB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACvE,CAAC","sourcesContent":["// Single source of truth for the disable-comment token and rule-name identifiers\n// shared across rules-config, ai-hook-rules, code-rules, and pr-gate.\n//\n// There is exactly ONE disable form: `// webpieces-disable <rule>[, <rule2>] -- reason`.\n// The legacy `ai-hook-disable` alias and the `-file`/`-next`/`-all` variants and the\n// `*`/bare (no-rule) wildcard have been removed — every disable MUST name a rule.\n\nexport const WEBPIECES_DISABLE = 'webpieces-disable';\n\n// Rule-name tokens as they appear AFTER `webpieces-disable` in a disable comment.\n// Values must match existing comments exactly — changing a value silently breaks every\n// disable that names that rule. Note MAX_LINES_MODIFIED is a prefix of\n// MAX_LINES_MODIFIED_FILES (a historical substring-match quirk preserved on purpose).\nexport const RULE_NAMES = {\n NO_ANY_UNKNOWN: 'no-any-unknown',\n NO_IMPLICIT_ANY: 'no-implicit-any',\n NO_DESTRUCTURE: 'no-destructure',\n NO_UNMANAGED_EXCEPTIONS: 'no-unmanaged-exceptions',\n CATCH_ERROR_PATTERN: 'catch-error-pattern',\n THROW_CAUSE_REQUIRED: 'throw-cause-required',\n REQUIRE_RETURN_TYPE: 'require-return-type',\n NO_SYMBOL_DI_TOKENS: 'no-symbol-di-tokens',\n NO_PROCESS_EXIT_OUTSIDE_MAIN: 'no-process-exit-outside-main',\n FRAMEWORK_TAG: 'framework-tag',\n ROLE_TAG: 'role-tag',\n NO_INLINE_TYPES: 'no-inline-types',\n NO_DIRECT_API_RESOLVER: 'no-direct-api-resolver',\n PRISMA_CONVERTER: 'prisma-converter',\n MAX_LINES_NEW_METHODS: 'max-lines-new-methods',\n MAX_LINES_MODIFIED_FILES: 'max-lines-modified-files',\n MAX_LINES_MODIFIED: 'max-lines-modified',\n} as const;\n\n// Merge-state convention shared by the pr-gate scripts (which WRITE the marker during a\n// conflicted 3-point merge) and the ai-hook-rules merge-in-progress-guard (which READS it\n// to block commit/push/PR until the merge is validated). Kept here so neither package\n// depends on the other — they only share this vocabulary.\n//\n// `.webpieces/` is the single working dir for all webpieces tooling: ai-hook-rules\n// bootstrap/cache, the instruct-ai docs, and the workflow state. To keep the top level\n// quiet, per-feature workflow dirs are nested one level down under `merge-info/<feature>`\n// and `pr-review/<feature>` rather than scattered as top-level `merge-<feature>`/`pr-<feature>`.\n// `.webpieces/` is gitignored; only the per-feature subdirs under those two homes are subject\n// to 30-day cleanup (the homes themselves, like hooks/ and instruct-ai/, are permanent).\nexport const WEBPIECES_TMP_DIR = '.webpieces';\nexport const MERGE_INFO_DIR = 'merge-info';\n// The PR working home. Renamed from the legacy `pr-info` to `pr-review` for clarity (it holds the\n// AI's PR review + rendered body). Old `pr-info/` dirs are gitignored local state and self-clear via\n// cleanTmp's legacy `pr-` sweep.\nexport const PR_REVIEW_DIR = 'pr-review';\nexport const MERGE_IN_PROGRESS_FILE = 'merge-in-progress.json';\n\n// Proof-of-work the AI must produce for every conflicted file it resolves during a 3-point\n// merge: a short explanation written NEXT TO that file's 3-point context (the same\n// `updatemain-<safe_path>/` dir that holds A-forkpoint.txt / B-A.diff / C-A.diff). The\n// wp-finish-upsert-pr gate requires a non-empty file of this name per conflicted file before passing —\n// it is the only check on the part of the process the AI actually owns (resolving files). Using a\n// sidecar file (rather than an in-source comment) works for any file type, including comment-less\n// ones like JSON and files resolved by deletion.\nexport const MERGE_EXPLANATION_FILE = 'merge-explanation.md';\n\n/**\n * Fast predicate: does this text carry a webpieces-disable for the given rule?\n * Line-agnostic — the caller decides which line(s) or block of text to feed it.\n * This is the cheap substring form used by code-rules detection and pr-gate's\n * dashboard grep/count. (ai-hook-rules uses a richer line-mapping parser.)\n */\nexport function hasDisable(text: string, ruleName: string): boolean {\n return text.includes(WEBPIECES_DISABLE) && text.includes(ruleName);\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { shouldSkipRule, getCurrentBranch } from './skip-rule';
|
|
|
20
20
|
export type { SkipRuleResult } from './skip-rule';
|
|
21
21
|
export { detectBase, resolveBase, getChangedFiles, getFileDiff, getChangedLineNumbers, findNewMethodSignaturesInDiff, hasChangesInRange, isNewOrModified, } from './diff-scope';
|
|
22
22
|
export { AbstractRule } from './abstract-rule';
|
|
23
|
-
export { WEBPIECES_DISABLE, RULE_NAMES, hasDisable, WEBPIECES_TMP_DIR, MERGE_INFO_DIR,
|
|
23
|
+
export { WEBPIECES_DISABLE, RULE_NAMES, hasDisable, WEBPIECES_TMP_DIR, MERGE_INFO_DIR, PR_REVIEW_DIR, MERGE_IN_PROGRESS_FILE, MERGE_EXPLANATION_FILE, } from './constants';
|
|
24
24
|
export { WebpiecesRulesConfig } from './WebpiecesRulesConfig';
|
|
25
25
|
export { MaxMethodLinesConfig, MaxFileLinesConfig, RequireReturnTypeConfig, NoInlineTypeLiteralsConfig, NoAnyUnknownConfig, NoImplicitAnyConfig, PrismaValidateDtosConfig, PrismaConverterConfig, NoDestructureConfig, NoUnmanagedExceptionsConfig, CatchErrorPatternConfig, ThrowCauseRequiredConfig, AngularNoDirectApiInResolverConfig, NoSymbolDiTokensConfig, NoProcessExitOutsideMainConfig, FrameworkTagConfig, RoleTagConfig, BranchCreationGuardConfig, PrCreationOrPushGuardConfig, MergeInProgressGuardConfig, PrMergeGuardConfig, RedirectHowToMergeMainConfig, FeatureBranchGuardConfig, NoFileImportCyclesConfig, RuntimeArchitectureConfig, NxWiringConfig, DiGraphConfig, NoJsFilesConfig, ValidateTsInSrcConfig, BaseRuleConfig, } from './rule-configs';
|
|
26
26
|
export { METHOD_LIMIT_MODES, FILE_LIMIT_MODES, RETURN_TYPE_MODES, INLINE_TYPE_MODES, MODIFIED_CODE_MODES, PROJECT_MODES, PRISMA_DTOS_MODES, PRISMA_CONVERTER_MODES, DIRECT_API_RESOLVER_MODES, THROW_CAUSE_MODES, ON_OFF_MODES, STRUCTURAL_MODES, VALIDATE_TS_MODES, } from './rule-configs';
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.hasDisable = exports.RULE_NAMES = exports.WEBPIECES_DISABLE = exports.AbstractRule = exports.isNewOrModified = exports.hasChangesInRange = exports.findNewMethodSignaturesInDiff = exports.getChangedLineNumbers = exports.getFileDiff = exports.getChangedFiles = exports.resolveBase = exports.detectBase = exports.getCurrentBranch = exports.shouldSkipRule = exports.FieldDef = exports.sectionForRule = exports.isHookGuard = exports.HOOK_GUARD_NAMES = exports.DEFAULT_MATCH_RULES = exports.renderMatchRuleMessage = exports.compileMatchRulePatterns = exports.isMatchRuleAllowedPath = exports.findMatchRuleViolations = exports.MatchRuleViolation = exports.MatchRuleConfig = exports.allRuleNames = exports.validateMatchRulesSection = exports.validateExcludePaths = exports.validateCommandsSection = exports.validateSectionPlacement = exports.validatePrGateSection = exports.validateWebpiecesConfig = exports.writeTemplate = exports.writeTemplateIfMissing = exports.loadTemplate = exports.defaultRulesDir = exports.defaultRules = exports.isPathExcluded = exports.ExcludePaths = exports.CONFIG_FILENAME = exports.findConfigFile = exports.LoadedConfig = exports.loadAndValidate = exports.toError = exports.runMain = exports.CliExitError = exports.RuleFailError = exports.InformAiError = exports.ResolvedRuleConfig = exports.ResolvedConfig = void 0;
|
|
4
|
-
exports.GateDefinition = exports.VALIDATE_TS_MODES = exports.STRUCTURAL_MODES = exports.ON_OFF_MODES = exports.THROW_CAUSE_MODES = exports.DIRECT_API_RESOLVER_MODES = exports.PRISMA_CONVERTER_MODES = exports.PRISMA_DTOS_MODES = exports.PROJECT_MODES = exports.MODIFIED_CODE_MODES = exports.INLINE_TYPE_MODES = exports.RETURN_TYPE_MODES = exports.FILE_LIMIT_MODES = exports.METHOD_LIMIT_MODES = exports.BaseRuleConfig = exports.ValidateTsInSrcConfig = exports.NoJsFilesConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = exports.FeatureBranchGuardConfig = exports.RedirectHowToMergeMainConfig = exports.PrMergeGuardConfig = exports.MergeInProgressGuardConfig = exports.PrCreationOrPushGuardConfig = exports.BranchCreationGuardConfig = exports.RoleTagConfig = exports.FrameworkTagConfig = exports.NoProcessExitOutsideMainConfig = exports.NoSymbolDiTokensConfig = exports.AngularNoDirectApiInResolverConfig = exports.ThrowCauseRequiredConfig = exports.CatchErrorPatternConfig = exports.NoUnmanagedExceptionsConfig = exports.NoDestructureConfig = exports.PrismaConverterConfig = exports.PrismaValidateDtosConfig = exports.NoImplicitAnyConfig = exports.NoAnyUnknownConfig = exports.NoInlineTypeLiteralsConfig = exports.RequireReturnTypeConfig = exports.MaxFileLinesConfig = exports.MaxMethodLinesConfig = exports.WebpiecesRulesConfig = exports.MERGE_EXPLANATION_FILE = exports.MERGE_IN_PROGRESS_FILE = exports.
|
|
4
|
+
exports.GateDefinition = exports.VALIDATE_TS_MODES = exports.STRUCTURAL_MODES = exports.ON_OFF_MODES = exports.THROW_CAUSE_MODES = exports.DIRECT_API_RESOLVER_MODES = exports.PRISMA_CONVERTER_MODES = exports.PRISMA_DTOS_MODES = exports.PROJECT_MODES = exports.MODIFIED_CODE_MODES = exports.INLINE_TYPE_MODES = exports.RETURN_TYPE_MODES = exports.FILE_LIMIT_MODES = exports.METHOD_LIMIT_MODES = exports.BaseRuleConfig = exports.ValidateTsInSrcConfig = exports.NoJsFilesConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = exports.FeatureBranchGuardConfig = exports.RedirectHowToMergeMainConfig = exports.PrMergeGuardConfig = exports.MergeInProgressGuardConfig = exports.PrCreationOrPushGuardConfig = exports.BranchCreationGuardConfig = exports.RoleTagConfig = exports.FrameworkTagConfig = exports.NoProcessExitOutsideMainConfig = exports.NoSymbolDiTokensConfig = exports.AngularNoDirectApiInResolverConfig = exports.ThrowCauseRequiredConfig = exports.CatchErrorPatternConfig = exports.NoUnmanagedExceptionsConfig = exports.NoDestructureConfig = exports.PrismaConverterConfig = exports.PrismaValidateDtosConfig = exports.NoImplicitAnyConfig = exports.NoAnyUnknownConfig = exports.NoInlineTypeLiteralsConfig = exports.RequireReturnTypeConfig = exports.MaxFileLinesConfig = exports.MaxMethodLinesConfig = exports.WebpiecesRulesConfig = exports.MERGE_EXPLANATION_FILE = exports.MERGE_IN_PROGRESS_FILE = exports.PR_REVIEW_DIR = exports.MERGE_INFO_DIR = exports.WEBPIECES_TMP_DIR = void 0;
|
|
5
5
|
exports.DEFAULT_MERGE_COMPLETE_COMMAND = exports.DEFAULT_UPSERT_PR_COMMAND = exports.buildCommandsConfig = exports.CommandsConfig = exports.logBranchMutation = exports.branchMutationLogPath = exports.BranchMutationEvent = exports.squashRecoverySteps = exports.stampCleanMainSyncStatus = exports.computeMainSyncStatus = exports.finishedLock = exports.inProcessLock = exports.isRefreshInProgress = exports.isLockStale = exports.writeMainSyncLock = exports.readMainSyncLock = exports.writeMainSyncStatus = exports.readMainSyncStatus = exports.mainSyncLockPath = exports.mainSyncStatusPath = exports.DEFAULT_HANG_TIMEOUT_MINUTES = exports.MainSyncLock = exports.MainSyncStatus = exports.reviewJsonSchemaHint = exports.reviewJsonPath = exports.prDirFor = exports.loadReviewJson = exports.ReviewJson = exports.buildPrGateConfig = exports.defaultPrGateConfig = exports.defaultGates = exports.PrGateConfig = void 0;
|
|
6
6
|
var types_1 = require("./types");
|
|
7
7
|
Object.defineProperty(exports, "ResolvedConfig", { enumerable: true, get: function () { return types_1.ResolvedConfig; } });
|
|
@@ -75,7 +75,7 @@ Object.defineProperty(exports, "RULE_NAMES", { enumerable: true, get: function (
|
|
|
75
75
|
Object.defineProperty(exports, "hasDisable", { enumerable: true, get: function () { return constants_1.hasDisable; } });
|
|
76
76
|
Object.defineProperty(exports, "WEBPIECES_TMP_DIR", { enumerable: true, get: function () { return constants_1.WEBPIECES_TMP_DIR; } });
|
|
77
77
|
Object.defineProperty(exports, "MERGE_INFO_DIR", { enumerable: true, get: function () { return constants_1.MERGE_INFO_DIR; } });
|
|
78
|
-
Object.defineProperty(exports, "
|
|
78
|
+
Object.defineProperty(exports, "PR_REVIEW_DIR", { enumerable: true, get: function () { return constants_1.PR_REVIEW_DIR; } });
|
|
79
79
|
Object.defineProperty(exports, "MERGE_IN_PROGRESS_FILE", { enumerable: true, get: function () { return constants_1.MERGE_IN_PROGRESS_FILE; } });
|
|
80
80
|
Object.defineProperty(exports, "MERGE_EXPLANATION_FILE", { enumerable: true, get: function () { return constants_1.MERGE_EXPLANATION_FILE; } });
|
|
81
81
|
var WebpiecesRulesConfig_1 = require("./WebpiecesRulesConfig");
|
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,6CAA8D;AAArD,8GAAA,eAAe,OAAA;AAAE,2GAAA,YAAY,OAAA;AACtC,6CAAgE;AAAvD,6GAAA,cAAc,OAAA;AAAE,8GAAA,eAAe,OAAA;AACxC,2DAAoD;AAA3C,kHAAA,YAAY,OAAA;AACrB,iDAAiD;AAAxC,+GAAA,cAAc,OAAA;AACvB,iDAAgE;AAAvD,6GAAA,YAAY,OAAA;AAAE,gHAAA,eAAe,OAAA;AACtC,iDAAsF;AAA7E,6GAAA,YAAY,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AAAE,8GAAA,aAAa,OAAA;AAC5D,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,2CASsB;AARlB,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;AAEnB,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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":"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","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 } from './load-config';\nexport { findConfigFile, CONFIG_FILENAME } from './config-file';\nexport { ExcludePaths } from './exclude-hook-paths';\nexport { isPathExcluded } from './exclude-paths';\nexport { defaultRules, defaultRulesDir } from './default-rules';\nexport { loadTemplate, writeTemplateIfMissing, writeTemplate } 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} 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 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 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 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.js
CHANGED
|
@@ -38,11 +38,11 @@ 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
|
-
// The per-feature PR working dir: `.webpieces/pr-
|
|
42
|
-
// dashboard) and review.json (AI-authored review). Nested under pr-
|
|
41
|
+
// The per-feature PR working dir: `.webpieces/pr-review/<feature>`. Holds pr-body.md (rendered
|
|
42
|
+
// dashboard) and review.json (AI-authored review). Nested under pr-review/ to keep `.webpieces/`
|
|
43
43
|
// top level quiet. Shared so the start/finish commands and the AI agree on one location.
|
|
44
44
|
function prDirFor(repoRoot, featureName) {
|
|
45
|
-
return path.join(repoRoot, constants_1.WEBPIECES_TMP_DIR, constants_1.
|
|
45
|
+
return path.join(repoRoot, constants_1.WEBPIECES_TMP_DIR, constants_1.PR_REVIEW_DIR, featureName);
|
|
46
46
|
}
|
|
47
47
|
// Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name so the
|
|
48
48
|
// AI and the finish command agree on the location without passing it around.
|
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_INFO_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-info/<feature>`. Holds pr-body.md (rendered\n// dashboard) and review.json (AI-authored review). Nested under pr-info/ 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_INFO_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":";;;AA+CA,4BAEC;AAID,wCAEC;AAID,oDAcC;AAgCD,wCAwDC;;AAjKD,+CAAyB;AACzB,mDAA6B;AAC7B,2CAA+D;AAC/D,uDAAkD;AAClD,yCAAqC;AAErC,mGAAmG;AACnG,+FAA+F;AAC/F,kGAAkG;AAClG,6DAA6D;AAC7D,MAAa,UAAU;IACnB,KAAK,CAAS,CAAC,8FAA8F;IAC7G,SAAS,CAAS,CAAC,6BAA6B;IAChD,SAAS,CAAS,CAAC,6BAA6B;IAChD,SAAS,CAAS,CAAC,2DAA2D;IAC9E,OAAO,CAAS,CAAC,4CAA4C;IAC7D,UAAU,CAAW,CAAC,yEAAyE;IAC/F,KAAK,CAAW;IAChB,aAAa,CAAW;IAExB,YACI,KAAa,EACb,SAAiB,EACjB,SAAiB,EACjB,SAAiB,EACjB,OAAe,EACf,UAAoB,EACpB,KAAe,EACf,aAAuB;QAEvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACvC,CAAC;CACJ;AA7BD,gCA6BC;AAED,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAU,CAAC;AACxD,MAAM,eAAe,GAA2B,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAEzF,+FAA+F;AAC/F,iGAAiG;AACjG,yFAAyF;AACzF,SAAgB,QAAQ,CAAC,QAAgB,EAAE,WAAmB;IAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,6BAAiB,EAAE,yBAAa,EAAE,WAAW,CAAC,CAAC;AAC9E,CAAC;AAED,kGAAkG;AAClG,6EAA6E;AAC7E,SAAgB,cAAc,CAAC,QAAgB,EAAE,WAAmB;IAChE,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,aAAa,CAAC,CAAC;AACrE,CAAC;AAED,4FAA4F;AAC5F,yEAAyE;AACzE,SAAgB,oBAAoB,CAAC,QAAgB;IACjD,OAAO,CACH,+BAA+B,QAAQ,MAAM;QAC7C,+EAA+E;QAC/E,KAAK;QACL,sFAAsF;QACtF,+EAA+E;QAC/E,0CAA0C;QAC1C,gDAAgD;QAChD,wFAAwF;QACxF,uDAAuD;QACvD,6EAA6E;QAC7E,GAAG,CACN,CAAC;AACN,CAAC;AAED,0FAA0F;AAC1F,SAAS,aAAa,CAAC,KAAc;IACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,kGAAkG;IAClG,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAU,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED,mGAAmG;AACnG,4FAA4F;AAC5F,sGAAsG;AACtG,SAAS,eAAe,CAAC,GAAW,EAAE,QAAgB;IAClD,yHAAyH;IACzH,8DAA8D;IAC9D,IAAI,CAAC;QACD,yFAAyF;QACzF,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;IACtD,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,IAAI,+BAAa,CACnB,kCAAkC,KAAK,CAAC,OAAO,SAAS,oBAAoB,CAAC,QAAQ,CAAC,MAAM;YAC5F,uCAAuC,CAC1C,CAAC;IACN,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,QAAgB;IAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,+BAAa,CACnB,sCAAsC,oBAAoB,CAAC,QAAQ,CAAC,MAAM;YAC1E,uCAAuC,CAC1C,CAAC;IACN,CAAC;IAED,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;IACzE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,+BAAa,CAAC,yCAAyC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;IACnC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QACnG,MAAM,CAAC,IAAI,CAAC,2CAA2C,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;IACnC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAuC,CAAC,EAAE,CAAC;QAClG,MAAM,CAAC,IAAI,CAAC,+BAA+B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1E,CAAC;IAED,kGAAkG;IAClG,mGAAmG;IACnG,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,GAAG,CAAC,OAAO,CAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACtF,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAC;IAC7G,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,+BAAa,CACnB,mBAAmB,MAAM,CAAC,MAAM,gEAAgE;YAChG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAS,EAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACxD,OAAO,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAC1C,CAAC;IACN,CAAC;IAED,MAAM,KAAK,GAAG,SAAmB,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,WAAW,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE;QACzE,CAAC,CAAE,GAAG,CAAC,WAAW,CAAY;QAC9B,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,GAAG,CAAC,SAAS,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAErF,OAAO,IAAI,UAAU,CACjB,KAAK,EACL,SAAmB,EACnB,KAAK,EACL,KAAK,EACL,OAAO,EACP,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EAChC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAC3B,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CACtC,CAAC;AACN,CAAC","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"]}
|