@lousy-agents/cli 5.9.5 → 5.10.0
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/api/copilot-with-fastify/.devcontainer/devcontainer.json +1 -1
- package/api/copilot-with-fastify/biome.template.json +1 -1
- package/api/copilot-with-fastify/package-lock.json +49 -49
- package/api/copilot-with-fastify/package.json +4 -4
- package/cli/copilot-with-citty/.devcontainer/devcontainer.json +1 -1
- package/cli/copilot-with-citty/biome.template.json +1 -1
- package/cli/copilot-with-citty/package.json +3 -3
- package/dist/index.js +401 -208
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/ui/copilot-with-react/.devcontainer/devcontainer.json +1 -1
- package/ui/copilot-with-react/biome.template.json +1 -1
- package/ui/copilot-with-react/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -10229,11 +10229,14 @@ var promises_ = __webpack_require__(1455);
|
|
|
10229
10229
|
|
|
10230
10230
|
// EXTERNAL MODULE: external "node:path"
|
|
10231
10231
|
var external_node_path_ = __webpack_require__(6760);
|
|
10232
|
+
// EXTERNAL MODULE: external "node:fs"
|
|
10233
|
+
var external_node_fs_ = __webpack_require__(3024);
|
|
10232
10234
|
;// CONCATENATED MODULE: ../core/src/gateways/file-system-utils.ts
|
|
10233
10235
|
/**
|
|
10234
10236
|
* Shared file system utilities for gateways.
|
|
10235
10237
|
*/
|
|
10236
10238
|
|
|
10239
|
+
|
|
10237
10240
|
/**
|
|
10238
10241
|
* Checks if a file or directory exists.
|
|
10239
10242
|
*/ async function file_system_utils_fileExists(path) {
|
|
@@ -10303,6 +10306,44 @@ function isPathWithinRoot(rootPath, candidatePath) {
|
|
|
10303
10306
|
throw new Error(`${context} exceeds size limit (${fileStats.size} bytes > ${maxBytes} bytes)`);
|
|
10304
10307
|
}
|
|
10305
10308
|
}
|
|
10309
|
+
/**
|
|
10310
|
+
* Reads a file atomically with symlink and size protection.
|
|
10311
|
+
*
|
|
10312
|
+
* Uses `O_NOFOLLOW` (where available) to atomically reject symlinks at
|
|
10313
|
+
* the kernel level, eliminating the TOCTOU window between `lstat()` and
|
|
10314
|
+
* `readFile()`. Falls back to `lstat()` on platforms without `O_NOFOLLOW`.
|
|
10315
|
+
* Validates file size via `fstat()` on the opened file descriptor so the
|
|
10316
|
+
* size check and the read operate on the same inode.
|
|
10317
|
+
*/ async function readFileNoFollow(filePath, maxBytes) {
|
|
10318
|
+
const hasNoFollow = typeof external_node_fs_.constants.O_NOFOLLOW === "number" && external_node_fs_.constants.O_NOFOLLOW !== 0;
|
|
10319
|
+
const safePath = JSON.stringify(filePath);
|
|
10320
|
+
let fh;
|
|
10321
|
+
if (hasNoFollow) {
|
|
10322
|
+
try {
|
|
10323
|
+
fh = await (0,promises_.open)(filePath, external_node_fs_.constants.O_RDONLY | external_node_fs_.constants.O_NOFOLLOW);
|
|
10324
|
+
} catch (error) {
|
|
10325
|
+
if (error instanceof Error && "code" in error && error.code === "ELOOP") {
|
|
10326
|
+
throw new Error(`Symlinks are not allowed: ${safePath}`);
|
|
10327
|
+
}
|
|
10328
|
+
throw error;
|
|
10329
|
+
}
|
|
10330
|
+
} else {
|
|
10331
|
+
const stats = await (0,promises_.lstat)(filePath);
|
|
10332
|
+
if (stats.isSymbolicLink()) {
|
|
10333
|
+
throw new Error(`Symlinks are not allowed: ${safePath}`);
|
|
10334
|
+
}
|
|
10335
|
+
fh = await (0,promises_.open)(filePath, external_node_fs_.constants.O_RDONLY);
|
|
10336
|
+
}
|
|
10337
|
+
try {
|
|
10338
|
+
const fdStats = await fh.stat();
|
|
10339
|
+
if (fdStats.size > maxBytes) {
|
|
10340
|
+
throw new Error(`File ${safePath} exceeds size limit (${fdStats.size} bytes > ${maxBytes} bytes)`);
|
|
10341
|
+
}
|
|
10342
|
+
return await fh.readFile("utf-8");
|
|
10343
|
+
} finally{
|
|
10344
|
+
await fh.close();
|
|
10345
|
+
}
|
|
10346
|
+
}
|
|
10306
10347
|
|
|
10307
10348
|
;// CONCATENATED MODULE: ../core/src/gateways/agent-file-gateway.ts
|
|
10308
10349
|
/**
|
|
@@ -10414,8 +10455,6 @@ function isPathWithinRoot(rootPath, candidatePath) {
|
|
|
10414
10455
|
"pip"
|
|
10415
10456
|
];
|
|
10416
10457
|
|
|
10417
|
-
// EXTERNAL MODULE: external "node:fs"
|
|
10418
|
-
var external_node_fs_ = __webpack_require__(3024);
|
|
10419
10458
|
;// CONCATENATED MODULE: ../../node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
10420
10459
|
let _lazyMatch = () => { var __lib__=(()=>{var m=Object.defineProperty,V=Object.getOwnPropertyDescriptor,G=Object.getOwnPropertyNames,T=Object.prototype.hasOwnProperty,q=(r,e)=>{for(var n in e)m(r,n,{get:e[n],enumerable:true});},H=(r,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of G(e))!T.call(r,t)&&t!==n&&m(r,t,{get:()=>e[t],enumerable:!(a=V(e,t))||a.enumerable});return r},J=r=>H(m({},"__esModule",{value:true}),r),w={};q(w,{default:()=>re});var A=r=>Array.isArray(r),d=r=>typeof r=="function",Q=r=>r.length===0,W=r=>typeof r=="number",K=r=>typeof r=="object"&&r!==null,X=r=>r instanceof RegExp,b=r=>typeof r=="string",h=r=>r===void 0,Y=r=>{const e=new Map;return n=>{const a=e.get(n);if(a)return a;const t=r(n);return e.set(n,t),t}},rr=(r,e,n={})=>{const a={cache:{},input:r,index:0,indexMax:0,options:n,output:[]};if(v(e)(a)&&a.index===r.length)return a.output;throw new Error(`Failed to parse at index ${a.indexMax}`)},i=(r,e)=>A(r)?er(r,e):b(r)?ar(r,e):nr(r,e),er=(r,e)=>{const n={};for(const a of r){if(a.length!==1)throw new Error(`Invalid character: "${a}"`);const t=a.charCodeAt(0);n[t]=true;}return a=>{const t=a.index,o=a.input;for(;a.index<o.length&&o.charCodeAt(a.index)in n;)a.index+=1;const u=a.index;if(u>t){if(!h(e)&&!a.options.silent){const s=a.input.slice(t,u),c=d(e)?e(s,o,String(t)):e;h(c)||a.output.push(c);}a.indexMax=Math.max(a.indexMax,a.index);}return true}},nr=(r,e)=>{const n=r.source,a=r.flags.replace(/y|$/,"y"),t=new RegExp(n,a);return g(o=>{t.lastIndex=o.index;const u=t.exec(o.input);if(u){if(!h(e)&&!o.options.silent){const s=d(e)?e(...u,o.input,String(o.index)):e;h(s)||o.output.push(s);}return o.index+=u[0].length,o.indexMax=Math.max(o.indexMax,o.index),true}else return false})},ar=(r,e)=>n=>{if(n.input.startsWith(r,n.index)){if(!h(e)&&!n.options.silent){const t=d(e)?e(r,n.input,String(n.index)):e;h(t)||n.output.push(t);}return n.index+=r.length,n.indexMax=Math.max(n.indexMax,n.index),true}else return false},C=(r,e,n,a)=>{const t=v(r);return g(_(M(o=>{let u=0;for(;u<n;){const s=o.index;if(!t(o)||(u+=1,o.index===s))break}return u>=e})))},tr=(r,e)=>C(r,0,1),f=(r,e)=>C(r,0,1/0),x=(r,e)=>{const n=r.map(v);return g(_(M(a=>{for(let t=0,o=n.length;t<o;t++)if(!n[t](a))return false;return true})))},l=(r,e)=>{const n=r.map(v);return g(_(a=>{for(let t=0,o=n.length;t<o;t++)if(n[t](a))return true;return false}))},M=(r,e=false)=>{const n=v(r);return a=>{const t=a.index,o=a.output.length,u=n(a);return (!u||e)&&(a.index=t,a.output.length!==o&&(a.output.length=o)),u}},_=(r,e)=>{const n=v(r);return n},g=(()=>{let r=0;return e=>{const n=v(e),a=r+=1;return t=>{var o;if(t.options.memoization===false)return n(t);const u=t.index,s=(o=t.cache)[a]||(o[a]=new Map),c=s.get(u);if(c===false)return false;if(W(c))return t.index=c,true;if(c)return t.index=c.index,c.output?.length&&t.output.push(...c.output),true;{const Z=t.output.length;if(n(t)){const D=t.index,U=t.output.length;if(U>Z){const ee=t.output.slice(Z,U);s.set(u,{index:D,output:ee});}else s.set(u,D);return true}else return s.set(u,false),false}}}})(),E=r=>{let e;return n=>(e||(e=v(r())),e(n))},v=Y(r=>{if(d(r))return Q(r)?E(r):r;if(b(r)||X(r))return i(r);if(A(r))return x(r);if(K(r))return l(Object.values(r));throw new Error("Invalid rule")}),P="abcdefghijklmnopqrstuvwxyz",ir=r=>{let e="";for(;r>0;){const n=(r-1)%26;e=P[n]+e,r=Math.floor((r-1)/26);}return e},O=r=>{let e=0;for(let n=0,a=r.length;n<a;n++)e=e*26+P.indexOf(r[n])+1;return e},S=(r,e)=>{if(e<r)return S(e,r);const n=[];for(;r<=e;)n.push(r++);return n},or=(r,e,n)=>S(r,e).map(a=>String(a).padStart(n,"0")),R=(r,e)=>S(O(r),O(e)).map(ir),p=r=>r,z=r=>ur(e=>rr(e,r,{memoization:false}).join("")),ur=r=>{const e={};return n=>e[n]??(e[n]=r(n))},sr=i(/^\*\*\/\*$/,".*"),cr=i(/^\*\*\/(\*)?([ a-zA-Z0-9._-]+)$/,(r,e,n)=>`.*${e?"":"(?:^|/)"}${n.replaceAll(".","\\.")}`),lr=i(/^\*\*\/(\*)?([ a-zA-Z0-9._-]*)\{([ a-zA-Z0-9._-]+(?:,[ a-zA-Z0-9._-]+)*)\}$/,(r,e,n,a)=>`.*${e?"":"(?:^|/)"}${n.replaceAll(".","\\.")}(?:${a.replaceAll(",","|").replaceAll(".","\\.")})`),y=i(/\\./,p),pr=i(/[$.*+?^(){}[\]\|]/,r=>`\\${r}`),vr=i(/./,p),hr=i(/^(?:!!)*!(.*)$/,(r,e)=>`(?!^${L(e)}$).*?`),dr=i(/^(!!)+/,""),fr=l([hr,dr]),xr=i(/\/(\*\*\/)+/,"(?:/.+/|/)"),gr=i(/^(\*\*\/)+/,"(?:^|.*/)"),mr=i(/\/(\*\*)$/,"(?:/.*|$)"),_r=i(/\*\*/,".*"),j=l([xr,gr,mr,_r]),Sr=i(/\*\/(?!\*\*\/)/,"[^/]*/"),yr=i(/\*/,"[^/]*"),N=l([Sr,yr]),k=i("?","[^/]"),$r=i("[",p),wr=i("]",p),Ar=i(/[!^]/,"^/"),br=i(/[a-z]-[a-z]|[0-9]-[0-9]/i,p),Cr=i(/[$.*+?^(){}[\|]/,r=>`\\${r}`),Mr=i(/[^\]]/,p),Er=l([y,Cr,br,Mr]),B=x([$r,tr(Ar),f(Er),wr]),Pr=i("{","(?:"),Or=i("}",")"),Rr=i(/(\d+)\.\.(\d+)/,(r,e,n)=>or(+e,+n,Math.min(e.length,n.length)).join("|")),zr=i(/([a-z]+)\.\.([a-z]+)/,(r,e,n)=>R(e,n).join("|")),jr=i(/([A-Z]+)\.\.([A-Z]+)/,(r,e,n)=>R(e.toLowerCase(),n.toLowerCase()).join("|").toUpperCase()),Nr=l([Rr,zr,jr]),I=x([Pr,Nr,Or]),kr=i("{","(?:"),Br=i("}",")"),Ir=i(",","|"),Fr=i(/[$.*+?^(){[\]\|]/,r=>`\\${r}`),Lr=i(/[^}]/,p),Zr=E(()=>F),Dr=l([j,N,k,B,I,Zr,y,Fr,Ir,Lr]),F=x([kr,f(Dr),Br]),Ur=f(l([sr,cr,lr,fr,j,N,k,B,I,F,y,pr,vr])),Vr=Ur,Gr=z(Vr),L=Gr,Tr=i(/\\./,p),qr=i(/./,p),Hr=i(/\*\*\*+/,"*"),Jr=i(/([^/{[(!])\*\*/,(r,e)=>`${e}*`),Qr=i(/(^|.)\*\*(?=[^*/)\]}])/,(r,e)=>`${e}*`),Wr=f(l([Tr,Hr,Jr,Qr,qr])),Kr=Wr,Xr=z(Kr),Yr=Xr,$=(r,e)=>{const n=Array.isArray(r)?r:[r];if(!n.length)return false;const a=n.map($.compile),t=n.every(s=>/(\/(?:\*\*)?|\[\/\])$/.test(s)),o=e.replace(/[\\\/]+/g,"/").replace(/\/$/,t?"/":"");return a.some(s=>s.test(o))};$.compile=r=>new RegExp(`^${L(Yr(r))}$`,"s");var re=$;return J(w)})();
|
|
10421
10460
|
return __lib__.default || __lib__; };
|
|
@@ -33436,6 +33475,7 @@ var dist = __webpack_require__(3519);
|
|
|
33436
33475
|
|
|
33437
33476
|
|
|
33438
33477
|
|
|
33478
|
+
/** Maximum skill file size: 1 MB */ const MAX_SKILL_FILE_BYTES = 1_048_576;
|
|
33439
33479
|
/**
|
|
33440
33480
|
* Skill directory locations to search for SKILL.md files.
|
|
33441
33481
|
*/ const SKILL_DIRECTORIES = [
|
|
@@ -33455,12 +33495,29 @@ var dist = __webpack_require__(3519);
|
|
|
33455
33495
|
return skills;
|
|
33456
33496
|
}
|
|
33457
33497
|
async discoverSkillsInDir(skillsDir) {
|
|
33458
|
-
|
|
33498
|
+
let dirStats;
|
|
33499
|
+
try {
|
|
33500
|
+
dirStats = await (0,promises_.lstat)(skillsDir);
|
|
33501
|
+
} catch (error) {
|
|
33502
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
33503
|
+
return [];
|
|
33504
|
+
}
|
|
33505
|
+
throw error;
|
|
33506
|
+
}
|
|
33507
|
+
if (dirStats.isSymbolicLink() || !dirStats.isDirectory()) {
|
|
33459
33508
|
return [];
|
|
33460
33509
|
}
|
|
33461
|
-
|
|
33462
|
-
|
|
33463
|
-
|
|
33510
|
+
let entries;
|
|
33511
|
+
try {
|
|
33512
|
+
entries = await (0,promises_.readdir)(skillsDir, {
|
|
33513
|
+
withFileTypes: true
|
|
33514
|
+
});
|
|
33515
|
+
} catch (error) {
|
|
33516
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
33517
|
+
return [];
|
|
33518
|
+
}
|
|
33519
|
+
throw error;
|
|
33520
|
+
}
|
|
33464
33521
|
const skills = [];
|
|
33465
33522
|
for (const entry of entries){
|
|
33466
33523
|
if (!entry.isDirectory()) {
|
|
@@ -33470,7 +33527,17 @@ var dist = __webpack_require__(3519);
|
|
|
33470
33527
|
continue;
|
|
33471
33528
|
}
|
|
33472
33529
|
const skillFilePath = (0,external_node_path_.join)(skillsDir, entry.name, "SKILL.md");
|
|
33473
|
-
|
|
33530
|
+
let skillStat;
|
|
33531
|
+
try {
|
|
33532
|
+
skillStat = await (0,promises_.lstat)(skillFilePath);
|
|
33533
|
+
} catch (error) {
|
|
33534
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
33535
|
+
skillStat = null;
|
|
33536
|
+
} else {
|
|
33537
|
+
throw error;
|
|
33538
|
+
}
|
|
33539
|
+
}
|
|
33540
|
+
if (skillStat && !skillStat.isSymbolicLink() && skillStat.isFile()) {
|
|
33474
33541
|
skills.push({
|
|
33475
33542
|
filePath: skillFilePath,
|
|
33476
33543
|
skillName: entry.name
|
|
@@ -33480,7 +33547,7 @@ var dist = __webpack_require__(3519);
|
|
|
33480
33547
|
return skills;
|
|
33481
33548
|
}
|
|
33482
33549
|
async readSkillFileContent(filePath) {
|
|
33483
|
-
return (
|
|
33550
|
+
return readFileNoFollow(filePath, MAX_SKILL_FILE_BYTES);
|
|
33484
33551
|
}
|
|
33485
33552
|
parseFrontmatter(content) {
|
|
33486
33553
|
const lines = content.split("\n");
|
|
@@ -36577,12 +36644,22 @@ const initCommand = defineCommand({
|
|
|
36577
36644
|
|
|
36578
36645
|
|
|
36579
36646
|
|
|
36647
|
+
/** Maximum agent file size: 1 MB */ const MAX_AGENT_FILE_BYTES = 1_048_576;
|
|
36580
36648
|
/**
|
|
36581
36649
|
* File system implementation of the agent lint gateway.
|
|
36582
36650
|
*/ class FileSystemAgentLintGateway {
|
|
36583
36651
|
async discoverAgents(targetDir) {
|
|
36584
36652
|
const agentsDir = (0,external_node_path_.join)(targetDir, ".github", "agents");
|
|
36585
|
-
|
|
36653
|
+
let agentsDirStats;
|
|
36654
|
+
try {
|
|
36655
|
+
agentsDirStats = await (0,promises_.lstat)(agentsDir);
|
|
36656
|
+
} catch (error) {
|
|
36657
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
36658
|
+
return [];
|
|
36659
|
+
}
|
|
36660
|
+
throw error;
|
|
36661
|
+
}
|
|
36662
|
+
if (agentsDirStats.isSymbolicLink() || !agentsDirStats.isDirectory()) {
|
|
36586
36663
|
return [];
|
|
36587
36664
|
}
|
|
36588
36665
|
const resolvedAgentsDir = (0,external_node_path_.resolve)(agentsDir);
|
|
@@ -36626,11 +36703,7 @@ const initCommand = defineCommand({
|
|
|
36626
36703
|
return agents;
|
|
36627
36704
|
}
|
|
36628
36705
|
async readAgentFileContent(filePath) {
|
|
36629
|
-
|
|
36630
|
-
if (stats.isSymbolicLink()) {
|
|
36631
|
-
throw new Error(`Symlinks are not allowed: ${filePath}`);
|
|
36632
|
-
}
|
|
36633
|
-
return (0,promises_.readFile)(filePath, "utf-8");
|
|
36706
|
+
return readFileNoFollow(filePath, MAX_AGENT_FILE_BYTES);
|
|
36634
36707
|
}
|
|
36635
36708
|
parseFrontmatter(content) {
|
|
36636
36709
|
const lines = content.split("\n");
|
|
@@ -36681,7 +36754,6 @@ const initCommand = defineCommand({
|
|
|
36681
36754
|
* Discovers hook config files for GitHub Copilot and Claude Code.
|
|
36682
36755
|
*/
|
|
36683
36756
|
|
|
36684
|
-
|
|
36685
36757
|
/** Maximum hook config file size: 1 MB */ const MAX_CONFIG_FILE_BYTES = 1_048_576;
|
|
36686
36758
|
/** Matches the Copilot hook key `"preToolUse":` to detect hook section presence */ const COPILOT_HOOK_PATTERN = /"preToolUse"\s*:/;
|
|
36687
36759
|
/** Matches the Claude hook key `"PreToolUse":` to detect hook section presence */ const CLAUDE_HOOK_PATTERN = /"PreToolUse"\s*:/;
|
|
@@ -36710,22 +36782,30 @@ const initCommand = defineCommand({
|
|
|
36710
36782
|
let safePath;
|
|
36711
36783
|
try {
|
|
36712
36784
|
safePath = await file_system_utils_resolveSafePath(targetDir, config.relativePath);
|
|
36713
|
-
} catch
|
|
36714
|
-
|
|
36715
|
-
|
|
36716
|
-
|
|
36717
|
-
|
|
36718
|
-
|
|
36719
|
-
|
|
36720
|
-
|
|
36721
|
-
continue;
|
|
36785
|
+
} catch (error) {
|
|
36786
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
36787
|
+
continue;
|
|
36788
|
+
}
|
|
36789
|
+
if (error instanceof Error && (error.message.startsWith("Resolved path is outside target directory:") || error.message.startsWith("Path contains symbolic link:"))) {
|
|
36790
|
+
continue;
|
|
36791
|
+
}
|
|
36792
|
+
throw error;
|
|
36722
36793
|
}
|
|
36794
|
+
let content;
|
|
36723
36795
|
try {
|
|
36724
|
-
await
|
|
36725
|
-
} catch
|
|
36726
|
-
|
|
36796
|
+
content = await readFileNoFollow(safePath, MAX_CONFIG_FILE_BYTES);
|
|
36797
|
+
} catch (error) {
|
|
36798
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
36799
|
+
continue;
|
|
36800
|
+
}
|
|
36801
|
+
if (error instanceof Error && error.message.startsWith("Symlinks are not allowed")) {
|
|
36802
|
+
continue;
|
|
36803
|
+
}
|
|
36804
|
+
if (error instanceof Error && error.message.includes("exceeds size limit")) {
|
|
36805
|
+
continue;
|
|
36806
|
+
}
|
|
36807
|
+
throw error;
|
|
36727
36808
|
}
|
|
36728
|
-
const content = await (0,promises_.readFile)(safePath, "utf-8");
|
|
36729
36809
|
if (this.mayContainHookSection(content, config.platform)) {
|
|
36730
36810
|
discovered.push({
|
|
36731
36811
|
filePath: safePath,
|
|
@@ -36736,12 +36816,7 @@ const initCommand = defineCommand({
|
|
|
36736
36816
|
return discovered;
|
|
36737
36817
|
}
|
|
36738
36818
|
async readFileContent(filePath) {
|
|
36739
|
-
|
|
36740
|
-
if (stats.isSymbolicLink()) {
|
|
36741
|
-
throw new Error(`Symlinks are not allowed: ${filePath}`);
|
|
36742
|
-
}
|
|
36743
|
-
await assertFileSizeWithinLimit(filePath, MAX_CONFIG_FILE_BYTES, `Hook config ${filePath}`);
|
|
36744
|
-
return (0,promises_.readFile)(filePath, "utf-8");
|
|
36819
|
+
return readFileNoFollow(filePath, MAX_CONFIG_FILE_BYTES);
|
|
36745
36820
|
}
|
|
36746
36821
|
/**
|
|
36747
36822
|
* Lightweight heuristic to check if a file may contain a pre-tool-use hooks section.
|
|
@@ -55689,11 +55764,12 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
55689
55764
|
*/
|
|
55690
55765
|
|
|
55691
55766
|
|
|
55767
|
+
/** Maximum instruction file size: 1 MB */ const MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
|
|
55692
55768
|
/**
|
|
55693
55769
|
* Remark-based implementation of the Markdown AST gateway.
|
|
55694
55770
|
*/ class RemarkMarkdownAstGateway {
|
|
55695
55771
|
async parseFile(filePath) {
|
|
55696
|
-
const content = await (
|
|
55772
|
+
const content = await readFileNoFollow(filePath, MAX_INSTRUCTION_FILE_BYTES);
|
|
55697
55773
|
return this.parseContent(content);
|
|
55698
55774
|
}
|
|
55699
55775
|
parseContent(content) {
|
|
@@ -56839,13 +56915,109 @@ function hasFrontmatterDelimiters(content) {
|
|
|
56839
56915
|
return false;
|
|
56840
56916
|
}
|
|
56841
56917
|
|
|
56842
|
-
;// CONCATENATED MODULE:
|
|
56918
|
+
;// CONCATENATED MODULE: ../lint/src/validate-directory.ts
|
|
56843
56919
|
/**
|
|
56844
|
-
*
|
|
56845
|
-
*
|
|
56920
|
+
* Directory path validation for the public lint API.
|
|
56921
|
+
*
|
|
56922
|
+
* Validates user-supplied directory paths by rejecting unsafe inputs
|
|
56923
|
+
* (control characters, path traversal, null bytes) and canonicalizing
|
|
56924
|
+
* the path via `realpath()` for cross-platform compatibility.
|
|
56846
56925
|
*/
|
|
56847
56926
|
|
|
56927
|
+
/**
|
|
56928
|
+
* Thrown when user-supplied directory input fails validation.
|
|
56929
|
+
*
|
|
56930
|
+
* Consumers can catch this type to distinguish user-input errors
|
|
56931
|
+
* (bad path, missing directory) from system-level errors (EACCES, EMFILE).
|
|
56932
|
+
*/ class LintValidationError extends Error {
|
|
56933
|
+
constructor(message){
|
|
56934
|
+
super(message);
|
|
56935
|
+
this.name = "LintValidationError";
|
|
56936
|
+
}
|
|
56937
|
+
}
|
|
56938
|
+
function isControlCharacter(code) {
|
|
56939
|
+
if (code >= 0x00 && code <= 0x1f) return true;
|
|
56940
|
+
if (code === 0x7f) return true;
|
|
56941
|
+
if (code >= 0x80 && code <= 0x9f) return true;
|
|
56942
|
+
if (code === 0x2028 || code === 0x2029) return true;
|
|
56943
|
+
if (code >= 0x202a && code <= 0x202e) return true;
|
|
56944
|
+
if (code >= 0x2066 && code <= 0x2069) return true;
|
|
56945
|
+
return false;
|
|
56946
|
+
}
|
|
56947
|
+
function containsControlCharacters(value) {
|
|
56948
|
+
for(let i = 0; i < value.length; i++){
|
|
56949
|
+
if (isControlCharacter(value.charCodeAt(i))) {
|
|
56950
|
+
return true;
|
|
56951
|
+
}
|
|
56952
|
+
}
|
|
56953
|
+
return false;
|
|
56954
|
+
}
|
|
56955
|
+
function sanitizeForErrorMessage(value) {
|
|
56956
|
+
return JSON.stringify(value);
|
|
56957
|
+
}
|
|
56958
|
+
function hasPathTraversalSegment(directory) {
|
|
56959
|
+
return directory.split(/[\\/]/).includes("..");
|
|
56960
|
+
}
|
|
56961
|
+
/**
|
|
56962
|
+
* Validates a user-supplied directory path for safety and existence.
|
|
56963
|
+
*
|
|
56964
|
+
* Rejects paths that contain control characters (ASCII C0/C1, Unicode
|
|
56965
|
+
* bidirectional overrides, line/paragraph separators), path traversal
|
|
56966
|
+
* segments, or that do not resolve to an existing directory. Returns
|
|
56967
|
+
* the canonicalized absolute path (all symlinks resolved) so downstream
|
|
56968
|
+
* code always operates on real paths.
|
|
56969
|
+
*
|
|
56970
|
+
* @throws {LintValidationError} If the path is empty, contains control
|
|
56971
|
+
* characters, traversal segments, does not exist, or is not a directory.
|
|
56972
|
+
*/ async function validateDirectory(directory) {
|
|
56973
|
+
if (directory.length === 0) {
|
|
56974
|
+
throw new LintValidationError("directory must not be empty");
|
|
56975
|
+
}
|
|
56976
|
+
const safeDir = sanitizeForErrorMessage(directory);
|
|
56977
|
+
if (containsControlCharacters(directory)) {
|
|
56978
|
+
throw new LintValidationError(`Invalid directory path (contains control characters): ${safeDir}`);
|
|
56979
|
+
}
|
|
56980
|
+
if (hasPathTraversalSegment(directory)) {
|
|
56981
|
+
throw new LintValidationError(`Invalid directory path (path traversal detected): ${safeDir}`);
|
|
56982
|
+
}
|
|
56983
|
+
const resolved = (0,external_node_path_.resolve)(directory);
|
|
56984
|
+
let canonical;
|
|
56985
|
+
try {
|
|
56986
|
+
canonical = await (0,promises_.realpath)(resolved);
|
|
56987
|
+
} catch (error) {
|
|
56988
|
+
if (error instanceof Error && "code" in error) {
|
|
56989
|
+
if (error.code === "ENOENT") {
|
|
56990
|
+
throw new LintValidationError(`Directory does not exist: ${safeDir}`);
|
|
56991
|
+
}
|
|
56992
|
+
if (error.code === "ENOTDIR") {
|
|
56993
|
+
throw new LintValidationError(`Path is not a directory: ${safeDir}`);
|
|
56994
|
+
}
|
|
56995
|
+
}
|
|
56996
|
+
throw error;
|
|
56997
|
+
}
|
|
56998
|
+
let stats;
|
|
56999
|
+
try {
|
|
57000
|
+
stats = await (0,promises_.lstat)(canonical);
|
|
57001
|
+
} catch (error) {
|
|
57002
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
57003
|
+
throw new LintValidationError(`Directory does not exist: ${safeDir}`);
|
|
57004
|
+
}
|
|
57005
|
+
throw error;
|
|
57006
|
+
}
|
|
57007
|
+
if (!stats.isDirectory()) {
|
|
57008
|
+
throw new LintValidationError(`Path is not a directory: ${safeDir}`);
|
|
57009
|
+
}
|
|
57010
|
+
return canonical;
|
|
57011
|
+
}
|
|
56848
57012
|
|
|
57013
|
+
;// CONCATENATED MODULE: ../lint/src/lint.ts
|
|
57014
|
+
/**
|
|
57015
|
+
* Lint composition root.
|
|
57016
|
+
*
|
|
57017
|
+
* Wires internal gateways, use cases, and severity filtering into a
|
|
57018
|
+
* single `runLint` entry point. This is a Layer 4 (Infrastructure)
|
|
57019
|
+
* module — the only place that instantiates concrete implementations.
|
|
57020
|
+
*/
|
|
56849
57021
|
|
|
56850
57022
|
|
|
56851
57023
|
|
|
@@ -56859,51 +57031,17 @@ function hasFrontmatterDelimiters(content) {
|
|
|
56859
57031
|
|
|
56860
57032
|
|
|
56861
57033
|
|
|
56862
|
-
|
|
56863
|
-
|
|
56864
|
-
|
|
56865
|
-
|
|
56866
|
-
|
|
56867
|
-
|
|
56868
|
-
|
|
56869
|
-
|
|
56870
|
-
|
|
56871
|
-
|
|
56872
|
-
|
|
56873
|
-
return (0,external_node_path_.resolve)(parsed);
|
|
56874
|
-
}
|
|
56875
|
-
/**
|
|
56876
|
-
* Converts skill lint output to unified LintOutput.
|
|
56877
|
-
*/ function skillOutputToLintOutput(output) {
|
|
56878
|
-
const diagnostics = [];
|
|
56879
|
-
for (const result of output.results){
|
|
56880
|
-
for (const d of result.diagnostics){
|
|
56881
|
-
diagnostics.push({
|
|
56882
|
-
filePath: result.filePath,
|
|
56883
|
-
line: d.line,
|
|
56884
|
-
severity: d.severity,
|
|
56885
|
-
message: d.message,
|
|
56886
|
-
field: d.field,
|
|
56887
|
-
ruleId: d.ruleId,
|
|
56888
|
-
target: "skill"
|
|
56889
|
-
});
|
|
56890
|
-
}
|
|
56891
|
-
}
|
|
56892
|
-
return {
|
|
56893
|
-
diagnostics,
|
|
56894
|
-
target: "skill",
|
|
56895
|
-
filesAnalyzed: output.results.map((r)=>r.filePath),
|
|
56896
|
-
summary: {
|
|
56897
|
-
totalFiles: output.totalSkills,
|
|
56898
|
-
totalErrors: output.totalErrors,
|
|
56899
|
-
totalWarnings: output.totalWarnings,
|
|
56900
|
-
totalInfos: 0
|
|
56901
|
-
}
|
|
56902
|
-
};
|
|
56903
|
-
}
|
|
56904
|
-
/**
|
|
56905
|
-
* Converts agent lint output to unified LintOutput.
|
|
56906
|
-
*/ function agentOutputToLintOutput(output) {
|
|
57034
|
+
const LintTargetsSchema = schemas_object({
|
|
57035
|
+
skills: schemas_boolean().optional(),
|
|
57036
|
+
agents: schemas_boolean().optional(),
|
|
57037
|
+
hooks: schemas_boolean().optional(),
|
|
57038
|
+
instructions: schemas_boolean().optional()
|
|
57039
|
+
}).strict().optional();
|
|
57040
|
+
const LintOptionsSchema = schemas_object({
|
|
57041
|
+
directory: schemas_string().min(1, "directory must not be empty"),
|
|
57042
|
+
targets: LintTargetsSchema
|
|
57043
|
+
}).strict();
|
|
57044
|
+
/** Converts a use-case lint output to the unified LintOutput shape. */ function toLintOutput(output, target, totalFiles) {
|
|
56907
57045
|
const diagnostics = [];
|
|
56908
57046
|
for (const result of output.results){
|
|
56909
57047
|
for (const d of result.diagnostics){
|
|
@@ -56914,114 +57052,47 @@ function hasFrontmatterDelimiters(content) {
|
|
|
56914
57052
|
message: d.message,
|
|
56915
57053
|
field: d.field,
|
|
56916
57054
|
ruleId: d.ruleId,
|
|
56917
|
-
target
|
|
57055
|
+
target
|
|
56918
57056
|
});
|
|
56919
57057
|
}
|
|
56920
57058
|
}
|
|
56921
57059
|
return {
|
|
56922
57060
|
diagnostics,
|
|
56923
|
-
target
|
|
57061
|
+
target,
|
|
56924
57062
|
filesAnalyzed: output.results.map((r)=>r.filePath),
|
|
56925
57063
|
summary: {
|
|
56926
|
-
totalFiles
|
|
57064
|
+
totalFiles,
|
|
56927
57065
|
totalErrors: output.totalErrors,
|
|
56928
57066
|
totalWarnings: output.totalWarnings,
|
|
56929
|
-
totalInfos:
|
|
57067
|
+
totalInfos: diagnostics.filter((d)=>d.severity === "info").length
|
|
56930
57068
|
}
|
|
56931
57069
|
};
|
|
56932
57070
|
}
|
|
56933
|
-
|
|
56934
|
-
* Formats and displays a LintOutput using consola.
|
|
56935
|
-
*/ function displayLintOutput(output, label) {
|
|
56936
|
-
if (output.summary.totalFiles === 0) {
|
|
56937
|
-
consola.info(`No ${label} found`);
|
|
56938
|
-
return;
|
|
56939
|
-
}
|
|
56940
|
-
consola.info(`Discovered ${output.summary.totalFiles} ${label}`);
|
|
56941
|
-
// Group diagnostics by file
|
|
56942
|
-
const filesWithDiagnostics = new Set();
|
|
56943
|
-
for (const d of output.diagnostics){
|
|
56944
|
-
filesWithDiagnostics.add(d.filePath);
|
|
56945
|
-
}
|
|
56946
|
-
for (const file of output.filesAnalyzed){
|
|
56947
|
-
if (!filesWithDiagnostics.has(file)) {
|
|
56948
|
-
consola.success(`${file}: OK`);
|
|
56949
|
-
}
|
|
56950
|
-
}
|
|
56951
|
-
for (const d of output.diagnostics){
|
|
56952
|
-
const prefix = `${d.filePath}:${d.line}`;
|
|
56953
|
-
const fieldInfo = d.field ? ` [${d.field}]` : "";
|
|
56954
|
-
if (d.severity === "error") {
|
|
56955
|
-
consola.error(`${prefix}${fieldInfo}: ${d.message}`);
|
|
56956
|
-
} else if (d.severity === "warning") {
|
|
56957
|
-
consola.warn(`${prefix}${fieldInfo}: ${d.message}`);
|
|
56958
|
-
} else {
|
|
56959
|
-
consola.info(`${prefix}${fieldInfo}: ${d.message}`);
|
|
56960
|
-
}
|
|
56961
|
-
}
|
|
56962
|
-
}
|
|
56963
|
-
/**
|
|
56964
|
-
* Runs skill linting.
|
|
56965
|
-
*/ async function lintSkills(targetDir) {
|
|
57071
|
+
async function lintSkills(targetDir) {
|
|
56966
57072
|
const gateway = createSkillLintGateway();
|
|
56967
57073
|
const useCase = new LintSkillFrontmatterUseCase(gateway);
|
|
56968
57074
|
const output = await useCase.execute({
|
|
56969
57075
|
targetDir
|
|
56970
57076
|
});
|
|
56971
|
-
return
|
|
57077
|
+
return toLintOutput(output, "skill", output.totalSkills);
|
|
56972
57078
|
}
|
|
56973
|
-
|
|
56974
|
-
* Runs agent linting.
|
|
56975
|
-
*/ async function lintAgents(targetDir) {
|
|
57079
|
+
async function lintAgents(targetDir) {
|
|
56976
57080
|
const gateway = createAgentLintGateway();
|
|
56977
57081
|
const useCase = new LintAgentFrontmatterUseCase(gateway);
|
|
56978
57082
|
const output = await useCase.execute({
|
|
56979
57083
|
targetDir
|
|
56980
57084
|
});
|
|
56981
|
-
return
|
|
57085
|
+
return toLintOutput(output, "agent", output.totalAgents);
|
|
56982
57086
|
}
|
|
56983
|
-
|
|
56984
|
-
* Converts hook lint output to unified LintOutput.
|
|
56985
|
-
*/ function hookOutputToLintOutput(output) {
|
|
56986
|
-
const diagnostics = [];
|
|
56987
|
-
for (const result of output.results){
|
|
56988
|
-
for (const d of result.diagnostics){
|
|
56989
|
-
diagnostics.push({
|
|
56990
|
-
filePath: result.filePath,
|
|
56991
|
-
line: d.line,
|
|
56992
|
-
severity: d.severity,
|
|
56993
|
-
message: d.message,
|
|
56994
|
-
field: d.field,
|
|
56995
|
-
ruleId: d.ruleId,
|
|
56996
|
-
target: "hook"
|
|
56997
|
-
});
|
|
56998
|
-
}
|
|
56999
|
-
}
|
|
57000
|
-
return {
|
|
57001
|
-
diagnostics,
|
|
57002
|
-
target: "hook",
|
|
57003
|
-
filesAnalyzed: output.results.map((r)=>r.filePath),
|
|
57004
|
-
summary: {
|
|
57005
|
-
totalFiles: output.totalFiles,
|
|
57006
|
-
totalErrors: output.totalErrors,
|
|
57007
|
-
totalWarnings: output.totalWarnings,
|
|
57008
|
-
totalInfos: 0
|
|
57009
|
-
}
|
|
57010
|
-
};
|
|
57011
|
-
}
|
|
57012
|
-
/**
|
|
57013
|
-
* Runs hook configuration linting.
|
|
57014
|
-
*/ async function lintHooks(targetDir) {
|
|
57087
|
+
async function lintHooks(targetDir) {
|
|
57015
57088
|
const gateway = createHookConfigGateway();
|
|
57016
57089
|
const useCase = new LintHookConfigUseCase(gateway);
|
|
57017
57090
|
const output = await useCase.execute({
|
|
57018
57091
|
targetDir
|
|
57019
57092
|
});
|
|
57020
|
-
return
|
|
57093
|
+
return toLintOutput(output, "hook", output.totalFiles);
|
|
57021
57094
|
}
|
|
57022
|
-
|
|
57023
|
-
* Runs instruction quality analysis.
|
|
57024
|
-
*/ async function lintInstructions(targetDir) {
|
|
57095
|
+
async function lintInstructions(targetDir) {
|
|
57025
57096
|
const discoveryGateway = createInstructionFileDiscoveryGateway();
|
|
57026
57097
|
const astGateway = createMarkdownAstGateway();
|
|
57027
57098
|
const commandsGateway = createFeedbackLoopCommandsGateway();
|
|
@@ -57043,6 +57114,141 @@ function hasFrontmatterDelimiters(content) {
|
|
|
57043
57114
|
}
|
|
57044
57115
|
};
|
|
57045
57116
|
}
|
|
57117
|
+
const LINT_TARGETS = [
|
|
57118
|
+
{
|
|
57119
|
+
key: "skills",
|
|
57120
|
+
execute: lintSkills
|
|
57121
|
+
},
|
|
57122
|
+
{
|
|
57123
|
+
key: "agents",
|
|
57124
|
+
execute: lintAgents
|
|
57125
|
+
},
|
|
57126
|
+
{
|
|
57127
|
+
key: "hooks",
|
|
57128
|
+
execute: lintHooks
|
|
57129
|
+
},
|
|
57130
|
+
{
|
|
57131
|
+
key: "instructions",
|
|
57132
|
+
execute: lintInstructions
|
|
57133
|
+
}
|
|
57134
|
+
];
|
|
57135
|
+
function isTargetEnabled(key, targets) {
|
|
57136
|
+
if (!targets) return true;
|
|
57137
|
+
const hasAnyEnabled = Object.values(targets).some(Boolean);
|
|
57138
|
+
return !hasAnyEnabled || targets[key] === true;
|
|
57139
|
+
}
|
|
57140
|
+
/**
|
|
57141
|
+
* Run lint checks on a project directory.
|
|
57142
|
+
*
|
|
57143
|
+
* Orchestrates all lint targets (skills, agents, hooks, instructions),
|
|
57144
|
+
* applies lint rule configuration, and returns structured results.
|
|
57145
|
+
*
|
|
57146
|
+
* When no targets are specified (or all are false), all targets are run.
|
|
57147
|
+
*
|
|
57148
|
+
* @example
|
|
57149
|
+
* ```typescript
|
|
57150
|
+
* import { runLint } from '@lousy-agents/lint';
|
|
57151
|
+
*
|
|
57152
|
+
* const result = await runLint({ directory: '/path/to/project' });
|
|
57153
|
+
* console.log(result.hasErrors);
|
|
57154
|
+
* console.log(result.outputs);
|
|
57155
|
+
* ```
|
|
57156
|
+
*
|
|
57157
|
+
* @throws {LintValidationError} If directory validation, schema validation, or lint configuration validation fails.
|
|
57158
|
+
*/ async function runLint(options) {
|
|
57159
|
+
let parsed;
|
|
57160
|
+
try {
|
|
57161
|
+
parsed = LintOptionsSchema.parse(options);
|
|
57162
|
+
} catch (error) {
|
|
57163
|
+
if (error instanceof ZodError) {
|
|
57164
|
+
throw new LintValidationError(error.issues.map((e)=>e.message).join("; "));
|
|
57165
|
+
}
|
|
57166
|
+
throw error;
|
|
57167
|
+
}
|
|
57168
|
+
const targetDir = await validateDirectory(parsed.directory);
|
|
57169
|
+
let rulesConfig;
|
|
57170
|
+
try {
|
|
57171
|
+
rulesConfig = await loadLintConfig(targetDir);
|
|
57172
|
+
} catch (error) {
|
|
57173
|
+
if (error instanceof ZodError) {
|
|
57174
|
+
throw new LintValidationError(`Invalid lint configuration: ${error.issues.map((e)=>e.message).join("; ")}`);
|
|
57175
|
+
}
|
|
57176
|
+
throw error;
|
|
57177
|
+
}
|
|
57178
|
+
const enabledTargets = LINT_TARGETS.filter((t)=>isTargetEnabled(t.key, parsed.targets));
|
|
57179
|
+
const outputs = [];
|
|
57180
|
+
for (const target of enabledTargets){
|
|
57181
|
+
const rawOutput = await target.execute(targetDir);
|
|
57182
|
+
outputs.push(applySeverityFilter(rawOutput, rulesConfig));
|
|
57183
|
+
}
|
|
57184
|
+
const totalErrors = outputs.reduce((sum, output)=>sum + output.summary.totalErrors, 0);
|
|
57185
|
+
return {
|
|
57186
|
+
outputs,
|
|
57187
|
+
hasErrors: totalErrors > 0
|
|
57188
|
+
};
|
|
57189
|
+
}
|
|
57190
|
+
|
|
57191
|
+
;// CONCATENATED MODULE: ../lint/src/index.ts
|
|
57192
|
+
/**
|
|
57193
|
+
* @lousy-agents/lint — Public Lint API
|
|
57194
|
+
*
|
|
57195
|
+
* Provides a programmatic lint interface for validating agent skills,
|
|
57196
|
+
* custom agents, hook configurations, and instruction files.
|
|
57197
|
+
*
|
|
57198
|
+
* @example
|
|
57199
|
+
* ```typescript
|
|
57200
|
+
* import { runLint, createFormatter } from '@lousy-agents/lint';
|
|
57201
|
+
*
|
|
57202
|
+
* const result = await runLint({ directory: '/path/to/project' });
|
|
57203
|
+
* if (result.hasErrors) {
|
|
57204
|
+
* const formatter = createFormatter('json');
|
|
57205
|
+
* console.error(formatter.format(result.outputs));
|
|
57206
|
+
* }
|
|
57207
|
+
* ```
|
|
57208
|
+
*/ // ── Instruction quality result types (returned in LintOutput) ────────
|
|
57209
|
+
|
|
57210
|
+
// ── Output formatters ────────────────────────────────────────────────
|
|
57211
|
+
|
|
57212
|
+
|
|
57213
|
+
|
|
57214
|
+
|
|
57215
|
+
;// CONCATENATED MODULE: ./src/commands/lint.ts
|
|
57216
|
+
/**
|
|
57217
|
+
* CLI command for linting agent skills, custom agents, and instruction files.
|
|
57218
|
+
* Delegates to the lint package facade and handles CLI display concerns.
|
|
57219
|
+
*/
|
|
57220
|
+
|
|
57221
|
+
|
|
57222
|
+
/**
|
|
57223
|
+
* Formats and displays a LintOutput using consola.
|
|
57224
|
+
*/ function displayLintOutput(output, label) {
|
|
57225
|
+
if (output.summary.totalFiles === 0) {
|
|
57226
|
+
consola.info(`No ${label} found`);
|
|
57227
|
+
return;
|
|
57228
|
+
}
|
|
57229
|
+
consola.info(`Discovered ${output.summary.totalFiles} ${label}`);
|
|
57230
|
+
// Group diagnostics by file
|
|
57231
|
+
const filesWithDiagnostics = new Set();
|
|
57232
|
+
for (const d of output.diagnostics){
|
|
57233
|
+
filesWithDiagnostics.add(d.filePath);
|
|
57234
|
+
}
|
|
57235
|
+
for (const file of output.filesAnalyzed){
|
|
57236
|
+
if (!filesWithDiagnostics.has(file)) {
|
|
57237
|
+
consola.success(`${file}: OK`);
|
|
57238
|
+
}
|
|
57239
|
+
}
|
|
57240
|
+
for (const d of output.diagnostics){
|
|
57241
|
+
const prefix = `${d.filePath}:${d.line}`;
|
|
57242
|
+
const fieldInfo = d.field ? ` [${d.field}]` : "";
|
|
57243
|
+
if (d.severity === "error") {
|
|
57244
|
+
consola.error(`${prefix}${fieldInfo}: ${d.message}`);
|
|
57245
|
+
} else if (d.severity === "warning") {
|
|
57246
|
+
consola.warn(`${prefix}${fieldInfo}: ${d.message}`);
|
|
57247
|
+
} else {
|
|
57248
|
+
consola.info(`${prefix}${fieldInfo}: ${d.message}`);
|
|
57249
|
+
}
|
|
57250
|
+
}
|
|
57251
|
+
}
|
|
57046
57252
|
/**
|
|
57047
57253
|
* Displays instruction quality analysis results using consola.
|
|
57048
57254
|
*/ function displayInstructionQuality(output) {
|
|
@@ -57099,57 +57305,43 @@ function hasFrontmatterDelimiters(content) {
|
|
|
57099
57305
|
},
|
|
57100
57306
|
run: async (context)=>{
|
|
57101
57307
|
const rawTargetDir = typeof context.data?.targetDir === "string" ? context.data.targetDir : process.cwd();
|
|
57102
|
-
const targetDir = validateTargetDir(rawTargetDir);
|
|
57103
|
-
let rulesConfig;
|
|
57104
|
-
try {
|
|
57105
|
-
rulesConfig = await loadLintConfig(targetDir);
|
|
57106
|
-
} catch (error) {
|
|
57107
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
57108
|
-
consola.error(`Failed to load lint configuration: ${message}`);
|
|
57109
|
-
process.exitCode = 1;
|
|
57110
|
-
return;
|
|
57111
|
-
}
|
|
57112
57308
|
const lintSkillsFlag = context.args?.skills === true || context.data?.skills === true;
|
|
57113
57309
|
const lintAgentsFlag = context.args?.agents === true || context.data?.agents === true;
|
|
57114
57310
|
const lintHooksFlag = context.args?.hooks === true || context.data?.hooks === true;
|
|
57115
57311
|
const lintInstructionsFlag = context.args?.instructions === true || context.data?.instructions === true;
|
|
57116
|
-
const
|
|
57117
|
-
const
|
|
57118
|
-
const format = [
|
|
57312
|
+
const rawFormat = typeof context.args?.format === "string" ? context.args.format : typeof context.data?.format === "string" ? context.data.format : "human";
|
|
57313
|
+
const validFormats = new Set([
|
|
57119
57314
|
"human",
|
|
57120
57315
|
"json",
|
|
57121
57316
|
"rdjsonl"
|
|
57122
|
-
]
|
|
57123
|
-
|
|
57317
|
+
]);
|
|
57318
|
+
function isLintFormatType(value) {
|
|
57319
|
+
return validFormats.has(value);
|
|
57320
|
+
}
|
|
57321
|
+
const format = isLintFormatType(rawFormat) ? rawFormat : "human";
|
|
57322
|
+
let result;
|
|
57323
|
+
try {
|
|
57324
|
+
result = await runLint({
|
|
57325
|
+
directory: rawTargetDir,
|
|
57326
|
+
targets: {
|
|
57327
|
+
skills: lintSkillsFlag,
|
|
57328
|
+
agents: lintAgentsFlag,
|
|
57329
|
+
hooks: lintHooksFlag,
|
|
57330
|
+
instructions: lintInstructionsFlag
|
|
57331
|
+
}
|
|
57332
|
+
});
|
|
57333
|
+
} catch (error) {
|
|
57334
|
+
if (error instanceof LintValidationError) {
|
|
57335
|
+
consola.error(`Lint failed: ${error.message}`);
|
|
57336
|
+
process.exitCode = 1;
|
|
57337
|
+
return;
|
|
57338
|
+
}
|
|
57339
|
+
throw error;
|
|
57340
|
+
}
|
|
57341
|
+
const { outputs, hasErrors } = result;
|
|
57124
57342
|
let totalWarnings = 0;
|
|
57125
|
-
const
|
|
57126
|
-
|
|
57127
|
-
const rawOutput = await lintSkills(targetDir);
|
|
57128
|
-
const skillOutput = applySeverityFilter(rawOutput, rulesConfig);
|
|
57129
|
-
allOutputs.push(skillOutput);
|
|
57130
|
-
totalErrors += skillOutput.summary.totalErrors;
|
|
57131
|
-
totalWarnings += skillOutput.summary.totalWarnings;
|
|
57132
|
-
}
|
|
57133
|
-
if (noFlagProvided || lintAgentsFlag) {
|
|
57134
|
-
const rawOutput = await lintAgents(targetDir);
|
|
57135
|
-
const agentOutput = applySeverityFilter(rawOutput, rulesConfig);
|
|
57136
|
-
allOutputs.push(agentOutput);
|
|
57137
|
-
totalErrors += agentOutput.summary.totalErrors;
|
|
57138
|
-
totalWarnings += agentOutput.summary.totalWarnings;
|
|
57139
|
-
}
|
|
57140
|
-
if (noFlagProvided || lintHooksFlag) {
|
|
57141
|
-
const rawOutput = await lintHooks(targetDir);
|
|
57142
|
-
const hookOutput = applySeverityFilter(rawOutput, rulesConfig);
|
|
57143
|
-
allOutputs.push(hookOutput);
|
|
57144
|
-
totalErrors += hookOutput.summary.totalErrors;
|
|
57145
|
-
totalWarnings += hookOutput.summary.totalWarnings;
|
|
57146
|
-
}
|
|
57147
|
-
if (noFlagProvided || lintInstructionsFlag) {
|
|
57148
|
-
const rawOutput = await lintInstructions(targetDir);
|
|
57149
|
-
const instructionOutput = applySeverityFilter(rawOutput, rulesConfig);
|
|
57150
|
-
allOutputs.push(instructionOutput);
|
|
57151
|
-
totalErrors += instructionOutput.summary.totalErrors;
|
|
57152
|
-
totalWarnings += instructionOutput.summary.totalWarnings;
|
|
57343
|
+
for (const output of outputs){
|
|
57344
|
+
totalWarnings += output.summary.totalWarnings;
|
|
57153
57345
|
}
|
|
57154
57346
|
const targetLabels = {
|
|
57155
57347
|
skill: "skill(s)",
|
|
@@ -57159,12 +57351,12 @@ function hasFrontmatterDelimiters(content) {
|
|
|
57159
57351
|
};
|
|
57160
57352
|
if (format !== "human") {
|
|
57161
57353
|
const formatter = createFormatter(format);
|
|
57162
|
-
const formatted = formatter.format(
|
|
57354
|
+
const formatted = formatter.format(outputs);
|
|
57163
57355
|
if (formatted) {
|
|
57164
57356
|
process.stdout.write(`${formatted}\n`);
|
|
57165
57357
|
}
|
|
57166
57358
|
} else {
|
|
57167
|
-
for (const output of
|
|
57359
|
+
for (const output of outputs){
|
|
57168
57360
|
const label = targetLabels[output.target] ?? output.target;
|
|
57169
57361
|
if (output.target === "instruction") {
|
|
57170
57362
|
displayInstructionQuality(output);
|
|
@@ -57173,9 +57365,10 @@ function hasFrontmatterDelimiters(content) {
|
|
|
57173
57365
|
}
|
|
57174
57366
|
}
|
|
57175
57367
|
}
|
|
57176
|
-
if (
|
|
57368
|
+
if (hasErrors) {
|
|
57177
57369
|
process.exitCode = 1;
|
|
57178
57370
|
if (format === "human") {
|
|
57371
|
+
const totalErrors = outputs.reduce((sum, o)=>sum + o.summary.totalErrors, 0);
|
|
57179
57372
|
consola.error(`lint failed: ${totalErrors} error(s), ${totalWarnings} warning(s)`);
|
|
57180
57373
|
}
|
|
57181
57374
|
return;
|
|
@@ -57184,7 +57377,7 @@ function hasFrontmatterDelimiters(content) {
|
|
|
57184
57377
|
if (totalWarnings > 0) {
|
|
57185
57378
|
consola.warn(`Lint passed with ${totalWarnings} warning(s)`);
|
|
57186
57379
|
} else {
|
|
57187
|
-
const targets =
|
|
57380
|
+
const targets = outputs.map((o)=>targetLabels[o.target] ?? o.target).join(", ");
|
|
57188
57381
|
consola.success(`All ${targets} passed lint checks`);
|
|
57189
57382
|
}
|
|
57190
57383
|
}
|