azcodr 2.2.0 → 2.4.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/.agents/scripts/agent_guard.js +63 -0
- package/.agents/skills/lets-build/SKILL.md +1 -1
- package/.github/workflows/ci.yml +21 -0
- package/.github/workflows/publish.yml +7 -0
- package/CODE_OF_CONDUCT.md +122 -0
- package/CONTRIBUTING.md +85 -0
- package/README.md +73 -3
- package/SECURITY.md +32 -0
- package/data/memory.template +36 -0
- package/docs/rules/clean_code.md +13 -0
- package/docs/tipping-points.md +37 -0
- package/lib/agent-guard-command.d.ts +28 -0
- package/lib/agent-guard-command.d.ts.map +1 -0
- package/lib/agent-guard-command.js +103 -0
- package/lib/agent-guard-command.js.map +1 -0
- package/lib/agent-guard-file.d.ts +32 -0
- package/lib/agent-guard-file.d.ts.map +1 -0
- package/lib/agent-guard-file.js +77 -0
- package/lib/agent-guard-file.js.map +1 -0
- package/lib/agent-guard-tdd.d.ts +34 -0
- package/lib/agent-guard-tdd.d.ts.map +1 -0
- package/lib/agent-guard-tdd.js +77 -0
- package/lib/agent-guard-tdd.js.map +1 -0
- package/lib/agent-guard.d.ts +38 -0
- package/lib/agent-guard.d.ts.map +1 -0
- package/lib/agent-guard.js +80 -0
- package/lib/agent-guard.js.map +1 -0
- package/lib/boundaries.d.ts +41 -0
- package/lib/boundaries.d.ts.map +1 -0
- package/lib/boundaries.js +158 -0
- package/lib/boundaries.js.map +1 -0
- package/lib/cli-boundaries.d.ts +18 -0
- package/lib/cli-boundaries.d.ts.map +1 -0
- package/lib/cli-boundaries.js +46 -0
- package/lib/cli-boundaries.js.map +1 -0
- package/lib/cli-parse.d.ts +5 -0
- package/lib/cli-parse.d.ts.map +1 -1
- package/lib/cli-parse.js +35 -5
- package/lib/cli-parse.js.map +1 -1
- package/lib/cli.d.ts +17 -0
- package/lib/cli.d.ts.map +1 -1
- package/lib/cli.js +47 -5
- package/lib/cli.js.map +1 -1
- package/lib/guards.d.ts +6 -0
- package/lib/guards.d.ts.map +1 -1
- package/lib/guards.js +10 -1
- package/lib/guards.js.map +1 -1
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -1
- package/lib/repo.js +4 -4
- package/lib/repo.js.map +1 -1
- package/lib/scaffold.d.ts +2 -7
- package/lib/scaffold.d.ts.map +1 -1
- package/lib/scaffold.js +7 -10
- package/lib/scaffold.js.map +1 -1
- package/lib/validate.d.ts +33 -0
- package/lib/validate.d.ts.map +1 -0
- package/lib/validate.js +24 -0
- package/lib/validate.js.map +1 -0
- package/memory.md +24 -0
- package/package.json +10 -2
- package/scripts/validate/links.js +0 -1
- package/scripts/validate/root.js +1 -1
- package/src/agent-guard-command.ts +118 -0
- package/src/agent-guard-file.ts +107 -0
- package/src/agent-guard-tdd.ts +96 -0
- package/src/agent-guard.ts +123 -0
- package/src/boundaries.ts +198 -0
- package/src/cli-boundaries.ts +67 -0
- package/src/cli-parse.ts +40 -4
- package/src/cli.ts +57 -15
- package/src/guards.ts +11 -1
- package/src/index.ts +40 -0
- package/src/repo.ts +4 -4
- package/src/scaffold.ts +7 -10
- package/src/validate.ts +47 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command safety guard inspecting shell commands against a destructive denylist.
|
|
3
|
+
*/
|
|
4
|
+
export const DESTRUCTIVE_RULES = [
|
|
5
|
+
{
|
|
6
|
+
pattern: /\brm\s+(-[a-z-]*[rf][a-z-]*\s+)+(\/|\/\*|~|~\/\*|\$home|\$\{home\}|\.|\.\.|\.\/|[a-z]:\\|[a-z]:\/)/i,
|
|
7
|
+
reason: 'destructive rm targeting root, home, drive, or the current directory'
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
pattern: /(^|\s)rm\s+(-[a-z-]*\s+)*(\/|~|[a-z]:\\|[a-z]:\/)($|\s)/i,
|
|
11
|
+
reason: 'destructive rm targeting root or home'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
pattern: /\brm\s+--recursive.*--force\s+(\/|~|\$home|\.)/i,
|
|
15
|
+
reason: 'destructive rm using long-form flags'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
pattern: /\brm\s+.*--no-preserve-root/i,
|
|
19
|
+
reason: 'rm --no-preserve-root is forbidden under agent execution'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /\b(wipefs\s+-a|shred\s+-u|find\s+.*-delete)\b/i,
|
|
23
|
+
reason: 'irreversible disk or mass-delete operation'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
pattern: /\bgit\s+push\s+.*(-f|--force|--mirror)\b/i,
|
|
27
|
+
reason: 'force-push or mirror-push can destroy remote history'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
pattern: /\bgit\s+push\b.*--force-with-lease\b/i,
|
|
31
|
+
reason: 'force-push variant detected'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: /\bgit\s+(reset\s+--hard|clean\s+-[a-z]*[fd])/i,
|
|
35
|
+
reason: 'destructive git worktree operation (reset --hard / clean)'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pattern: /\bgit\s+checkout\s+--\s+\./i,
|
|
39
|
+
reason: 'git checkout -- . discards all uncommitted work'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
pattern: /\bgit\s+branch\s+-D\b/i,
|
|
43
|
+
reason: 'force branch delete'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
pattern: /\b(drop\s+(database|schema|table)|truncate\s+(table\s+)?[a-z_])/i,
|
|
47
|
+
reason: 'destructive SQL DDL'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
pattern: /\b(delete\s+from|update\s+[a-z_]+\s+set|alter\s+table)\b/i,
|
|
51
|
+
reason: 'destructive SQL DML'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: /\b(grant\s+all|revoke\s+all)\b/i,
|
|
55
|
+
reason: 'privilege escalation in SQL'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
pattern: /\b(mkfs|dd\s+if=|fdisk|parted\s+\/dev)\b/i,
|
|
59
|
+
reason: 'raw disk operation'
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
pattern: /(:[\s]*\([\s]*\)[\s]*\{|:[\s]*\|[\s]*:)/,
|
|
63
|
+
reason: 'fork bomb'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
pattern: /\b(curl|wget)\s+.*\|\s*(sudo\s+)?(ba)?sh\b/i,
|
|
67
|
+
reason: 'piping download directly into shell execution'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
pattern: /\b(base64\s+-d|base64\s+--decode)\s+.*\|/i,
|
|
71
|
+
reason: 'decoding encoded payload into a pipe'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
pattern: /\b(npm|yarn|pnpm)\s+publish\b/i,
|
|
75
|
+
reason: 'package publish must run via release workflow, not ad-hoc tool call'
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
export function normalizeCommand(raw) {
|
|
79
|
+
return raw.replace(/[\r\n\t]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks a command string against the safety denylist.
|
|
83
|
+
*
|
|
84
|
+
* @param command - Raw shell command string.
|
|
85
|
+
* @returns Violation details if blocked, or clean outcome.
|
|
86
|
+
*/
|
|
87
|
+
export function inspectCommand(command) {
|
|
88
|
+
const normalized = normalizeCommand(command);
|
|
89
|
+
if (!normalized)
|
|
90
|
+
return { blocked: false };
|
|
91
|
+
for (const rule of DESTRUCTIVE_RULES) {
|
|
92
|
+
if (rule.pattern.test(normalized)) {
|
|
93
|
+
return {
|
|
94
|
+
blocked: true,
|
|
95
|
+
reason: rule.reason,
|
|
96
|
+
command: normalized.slice(0, 300)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { blocked: false, command: normalized };
|
|
101
|
+
}
|
|
102
|
+
export default { DESTRUCTIVE_RULES, normalizeCommand, inspectCommand };
|
|
103
|
+
//# sourceMappingURL=agent-guard-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-command.js","sourceRoot":"","sources":["../src/agent-guard-command.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,CAAC,MAAM,iBAAiB,GAA4B;IACxD;QACE,OAAO,EAAE,qGAAqG;QAC9G,MAAM,EAAE,sEAAsE;KAC/E;IACD;QACE,OAAO,EAAE,0DAA0D;QACnE,MAAM,EAAE,uCAAuC;KAChD;IACD;QACE,OAAO,EAAE,iDAAiD;QAC1D,MAAM,EAAE,sCAAsC;KAC/C;IACD;QACE,OAAO,EAAE,8BAA8B;QACvC,MAAM,EAAE,0DAA0D;KACnE;IACD;QACE,OAAO,EAAE,gDAAgD;QACzD,MAAM,EAAE,4CAA4C;KACrD;IACD;QACE,OAAO,EAAE,2CAA2C;QACpD,MAAM,EAAE,sDAAsD;KAC/D;IACD;QACE,OAAO,EAAE,uCAAuC;QAChD,MAAM,EAAE,6BAA6B;KACtC;IACD;QACE,OAAO,EAAE,+CAA+C;QACxD,MAAM,EAAE,2DAA2D;KACpE;IACD;QACE,OAAO,EAAE,6BAA6B;QACtC,MAAM,EAAE,iDAAiD;KAC1D;IACD;QACE,OAAO,EAAE,wBAAwB;QACjC,MAAM,EAAE,qBAAqB;KAC9B;IACD;QACE,OAAO,EAAE,kEAAkE;QAC3E,MAAM,EAAE,qBAAqB;KAC9B;IACD;QACE,OAAO,EAAE,2DAA2D;QACpE,MAAM,EAAE,qBAAqB;KAC9B;IACD;QACE,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EAAE,6BAA6B;KACtC;IACD;QACE,OAAO,EAAE,2CAA2C;QACpD,MAAM,EAAE,oBAAoB;KAC7B;IACD;QACE,OAAO,EAAE,yCAAyC;QAClD,MAAM,EAAE,WAAW;KACpB;IACD;QACE,OAAO,EAAE,6CAA6C;QACtD,MAAM,EAAE,+CAA+C;KACxD;IACD;QACE,OAAO,EAAE,2CAA2C;QACpD,MAAM,EAAE,sCAAsC;KAC/C;IACD;QACE,OAAO,EAAE,gCAAgC;QACzC,MAAM,EAAE,qEAAqE;KAC9E;CACF,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAE3C,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;aAClC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACjD,CAAC;AAED,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const DEFAULT_MAX_FILE_LINES = 300;
|
|
2
|
+
export interface FileCheckInput {
|
|
3
|
+
filePath: string;
|
|
4
|
+
incomingContent?: string;
|
|
5
|
+
replacementContent?: string;
|
|
6
|
+
targetContent?: string;
|
|
7
|
+
maxLines?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface FileCheckResult {
|
|
10
|
+
blocked: boolean;
|
|
11
|
+
reason?: string;
|
|
12
|
+
currentLines: number;
|
|
13
|
+
projectedLines: number;
|
|
14
|
+
maxLines: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function countLines(text: string): number;
|
|
17
|
+
export declare function readExistingLines(filePath: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Checks a proposed file write or edit against Refactor-Before-Add rules.
|
|
20
|
+
*
|
|
21
|
+
* @param input - File path and proposed content.
|
|
22
|
+
* @returns Decision result indicating whether the write is blocked.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inspectFileWrite(input: FileCheckInput): FileCheckResult;
|
|
25
|
+
declare const _default: {
|
|
26
|
+
DEFAULT_MAX_FILE_LINES: number;
|
|
27
|
+
countLines: typeof countLines;
|
|
28
|
+
readExistingLines: typeof readExistingLines;
|
|
29
|
+
inspectFileWrite: typeof inspectFileWrite;
|
|
30
|
+
};
|
|
31
|
+
export default _default;
|
|
32
|
+
//# sourceMappingURL=agent-guard-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-file.d.ts","sourceRoot":"","sources":["../src/agent-guard-file.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAgDD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAWvE;;;;;;;AAED,wBAKE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const DEFAULT_MAX_FILE_LINES = 300;
|
|
4
|
+
export function countLines(text) {
|
|
5
|
+
if (!text)
|
|
6
|
+
return 0;
|
|
7
|
+
return text.split('\n').length;
|
|
8
|
+
}
|
|
9
|
+
export function readExistingLines(filePath) {
|
|
10
|
+
if (!fs.existsSync(filePath))
|
|
11
|
+
return 0;
|
|
12
|
+
try {
|
|
13
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
+
return countLines(content);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function calculateProjectedLines(currentLines, input) {
|
|
21
|
+
if (input.incomingContent !== undefined) {
|
|
22
|
+
return countLines(input.incomingContent);
|
|
23
|
+
}
|
|
24
|
+
if (input.replacementContent !== undefined) {
|
|
25
|
+
const addedLines = countLines(input.replacementContent);
|
|
26
|
+
const removedLines = input.targetContent !== undefined ? countLines(input.targetContent) : 1;
|
|
27
|
+
return Math.max(0, currentLines + addedLines - removedLines);
|
|
28
|
+
}
|
|
29
|
+
return currentLines;
|
|
30
|
+
}
|
|
31
|
+
function evaluateLineThresholds(params) {
|
|
32
|
+
const { filePath, currentLines, projectedLines, maxLines } = params;
|
|
33
|
+
const base = { currentLines, projectedLines, maxLines };
|
|
34
|
+
const relPath = path.basename(filePath);
|
|
35
|
+
if (currentLines > maxLines) {
|
|
36
|
+
if (projectedLines >= currentLines) {
|
|
37
|
+
return {
|
|
38
|
+
...base,
|
|
39
|
+
blocked: true,
|
|
40
|
+
reason: `Refactor-Before-Add violation: '${relPath}' currently has ${currentLines} lines (limit: ${maxLines}). Adding or maintaining lines in an overflowing file is blocked. Refactor into modular units under green tests first.`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { ...base, blocked: false };
|
|
44
|
+
}
|
|
45
|
+
if (projectedLines > maxLines) {
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
blocked: true,
|
|
49
|
+
reason: `File line budget exceeded: '${relPath}' would grow to ${projectedLines} lines (limit: ${maxLines}). Decompose into modular components before appending more code.`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { ...base, blocked: false };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Checks a proposed file write or edit against Refactor-Before-Add rules.
|
|
56
|
+
*
|
|
57
|
+
* @param input - File path and proposed content.
|
|
58
|
+
* @returns Decision result indicating whether the write is blocked.
|
|
59
|
+
*/
|
|
60
|
+
export function inspectFileWrite(input) {
|
|
61
|
+
const maxLines = input.maxLines ?? DEFAULT_MAX_FILE_LINES;
|
|
62
|
+
const currentLines = readExistingLines(input.filePath);
|
|
63
|
+
const projectedLines = calculateProjectedLines(currentLines, input);
|
|
64
|
+
return evaluateLineThresholds({
|
|
65
|
+
filePath: input.filePath,
|
|
66
|
+
currentLines,
|
|
67
|
+
projectedLines,
|
|
68
|
+
maxLines
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export default {
|
|
72
|
+
DEFAULT_MAX_FILE_LINES,
|
|
73
|
+
countLines,
|
|
74
|
+
readExistingLines,
|
|
75
|
+
inspectFileWrite
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=agent-guard-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-file.js","sourceRoot":"","sources":["../src/agent-guard-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAkB1C,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,YAAoB,EAAE,KAAqB;IAC1E,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACxC,OAAO,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACxD,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7F,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,YAAY,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AASD,SAAS,sBAAsB,CAAC,MAAuB;IACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACpE,MAAM,IAAI,GAAG,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAExC,IAAI,YAAY,GAAG,QAAQ,EAAE,CAAC;QAC5B,IAAI,cAAc,IAAI,YAAY,EAAE,CAAC;YACnC,OAAO;gBACL,GAAG,IAAI;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,mCAAmC,OAAO,mBAAmB,YAAY,kBAAkB,QAAQ,wHAAwH;aACpO,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACrC,CAAC;IAED,IAAI,cAAc,GAAG,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,GAAG,IAAI;YACP,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,+BAA+B,OAAO,mBAAmB,cAAc,kBAAkB,QAAQ,kEAAkE;SAC5K,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAqB;IACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,sBAAsB,CAAC;IAC1D,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG,uBAAuB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAEpE,OAAO,sBAAsB,CAAC;QAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,YAAY;QACZ,cAAc;QACd,QAAQ;KACT,CAAC,CAAC;AACL,CAAC;AAED,eAAe;IACb,sBAAsB;IACtB,UAAU;IACV,iBAAiB;IACjB,gBAAgB;CACjB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface SessionTddState {
|
|
2
|
+
lastFailingTestRecorded: boolean;
|
|
3
|
+
lastTestRunTime?: number;
|
|
4
|
+
lastTestPath?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TddCheckResult {
|
|
7
|
+
blocked: boolean;
|
|
8
|
+
reason?: string;
|
|
9
|
+
isProductionFile: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function isTestFile(filePath: string): boolean;
|
|
12
|
+
export declare function isNonCodeFile(filePath: string): boolean;
|
|
13
|
+
export declare function isProductionFile(filePath: string): boolean;
|
|
14
|
+
export declare function readTddState(stateFile: string): SessionTddState;
|
|
15
|
+
export declare function writeTddState(stateFile: string, state: SessionTddState): void;
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether an edit to a file is allowed under RED-before-GREEN TDD rules.
|
|
18
|
+
*
|
|
19
|
+
* @param filePath - Path to file being edited.
|
|
20
|
+
* @param state - Current session TDD state.
|
|
21
|
+
* @param enforceTestFirst - Whether test-first enforcement is strictly enabled.
|
|
22
|
+
* @returns Decision result indicating whether edit is blocked.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inspectTddRequirement(filePath: string, state: SessionTddState, enforceTestFirst?: boolean): TddCheckResult;
|
|
25
|
+
declare const _default: {
|
|
26
|
+
isTestFile: typeof isTestFile;
|
|
27
|
+
isNonCodeFile: typeof isNonCodeFile;
|
|
28
|
+
isProductionFile: typeof isProductionFile;
|
|
29
|
+
readTddState: typeof readTddState;
|
|
30
|
+
writeTddState: typeof writeTddState;
|
|
31
|
+
inspectTddRequirement: typeof inspectTddRequirement;
|
|
32
|
+
};
|
|
33
|
+
export default _default;
|
|
34
|
+
//# sourceMappingURL=agent-guard-tdd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-tdd.d.ts","sourceRoot":"","sources":["../src/agent-guard-tdd.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMpD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK1D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAU/D;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,EACtB,gBAAgB,GAAE,OAAe,GAChC,cAAc,CAgBhB;;;;;;;;;AAED,wBAOE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function isTestFile(filePath) {
|
|
4
|
+
const norm = filePath.replace(/\\/g, '/').toLowerCase();
|
|
5
|
+
if (/(^|\/)(tests?|__tests__)(\/|$)/i.test(norm)) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
return /\.(test|spec)\.[a-z0-9]+$/i.test(norm);
|
|
9
|
+
}
|
|
10
|
+
export function isNonCodeFile(filePath) {
|
|
11
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
12
|
+
const nonCodeExts = new Set(['.md', '.json', '.yml', '.yaml', '.txt', '.toml', '.lock', '.svg', '.png']);
|
|
13
|
+
return nonCodeExts.has(ext);
|
|
14
|
+
}
|
|
15
|
+
export function isProductionFile(filePath) {
|
|
16
|
+
if (isNonCodeFile(filePath))
|
|
17
|
+
return false;
|
|
18
|
+
if (isTestFile(filePath))
|
|
19
|
+
return false;
|
|
20
|
+
const norm = filePath.replace(/\\/g, '/').toLowerCase();
|
|
21
|
+
return norm.includes('src/') || norm.includes('lib/') || norm.includes('app/');
|
|
22
|
+
}
|
|
23
|
+
export function readTddState(stateFile) {
|
|
24
|
+
if (!fs.existsSync(stateFile)) {
|
|
25
|
+
return { lastFailingTestRecorded: false };
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const raw = fs.readFileSync(stateFile, 'utf-8');
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { lastFailingTestRecorded: false };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function writeTddState(stateFile, state) {
|
|
36
|
+
try {
|
|
37
|
+
const dir = path.dirname(stateFile);
|
|
38
|
+
if (!fs.existsSync(dir))
|
|
39
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
40
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Fail-safe write: do not crash agent if state cannot be persisted
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Checks whether an edit to a file is allowed under RED-before-GREEN TDD rules.
|
|
48
|
+
*
|
|
49
|
+
* @param filePath - Path to file being edited.
|
|
50
|
+
* @param state - Current session TDD state.
|
|
51
|
+
* @param enforceTestFirst - Whether test-first enforcement is strictly enabled.
|
|
52
|
+
* @returns Decision result indicating whether edit is blocked.
|
|
53
|
+
*/
|
|
54
|
+
export function inspectTddRequirement(filePath, state, enforceTestFirst = false) {
|
|
55
|
+
const prod = isProductionFile(filePath);
|
|
56
|
+
if (!enforceTestFirst || !prod) {
|
|
57
|
+
return { blocked: false, isProductionFile: prod };
|
|
58
|
+
}
|
|
59
|
+
if (!state.lastFailingTestRecorded) {
|
|
60
|
+
const baseName = path.basename(filePath);
|
|
61
|
+
return {
|
|
62
|
+
blocked: true,
|
|
63
|
+
isProductionFile: true,
|
|
64
|
+
reason: `Test-First (RED-before-GREEN) requirement: Production file '${baseName}' cannot be edited before a failing test (RED) is recorded. Author a failing unit or acceptance test first, verify failure output, then edit production code.`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { blocked: false, isProductionFile: true };
|
|
68
|
+
}
|
|
69
|
+
export default {
|
|
70
|
+
isTestFile,
|
|
71
|
+
isNonCodeFile,
|
|
72
|
+
isProductionFile,
|
|
73
|
+
readTddState,
|
|
74
|
+
writeTddState,
|
|
75
|
+
inspectTddRequirement
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=agent-guard-tdd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-tdd.js","sourceRoot":"","sources":["../src/agent-guard-tdd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { inspectCommand } from './agent-guard-command.js';
|
|
2
|
+
import type { CommandViolation } from './agent-guard-command.js';
|
|
3
|
+
import { inspectFileWrite } from './agent-guard-file.js';
|
|
4
|
+
import type { FileCheckResult } from './agent-guard-file.js';
|
|
5
|
+
import { inspectTddRequirement, readTddState, writeTddState } from './agent-guard-tdd.js';
|
|
6
|
+
import type { SessionTddState } from './agent-guard-tdd.js';
|
|
7
|
+
export interface GuardDecision {
|
|
8
|
+
allowed: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
source?: 'command' | 'file' | 'tdd';
|
|
11
|
+
}
|
|
12
|
+
export interface ToolEnvelope {
|
|
13
|
+
tool_name?: string;
|
|
14
|
+
tool_input?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export interface GuardOptions {
|
|
17
|
+
workspaceRoot?: string;
|
|
18
|
+
enforceTestFirst?: boolean;
|
|
19
|
+
maxFileLines?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Inspects a tool-use request before execution (PreToolUse hook).
|
|
23
|
+
*
|
|
24
|
+
* @param rawInput - Raw command string, JSON envelope, or argv slice.
|
|
25
|
+
* @param options - Guard configuration options.
|
|
26
|
+
* @returns Guard decision indicating whether tool execution is permitted.
|
|
27
|
+
*/
|
|
28
|
+
export declare function inspectPreTool(rawInput: string, options?: GuardOptions): GuardDecision;
|
|
29
|
+
export { inspectCommand, inspectFileWrite, inspectTddRequirement, readTddState, writeTddState };
|
|
30
|
+
export type { CommandViolation, FileCheckResult, SessionTddState };
|
|
31
|
+
declare const _default: {
|
|
32
|
+
inspectPreTool: typeof inspectPreTool;
|
|
33
|
+
inspectCommand: typeof inspectCommand;
|
|
34
|
+
inspectFileWrite: typeof inspectFileWrite;
|
|
35
|
+
inspectTddRequirement: typeof inspectTddRequirement;
|
|
36
|
+
};
|
|
37
|
+
export default _default;
|
|
38
|
+
//# sourceMappingURL=agent-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard.d.ts","sourceRoot":"","sources":["../src/agent-guard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,aAAa,CAY1F;AAED,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACd,CAAC;AACF,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC;;;;;;;AACnE,wBAA2F"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { inspectCommand } from './agent-guard-command.js';
|
|
3
|
+
import { inspectFileWrite } from './agent-guard-file.js';
|
|
4
|
+
import { inspectTddRequirement, readTddState, writeTddState } from './agent-guard-tdd.js';
|
|
5
|
+
function parseEnvelope(raw) {
|
|
6
|
+
try {
|
|
7
|
+
const trimmed = raw.trim();
|
|
8
|
+
if (!trimmed.startsWith('{'))
|
|
9
|
+
return null;
|
|
10
|
+
return JSON.parse(trimmed);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function extractKey(input, keys) {
|
|
17
|
+
for (const k of keys) {
|
|
18
|
+
if (typeof input[k] === 'string')
|
|
19
|
+
return input[k];
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function extractFilePath(input) {
|
|
24
|
+
return extractKey(input, ['TargetFile', 'file_path', 'path', 'filePath']);
|
|
25
|
+
}
|
|
26
|
+
function extractCommand(input) {
|
|
27
|
+
return extractKey(input, ['CommandLine', 'command', 'cmd']);
|
|
28
|
+
}
|
|
29
|
+
function inspectPreCommand(cmd) {
|
|
30
|
+
const violation = inspectCommand(cmd);
|
|
31
|
+
if (violation.blocked) {
|
|
32
|
+
return { allowed: false, reason: violation.reason, source: 'command' };
|
|
33
|
+
}
|
|
34
|
+
return { allowed: true };
|
|
35
|
+
}
|
|
36
|
+
function inspectPreFile(filePath, input, options) {
|
|
37
|
+
const incoming = extractKey(input, ['CodeContent', 'content']);
|
|
38
|
+
const replacement = extractKey(input, ['ReplacementContent', 'replacement']);
|
|
39
|
+
const target = extractKey(input, ['TargetContent', 'target']);
|
|
40
|
+
const fileDecision = inspectFileWrite({
|
|
41
|
+
filePath,
|
|
42
|
+
incomingContent: incoming,
|
|
43
|
+
replacementContent: replacement,
|
|
44
|
+
targetContent: target,
|
|
45
|
+
maxLines: options.maxFileLines
|
|
46
|
+
});
|
|
47
|
+
if (fileDecision.blocked) {
|
|
48
|
+
return { allowed: false, reason: fileDecision.reason, source: 'file' };
|
|
49
|
+
}
|
|
50
|
+
const root = options.workspaceRoot ?? process.cwd();
|
|
51
|
+
const statePath = path.join(root, '.agents', '.session-state.json');
|
|
52
|
+
const state = readTddState(statePath);
|
|
53
|
+
const tddDecision = inspectTddRequirement(filePath, state, options.enforceTestFirst);
|
|
54
|
+
if (tddDecision.blocked) {
|
|
55
|
+
return { allowed: false, reason: tddDecision.reason, source: 'tdd' };
|
|
56
|
+
}
|
|
57
|
+
return { allowed: true };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Inspects a tool-use request before execution (PreToolUse hook).
|
|
61
|
+
*
|
|
62
|
+
* @param rawInput - Raw command string, JSON envelope, or argv slice.
|
|
63
|
+
* @param options - Guard configuration options.
|
|
64
|
+
* @returns Guard decision indicating whether tool execution is permitted.
|
|
65
|
+
*/
|
|
66
|
+
export function inspectPreTool(rawInput, options = {}) {
|
|
67
|
+
const env = parseEnvelope(rawInput);
|
|
68
|
+
if (env && env.tool_input) {
|
|
69
|
+
const cmd = extractCommand(env.tool_input);
|
|
70
|
+
if (cmd)
|
|
71
|
+
return inspectPreCommand(cmd);
|
|
72
|
+
const targetFile = extractFilePath(env.tool_input);
|
|
73
|
+
if (targetFile)
|
|
74
|
+
return inspectPreFile(targetFile, env.tool_input, options);
|
|
75
|
+
}
|
|
76
|
+
return inspectPreCommand(rawInput);
|
|
77
|
+
}
|
|
78
|
+
export { inspectCommand, inspectFileWrite, inspectTddRequirement, readTddState, writeTddState };
|
|
79
|
+
export default { inspectPreTool, inspectCommand, inspectFileWrite, inspectTddRequirement };
|
|
80
|
+
//# sourceMappingURL=agent-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard.js","sourceRoot":"","sources":["../src/agent-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface BoundaryRule {
|
|
2
|
+
fromLayer: string;
|
|
3
|
+
disallowImportsFrom: readonly string[];
|
|
4
|
+
}
|
|
5
|
+
export interface BoundaryViolation {
|
|
6
|
+
file: string;
|
|
7
|
+
importedFile: string;
|
|
8
|
+
fromLayer: string;
|
|
9
|
+
toLayer: string;
|
|
10
|
+
}
|
|
11
|
+
export interface BoundaryReport {
|
|
12
|
+
moduleCount: number;
|
|
13
|
+
cycles: string[][];
|
|
14
|
+
violations: BoundaryViolation[];
|
|
15
|
+
ok: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_BOUNDARY_RULES: readonly BoundaryRule[];
|
|
18
|
+
export declare function findSourceFiles(dir: string): string[];
|
|
19
|
+
export declare function extractLocalImports(filePath: string): string[];
|
|
20
|
+
export declare function buildDependencyGraph(files: readonly string[]): Map<string, string[]>;
|
|
21
|
+
export declare function detectDependencyCycles(graph: Map<string, string[]>): string[][];
|
|
22
|
+
export declare function detectBoundaryViolations(graph: Map<string, string[]>, rules?: readonly BoundaryRule[]): BoundaryViolation[];
|
|
23
|
+
/**
|
|
24
|
+
* Inspects a source tree for architectural boundary violations and circular dependency cycles.
|
|
25
|
+
*
|
|
26
|
+
* @param sourceDir - Directory containing source code (e.g. src/).
|
|
27
|
+
* @param rules - Architectural boundary rules.
|
|
28
|
+
* @returns Report summarizing cycle and boundary findings.
|
|
29
|
+
*/
|
|
30
|
+
export declare function inspectBoundaries(sourceDir: string, rules?: readonly BoundaryRule[]): BoundaryReport;
|
|
31
|
+
declare const _default: {
|
|
32
|
+
DEFAULT_BOUNDARY_RULES: readonly BoundaryRule[];
|
|
33
|
+
findSourceFiles: typeof findSourceFiles;
|
|
34
|
+
extractLocalImports: typeof extractLocalImports;
|
|
35
|
+
buildDependencyGraph: typeof buildDependencyGraph;
|
|
36
|
+
detectDependencyCycles: typeof detectDependencyCycles;
|
|
37
|
+
detectBoundaryViolations: typeof detectBoundaryViolations;
|
|
38
|
+
inspectBoundaries: typeof inspectBoundaries;
|
|
39
|
+
};
|
|
40
|
+
export default _default;
|
|
41
|
+
//# sourceMappingURL=boundaries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundaries.d.ts","sourceRoot":"","sources":["../src/boundaries.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;IACnB,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,EAAE,EAAE,OAAO,CAAC;CACb;AAED,eAAO,MAAM,sBAAsB,EAAE,SAAS,YAAY,EASzD,CAAC;AAYF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBrD;AAcD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB9D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAMpF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,EAAE,CA+B/E;AAMD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,KAAK,GAAE,SAAS,YAAY,EAA2B,GACtD,iBAAiB,EAAE,CAuBrB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,SAAS,YAAY,EAA2B,GACtD,cAAc,CAYhB;;;;;;;;;;AAED,wBAQE"}
|