azcodr 2.1.0 → 2.3.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.
Files changed (96) hide show
  1. package/.agents/scripts/agent_guard.js +63 -0
  2. package/.agents/skills/lets-build/SKILL.md +1 -1
  3. package/.github/workflows/ci.yml +9 -0
  4. package/AGENTS.md +1 -1
  5. package/CONTRIBUTING.md +79 -0
  6. package/README.md +4 -3
  7. package/SECURITY.md +32 -0
  8. package/bin/azcodr.js +3 -1
  9. package/data/memory.template +36 -0
  10. package/docs/rules/clean_code.md +13 -0
  11. package/docs/tipping-points.md +37 -0
  12. package/lib/agent-guard-command.d.ts +28 -0
  13. package/lib/agent-guard-command.d.ts.map +1 -0
  14. package/lib/agent-guard-command.js +103 -0
  15. package/lib/agent-guard-command.js.map +1 -0
  16. package/lib/agent-guard-file.d.ts +32 -0
  17. package/lib/agent-guard-file.d.ts.map +1 -0
  18. package/lib/agent-guard-file.js +77 -0
  19. package/lib/agent-guard-file.js.map +1 -0
  20. package/lib/agent-guard-tdd.d.ts +34 -0
  21. package/lib/agent-guard-tdd.d.ts.map +1 -0
  22. package/lib/agent-guard-tdd.js +77 -0
  23. package/lib/agent-guard-tdd.js.map +1 -0
  24. package/lib/agent-guard.d.ts +38 -0
  25. package/lib/agent-guard.d.ts.map +1 -0
  26. package/lib/agent-guard.js +80 -0
  27. package/lib/agent-guard.js.map +1 -0
  28. package/lib/boundaries.d.ts +41 -0
  29. package/lib/boundaries.d.ts.map +1 -0
  30. package/lib/boundaries.js +158 -0
  31. package/lib/boundaries.js.map +1 -0
  32. package/lib/cli-parse.d.ts +4 -0
  33. package/lib/cli-parse.d.ts.map +1 -1
  34. package/lib/cli-parse.js +23 -5
  35. package/lib/cli-parse.js.map +1 -1
  36. package/lib/cli.d.ts +14 -0
  37. package/lib/cli.d.ts.map +1 -1
  38. package/lib/cli.js +33 -5
  39. package/lib/cli.js.map +1 -1
  40. package/lib/errors.d.ts +15 -0
  41. package/lib/errors.d.ts.map +1 -1
  42. package/lib/errors.js +9 -0
  43. package/lib/errors.js.map +1 -1
  44. package/lib/git.d.ts +8 -0
  45. package/lib/git.d.ts.map +1 -1
  46. package/lib/git.js +8 -0
  47. package/lib/git.js.map +1 -1
  48. package/lib/guards.d.ts +22 -1
  49. package/lib/guards.d.ts.map +1 -1
  50. package/lib/guards.js +23 -2
  51. package/lib/guards.js.map +1 -1
  52. package/lib/index.d.ts +34 -0
  53. package/lib/index.d.ts.map +1 -1
  54. package/lib/index.js +25 -0
  55. package/lib/index.js.map +1 -1
  56. package/lib/links.d.ts +14 -0
  57. package/lib/links.d.ts.map +1 -1
  58. package/lib/links.js +11 -0
  59. package/lib/links.js.map +1 -1
  60. package/lib/permissions.d.ts +4 -0
  61. package/lib/permissions.d.ts.map +1 -1
  62. package/lib/permissions.js +4 -0
  63. package/lib/permissions.js.map +1 -1
  64. package/lib/repo.d.ts +10 -0
  65. package/lib/repo.d.ts.map +1 -1
  66. package/lib/repo.js +11 -4
  67. package/lib/repo.js.map +1 -1
  68. package/lib/scaffold.d.ts +29 -5
  69. package/lib/scaffold.d.ts.map +1 -1
  70. package/lib/scaffold.js +27 -25
  71. package/lib/scaffold.js.map +1 -1
  72. package/lib/validate.d.ts +33 -0
  73. package/lib/validate.d.ts.map +1 -0
  74. package/lib/validate.js +24 -0
  75. package/lib/validate.js.map +1 -0
  76. package/memory.md +16 -0
  77. package/package.json +7 -6
  78. package/scripts/validate/links.js +0 -1
  79. package/scripts/validate/root.js +1 -1
  80. package/src/agent-guard-command.ts +118 -0
  81. package/src/agent-guard-file.ts +107 -0
  82. package/src/agent-guard-tdd.ts +96 -0
  83. package/src/agent-guard.ts +123 -0
  84. package/src/boundaries.ts +198 -0
  85. package/src/cli-parse.ts +26 -4
  86. package/src/cli.ts +41 -15
  87. package/src/errors.ts +15 -0
  88. package/src/git.ts +8 -0
  89. package/src/guards.ts +27 -2
  90. package/src/index.ts +61 -0
  91. package/src/links.ts +14 -0
  92. package/src/permissions.ts +4 -0
  93. package/src/repo.ts +14 -4
  94. package/src/scaffold.ts +42 -36
  95. package/src/validate.ts +47 -0
  96. package/scripts/test_coverage.js +0 -66
