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
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { readdirSync as $, statSync as L, readFileSync as N, rmdirSync as O, unlinkSync as x, mkdirSync as T, writeFileSync as W } from "node:fs";
|
|
2
|
+
import { join as k, sep as K } from "node:path";
|
|
3
|
+
import { fileURLToPath as I } from "node:url";
|
|
4
|
+
import { l as z } from "./services-IgZptnTi.js";
|
|
5
|
+
import { n as b, d as B, b as M, p as j, r as G } from "./shared-9J_oXE74.js";
|
|
6
|
+
class H {
|
|
7
|
+
/** @purpose List of all synced file entries, sorted lexicographically by skillName then relativePath. */
|
|
8
|
+
entries;
|
|
9
|
+
/**
|
|
10
|
+
* @purpose Construct a SyncSkillsResult from a list of entries.
|
|
11
|
+
* @param entries File entries.
|
|
12
|
+
*/
|
|
13
|
+
constructor(e) {
|
|
14
|
+
this.entries = e;
|
|
15
|
+
}
|
|
16
|
+
/** @purpose Files with status "added". | @returns Array of added entries. */
|
|
17
|
+
get added() {
|
|
18
|
+
return this.entries.filter((e) => e.status === "added");
|
|
19
|
+
}
|
|
20
|
+
/** @purpose Files with status "updated". | @returns Array of updated entries. */
|
|
21
|
+
get updated() {
|
|
22
|
+
return this.entries.filter((e) => e.status === "updated");
|
|
23
|
+
}
|
|
24
|
+
/** @purpose Files with status "deleted". | @returns Array of deleted entries. */
|
|
25
|
+
get deleted() {
|
|
26
|
+
return this.entries.filter((e) => e.status === "deleted");
|
|
27
|
+
}
|
|
28
|
+
/** @purpose Files with status "unchanged". | @returns Array of unchanged entries. */
|
|
29
|
+
get unchanged() {
|
|
30
|
+
return this.entries.filter((e) => e.status === "unchanged");
|
|
31
|
+
}
|
|
32
|
+
/** @purpose Files with status "deleteFailed". | @returns Array of delete-failed entries. */
|
|
33
|
+
get deleteFailed() {
|
|
34
|
+
return this.entries.filter((e) => e.status === "deleteFailed");
|
|
35
|
+
}
|
|
36
|
+
/** @purpose Human-readable summary of sync counts. | @returns Summary string. */
|
|
37
|
+
get summary() {
|
|
38
|
+
const e = this.added.length, r = this.updated.length, a = this.unchanged.length, l = this.deleted.length, i = this.deleteFailed.length;
|
|
39
|
+
let c = `Synced: ${e} added, ${r} updated, ${a} skipped, ${l} deleted`;
|
|
40
|
+
return i > 0 && (c += `, ${i} delete failed`), c;
|
|
41
|
+
}
|
|
42
|
+
/** @purpose Dry-run summary message. | @returns Dry-run message. */
|
|
43
|
+
get dryRunSummary() {
|
|
44
|
+
return "Dry-run: no files written.";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const C = "ERR_SKILLS_SOURCE_NOT_FOUND", R = "ERR_SKILLS_SKILL_NOT_FOUND", v = /* @__PURE__ */ new Set([".DS_Store"]);
|
|
48
|
+
function J(t, e) {
|
|
49
|
+
const r = $(t).filter((i) => {
|
|
50
|
+
if (i.startsWith(".") || v.has(i)) return !1;
|
|
51
|
+
try {
|
|
52
|
+
return L(k(t, i)).isDirectory();
|
|
53
|
+
} catch {
|
|
54
|
+
return !1;
|
|
55
|
+
}
|
|
56
|
+
}), a = e && e.length > 0 ? e : r;
|
|
57
|
+
for (const i of a)
|
|
58
|
+
if (!r.includes(i)) {
|
|
59
|
+
const c = `[scanSkills] Skill "${i}" not found in source.
|
|
60
|
+
Available: ${r.join(", ")}`, n = new Error(c);
|
|
61
|
+
throw n.code = R, n;
|
|
62
|
+
}
|
|
63
|
+
const l = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const i of a) {
|
|
65
|
+
const c = k(t, i), n = /* @__PURE__ */ new Map();
|
|
66
|
+
P(c, "", void 0, n), l.set(i, n);
|
|
67
|
+
}
|
|
68
|
+
return l;
|
|
69
|
+
}
|
|
70
|
+
function P(t, e, r, a) {
|
|
71
|
+
const l = r ? r.readdir : $, i = r ? r.stat : L, c = r ? r.readFile : N;
|
|
72
|
+
let n;
|
|
73
|
+
try {
|
|
74
|
+
n = l(t);
|
|
75
|
+
} catch {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
for (const d of n) {
|
|
79
|
+
if (d.startsWith(".") || v.has(d)) continue;
|
|
80
|
+
const g = k(t, d), y = e ? k(e, d) : d;
|
|
81
|
+
let s;
|
|
82
|
+
try {
|
|
83
|
+
s = i(g);
|
|
84
|
+
} catch {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (s) {
|
|
88
|
+
if (s.isDirectory())
|
|
89
|
+
P(g, y, r, a);
|
|
90
|
+
else if (s.isFile()) {
|
|
91
|
+
const u = y.split(K).join("/");
|
|
92
|
+
a.set(u, c(g));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function V(t, e, r, a, l, i, c, n) {
|
|
98
|
+
let d;
|
|
99
|
+
if (a ? M(r, a) ? d = "updated" : d = "unchanged" : d = "added", !i && d !== "unchanged") {
|
|
100
|
+
const g = k(l, e);
|
|
101
|
+
n(k(g, ".."), { recursive: !0 }), c(g, r);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
skillName: t,
|
|
105
|
+
relativePath: e,
|
|
106
|
+
status: d,
|
|
107
|
+
sourceSize: r.length,
|
|
108
|
+
targetSize: a?.length
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function F(t, e) {
|
|
112
|
+
const r = [];
|
|
113
|
+
let a;
|
|
114
|
+
try {
|
|
115
|
+
a = e.readdir(t);
|
|
116
|
+
} catch {
|
|
117
|
+
return r;
|
|
118
|
+
}
|
|
119
|
+
for (const l of a) {
|
|
120
|
+
if (l.startsWith(".") || v.has(l)) continue;
|
|
121
|
+
const i = k(t, l);
|
|
122
|
+
try {
|
|
123
|
+
const c = e.stat(i);
|
|
124
|
+
c.isDirectory() ? r.push(...F(i, e).map((n) => k(l, n))) : c.isFile() && r.push(l);
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return r;
|
|
129
|
+
}
|
|
130
|
+
function X(t, e, r, a) {
|
|
131
|
+
const l = k(e, t);
|
|
132
|
+
let i = [];
|
|
133
|
+
try {
|
|
134
|
+
i = F(l, a);
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
if (r) {
|
|
138
|
+
const y = [
|
|
139
|
+
{
|
|
140
|
+
skillName: t,
|
|
141
|
+
relativePath: "",
|
|
142
|
+
status: "deleted",
|
|
143
|
+
sourceSize: void 0,
|
|
144
|
+
targetSize: void 0
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
for (const s of i.sort())
|
|
148
|
+
y.push({
|
|
149
|
+
skillName: t,
|
|
150
|
+
relativePath: s,
|
|
151
|
+
status: "deleted",
|
|
152
|
+
sourceSize: void 0,
|
|
153
|
+
targetSize: void 0
|
|
154
|
+
});
|
|
155
|
+
return y;
|
|
156
|
+
}
|
|
157
|
+
const c = a.unlink ?? (() => {
|
|
158
|
+
}), n = a.rmdir ?? (() => {
|
|
159
|
+
});
|
|
160
|
+
let d = !1, g;
|
|
161
|
+
for (const y of i) {
|
|
162
|
+
const s = k(l, y);
|
|
163
|
+
try {
|
|
164
|
+
c(s);
|
|
165
|
+
} catch (u) {
|
|
166
|
+
d = !0, g = u.code ?? "UNKNOWN";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
n(l, { recursive: !0 });
|
|
171
|
+
} catch (y) {
|
|
172
|
+
d = !0, g = y.code ?? "UNKNOWN";
|
|
173
|
+
}
|
|
174
|
+
return d ? [
|
|
175
|
+
{
|
|
176
|
+
skillName: t,
|
|
177
|
+
relativePath: "",
|
|
178
|
+
status: "deleteFailed",
|
|
179
|
+
errorCode: g
|
|
180
|
+
}
|
|
181
|
+
] : [
|
|
182
|
+
{
|
|
183
|
+
skillName: t,
|
|
184
|
+
relativePath: "",
|
|
185
|
+
status: "deleted"
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
function Y(t, e) {
|
|
190
|
+
let r;
|
|
191
|
+
try {
|
|
192
|
+
r = t.stat(e.sourceDir);
|
|
193
|
+
} catch {
|
|
194
|
+
const s = `[collectAndCompareSkills] Source directory not found: ${e.sourceDir}`, u = new Error(s);
|
|
195
|
+
throw u.code = C, u;
|
|
196
|
+
}
|
|
197
|
+
if (!r.isDirectory())
|
|
198
|
+
throw new Error(`[collectAndCompareSkills] sourceDir is not a directory: ${e.sourceDir}`);
|
|
199
|
+
const a = k(e.targetDir, "..");
|
|
200
|
+
try {
|
|
201
|
+
if (t.stat(a).isFile())
|
|
202
|
+
throw new Error("[collectAndCompareSkills] .claude exists but is not a directory");
|
|
203
|
+
} catch (s) {
|
|
204
|
+
if (s.code !== "ENOENT") throw s;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
t.mkdir(e.targetDir, { recursive: !0 });
|
|
208
|
+
} catch (s) {
|
|
209
|
+
const u = s;
|
|
210
|
+
throw u.code === "EACCES" ? new Error(`[collectAndCompareSkills] cannot write to ${e.targetDir}: ${u.code}`, {
|
|
211
|
+
cause: s
|
|
212
|
+
}) : u.code === "ENOTDIR" ? new Error("[collectAndCompareSkills] .claude exists but is not a directory", {
|
|
213
|
+
cause: s
|
|
214
|
+
}) : s;
|
|
215
|
+
}
|
|
216
|
+
const l = t.writeFile, i = t.mkdir, c = J(e.sourceDir, e.skillNames);
|
|
217
|
+
let n = [];
|
|
218
|
+
try {
|
|
219
|
+
n = t.readdir(e.targetDir).filter((s) => {
|
|
220
|
+
if (s.startsWith(".") || v.has(s)) return !1;
|
|
221
|
+
try {
|
|
222
|
+
return t.stat(k(e.targetDir, s)).isDirectory();
|
|
223
|
+
} catch {
|
|
224
|
+
return !1;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
const d = [];
|
|
230
|
+
for (const s of [...c.keys()].sort()) {
|
|
231
|
+
const u = c.get(s), o = k(e.targetDir, s);
|
|
232
|
+
let f = /* @__PURE__ */ new Map();
|
|
233
|
+
P(o, "", t, f);
|
|
234
|
+
for (const [p, D] of [...u.entries()].sort(
|
|
235
|
+
([h], [S]) => h.localeCompare(S)
|
|
236
|
+
)) {
|
|
237
|
+
const h = b(D.toString("utf-8"), B), S = Buffer.from(h, "utf-8"), m = f.get(p);
|
|
238
|
+
d.push(
|
|
239
|
+
V(
|
|
240
|
+
s,
|
|
241
|
+
p,
|
|
242
|
+
S,
|
|
243
|
+
m,
|
|
244
|
+
o,
|
|
245
|
+
e.dryRun ?? !1,
|
|
246
|
+
l,
|
|
247
|
+
i
|
|
248
|
+
)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
n = n.filter((p) => p !== s);
|
|
252
|
+
}
|
|
253
|
+
const g = e.skillNames, y = g ? n.filter((s) => g.includes(s)) : n;
|
|
254
|
+
for (const s of y.sort())
|
|
255
|
+
d.push(...X(s, e.targetDir, e.dryRun ?? !1, t));
|
|
256
|
+
return new H(d);
|
|
257
|
+
}
|
|
258
|
+
const q = "(would add)", Q = "(would update)", Z = "(would delete)", ee = "(unchanged, skip)", te = "(unchanged)", w = {
|
|
259
|
+
added: 0,
|
|
260
|
+
updated: 1,
|
|
261
|
+
deleted: 2,
|
|
262
|
+
deleteFailed: 3,
|
|
263
|
+
unchanged: 4
|
|
264
|
+
};
|
|
265
|
+
function re(t, e = {}) {
|
|
266
|
+
const r = e.dryRun === !0;
|
|
267
|
+
if (t.length === 0)
|
|
268
|
+
return r ? ["Dry-run: no files written."] : ["Synced: 0 added, 0 updated, 0 skipped, 0 deleted"];
|
|
269
|
+
const a = /* @__PURE__ */ new Map();
|
|
270
|
+
for (const o of t) {
|
|
271
|
+
let f = a.get(o.skillName);
|
|
272
|
+
f || (f = { entries: [], dominantStatus: "unchanged" }, a.set(o.skillName, f)), f.entries.push(o);
|
|
273
|
+
const p = w[o.status], D = w[f.dominantStatus];
|
|
274
|
+
p < D && (f.dominantStatus = o.status);
|
|
275
|
+
}
|
|
276
|
+
const l = [...a.entries()].sort(([o, f], [p, D]) => {
|
|
277
|
+
const h = w[f.dominantStatus] - w[D.dominantStatus];
|
|
278
|
+
return h !== 0 ? h : o.localeCompare(p);
|
|
279
|
+
}), c = Math.max(...l.map(([o]) => ` _ ${o}/`.length)) + 4, n = [];
|
|
280
|
+
for (const [o, f] of l) {
|
|
281
|
+
const p = f.dominantStatus;
|
|
282
|
+
if (p === "added") {
|
|
283
|
+
const h = ` + ${o}/`;
|
|
284
|
+
n.push(h);
|
|
285
|
+
for (const S of f.entries.sort((m, _) => m.relativePath.localeCompare(_.relativePath)))
|
|
286
|
+
r ? n.push(` ${S.relativePath}`.padEnd(c) + q) : n.push(` ${S.relativePath}`);
|
|
287
|
+
} else if (p === "updated") {
|
|
288
|
+
const h = ` ~ ${o}/`;
|
|
289
|
+
n.push(h);
|
|
290
|
+
const S = f.entries.filter((m) => m.status === "added" || m.status === "updated").sort((m, _) => m.relativePath.localeCompare(_.relativePath));
|
|
291
|
+
for (const m of S)
|
|
292
|
+
r ? n.push(` ${m.relativePath}`.padEnd(c) + Q) : n.push(` ${m.relativePath}`);
|
|
293
|
+
} else if (p === "deleted") {
|
|
294
|
+
const h = ` - ${o}/`;
|
|
295
|
+
r ? n.push(h.padEnd(c) + Z) : n.push(h);
|
|
296
|
+
for (const S of f.entries.filter((m) => m.relativePath !== "").sort((m, _) => m.relativePath.localeCompare(_.relativePath)))
|
|
297
|
+
n.push(` ${S.relativePath}`);
|
|
298
|
+
} else if (p === "deleteFailed") {
|
|
299
|
+
const h = f.entries.find((m) => m.errorCode)?.errorCode ?? "UNKNOWN", S = ` ! ${o}/`;
|
|
300
|
+
n.push(S.padEnd(c) + `(delete failed: ${h})`);
|
|
301
|
+
} else {
|
|
302
|
+
const h = ` = ${o}/`, S = r ? ee : te;
|
|
303
|
+
n.push(h.padEnd(c) + S);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const d = t.filter((o) => o.status === "added").length, g = t.filter((o) => o.status === "updated").length, y = t.filter((o) => o.status === "unchanged").length, s = t.filter((o) => o.status === "deleted").length, u = t.filter((o) => o.status === "deleteFailed").length;
|
|
307
|
+
if (r)
|
|
308
|
+
n.push("Dry-run: no files written.");
|
|
309
|
+
else {
|
|
310
|
+
let o = `Synced: ${d} added, ${g} updated, ${y} skipped, ${s} deleted`;
|
|
311
|
+
u > 0 && (o += `, ${u} delete failed`), n.push(o);
|
|
312
|
+
}
|
|
313
|
+
return n;
|
|
314
|
+
}
|
|
315
|
+
function ne(t) {
|
|
316
|
+
try {
|
|
317
|
+
const e = k(t, "..", ".."), r = N(k(e, "package.json"), "utf-8");
|
|
318
|
+
return JSON.parse(r).version ?? "0.0.0";
|
|
319
|
+
} catch {
|
|
320
|
+
return "0.0.0";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function se(t, e, r, a, l) {
|
|
324
|
+
r.write(`Sync skills (v${l})${e ? " (dry-run)" : ""}: ${a}
|
|
325
|
+
`);
|
|
326
|
+
const i = re(t, { dryRun: e });
|
|
327
|
+
for (const c of i)
|
|
328
|
+
r.write(c + `
|
|
329
|
+
`);
|
|
330
|
+
}
|
|
331
|
+
function A(t, e) {
|
|
332
|
+
const r = N, a = W, l = T, i = L, c = $, n = x, d = O, g = G, y = process.stdout, s = process.stderr, u = j(t, {
|
|
333
|
+
dryRun: ["dry-run"]
|
|
334
|
+
}), o = u.dryRun === !0 || u.dryRun === "true", f = u._.filter(
|
|
335
|
+
(_) => typeof _ == "string" && _ !== "sync-skills"
|
|
336
|
+
), p = process.cwd(), D = g(p, "ai/skills");
|
|
337
|
+
if (!D)
|
|
338
|
+
return s.write(`Error: gennady package not found. Install it locally: npm i -D gennady
|
|
339
|
+
`), 1;
|
|
340
|
+
const h = ne(D), S = k(p, ".claude", "skills"), m = {
|
|
341
|
+
sourceDir: D,
|
|
342
|
+
targetDir: S,
|
|
343
|
+
skillNames: f.length > 0 ? f : void 0,
|
|
344
|
+
dryRun: o
|
|
345
|
+
};
|
|
346
|
+
try {
|
|
347
|
+
const E = Y({
|
|
348
|
+
readFile: r,
|
|
349
|
+
writeFile: a,
|
|
350
|
+
mkdir: l,
|
|
351
|
+
stat: (U) => i(U),
|
|
352
|
+
readdir: c,
|
|
353
|
+
unlink: n,
|
|
354
|
+
rmdir: d,
|
|
355
|
+
resolvePackageDir: g
|
|
356
|
+
}, m);
|
|
357
|
+
return se(E.entries, o, y, p, h), 0;
|
|
358
|
+
} catch (_) {
|
|
359
|
+
const E = _;
|
|
360
|
+
if (E.code === R || E.code === C)
|
|
361
|
+
return s.write(`Error: ${E.message}
|
|
362
|
+
`), 1;
|
|
363
|
+
throw E;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (process.argv[1]) {
|
|
367
|
+
const t = I(import.meta.url);
|
|
368
|
+
if (t === process.argv[1] || t.endsWith(process.argv[1]))
|
|
369
|
+
try {
|
|
370
|
+
const e = A(process.argv);
|
|
371
|
+
process.exit(e);
|
|
372
|
+
} catch (e) {
|
|
373
|
+
const r = new Error("[syncSkillsCmd] Self-execution failed", { cause: e });
|
|
374
|
+
z.error("[syncSkillsCmd#run] [self-executing → failed]", { error: r }), process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
process.exit(A(process.argv));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LintError } from '../lint.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* @purpose Detects #region START / #endregion END blocks with fewer than 2 meaningful lines
|
|
4
|
+
* of code. Regions wrapping only comments should be replaced with plain comments.
|
|
5
|
+
* @invariant Meaningful line = non-empty, non-`//`-comment, non-region-marker.
|
|
6
|
+
* @invariant Nested regions: code inside inner regions counts toward outer region totals.
|
|
7
|
+
* @param content Source text to validate.
|
|
8
|
+
* @param filePath File path for error messages.
|
|
9
|
+
* @returns List of lint errors, empty when all regions have sufficient content.
|
|
10
|
+
*/
|
|
11
|
+
export declare function check(content: string, filePath: string): LintError[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LintReport } from './lint.types.js';
|
|
2
2
|
import type { LintError } from './lint.types.js';
|
|
3
3
|
/**
|
|
4
|
-
* @purpose Execute the gennady lint command — collect files, run
|
|
4
|
+
* @purpose Execute the gennady lint command — collect files, run 8 checks, output ESLint-format report.
|
|
5
5
|
* @implements {LintCommand} in specs/cli/lint/lint.spec.md
|
|
6
6
|
* @param rawArgs Raw command-line arguments (process.argv).
|
|
7
7
|
* @returns LintReport with aggregated errors and exit code.
|
|
@@ -76,3 +76,5 @@ export declare const ERR_CLI_LINT_TOO_MANY_INVARIANTS: "ERR_CLI_LINT_TOO_MANY_IN
|
|
|
76
76
|
export declare const ERR_CLI_LINT_ANCHOR_AT_CLASS_BODY: "ERR_CLI_LINT_ANCHOR_AT_CLASS_BODY";
|
|
77
77
|
/** @purpose Two #region START at the same brace depth without an intervening #endregion END — merge or close the first region. */
|
|
78
78
|
export declare const ERR_CLI_LINT_ANCHOR_CONSECUTIVE_START: "ERR_CLI_LINT_ANCHOR_CONSECUTIVE_START";
|
|
79
|
+
/** @purpose Region contains fewer than 2 meaningful lines — region is too thin. Keep any comment and remove the region wrapper. */
|
|
80
|
+
export declare const ERR_CLI_LINT_ANCHOR_TOO_THIN: "ERR_CLI_LINT_ANCHOR_TOO_THIN";
|
package/dist/gennady.js
CHANGED
|
@@ -3,7 +3,7 @@ import { c as s } from "./chunks/shared-9J_oXE74.js";
|
|
|
3
3
|
import "node:fs";
|
|
4
4
|
import "node:path";
|
|
5
5
|
import "node:url";
|
|
6
|
-
const r = "0.8.
|
|
6
|
+
const r = "0.8.4", i = /* @__PURE__ */ new Set(["help", "--help", "-h"]), o = /* @__PURE__ */ new Set(["--version", "-v"]), t = process.argv[2];
|
|
7
7
|
o.has(t) && (console.log(r), process.exit(0));
|
|
8
8
|
(!t || i.has(t)) && (await import("./chunks/help.cmd-BDO4k0_z.js"), process.exit(0));
|
|
9
9
|
s({ name: "gennady", version: r });
|
|
@@ -79,7 +79,7 @@ switch (t) {
|
|
|
79
79
|
await import("./chunks/index-9JlEjsvH.js");
|
|
80
80
|
break;
|
|
81
81
|
case "lint":
|
|
82
|
-
await import("./chunks/index-
|
|
82
|
+
await import("./chunks/index-DqSFtXFv.js");
|
|
83
83
|
break;
|
|
84
84
|
case "alt-opinion":
|
|
85
85
|
await import("./chunks/index-BXWDYM_9.js");
|
|
@@ -88,10 +88,10 @@ switch (t) {
|
|
|
88
88
|
await import("./chunks/index-DoaxOhQ8.js");
|
|
89
89
|
break;
|
|
90
90
|
case "sync":
|
|
91
|
-
await import("./chunks/index-
|
|
91
|
+
await import("./chunks/index-BHEP1kYv.js");
|
|
92
92
|
break;
|
|
93
93
|
case "sync-skills":
|
|
94
|
-
await import("./chunks/index-
|
|
94
|
+
await import("./chunks/index-Ukr3nSKB.js");
|
|
95
95
|
break;
|
|
96
96
|
case "orient":
|
|
97
97
|
await import("./chunks/index-BgKyH0dB.js");
|
package/package.json
CHANGED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { readdirSync as D, statSync as S, mkdirSync as F, writeFileSync as k, readFileSync as O } from "node:fs";
|
|
2
|
-
import { join as d, sep as P } from "node:path";
|
|
3
|
-
import { fileURLToPath as U } from "node:url";
|
|
4
|
-
import { l as $ } from "./services-IgZptnTi.js";
|
|
5
|
-
import { n as x, b as T, r as A, S as b, p as z, f as I } from "./shared-9J_oXE74.js";
|
|
6
|
-
class Y {
|
|
7
|
-
/** @purpose List of all synced file entries. */
|
|
8
|
-
entries;
|
|
9
|
-
/**
|
|
10
|
-
* @purpose Construct a SyncResult from a list of entries.
|
|
11
|
-
* @param entries File entries.
|
|
12
|
-
*/
|
|
13
|
-
constructor(e) {
|
|
14
|
-
this.entries = e;
|
|
15
|
-
}
|
|
16
|
-
/** @purpose Files with status "added". | @returns Array of added entries. */
|
|
17
|
-
get added() {
|
|
18
|
-
return this.entries.filter((e) => e.status === "added");
|
|
19
|
-
}
|
|
20
|
-
/** @purpose Files with status "updated". | @returns Array of updated entries. */
|
|
21
|
-
get updated() {
|
|
22
|
-
return this.entries.filter((e) => e.status === "updated");
|
|
23
|
-
}
|
|
24
|
-
/** @purpose Files with status "unchanged". | @returns Array of unchanged entries. */
|
|
25
|
-
get unchanged() {
|
|
26
|
-
return this.entries.filter((e) => e.status === "unchanged");
|
|
27
|
-
}
|
|
28
|
-
/** @purpose Human-readable summary of added/updated/unchanged counts. | @returns Summary string. */
|
|
29
|
-
get summary() {
|
|
30
|
-
const e = this.added.length, s = this.updated.length, o = this.unchanged.length;
|
|
31
|
-
return `Synced: ${e} added, ${s} updated, ${o} skipped (unchanged)`;
|
|
32
|
-
}
|
|
33
|
-
/** @purpose Dry-run summary message. | @returns Dry-run message. */
|
|
34
|
-
get dryRunSummary() {
|
|
35
|
-
return "Dry-run: no files written.";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const v = "ERR_SYNC_SUBDIR_NOT_FOUND", B = "ERR_SYNC_SOURCE_NOT_FOUND", R = /* @__PURE__ */ new Set([
|
|
39
|
-
"architecture",
|
|
40
|
-
"dbc-audit.directive.xml",
|
|
41
|
-
"dev-review.directive.xml",
|
|
42
|
-
"semantic-change-extractor.directive.xml"
|
|
43
|
-
]);
|
|
44
|
-
function j(t, e = "ai/directives") {
|
|
45
|
-
return A(t, e);
|
|
46
|
-
}
|
|
47
|
-
function L(t, e) {
|
|
48
|
-
if (e && e.length > 0) {
|
|
49
|
-
const o = D(t).filter(
|
|
50
|
-
(r) => !R.has(r) && S(d(t, r)).isDirectory()
|
|
51
|
-
);
|
|
52
|
-
for (const r of e)
|
|
53
|
-
if (!o.includes(r)) {
|
|
54
|
-
const c = `[scanDirectives] ai/directives/${r}/ not found in package.
|
|
55
|
-
Available: ${o.join(", ")}`, i = new Error(c);
|
|
56
|
-
throw i.code = v, i;
|
|
57
|
-
}
|
|
58
|
-
const n = [];
|
|
59
|
-
for (const r of e)
|
|
60
|
-
p(d(t, r), r, n);
|
|
61
|
-
return n.sort();
|
|
62
|
-
}
|
|
63
|
-
const s = [];
|
|
64
|
-
return p(t, "", s), s.sort();
|
|
65
|
-
}
|
|
66
|
-
function p(t, e, s) {
|
|
67
|
-
let o;
|
|
68
|
-
try {
|
|
69
|
-
o = D(t);
|
|
70
|
-
} catch {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
for (const n of o) {
|
|
74
|
-
if (n.startsWith(".") || R.has(n)) continue;
|
|
75
|
-
const r = d(t, n), c = e ? d(e, n) : n, i = S(r, { throwIfNoEntry: !1 });
|
|
76
|
-
i && (i.isDirectory() ? p(r, c, s) : i.isFile() && s.push(c.split(P).join("/")));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function W(t, e) {
|
|
80
|
-
try {
|
|
81
|
-
t.stat(e.sourceDir);
|
|
82
|
-
} catch {
|
|
83
|
-
const n = `[collectAndCompare] Source directory not found: ${e.sourceDir}`, r = new Error(n);
|
|
84
|
-
throw r.code = B, r;
|
|
85
|
-
}
|
|
86
|
-
const s = L(e.sourceDir, e.subdirs), o = [];
|
|
87
|
-
for (const n of s) {
|
|
88
|
-
const r = d(e.sourceDir, n), c = d(e.targetDir, n), i = t.readFile(r), y = x(i.toString("utf-8"), b), l = Buffer.from(y, "utf-8"), f = l.length;
|
|
89
|
-
let u = null;
|
|
90
|
-
try {
|
|
91
|
-
u = t.readFile(c);
|
|
92
|
-
} catch {
|
|
93
|
-
}
|
|
94
|
-
let a;
|
|
95
|
-
u === null ? a = "added" : T(l, u) ? a = "updated" : a = "unchanged", o.push({
|
|
96
|
-
relativePath: n,
|
|
97
|
-
status: a,
|
|
98
|
-
sourceSize: f,
|
|
99
|
-
targetSize: u?.length
|
|
100
|
-
}), !e.dryRun && a !== "unchanged" && (t.mkdir(d(e.targetDir, n, ".."), { recursive: !0 }), t.writeFile(c, l));
|
|
101
|
-
}
|
|
102
|
-
return new Y(o);
|
|
103
|
-
}
|
|
104
|
-
function H(t, e, s, o) {
|
|
105
|
-
s.write(`Sync${e ? " (dry-run)" : ""}: ${o}
|
|
106
|
-
`);
|
|
107
|
-
const n = I(t, { dryRun: e });
|
|
108
|
-
for (const r of n)
|
|
109
|
-
s.write(r + `
|
|
110
|
-
`);
|
|
111
|
-
}
|
|
112
|
-
function w(t, e) {
|
|
113
|
-
const s = O, o = k, n = F, r = S, c = D, i = j, y = process.stdout, l = process.stderr, f = z(t, {
|
|
114
|
-
dryRun: ["dry-run"]
|
|
115
|
-
}), u = f.dryRun === !0 || f.dryRun === "true", a = f._.filter(
|
|
116
|
-
(g) => typeof g == "string" && g !== "sync"
|
|
117
|
-
), m = process.cwd(), _ = i(m, "ai/directives");
|
|
118
|
-
if (!_)
|
|
119
|
-
return l.write(`Error: gennady package not found. Install it locally: npm i -D gennady
|
|
120
|
-
`), 1;
|
|
121
|
-
const E = d(m, "ai", "directives"), N = {
|
|
122
|
-
sourceDir: _,
|
|
123
|
-
targetDir: E,
|
|
124
|
-
subdirs: a.length > 0 ? a : void 0,
|
|
125
|
-
dryRun: u
|
|
126
|
-
};
|
|
127
|
-
try {
|
|
128
|
-
const h = W({
|
|
129
|
-
readFile: s,
|
|
130
|
-
writeFile: o,
|
|
131
|
-
mkdir: n,
|
|
132
|
-
stat: (C) => r(C),
|
|
133
|
-
readdir: c,
|
|
134
|
-
cwd: m
|
|
135
|
-
}, N);
|
|
136
|
-
return H(h.entries, u, y, m), 0;
|
|
137
|
-
} catch (g) {
|
|
138
|
-
const h = g;
|
|
139
|
-
if (h.code === v)
|
|
140
|
-
return l.write(`Error: ${h.message}
|
|
141
|
-
`), 1;
|
|
142
|
-
throw h;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (process.argv[1]) {
|
|
146
|
-
const t = U(import.meta.url);
|
|
147
|
-
if (t === process.argv[1] || t.endsWith(process.argv[1]))
|
|
148
|
-
try {
|
|
149
|
-
const e = w(process.argv);
|
|
150
|
-
process.exit(e);
|
|
151
|
-
} catch (e) {
|
|
152
|
-
const s = new Error("[syncCmd] Self-execution failed", { cause: e });
|
|
153
|
-
$.error("[syncCmd#run] [self-executing → failed]", { error: s }), process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
process.exit(w(process.argv));
|