gennady 0.8.2 → 0.8.4
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/README.md +212 -3
- package/ai/agents/agent-resolve-conflicts.xml +148 -3
- package/ai/agents/agent-review-verifier.xml +181 -3
- package/ai/directives/sdd/audit.directive.xml +22 -11
- package/ai/directives/sdd/critic-protocol.xml +3 -0
- package/ai/directives/sdd/critic.directive.xml +25 -3
- package/ai/skills/README.md +150 -0
- package/ai/skills/sdd-check/SKILL.md +19 -9
- package/ai/skills/sdd-execute/scripts/README.md +3 -0
- package/ai/skills/sdd-execute/scripts/_sdd-lib.sh +60 -0
- package/ai/skills/sdd-execute/scripts/check.sh +238 -0
- package/ai/skills/sdd-execute/scripts/sdd +12 -0
- package/ai/skills/sdd-hooks-install/SKILL.md +88 -0
- package/ai/skills/workspace-permission-setup/SKILL.md +193 -0
- package/cli/cmd/README.md +185 -0
- package/dist/ai/agents/agent-resolve-conflicts.xml +148 -3
- package/dist/ai/agents/agent-review-verifier.xml +181 -3
- package/dist/ai/directives/sdd/audit.directive.xml +22 -11
- package/dist/ai/directives/sdd/critic-protocol.xml +3 -0
- package/dist/ai/directives/sdd/critic.directive.xml +25 -3
- package/dist/ai/skills/README.md +150 -0
- package/dist/ai/skills/sdd-check/SKILL.md +19 -9
- package/dist/ai/skills/sdd-execute/scripts/README.md +3 -0
- package/dist/ai/skills/sdd-execute/scripts/_sdd-lib.sh +60 -0
- package/dist/ai/skills/sdd-execute/scripts/check.sh +238 -0
- package/dist/ai/skills/sdd-execute/scripts/sdd +12 -0
- package/dist/ai/skills/sdd-hooks-install/SKILL.md +88 -0
- package/dist/ai/skills/workspace-permission-setup/SKILL.md +193 -0
- package/dist/chunks/index-BHEP1kYv.js +164 -0
- package/dist/chunks/index-DqSFtXFv.js +813 -0
- package/dist/chunks/index-Ukr3nSKB.js +377 -0
- package/dist/cli/cmd/lint/checks/anchor-thin.check.d.ts +11 -0
- package/dist/cli/cmd/lint/lint.cmd.d.ts +1 -1
- package/dist/cli/cmd/lint/lint.types.d.ts +2 -0
- package/dist/gennady.js +4 -4
- package/package.json +1 -1
- package/dist/chunks/index-4fUNp3za.js +0 -156
- package/dist/chunks/index-C9UEDrqg.js +0 -754
- package/dist/chunks/index-jOIhIHdS.js +0 -369
|
@@ -1,754 +0,0 @@
|
|
|
1
|
-
import { execSync as z } from "node:child_process";
|
|
2
|
-
import { existsSync as K, readFileSync as Y, readdirSync as P, statSync as X, lstatSync as q } from "node:fs";
|
|
3
|
-
import { resolve as y, relative as H, extname as F, join as Q } from "node:path";
|
|
4
|
-
import { l as C, D as ee, a as te, b as ne, s as se } from "./services-IgZptnTi.js";
|
|
5
|
-
import { p as oe } from "./shared-9J_oXE74.js";
|
|
6
|
-
class w {
|
|
7
|
-
/** @purpose Collected lint errors — empty array when clean. */
|
|
8
|
-
errors;
|
|
9
|
-
/** @purpose Count of errors auto-fixed by DbcContractCheck. */
|
|
10
|
-
autoFixed;
|
|
11
|
-
/** @purpose Deduplicated task file paths resolved from @tasks annotations in linted files. */
|
|
12
|
-
taskPaths;
|
|
13
|
-
/** @purpose Deduplicated spec file paths resolved from task files. */
|
|
14
|
-
specPaths;
|
|
15
|
-
/**
|
|
16
|
-
* @purpose Creates a LintReport with collected errors, autoFixed count, and resolved references.
|
|
17
|
-
* @param errors Collected lint errors.
|
|
18
|
-
* @param [autoFixed] Count of auto-fixed errors, defaults to 0.
|
|
19
|
-
* @param [taskPaths] Resolved task file paths (deduplicated).
|
|
20
|
-
* @param [specPaths] Resolved spec file paths (deduplicated).
|
|
21
|
-
*/
|
|
22
|
-
constructor(t, e = 0, n = [], s = []) {
|
|
23
|
-
this.errors = t, this.autoFixed = e, this.taskPaths = n, this.specPaths = s;
|
|
24
|
-
}
|
|
25
|
-
/** @purpose Returns 0 when no errors, 1 otherwise — ESLint convention. | @returns 0 for clean, 1 for errors. */
|
|
26
|
-
get exitCode() {
|
|
27
|
-
return this.errors.length > 0 ? 1 : 0;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* @purpose Formats errors in ESLint-compatible output with resolved task/spec references.
|
|
31
|
-
* @returns ESLint-compatible formatted string with References block.
|
|
32
|
-
*/
|
|
33
|
-
format() {
|
|
34
|
-
const t = [];
|
|
35
|
-
if (this.autoFixed > 0 && t.push(`Auto-fixed: ${this.autoFixed} error(s)`), this.errors.length > 0 && t.push(
|
|
36
|
-
...this.errors.map(
|
|
37
|
-
(e) => `${e.file}:${e.line}:${e.col}: ${e.severity}: ${e.code}: ${e.message}`
|
|
38
|
-
)
|
|
39
|
-
), this.errors.length > 0 && (this.specPaths.length > 0 || this.taskPaths.length > 0)) {
|
|
40
|
-
t.push(""), t.push("---"), t.push("Relevant specs & tasks for errors above:");
|
|
41
|
-
for (const e of this.specPaths)
|
|
42
|
-
t.push(` ${e}`);
|
|
43
|
-
for (const e of this.taskPaths)
|
|
44
|
-
t.push(` ${e}`);
|
|
45
|
-
}
|
|
46
|
-
return t.join(`
|
|
47
|
-
`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const U = "ERR_CLI_LINT_MISSING_FILE", W = "ERR_CLI_LINT_MISSING_CONSUMERS", ie = "ERR_CLI_LINT_ANCHOR_UNPAIRED_START", re = "ERR_CLI_LINT_ANCHOR_UNPAIRED_END", ce = "ERR_CLI_LINT_ANCHOR_NESTING", O = "ERR_CLI_LINT_ANCHOR_MALFORMED", G = "ERR_CLI_LINT_NON_ENGLISH", le = "ERR_CLI_LINT_RESOLVE_FAILED", ae = "ERR_CLI_LINT_STAGED_CONFLICT", fe = "ERR_CLI_LINT_UNAUTHORIZED_DISABLE", ue = "ERR_CLI_LINT_DISABLE_MISSING_PURPOSE", k = "ERR_CLI_LINT_TOO_MANY_INVARIANTS", de = "ERR_CLI_LINT_ANCHOR_AT_CLASS_BODY", he = "ERR_CLI_LINT_ANCHOR_CONSECUTIVE_START";
|
|
51
|
-
function ge(o, t) {
|
|
52
|
-
const e = [];
|
|
53
|
-
if (o.length === 0)
|
|
54
|
-
return e.push({
|
|
55
|
-
file: t,
|
|
56
|
-
line: 1,
|
|
57
|
-
col: 1,
|
|
58
|
-
severity: "error",
|
|
59
|
-
code: U,
|
|
60
|
-
message: "[FileHeaderCheck#check] Missing // @file: directive"
|
|
61
|
-
}), e.push({
|
|
62
|
-
file: t,
|
|
63
|
-
line: 1,
|
|
64
|
-
col: 1,
|
|
65
|
-
severity: "error",
|
|
66
|
-
code: W,
|
|
67
|
-
message: "[FileHeaderCheck#check] Missing // @consumers: directive"
|
|
68
|
-
}), e;
|
|
69
|
-
const n = o.split(`
|
|
70
|
-
`);
|
|
71
|
-
let s = !1, i = !1;
|
|
72
|
-
for (const r of n) {
|
|
73
|
-
const c = r.trim();
|
|
74
|
-
if (c.startsWith("import ") || (c.startsWith("// @file:") && (s = !0), c.startsWith("// @consumers:") && (i = !0), s && i))
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
return s || e.push({
|
|
78
|
-
file: t,
|
|
79
|
-
line: 1,
|
|
80
|
-
col: 1,
|
|
81
|
-
severity: "error",
|
|
82
|
-
code: U,
|
|
83
|
-
message: "[FileHeaderCheck#check] Missing // @file: directive"
|
|
84
|
-
}), i || e.push({
|
|
85
|
-
file: t,
|
|
86
|
-
line: 1,
|
|
87
|
-
col: 1,
|
|
88
|
-
severity: "error",
|
|
89
|
-
code: W,
|
|
90
|
-
message: "[FileHeaderCheck#check] Missing // @consumers: directive"
|
|
91
|
-
}), e;
|
|
92
|
-
}
|
|
93
|
-
function D(o) {
|
|
94
|
-
let t = "", e = 0, n = !1, s = !1, i = !1;
|
|
95
|
-
for (; e < o.length; ) {
|
|
96
|
-
const r = o[e], c = o[e + 1] ?? "";
|
|
97
|
-
if (!n && !s && !i) {
|
|
98
|
-
if (r === "/" && c === "/") break;
|
|
99
|
-
if (r === "/" && c === "*") {
|
|
100
|
-
for (e += 2; e < o.length - 1 && !(o[e] === "*" && o[e + 1] === "/"); ) e++;
|
|
101
|
-
e += 2;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (r === "\\") {
|
|
106
|
-
e += 2;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (!s && !i && r === "'") {
|
|
110
|
-
n = !n, e++;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (!n && !i && r === '"') {
|
|
114
|
-
s = !s, e++;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (!n && !s && r === "`") {
|
|
118
|
-
i = !i, e++;
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
!n && !s && !i && (t += r), e++;
|
|
122
|
-
}
|
|
123
|
-
return t;
|
|
124
|
-
}
|
|
125
|
-
const me = /\/\/ #(region|endregion)\s+(START|END)_([A-Z0-9_]+)/, _e = /\/\/ #(region|endregion)\s*$/;
|
|
126
|
-
function pe(o, t) {
|
|
127
|
-
const e = o.split(`
|
|
128
|
-
`), n = [], s = [];
|
|
129
|
-
let i = 0;
|
|
130
|
-
const r = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map();
|
|
131
|
-
for (let l = 0; l < e.length; l++) {
|
|
132
|
-
const f = e[l], d = f.match(me);
|
|
133
|
-
if (i = Ce(f, i), d) {
|
|
134
|
-
const u = d[2], g = d[3], a = l + 1, R = d.index ?? 0, N = f.indexOf(u, R), p = (N >= 0 ? N : R) + 1;
|
|
135
|
-
if (u === "START") {
|
|
136
|
-
const E = r.get(i), T = c.get(i) ?? !1;
|
|
137
|
-
E && Ee(s, E) && !T && n.push({
|
|
138
|
-
file: t,
|
|
139
|
-
line: a,
|
|
140
|
-
col: p,
|
|
141
|
-
severity: "error",
|
|
142
|
-
code: he,
|
|
143
|
-
message: `START_${g} immediately follows START_${E.name} at the same level — merge regions or close START_${E.name} first`
|
|
144
|
-
}), s.push({ name: g, line: a, col: p }), r.set(i, { name: g, line: a, col: p }), c.set(i, !1);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
const S = Re(s, (E) => E.name === g);
|
|
148
|
-
if (S === -1)
|
|
149
|
-
n.push({
|
|
150
|
-
file: t,
|
|
151
|
-
line: a,
|
|
152
|
-
col: p,
|
|
153
|
-
severity: "error",
|
|
154
|
-
code: re,
|
|
155
|
-
message: `END_${g} without matching START_${g} — add the missing opening anchor or remove this closing one`
|
|
156
|
-
});
|
|
157
|
-
else {
|
|
158
|
-
for (let E = s.length - 1; E > S; E--) {
|
|
159
|
-
const T = s[E];
|
|
160
|
-
n.push({
|
|
161
|
-
file: t,
|
|
162
|
-
line: a,
|
|
163
|
-
col: p,
|
|
164
|
-
severity: "error",
|
|
165
|
-
code: ce,
|
|
166
|
-
message: `END_${g} closes parent while START_${T.name} at line ${T.line} is still open — close inner block first`
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
s.length = S, r.delete(i), c.set(i, !1);
|
|
170
|
-
}
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
Le(f) && c.set(i, !0);
|
|
174
|
-
const _ = f.match(_e);
|
|
175
|
-
if (_) {
|
|
176
|
-
const u = l + 1, g = (_.index ?? 0) + 1;
|
|
177
|
-
if (_[1] === "endregion")
|
|
178
|
-
if (s.length > 0) {
|
|
179
|
-
const a = s[s.length - 1];
|
|
180
|
-
n.push({
|
|
181
|
-
file: t,
|
|
182
|
-
line: u,
|
|
183
|
-
col: g,
|
|
184
|
-
severity: "error",
|
|
185
|
-
code: O,
|
|
186
|
-
message: `#endregion without END_<NAME> — expected END_${a.name} (auto-closed by stack top START_${a.name} at line ${a.line})`
|
|
187
|
-
}), s.pop(), r.delete(i), c.set(i, !1);
|
|
188
|
-
} else
|
|
189
|
-
n.push({
|
|
190
|
-
file: t,
|
|
191
|
-
line: u,
|
|
192
|
-
col: g,
|
|
193
|
-
severity: "error",
|
|
194
|
-
code: O,
|
|
195
|
-
message: "#endregion without END_<NAME> — no open block to close"
|
|
196
|
-
});
|
|
197
|
-
else
|
|
198
|
-
n.push({
|
|
199
|
-
file: t,
|
|
200
|
-
line: u,
|
|
201
|
-
col: g,
|
|
202
|
-
severity: "error",
|
|
203
|
-
code: O,
|
|
204
|
-
message: "#region without START_<NAME> — add START_ prefix with a block name"
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
for (const l of s)
|
|
209
|
-
n.push({
|
|
210
|
-
file: t,
|
|
211
|
-
line: l.line,
|
|
212
|
-
col: l.col,
|
|
213
|
-
severity: "error",
|
|
214
|
-
code: ie,
|
|
215
|
-
message: `START_${l.name} without matching END_${l.name} — add the missing closing anchor`
|
|
216
|
-
});
|
|
217
|
-
return n.sort((l, f) => l.line - f.line || l.col - f.col), n;
|
|
218
|
-
}
|
|
219
|
-
function Re(o, t) {
|
|
220
|
-
for (let e = o.length - 1; e >= 0; e--)
|
|
221
|
-
if (t(o[e])) return e;
|
|
222
|
-
return -1;
|
|
223
|
-
}
|
|
224
|
-
function Ce(o, t) {
|
|
225
|
-
let e = t;
|
|
226
|
-
const n = D(o);
|
|
227
|
-
for (const s of n)
|
|
228
|
-
s === "{" ? e++ : s === "}" && e--;
|
|
229
|
-
return e;
|
|
230
|
-
}
|
|
231
|
-
function Ee(o, t) {
|
|
232
|
-
return o.some(
|
|
233
|
-
(e) => e.name === t.name && e.line === t.line && e.col === t.col
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
function Le(o) {
|
|
237
|
-
const t = o.trim();
|
|
238
|
-
return !(t === "" || t.startsWith("//") || t.startsWith("/*") && t.endsWith("*/"));
|
|
239
|
-
}
|
|
240
|
-
async function Ie(o, t, e) {
|
|
241
|
-
C.debug(`[DbcContractCheck#check] [idle → creating] ${t}`);
|
|
242
|
-
const n = new te(), s = new ne(), i = new ee(n, s);
|
|
243
|
-
try {
|
|
244
|
-
if (!e) {
|
|
245
|
-
const c = await i.lint(t, { content: o });
|
|
246
|
-
return C.info(
|
|
247
|
-
`[DbcContractCheck#check] [creating → linted] ${t} (${c.errors.length} errors)`
|
|
248
|
-
), { errors: c.errors.map(B), autoFixed: 0 };
|
|
249
|
-
}
|
|
250
|
-
const r = await i.lintAndFix(t, { content: o });
|
|
251
|
-
return C.info(
|
|
252
|
-
`[DbcContractCheck#check] [creating → fixed] ${t} (autoFixed=${r.autoFixed}, remaining=${r.errors.length})`
|
|
253
|
-
), {
|
|
254
|
-
errors: r.errors.map(B),
|
|
255
|
-
autoFixed: r.autoFixed
|
|
256
|
-
};
|
|
257
|
-
} catch (r) {
|
|
258
|
-
const c = new Error(`[DbcContractCheck#check] Lint failed for ${t}`, { cause: r });
|
|
259
|
-
throw C.error(`[DbcContractCheck#check] [creating → failed] ${t}`, { error: c }), c;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
function B(o) {
|
|
263
|
-
return {
|
|
264
|
-
file: o.file,
|
|
265
|
-
line: o.line,
|
|
266
|
-
col: o.col,
|
|
267
|
-
severity: "error",
|
|
268
|
-
code: o.code,
|
|
269
|
-
message: o.message
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
const j = 8, Ne = /@ts-ignore|@ts-nocheck|@ts-expect-error|eslint-disable(?:-next-line|-line)?(?:-[a-z-]+)?\b/, Te = /\bd-\d+\b/i;
|
|
273
|
-
function Se(o) {
|
|
274
|
-
let t = !1, e = !1, n = !1;
|
|
275
|
-
for (let s = 0; s < o.length - 1; s++) {
|
|
276
|
-
const i = o[s], r = s > 0 ? o[s - 1] : "";
|
|
277
|
-
if (!t && !e && !n) {
|
|
278
|
-
if (i === "/" && (o[s + 1] === "/" || o[s + 1] === "*")) return s;
|
|
279
|
-
i === "'" ? t = !0 : i === '"' ? e = !0 : i === "`" && (n = !0);
|
|
280
|
-
} else r !== "\\" && (t && i === "'" ? t = !1 : e && i === '"' ? e = !1 : n && i === "`" && (n = !1));
|
|
281
|
-
}
|
|
282
|
-
return -1;
|
|
283
|
-
}
|
|
284
|
-
function ye(o, t) {
|
|
285
|
-
const e = o.split(`
|
|
286
|
-
`), n = [];
|
|
287
|
-
for (let s = 0; s < e.length; s++) {
|
|
288
|
-
const i = e[s], r = Se(i);
|
|
289
|
-
if (r < 0) continue;
|
|
290
|
-
const c = i.slice(r), l = Ne.exec(c);
|
|
291
|
-
if (!l) continue;
|
|
292
|
-
const f = l[0], d = r + l.index + 1, _ = Te.exec(i);
|
|
293
|
-
if (!_) {
|
|
294
|
-
n.push({
|
|
295
|
-
file: t,
|
|
296
|
-
line: s + 1,
|
|
297
|
-
col: d,
|
|
298
|
-
severity: "error",
|
|
299
|
-
code: fe,
|
|
300
|
-
message: `Unauthorized disable: \`${f}\` has no Decision Log reference. Add \`D-NNN\` (e.g., \`D-042\`) in the same comment line pointing to a Decision Log entry that authorizes this disable. Policy: see specs/cli/cli.spec.md#d-007.`
|
|
301
|
-
});
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
const u = xe(c, f, _[0]);
|
|
305
|
-
u < j && n.push({
|
|
306
|
-
file: t,
|
|
307
|
-
line: s + 1,
|
|
308
|
-
col: d,
|
|
309
|
-
severity: "error",
|
|
310
|
-
code: ue,
|
|
311
|
-
message: `Disable lacks purpose: \`${f}\` cites \`${_[0]}\` but the explanation is too short (${u} non-whitespace chars; need ≥ ${j}). Add a purpose after the D-NNN reference, e.g., \`${f}: ${_[0]} — <why this disable is necessary>\`. Policy: see specs/cli/cli.spec.md#d-007.`
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
return n;
|
|
315
|
-
}
|
|
316
|
-
function xe(o, t, e) {
|
|
317
|
-
let n = o.replace(t, "");
|
|
318
|
-
return n = n.replace(e, ""), n = n.replace(/\*\//g, ""), n = n.replace(/^\/\/|^\/\*/g, ""), n.replace(/\s/g, "").length;
|
|
319
|
-
}
|
|
320
|
-
const J = new RegExp("\\p{Script=Cyrillic}", "u");
|
|
321
|
-
function Ae(o, t) {
|
|
322
|
-
const e = [], n = o.split(`
|
|
323
|
-
`), s = ke(n);
|
|
324
|
-
for (let i = 0; i < n.length; i++) {
|
|
325
|
-
const r = n[i], c = i + 1, l = r.trim();
|
|
326
|
-
if (l.startsWith("// @file:") || l.startsWith("// @consumers:")) {
|
|
327
|
-
const f = [...r];
|
|
328
|
-
for (let d = 0; d < f.length; d++)
|
|
329
|
-
J.test(f[d]) && e.push({
|
|
330
|
-
file: t,
|
|
331
|
-
line: c,
|
|
332
|
-
col: d + 1,
|
|
333
|
-
severity: "error",
|
|
334
|
-
code: G,
|
|
335
|
-
message: `[LanguageCheck#check] Cyrillic character '${f[d]}' in file header — use English only`
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
if (s.has(i)) {
|
|
339
|
-
const f = [...r];
|
|
340
|
-
for (let d = 0; d < f.length; d++)
|
|
341
|
-
if (J.test(f[d])) {
|
|
342
|
-
e.push({
|
|
343
|
-
file: t,
|
|
344
|
-
line: c,
|
|
345
|
-
col: d + 1,
|
|
346
|
-
severity: "error",
|
|
347
|
-
code: G,
|
|
348
|
-
message: `[LanguageCheck#check] Cyrillic character '${f[d]}' in JSDoc contract — use English only`
|
|
349
|
-
});
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return e;
|
|
355
|
-
}
|
|
356
|
-
function ke(o) {
|
|
357
|
-
const t = /* @__PURE__ */ new Set();
|
|
358
|
-
let e = !1;
|
|
359
|
-
for (let n = 0; n < o.length; n++) {
|
|
360
|
-
const s = o[n].trim();
|
|
361
|
-
if (s.startsWith("/**")) {
|
|
362
|
-
e = !0, t.add(n), s.endsWith("*/") && (e = !1);
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
e && (t.add(n), s.includes("*/") && (e = !1));
|
|
366
|
-
}
|
|
367
|
-
return t;
|
|
368
|
-
}
|
|
369
|
-
const ve = /^\s*export\s+(?:(?:default\s+)?(?:function|class|abstract\s+class|interface))\s+(\w+)/, De = /^\s*(?:function|class|abstract\s+class|interface)\s+(\w+)/, $e = /\/\/\s*#region\s+START_\w+.*\binvariant\s*:/;
|
|
370
|
-
function be(o, t, e) {
|
|
371
|
-
const n = o.split(`
|
|
372
|
-
`), s = [];
|
|
373
|
-
let i = 0;
|
|
374
|
-
const r = [];
|
|
375
|
-
let c = null, l = !1, f = "", d = 0, _ = 0, u = !1, g = [];
|
|
376
|
-
for (let a = 0; a < n.length; a++) {
|
|
377
|
-
const R = n[a], N = a + 1, p = R.trim();
|
|
378
|
-
if (p.startsWith("/**"))
|
|
379
|
-
if (p.includes("*/") && p.length > 3) {
|
|
380
|
-
const m = V(p);
|
|
381
|
-
g = [];
|
|
382
|
-
for (let L = 0; L < m; L++) g.push(N);
|
|
383
|
-
} else {
|
|
384
|
-
u = !0, g = [];
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
if (u) {
|
|
388
|
-
if (p.includes("*/")) {
|
|
389
|
-
u = !1;
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
const m = V(p);
|
|
393
|
-
for (let L = 0; L < m; L++) g.push(N);
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
const S = R.match(ve), E = p.includes("*/") ? p.slice(p.lastIndexOf("*/") + 2) : null, T = E ? E.match(
|
|
397
|
-
/^\s*export\s+(?:(?:default\s+)?(?:function|class|abstract\s+class|interface))\s+(\w+)/
|
|
398
|
-
) : null, x = S || T, h = T ? p.lastIndexOf("*/") + 2 : 0;
|
|
399
|
-
if (x) {
|
|
400
|
-
l = !0, f = x[1], d = N;
|
|
401
|
-
const m = (x.index ?? 0) + h, L = R.indexOf(f, m);
|
|
402
|
-
_ = (L >= 0 ? L : m) + 1;
|
|
403
|
-
} else g.length > 0 && De.test(R) && (g = []);
|
|
404
|
-
const I = D(R);
|
|
405
|
-
for (const m of I)
|
|
406
|
-
if (m === "{") {
|
|
407
|
-
if (i++, l) {
|
|
408
|
-
const L = {
|
|
409
|
-
name: f,
|
|
410
|
-
nameLine: d,
|
|
411
|
-
nameCol: _,
|
|
412
|
-
bodyDepth: i,
|
|
413
|
-
jsdocCount: g.length,
|
|
414
|
-
regionCount: 0
|
|
415
|
-
};
|
|
416
|
-
r.push(L), c = L, l = !1, g = [];
|
|
417
|
-
}
|
|
418
|
-
} else m === "}" && (c && i === c.bodyDepth && (c.jsdocCount > e && s.push({
|
|
419
|
-
file: t,
|
|
420
|
-
line: c.nameLine,
|
|
421
|
-
col: c.nameCol,
|
|
422
|
-
severity: "error",
|
|
423
|
-
code: k,
|
|
424
|
-
message: `Entity "${c.name}" has ${c.jsdocCount} JSDoc @invariant tags (max ${e}) — consider reviewing the contract; too many invariants suggest the entity is overloaded`
|
|
425
|
-
}), c.regionCount > e && s.push({
|
|
426
|
-
file: t,
|
|
427
|
-
line: c.nameLine,
|
|
428
|
-
col: c.nameCol,
|
|
429
|
-
severity: "error",
|
|
430
|
-
code: k,
|
|
431
|
-
message: `Entity "${c.name}" has ${c.regionCount} region invariants (max ${e}) — consider reviewing the contract; too many region invariants suggest the entity is overloaded`
|
|
432
|
-
}), r.pop(), c = r.length > 0 ? r[r.length - 1] : null), i--);
|
|
433
|
-
c && $e.test(R) && c.regionCount++;
|
|
434
|
-
}
|
|
435
|
-
for (const a of r)
|
|
436
|
-
a.jsdocCount > e && s.push({
|
|
437
|
-
file: t,
|
|
438
|
-
line: a.nameLine,
|
|
439
|
-
col: a.nameCol,
|
|
440
|
-
severity: "error",
|
|
441
|
-
code: k,
|
|
442
|
-
message: `Entity "${a.name}" has ${a.jsdocCount} JSDoc @invariant tags (max ${e}) — consider reviewing the contract; too many invariants suggest the entity is overloaded`
|
|
443
|
-
}), a.regionCount > e && s.push({
|
|
444
|
-
file: t,
|
|
445
|
-
line: a.nameLine,
|
|
446
|
-
col: a.nameCol,
|
|
447
|
-
severity: "error",
|
|
448
|
-
code: k,
|
|
449
|
-
message: `Entity "${a.name}" has ${a.regionCount} region invariants (max ${e}) — consider reviewing the contract; too many region invariants suggest the entity is overloaded`
|
|
450
|
-
});
|
|
451
|
-
return s.sort((a, R) => a.line - R.line || a.col - R.col), s;
|
|
452
|
-
}
|
|
453
|
-
function V(o) {
|
|
454
|
-
const t = o.match(/@invariant\b/g);
|
|
455
|
-
return t ? t.length : 0;
|
|
456
|
-
}
|
|
457
|
-
const we = /\/\/ #(region|endregion)\s+(START|END)_([A-Z0-9_]+)/;
|
|
458
|
-
function Oe(o, t) {
|
|
459
|
-
const e = o.split(`
|
|
460
|
-
`), n = [];
|
|
461
|
-
let s = 0;
|
|
462
|
-
const i = [];
|
|
463
|
-
let r = !1;
|
|
464
|
-
for (let c = 0; c < e.length; c++) {
|
|
465
|
-
const l = e[c], f = c + 1;
|
|
466
|
-
/\b(?:class|namespace)\s+\w/.test(D(l)) && (r = !0);
|
|
467
|
-
const d = D(l);
|
|
468
|
-
for (const u of d)
|
|
469
|
-
u === "{" ? (s++, r && (i.push(s), r = !1)) : u === "}" && (i.length > 0 && s === i[i.length - 1] && i.pop(), s--);
|
|
470
|
-
const _ = l.match(we);
|
|
471
|
-
if (_ && i.length > 0 && s === i[i.length - 1]) {
|
|
472
|
-
const u = _[3], a = _[2] === "START" ? "START" : "END", N = (_.index ?? 0) + 1;
|
|
473
|
-
n.push({
|
|
474
|
-
file: t,
|
|
475
|
-
line: f,
|
|
476
|
-
col: N,
|
|
477
|
-
severity: "error",
|
|
478
|
-
code: de,
|
|
479
|
-
message: `#region ${a}_${u} at class body level — regions must not separate class members; move inside a method body or remove`
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
return n.sort((c, l) => c.line - l.line || c.col - l.col), n;
|
|
484
|
-
}
|
|
485
|
-
function Fe(o, t = "tasks") {
|
|
486
|
-
const e = /* @__PURE__ */ new Map(), n = y(o, t);
|
|
487
|
-
if (!K(n))
|
|
488
|
-
return e;
|
|
489
|
-
const s = He(n);
|
|
490
|
-
for (const i of s) {
|
|
491
|
-
let r;
|
|
492
|
-
try {
|
|
493
|
-
r = Y(i, "utf-8");
|
|
494
|
-
} catch {
|
|
495
|
-
continue;
|
|
496
|
-
}
|
|
497
|
-
const c = r.match(/-\s+\*\*Task-ID:\*\*\s+(TSK-\d+)/);
|
|
498
|
-
if (!c) continue;
|
|
499
|
-
const l = c[1], f = H(o, i), d = Me(r, o);
|
|
500
|
-
e.set(l, { taskPath: f, specPaths: d });
|
|
501
|
-
}
|
|
502
|
-
return e;
|
|
503
|
-
}
|
|
504
|
-
function Me(o, t) {
|
|
505
|
-
const e = /* @__PURE__ */ new Set(), n = o.match(/\*\*Spec References:\*\*[\s\S]*?(?=\n- \*\*|$)/);
|
|
506
|
-
if (!n) return [];
|
|
507
|
-
const s = n[0], i = /\[([^\]]*)\]\(([^)]+\.md)[^)]*\)/g;
|
|
508
|
-
let r;
|
|
509
|
-
for (; (r = i.exec(s)) !== null; ) {
|
|
510
|
-
const c = r[2].split("/").pop();
|
|
511
|
-
if (!c) continue;
|
|
512
|
-
const l = Pe(y(t, "specs"), c);
|
|
513
|
-
if (l) {
|
|
514
|
-
const f = H(t, l);
|
|
515
|
-
f.startsWith("..") || e.add(f);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
return [...e].sort();
|
|
519
|
-
}
|
|
520
|
-
function Pe(o, t) {
|
|
521
|
-
if (!K(o)) return null;
|
|
522
|
-
const e = [o];
|
|
523
|
-
for (; e.length > 0; ) {
|
|
524
|
-
const n = e.pop();
|
|
525
|
-
let s;
|
|
526
|
-
try {
|
|
527
|
-
s = P(n);
|
|
528
|
-
} catch {
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
for (const i of s) {
|
|
532
|
-
const r = y(n, i);
|
|
533
|
-
try {
|
|
534
|
-
const c = X(r);
|
|
535
|
-
if (c.isDirectory())
|
|
536
|
-
e.push(r);
|
|
537
|
-
else if (c.isFile() && i === t)
|
|
538
|
-
return r;
|
|
539
|
-
} catch {
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
return null;
|
|
544
|
-
}
|
|
545
|
-
function He(o) {
|
|
546
|
-
const t = [], e = [o];
|
|
547
|
-
for (; e.length > 0; ) {
|
|
548
|
-
const n = e.pop();
|
|
549
|
-
let s;
|
|
550
|
-
try {
|
|
551
|
-
s = P(n);
|
|
552
|
-
} catch {
|
|
553
|
-
continue;
|
|
554
|
-
}
|
|
555
|
-
for (const i of s) {
|
|
556
|
-
const r = y(n, i);
|
|
557
|
-
try {
|
|
558
|
-
const c = X(r);
|
|
559
|
-
c.isDirectory() ? e.push(r) : c.isFile() && i.endsWith(".md") && t.push(r);
|
|
560
|
-
} catch {
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
return t;
|
|
565
|
-
}
|
|
566
|
-
function Ue(o) {
|
|
567
|
-
const t = o.match(/@tasks:\s*(.+)/m);
|
|
568
|
-
return t ? t[1].split(/[,;\s]+/).map((e) => e.trim()).filter((e) => /^TSK-\d+$/.test(e)) : [];
|
|
569
|
-
}
|
|
570
|
-
function We(o, t) {
|
|
571
|
-
const e = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
|
|
572
|
-
for (const s of o) {
|
|
573
|
-
const i = t.get(s);
|
|
574
|
-
if (i) {
|
|
575
|
-
e.add(i.taskPath);
|
|
576
|
-
for (const r of i.specPaths)
|
|
577
|
-
n.add(r);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
return {
|
|
581
|
-
taskPaths: [...e].sort(),
|
|
582
|
-
specPaths: [...n].sort()
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
function Ge(o) {
|
|
586
|
-
let t = "", e = 0;
|
|
587
|
-
for (; e < o.length; ) {
|
|
588
|
-
const n = o[e], s = o[e + 1];
|
|
589
|
-
if (n === "*" && s === "*")
|
|
590
|
-
o[e + 2] === "/" ? (t += "(?:.*\\/)?", e += 3) : (e + 2 >= o.length, t += ".*", e += 2);
|
|
591
|
-
else if (n === "*")
|
|
592
|
-
t += "[^/]*", e++;
|
|
593
|
-
else if (n === "?")
|
|
594
|
-
t += "[^/]", e++;
|
|
595
|
-
else if (n === ".")
|
|
596
|
-
t += "\\.", e++;
|
|
597
|
-
else if (n === "[") {
|
|
598
|
-
const i = o.indexOf("]", e);
|
|
599
|
-
i !== -1 ? (t += o.slice(e, i + 1), e = i + 1) : (t += "\\[", e++);
|
|
600
|
-
} else
|
|
601
|
-
t += Be(n), e++;
|
|
602
|
-
}
|
|
603
|
-
return new RegExp("^" + t + "$");
|
|
604
|
-
}
|
|
605
|
-
function Be(o) {
|
|
606
|
-
return "^$\\+*.?{}[]()|/".includes(o) ? "\\" + o : o;
|
|
607
|
-
}
|
|
608
|
-
async function je(o) {
|
|
609
|
-
const t = oe(o, {
|
|
610
|
-
autofix: ["autofix"],
|
|
611
|
-
staged: ["staged"],
|
|
612
|
-
verbose: ["verbose", "v"],
|
|
613
|
-
maxInvariants: ["max-invariants"],
|
|
614
|
-
exclude: ["exclude"]
|
|
615
|
-
}), e = t._.filter(
|
|
616
|
-
(h) => typeof h == "string" && h !== "lint"
|
|
617
|
-
), n = t.autofix === !0 || t.autofix === "true", s = t.staged === !0 || t.staged === "true", i = t.verbose === !0 || t.verbose === "true", r = typeof t.maxInvariants == "string" ? parseInt(t.maxInvariants, 10) : 3, c = [
|
|
618
|
-
"**/node_modules/**",
|
|
619
|
-
"**/__tests__/**",
|
|
620
|
-
"**/fixtures/**",
|
|
621
|
-
"**/__tests__/fixtures/**",
|
|
622
|
-
"**/dist/**",
|
|
623
|
-
"**/coverage/**",
|
|
624
|
-
"**/build/**",
|
|
625
|
-
"**/out/**"
|
|
626
|
-
], l = t.exclude, f = Array.isArray(l) ? l : typeof l == "string" ? [l] : [], _ = [...c, ...f].map(Ge);
|
|
627
|
-
i && se("debug");
|
|
628
|
-
let u;
|
|
629
|
-
const g = [];
|
|
630
|
-
if (s) {
|
|
631
|
-
if (e.length > 0) {
|
|
632
|
-
const h = {
|
|
633
|
-
file: "",
|
|
634
|
-
line: 0,
|
|
635
|
-
col: 0,
|
|
636
|
-
severity: "error",
|
|
637
|
-
code: ae,
|
|
638
|
-
message: "--staged and positional targets are mutually exclusive. Use either --staged or provide file/directory paths, not both."
|
|
639
|
-
};
|
|
640
|
-
return C.warn("[LintCommand#run] --staged and positional targets are mutually exclusive"), new w([h]);
|
|
641
|
-
}
|
|
642
|
-
C.debug("[LintCommand#run] [idle → collecting] staged mode");
|
|
643
|
-
try {
|
|
644
|
-
u = z("git diff --staged --name-only", { encoding: "utf-8" }).trim().split(`
|
|
645
|
-
`).filter(Boolean).filter((I) => I.endsWith(".ts")), u = [...new Set(u)];
|
|
646
|
-
} catch (h) {
|
|
647
|
-
const I = new Error("[LintCommand#run] Git scan failed — not a git repository?", {
|
|
648
|
-
cause: h
|
|
649
|
-
});
|
|
650
|
-
throw C.error("[LintCommand#run] [collecting → failed]", { error: I }), I;
|
|
651
|
-
}
|
|
652
|
-
} else if (e.length > 0) {
|
|
653
|
-
C.debug(`[LintCommand#run] [idle → resolving] ${e.length} target(s)`);
|
|
654
|
-
const h = Ve(e);
|
|
655
|
-
u = h.files, g.push(...h.errors);
|
|
656
|
-
for (const I of h.errors)
|
|
657
|
-
C.warn(`[LintCommand#run] [resolving → failed] ${I.file}: ${I.message}`);
|
|
658
|
-
} else
|
|
659
|
-
u = [];
|
|
660
|
-
if (u.length > 0 && _.length > 0) {
|
|
661
|
-
const h = u.length, I = y(".");
|
|
662
|
-
u = u.filter((m) => {
|
|
663
|
-
const L = m.startsWith("/") ? H(I, m) : m;
|
|
664
|
-
return !_.some((A) => A.test(L));
|
|
665
|
-
}), C.debug(`[LintCommand#run] [filtering → filtered] ${h} → ${u.length} file(s)`);
|
|
666
|
-
}
|
|
667
|
-
if (u.length === 0)
|
|
668
|
-
return C.debug("[LintCommand#run] [collecting → done] no files to lint"), new w(g);
|
|
669
|
-
C.debug(`[LintCommand#run] [collecting → linting] ${u.length} file(s)`);
|
|
670
|
-
const a = [...g];
|
|
671
|
-
let R = 0;
|
|
672
|
-
const N = y("."), p = Fe(N), S = /* @__PURE__ */ new Set();
|
|
673
|
-
for (const h of u) {
|
|
674
|
-
const I = y(h);
|
|
675
|
-
let m;
|
|
676
|
-
try {
|
|
677
|
-
C.debug(`[LintCommand#run] [linting → reading] ${h}`), m = Y(I, "utf-8");
|
|
678
|
-
} catch ($) {
|
|
679
|
-
const b = new Error(`[LintCommand#run] Cannot read file: ${h}`, { cause: $ });
|
|
680
|
-
C.error(`[LintCommand#run] [reading → failed] ${h}`, { error: b });
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
const L = a.length;
|
|
684
|
-
a.push(...ge(m, h)), a.push(...pe(m, h)), a.push(...Ae(m, h)), a.push(...ye(m, h)), a.push(...Oe(m, h)), a.push(...be(m, h, r));
|
|
685
|
-
const A = await Ie(m, h, n);
|
|
686
|
-
if (a.push(...A.errors), R += A.autoFixed, a.length > L) {
|
|
687
|
-
const $ = Ue(m);
|
|
688
|
-
for (const b of $)
|
|
689
|
-
S.add(b);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
const { taskPaths: E, specPaths: T } = We([...S], p), x = new w(a, R, E, T);
|
|
693
|
-
return x.exitCode === 1 ? console.log(`❌ [LintCommand#run] [linting → failed] ${a.length} error(s)`) : console.log("✅ [LintCommand#run] [linting → clean] no errors"), x;
|
|
694
|
-
}
|
|
695
|
-
const M = [".ts", ".tsx"], Je = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", "build", "out", "__tests__"]);
|
|
696
|
-
function Ve(o) {
|
|
697
|
-
C.debug(`[resolveTargets] [idle → resolving] ${o.length} target(s)`);
|
|
698
|
-
const t = /* @__PURE__ */ new Set(), e = [];
|
|
699
|
-
for (const s of o) {
|
|
700
|
-
const i = F(s).toLowerCase();
|
|
701
|
-
if (i && !M.includes(i) && !s.endsWith("/"))
|
|
702
|
-
continue;
|
|
703
|
-
let r;
|
|
704
|
-
try {
|
|
705
|
-
r = q(s);
|
|
706
|
-
} catch (l) {
|
|
707
|
-
const f = l;
|
|
708
|
-
e.push({
|
|
709
|
-
file: s,
|
|
710
|
-
line: 0,
|
|
711
|
-
col: 0,
|
|
712
|
-
severity: "error",
|
|
713
|
-
code: le,
|
|
714
|
-
message: `${s}: ${f.code ?? "UNKNOWN"}: ${f.message}`
|
|
715
|
-
});
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
if (r.isSymbolicLink())
|
|
719
|
-
continue;
|
|
720
|
-
const c = y(s);
|
|
721
|
-
if (r.isDirectory())
|
|
722
|
-
Z(c, t);
|
|
723
|
-
else if (r.isFile()) {
|
|
724
|
-
const l = F(s).toLowerCase();
|
|
725
|
-
M.includes(l) && t.add(c);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
const n = [...t].sort();
|
|
729
|
-
return C.debug(
|
|
730
|
-
`[resolveTargets] [resolving → resolved] ${n.length} file(s), ${e.length} error(s)`
|
|
731
|
-
), { files: n, errors: e };
|
|
732
|
-
}
|
|
733
|
-
function Z(o, t) {
|
|
734
|
-
let e;
|
|
735
|
-
try {
|
|
736
|
-
e = P(o, { withFileTypes: !0 });
|
|
737
|
-
} catch {
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
for (const n of e) {
|
|
741
|
-
const s = Q(o, n.name);
|
|
742
|
-
if (!n.name.startsWith(".") && !Je.has(n.name) && !n.isSymbolicLink()) {
|
|
743
|
-
if (n.isDirectory())
|
|
744
|
-
Z(s, t);
|
|
745
|
-
else if (n.isFile()) {
|
|
746
|
-
const i = F(n.name).toLowerCase();
|
|
747
|
-
M.includes(i) && t.add(s);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
const v = await je(process.argv);
|
|
753
|
-
(v.exitCode === 1 || v.autoFixed > 0) && console.log(v.format());
|
|
754
|
-
process.exit(v.exitCode);
|