@@ -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"}
@@ -0,0 +1,158 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export const DEFAULT_BOUNDARY_RULES = [
4
+ {
5
+ fromLayer: 'domain',
6
+ disallowImportsFrom: ['infrastructure', 'infra', 'adapters', 'cli', 'transport', 'controllers']
7
+ },
8
+ {
9
+ fromLayer: 'core',
10
+ disallowImportsFrom: ['infrastructure', 'infra', 'adapters', 'presentation', 'ui']
11
+ }
12
+ ];
13
+ function isSkippedDir(name) {
14
+ return name.startsWith('.') || name === 'node_modules' || name === 'lib';
15
+ }
16
+ function isEligibleSource(name) {
17
+ const isScript = name.endsWith('.ts') || name.endsWith('.js');
18
+ if (!isScript || name.endsWith('.d.ts'))
19
+ return false;
20
+ return !name.includes('.test.') && !name.includes('.spec.');
21
+ }
22
+ export function findSourceFiles(dir) {
23
+ if (!fs.existsSync(dir))
24
+ return [];
25
+ const results = [];
26
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
27
+ for (const entry of entries) {
28
+ if (isSkippedDir(entry.name))
29
+ continue;
30
+ const full = path.join(dir, entry.name);
31
+ if (entry.isDirectory()) {
32
+ results.push(...findSourceFiles(full));
33
+ }
34
+ else if (isEligibleSource(entry.name)) {
35
+ results.push(path.resolve(full));
36
+ }
37
+ }
38
+ return results.sort();
39
+ }
40
+ function resolveImportTarget(sourceFile, specifier) {
41
+ const dir = path.dirname(sourceFile);
42
+ const candidate = path.resolve(dir, specifier);
43
+ if (fs.existsSync(candidate))
44
+ return candidate;
45
+ if (specifier.endsWith('.js')) {
46
+ const tsCandidate = path.resolve(dir, specifier.slice(0, -3) + '.ts');
47
+ if (fs.existsSync(tsCandidate))
48
+ return tsCandidate;
49
+ }
50
+ return null;
51
+ }
52
+ export function extractLocalImports(filePath) {
53
+ if (!fs.existsSync(filePath))
54
+ return [];
55
+ const content = fs.readFileSync(filePath, 'utf-8');
56
+ const imports = [];
57
+ const regex = /(?:import|export)\s+(?:(?:[\s\S]*?from\s+)|)['"](\.[^'"]+)['"]|import\s*\(\s*['"](\.[^'"]+)['"]\s*\)/g;
58
+ for (const match of content.matchAll(regex)) {
59
+ const spec = match[1] ?? match[2];
60
+ if (spec) {
61
+ const resolved = resolveImportTarget(filePath, spec);
62
+ if (resolved && !imports.includes(resolved)) {
63
+ imports.push(resolved);
64
+ }
65
+ }
66
+ }
67
+ return imports.sort();
68
+ }
69
+ export function buildDependencyGraph(files) {
70
+ const graph = new Map();
71
+ for (const file of files) {
72
+ graph.set(file, extractLocalImports(file));
73
+ }
74
+ return graph;
75
+ }
76
+ export function detectDependencyCycles(graph) {
77
+ const cycles = [];
78
+ const visited = new Set();
79
+ const inStack = new Set();
80
+ const pathStack = [];
81
+ function dfs(node) {
82
+ visited.add(node);
83
+ inStack.add(node);
84
+ pathStack.push(node);
85
+ for (const neighbor of graph.get(node) ?? []) {
86
+ if (!visited.has(neighbor)) {
87
+ dfs(neighbor);
88
+ }
89
+ else if (inStack.has(neighbor)) {
90
+ const startIdx = pathStack.indexOf(neighbor);
91
+ if (startIdx !== -1) {
92
+ cycles.push([...pathStack.slice(startIdx), neighbor]);
93
+ }
94
+ }
95
+ }
96
+ inStack.delete(node);
97
+ pathStack.pop();
98
+ }
99
+ for (const node of graph.keys()) {
100
+ if (!visited.has(node))
101
+ dfs(node);
102
+ }
103
+ return cycles;
104
+ }
105
+ function hasLayerSegment(pathStr, segment) {
106
+ return new RegExp(`(^|[/\\\\])${segment}([/\\\\]|$)`, 'i').test(pathStr);
107
+ }
108
+ export function detectBoundaryViolations(graph, rules = DEFAULT_BOUNDARY_RULES) {
109
+ const violations = [];
110
+ for (const [file, imports] of graph.entries()) {
111
+ for (const rule of rules) {
112
+ if (hasLayerSegment(file, rule.fromLayer)) {
113
+ for (const imported of imports) {
114
+ for (const disallowed of rule.disallowImportsFrom) {
115
+ if (hasLayerSegment(imported, disallowed)) {
116
+ violations.push({
117
+ file,
118
+ importedFile: imported,
119
+ fromLayer: rule.fromLayer,
120
+ toLayer: disallowed
121
+ });
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ return violations;
129
+ }
130
+ /**
131
+ * Inspects a source tree for architectural boundary violations and circular dependency cycles.
132
+ *
133
+ * @param sourceDir - Directory containing source code (e.g. src/).
134
+ * @param rules - Architectural boundary rules.
135
+ * @returns Report summarizing cycle and boundary findings.
136
+ */
137
+ export function inspectBoundaries(sourceDir, rules = DEFAULT_BOUNDARY_RULES) {
138
+ const files = findSourceFiles(sourceDir);
139
+ const graph = buildDependencyGraph(files);
140
+ const cycles = detectDependencyCycles(graph);
141
+ const violations = detectBoundaryViolations(graph, rules);
142
+ return {
143
+ moduleCount: files.length,
144
+ cycles,
145
+ violations,
146
+ ok: cycles.length === 0 && violations.length === 0
147
+ };
148
+ }
149
+ export default {
150
+ DEFAULT_BOUNDARY_RULES,
151
+ findSourceFiles,
152
+ extractLocalImports,
153
+ buildDependencyGraph,
154
+ detectDependencyCycles,
155
+ detectBoundaryViolations,
156
+ inspectBoundaries
157
+ };
158
+ //# sourceMappingURL=boundaries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../src/boundaries.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,9 @@
4
4
  * Table-driven: every boolean flag is one Map entry, so adding a flag cannot
5
5
  * raise the complexity of the parser itself.
6
6
  */
7
+ export type CliCommand = 'scaffold' | 'check';
7
8
  export interface CliParsedOptions {
9
+ command: CliCommand;
8
10
  targetDir: string | null;
9
11
  force: boolean;
10
12
  noGit: boolean;
@@ -15,6 +17,7 @@ export interface CliParsedOptions {
15
17
  }
16
18
  export type BooleanFlagKey = 'force' | 'noGit' | 'dryRun' | 'silent';
17
19
  export declare function emptyOptions(): Omit<CliParsedOptions, 'terminal' | 'message'>;
20
+ export declare const CHECK_COMMANDS: Set<string>;
18
21
  export declare const BOOLEAN_FLAGS: Map<string, BooleanFlagKey>;
19
22
  export interface ArgOutcome {
20
23
  terminal: 'help' | 'version' | 'unknown-flag' | 'extra-arg' | null;
@@ -25,6 +28,7 @@ export declare function parseArgs(rawArgs: readonly string[]): CliParsedOptions;
25
28
  declare const _default: {
26
29
  emptyOptions: typeof emptyOptions;
27
30
  BOOLEAN_FLAGS: Map<string, BooleanFlagKey>;
31
+ CHECK_COMMANDS: Set<string>;
28
32
  applyArg: typeof applyArg;
29
33
  parseArgs: typeof parseArgs;
30
34
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cli-parse.d.ts","sourceRoot":"","sources":["../src/cli-parse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErE,wBAAgB,YAAY,IAAI,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC,CAE7E;AAED,eAAO,MAAM,aAAa,6BAQxB,CAAC;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC,EACrD,GAAG,EAAE,MAAM,GACV,UAAU,CAsBZ;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAStE;;;;;;;AAED,wBAAoE"}
1
+ {"version":3,"file":"cli-parse.d.ts","sourceRoot":"","sources":["../src/cli-parse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErE,wBAAgB,YAAY,IAAI,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC,CAS7E;AAED,eAAO,MAAM,cAAc,aAA0C,CAAC;AAEtE,eAAO,MAAM,aAAa,6BAQxB,CAAC;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC,EACrD,GAAG,EAAE,MAAM,GACV,UAAU,CA0BZ;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAStE;;;;;;;;AAED,wBAAoF"}
package/lib/cli-parse.js CHANGED
@@ -5,8 +5,16 @@
5
5
  * raise the complexity of the parser itself.
6
6
  */
7
7
  export function emptyOptions() {
8
- return { targetDir: null, force: false, noGit: false, dryRun: false, silent: false };
8
+ return {
9
+ command: 'scaffold',
10
+ targetDir: null,
11
+ force: false,
12
+ noGit: false,
13
+ dryRun: false,
14
+ silent: false
15
+ };
9
16
  }
17
+ export const CHECK_COMMANDS = new Set(['check', 'validate', 'audit']);
10
18
  export const BOOLEAN_FLAGS = new Map([
11
19
  ['-f', 'force'],
12
20
  ['--force', 'force'],
@@ -16,11 +24,17 @@ export const BOOLEAN_FLAGS = new Map([
16
24
  ['-s', 'silent'],
17
25
  ['--silent', 'silent']
18
26
  ]);
19
- export function applyArg(state, arg) {
27
+ function checkSpecialFlag(arg) {
20
28
  if (arg === '-h' || arg === '--help')
21
- return { terminal: 'help' };
29
+ return 'help';
22
30
  if (arg === '-v' || arg === '--version')
23
- return { terminal: 'version' };
31
+ return 'version';
32
+ return null;
33
+ }
34
+ export function applyArg(state, arg) {
35
+ const special = checkSpecialFlag(arg);
36
+ if (special !== null)
37
+ return { terminal: special };
24
38
  const flag = BOOLEAN_FLAGS.get(arg);
25
39
  if (flag !== undefined) {
26
40
  state[flag] = true;
@@ -32,6 +46,10 @@ export function applyArg(state, arg) {
32
46
  message: `Unknown argument '${arg}'. Run 'npx azcodr --help' for available options.`
33
47
  };
34
48
  }
49
+ if (state.targetDir === null && state.command === 'scaffold' && CHECK_COMMANDS.has(arg)) {
50
+ state.command = 'check';
51
+ return { terminal: null };
52
+ }
35
53
  if (!state.targetDir) {
36
54
  state.targetDir = arg;
37
55
  return { terminal: null };
@@ -51,5 +69,5 @@ export function parseArgs(rawArgs) {
51
69
  }
52
70
  return { ...state, terminal: null, message: undefined };
53
71
  }
54
- export default { emptyOptions, BOOLEAN_FLAGS, applyArg, parseArgs };
72
+ export default { emptyOptions, BOOLEAN_FLAGS, CHECK_COMMANDS, applyArg, parseArgs };
55
73
  //# sourceMappingURL=cli-parse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-parse.js","sourceRoot":"","sources":["../src/cli-parse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,UAAU,YAAY;IAC1B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACvF,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAyB;IAC3D,CAAC,IAAI,EAAE,OAAO,CAAC;IACf,CAAC,SAAS,EAAE,OAAO,CAAC;IACpB,CAAC,UAAU,EAAE,OAAO,CAAC;IACrB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,WAAW,EAAE,QAAQ,CAAC;IACvB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,UAAU,EAAE,QAAQ,CAAC;CACvB,CAAC,CAAC;AAOH,MAAM,UAAU,QAAQ,CACtB,KAAqD,EACrD,GAAW;IAEX,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAClE,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,WAAW;QAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACxE,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACnB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,QAAQ,EAAE,cAAc;YACxB,OAAO,EAAE,qBAAqB,GAAG,mDAAmD;SACrF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;QACtB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,wBAAwB,GAAG,mDAAmD;KACxF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAA0B;IAClD,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAC1D,CAAC;AAED,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"cli-parse.js","sourceRoot":"","sources":["../src/cli-parse.ts"],"names":[],"mappings":"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"}
package/lib/cli.d.ts CHANGED
@@ -13,6 +13,13 @@ export interface CliIo {
13
13
  cwd?: string;
14
14
  templateDir?: string;
15
15
  scaffold?: (options?: ScaffoldOptions) => ScaffoldResult;
16
+ validate?: (workspaceRoot: string, reporter?: any) => {
17
+ errors: number;
18
+ warnings: number;
19
+ } | Promise<{
20
+ errors: number;
21
+ warnings: number;
22
+ }>;
16
23
  }
17
24
  export interface NormalizedCliIo {
18
25
  out: (msg: string) => void;
@@ -25,6 +32,13 @@ export interface NormalizedCliIo {
25
32
  cwd: string;
26
33
  templateDir: string;
27
34
  scaffoldFn: (options?: ScaffoldOptions) => ScaffoldResult;
35
+ validateFn: (workspaceRoot: string, reporter?: any) => {
36
+ errors: number;
37
+ warnings: number;
38
+ } | Promise<{
39
+ errors: number;
40
+ warnings: number;
41
+ }>;
28
42
  }
29
43
  export declare function runCli(rawArgs?: string[], io?: CliIo): Promise<void | number>;
30
44
  export declare function main(): Promise<void>;
package/lib/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGrE,OAAO,EAAE,WAAW,EAA0C,MAAM,iBAAiB,CAAC;AAatF,wBAAgB,SAAS,CAAC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CAwBxE;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CAE3E;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,cAAc,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AA6HD,wBAAsB,MAAM,CAC1B,OAAO,GAAE,MAAM,EAA0B,EACzC,EAAE,GAAE,KAAU,GACb,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAqBxB;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAO1C;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;;;;;;;;AAEvB,wBAME"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIrE,OAAO,EAAE,WAAW,EAA0C,MAAM,iBAAiB,CAAC;AAatF,wBAAgB,SAAS,CAAC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CA2BxE;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CAE3E;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,cAAc,CAAC;IACzD,QAAQ,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,KAAK;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5I;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,cAAc,CAAC;IAC1D,UAAU,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,KAAK;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7I;AA8ID,wBAAsB,MAAM,CAC1B,OAAO,GAAE,MAAM,EAA0B,EACzC,EAAE,GAAE,KAAU,GACb,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAwBxB;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAO1C;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;;;;;;;;AAEvB,wBAME"}
package/lib/cli.js CHANGED
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import process from 'node:process';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { scaffold, getTemplateDir } from './scaffold.js';
6
+ import { validate, createSilentReporter } from './validate.js';
6
7
  import { parseArgs } from './cli-parse.js';
7
8
  import { askQuestion, resolveTargetDir, ensureWritableTarget } from './cli-target.js';
8
9
  const pkg = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf-8'));
@@ -13,9 +14,11 @@ Enterprise Multi-Tenant Architecture & Agentic Engineering Starter Template
13
14
 
14
15
  Usage:
15
16
  npx azcodr [directory] [options]
17
+ npx azcodr check [directory] [options]
16
18
 
17
19
  Commands:
18
- [directory] Scaffold azcodr template into directory (default: current directory)
20
+ [directory] Scaffold azcodr template into directory (default: current directory)
21
+ check [directory] Validate architecture and governance rules (alias: validate, audit)
19
22
 
20
23
  Options:
21
24
  -d, --dry-run Simulate scaffolding without modifying filesystem
@@ -27,16 +30,32 @@ Options:
27
30
 
28
31
  Examples:
29
32
  npx azcodr my-project
33
+ npx azcodr check
34
+ npx azcodr check ./existing-repo
30
35
  npx azcodr . --dry-run
31
- npx azcodr . --force
32
36
  `);
33
37
  }
34
38
  export function printVersion(out = console.log) {
35
39
  out(pkg.version);
36
40
  }
37
- function normalizeIo(io = {}) {
38
- const { out = console.log, err = console.error, exit = process.exit, stdin = process.stdin, stdout = process.stdout, cwd = process.cwd(), templateDir = getTemplateDir(), scaffold: scaffoldFn = scaffold } = io;
39
- return { out, err, exit, stdin, stdout, cwd, templateDir, scaffoldFn };
41
+ function defaultStreams(io) {
42
+ return {
43
+ out: io.out ?? console.log,
44
+ err: io.err ?? console.error,
45
+ exit: io.exit ?? process.exit,
46
+ stdin: io.stdin ?? process.stdin,
47
+ stdout: io.stdout ?? process.stdout
48
+ };
49
+ }
50
+ function normalizeIo(io) {
51
+ const streams = defaultStreams(io);
52
+ return {
53
+ ...streams,
54
+ cwd: io.cwd ?? process.cwd(),
55
+ templateDir: io.templateDir ?? getTemplateDir(),
56
+ scaffoldFn: io.scaffold ?? scaffold,
57
+ validateFn: io.validate ?? validate
58
+ };
40
59
  }
41
60
  function outBanner(fullIo) {
42
61
  fullIo.out('\nšŸš€ azcodr - Enterprise Multi-Tenant Architecture & Agentic Engineering\n');
@@ -54,6 +73,12 @@ function handleTerminal(parsed, io) {
54
73
  err(`āŒ Error: ${parsed.message}`);
55
74
  return exit(1);
56
75
  }
76
+ async function handleCheckCommand(parsed, fullIo) {
77
+ const checkDir = parsed.targetDir ? path.resolve(fullIo.cwd, parsed.targetDir) : fullIo.cwd;
78
+ const reporter = parsed.silent ? createSilentReporter() : undefined;
79
+ const result = await fullIo.validateFn(checkDir, reporter);
80
+ return fullIo.exit(result.errors === 0 ? 0 : 1);
81
+ }
57
82
  function reportDryRun(result, out) {
58
83
  for (const action of result.actions) {
59
84
  out(` [preview] ${action}`);
@@ -135,6 +160,9 @@ export async function runCli(rawArgs = process.argv.slice(2), io = {}) {
135
160
  const parsed = parseArgs(rawArgs);
136
161
  if (parsed.terminal !== null)
137
162
  return handleTerminal(parsed, fullIo);
163
+ if (parsed.command === 'check') {
164
+ return handleCheckCommand(parsed, fullIo);
165
+ }
138
166
  if (!parsed.silent) {
139
167
  outBanner(fullIo);
140
168
  }