@webpieces/rules-config 0.4.564 → 0.4.566
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/checklist-config.d.ts +24 -1
- package/src/checklist-config.js +26 -2
- package/src/checklist-config.js.map +1 -1
- package/src/cli-args.d.ts +58 -6
- package/src/cli-args.js +139 -13
- package/src/cli-args.js.map +1 -1
- package/src/constants.d.ts +1 -0
- package/src/constants.js +11 -1
- package/src/constants.js.map +1 -1
- package/src/index.d.ts +4 -4
- package/src/index.js +17 -7
- package/src/index.js.map +1 -1
- package/src/pr-gate-config.d.ts +40 -0
- package/src/pr-gate-config.js +67 -1
- package/src/pr-gate-config.js.map +1 -1
- package/src/pr-gate-section-validators.d.ts +10 -0
- package/src/pr-gate-section-validators.js +100 -2
- package/src/pr-gate-section-validators.js.map +1 -1
- package/src/review-json-data.d.ts +11 -1
- package/src/review-json-data.js +18 -4
- package/src/review-json-data.js.map +1 -1
- package/src/review-json.d.ts +13 -0
- package/src/review-json.js +21 -0
- package/src/review-json.js.map +1 -1
- package/src/reviewer-instructions.d.ts +1 -0
- package/src/reviewer-instructions.js +9 -0
- package/src/reviewer-instructions.js.map +1 -1
- package/src/sync-flow-guidance.d.ts +11 -0
- package/src/sync-flow-guidance.js +23 -1
- package/src/sync-flow-guidance.js.map +1 -1
- package/src/validate-config.js +3 -0
- package/src/validate-config.js.map +1 -1
- package/templates/webpieces.git-workflow.md +67 -0
- package/templates/webpieces.review-checklists.md +58 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/rules-config",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.566",
|
|
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",
|
|
@@ -3,16 +3,39 @@ export declare class ChecklistDefinition {
|
|
|
3
3
|
subagent: string;
|
|
4
4
|
doc: string;
|
|
5
5
|
patterns: string[];
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* true — BLOCKING. `wp-finish-upsert-pr` refuses the PR until this checklist has a passing verdict.
|
|
8
|
+
* false — OPTIONAL. When it matches, stage ② OFFERS it: the AI asks the human which optional reviewers
|
|
9
|
+
* to run, and the human may decline every one of them. A declined optional checklist never
|
|
10
|
+
* blocks.
|
|
11
|
+
*
|
|
12
|
+
* This governs whether the reviewer must RUN — NOT whether its verdict counts. An optional reviewer
|
|
13
|
+
* that is actually spawned and comes back red blocks finish exactly like a required one; otherwise
|
|
14
|
+
* running it would be theater.
|
|
15
|
+
*
|
|
16
|
+
* There is deliberately NO default (see the validator): a review process is the last thing that should
|
|
17
|
+
* acquire a scope silently, in either direction. Defaulting to true would leave the all-blocking status
|
|
18
|
+
* quo in place for anyone who did not read the release note; defaulting to false would quietly
|
|
19
|
+
* DOWNGRADE every existing consumer's gate on upgrade. Both are worse than one mechanical config edit
|
|
20
|
+
* that the coding agent reading the error applies in a single pass.
|
|
21
|
+
*/
|
|
22
|
+
required: boolean;
|
|
23
|
+
constructor(id: string, subagent: string, doc: string, patterns: string[], required: boolean);
|
|
7
24
|
}
|
|
8
25
|
export interface RawChecklistItem {
|
|
9
26
|
subagent?: string;
|
|
10
27
|
doc?: string;
|
|
11
28
|
patterns?: string[];
|
|
29
|
+
required?: boolean;
|
|
12
30
|
}
|
|
13
31
|
/**
|
|
14
32
|
* Build a ChecklistDefinition from an omitting-friendly raw entry. `id` defaults to the subagent name (the
|
|
15
33
|
* only stable, human-meaningful key we have).
|
|
34
|
+
*
|
|
35
|
+
* `required` is coerced with `=== true` rather than defaulted, and that is not a default in disguise:
|
|
36
|
+
* `validateChecklistArray` has already REJECTED any entry that omitted it or gave a non-boolean, so the
|
|
37
|
+
* only values that reach here are real booleans. The coercion exists so a validator that runs without a
|
|
38
|
+
* repoRoot (structure-only) still produces a well-typed def instead of `undefined` leaking through.
|
|
16
39
|
*/
|
|
17
40
|
export declare function toChecklist(raw: RawChecklistItem): ChecklistDefinition;
|
|
18
41
|
/** Normalize a checklist entry's repo-relative `doc` to a POSIX path, so every printed path matches. */
|
package/src/checklist-config.js
CHANGED
|
@@ -25,22 +25,46 @@ class ChecklistDefinition {
|
|
|
25
25
|
// relative to anything else is unresolvable from where that subagent stands.
|
|
26
26
|
doc;
|
|
27
27
|
patterns; // path globs (isPathExcluded semantics); [] = matches any changed file (always runs)
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* true — BLOCKING. `wp-finish-upsert-pr` refuses the PR until this checklist has a passing verdict.
|
|
30
|
+
* false — OPTIONAL. When it matches, stage ② OFFERS it: the AI asks the human which optional reviewers
|
|
31
|
+
* to run, and the human may decline every one of them. A declined optional checklist never
|
|
32
|
+
* blocks.
|
|
33
|
+
*
|
|
34
|
+
* This governs whether the reviewer must RUN — NOT whether its verdict counts. An optional reviewer
|
|
35
|
+
* that is actually spawned and comes back red blocks finish exactly like a required one; otherwise
|
|
36
|
+
* running it would be theater.
|
|
37
|
+
*
|
|
38
|
+
* There is deliberately NO default (see the validator): a review process is the last thing that should
|
|
39
|
+
* acquire a scope silently, in either direction. Defaulting to true would leave the all-blocking status
|
|
40
|
+
* quo in place for anyone who did not read the release note; defaulting to false would quietly
|
|
41
|
+
* DOWNGRADE every existing consumer's gate on upgrade. Both are worse than one mechanical config edit
|
|
42
|
+
* that the coding agent reading the error applies in a single pass.
|
|
43
|
+
*/
|
|
44
|
+
required;
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
46
|
+
constructor(id, subagent, doc, patterns, required) {
|
|
29
47
|
this.id = id;
|
|
30
48
|
this.subagent = subagent;
|
|
31
49
|
this.doc = doc;
|
|
32
50
|
this.patterns = patterns;
|
|
51
|
+
this.required = required;
|
|
33
52
|
}
|
|
34
53
|
}
|
|
35
54
|
exports.ChecklistDefinition = ChecklistDefinition;
|
|
36
55
|
/**
|
|
37
56
|
* Build a ChecklistDefinition from an omitting-friendly raw entry. `id` defaults to the subagent name (the
|
|
38
57
|
* only stable, human-meaningful key we have).
|
|
58
|
+
*
|
|
59
|
+
* `required` is coerced with `=== true` rather than defaulted, and that is not a default in disguise:
|
|
60
|
+
* `validateChecklistArray` has already REJECTED any entry that omitted it or gave a non-boolean, so the
|
|
61
|
+
* only values that reach here are real booleans. The coercion exists so a validator that runs without a
|
|
62
|
+
* repoRoot (structure-only) still produces a well-typed def instead of `undefined` leaking through.
|
|
39
63
|
*/
|
|
40
64
|
// webpieces-disable no-function-outside-class -- pure config transform beside its data class
|
|
41
65
|
function toChecklist(raw) {
|
|
42
66
|
const subagent = raw.subagent ?? '';
|
|
43
|
-
return new ChecklistDefinition(subagent, subagent, normalizeChecklistDoc(raw.doc ?? ''), raw.patterns ?? []);
|
|
67
|
+
return new ChecklistDefinition(subagent, subagent, normalizeChecklistDoc(raw.doc ?? ''), raw.patterns ?? [], raw.required === true);
|
|
44
68
|
}
|
|
45
69
|
/** Normalize a checklist entry's repo-relative `doc` to a POSIX path, so every printed path matches. */
|
|
46
70
|
// webpieces-disable no-function-outside-class -- pure path transform beside its data class
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checklist-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/checklist-config.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"checklist-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/checklist-config.ts"],"names":[],"mappings":";;;AAmEA,kCAIC;AAID,sDAIC;AAYD,wCAIC;;AA/FD,mDAA6B;AAE7B,mGAAmG;AACnG,yGAAyG;AACzG,wGAAwG;AACxG,gHAAgH;AAChH,2GAA2G;AAC3G,oFAAoF;AACpF,EAAE;AACF,oGAAoG;AACpG,sGAAsG;AACtG,wGAAwG;AACxG,aAAa;AACb,MAAa,mBAAmB;IAC5B,EAAE,CAAS,CAAS,mEAAmE;IACvF,QAAQ,CAAS,CAAG,uFAAuF;IAC3G,yGAAyG;IACzG,kGAAkG;IAClG,6EAA6E;IAC7E,GAAG,CAAS;IACZ,QAAQ,CAAW,CAAC,qFAAqF;IACzG;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAU;IAElB,yDAAyD;IACzD,YAAY,EAAU,EAAE,QAAgB,EAAE,GAAW,EAAE,QAAkB,EAAE,QAAiB;QACxF,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AAlCD,kDAkCC;AAUD;;;;;;;;GAQG;AACH,6FAA6F;AAC7F,SAAgB,WAAW,CAAC,GAAqB;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;IACpC,OAAO,IAAI,mBAAmB,CAC1B,QAAQ,EAAE,QAAQ,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,EAAE,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC;AAC7G,CAAC;AAED,wGAAwG;AACxG,2FAA2F;AAC3F,SAAgB,qBAAqB,CAAC,GAAW;IAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,0GAA0G;AAC1G,+FAA+F;AAClF,QAAA,iBAAiB,GAAG,CAAC,CAAC;AAEnC;;;;GAIG;AACH,mGAAmG;AACnG,SAAgB,cAAc,CAAC,KAAwB,EAAE,MAAc,yBAAiB;IACpF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,GAAG,GAAG,UAAU,KAAK,CAAC,MAAM,SAAS,CAAC;AACpG,CAAC","sourcesContent":["import * as path from 'path';\n\n// A company review checklist: a diff-triggered extension point that lets a CONSUMER inject its own\n// PR-time review process into the webpieces gated flow WITHOUT forking the tooling. Each checklist names\n// a reviewer SUBAGENT (a `.claude/agents/<subagent>.md`) and the doc that reviewer reads; when the diff\n// matches the checklist's `patterns`, wp-review-upsert-pr tells the AI to spawn that subagent to review it, and\n// wp-finish-upsert-pr refuses to open the PR until a well-formed, passing review-<id>.json exists AND that\n// named subagent is proven (from the harness's own artifacts) to have actually run.\n//\n// Checklists are configured as an ARRAY in `pr-gate.checklists` in webpieces.config.json — the ONLY\n// accepted shape. `patterns` is a path-glob dispatch table and `subagent` is a name binding: both are\n// config, so they live where every tool that reads webpieces.config.json can see, grep and schema them.\n// Data-only.\nexport class ChecklistDefinition {\n id: string; // = the subagent name; keys review-<id>.json and the dashboard row\n subagent: string; // reviewer agent name → .claude/agents/<subagent>.md; the agentType the harness stamps\n // REPO-RELATIVE guidance doc the reviewer reads (may be '' — then it just reads the diff). Repo-relative\n // because this value is printed verbatim to a reviewer subagent as \"the file to open\", and a path\n // relative to anything else is unresolvable from where that subagent stands.\n doc: string;\n patterns: string[]; // path globs (isPathExcluded semantics); [] = matches any changed file (always runs)\n /**\n * true — BLOCKING. `wp-finish-upsert-pr` refuses the PR until this checklist has a passing verdict.\n * false — OPTIONAL. When it matches, stage ② OFFERS it: the AI asks the human which optional reviewers\n * to run, and the human may decline every one of them. A declined optional checklist never\n * blocks.\n *\n * This governs whether the reviewer must RUN — NOT whether its verdict counts. An optional reviewer\n * that is actually spawned and comes back red blocks finish exactly like a required one; otherwise\n * running it would be theater.\n *\n * There is deliberately NO default (see the validator): a review process is the last thing that should\n * acquire a scope silently, in either direction. Defaulting to true would leave the all-blocking status\n * quo in place for anyone who did not read the release note; defaulting to false would quietly\n * DOWNGRADE every existing consumer's gate on upgrade. Both are worse than one mechanical config edit\n * that the coding agent reading the error applies in a single pass.\n */\n required: boolean;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(id: string, subagent: string, doc: string, patterns: string[], required: boolean) {\n this.id = id;\n this.subagent = subagent;\n this.doc = doc;\n this.patterns = patterns;\n this.required = required;\n }\n}\n\n// One config entry straight from JSON, before it is validated + narrowed into a class.\nexport interface RawChecklistItem {\n subagent?: string;\n doc?: string;\n patterns?: string[];\n required?: boolean;\n}\n\n/**\n * Build a ChecklistDefinition from an omitting-friendly raw entry. `id` defaults to the subagent name (the\n * only stable, human-meaningful key we have).\n *\n * `required` is coerced with `=== true` rather than defaulted, and that is not a default in disguise:\n * `validateChecklistArray` has already REJECTED any entry that omitted it or gave a non-boolean, so the\n * only values that reach here are real booleans. The coercion exists so a validator that runs without a\n * repoRoot (structure-only) still produces a well-typed def instead of `undefined` leaking through.\n */\n// webpieces-disable no-function-outside-class -- pure config transform beside its data class\nexport function toChecklist(raw: RawChecklistItem): ChecklistDefinition {\n const subagent = raw.subagent ?? '';\n return new ChecklistDefinition(\n subagent, subagent, normalizeChecklistDoc(raw.doc ?? ''), raw.patterns ?? [], raw.required === true);\n}\n\n/** Normalize a checklist entry's repo-relative `doc` to a POSIX path, so every printed path matches. */\n// webpieces-disable no-function-outside-class -- pure path transform beside its data class\nexport function normalizeChecklistDoc(doc: string): string {\n const trimmed = doc.trim();\n if (trimmed === '') return '';\n return path.posix.normalize(trimmed.split(path.sep).join('/'));\n}\n\n// The ONE cap for every printed matched-file list. Two print sites once used to slice to 4 and to 5 — two\n// different caps for the same list, neither chosen deliberately, and both without an ellipsis.\nexport const MATCHED_FILES_CAP = 6;\n\n/**\n * Render a file list for a message, NEVER silently. A truncated list that looks complete is how a reviewer\n * gets pointed at 4 of 40 changed files and reports success having reviewed a tenth of the diff, so the\n * dropped count is always stated and the caller is expected to name the file holding the full set.\n */\n// webpieces-disable no-function-outside-class -- pure display formatter beside the data it formats\nexport function formatFileList(files: readonly string[], cap: number = MATCHED_FILES_CAP): string {\n if (files.length === 0) return '(none)';\n if (files.length <= cap) return files.join(', ');\n return `${files.slice(0, cap).join(', ')}, +${files.length - cap} more (${files.length} total)`;\n}\n"]}
|
package/src/cli-args.d.ts
CHANGED
|
@@ -1,11 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* One optional `--flag` a command accepts. Data-only. The description is printed in `--help`, so it is
|
|
3
|
+
* written for the reader who has to DECIDE whether to pass it, not as a restatement of the name.
|
|
4
|
+
*/
|
|
5
|
+
export declare class CliFlag {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
/**
|
|
9
|
+
* The flag MAY carry a value: `--resolve dean/ONE-2275` or `--resolve=dean/ONE-2275`.
|
|
10
|
+
*
|
|
11
|
+
* "May", not "must". The one flag that needs this (`wp-push-dev --resolve`) is meaningful both bare
|
|
12
|
+
* (queue every other copy) and with an argument (queue just that one), and a `valueRequired` variant
|
|
13
|
+
* would be a second concept for a case nothing has. A following token is consumed as the value only
|
|
14
|
+
* when it does not itself start with `-`, so `--resolve --force` still reads as two flags.
|
|
15
|
+
*/
|
|
16
|
+
takesValue: boolean;
|
|
17
|
+
constructor(name: string, description: string, takesValue?: boolean);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Usage descriptor for a `wp-*` bin. Data-only (classes-over-interfaces): a command name, its one-line
|
|
21
|
+
* summary, and the flags it accepts. `CliArgs.classify` turns it into the `--help` / unknown-arg message.
|
|
22
|
+
*
|
|
23
|
+
* `flags` defaults to [] — the no-argument case stays a two-arg construction, which is what eight of the
|
|
24
|
+
* nine `wp-*` bins are. A flag a command does not DECLARE here is still rejected with exit 2: that guard is
|
|
25
|
+
* the reason this class exists (`wp-start-upsert-pr --help` once launched a squash-merge), and making it
|
|
26
|
+
* flag-aware must not soften it.
|
|
4
27
|
*/
|
|
5
28
|
export declare class CliUsage {
|
|
6
29
|
command: string;
|
|
7
30
|
summary: string;
|
|
8
|
-
|
|
31
|
+
flags: CliFlag[];
|
|
32
|
+
constructor(command: string, summary: string, flags?: CliFlag[]);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Which declared flags argv actually carried. Data-only (a class, per CLAUDE.md), with a `has()` accessor
|
|
36
|
+
* so every consumer asks the question the same way instead of open-coding `includes` against a raw array.
|
|
37
|
+
*/
|
|
38
|
+
export declare class CliArgSet {
|
|
39
|
+
present: string[];
|
|
40
|
+
values: Map<string, string>;
|
|
41
|
+
constructor(present?: string[], values?: Map<string, string>);
|
|
42
|
+
has(flag: string): boolean;
|
|
43
|
+
/** The value passed with `flag`, or '' when the flag was absent or passed bare. */
|
|
44
|
+
value(flag: string): string;
|
|
9
45
|
}
|
|
10
46
|
/**
|
|
11
47
|
* Data-only outcome of checking argv against a no-argument command. `ok` true → run normally; else
|
|
@@ -22,11 +58,27 @@ export declare class CliArgsCheck {
|
|
|
22
58
|
export declare class CliArgs {
|
|
23
59
|
private usageText;
|
|
24
60
|
/**
|
|
25
|
-
* Pure argv classifier
|
|
26
|
-
*
|
|
27
|
-
*
|
|
61
|
+
* Pure argv classifier. No args → ok. `--help`/`-h` → not-ok, exit 0 with the usage block. Any token
|
|
62
|
+
* the command did not DECLARE → not-ok, exit 2 naming the offending one(s). Split out from
|
|
63
|
+
* `assertNoArgs`/`parse` so the decision is unit-testable without a thrown exception.
|
|
64
|
+
*
|
|
65
|
+
* An undeclared token is still fatal even for a command that accepts flags — a mistyped `--no-optionl`
|
|
66
|
+
* must never be silently ignored and then run the flow WITH the reviews the caller meant to skip.
|
|
28
67
|
*/
|
|
29
68
|
classify(args: string[], usage: CliUsage): CliArgsCheck;
|
|
69
|
+
/**
|
|
70
|
+
* Walk argv once, classifying every token as a declared flag, a value belonging to the
|
|
71
|
+
* value-taking flag before it, or unknown. ONE walk backs both `classify` and `parse` so the set of
|
|
72
|
+
* tokens the guard accepts and the set `parse` reports can never diverge — an accepted-but-unreported
|
|
73
|
+
* flag would silently run the flow without the behaviour the caller asked for.
|
|
74
|
+
*/
|
|
75
|
+
private scan;
|
|
76
|
+
/**
|
|
77
|
+
* The flag-accepting sibling of {@link assertNoArgs}: same guard, but it RETURNS which declared flags
|
|
78
|
+
* were passed. Call it in exactly the same place — first thing inside `runMain`, before the app touches
|
|
79
|
+
* git.
|
|
80
|
+
*/
|
|
81
|
+
parse(usage: CliUsage): CliArgSet;
|
|
30
82
|
/**
|
|
31
83
|
* Call it as the FIRST thing inside `runMain`, BEFORE the app touches git — a bogus flag must
|
|
32
84
|
* never start a mutation flow (the `wp-start-upsert-pr --help` incident: an ignored flag silently
|
package/src/cli-args.js
CHANGED
|
@@ -1,22 +1,75 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CliArgs = exports.CliArgsCheck = exports.CliUsage = void 0;
|
|
3
|
+
exports.CliArgs = exports.CliArgsCheck = exports.CliArgSet = exports.CliUsage = exports.CliFlag = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const inversify_1 = require("inversify");
|
|
6
6
|
const cli_exit_error_1 = require("./cli-exit-error");
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* One optional `--flag` a command accepts. Data-only. The description is printed in `--help`, so it is
|
|
9
|
+
* written for the reader who has to DECIDE whether to pass it, not as a restatement of the name.
|
|
10
|
+
*/
|
|
11
|
+
class CliFlag {
|
|
12
|
+
name; // including the leading dashes, e.g. '--no-optional'
|
|
13
|
+
description;
|
|
14
|
+
/**
|
|
15
|
+
* The flag MAY carry a value: `--resolve dean/ONE-2275` or `--resolve=dean/ONE-2275`.
|
|
16
|
+
*
|
|
17
|
+
* "May", not "must". The one flag that needs this (`wp-push-dev --resolve`) is meaningful both bare
|
|
18
|
+
* (queue every other copy) and with an argument (queue just that one), and a `valueRequired` variant
|
|
19
|
+
* would be a second concept for a case nothing has. A following token is consumed as the value only
|
|
20
|
+
* when it does not itself start with `-`, so `--resolve --force` still reads as two flags.
|
|
21
|
+
*/
|
|
22
|
+
takesValue;
|
|
23
|
+
constructor(name, description, takesValue = false) {
|
|
24
|
+
this.name = name;
|
|
25
|
+
this.description = description;
|
|
26
|
+
this.takesValue = takesValue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.CliFlag = CliFlag;
|
|
30
|
+
/**
|
|
31
|
+
* Usage descriptor for a `wp-*` bin. Data-only (classes-over-interfaces): a command name, its one-line
|
|
32
|
+
* summary, and the flags it accepts. `CliArgs.classify` turns it into the `--help` / unknown-arg message.
|
|
33
|
+
*
|
|
34
|
+
* `flags` defaults to [] — the no-argument case stays a two-arg construction, which is what eight of the
|
|
35
|
+
* nine `wp-*` bins are. A flag a command does not DECLARE here is still rejected with exit 2: that guard is
|
|
36
|
+
* the reason this class exists (`wp-start-upsert-pr --help` once launched a squash-merge), and making it
|
|
37
|
+
* flag-aware must not soften it.
|
|
10
38
|
*/
|
|
11
39
|
class CliUsage {
|
|
12
40
|
command;
|
|
13
41
|
summary;
|
|
14
|
-
|
|
42
|
+
flags;
|
|
43
|
+
constructor(command, summary, flags = []) {
|
|
15
44
|
this.command = command;
|
|
16
45
|
this.summary = summary;
|
|
46
|
+
this.flags = flags;
|
|
17
47
|
}
|
|
18
48
|
}
|
|
19
49
|
exports.CliUsage = CliUsage;
|
|
50
|
+
/**
|
|
51
|
+
* Which declared flags argv actually carried. Data-only (a class, per CLAUDE.md), with a `has()` accessor
|
|
52
|
+
* so every consumer asks the question the same way instead of open-coding `includes` against a raw array.
|
|
53
|
+
*/
|
|
54
|
+
class CliArgSet {
|
|
55
|
+
present;
|
|
56
|
+
// Values for the value-taking flags that carried one, keyed by flag name. A flag passed bare is in
|
|
57
|
+
// `present` but absent here, which is exactly the distinction `--resolve` (bare) vs
|
|
58
|
+
// `--resolve <branch>` needs.
|
|
59
|
+
values;
|
|
60
|
+
constructor(present = [], values = new Map()) {
|
|
61
|
+
this.present = present;
|
|
62
|
+
this.values = values;
|
|
63
|
+
}
|
|
64
|
+
has(flag) {
|
|
65
|
+
return this.present.includes(flag);
|
|
66
|
+
}
|
|
67
|
+
/** The value passed with `flag`, or '' when the flag was absent or passed bare. */
|
|
68
|
+
value(flag) {
|
|
69
|
+
return this.values.get(flag) ?? '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.CliArgSet = CliArgSet;
|
|
20
73
|
/**
|
|
21
74
|
* Data-only outcome of checking argv against a no-argument command. `ok` true → run normally; else
|
|
22
75
|
* `exitCode`/`message` are what the bin should exit with (help = 0, unknown arg = 2). Kept a pure
|
|
@@ -33,19 +86,33 @@ class CliArgsCheck {
|
|
|
33
86
|
}
|
|
34
87
|
}
|
|
35
88
|
exports.CliArgsCheck = CliArgsCheck;
|
|
89
|
+
/** One argv walk's result: which declared flags were seen, their values, and every unrecognized token. */
|
|
90
|
+
class CliScan {
|
|
91
|
+
present = [];
|
|
92
|
+
values = new Map();
|
|
93
|
+
unknown = [];
|
|
94
|
+
}
|
|
36
95
|
/** Argument guard for the no-argument `wp-*` bins. */
|
|
37
96
|
let CliArgs = class CliArgs {
|
|
38
|
-
// The help/usage block shown for `--help` and appended to an unknown-arg error.
|
|
39
|
-
//
|
|
97
|
+
// The help/usage block shown for `--help` and appended to an unknown-arg error. A command with no
|
|
98
|
+
// declared flags says so outright, because "takes no arguments" is the whole usage for eight of nine.
|
|
40
99
|
usageText(usage) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
`
|
|
100
|
+
const head = `${usage.command} — ${usage.summary}\n\n`;
|
|
101
|
+
if (usage.flags.length === 0) {
|
|
102
|
+
return head + `Usage: pnpm ${usage.command}\nThis command takes no arguments.`;
|
|
103
|
+
}
|
|
104
|
+
const label = (f) => (f.takesValue ? `${f.name} [<value>]` : f.name);
|
|
105
|
+
const width = Math.max(...usage.flags.map((f) => label(f).length));
|
|
106
|
+
const rows = usage.flags.map((f) => ` ${label(f).padEnd(width)} ${f.description}`);
|
|
107
|
+
return head + `Usage: pnpm ${usage.command} [flags]\n\nFlags:\n${rows.join('\n')}`;
|
|
44
108
|
}
|
|
45
109
|
/**
|
|
46
|
-
* Pure argv classifier
|
|
47
|
-
*
|
|
48
|
-
*
|
|
110
|
+
* Pure argv classifier. No args → ok. `--help`/`-h` → not-ok, exit 0 with the usage block. Any token
|
|
111
|
+
* the command did not DECLARE → not-ok, exit 2 naming the offending one(s). Split out from
|
|
112
|
+
* `assertNoArgs`/`parse` so the decision is unit-testable without a thrown exception.
|
|
113
|
+
*
|
|
114
|
+
* An undeclared token is still fatal even for a command that accepts flags — a mistyped `--no-optionl`
|
|
115
|
+
* must never be silently ignored and then run the flow WITH the reviews the caller meant to skip.
|
|
49
116
|
*/
|
|
50
117
|
classify(args, usage) {
|
|
51
118
|
if (args.length === 0)
|
|
@@ -53,7 +120,66 @@ let CliArgs = class CliArgs {
|
|
|
53
120
|
if (args.includes('--help') || args.includes('-h')) {
|
|
54
121
|
return new CliArgsCheck(false, 0, this.usageText(usage));
|
|
55
122
|
}
|
|
56
|
-
|
|
123
|
+
const scan = this.scan(args, usage);
|
|
124
|
+
if (scan.unknown.length > 0) {
|
|
125
|
+
return new CliArgsCheck(false, 2, `❌ Unknown argument(s): ${scan.unknown.join(' ')}\n\n` + this.usageText(usage));
|
|
126
|
+
}
|
|
127
|
+
return new CliArgsCheck(true, 0, '');
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Walk argv once, classifying every token as a declared flag, a value belonging to the
|
|
131
|
+
* value-taking flag before it, or unknown. ONE walk backs both `classify` and `parse` so the set of
|
|
132
|
+
* tokens the guard accepts and the set `parse` reports can never diverge — an accepted-but-unreported
|
|
133
|
+
* flag would silently run the flow without the behaviour the caller asked for.
|
|
134
|
+
*/
|
|
135
|
+
scan(args, usage) {
|
|
136
|
+
const byName = new Map();
|
|
137
|
+
for (const flag of usage.flags)
|
|
138
|
+
byName.set(flag.name, flag);
|
|
139
|
+
const scan = new CliScan();
|
|
140
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
141
|
+
const token = args[i];
|
|
142
|
+
const eq = token.indexOf('=');
|
|
143
|
+
// `--flag=value` — split before lookup so the name is what gets matched, not the whole token.
|
|
144
|
+
const name = eq > 0 ? token.slice(0, eq) : token;
|
|
145
|
+
const flag = byName.get(name);
|
|
146
|
+
if (flag === undefined) {
|
|
147
|
+
scan.unknown.push(token);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
scan.present.push(name);
|
|
151
|
+
if (!flag.takesValue) {
|
|
152
|
+
// `--no-optional=x` is a typo, not an accepted flag: the value would be silently dropped.
|
|
153
|
+
if (eq > 0)
|
|
154
|
+
scan.unknown.push(token);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (eq > 0) {
|
|
158
|
+
scan.values.set(name, token.slice(eq + 1));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// OPTIONAL value: only a following token that is not itself a flag. `--resolve --force`
|
|
162
|
+
// therefore reads as two flags, not as a resolve of a branch literally named `--force`.
|
|
163
|
+
const next = i + 1 < args.length ? args[i + 1] : '';
|
|
164
|
+
if (next !== '' && !next.startsWith('-')) {
|
|
165
|
+
scan.values.set(name, next);
|
|
166
|
+
i += 1;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return scan;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The flag-accepting sibling of {@link assertNoArgs}: same guard, but it RETURNS which declared flags
|
|
173
|
+
* were passed. Call it in exactly the same place — first thing inside `runMain`, before the app touches
|
|
174
|
+
* git.
|
|
175
|
+
*/
|
|
176
|
+
parse(usage) {
|
|
177
|
+
const args = process.argv.slice(2);
|
|
178
|
+
const check = this.classify(args, usage);
|
|
179
|
+
if (!check.ok)
|
|
180
|
+
throw new cli_exit_error_1.CliExitError(check.exitCode, check.message);
|
|
181
|
+
const scan = this.scan(args, usage);
|
|
182
|
+
return new CliArgSet(scan.present, scan.values);
|
|
57
183
|
}
|
|
58
184
|
/**
|
|
59
185
|
* Call it as the FIRST thing inside `runMain`, BEFORE the app touches git — a bogus flag must
|
package/src/cli-args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/cli-args.ts"],"names":[],"mappings":";;;;AAAA,yCAA2D;AAC3D,qDAAgD;AAEhD;;;GAGG;AACH,MAAa,QAAQ;IACjB,OAAO,CAAS;IAChB,OAAO,CAAS;IAEhB,YAAY,OAAe,EAAE,OAAe;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;CACJ;AARD,4BAQC;AAED;;;;GAIG;AACH,MAAa,YAAY;IACrB,EAAE,CAAU;IACZ,QAAQ,CAAS;IACjB,OAAO,CAAS;IAEhB,YAAY,EAAW,EAAE,QAAgB,EAAE,OAAe;QACtD,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;CACJ;AAVD,oCAUC;AAED,sDAAsD;AAE/C,IAAM,OAAO,GAAb,MAAM,OAAO;IAChB,+FAA+F;IAC/F,wDAAwD;IAChD,SAAS,CAAC,KAAe;QAC7B,OAAO,CACH,GAAG,KAAK,CAAC,OAAO,MAAM,KAAK,CAAC,OAAO,MAAM;YACzC,gBAAgB,KAAK,CAAC,OAAO,IAAI;YACjC,kCAAkC,CACrC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,IAAc,EAAE,KAAe;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,0BAA0B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9G,CAAC;IAED;;;;;;;;OAQG;IACH,YAAY,CAAC,KAAe;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,EAAE;YAAE,OAAO;QACrB,MAAM,IAAI,6BAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;CACJ,CAAA;AAtCY,0BAAO;kBAAP,OAAO;IADnB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,OAAO,CAsCnB","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\nimport { CliExitError } from './cli-exit-error';\n\n/**\n * Usage descriptor for a `wp-*` bin. Data-only (classes-over-interfaces): a command name and its\n * one-line summary. `CliArgs.classify` turns it into the `--help` / unknown-arg message.\n */\nexport class CliUsage {\n command: string;\n summary: string;\n\n constructor(command: string, summary: string) {\n this.command = command;\n this.summary = summary;\n }\n}\n\n/**\n * Data-only outcome of checking argv against a no-argument command. `ok` true → run normally; else\n * `exitCode`/`message` are what the bin should exit with (help = 0, unknown arg = 2). Kept a pure\n * value so it can be asserted directly in tests without provoking a throw.\n */\nexport class CliArgsCheck {\n ok: boolean;\n exitCode: number;\n message: string;\n\n constructor(ok: boolean, exitCode: number, message: string) {\n this.ok = ok;\n this.exitCode = exitCode;\n this.message = message;\n }\n}\n\n/** Argument guard for the no-argument `wp-*` bins. */\n@injectable(bindingScopeValues.Singleton)\nexport class CliArgs {\n // The help/usage block shown for `--help` and appended to an unknown-arg error. These commands\n // take NO arguments, so that fact is the whole \"usage\".\n private usageText(usage: CliUsage): string {\n return (\n `${usage.command} — ${usage.summary}\\n\\n` +\n `Usage: pnpm ${usage.command}\\n` +\n `This command takes no arguments.`\n );\n }\n\n /**\n * Pure argv classifier for a no-argument command. No args → ok. `--help`/`-h` → not-ok, exit 0\n * with the usage block. Anything else → not-ok, exit 2 naming the offending token(s). Split out\n * from `assertNoArgs` so the decision is unit-testable without a thrown exception.\n */\n classify(args: string[], usage: CliUsage): CliArgsCheck {\n if (args.length === 0) return new CliArgsCheck(true, 0, '');\n if (args.includes('--help') || args.includes('-h')) {\n return new CliArgsCheck(false, 0, this.usageText(usage));\n }\n return new CliArgsCheck(false, 2, `❌ Unknown argument(s): ${args.join(' ')}\\n\\n` + this.usageText(usage));\n }\n\n /**\n * Call it as the FIRST thing inside `runMain`, BEFORE the app touches git — a bogus flag must\n * never start a mutation flow (the `wp-start-upsert-pr --help` incident: an ignored flag silently\n * launched the squash-merge and stranded the checkout on a `…PreMerge<n>` branch).\n *\n * Throws `CliExitError` (never `process.exit`) so `runMain` stays the single sanctioned exit site\n * (`no-process-exit-outside-main`): help exits 0, an unknown arg exits 2, and in both cases the\n * flow never begins.\n */\n assertNoArgs(usage: CliUsage): void {\n const check = this.classify(process.argv.slice(2), usage);\n if (check.ok) return;\n throw new CliExitError(check.exitCode, check.message);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/cli-args.ts"],"names":[],"mappings":";;;;AAAA,yCAA2D;AAC3D,qDAAgD;AAEhD;;;GAGG;AACH,MAAa,OAAO;IAChB,IAAI,CAAS,CAAQ,qDAAqD;IAC1E,WAAW,CAAS;IACpB;;;;;;;OAOG;IACH,UAAU,CAAU;IAEpB,YAAY,IAAY,EAAE,WAAmB,EAAE,UAAU,GAAG,KAAK;QAC7D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,CAAC;CACJ;AAlBD,0BAkBC;AAED;;;;;;;;GAQG;AACH,MAAa,QAAQ;IACjB,OAAO,CAAS;IAChB,OAAO,CAAS;IAChB,KAAK,CAAY;IAEjB,YAAY,OAAe,EAAE,OAAe,EAAE,QAAmB,EAAE;QAC/D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ;AAVD,4BAUC;AAED;;;GAGG;AACH,MAAa,SAAS;IAClB,OAAO,CAAW;IAClB,mGAAmG;IACnG,oFAAoF;IACpF,8BAA8B;IAC9B,MAAM,CAAsB;IAE5B,YAAY,UAAoB,EAAE,EAAE,SAA8B,IAAI,GAAG,EAAkB;QACvF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,GAAG,CAAC,IAAY;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,mFAAmF;IACnF,KAAK,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACvC,CAAC;CACJ;AApBD,8BAoBC;AAED;;;;GAIG;AACH,MAAa,YAAY;IACrB,EAAE,CAAU;IACZ,QAAQ,CAAS;IACjB,OAAO,CAAS;IAEhB,YAAY,EAAW,EAAE,QAAgB,EAAE,OAAe;QACtD,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;CACJ;AAVD,oCAUC;AAED,0GAA0G;AAC1G,MAAM,OAAO;IACT,OAAO,GAAa,EAAE,CAAC;IACvB,MAAM,GAAwB,IAAI,GAAG,EAAkB,CAAC;IACxD,OAAO,GAAa,EAAE,CAAC;CAC1B;AAED,sDAAsD;AAE/C,IAAM,OAAO,GAAb,MAAM,OAAO;IAChB,kGAAkG;IAClG,sGAAsG;IAC9F,SAAS,CAAC,KAAe;QAC7B,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,MAAM,KAAK,CAAC,OAAO,MAAM,CAAC;QACvD,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,IAAI,GAAG,gBAAgB,KAAK,CAAC,OAAO,oCAAoC,CAAC;QACpF,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAU,EAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAU,EAAU,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACtG,OAAO,IAAI,GAAG,gBAAgB,KAAK,CAAC,OAAO,uBAAuB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACxF,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAc,EAAE,KAAe;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,0BAA0B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACtH,CAAC;QACD,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACK,IAAI,CAAC,IAAc,EAAE,KAAe;QACxC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9B,8FAA8F;YAC9F,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACrB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACzB,SAAS;YACb,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;gBACnB,0FAA0F;gBAC1F,IAAI,EAAE,GAAG,CAAC;oBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrC,SAAS;YACb,CAAC;YACD,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACT,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC3C,SAAS;YACb,CAAC;YACD,wFAAwF;YACxF,wFAAwF;YACxF,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,IAAI,IAAI,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC5B,CAAC,IAAI,CAAC,CAAC;YACX,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAe;QACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,EAAE;YAAE,MAAM,IAAI,6BAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;;OAQG;IACH,YAAY,CAAC,KAAe;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,EAAE;YAAE,OAAO;QACrB,MAAM,IAAI,6BAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;CACJ,CAAA;AAtGY,0BAAO;kBAAP,OAAO;IADnB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,OAAO,CAsGnB","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\nimport { CliExitError } from './cli-exit-error';\n\n/**\n * One optional `--flag` a command accepts. Data-only. The description is printed in `--help`, so it is\n * written for the reader who has to DECIDE whether to pass it, not as a restatement of the name.\n */\nexport class CliFlag {\n name: string; // including the leading dashes, e.g. '--no-optional'\n description: string;\n /**\n * The flag MAY carry a value: `--resolve dean/ONE-2275` or `--resolve=dean/ONE-2275`.\n *\n * \"May\", not \"must\". The one flag that needs this (`wp-push-dev --resolve`) is meaningful both bare\n * (queue every other copy) and with an argument (queue just that one), and a `valueRequired` variant\n * would be a second concept for a case nothing has. A following token is consumed as the value only\n * when it does not itself start with `-`, so `--resolve --force` still reads as two flags.\n */\n takesValue: boolean;\n\n constructor(name: string, description: string, takesValue = false) {\n this.name = name;\n this.description = description;\n this.takesValue = takesValue;\n }\n}\n\n/**\n * Usage descriptor for a `wp-*` bin. Data-only (classes-over-interfaces): a command name, its one-line\n * summary, and the flags it accepts. `CliArgs.classify` turns it into the `--help` / unknown-arg message.\n *\n * `flags` defaults to [] — the no-argument case stays a two-arg construction, which is what eight of the\n * nine `wp-*` bins are. A flag a command does not DECLARE here is still rejected with exit 2: that guard is\n * the reason this class exists (`wp-start-upsert-pr --help` once launched a squash-merge), and making it\n * flag-aware must not soften it.\n */\nexport class CliUsage {\n command: string;\n summary: string;\n flags: CliFlag[];\n\n constructor(command: string, summary: string, flags: CliFlag[] = []) {\n this.command = command;\n this.summary = summary;\n this.flags = flags;\n }\n}\n\n/**\n * Which declared flags argv actually carried. Data-only (a class, per CLAUDE.md), with a `has()` accessor\n * so every consumer asks the question the same way instead of open-coding `includes` against a raw array.\n */\nexport class CliArgSet {\n present: string[];\n // Values for the value-taking flags that carried one, keyed by flag name. A flag passed bare is in\n // `present` but absent here, which is exactly the distinction `--resolve` (bare) vs\n // `--resolve <branch>` needs.\n values: Map<string, string>;\n\n constructor(present: string[] = [], values: Map<string, string> = new Map<string, string>()) {\n this.present = present;\n this.values = values;\n }\n\n has(flag: string): boolean {\n return this.present.includes(flag);\n }\n\n /** The value passed with `flag`, or '' when the flag was absent or passed bare. */\n value(flag: string): string {\n return this.values.get(flag) ?? '';\n }\n}\n\n/**\n * Data-only outcome of checking argv against a no-argument command. `ok` true → run normally; else\n * `exitCode`/`message` are what the bin should exit with (help = 0, unknown arg = 2). Kept a pure\n * value so it can be asserted directly in tests without provoking a throw.\n */\nexport class CliArgsCheck {\n ok: boolean;\n exitCode: number;\n message: string;\n\n constructor(ok: boolean, exitCode: number, message: string) {\n this.ok = ok;\n this.exitCode = exitCode;\n this.message = message;\n }\n}\n\n/** One argv walk's result: which declared flags were seen, their values, and every unrecognized token. */\nclass CliScan {\n present: string[] = [];\n values: Map<string, string> = new Map<string, string>();\n unknown: string[] = [];\n}\n\n/** Argument guard for the no-argument `wp-*` bins. */\n@injectable(bindingScopeValues.Singleton)\nexport class CliArgs {\n // The help/usage block shown for `--help` and appended to an unknown-arg error. A command with no\n // declared flags says so outright, because \"takes no arguments\" is the whole usage for eight of nine.\n private usageText(usage: CliUsage): string {\n const head = `${usage.command} — ${usage.summary}\\n\\n`;\n if (usage.flags.length === 0) {\n return head + `Usage: pnpm ${usage.command}\\nThis command takes no arguments.`;\n }\n const label = (f: CliFlag): string => (f.takesValue ? `${f.name} [<value>]` : f.name);\n const width = Math.max(...usage.flags.map((f: CliFlag): number => label(f).length));\n const rows = usage.flags.map((f: CliFlag): string => ` ${label(f).padEnd(width)} ${f.description}`);\n return head + `Usage: pnpm ${usage.command} [flags]\\n\\nFlags:\\n${rows.join('\\n')}`;\n }\n\n /**\n * Pure argv classifier. No args → ok. `--help`/`-h` → not-ok, exit 0 with the usage block. Any token\n * the command did not DECLARE → not-ok, exit 2 naming the offending one(s). Split out from\n * `assertNoArgs`/`parse` so the decision is unit-testable without a thrown exception.\n *\n * An undeclared token is still fatal even for a command that accepts flags — a mistyped `--no-optionl`\n * must never be silently ignored and then run the flow WITH the reviews the caller meant to skip.\n */\n classify(args: string[], usage: CliUsage): CliArgsCheck {\n if (args.length === 0) return new CliArgsCheck(true, 0, '');\n if (args.includes('--help') || args.includes('-h')) {\n return new CliArgsCheck(false, 0, this.usageText(usage));\n }\n const scan = this.scan(args, usage);\n if (scan.unknown.length > 0) {\n return new CliArgsCheck(false, 2, `❌ Unknown argument(s): ${scan.unknown.join(' ')}\\n\\n` + this.usageText(usage));\n }\n return new CliArgsCheck(true, 0, '');\n }\n\n /**\n * Walk argv once, classifying every token as a declared flag, a value belonging to the\n * value-taking flag before it, or unknown. ONE walk backs both `classify` and `parse` so the set of\n * tokens the guard accepts and the set `parse` reports can never diverge — an accepted-but-unreported\n * flag would silently run the flow without the behaviour the caller asked for.\n */\n private scan(args: string[], usage: CliUsage): CliScan {\n const byName = new Map<string, CliFlag>();\n for (const flag of usage.flags) byName.set(flag.name, flag);\n const scan = new CliScan();\n for (let i = 0; i < args.length; i += 1) {\n const token = args[i];\n const eq = token.indexOf('=');\n // `--flag=value` — split before lookup so the name is what gets matched, not the whole token.\n const name = eq > 0 ? token.slice(0, eq) : token;\n const flag = byName.get(name);\n if (flag === undefined) {\n scan.unknown.push(token);\n continue;\n }\n scan.present.push(name);\n if (!flag.takesValue) {\n // `--no-optional=x` is a typo, not an accepted flag: the value would be silently dropped.\n if (eq > 0) scan.unknown.push(token);\n continue;\n }\n if (eq > 0) {\n scan.values.set(name, token.slice(eq + 1));\n continue;\n }\n // OPTIONAL value: only a following token that is not itself a flag. `--resolve --force`\n // therefore reads as two flags, not as a resolve of a branch literally named `--force`.\n const next = i + 1 < args.length ? args[i + 1] : '';\n if (next !== '' && !next.startsWith('-')) {\n scan.values.set(name, next);\n i += 1;\n }\n }\n return scan;\n }\n\n /**\n * The flag-accepting sibling of {@link assertNoArgs}: same guard, but it RETURNS which declared flags\n * were passed. Call it in exactly the same place — first thing inside `runMain`, before the app touches\n * git.\n */\n parse(usage: CliUsage): CliArgSet {\n const args = process.argv.slice(2);\n const check = this.classify(args, usage);\n if (!check.ok) throw new CliExitError(check.exitCode, check.message);\n const scan = this.scan(args, usage);\n return new CliArgSet(scan.present, scan.values);\n }\n\n /**\n * Call it as the FIRST thing inside `runMain`, BEFORE the app touches git — a bogus flag must\n * never start a mutation flow (the `wp-start-upsert-pr --help` incident: an ignored flag silently\n * launched the squash-merge and stranded the checkout on a `…PreMerge<n>` branch).\n *\n * Throws `CliExitError` (never `process.exit`) so `runMain` stays the single sanctioned exit site\n * (`no-process-exit-outside-main`): help exits 0, an unknown arg exits 2, and in both cases the\n * flow never begins.\n */\n assertNoArgs(usage: CliUsage): void {\n const check = this.classify(process.argv.slice(2), usage);\n if (check.ok) return;\n throw new CliExitError(check.exitCode, check.message);\n }\n}\n"]}
|
package/src/constants.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare const WEBPIECES_TMP_DIR = ".webpieces";
|
|
|
26
26
|
export declare const MERGE_INFO_DIR = "merge-info";
|
|
27
27
|
export declare const PR_REVIEW_DIR = "pr-review";
|
|
28
28
|
export declare const MERGE_IN_PROGRESS_FILE = "merge-in-progress.json";
|
|
29
|
+
export declare const PUSH_DEV_STATE_FILE = "push-dev-in-progress.json";
|
|
29
30
|
export declare const MERGE_EXPLANATION_FILE = "merge-explanation.md";
|
|
30
31
|
/**
|
|
31
32
|
* Fast predicate: does this text carry a webpieces-disable for the given rule?
|
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.PR_REVIEW_DIR = exports.MERGE_INFO_DIR = exports.WEBPIECES_TMP_DIR = exports.RULE_NAMES = exports.WEBPIECES_DISABLE = void 0;
|
|
9
|
+
exports.MERGE_EXPLANATION_FILE = exports.PUSH_DEV_STATE_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.
|
|
@@ -58,6 +58,16 @@ exports.MERGE_INFO_DIR = 'merge-info';
|
|
|
58
58
|
// cleanTmp's legacy `pr-` sweep.
|
|
59
59
|
exports.PR_REVIEW_DIR = 'pr-review';
|
|
60
60
|
exports.MERGE_IN_PROGRESS_FILE = 'merge-in-progress.json';
|
|
61
|
+
// The dev-deploy resolve state file, written by `wp-push-dev --resolve` and cleared by
|
|
62
|
+
// `wp-finish-push-dev` (or `--abort`). Named here for the same reason MERGE_IN_PROGRESS_FILE is: the
|
|
63
|
+
// pr-gate commands WRITE it and things outside pr-gate READ it to decide whether a resolve is
|
|
64
|
+
// half-finished, and neither side may depend on the other.
|
|
65
|
+
//
|
|
66
|
+
// It lives directly under `.webpieces/` local state (NOT under merge-info/), because a dev-deploy
|
|
67
|
+
// resolve is not a 3-point merge: it never touches the feature branch, never produces merge-info
|
|
68
|
+
// context, and must NOT be picked up by merge-in-progress-guard's marker scan — that guard's remedy
|
|
69
|
+
// is `pnpm wp-finish-upsert-pr`, which is the wrong command here and would strand the tmp branch.
|
|
70
|
+
exports.PUSH_DEV_STATE_FILE = 'push-dev-in-progress.json';
|
|
61
71
|
// Proof-of-work the AI must produce for every conflicted file it resolves during a 3-point
|
|
62
72
|
// merge: a short explanation written NEXT TO that file's 3-point context (the same
|
|
63
73
|
// `updatemain-<safe_path>/` dir that holds A-forkpoint.txt / B-A.diff / C-A.diff). The
|
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;;;AAiFlF,gCAEC;AAjFY,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,2CAA2C,EAAE,6CAA6C;IAC1F,4BAA4B,EAAE,8BAA8B;IAC5D,yBAAyB,EAAE,2BAA2B;IACtD,+CAA+C,EAAE,iDAAiD;IAClG,aAAa,EAAE,eAAe;IAC9B,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,iBAAiB;IAClC,sBAAsB,EAAE,wBAAwB;IAChD,aAAa,EAAE,eAAe;IAC9B,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;AACzF,mGAAmG;AACnG,wGAAwG;AACxG,sGAAsG;AACtG,0CAA0C;AAC7B,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,uFAAuF;AACvF,qGAAqG;AACrG,8FAA8F;AAC9F,2DAA2D;AAC3D,EAAE;AACF,kGAAkG;AAClG,iGAAiG;AACjG,oGAAoG;AACpG,kGAAkG;AACrF,QAAA,mBAAmB,GAAG,2BAA2B,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_CLIENT_CREATION_OUTSIDE_SERVER_OR_CLIENT: 'no-client-creation-outside-server-or-client',\n NO_PROCESS_EXIT_OUTSIDE_MAIN: 'no-process-exit-outside-main',\n NO_FUNCTION_OUTSIDE_CLASS: 'no-function-outside-class',\n INJECT_ANNOTATION_NOT_NEEDED_FOR_CONCRETE_CLASS: 'inject-annotation-not-needed-for-concrete-class',\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 NO_CUSTOM_CSS: 'no-custom-css',\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).\n// The DIRECTORY NAME only. Never join it onto a root yourself — go through `DotWebpieces.shared()`\n// (repo-wide state) or `DotWebpieces.local()` (this worktree's own state) so the call site declares its\n// scope. In a linked worktree the two resolve to different places, and getting that silently wrong is\n// the bug those methods exist to prevent.\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// The dev-deploy resolve state file, written by `wp-push-dev --resolve` and cleared by\n// `wp-finish-push-dev` (or `--abort`). Named here for the same reason MERGE_IN_PROGRESS_FILE is: the\n// pr-gate commands WRITE it and things outside pr-gate READ it to decide whether a resolve is\n// half-finished, and neither side may depend on the other.\n//\n// It lives directly under `.webpieces/` local state (NOT under merge-info/), because a dev-deploy\n// resolve is not a 3-point merge: it never touches the feature branch, never produces merge-info\n// context, and must NOT be picked up by merge-in-progress-guard's marker scan — that guard's remedy\n// is `pnpm wp-finish-upsert-pr`, which is the wrong command here and would strand the tmp branch.\nexport const PUSH_DEV_STATE_FILE = 'push-dev-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
|
@@ -2,7 +2,7 @@ export { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';
|
|
|
2
2
|
export { InformAiError } from './inform-ai-error';
|
|
3
3
|
export { RuleFailError } from './rule-fail-error';
|
|
4
4
|
export { CliExitError } from './cli-exit-error';
|
|
5
|
-
export { CliUsage, CliArgsCheck, CliArgs } from './cli-args';
|
|
5
|
+
export { CliUsage, CliFlag, CliArgSet, CliArgsCheck, CliArgs } from './cli-args';
|
|
6
6
|
export { runMain } from './run-main';
|
|
7
7
|
export { toError } from './to-error';
|
|
8
8
|
export { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';
|
|
@@ -34,16 +34,16 @@ export { shouldSkipRule, getCurrentBranch } from './skip-rule';
|
|
|
34
34
|
export type { SkipRuleResult } from './skip-rule';
|
|
35
35
|
export { detectBase, resolveBase, getChangedFiles, getFileDiff, getChangedLineNumbers, findNewMethodSignaturesInDiff, hasChangesInRange, isNewOrModified, DiffScope, DiffRange, ChangedFilesOptions, } from './diff-scope';
|
|
36
36
|
export { AbstractRule } from './abstract-rule';
|
|
37
|
-
export { WEBPIECES_DISABLE, RULE_NAMES, hasDisable, WEBPIECES_TMP_DIR, MERGE_INFO_DIR, PR_REVIEW_DIR, MERGE_IN_PROGRESS_FILE, MERGE_EXPLANATION_FILE, } from './constants';
|
|
37
|
+
export { WEBPIECES_DISABLE, RULE_NAMES, hasDisable, WEBPIECES_TMP_DIR, MERGE_INFO_DIR, PR_REVIEW_DIR, MERGE_IN_PROGRESS_FILE, MERGE_EXPLANATION_FILE, PUSH_DEV_STATE_FILE, } from './constants';
|
|
38
38
|
export { WebpiecesRulesConfig } from './WebpiecesRulesConfig';
|
|
39
|
-
export { SyncFlowGuidance, WP_START_UPDATE, WP_FINISH_UPDATE, WP_START_UPSERT_PR, WP_FINISH_UPSERT_PR, } from './sync-flow-guidance';
|
|
39
|
+
export { SyncFlowGuidance, WP_START_UPDATE, WP_FINISH_UPDATE, WP_START_UPSERT_PR, WP_FINISH_UPSERT_PR, WP_PUSH_DEV, WP_FINISH_PUSH_DEV, } from './sync-flow-guidance';
|
|
40
40
|
export { MaxMethodLinesConfig, MaxFileLinesConfig, RequireReturnTypeConfig, NoInlineTypeLiteralsConfig, NoAnyUnknownConfig, NoImplicitAnyConfig, PrismaValidateDtosConfig, PrismaConverterConfig, NoDestructureConfig, NoUnmanagedExceptionsConfig, CatchErrorPatternConfig, ThrowCauseRequiredConfig, AngularNoDirectApiInResolverConfig, NoSymbolDiTokensConfig, NoCustomCssConfig, NoProcessExitOutsideMainConfig, NoFunctionOutsideClassConfig, InjectAnnotationNotNeededForConcreteClassConfig, FrameworkTagConfig, RoleTagConfig, BranchCreationGuardConfig, PrCreationOrPushGuardConfig, MergeInProgressGuardConfig, PrMergeGuardConfig, RedirectHowToMergeMainConfig, NoFileImportCyclesConfig, RuntimeArchitectureConfig, NxWiringConfig, DiGraphConfig, NoJsFilesConfig, ValidateTsInSrcConfig, ValidateArchitectureUnchangedConfig, ValidateNoArchitectureCyclesConfig, ValidatePackageJsonConfig, ValidateVersionsLockedConfig, ValidateEslintSyncConfig, BaseRuleConfig, } from './rule-configs';
|
|
41
41
|
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';
|
|
42
42
|
export { NoClientCreationOutsideServerOrClientConfig, CLIENT_CREATION_SEVERITIES, } from './no-client-creation-config';
|
|
43
43
|
export type { ClientCreationSeverity } from './no-client-creation-config';
|
|
44
44
|
export type { MethodLimitMode, FileLimitMode, ReturnTypeMode, InlineTypeMode, ModifiedCodeMode, ProjectMode, PrismaValidateDtosMode, PrismaConverterMode, DirectApiResolverMode, ThrowCauseMode, OnOffMode, StructuralMode, ValidateTsMode, } from './rule-configs';
|
|
45
45
|
export { FeatureBranchGuardConfig, ReadStaleGuardConfig, MergedBranchBashGuardConfig, StaleMainBashGuardConfig, } from './main-sync-guard-configs';
|
|
46
|
-
export { GateDefinition, PrGateConfig, LandPrConfig, ReviewContextEntry, defaultGates, defaultPrGateConfig, defaultLandPrConfig, buildPrGateConfig, buildLandPrConfig, MERGE_MODE_AUTO, MERGE_MODE_NONE, MERGE_MODES, } from './pr-gate-config';
|
|
46
|
+
export { GateDefinition, PrGateConfig, LandPrConfig, DevDeployConfig, DEFAULT_DEV_BRANCH_NAMESPACE, DEFAULT_DEV_BRANCH, ReviewContextEntry, defaultGates, defaultPrGateConfig, defaultLandPrConfig, defaultDevDeployConfig, buildPrGateConfig, buildLandPrConfig, buildDevDeployConfig, MERGE_MODE_AUTO, MERGE_MODE_NONE, MERGE_MODES, } from './pr-gate-config';
|
|
47
47
|
export { ChecklistDefinition, toChecklist, normalizeChecklistDoc, formatFileList, } from './checklist-config';
|
|
48
48
|
export type { RawChecklistItem } from './checklist-config';
|
|
49
49
|
export { ChecklistValidator } from './checklist-validator';
|