@lousy-agents/cli 5.12.2 → 5.13.1
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.
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
"kysely": "0.28.17",
|
|
15
15
|
"pino": "10.3.1",
|
|
16
16
|
"postgres": "3.4.9",
|
|
17
|
-
"zod": "4.4.
|
|
17
|
+
"zod": "4.4.3"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@biomejs/biome": "2.4.14",
|
|
21
|
-
"@lousy-agents/mcp": "5.
|
|
21
|
+
"@lousy-agents/mcp": "5.13.0",
|
|
22
22
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
23
23
|
"@testcontainers/postgresql": "11.14.0",
|
|
24
24
|
"@types/node": "24.12.2",
|
|
@@ -956,9 +956,9 @@
|
|
|
956
956
|
}
|
|
957
957
|
},
|
|
958
958
|
"node_modules/@lousy-agents/mcp": {
|
|
959
|
-
"version": "5.
|
|
960
|
-
"resolved": "https://registry.npmjs.org/@lousy-agents/mcp/-/mcp-5.
|
|
961
|
-
"integrity": "sha512-
|
|
959
|
+
"version": "5.13.0",
|
|
960
|
+
"resolved": "https://registry.npmjs.org/@lousy-agents/mcp/-/mcp-5.13.0.tgz",
|
|
961
|
+
"integrity": "sha512-MXQIYQGn6L1tDPY/Di2n/bab/JtyAdVJ4Qy1ozhQC3RM2Kd/riKyYZky0RqS7PD3qFQMU6iMAH45f787otNG0w==",
|
|
962
962
|
"dev": true,
|
|
963
963
|
"license": "BSD-2-Clause-Patent",
|
|
964
964
|
"dependencies": {
|
|
@@ -6009,9 +6009,9 @@
|
|
|
6009
6009
|
}
|
|
6010
6010
|
},
|
|
6011
6011
|
"node_modules/zod": {
|
|
6012
|
-
"version": "4.4.
|
|
6013
|
-
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.
|
|
6014
|
-
"integrity": "sha512-
|
|
6012
|
+
"version": "4.4.3",
|
|
6013
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
|
6014
|
+
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
|
6015
6015
|
"license": "MIT",
|
|
6016
6016
|
"funding": {
|
|
6017
6017
|
"url": "https://github.com/sponsors/colinhacks"
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
"kysely": "0.28.17",
|
|
23
23
|
"pino": "10.3.1",
|
|
24
24
|
"postgres": "3.4.9",
|
|
25
|
-
"zod": "4.4.
|
|
25
|
+
"zod": "4.4.3"
|
|
26
26
|
},
|
|
27
27
|
"overrides": {
|
|
28
|
-
"zod": "4.4.
|
|
28
|
+
"zod": "4.4.3"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@biomejs/biome": "2.4.14",
|
|
32
|
-
"@lousy-agents/mcp": "5.
|
|
32
|
+
"@lousy-agents/mcp": "5.13.0",
|
|
33
33
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
34
34
|
"@testcontainers/postgresql": "11.14.0",
|
|
35
35
|
"@types/node": "24.12.2",
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"citty": "0.2.2",
|
|
18
18
|
"consola": "3.4.2",
|
|
19
|
-
"zod": "4.4.
|
|
19
|
+
"zod": "4.4.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@biomejs/biome": "2.4.14",
|
|
23
|
-
"@lousy-agents/mcp": "5.
|
|
23
|
+
"@lousy-agents/mcp": "5.13.0",
|
|
24
24
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
25
25
|
"@types/node": "24.12.2",
|
|
26
26
|
"@upstash/context7-mcp": "2.2.3",
|
package/dist/index.js
CHANGED
|
@@ -567,7 +567,7 @@ module.exports = webpackEmptyAsyncContext;
|
|
|
567
567
|
|
|
568
568
|
|
|
569
569
|
},
|
|
570
|
-
|
|
570
|
+
2091(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
571
571
|
// NAMESPACE OBJECT: ../../node_modules/micromark/lib/constructs.js
|
|
572
572
|
var constructs_namespaceObject = {};
|
|
573
573
|
__webpack_require__.r(constructs_namespaceObject);
|
|
@@ -5685,7 +5685,7 @@ const safeDecodeAsync = /* @__PURE__*/ _safeDecodeAsync($ZodRealError);
|
|
|
5685
5685
|
const versions_version = {
|
|
5686
5686
|
major: 4,
|
|
5687
5687
|
minor: 4,
|
|
5688
|
-
patch:
|
|
5688
|
+
patch: 3,
|
|
5689
5689
|
};
|
|
5690
5690
|
|
|
5691
5691
|
;// CONCATENATED MODULE: ../../node_modules/zod/v4/core/schemas.js
|
|
@@ -7408,6 +7408,7 @@ const $ZodFile = /*@__PURE__*/ (/* unused pure expression or super */ null && (c
|
|
|
7408
7408
|
})));
|
|
7409
7409
|
const $ZodTransform = /*@__PURE__*/ $constructor("$ZodTransform", (inst, def) => {
|
|
7410
7410
|
$ZodType.init(inst, def);
|
|
7411
|
+
inst._zod.optin = "optional";
|
|
7411
7412
|
inst._zod.parse = (payload, ctx) => {
|
|
7412
7413
|
if (ctx.direction === "backward") {
|
|
7413
7414
|
throw new $ZodEncodeError(inst.constructor.name);
|
|
@@ -7417,6 +7418,7 @@ const $ZodTransform = /*@__PURE__*/ $constructor("$ZodTransform", (inst, def) =>
|
|
|
7417
7418
|
const output = _out instanceof Promise ? _out : Promise.resolve(_out);
|
|
7418
7419
|
return output.then((output) => {
|
|
7419
7420
|
payload.value = output;
|
|
7421
|
+
payload.fallback = true;
|
|
7420
7422
|
return payload;
|
|
7421
7423
|
});
|
|
7422
7424
|
}
|
|
@@ -7424,11 +7426,12 @@ const $ZodTransform = /*@__PURE__*/ $constructor("$ZodTransform", (inst, def) =>
|
|
|
7424
7426
|
throw new $ZodAsyncError();
|
|
7425
7427
|
}
|
|
7426
7428
|
payload.value = _out;
|
|
7429
|
+
payload.fallback = true;
|
|
7427
7430
|
return payload;
|
|
7428
7431
|
};
|
|
7429
7432
|
});
|
|
7430
7433
|
function handleOptionalResult(result, input) {
|
|
7431
|
-
if (result.issues.length
|
|
7434
|
+
if (input === undefined && (result.issues.length || result.fallback)) {
|
|
7432
7435
|
return { issues: [], value: undefined };
|
|
7433
7436
|
}
|
|
7434
7437
|
return result;
|
|
@@ -7446,10 +7449,11 @@ const $ZodOptional = /*@__PURE__*/ $constructor("$ZodOptional", (inst, def) => {
|
|
|
7446
7449
|
});
|
|
7447
7450
|
inst._zod.parse = (payload, ctx) => {
|
|
7448
7451
|
if (def.innerType._zod.optin === "optional") {
|
|
7452
|
+
const input = payload.value;
|
|
7449
7453
|
const result = def.innerType._zod.run(payload, ctx);
|
|
7450
7454
|
if (result instanceof Promise)
|
|
7451
|
-
return result.then((r) => handleOptionalResult(r,
|
|
7452
|
-
return handleOptionalResult(result,
|
|
7455
|
+
return result.then((r) => handleOptionalResult(r, input));
|
|
7456
|
+
return handleOptionalResult(result, input);
|
|
7453
7457
|
}
|
|
7454
7458
|
if (payload.value === undefined) {
|
|
7455
7459
|
return payload;
|
|
@@ -7576,7 +7580,7 @@ const $ZodSuccess = /*@__PURE__*/ (/* unused pure expression or super */ null &&
|
|
|
7576
7580
|
})));
|
|
7577
7581
|
const $ZodCatch = /*@__PURE__*/ $constructor("$ZodCatch", (inst, def) => {
|
|
7578
7582
|
$ZodType.init(inst, def);
|
|
7579
|
-
|
|
7583
|
+
inst._zod.optin = "optional";
|
|
7580
7584
|
defineLazy(inst._zod, "optout", () => def.innerType._zod.optout);
|
|
7581
7585
|
defineLazy(inst._zod, "values", () => def.innerType._zod.values);
|
|
7582
7586
|
inst._zod.parse = (payload, ctx) => {
|
|
@@ -7597,6 +7601,7 @@ const $ZodCatch = /*@__PURE__*/ $constructor("$ZodCatch", (inst, def) => {
|
|
|
7597
7601
|
input: payload.value,
|
|
7598
7602
|
});
|
|
7599
7603
|
payload.issues = [];
|
|
7604
|
+
payload.fallback = true;
|
|
7600
7605
|
}
|
|
7601
7606
|
return payload;
|
|
7602
7607
|
});
|
|
@@ -7611,6 +7616,7 @@ const $ZodCatch = /*@__PURE__*/ $constructor("$ZodCatch", (inst, def) => {
|
|
|
7611
7616
|
input: payload.value,
|
|
7612
7617
|
});
|
|
7613
7618
|
payload.issues = [];
|
|
7619
|
+
payload.fallback = true;
|
|
7614
7620
|
}
|
|
7615
7621
|
return payload;
|
|
7616
7622
|
};
|
|
@@ -7657,7 +7663,7 @@ function handlePipeResult(left, next, ctx) {
|
|
|
7657
7663
|
left.aborted = true;
|
|
7658
7664
|
return left;
|
|
7659
7665
|
}
|
|
7660
|
-
return next._zod.run({ value: left.value, issues: left.issues }, ctx);
|
|
7666
|
+
return next._zod.run({ value: left.value, issues: left.issues, fallback: left.fallback }, ctx);
|
|
7661
7667
|
}
|
|
7662
7668
|
const $ZodCodec = /*@__PURE__*/ (/* unused pure expression or super */ null && (core.$constructor("$ZodCodec", (inst, def) => {
|
|
7663
7669
|
$ZodType.init(inst, def);
|
|
@@ -7715,8 +7721,6 @@ function handleCodecTxResult(left, value, nextSchema, ctx) {
|
|
|
7715
7721
|
}
|
|
7716
7722
|
const $ZodPreprocess = /*@__PURE__*/ (/* unused pure expression or super */ null && (core.$constructor("$ZodPreprocess", (inst, def) => {
|
|
7717
7723
|
$ZodPipe.init(inst, def);
|
|
7718
|
-
util.defineLazy(inst._zod, "optin", () => def.out._zod.optin);
|
|
7719
|
-
util.defineLazy(inst._zod, "optout", () => def.out._zod.optout);
|
|
7720
7724
|
})));
|
|
7721
7725
|
const $ZodReadonly = /*@__PURE__*/ $constructor("$ZodReadonly", (inst, def) => {
|
|
7722
7726
|
$ZodType.init(inst, def);
|
|
@@ -8914,7 +8918,7 @@ function _promise(Class, innerType) {
|
|
|
8914
8918
|
}
|
|
8915
8919
|
// @__NO_SIDE_EFFECTS__
|
|
8916
8920
|
function _custom(Class, fn, _params) {
|
|
8917
|
-
const norm =
|
|
8921
|
+
const norm = normalizeParams(_params);
|
|
8918
8922
|
norm.abort ?? (norm.abort = true); // default to abort:false
|
|
8919
8923
|
const schema = new Class({
|
|
8920
8924
|
type: "custom",
|
|
@@ -11303,10 +11307,12 @@ const ZodTransform = /*@__PURE__*/ $constructor("ZodTransform", (inst, def) => {
|
|
|
11303
11307
|
if (output instanceof Promise) {
|
|
11304
11308
|
return output.then((output) => {
|
|
11305
11309
|
payload.value = output;
|
|
11310
|
+
payload.fallback = true;
|
|
11306
11311
|
return payload;
|
|
11307
11312
|
});
|
|
11308
11313
|
}
|
|
11309
11314
|
payload.value = output;
|
|
11315
|
+
payload.fallback = true;
|
|
11310
11316
|
return payload;
|
|
11311
11317
|
};
|
|
11312
11318
|
});
|
|
@@ -11553,7 +11559,7 @@ function schemas_check(fn) {
|
|
|
11553
11559
|
return ch;
|
|
11554
11560
|
}
|
|
11555
11561
|
function custom(fn, _params) {
|
|
11556
|
-
return
|
|
11562
|
+
return _custom(ZodCustom, fn ?? (() => true), _params);
|
|
11557
11563
|
}
|
|
11558
11564
|
function refine(fn, _params = {}) {
|
|
11559
11565
|
return _refine(ZodCustom, fn, _params);
|
|
@@ -11817,6 +11823,26 @@ var compat_ZodFirstPartyTypeKind;
|
|
|
11817
11823
|
setupActionPatterns: DEFAULT_SETUP_ACTION_PATTERNS,
|
|
11818
11824
|
packageManagers: DEFAULT_PACKAGE_MANAGERS
|
|
11819
11825
|
};
|
|
11826
|
+
/**
|
|
11827
|
+
* Gets a version type to action name mapping from config.
|
|
11828
|
+
* Returns a partial record as not all types may be configured.
|
|
11829
|
+
*/ function getVersionTypeToActionMap(config) {
|
|
11830
|
+
const map = {};
|
|
11831
|
+
for (const action of config.setupActions){
|
|
11832
|
+
map[action.type] = action.action;
|
|
11833
|
+
}
|
|
11834
|
+
return map;
|
|
11835
|
+
}
|
|
11836
|
+
/**
|
|
11837
|
+
* Gets a version file type to config key mapping from config.
|
|
11838
|
+
* Returns a partial record as not all types may be configured.
|
|
11839
|
+
*/ function getVersionFileConfigKeyMap(config) {
|
|
11840
|
+
const map = {};
|
|
11841
|
+
for (const action of config.setupActions){
|
|
11842
|
+
map[action.type] = action.versionFileKey;
|
|
11843
|
+
}
|
|
11844
|
+
return map;
|
|
11845
|
+
}
|
|
11820
11846
|
|
|
11821
11847
|
;// CONCATENATED MODULE: ../core/src/lib/copilot-setup-config.ts
|
|
11822
11848
|
/**
|
|
@@ -11944,29 +11970,8 @@ const CopilotSetupConfigSchema = schemas_object({
|
|
|
11944
11970
|
*
|
|
11945
11971
|
* This function is retained for backwards compatibility but is a no-op
|
|
11946
11972
|
* because configuration is no longer cached at module scope.
|
|
11947
|
-
*/ function resetCopilotSetupConfigCache() {
|
|
11948
|
-
|
|
11949
|
-
}
|
|
11950
|
-
/**
|
|
11951
|
-
* Gets a version file type to action mapping from config.
|
|
11952
|
-
* Returns a partial record as not all types may be configured.
|
|
11953
|
-
*/ function getVersionTypeToActionMap(config) {
|
|
11954
|
-
const map = {};
|
|
11955
|
-
for (const action of config.setupActions){
|
|
11956
|
-
map[action.type] = action.action;
|
|
11957
|
-
}
|
|
11958
|
-
return map;
|
|
11959
|
-
}
|
|
11960
|
-
/**
|
|
11961
|
-
* Gets a version file type to config key mapping from config.
|
|
11962
|
-
* Returns a partial record as not all types may be configured.
|
|
11963
|
-
*/ function getVersionFileConfigKeyMap(config) {
|
|
11964
|
-
const map = {};
|
|
11965
|
-
for (const action of config.setupActions){
|
|
11966
|
-
map[action.type] = action.versionFileKey;
|
|
11967
|
-
}
|
|
11968
|
-
return map;
|
|
11969
|
-
}
|
|
11973
|
+
*/ function resetCopilotSetupConfigCache() {}
|
|
11974
|
+
|
|
11970
11975
|
/**
|
|
11971
11976
|
* Gets a filename to version type mapping from config.
|
|
11972
11977
|
*/ function getVersionFilenameToTypeMap(config) {
|
|
@@ -16512,15 +16517,12 @@ function defaultExec(command, args, options) {
|
|
|
16512
16517
|
* File system implementation of instruction analysis gateway
|
|
16513
16518
|
*/ class FileSystemInstructionAnalysisGateway {
|
|
16514
16519
|
async analyzeCoverage(targetDir, scripts, tools) {
|
|
16515
|
-
// Find all instruction files
|
|
16516
16520
|
const instructionFiles = await this.findInstructionFiles(targetDir);
|
|
16517
|
-
// Read and search instruction content
|
|
16518
16521
|
const references = [];
|
|
16519
16522
|
const documentedTargets = new Set();
|
|
16520
16523
|
for (const file of instructionFiles){
|
|
16521
16524
|
const content = await readFile(file, "utf-8");
|
|
16522
16525
|
const lines = content.split("\n");
|
|
16523
|
-
// Check for script references (e.g., "npm test", "npm run build")
|
|
16524
16526
|
for (const script of scripts){
|
|
16525
16527
|
const scriptRefs = this.findReferencesInContent(script.name, content, lines, file, targetDir);
|
|
16526
16528
|
if (scriptRefs.length > 0) {
|
|
@@ -16528,7 +16530,6 @@ function defaultExec(command, args, options) {
|
|
|
16528
16530
|
documentedTargets.add(script.name);
|
|
16529
16531
|
}
|
|
16530
16532
|
}
|
|
16531
|
-
// Check for tool references (e.g., "mise run test", "biome check")
|
|
16532
16533
|
for (const tool of tools){
|
|
16533
16534
|
const toolRefs = this.findReferencesInContent(tool.name, content, lines, file, targetDir);
|
|
16534
16535
|
if (toolRefs.length > 0) {
|
|
@@ -16537,14 +16538,12 @@ function defaultExec(command, args, options) {
|
|
|
16537
16538
|
}
|
|
16538
16539
|
}
|
|
16539
16540
|
}
|
|
16540
|
-
// Filter mandatory scripts/tools
|
|
16541
16541
|
const mandatoryScripts = scripts.filter((s)=>s.isMandatory);
|
|
16542
16542
|
const mandatoryTools = tools.filter((t)=>t.isMandatory);
|
|
16543
16543
|
const allMandatory = [
|
|
16544
16544
|
...mandatoryScripts,
|
|
16545
16545
|
...mandatoryTools
|
|
16546
16546
|
];
|
|
16547
|
-
// Categorize as missing or documented
|
|
16548
16547
|
const missingInInstructions = allMandatory.filter((item)=>!documentedTargets.has(item.name));
|
|
16549
16548
|
const documentedInInstructions = allMandatory.filter((item)=>documentedTargets.has(item.name));
|
|
16550
16549
|
const totalMandatory = allMandatory.length;
|
|
@@ -16563,12 +16562,10 @@ function defaultExec(command, args, options) {
|
|
|
16563
16562
|
}
|
|
16564
16563
|
async findInstructionFiles(targetDir) {
|
|
16565
16564
|
const files = [];
|
|
16566
|
-
// Check for .github/copilot-instructions.md
|
|
16567
16565
|
const copilotInstructions = join(targetDir, ".github", "copilot-instructions.md");
|
|
16568
16566
|
if (await fileExists(copilotInstructions)) {
|
|
16569
16567
|
files.push(copilotInstructions);
|
|
16570
16568
|
}
|
|
16571
|
-
// Check for .github/instructions/*.md
|
|
16572
16569
|
const instructionsDir = join(targetDir, ".github", "instructions");
|
|
16573
16570
|
if (await fileExists(instructionsDir)) {
|
|
16574
16571
|
try {
|
|
@@ -16578,31 +16575,20 @@ function defaultExec(command, args, options) {
|
|
|
16578
16575
|
files.push(join(instructionsDir, file));
|
|
16579
16576
|
}
|
|
16580
16577
|
}
|
|
16581
|
-
} catch {
|
|
16582
|
-
// Skip directory if we can't read it (e.g., permissions issues)
|
|
16583
|
-
// Similar to how workflow parsing errors are handled
|
|
16584
|
-
}
|
|
16578
|
+
} catch {}
|
|
16585
16579
|
}
|
|
16586
16580
|
return files;
|
|
16587
16581
|
}
|
|
16588
16582
|
findReferencesInContent(target, content, lines, file, targetDir) {
|
|
16589
16583
|
const references = [];
|
|
16590
|
-
// Build a case-insensitive, word-boundary-aware pattern for the target.
|
|
16591
|
-
// This reduces false positives from simple substring matches like
|
|
16592
|
-
// "test" in "testing" or "latest", while still matching common
|
|
16593
|
-
// separators such as spaces, punctuation, etc.
|
|
16594
16584
|
const escapedTarget = target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16595
16585
|
const targetPattern = new RegExp(`(?:^|[^\\w])(${escapedTarget})(?=$|[^\\w])`, "i");
|
|
16596
|
-
// Fast path: skip line-by-line processing if the target pattern
|
|
16597
|
-
// never appears in the full content.
|
|
16598
16586
|
if (!targetPattern.test(content)) {
|
|
16599
16587
|
return references;
|
|
16600
16588
|
}
|
|
16601
|
-
// Find line numbers where target appears
|
|
16602
16589
|
for(let i = 0; i < lines.length; i++){
|
|
16603
16590
|
const line = lines[i];
|
|
16604
16591
|
if (targetPattern.test(line)) {
|
|
16605
|
-
// Get context (line before and after if available)
|
|
16606
16592
|
const contextLines = [];
|
|
16607
16593
|
if (i > 0) contextLines.push(lines[i - 1]);
|
|
16608
16594
|
contextLines.push(line);
|
|
@@ -18314,47 +18300,78 @@ const MAX_NPMRC_BYTES = 64 * 1024;
|
|
|
18314
18300
|
|
|
18315
18301
|
|
|
18316
18302
|
|
|
18303
|
+
|
|
18304
|
+
// 1 MB — covers even the largest real-world manifests
|
|
18305
|
+
const MAX_PACKAGE_JSON_BYTES = 1024 * 1024;
|
|
18306
|
+
const PackageJsonSchema = schemas_object({
|
|
18307
|
+
scripts: record(schemas_string(), schemas_string()).optional()
|
|
18308
|
+
});
|
|
18317
18309
|
/**
|
|
18318
18310
|
* File system implementation of script discovery gateway
|
|
18319
18311
|
*/ class FileSystemScriptDiscoveryGateway {
|
|
18312
|
+
constructor(logger){
|
|
18313
|
+
this.logger = logger ?? consola;
|
|
18314
|
+
}
|
|
18315
|
+
logger;
|
|
18320
18316
|
async discoverScripts(targetDir) {
|
|
18321
18317
|
const packageJsonPath = (0,external_node_path_.join)(targetDir, "package.json");
|
|
18322
|
-
|
|
18318
|
+
let content;
|
|
18319
|
+
try {
|
|
18320
|
+
content = await readFileNoFollow(packageJsonPath, MAX_PACKAGE_JSON_BYTES);
|
|
18321
|
+
} catch (error) {
|
|
18322
|
+
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
|
18323
|
+
if (code !== "ENOENT") {
|
|
18324
|
+
this.logger.warn(`script-discovery: could not read ${JSON.stringify(packageJsonPath)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`);
|
|
18325
|
+
}
|
|
18323
18326
|
return [];
|
|
18324
18327
|
}
|
|
18328
|
+
let parsed;
|
|
18325
18329
|
try {
|
|
18326
|
-
|
|
18327
|
-
|
|
18328
|
-
if (
|
|
18330
|
+
parsed = JSON.parse(content);
|
|
18331
|
+
} catch (e) {
|
|
18332
|
+
if (e instanceof SyntaxError) {
|
|
18333
|
+
this.logger.warn(`script-discovery: ${JSON.stringify(packageJsonPath)} contains invalid JSON — ${JSON.stringify(e.message)}`);
|
|
18329
18334
|
return [];
|
|
18330
18335
|
}
|
|
18331
|
-
|
|
18332
|
-
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
name,
|
|
18337
|
-
command,
|
|
18338
|
-
phase,
|
|
18339
|
-
isMandatory
|
|
18340
|
-
});
|
|
18341
|
-
}
|
|
18342
|
-
return scripts;
|
|
18343
|
-
} catch {
|
|
18344
|
-
// If package.json is malformed or cannot be parsed, return empty array
|
|
18336
|
+
throw e;
|
|
18337
|
+
}
|
|
18338
|
+
const parseResult = PackageJsonSchema.safeParse(parsed);
|
|
18339
|
+
if (!parseResult.success) {
|
|
18340
|
+
this.logger.warn(`script-discovery: ${JSON.stringify(packageJsonPath)} has an unexpected structure — scripts field must be a record of strings`);
|
|
18345
18341
|
return [];
|
|
18346
18342
|
}
|
|
18343
|
+
if (!parseResult.data.scripts) {
|
|
18344
|
+
return [];
|
|
18345
|
+
}
|
|
18346
|
+
const scripts = [];
|
|
18347
|
+
for (const [name, command] of Object.entries(parseResult.data.scripts)){
|
|
18348
|
+
const phase = feedback_loop_determineScriptPhase(name, command);
|
|
18349
|
+
const isMandatory = feedback_loop_isScriptMandatory(phase);
|
|
18350
|
+
scripts.push({
|
|
18351
|
+
name,
|
|
18352
|
+
command,
|
|
18353
|
+
phase,
|
|
18354
|
+
isMandatory
|
|
18355
|
+
});
|
|
18356
|
+
}
|
|
18357
|
+
return scripts;
|
|
18347
18358
|
}
|
|
18348
18359
|
}
|
|
18349
18360
|
/**
|
|
18350
18361
|
* Creates and returns the default script discovery gateway
|
|
18351
|
-
*/ function createScriptDiscoveryGateway() {
|
|
18352
|
-
return new FileSystemScriptDiscoveryGateway();
|
|
18362
|
+
*/ function createScriptDiscoveryGateway(logger) {
|
|
18363
|
+
return new FileSystemScriptDiscoveryGateway(logger);
|
|
18353
18364
|
}
|
|
18354
18365
|
/**
|
|
18355
18366
|
* Creates a FeedbackLoopCommandsGateway that discovers mandatory commands from package.json scripts.
|
|
18356
|
-
|
|
18357
|
-
|
|
18367
|
+
*
|
|
18368
|
+
* @param scriptGateway - Optional gateway implementation. If omitted, a default
|
|
18369
|
+
* `FileSystemScriptDiscoveryGateway` is created using `logger`.
|
|
18370
|
+
* @param logger - Optional logger for the default gateway. Only used when
|
|
18371
|
+
* `scriptGateway` is omitted; if a custom `scriptGateway` is supplied, its
|
|
18372
|
+
* own logger configuration applies and this parameter is ignored.
|
|
18373
|
+
*/ function createFeedbackLoopCommandsGateway(scriptGateway, logger) {
|
|
18374
|
+
const gateway = scriptGateway ?? createScriptDiscoveryGateway(logger);
|
|
18358
18375
|
return {
|
|
18359
18376
|
async getMandatoryCommands (targetDir) {
|
|
18360
18377
|
const scripts = await gateway.discoverScripts(targetDir);
|
|
@@ -18404,6 +18421,56 @@ const MAX_NPMRC_BYTES = 64 * 1024;
|
|
|
18404
18421
|
|
|
18405
18422
|
// EXTERNAL MODULE: ../../node_modules/yaml/dist/index.js
|
|
18406
18423
|
var dist = __webpack_require__(3519);
|
|
18424
|
+
;// CONCATENATED MODULE: ../core/src/lib/frontmatter.ts
|
|
18425
|
+
/**
|
|
18426
|
+
* Shared YAML frontmatter parser for markdown files.
|
|
18427
|
+
* Extracts YAML data between leading `---` delimiters.
|
|
18428
|
+
*/
|
|
18429
|
+
/**
|
|
18430
|
+
* Parses YAML frontmatter from markdown content.
|
|
18431
|
+
*
|
|
18432
|
+
* Returns `null` when the content has no opening `---`, no closing `---`,
|
|
18433
|
+
* or contains invalid YAML.
|
|
18434
|
+
*/ function parseFrontmatter(content) {
|
|
18435
|
+
const lines = content.split("\n");
|
|
18436
|
+
if (lines[0]?.trim() !== "---") {
|
|
18437
|
+
return null;
|
|
18438
|
+
}
|
|
18439
|
+
let endIndex = -1;
|
|
18440
|
+
for(let i = 1; i < lines.length; i++){
|
|
18441
|
+
if (lines[i]?.trim() === "---") {
|
|
18442
|
+
endIndex = i;
|
|
18443
|
+
break;
|
|
18444
|
+
}
|
|
18445
|
+
}
|
|
18446
|
+
if (endIndex === -1) {
|
|
18447
|
+
return null;
|
|
18448
|
+
}
|
|
18449
|
+
const yamlContent = lines.slice(1, endIndex).join("\n");
|
|
18450
|
+
let data;
|
|
18451
|
+
try {
|
|
18452
|
+
const parsed = (0,dist/* .parse */.qg)(yamlContent, {
|
|
18453
|
+
maxAliasCount: 0
|
|
18454
|
+
});
|
|
18455
|
+
data = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
18456
|
+
} catch {
|
|
18457
|
+
return null;
|
|
18458
|
+
}
|
|
18459
|
+
const fieldLines = new Map();
|
|
18460
|
+
for(let i = 1; i < endIndex; i++){
|
|
18461
|
+
// Match YAML top-level field names: non-whitespace start, any chars except colon, then colon followed by whitespace or end-of-line
|
|
18462
|
+
const match = lines[i]?.match(/^([^\s:][^:]*?):(?:\s|$)/);
|
|
18463
|
+
if (match?.[1]) {
|
|
18464
|
+
fieldLines.set(match[1], i + 1);
|
|
18465
|
+
}
|
|
18466
|
+
}
|
|
18467
|
+
return {
|
|
18468
|
+
data: data ?? {},
|
|
18469
|
+
fieldLines,
|
|
18470
|
+
frontmatterStartLine: 1
|
|
18471
|
+
};
|
|
18472
|
+
}
|
|
18473
|
+
|
|
18407
18474
|
;// CONCATENATED MODULE: ../core/src/gateways/skill-lint-gateway.ts
|
|
18408
18475
|
/**
|
|
18409
18476
|
* Gateway for skill lint file system operations.
|
|
@@ -18417,7 +18484,8 @@ var dist = __webpack_require__(3519);
|
|
|
18417
18484
|
* Skill directory locations to search for SKILL.md files.
|
|
18418
18485
|
*/ const SKILL_DIRECTORIES = [
|
|
18419
18486
|
(0,external_node_path_.join)(".github", "skills"),
|
|
18420
|
-
(0,external_node_path_.join)(".claude", "skills")
|
|
18487
|
+
(0,external_node_path_.join)(".claude", "skills"),
|
|
18488
|
+
(0,external_node_path_.join)(".agents", "skills")
|
|
18421
18489
|
];
|
|
18422
18490
|
/**
|
|
18423
18491
|
* File system implementation of the skill lint gateway.
|
|
@@ -18487,41 +18555,7 @@ var dist = __webpack_require__(3519);
|
|
|
18487
18555
|
return readFileNoFollow(filePath, MAX_SKILL_FILE_BYTES);
|
|
18488
18556
|
}
|
|
18489
18557
|
parseFrontmatter(content) {
|
|
18490
|
-
|
|
18491
|
-
if (lines[0]?.trim() !== "---") {
|
|
18492
|
-
return null;
|
|
18493
|
-
}
|
|
18494
|
-
let endIndex = -1;
|
|
18495
|
-
for(let i = 1; i < lines.length; i++){
|
|
18496
|
-
if (lines[i]?.trim() === "---") {
|
|
18497
|
-
endIndex = i;
|
|
18498
|
-
break;
|
|
18499
|
-
}
|
|
18500
|
-
}
|
|
18501
|
-
if (endIndex === -1) {
|
|
18502
|
-
return null;
|
|
18503
|
-
}
|
|
18504
|
-
const yamlContent = lines.slice(1, endIndex).join("\n");
|
|
18505
|
-
let data;
|
|
18506
|
-
try {
|
|
18507
|
-
const parsed = (0,dist/* .parse */.qg)(yamlContent);
|
|
18508
|
-
data = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
18509
|
-
} catch {
|
|
18510
|
-
return null;
|
|
18511
|
-
}
|
|
18512
|
-
const fieldLines = new Map();
|
|
18513
|
-
for(let i = 1; i < endIndex; i++){
|
|
18514
|
-
// Match YAML top-level field names: non-whitespace start, any chars except colon, then colon+space
|
|
18515
|
-
const match = lines[i]?.match(/^([^\s:][^:]*?):\s/);
|
|
18516
|
-
if (match?.[1]) {
|
|
18517
|
-
fieldLines.set(match[1], i + 1);
|
|
18518
|
-
}
|
|
18519
|
-
}
|
|
18520
|
-
return {
|
|
18521
|
-
data: data ?? {},
|
|
18522
|
-
fieldLines,
|
|
18523
|
-
frontmatterStartLine: 1
|
|
18524
|
-
};
|
|
18558
|
+
return parseFrontmatter(content);
|
|
18525
18559
|
}
|
|
18526
18560
|
}
|
|
18527
18561
|
/**
|
|
@@ -18556,9 +18590,7 @@ var dist = __webpack_require__(3519);
|
|
|
18556
18590
|
const workflow = parseYaml(content);
|
|
18557
18591
|
const tools = this.extractToolsFromWorkflow(workflow, file);
|
|
18558
18592
|
allTools.push(...tools);
|
|
18559
|
-
} catch {
|
|
18560
|
-
// Skip files that can't be parsed as valid YAML
|
|
18561
|
-
}
|
|
18593
|
+
} catch {}
|
|
18562
18594
|
}
|
|
18563
18595
|
return this.deduplicateTools(allTools);
|
|
18564
18596
|
}
|
|
@@ -18586,7 +18618,6 @@ var dist = __webpack_require__(3519);
|
|
|
18586
18618
|
const stepObj = step;
|
|
18587
18619
|
const run = stepObj.run;
|
|
18588
18620
|
if (typeof run === "string") {
|
|
18589
|
-
// Extract tools from run commands
|
|
18590
18621
|
const extractedTools = this.extractToolsFromRunCommand(run, sourceFile);
|
|
18591
18622
|
tools.push(...extractedTools);
|
|
18592
18623
|
}
|
|
@@ -18596,35 +18627,24 @@ var dist = __webpack_require__(3519);
|
|
|
18596
18627
|
}
|
|
18597
18628
|
extractToolsFromRunCommand(runCommand, sourceFile) {
|
|
18598
18629
|
const tools = [];
|
|
18599
|
-
// Split by newlines and pipe tokens with surrounding whitespace to handle
|
|
18600
|
-
// multi-line and piped commands without breaking on constructs like "cmd || true"
|
|
18601
18630
|
const commands = runCommand.split(/\n|\s\|\s/).map((c)=>c.trim()).filter((c)=>c.length > 0 && !c.startsWith("#"));
|
|
18602
18631
|
for (const command of commands){
|
|
18603
|
-
// Extract the base command (first word)
|
|
18604
18632
|
const parts = command.split(/\s+/);
|
|
18605
18633
|
const baseCommand = parts[0];
|
|
18606
|
-
// Skip shell built-ins and common utilities
|
|
18607
18634
|
if (this.isShellBuiltin(baseCommand)) {
|
|
18608
18635
|
continue;
|
|
18609
18636
|
}
|
|
18610
|
-
// Determine tool name and full command
|
|
18611
18637
|
let toolName;
|
|
18612
18638
|
const fullCommand = command;
|
|
18613
|
-
// Handle special cases like "npm run", "mise run", etc.
|
|
18614
18639
|
if (parts.length >= 2 && (baseCommand === "npm" || baseCommand === "mise") && parts[1] === "run") {
|
|
18615
18640
|
if (parts.length >= 3 && parts[2]) {
|
|
18616
|
-
// "npm run test" -> name: "npm run test"
|
|
18617
18641
|
toolName = parts.slice(0, 3).join(" ");
|
|
18618
18642
|
} else {
|
|
18619
|
-
// Handle commands like "npm run" without a script name
|
|
18620
18643
|
toolName = parts.slice(0, 2).join(" ");
|
|
18621
18644
|
}
|
|
18622
18645
|
} else if (parts.length >= 2) {
|
|
18623
|
-
// For most tools, include the subcommand for better specificity
|
|
18624
|
-
// e.g., "npm test", "npx biome", "pnpm lint"
|
|
18625
18646
|
toolName = parts.slice(0, 2).join(" ");
|
|
18626
18647
|
} else {
|
|
18627
|
-
// Fallback to the base command if no subcommand is present
|
|
18628
18648
|
toolName = baseCommand;
|
|
18629
18649
|
}
|
|
18630
18650
|
const phase = this.determineToolPhase(toolName, fullCommand);
|
|
@@ -18663,13 +18683,11 @@ var dist = __webpack_require__(3519);
|
|
|
18663
18683
|
return builtins.includes(command);
|
|
18664
18684
|
}
|
|
18665
18685
|
determineToolPhase(toolName, fullCommand) {
|
|
18666
|
-
// Use the same logic as script phase determination
|
|
18667
18686
|
return determineScriptPhase(toolName, fullCommand);
|
|
18668
18687
|
}
|
|
18669
18688
|
deduplicateTools(tools) {
|
|
18670
18689
|
const seen = new Map();
|
|
18671
18690
|
for (const tool of tools){
|
|
18672
|
-
// Use full command as key for deduplication
|
|
18673
18691
|
const key = tool.fullCommand;
|
|
18674
18692
|
if (!seen.has(key)) {
|
|
18675
18693
|
seen.set(key, tool);
|
|
@@ -18926,9 +18944,7 @@ class FileSystemWorkflowGateway {
|
|
|
18926
18944
|
const workflow = (0,dist/* .parse */.qg)(content);
|
|
18927
18945
|
const candidates = extractSetupStepsFromWorkflow(workflow, config.setupActionPatterns);
|
|
18928
18946
|
allCandidates.push(...candidates);
|
|
18929
|
-
} catch {
|
|
18930
|
-
// Skip unreadable or malformed YAML workflow files while continuing scan.
|
|
18931
|
-
}
|
|
18947
|
+
} catch {}
|
|
18932
18948
|
}
|
|
18933
18949
|
return deduplicateCandidates(allCandidates);
|
|
18934
18950
|
}
|
|
@@ -18965,8 +18981,7 @@ function createWorkflowGateway(cwd) {
|
|
|
18965
18981
|
;// CONCATENATED MODULE: ../core/src/gateways/workflow-gateway.ts
|
|
18966
18982
|
/**
|
|
18967
18983
|
* Gateway interface for GitHub Actions workflow operations.
|
|
18968
|
-
*/
|
|
18969
|
-
|
|
18984
|
+
*/
|
|
18970
18985
|
|
|
18971
18986
|
;// CONCATENATED MODULE: ../core/src/gateways/index.ts
|
|
18972
18987
|
/**
|
|
@@ -19152,7 +19167,6 @@ function isCopilotCodeScanningRule(rule) {
|
|
|
19152
19167
|
* This module handles the logic of determining which GitHub Actions
|
|
19153
19168
|
* setup steps should be added based on detected version files and package managers.
|
|
19154
19169
|
*/
|
|
19155
|
-
|
|
19156
19170
|
/**
|
|
19157
19171
|
* Creates an ActionVersionPort backed by a static version map.
|
|
19158
19172
|
*/ function createActionVersionPort(versionMap) {
|
|
@@ -21659,40 +21673,7 @@ const initCommand = defineCommand({
|
|
|
21659
21673
|
return readFileNoFollow(filePath, MAX_AGENT_FILE_BYTES);
|
|
21660
21674
|
}
|
|
21661
21675
|
parseFrontmatter(content) {
|
|
21662
|
-
|
|
21663
|
-
if (lines[0]?.trim() !== "---") {
|
|
21664
|
-
return null;
|
|
21665
|
-
}
|
|
21666
|
-
let endIndex = -1;
|
|
21667
|
-
for(let i = 1; i < lines.length; i++){
|
|
21668
|
-
if (lines[i]?.trim() === "---") {
|
|
21669
|
-
endIndex = i;
|
|
21670
|
-
break;
|
|
21671
|
-
}
|
|
21672
|
-
}
|
|
21673
|
-
if (endIndex === -1) {
|
|
21674
|
-
return null;
|
|
21675
|
-
}
|
|
21676
|
-
const yamlContent = lines.slice(1, endIndex).join("\n");
|
|
21677
|
-
let data;
|
|
21678
|
-
try {
|
|
21679
|
-
const parsed = (0,dist/* .parse */.qg)(yamlContent);
|
|
21680
|
-
data = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
21681
|
-
} catch {
|
|
21682
|
-
return null;
|
|
21683
|
-
}
|
|
21684
|
-
const fieldLines = new Map();
|
|
21685
|
-
for(let i = 1; i < endIndex; i++){
|
|
21686
|
-
const match = lines[i]?.match(/^([^\s:][^:]*?):\s/);
|
|
21687
|
-
if (match?.[1]) {
|
|
21688
|
-
fieldLines.set(match[1], i + 1);
|
|
21689
|
-
}
|
|
21690
|
-
}
|
|
21691
|
-
return {
|
|
21692
|
-
data: data ?? {},
|
|
21693
|
-
fieldLines,
|
|
21694
|
-
frontmatterStartLine: 1
|
|
21695
|
-
};
|
|
21676
|
+
return parseFrontmatter(content);
|
|
21696
21677
|
}
|
|
21697
21678
|
}
|
|
21698
21679
|
/**
|
|
@@ -42047,16 +42028,14 @@ function hasFrontmatterDelimiters(content) {
|
|
|
42047
42028
|
return false;
|
|
42048
42029
|
}
|
|
42049
42030
|
|
|
42050
|
-
;// CONCATENATED MODULE: ../lint/src/
|
|
42031
|
+
;// CONCATENATED MODULE: ../lint/src/lint-errors.ts
|
|
42051
42032
|
/**
|
|
42052
|
-
*
|
|
42033
|
+
* Public lint API error types.
|
|
42053
42034
|
*
|
|
42054
|
-
*
|
|
42055
|
-
*
|
|
42056
|
-
*
|
|
42057
|
-
*/
|
|
42058
|
-
|
|
42059
|
-
/**
|
|
42035
|
+
* Kept separate from filesystem-backed validation internals so the public
|
|
42036
|
+
* package barrel can export stable error contracts without depending on
|
|
42037
|
+
* implementation modules.
|
|
42038
|
+
*/ /**
|
|
42060
42039
|
* Thrown when user-supplied directory input fails validation.
|
|
42061
42040
|
*
|
|
42062
42041
|
* Consumers can catch this type to distinguish user-input errors
|
|
@@ -42067,6 +42046,17 @@ function hasFrontmatterDelimiters(content) {
|
|
|
42067
42046
|
this.name = "LintValidationError";
|
|
42068
42047
|
}
|
|
42069
42048
|
}
|
|
42049
|
+
|
|
42050
|
+
;// CONCATENATED MODULE: ../lint/src/validate-directory.ts
|
|
42051
|
+
/**
|
|
42052
|
+
* Directory path validation for the public lint API.
|
|
42053
|
+
*
|
|
42054
|
+
* Validates user-supplied directory paths by rejecting unsafe inputs
|
|
42055
|
+
* (control characters, path traversal, null bytes) and canonicalizing
|
|
42056
|
+
* the path via `realpath()` for cross-platform compatibility.
|
|
42057
|
+
*/
|
|
42058
|
+
|
|
42059
|
+
|
|
42070
42060
|
function isControlCharacter(code) {
|
|
42071
42061
|
if (code >= 0x00 && code <= 0x1f) return true;
|
|
42072
42062
|
if (code === 0x7f) return true;
|
|
@@ -42087,8 +42077,17 @@ function containsControlCharacters(value) {
|
|
|
42087
42077
|
function sanitizeForErrorMessage(value) {
|
|
42088
42078
|
return JSON.stringify(value);
|
|
42089
42079
|
}
|
|
42090
|
-
|
|
42091
|
-
|
|
42080
|
+
/**
|
|
42081
|
+
* Detects path traversal segments in a user-supplied directory string.
|
|
42082
|
+
*
|
|
42083
|
+
* On Windows, both '/' and '\' are path separators, so the check splits on
|
|
42084
|
+
* both. On POSIX, '\' is a valid filename character (not a separator), so only
|
|
42085
|
+
* '/' is used as the split boundary to avoid false-rejecting legitimate paths
|
|
42086
|
+
* whose names contain backslashes. Any `platform` value other than `'win32'`
|
|
42087
|
+
* is treated as POSIX.
|
|
42088
|
+
*/ function hasPathTraversalSegment(directory, platform = process.platform) {
|
|
42089
|
+
const separator = platform === "win32" ? /[\\/]/ : "/";
|
|
42090
|
+
return directory.split(separator).includes("..");
|
|
42092
42091
|
}
|
|
42093
42092
|
/**
|
|
42094
42093
|
* Validates a user-supplied directory path for safety and existence.
|
|
@@ -42099,9 +42098,14 @@ function hasPathTraversalSegment(directory) {
|
|
|
42099
42098
|
* the canonicalized absolute path (all symlinks resolved) so downstream
|
|
42100
42099
|
* code always operates on real paths.
|
|
42101
42100
|
*
|
|
42101
|
+
* @param directory - The path to validate.
|
|
42102
|
+
* @param platform - The platform identifier used for path-separator detection.
|
|
42103
|
+
* Defaults to `process.platform`. Only `'win32'` uses `\` as a separator;
|
|
42104
|
+
* all other values are treated as POSIX. Overridable for cross-platform unit
|
|
42105
|
+
* tests so that both branches can be exercised on any host OS.
|
|
42102
42106
|
* @throws {LintValidationError} If the path is empty, contains control
|
|
42103
42107
|
* characters, traversal segments, does not exist, or is not a directory.
|
|
42104
|
-
*/ async function validateDirectory(directory) {
|
|
42108
|
+
*/ async function validateDirectory(directory, platform = process.platform) {
|
|
42105
42109
|
if (directory.length === 0) {
|
|
42106
42110
|
throw new LintValidationError("directory must not be empty");
|
|
42107
42111
|
}
|
|
@@ -42109,7 +42113,7 @@ function hasPathTraversalSegment(directory) {
|
|
|
42109
42113
|
if (containsControlCharacters(directory)) {
|
|
42110
42114
|
throw new LintValidationError(`Invalid directory path (contains control characters): ${safeDir}`);
|
|
42111
42115
|
}
|
|
42112
|
-
if (hasPathTraversalSegment(directory)) {
|
|
42116
|
+
if (hasPathTraversalSegment(directory, platform)) {
|
|
42113
42117
|
throw new LintValidationError(`Invalid directory path (path traversal detected): ${safeDir}`);
|
|
42114
42118
|
}
|
|
42115
42119
|
const resolved = (0,external_node_path_.resolve)(directory);
|
|
@@ -42163,6 +42167,7 @@ function hasPathTraversalSegment(directory) {
|
|
|
42163
42167
|
|
|
42164
42168
|
|
|
42165
42169
|
|
|
42170
|
+
|
|
42166
42171
|
const LintTargetsSchema = schemas_object({
|
|
42167
42172
|
skills: schemas_boolean().optional(),
|
|
42168
42173
|
agents: schemas_boolean().optional(),
|
|
@@ -42171,7 +42176,14 @@ const LintTargetsSchema = schemas_object({
|
|
|
42171
42176
|
}).strict().optional();
|
|
42172
42177
|
const LintOptionsSchema = schemas_object({
|
|
42173
42178
|
directory: schemas_string().min(1, "directory must not be empty"),
|
|
42174
|
-
targets: LintTargetsSchema
|
|
42179
|
+
targets: LintTargetsSchema,
|
|
42180
|
+
// logger is a runtime object — `z.custom` validates its structural contract
|
|
42181
|
+
// at the boundary; TypeScript enforces it further at compile time.
|
|
42182
|
+
// `v === undefined` is NOT needed here: ZodOptional intercepts undefined
|
|
42183
|
+
// before the predicate is invoked, so the guard would be dead code.
|
|
42184
|
+
logger: custom((v)=>v !== null && typeof v === "object" && typeof v.warn === "function", {
|
|
42185
|
+
message: "logger must be an object with a .warn method"
|
|
42186
|
+
}).optional()
|
|
42175
42187
|
}).strict();
|
|
42176
42188
|
/** Converts a use-case lint output to the unified LintOutput shape. */ function toLintOutput(output, target, totalFiles) {
|
|
42177
42189
|
const diagnostics = [];
|
|
@@ -42224,10 +42236,10 @@ async function lintHooks(targetDir) {
|
|
|
42224
42236
|
});
|
|
42225
42237
|
return toLintOutput(output, "hook", output.totalFiles);
|
|
42226
42238
|
}
|
|
42227
|
-
async function lintInstructions(targetDir) {
|
|
42239
|
+
async function lintInstructions(targetDir, logger) {
|
|
42228
42240
|
const discoveryGateway = createInstructionFileDiscoveryGateway();
|
|
42229
42241
|
const astGateway = createMarkdownAstGateway();
|
|
42230
|
-
const commandsGateway = createFeedbackLoopCommandsGateway();
|
|
42242
|
+
const commandsGateway = createFeedbackLoopCommandsGateway(undefined, logger);
|
|
42231
42243
|
const useCase = new AnalyzeInstructionQualityUseCase(discoveryGateway, astGateway, commandsGateway);
|
|
42232
42244
|
const output = await useCase.execute({
|
|
42233
42245
|
targetDir
|
|
@@ -42246,24 +42258,6 @@ async function lintInstructions(targetDir) {
|
|
|
42246
42258
|
}
|
|
42247
42259
|
};
|
|
42248
42260
|
}
|
|
42249
|
-
const LINT_TARGETS = [
|
|
42250
|
-
{
|
|
42251
|
-
key: "skills",
|
|
42252
|
-
execute: lintSkills
|
|
42253
|
-
},
|
|
42254
|
-
{
|
|
42255
|
-
key: "agents",
|
|
42256
|
-
execute: lintAgents
|
|
42257
|
-
},
|
|
42258
|
-
{
|
|
42259
|
-
key: "hooks",
|
|
42260
|
-
execute: lintHooks
|
|
42261
|
-
},
|
|
42262
|
-
{
|
|
42263
|
-
key: "instructions",
|
|
42264
|
-
execute: lintInstructions
|
|
42265
|
-
}
|
|
42266
|
-
];
|
|
42267
42261
|
function isTargetEnabled(key, targets) {
|
|
42268
42262
|
if (!targets) return true;
|
|
42269
42263
|
const hasAnyEnabled = Object.values(targets).some(Boolean);
|
|
@@ -42307,7 +42301,25 @@ function isTargetEnabled(key, targets) {
|
|
|
42307
42301
|
}
|
|
42308
42302
|
throw error;
|
|
42309
42303
|
}
|
|
42310
|
-
const
|
|
42304
|
+
const lintTargets = [
|
|
42305
|
+
{
|
|
42306
|
+
key: "skills",
|
|
42307
|
+
execute: lintSkills
|
|
42308
|
+
},
|
|
42309
|
+
{
|
|
42310
|
+
key: "agents",
|
|
42311
|
+
execute: lintAgents
|
|
42312
|
+
},
|
|
42313
|
+
{
|
|
42314
|
+
key: "hooks",
|
|
42315
|
+
execute: lintHooks
|
|
42316
|
+
},
|
|
42317
|
+
{
|
|
42318
|
+
key: "instructions",
|
|
42319
|
+
execute: (dir)=>lintInstructions(dir, parsed.logger)
|
|
42320
|
+
}
|
|
42321
|
+
];
|
|
42322
|
+
const enabledTargets = lintTargets.filter((t)=>isTargetEnabled(t.key, parsed.targets));
|
|
42311
42323
|
const outputs = [];
|
|
42312
42324
|
for (const target of enabledTargets){
|
|
42313
42325
|
const rawOutput = await target.execute(targetDir);
|
|
@@ -42337,9 +42349,7 @@ function isTargetEnabled(key, targets) {
|
|
|
42337
42349
|
* console.error(formatter.format(result.outputs));
|
|
42338
42350
|
* }
|
|
42339
42351
|
* ```
|
|
42340
|
-
*/
|
|
42341
|
-
|
|
42342
|
-
// ── Output formatters ────────────────────────────────────────────────
|
|
42352
|
+
*/
|
|
42343
42353
|
|
|
42344
42354
|
|
|
42345
42355
|
|
|
@@ -42590,7 +42600,6 @@ You are a ${normalizedName} agent specialized in {domain/responsibility}.
|
|
|
42590
42600
|
* @param agentName The name of the agent to create
|
|
42591
42601
|
* @returns Result of the operation
|
|
42592
42602
|
*/ async execute(targetDir, agentName) {
|
|
42593
|
-
// Validate the agent name using Zod schema
|
|
42594
42603
|
const validationResult = AgentNameSchema.safeParse(agentName);
|
|
42595
42604
|
if (!validationResult.success) {
|
|
42596
42605
|
const errorMessage = validationResult.error.issues[0]?.message ?? "Invalid agent name";
|
|
@@ -42599,18 +42608,14 @@ You are a ${normalizedName} agent specialized in {domain/responsibility}.
|
|
|
42599
42608
|
error: errorMessage
|
|
42600
42609
|
};
|
|
42601
42610
|
}
|
|
42602
|
-
// Normalize the agent name
|
|
42603
42611
|
const normalizedName = normalizeAgentName(validationResult.data);
|
|
42604
|
-
// Validate that the normalized name is not empty (handles whitespace-only input)
|
|
42605
42612
|
if (!normalizedName) {
|
|
42606
42613
|
return {
|
|
42607
42614
|
success: false,
|
|
42608
42615
|
error: "Agent name is required"
|
|
42609
42616
|
};
|
|
42610
42617
|
}
|
|
42611
|
-
// Get the file path
|
|
42612
42618
|
const filePath = this.gateway.getAgentFilePath(targetDir, normalizedName);
|
|
42613
|
-
// Check if the file already exists
|
|
42614
42619
|
if (await this.gateway.agentFileExists(targetDir, normalizedName)) {
|
|
42615
42620
|
return {
|
|
42616
42621
|
success: false,
|
|
@@ -42618,11 +42623,8 @@ You are a ${normalizedName} agent specialized in {domain/responsibility}.
|
|
|
42618
42623
|
filePath
|
|
42619
42624
|
};
|
|
42620
42625
|
}
|
|
42621
|
-
// Ensure the directory exists
|
|
42622
42626
|
await this.gateway.ensureAgentsDirectory(targetDir);
|
|
42623
|
-
// Generate the content
|
|
42624
42627
|
const content = generateAgentContent(normalizedName);
|
|
42625
|
-
// Write the file
|
|
42626
42628
|
await this.gateway.writeAgentFile(targetDir, normalizedName, content);
|
|
42627
42629
|
return {
|
|
42628
42630
|
success: true,
|
|
@@ -42718,7 +42720,6 @@ Copilot should use this skill when:
|
|
|
42718
42720
|
* @param skillName The name of the skill to create
|
|
42719
42721
|
* @returns Result of the operation
|
|
42720
42722
|
*/ async execute(targetDir, skillName) {
|
|
42721
|
-
// Validate the skill name using Zod schema
|
|
42722
42723
|
const validationResult = SkillNameSchema.safeParse(skillName);
|
|
42723
42724
|
if (!validationResult.success) {
|
|
42724
42725
|
const errorMessage = validationResult.error.issues[0]?.message ?? "Invalid skill name";
|
|
@@ -42727,19 +42728,15 @@ Copilot should use this skill when:
|
|
|
42727
42728
|
error: errorMessage
|
|
42728
42729
|
};
|
|
42729
42730
|
}
|
|
42730
|
-
// Normalize the skill name
|
|
42731
42731
|
const normalizedName = normalizeSkillName(validationResult.data);
|
|
42732
|
-
// Validate that the normalized name is not empty (handles whitespace-only input)
|
|
42733
42732
|
if (!normalizedName) {
|
|
42734
42733
|
return {
|
|
42735
42734
|
success: false,
|
|
42736
42735
|
error: "Skill name is required"
|
|
42737
42736
|
};
|
|
42738
42737
|
}
|
|
42739
|
-
// Get the paths
|
|
42740
42738
|
const skillDirectoryPath = this.gateway.getSkillDirectoryPath(targetDir, normalizedName);
|
|
42741
42739
|
const skillFilePath = this.gateway.getSkillFilePath(targetDir, normalizedName);
|
|
42742
|
-
// Check if the skill directory already exists
|
|
42743
42740
|
if (await this.gateway.skillDirectoryExists(targetDir, normalizedName)) {
|
|
42744
42741
|
return {
|
|
42745
42742
|
success: false,
|
|
@@ -42747,11 +42744,8 @@ Copilot should use this skill when:
|
|
|
42747
42744
|
skillDirectoryPath
|
|
42748
42745
|
};
|
|
42749
42746
|
}
|
|
42750
|
-
// Ensure the skill directory exists
|
|
42751
42747
|
await this.gateway.ensureSkillDirectory(targetDir, normalizedName);
|
|
42752
|
-
// Generate the content
|
|
42753
42748
|
const content = generateSkillContent(normalizedName);
|
|
42754
|
-
// Write the SKILL.md file
|
|
42755
42749
|
await this.gateway.writeSkillFile(targetDir, normalizedName, content);
|
|
42756
42750
|
return {
|
|
42757
42751
|
success: true,
|
|
@@ -51694,4 +51688,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
51694
51688
|
// module factories are used so entry inlining is disabled
|
|
51695
51689
|
// startup
|
|
51696
51690
|
// Load entry module and return exports
|
|
51697
|
-
var __webpack_exports__ = __webpack_require__(
|
|
51691
|
+
var __webpack_exports__ = __webpack_require__(2091);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lousy-agents/cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.1",
|
|
4
4
|
"description": "CLI scaffolding tool that sets up projects with structure, instructions, and feedback loops for AI coding assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"citty": "0.2.2",
|
|
46
46
|
"consola": "3.4.2",
|
|
47
47
|
"eta": "4.6.0",
|
|
48
|
-
"zod": "4.4.
|
|
48
|
+
"zod": "4.4.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@lousy-agents/core": "*",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@biomejs/biome": "2.4.14",
|
|
25
25
|
"@eslint/eslintrc": "3.3.5",
|
|
26
|
-
"@lousy-agents/mcp": "5.
|
|
26
|
+
"@lousy-agents/mcp": "5.13.0",
|
|
27
27
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
28
28
|
"@testing-library/jest-dom": "6.9.1",
|
|
29
29
|
"@testing-library/react": "16.3.2",
|