@aiready/cli 0.12.20 → 0.13.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 (55) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-lint.log +24 -5
  3. package/.turbo/turbo-test.log +66 -85
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/clover.xml +865 -0
  7. package/coverage/coverage-final.json +15 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +146 -0
  10. package/coverage/prettify.css +1 -0
  11. package/coverage/prettify.js +2 -0
  12. package/coverage/sort-arrow-sprite.png +0 -0
  13. package/coverage/sorter.js +210 -0
  14. package/coverage/src/commands/agent-grounding.ts.html +271 -0
  15. package/coverage/src/commands/ai-signal-clarity.ts.html +253 -0
  16. package/coverage/src/commands/change-amplification.ts.html +94 -0
  17. package/coverage/src/commands/consistency.ts.html +781 -0
  18. package/coverage/src/commands/context.ts.html +871 -0
  19. package/coverage/src/commands/deps-health.ts.html +280 -0
  20. package/coverage/src/commands/doc-drift.ts.html +271 -0
  21. package/coverage/src/commands/index.html +281 -0
  22. package/coverage/src/commands/patterns.ts.html +745 -0
  23. package/coverage/src/commands/scan.ts.html +1393 -0
  24. package/coverage/src/commands/testability.ts.html +304 -0
  25. package/coverage/src/commands/upload.ts.html +466 -0
  26. package/coverage/src/commands/visualize.ts.html +1027 -0
  27. package/coverage/src/index.html +116 -0
  28. package/coverage/src/index.ts.html +1372 -0
  29. package/coverage/src/utils/helpers.ts.html +559 -0
  30. package/coverage/src/utils/index.html +116 -0
  31. package/dist/cli.js +259 -16
  32. package/dist/cli.mjs +259 -15
  33. package/package.json +13 -12
  34. package/src/.aiready/aiready-report-20260308-174006.json +29526 -0
  35. package/src/.aiready/aiready-report-20260310-153526.json +34441 -0
  36. package/src/.aiready/aiready-report-20260310-155555.json +34441 -0
  37. package/src/.aiready/aiready-report-20260310-162403.json +34441 -0
  38. package/src/.aiready/aiready-report-20260310-183821.json +34448 -0
  39. package/src/__tests__/unified.test.ts +95 -0
  40. package/src/cli.ts +86 -0
  41. package/src/commands/__tests__/agent-grounding.test.ts +24 -0
  42. package/src/commands/__tests__/ai-signal-clarity.test.ts +32 -0
  43. package/src/commands/__tests__/consistency.test.ts +97 -0
  44. package/src/commands/__tests__/deps-health.test.ts +26 -0
  45. package/src/commands/__tests__/doc-drift.test.ts +26 -0
  46. package/src/commands/__tests__/extra-commands.test.ts +177 -0
  47. package/src/commands/__tests__/scan.test.ts +151 -0
  48. package/src/commands/__tests__/testability.test.ts +36 -0
  49. package/src/commands/__tests__/upload.test.ts +51 -0
  50. package/src/commands/__tests__/visualize.test.ts +82 -0
  51. package/src/commands/clawmart.ts +162 -0
  52. package/src/commands/index.ts +8 -0
  53. package/src/commands/scan.ts +45 -13
  54. package/src/utils/__tests__/helpers.test.ts +35 -0
  55. package/vitest.config.ts +20 -0
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { analyzeUnified, scoreUnified, generateUnifiedSummary } from '../index';
3
+ import { ToolRegistry, ToolName } from '@aiready/core';
4
+
5
+ vi.mock('@aiready/core', async () => {
6
+ const actual = await vi.importActual('@aiready/core');
7
+ return {
8
+ ...actual,
9
+ initializeParsers: vi.fn().mockResolvedValue(undefined),
10
+ calculateOverallScore: vi
11
+ .fn()
12
+ .mockReturnValue({ overall: 85, breakdown: [] }),
13
+ calculateTokenBudget: vi.fn().mockReturnValue({}),
14
+ };
15
+ });
16
+
17
+ describe('Unified CLI logic', () => {
18
+ beforeEach(() => {
19
+ vi.clearAllMocks();
20
+ });
21
+
22
+ describe('analyzeUnified', () => {
23
+ it('should run multiple tools and aggregate results', async () => {
24
+ const mockProvider1 = {
25
+ id: 'tool1',
26
+ analyze: vi.fn().mockResolvedValue({
27
+ results: [{ fileName: 'f1.ts', issues: [{}, {}] }],
28
+ summary: { totalFiles: 1 },
29
+ }),
30
+ score: vi.fn(),
31
+ };
32
+
33
+ vi.spyOn(ToolRegistry, 'find').mockImplementation((id) => {
34
+ if (id === 'tool1') return mockProvider1 as any;
35
+ return null;
36
+ });
37
+
38
+ const options = {
39
+ rootDir: '.',
40
+ tools: ['tool1'],
41
+ };
42
+
43
+ const result = await analyzeUnified(options as any);
44
+ expect(result.summary.totalIssues).toBe(2);
45
+ expect(result.summary.toolsRun).toContain('tool1');
46
+ });
47
+ });
48
+
49
+ describe('scoreUnified', () => {
50
+ it('should score results from multiple tools', async () => {
51
+ const mockProvider = {
52
+ id: 'tool1',
53
+ score: vi.fn().mockReturnValue({ score: 80 }),
54
+ };
55
+ vi.spyOn(ToolRegistry, 'get').mockReturnValue(mockProvider as any);
56
+
57
+ const results = {
58
+ summary: { toolsRun: ['tool1'] },
59
+ tool1: { results: [] },
60
+ };
61
+
62
+ const score = await scoreUnified(results as any, { rootDir: '.' } as any);
63
+ expect(score.overall).toBe(85); // From mocked calculateOverallScore
64
+ expect(mockProvider.score).toHaveBeenCalled();
65
+ });
66
+
67
+ it('should handle empty tools', async () => {
68
+ const results = { summary: { toolsRun: [] } };
69
+ const score = await scoreUnified(results as any, { rootDir: '.' } as any);
70
+ expect(score.overall).toBe(0);
71
+ });
72
+ });
73
+
74
+ describe('generateUnifiedSummary', () => {
75
+ it('should generate human-readable summary', () => {
76
+ const result = {
77
+ summary: {
78
+ toolsRun: ['tool1'],
79
+ totalIssues: 5,
80
+ executionTime: 1200,
81
+ },
82
+ tool1: { results: [{ issues: [{}, {}] }] },
83
+ };
84
+
85
+ vi.spyOn(ToolRegistry, 'getAll').mockReturnValue([
86
+ { id: 'tool1' },
87
+ ] as any);
88
+
89
+ const summary = generateUnifiedSummary(result as any);
90
+ expect(summary).toContain('AIReady Analysis Complete');
91
+ expect(summary).toContain('Total issues found: 5');
92
+ expect(summary).toContain('1.20s');
93
+ });
94
+ });
95
+ });
package/src/cli.ts CHANGED
@@ -16,8 +16,15 @@ import {
16
16
  visualizeHelpText,
17
17
  visualiseHelpText,
18
18
  changeAmplificationAction,
19
+ testabilityAction,
19
20
  uploadAction,
20
21
  uploadHelpText,
22
+ clawmartMeAction,
23
+ clawmartListingsAction,
24
+ clawmartCreateAction,
25
+ clawmartUploadAction,
26
+ clawmartDownloadAction,
27
+ clawmartHelpText,
21
28
  } from './commands';
22
29
 
23
30
  const getDirname = () => {
@@ -279,6 +286,20 @@ program
279
286
  await changeAmplificationAction(directory, options);
280
287
  });
281
288
 
289
+ // Testability command
290
+ program
291
+ .command('testability')
292
+ .description('Analyze test coverage and AI readiness')
293
+ .argument('[directory]', 'Directory to analyze', '.')
294
+ .option('--min-coverage <ratio>', 'Minimum acceptable coverage ratio', '0.3')
295
+ .option('--include <patterns>', 'File patterns to include (comma-separated)')
296
+ .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
297
+ .option('-o, --output <format>', 'Output format: console, json', 'console')
298
+ .option('--output-file <path>', 'Output file path (for json)')
299
+ .action(async (directory, options) => {
300
+ await testabilityAction(directory, options);
301
+ });
302
+
282
303
  // Upload command - Upload report JSON to platform
283
304
  program
284
305
  .command('upload')
@@ -292,4 +313,69 @@ program
292
313
  await uploadAction(file, options);
293
314
  });
294
315
 
316
+ // ClawMart commands
317
+ const clawmart = program
318
+ .command('clawmart')
319
+ .description('Manage ClawMart personas and skills')
320
+ .addHelpText('after', clawmartHelpText);
321
+
322
+ clawmart
323
+ .command('me')
324
+ .description('Show my ClawMart creator profile')
325
+ .option('--api-key <key>', 'ClawMart API key')
326
+ .option('--server <url>', 'Custom ClawMart API server')
327
+ .action(async (options) => {
328
+ await clawmartMeAction(options);
329
+ });
330
+
331
+ clawmart
332
+ .command('listings')
333
+ .description('Show my ClawMart listings')
334
+ .option('-q, --query <string>', 'Search query')
335
+ .option('-t, --type <type>', 'Filter by type: skill, persona')
336
+ .option('-l, --limit <number>', 'Limit results', '10')
337
+ .option('--api-key <key>', 'ClawMart API key')
338
+ .option('--server <url>', 'Custom ClawMart API server')
339
+ .action(async (options) => {
340
+ await clawmartListingsAction(options);
341
+ });
342
+
343
+ clawmart
344
+ .command('create')
345
+ .description('Create a new listing on ClawMart')
346
+ .requiredOption('--name <string>', 'Listing name')
347
+ .requiredOption('--tagline <string>', 'Short tagline')
348
+ .option('--about <string>', 'Full description')
349
+ .option('--category <string>', 'Category', 'Utility')
350
+ .option('--capabilities <string>', 'Comma-separated list of capabilities')
351
+ .option('--price <number>', 'Price in USD', '0')
352
+ .option('--type <type>', 'Product type: skill, persona', 'skill')
353
+ .option('--api-key <key>', 'ClawMart API key')
354
+ .option('--server <url>', 'Custom ClawMart API server')
355
+ .action(async (options) => {
356
+ await clawmartCreateAction(options);
357
+ });
358
+
359
+ clawmart
360
+ .command('upload')
361
+ .description('Upload content to a listing')
362
+ .argument('<id>', 'Listing ID')
363
+ .argument('<files...>', 'Files or directories to upload')
364
+ .option('--api-key <key>', 'ClawMart API key')
365
+ .option('--server <url>', 'Custom ClawMart API server')
366
+ .action(async (id, files, options) => {
367
+ await clawmartUploadAction(id, files as string[], options);
368
+ });
369
+
370
+ clawmart
371
+ .command('download')
372
+ .description('Download a package from ClawMart')
373
+ .argument('<idOrSlug>', 'Listing ID or Slug')
374
+ .option('--outDir <path>', 'Output directory')
375
+ .option('--api-key <key>', 'ClawMart API key')
376
+ .option('--server <url>', 'Custom ClawMart API server')
377
+ .action(async (idOrSlug, options) => {
378
+ await clawmartDownloadAction(idOrSlug, options);
379
+ });
380
+
295
381
  program.parse();
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { agentGroundingAction } from '../agent-grounding';
3
+
4
+ vi.mock('@aiready/agent-grounding', () => ({
5
+ analyzeAgentGrounding: vi.fn().mockResolvedValue({
6
+ summary: { score: 75, rating: 'good', dimensions: { apiClarityScore: 60 } },
7
+ results: [],
8
+ }),
9
+ calculateGroundingScore: vi.fn().mockReturnValue({ score: 75 }),
10
+ }));
11
+
12
+ vi.mock('@aiready/core', () => ({
13
+ loadConfig: vi.fn().mockResolvedValue({}),
14
+ mergeConfigWithDefaults: vi
15
+ .fn()
16
+ .mockImplementation((c, d) => ({ ...d, ...c })),
17
+ }));
18
+
19
+ describe('Agent Grounding CLI Action', () => {
20
+ it('should run analysis and return scoring', async () => {
21
+ const result = await agentGroundingAction('.', { output: 'json' });
22
+ expect(result?.score).toBe(75);
23
+ });
24
+ });
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { aiSignalClarityAction } from '../ai-signal-clarity';
3
+
4
+ vi.mock('@aiready/ai-signal-clarity', () => ({
5
+ analyzeAiSignalClarity: vi.fn().mockResolvedValue({
6
+ summary: {
7
+ score: 85,
8
+ rating: 'low',
9
+ topRisk: 'none',
10
+ totalSignals: 0,
11
+ criticalSignals: 0,
12
+ majorSignals: 0,
13
+ minorSignals: 0,
14
+ },
15
+ results: [],
16
+ }),
17
+ calculateAiSignalClarityScore: vi.fn().mockReturnValue({ score: 85 }),
18
+ }));
19
+
20
+ vi.mock('@aiready/core', () => ({
21
+ loadConfig: vi.fn().mockResolvedValue({}),
22
+ mergeConfigWithDefaults: vi
23
+ .fn()
24
+ .mockImplementation((c, d) => ({ ...d, ...c })),
25
+ }));
26
+
27
+ describe('AI Signal Clarity CLI Action', () => {
28
+ it('should run analysis and return scoring', async () => {
29
+ const result = await aiSignalClarityAction('.', { output: 'json' });
30
+ expect(result?.score).toBe(85);
31
+ });
32
+ });
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { consistencyAction } from '../consistency';
3
+ import * as core from '@aiready/core';
4
+ import * as fs from 'fs';
5
+
6
+ vi.mock('@aiready/core', async () => {
7
+ const actual = await vi.importActual('@aiready/core');
8
+ return {
9
+ ...actual,
10
+ loadMergedConfig: vi.fn(),
11
+ handleJSONOutput: vi.fn(),
12
+ handleCLIError: vi.fn(),
13
+ getElapsedTime: vi.fn().mockReturnValue('1.0'),
14
+ resolveOutputPath: vi.fn().mockReturnValue('report.json'),
15
+ formatToolScore: vi.fn().mockReturnValue('Score: 80'),
16
+ };
17
+ });
18
+
19
+ vi.mock('fs', async () => {
20
+ const actual = await vi.importActual('fs');
21
+ return {
22
+ ...actual,
23
+ writeFileSync: vi.fn(),
24
+ };
25
+ });
26
+
27
+ vi.mock('@aiready/consistency', () => ({
28
+ analyzeConsistency: vi.fn().mockResolvedValue({
29
+ results: [
30
+ {
31
+ fileName: 'f1.ts',
32
+ issues: [
33
+ {
34
+ category: 'naming',
35
+ severity: 'major',
36
+ message: 'Bad name',
37
+ location: { file: 'f1.ts', line: 1 },
38
+ },
39
+ ],
40
+ },
41
+ ],
42
+ summary: {
43
+ filesAnalyzed: 1,
44
+ totalIssues: 1,
45
+ namingIssues: 1,
46
+ patternIssues: 0,
47
+ },
48
+ recommendations: ['Fix names'],
49
+ }),
50
+ calculateConsistencyScore: vi.fn().mockReturnValue({ score: 80 }),
51
+ }));
52
+
53
+ import { analyzeConsistency } from '@aiready/consistency';
54
+
55
+ describe('Consistency CLI Action', () => {
56
+ let consoleSpy: any;
57
+
58
+ beforeEach(() => {
59
+ vi.clearAllMocks();
60
+ consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
61
+ vi.mocked(core.loadMergedConfig).mockResolvedValue({
62
+ output: { format: 'console' },
63
+ });
64
+ });
65
+
66
+ it('runs consistency analysis and outputs to console', async () => {
67
+ await consistencyAction('.', {});
68
+ expect(analyzeConsistency).toHaveBeenCalled();
69
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Summary'));
70
+ expect(consoleSpy).toHaveBeenCalledWith(
71
+ expect.stringContaining('Naming Issues')
72
+ );
73
+ });
74
+
75
+ it('supports JSON output', async () => {
76
+ vi.mocked(core.loadMergedConfig).mockResolvedValue({
77
+ output: { format: 'json' },
78
+ });
79
+ await consistencyAction('.', {});
80
+ expect(core.handleJSONOutput).toHaveBeenCalled();
81
+ });
82
+
83
+ it('supports Markdown output', async () => {
84
+ vi.mocked(core.loadMergedConfig).mockResolvedValue({
85
+ output: { format: 'markdown' },
86
+ });
87
+ await consistencyAction('.', {});
88
+ expect(fs.writeFileSync).toHaveBeenCalled();
89
+ });
90
+
91
+ it('calculates score if requested', async () => {
92
+ await consistencyAction('.', { score: true });
93
+ expect(consoleSpy).toHaveBeenCalledWith(
94
+ expect.stringContaining('AI Readiness Score')
95
+ );
96
+ });
97
+ });
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { depsHealthAction } from '../deps-health';
3
+
4
+ vi.mock('@aiready/deps', () => ({
5
+ analyzeDeps: vi.fn().mockResolvedValue({
6
+ summary: { score: 90, rating: 'excellent' },
7
+ rawData: {},
8
+ recommendations: [],
9
+ issues: [],
10
+ }),
11
+ }));
12
+
13
+ vi.mock('@aiready/core', () => ({
14
+ loadConfig: vi.fn().mockResolvedValue({}),
15
+ mergeConfigWithDefaults: vi
16
+ .fn()
17
+ .mockImplementation((c, d) => ({ ...d, ...c })),
18
+ }));
19
+
20
+ describe('Deps Health CLI Action', () => {
21
+ it('should run analysis and return scoring', async () => {
22
+ const result = await depsHealthAction('.', { output: 'json' });
23
+ expect(result?.toolName).toBe('dependency-health');
24
+ expect(result?.score).toBe(90);
25
+ });
26
+ });
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { docDriftAction } from '../doc-drift';
3
+
4
+ vi.mock('@aiready/doc-drift', () => ({
5
+ analyzeDocDrift: vi.fn().mockResolvedValue({
6
+ summary: { score: 20, rating: 'low' },
7
+ rawData: {},
8
+ recommendations: ['Update docs'],
9
+ issues: [],
10
+ }),
11
+ }));
12
+
13
+ vi.mock('@aiready/core', () => ({
14
+ loadConfig: vi.fn().mockResolvedValue({}),
15
+ mergeConfigWithDefaults: vi
16
+ .fn()
17
+ .mockImplementation((c, d) => ({ ...d, ...c })),
18
+ }));
19
+
20
+ describe('Doc Drift CLI Action', () => {
21
+ it('should run analysis and return scoring', async () => {
22
+ const result = await docDriftAction('.', { output: 'json' });
23
+ expect(result?.toolName).toBe('doc-drift');
24
+ expect(result?.score).toBe(20);
25
+ });
26
+ });
@@ -0,0 +1,177 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { changeAmplificationAction } from '../change-amplification';
3
+ import { agentGroundingAction } from '../agent-grounding';
4
+ import { aiSignalClarityAction } from '../ai-signal-clarity';
5
+ import { docDriftAction } from '../doc-drift';
6
+ import { testabilityAction } from '../testability';
7
+ import { depsHealthAction } from '../deps-health';
8
+ import { patternsAction } from '../patterns';
9
+ import { contextAction } from '../context';
10
+ import * as core from '@aiready/core';
11
+
12
+ vi.mock('@aiready/core', async () => {
13
+ const actual = await vi.importActual('@aiready/core');
14
+ return {
15
+ ...actual,
16
+ loadConfig: vi.fn().mockResolvedValue({}),
17
+ mergeConfigWithDefaults: vi
18
+ .fn()
19
+ .mockImplementation((c, d) => ({ ...d, ...c })),
20
+ loadMergedConfig: vi.fn().mockResolvedValue({
21
+ rootDir: '.',
22
+ output: { format: 'console' },
23
+ checkNaming: true,
24
+ checkPatterns: true,
25
+ }),
26
+ handleJSONOutput: vi.fn(),
27
+ handleCLIError: vi.fn(),
28
+ getElapsedTime: vi.fn().mockReturnValue('1.0'),
29
+ resolveOutputPath: vi.fn().mockReturnValue('report.json'),
30
+ formatToolScore: vi.fn().mockReturnValue('Score: 80'),
31
+ };
32
+ });
33
+
34
+ // Mock all spokes
35
+ vi.mock('@aiready/change-amplification', () => ({
36
+ analyzeChangeAmplification: vi
37
+ .fn()
38
+ .mockResolvedValue({
39
+ results: [],
40
+ summary: { rating: 'contained' },
41
+ recommendations: [],
42
+ }),
43
+ calculateChangeAmplificationScore: vi.fn().mockReturnValue({ score: 80 }),
44
+ }));
45
+ vi.mock('@aiready/agent-grounding', () => ({
46
+ analyzeAgentGrounding: vi.fn().mockResolvedValue({
47
+ results: [],
48
+ summary: {
49
+ rating: 'grounded',
50
+ dimensions: { structure: 80, metadata: 80 },
51
+ },
52
+ recommendations: [],
53
+ }),
54
+ calculateGroundingScore: vi.fn().mockReturnValue({ score: 80 }),
55
+ }));
56
+ vi.mock('@aiready/ai-signal-clarity', () => ({
57
+ analyzeAiSignalClarity: vi.fn().mockResolvedValue({
58
+ results: [],
59
+ summary: { rating: 'clear', topRisk: 'none' },
60
+ recommendations: [],
61
+ }),
62
+ calculateAiSignalClarityScore: vi.fn().mockReturnValue({ score: 80 }),
63
+ }));
64
+ vi.mock('@aiready/doc-drift', () => ({
65
+ analyzeDocDrift: vi.fn().mockResolvedValue({
66
+ results: [],
67
+ summary: { rating: 'fresh' },
68
+ issues: [],
69
+ rawData: {},
70
+ recommendations: [],
71
+ }),
72
+ calculateDocDriftScore: vi.fn().mockReturnValue({ score: 80 }),
73
+ }));
74
+ vi.mock('@aiready/testability', () => ({
75
+ analyzeTestability: vi.fn().mockResolvedValue({
76
+ results: [],
77
+ summary: { rating: 'testable', aiChangeSafetyRating: 'safe' },
78
+ rawData: {},
79
+ recommendations: [],
80
+ }),
81
+ calculateTestabilityScore: vi.fn().mockReturnValue({ score: 80 }),
82
+ }));
83
+ vi.mock('@aiready/deps', () => ({
84
+ analyzeDeps: vi.fn().mockResolvedValue({
85
+ results: [],
86
+ summary: { packagesAnalyzed: 0, filesAnalyzed: 0, rating: 'healthy' },
87
+ issues: [],
88
+ rawData: {},
89
+ recommendations: [],
90
+ }),
91
+ calculateDepsScore: vi.fn().mockReturnValue({ score: 80 }),
92
+ }));
93
+ vi.mock('@aiready/pattern-detect', () => ({
94
+ analyzePatterns: vi
95
+ .fn()
96
+ .mockResolvedValue({
97
+ results: [],
98
+ summary: { totalPatterns: 0 },
99
+ config: {},
100
+ }),
101
+ generateSummary: vi
102
+ .fn()
103
+ .mockReturnValue({
104
+ totalPatterns: 0,
105
+ totalTokenCost: 0,
106
+ patternsByType: {},
107
+ topDuplicates: [],
108
+ }),
109
+ getSmartDefaults: vi.fn().mockResolvedValue({}),
110
+ }));
111
+ vi.mock('@aiready/context-analyzer', () => ({
112
+ analyzeContext: vi.fn().mockResolvedValue([]),
113
+ generateSummary: vi
114
+ .fn()
115
+ .mockReturnValue({
116
+ score: 80,
117
+ rating: 'good',
118
+ totalFiles: 0,
119
+ totalTokens: 0,
120
+ avgImportDepth: 0,
121
+ maxImportDepth: 0,
122
+ avgFragmentation: 0,
123
+ criticalIssues: 0,
124
+ majorIssues: 0,
125
+ totalPotentialSavings: 0,
126
+ }),
127
+ getSmartDefaults: vi.fn().mockResolvedValue({}),
128
+ }));
129
+
130
+ describe('Extra CLI Actions', () => {
131
+ let consoleSpy: any;
132
+
133
+ beforeEach(() => {
134
+ vi.clearAllMocks();
135
+ consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
136
+ });
137
+
138
+ it('runs change-amplification', async () => {
139
+ await changeAmplificationAction('.', {});
140
+ expect(consoleSpy).toHaveBeenCalled();
141
+ });
142
+
143
+ it('runs agent-grounding', async () => {
144
+ await agentGroundingAction('.', {});
145
+ expect(consoleSpy).toHaveBeenCalled();
146
+ });
147
+
148
+ it('runs ai-signal-clarity', async () => {
149
+ await aiSignalClarityAction('.', {});
150
+ expect(consoleSpy).toHaveBeenCalled();
151
+ });
152
+
153
+ it('runs doc-drift', async () => {
154
+ await docDriftAction('.', {});
155
+ expect(consoleSpy).toHaveBeenCalled();
156
+ });
157
+
158
+ it('runs testability', async () => {
159
+ await testabilityAction('.', {});
160
+ expect(consoleSpy).toHaveBeenCalled();
161
+ });
162
+
163
+ it('runs deps-health', async () => {
164
+ await depsHealthAction('.', {});
165
+ expect(consoleSpy).toHaveBeenCalled();
166
+ });
167
+
168
+ it('runs patterns', async () => {
169
+ await patternsAction('.', {});
170
+ expect(consoleSpy).toHaveBeenCalled();
171
+ });
172
+
173
+ it('runs context', async () => {
174
+ await contextAction('.', {});
175
+ expect(consoleSpy).toHaveBeenCalled();
176
+ });
177
+ });