@aiready/cli 0.3.3 → 0.3.5

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.
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > @aiready/cli@0.3.3 build /Users/pengcao/projects/aiready/packages/cli
3
+ > @aiready/cli@0.3.5 build /Users/pengcao/projects/aiready/packages/cli
4
4
  > tsup src/index.ts src/cli.ts --format cjs,esm --dts
5
5
 
6
6
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,15 +9,15 @@
9
9
  CLI Target: es2020
10
10
  CJS Build start
11
11
  ESM Build start
12
+ CJS dist/cli.js 10.43 KB
12
13
  CJS dist/index.js 2.57 KB
13
- CJS dist/cli.js 11.02 KB
14
- CJS ⚡️ Build success in 56ms
15
- ESM dist/chunk-KZKXZKES.mjs 1.45 KB
16
- ESM dist/cli.mjs 8.12 KB
14
+ CJS ⚡️ Build success in 60ms
17
15
  ESM dist/index.mjs 138.00 B
18
- ESM ⚡️ Build success in 56ms
16
+ ESM dist/chunk-KZKXZKES.mjs 1.45 KB
17
+ ESM dist/cli.mjs 7.58 KB
18
+ ESM ⚡️ Build success in 60ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 513ms
20
+ DTS ⚡️ Build success in 523ms
21
21
  DTS dist/cli.d.ts 20.00 B
22
22
  DTS dist/index.d.ts 731.00 B
23
23
  DTS dist/cli.d.mts 20.00 B
package/dist/cli.js CHANGED
@@ -78,18 +78,16 @@ function generateUnifiedSummary(result) {
78
78
 
79
79
  // src/cli.ts
80
80
  var import_chalk = __toESM(require("chalk"));
81
- var import_fs = require("fs");
82
81
  var import_path = require("path");
83
82
  var import_core = require("@aiready/core");
84
- var import_fs2 = require("fs");
85
- var packageJson = JSON.parse((0, import_fs2.readFileSync)((0, import_path.join)(__dirname, "../package.json"), "utf8"));
83
+ var import_fs = require("fs");
84
+ var packageJson = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(__dirname, "../package.json"), "utf8"));
86
85
  var program = new import_commander.Command();
87
86
  program.name("aiready").description("AIReady - Unified AI-readiness analysis tools").version(packageJson.version).addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings");
88
87
  program.command("scan").description("Run unified analysis on a codebase").argument("<directory>", "Directory to analyze").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context)", "patterns,context").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
89
88
  console.log(import_chalk.default.blue("\u{1F680} Starting AIReady unified analysis...\n"));
90
89
  const startTime = Date.now();
91
90
  try {
92
- const config = (0, import_core.loadConfig)(directory);
93
91
  const defaults = {
94
92
  tools: ["patterns", "context"],
95
93
  include: void 0,
@@ -99,17 +97,15 @@ program.command("scan").description("Run unified analysis on a codebase").argume
99
97
  file: void 0
100
98
  }
101
99
  };
102
- const mergedConfig = (0, import_core.mergeConfigWithDefaults)(config, defaults);
103
- const finalOptions = {
104
- rootDir: directory,
105
- tools: options.tools ? options.tools.split(",").map((t) => t.trim()) : mergedConfig.tools,
106
- include: options.include?.split(",") || mergedConfig.include,
107
- exclude: options.exclude?.split(",") || mergedConfig.exclude
108
- };
100
+ const finalOptions = (0, import_core.loadMergedConfig)(directory, defaults, {
101
+ tools: options.tools ? options.tools.split(",").map((t) => t.trim()) : void 0,
102
+ include: options.include?.split(","),
103
+ exclude: options.exclude?.split(",")
104
+ });
109
105
  const results = await analyzeUnified(finalOptions);
110
- const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
111
- const outputFormat = options.output || mergedConfig.output?.format || "console";
112
- const outputFile = options.outputFile || mergedConfig.output?.file;
106
+ const elapsedTime = (0, import_core.getElapsedTime)(startTime);
107
+ const outputFormat = options.output || finalOptions.output?.format || "console";
108
+ const outputFile = options.outputFile || finalOptions.output?.file;
113
109
  if (outputFormat === "json") {
114
110
  const outputData = {
115
111
  ...results,
@@ -118,28 +114,24 @@ program.command("scan").description("Run unified analysis on a codebase").argume
118
114
  executionTime: parseFloat(elapsedTime)
119
115
  }
120
116
  };
121
- if (outputFile) {
122
- (0, import_fs.writeFileSync)(outputFile, JSON.stringify(outputData, null, 2));
123
- console.log(import_chalk.default.green(`\u2705 Results saved to ${outputFile}`));
124
- } else {
125
- console.log(JSON.stringify(outputData, null, 2));
126
- }
117
+ (0, import_core.handleJSONOutput)(outputData, outputFile, `\u2705 Results saved to ${outputFile}`);
127
118
  } else {
128
119
  console.log(generateUnifiedSummary(results));
129
120
  }
130
121
  } catch (error) {
131
- console.error(import_chalk.default.red("\u274C Analysis failed:"), error);
132
- process.exit(1);
122
+ (0, import_core.handleCLIError)(error, "Analysis");
133
123
  }
134
124
  });
135
- program.command("patterns").description("Run pattern detection analysis").argument("<directory>", "Directory to analyze").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
125
+ program.command("patterns").description("Run pattern detection analysis").argument("<directory>", "Directory to analyze").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
136
126
  console.log(import_chalk.default.blue("\u{1F50D} Analyzing patterns...\n"));
137
127
  const startTime = Date.now();
138
128
  try {
139
- const config = (0, import_core.loadConfig)(directory);
140
129
  const defaults = {
141
130
  minSimilarity: 0.4,
142
131
  minLines: 5,
132
+ maxCandidatesPerBlock: 100,
133
+ minSharedTokens: 8,
134
+ useSmartDefaults: true,
143
135
  include: void 0,
144
136
  exclude: void 0,
145
137
  output: {
@@ -147,46 +139,41 @@ program.command("patterns").description("Run pattern detection analysis").argume
147
139
  file: void 0
148
140
  }
149
141
  };
150
- const mergedConfig = (0, import_core.mergeConfigWithDefaults)(config, defaults);
151
- const finalOptions = {
152
- rootDir: directory,
153
- minSimilarity: options.similarity ? parseFloat(options.similarity) : mergedConfig.minSimilarity,
154
- minLines: options.minLines ? parseInt(options.minLines) : mergedConfig.minLines,
155
- include: options.include?.split(",") || mergedConfig.include,
156
- exclude: options.exclude?.split(",") || mergedConfig.exclude
157
- };
142
+ const finalOptions = (0, import_core.loadMergedConfig)(directory, defaults, {
143
+ minSimilarity: options.similarity ? parseFloat(options.similarity) : void 0,
144
+ minLines: options.minLines ? parseInt(options.minLines) : void 0,
145
+ maxCandidatesPerBlock: options.maxCandidates ? parseInt(options.maxCandidates) : void 0,
146
+ minSharedTokens: options.minSharedTokens ? parseInt(options.minSharedTokens) : void 0,
147
+ // If --full-scan is specified, don't use smart defaults
148
+ useSmartDefaults: !options.fullScan,
149
+ include: options.include?.split(","),
150
+ exclude: options.exclude?.split(",")
151
+ });
158
152
  const { analyzePatterns: analyzePatterns2, generateSummary } = await import("@aiready/pattern-detect");
159
153
  const { results } = await analyzePatterns2(finalOptions);
160
- const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
154
+ const elapsedTime = (0, import_core.getElapsedTime)(startTime);
161
155
  const summary = generateSummary(results);
162
- const outputFormat = options.output || mergedConfig.output?.format || "console";
163
- const outputFile = options.outputFile || mergedConfig.output?.file;
156
+ const outputFormat = options.output || finalOptions.output?.format || "console";
157
+ const outputFile = options.outputFile || finalOptions.output?.file;
164
158
  if (outputFormat === "json") {
165
159
  const outputData = {
166
160
  results,
167
161
  summary: { ...summary, executionTime: parseFloat(elapsedTime) }
168
162
  };
169
- if (outputFile) {
170
- (0, import_fs.writeFileSync)(outputFile, JSON.stringify(outputData, null, 2));
171
- console.log(import_chalk.default.green(`\u2705 Results saved to ${outputFile}`));
172
- } else {
173
- console.log(JSON.stringify(outputData, null, 2));
174
- }
163
+ (0, import_core.handleJSONOutput)(outputData, outputFile, `\u2705 Results saved to ${outputFile}`);
175
164
  } else {
176
165
  console.log(`Pattern Analysis Complete (${elapsedTime}s)`);
177
166
  console.log(`Found ${summary.totalPatterns} duplicate patterns`);
178
167
  console.log(`Total token cost: ${summary.totalTokenCost} tokens`);
179
168
  }
180
169
  } catch (error) {
181
- console.error(import_chalk.default.red("\u274C Pattern analysis failed:"), error);
182
- process.exit(1);
170
+ (0, import_core.handleCLIError)(error, "Pattern analysis");
183
171
  }
184
172
  });
185
173
  program.command("context").description("Run context window cost analysis").argument("<directory>", "Directory to analyze").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
186
174
  console.log(import_chalk.default.blue("\u{1F9E0} Analyzing context costs...\n"));
187
175
  const startTime = Date.now();
188
176
  try {
189
- const config = (0, import_core.loadConfig)(directory);
190
177
  const defaults = {
191
178
  maxDepth: 5,
192
179
  maxContextBudget: 1e4,
@@ -197,31 +184,24 @@ program.command("context").description("Run context window cost analysis").argum
197
184
  file: void 0
198
185
  }
199
186
  };
200
- const mergedConfig = (0, import_core.mergeConfigWithDefaults)(config, defaults);
201
- const finalOptions = {
202
- rootDir: directory,
203
- maxDepth: options.maxDepth ? parseInt(options.maxDepth) : mergedConfig.maxDepth,
204
- maxContextBudget: options.maxContext ? parseInt(options.maxContext) : mergedConfig.maxContextBudget,
205
- include: options.include?.split(",") || mergedConfig.include,
206
- exclude: options.exclude?.split(",") || mergedConfig.exclude
207
- };
187
+ const finalOptions = (0, import_core.loadMergedConfig)(directory, defaults, {
188
+ maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
189
+ maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
190
+ include: options.include?.split(","),
191
+ exclude: options.exclude?.split(",")
192
+ });
208
193
  const { analyzeContext: analyzeContext2, generateSummary } = await import("@aiready/context-analyzer");
209
194
  const results = await analyzeContext2(finalOptions);
210
- const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
195
+ const elapsedTime = (0, import_core.getElapsedTime)(startTime);
211
196
  const summary = generateSummary(results);
212
- const outputFormat = options.output || mergedConfig.output?.format || "console";
213
- const outputFile = options.outputFile || mergedConfig.output?.file;
197
+ const outputFormat = options.output || finalOptions.output?.format || "console";
198
+ const outputFile = options.outputFile || finalOptions.output?.file;
214
199
  if (outputFormat === "json") {
215
200
  const outputData = {
216
201
  results,
217
202
  summary: { ...summary, executionTime: parseFloat(elapsedTime) }
218
203
  };
219
- if (outputFile) {
220
- (0, import_fs.writeFileSync)(outputFile, JSON.stringify(outputData, null, 2));
221
- console.log(import_chalk.default.green(`\u2705 Results saved to ${outputFile}`));
222
- } else {
223
- console.log(JSON.stringify(outputData, null, 2));
224
- }
204
+ (0, import_core.handleJSONOutput)(outputData, outputFile, `\u2705 Results saved to ${outputFile}`);
225
205
  } else {
226
206
  console.log(`Context Analysis Complete (${elapsedTime}s)`);
227
207
  console.log(`Files analyzed: ${summary.totalFiles}`);
@@ -230,8 +210,7 @@ program.command("context").description("Run context window cost analysis").argum
230
210
  console.log(`Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(1)}%`);
231
211
  }
232
212
  } catch (error) {
233
- console.error(import_chalk.default.red("\u274C Context analysis failed:"), error);
234
- process.exit(1);
213
+ (0, import_core.handleCLIError)(error, "Context analysis");
235
214
  }
236
215
  });
237
216
  program.parse();
package/dist/cli.mjs CHANGED
@@ -7,9 +7,8 @@ import {
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
9
9
  import chalk from "chalk";
10
- import { writeFileSync } from "fs";
11
10
  import { join } from "path";
12
- import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
11
+ import { loadMergedConfig, handleJSONOutput, handleCLIError, getElapsedTime } from "@aiready/core";
13
12
  import { readFileSync } from "fs";
14
13
  var packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8"));
15
14
  var program = new Command();
@@ -18,7 +17,6 @@ program.command("scan").description("Run unified analysis on a codebase").argume
18
17
  console.log(chalk.blue("\u{1F680} Starting AIReady unified analysis...\n"));
19
18
  const startTime = Date.now();
20
19
  try {
21
- const config = loadConfig(directory);
22
20
  const defaults = {
23
21
  tools: ["patterns", "context"],
24
22
  include: void 0,
@@ -28,17 +26,15 @@ program.command("scan").description("Run unified analysis on a codebase").argume
28
26
  file: void 0
29
27
  }
30
28
  };
31
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
32
- const finalOptions = {
33
- rootDir: directory,
34
- tools: options.tools ? options.tools.split(",").map((t) => t.trim()) : mergedConfig.tools,
35
- include: options.include?.split(",") || mergedConfig.include,
36
- exclude: options.exclude?.split(",") || mergedConfig.exclude
37
- };
29
+ const finalOptions = loadMergedConfig(directory, defaults, {
30
+ tools: options.tools ? options.tools.split(",").map((t) => t.trim()) : void 0,
31
+ include: options.include?.split(","),
32
+ exclude: options.exclude?.split(",")
33
+ });
38
34
  const results = await analyzeUnified(finalOptions);
39
- const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
40
- const outputFormat = options.output || mergedConfig.output?.format || "console";
41
- const outputFile = options.outputFile || mergedConfig.output?.file;
35
+ const elapsedTime = getElapsedTime(startTime);
36
+ const outputFormat = options.output || finalOptions.output?.format || "console";
37
+ const outputFile = options.outputFile || finalOptions.output?.file;
42
38
  if (outputFormat === "json") {
43
39
  const outputData = {
44
40
  ...results,
@@ -47,28 +43,24 @@ program.command("scan").description("Run unified analysis on a codebase").argume
47
43
  executionTime: parseFloat(elapsedTime)
48
44
  }
49
45
  };
50
- if (outputFile) {
51
- writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
52
- console.log(chalk.green(`\u2705 Results saved to ${outputFile}`));
53
- } else {
54
- console.log(JSON.stringify(outputData, null, 2));
55
- }
46
+ handleJSONOutput(outputData, outputFile, `\u2705 Results saved to ${outputFile}`);
56
47
  } else {
57
48
  console.log(generateUnifiedSummary(results));
58
49
  }
59
50
  } catch (error) {
60
- console.error(chalk.red("\u274C Analysis failed:"), error);
61
- process.exit(1);
51
+ handleCLIError(error, "Analysis");
62
52
  }
63
53
  });
64
- program.command("patterns").description("Run pattern detection analysis").argument("<directory>", "Directory to analyze").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
54
+ program.command("patterns").description("Run pattern detection analysis").argument("<directory>", "Directory to analyze").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
65
55
  console.log(chalk.blue("\u{1F50D} Analyzing patterns...\n"));
66
56
  const startTime = Date.now();
67
57
  try {
68
- const config = loadConfig(directory);
69
58
  const defaults = {
70
59
  minSimilarity: 0.4,
71
60
  minLines: 5,
61
+ maxCandidatesPerBlock: 100,
62
+ minSharedTokens: 8,
63
+ useSmartDefaults: true,
72
64
  include: void 0,
73
65
  exclude: void 0,
74
66
  output: {
@@ -76,46 +68,41 @@ program.command("patterns").description("Run pattern detection analysis").argume
76
68
  file: void 0
77
69
  }
78
70
  };
79
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
80
- const finalOptions = {
81
- rootDir: directory,
82
- minSimilarity: options.similarity ? parseFloat(options.similarity) : mergedConfig.minSimilarity,
83
- minLines: options.minLines ? parseInt(options.minLines) : mergedConfig.minLines,
84
- include: options.include?.split(",") || mergedConfig.include,
85
- exclude: options.exclude?.split(",") || mergedConfig.exclude
86
- };
71
+ const finalOptions = loadMergedConfig(directory, defaults, {
72
+ minSimilarity: options.similarity ? parseFloat(options.similarity) : void 0,
73
+ minLines: options.minLines ? parseInt(options.minLines) : void 0,
74
+ maxCandidatesPerBlock: options.maxCandidates ? parseInt(options.maxCandidates) : void 0,
75
+ minSharedTokens: options.minSharedTokens ? parseInt(options.minSharedTokens) : void 0,
76
+ // If --full-scan is specified, don't use smart defaults
77
+ useSmartDefaults: !options.fullScan,
78
+ include: options.include?.split(","),
79
+ exclude: options.exclude?.split(",")
80
+ });
87
81
  const { analyzePatterns, generateSummary } = await import("@aiready/pattern-detect");
88
82
  const { results } = await analyzePatterns(finalOptions);
89
- const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
83
+ const elapsedTime = getElapsedTime(startTime);
90
84
  const summary = generateSummary(results);
91
- const outputFormat = options.output || mergedConfig.output?.format || "console";
92
- const outputFile = options.outputFile || mergedConfig.output?.file;
85
+ const outputFormat = options.output || finalOptions.output?.format || "console";
86
+ const outputFile = options.outputFile || finalOptions.output?.file;
93
87
  if (outputFormat === "json") {
94
88
  const outputData = {
95
89
  results,
96
90
  summary: { ...summary, executionTime: parseFloat(elapsedTime) }
97
91
  };
98
- if (outputFile) {
99
- writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
100
- console.log(chalk.green(`\u2705 Results saved to ${outputFile}`));
101
- } else {
102
- console.log(JSON.stringify(outputData, null, 2));
103
- }
92
+ handleJSONOutput(outputData, outputFile, `\u2705 Results saved to ${outputFile}`);
104
93
  } else {
105
94
  console.log(`Pattern Analysis Complete (${elapsedTime}s)`);
106
95
  console.log(`Found ${summary.totalPatterns} duplicate patterns`);
107
96
  console.log(`Total token cost: ${summary.totalTokenCost} tokens`);
108
97
  }
109
98
  } catch (error) {
110
- console.error(chalk.red("\u274C Pattern analysis failed:"), error);
111
- process.exit(1);
99
+ handleCLIError(error, "Pattern analysis");
112
100
  }
113
101
  });
114
102
  program.command("context").description("Run context window cost analysis").argument("<directory>", "Directory to analyze").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
115
103
  console.log(chalk.blue("\u{1F9E0} Analyzing context costs...\n"));
116
104
  const startTime = Date.now();
117
105
  try {
118
- const config = loadConfig(directory);
119
106
  const defaults = {
120
107
  maxDepth: 5,
121
108
  maxContextBudget: 1e4,
@@ -126,31 +113,24 @@ program.command("context").description("Run context window cost analysis").argum
126
113
  file: void 0
127
114
  }
128
115
  };
129
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
130
- const finalOptions = {
131
- rootDir: directory,
132
- maxDepth: options.maxDepth ? parseInt(options.maxDepth) : mergedConfig.maxDepth,
133
- maxContextBudget: options.maxContext ? parseInt(options.maxContext) : mergedConfig.maxContextBudget,
134
- include: options.include?.split(",") || mergedConfig.include,
135
- exclude: options.exclude?.split(",") || mergedConfig.exclude
136
- };
116
+ const finalOptions = loadMergedConfig(directory, defaults, {
117
+ maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
118
+ maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
119
+ include: options.include?.split(","),
120
+ exclude: options.exclude?.split(",")
121
+ });
137
122
  const { analyzeContext, generateSummary } = await import("@aiready/context-analyzer");
138
123
  const results = await analyzeContext(finalOptions);
139
- const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
124
+ const elapsedTime = getElapsedTime(startTime);
140
125
  const summary = generateSummary(results);
141
- const outputFormat = options.output || mergedConfig.output?.format || "console";
142
- const outputFile = options.outputFile || mergedConfig.output?.file;
126
+ const outputFormat = options.output || finalOptions.output?.format || "console";
127
+ const outputFile = options.outputFile || finalOptions.output?.file;
143
128
  if (outputFormat === "json") {
144
129
  const outputData = {
145
130
  results,
146
131
  summary: { ...summary, executionTime: parseFloat(elapsedTime) }
147
132
  };
148
- if (outputFile) {
149
- writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
150
- console.log(chalk.green(`\u2705 Results saved to ${outputFile}`));
151
- } else {
152
- console.log(JSON.stringify(outputData, null, 2));
153
- }
133
+ handleJSONOutput(outputData, outputFile, `\u2705 Results saved to ${outputFile}`);
154
134
  } else {
155
135
  console.log(`Context Analysis Complete (${elapsedTime}s)`);
156
136
  console.log(`Files analyzed: ${summary.totalFiles}`);
@@ -159,8 +139,7 @@ program.command("context").description("Run context window cost analysis").argum
159
139
  console.log(`Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(1)}%`);
160
140
  }
161
141
  } catch (error) {
162
- console.error(chalk.red("\u274C Context analysis failed:"), error);
163
- process.exit(1);
142
+ handleCLIError(error, "Context analysis");
164
143
  }
165
144
  });
166
145
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,9 +11,9 @@
11
11
  "dependencies": {
12
12
  "commander": "^12.1.0",
13
13
  "chalk": "^5.3.0",
14
- "@aiready/pattern-detect": "0.7.5",
15
- "@aiready/core": "0.3.1",
16
- "@aiready/context-analyzer": "0.3.3"
14
+ "@aiready/core": "0.3.3",
15
+ "@aiready/context-analyzer": "0.3.5",
16
+ "@aiready/pattern-detect": "0.7.7"
17
17
  },
18
18
  "devDependencies": {
19
19
  "tsup": "^8.3.5",
package/src/cli.ts CHANGED
@@ -5,7 +5,7 @@ import { analyzeUnified, generateUnifiedSummary } from './index';
5
5
  import chalk from 'chalk';
6
6
  import { writeFileSync } from 'fs';
7
7
  import { join } from 'path';
8
- import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
8
+ import { loadMergedConfig, handleJSONOutput, handleCLIError, getElapsedTime } from '@aiready/core';
9
9
  import { readFileSync } from 'fs';
10
10
 
11
11
  const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
@@ -33,9 +33,6 @@ program
33
33
  const startTime = Date.now();
34
34
 
35
35
  try {
36
- // Load config file if it exists
37
- const config = loadConfig(directory);
38
-
39
36
  // Define defaults
40
37
  const defaults = {
41
38
  tools: ['patterns', 'context'],
@@ -47,23 +44,19 @@ program
47
44
  },
48
45
  };
49
46
 
50
- // Merge config with defaults
51
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
52
-
53
- // Override with CLI options (CLI takes precedence)
54
- const finalOptions = {
55
- rootDir: directory,
56
- tools: options.tools ? options.tools.split(',').map((t: string) => t.trim()) as ('patterns' | 'context')[] : mergedConfig.tools,
57
- include: options.include?.split(',') || mergedConfig.include,
58
- exclude: options.exclude?.split(',') || mergedConfig.exclude,
59
- };
47
+ // Load and merge config with CLI options
48
+ const finalOptions = loadMergedConfig(directory, defaults, {
49
+ tools: options.tools ? options.tools.split(',').map((t: string) => t.trim()) as ('patterns' | 'context')[] : undefined,
50
+ include: options.include?.split(','),
51
+ exclude: options.exclude?.split(','),
52
+ }) as any;
60
53
 
61
54
  const results = await analyzeUnified(finalOptions);
62
55
 
63
- const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
56
+ const elapsedTime = getElapsedTime(startTime);
64
57
 
65
- const outputFormat = options.output || mergedConfig.output?.format || 'console';
66
- const outputFile = options.outputFile || mergedConfig.output?.file;
58
+ const outputFormat = options.output || finalOptions.output?.format || 'console';
59
+ const outputFile = options.outputFile || finalOptions.output?.file;
67
60
 
68
61
  if (outputFormat === 'json') {
69
62
  const outputData = {
@@ -74,19 +67,13 @@ program
74
67
  },
75
68
  };
76
69
 
77
- if (outputFile) {
78
- writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
79
- console.log(chalk.green(`✅ Results saved to ${outputFile}`));
80
- } else {
81
- console.log(JSON.stringify(outputData, null, 2));
82
- }
70
+ handleJSONOutput(outputData, outputFile, `✅ Results saved to ${outputFile}`);
83
71
  } else {
84
72
  // Console output
85
73
  console.log(generateUnifiedSummary(results));
86
74
  }
87
75
  } catch (error) {
88
- console.error(chalk.red('Analysis failed:'), error);
89
- process.exit(1);
76
+ handleCLIError(error, 'Analysis');
90
77
  }
91
78
  });
92
79
 
@@ -97,6 +84,9 @@ program
97
84
  .argument('<directory>', 'Directory to analyze')
98
85
  .option('-s, --similarity <number>', 'Minimum similarity score (0-1)', '0.40')
99
86
  .option('-l, --min-lines <number>', 'Minimum lines to consider', '5')
87
+ .option('--max-candidates <number>', 'Maximum candidates per block (performance tuning)')
88
+ .option('--min-shared-tokens <number>', 'Minimum shared tokens for candidates (performance tuning)')
89
+ .option('--full-scan', 'Disable smart defaults for comprehensive analysis (slower)')
100
90
  .option('--include <patterns>', 'File patterns to include (comma-separated)')
101
91
  .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
102
92
  .option('-o, --output <format>', 'Output format: console, json', 'console')
@@ -107,13 +97,13 @@ program
107
97
  const startTime = Date.now();
108
98
 
109
99
  try {
110
- // Load config file if it exists
111
- const config = loadConfig(directory);
112
-
113
100
  // Define defaults
114
101
  const defaults = {
115
102
  minSimilarity: 0.4,
116
103
  minLines: 5,
104
+ maxCandidatesPerBlock: 100,
105
+ minSharedTokens: 8,
106
+ useSmartDefaults: true,
117
107
  include: undefined,
118
108
  exclude: undefined,
119
109
  output: {
@@ -122,27 +112,27 @@ program
122
112
  },
123
113
  };
124
114
 
125
- // Merge config with defaults
126
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
127
-
128
- // Override with CLI options (CLI takes precedence)
129
- const finalOptions = {
130
- rootDir: directory,
131
- minSimilarity: options.similarity ? parseFloat(options.similarity) : mergedConfig.minSimilarity,
132
- minLines: options.minLines ? parseInt(options.minLines) : mergedConfig.minLines,
133
- include: options.include?.split(',') || mergedConfig.include,
134
- exclude: options.exclude?.split(',') || mergedConfig.exclude,
135
- };
115
+ // Load and merge config with CLI options
116
+ const finalOptions = loadMergedConfig(directory, defaults, {
117
+ minSimilarity: options.similarity ? parseFloat(options.similarity) : undefined,
118
+ minLines: options.minLines ? parseInt(options.minLines) : undefined,
119
+ maxCandidatesPerBlock: options.maxCandidates ? parseInt(options.maxCandidates) : undefined,
120
+ minSharedTokens: options.minSharedTokens ? parseInt(options.minSharedTokens) : undefined,
121
+ // If --full-scan is specified, don't use smart defaults
122
+ useSmartDefaults: !options.fullScan,
123
+ include: options.include?.split(','),
124
+ exclude: options.exclude?.split(','),
125
+ });
136
126
 
137
127
  const { analyzePatterns, generateSummary } = await import('@aiready/pattern-detect');
138
128
 
139
129
  const { results } = await analyzePatterns(finalOptions);
140
130
 
141
- const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
131
+ const elapsedTime = getElapsedTime(startTime);
142
132
  const summary = generateSummary(results);
143
133
 
144
- const outputFormat = options.output || mergedConfig.output?.format || 'console';
145
- const outputFile = options.outputFile || mergedConfig.output?.file;
134
+ const outputFormat = options.output || finalOptions.output?.format || 'console';
135
+ const outputFile = options.outputFile || finalOptions.output?.file;
146
136
 
147
137
  if (outputFormat === 'json') {
148
138
  const outputData = {
@@ -150,20 +140,14 @@ program
150
140
  summary: { ...summary, executionTime: parseFloat(elapsedTime) },
151
141
  };
152
142
 
153
- if (outputFile) {
154
- writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
155
- console.log(chalk.green(`✅ Results saved to ${outputFile}`));
156
- } else {
157
- console.log(JSON.stringify(outputData, null, 2));
158
- }
143
+ handleJSONOutput(outputData, outputFile, `✅ Results saved to ${outputFile}`);
159
144
  } else {
160
145
  console.log(`Pattern Analysis Complete (${elapsedTime}s)`);
161
146
  console.log(`Found ${summary.totalPatterns} duplicate patterns`);
162
147
  console.log(`Total token cost: ${summary.totalTokenCost} tokens`);
163
148
  }
164
149
  } catch (error) {
165
- console.error(chalk.red('Pattern analysis failed:'), error);
166
- process.exit(1);
150
+ handleCLIError(error, 'Pattern analysis');
167
151
  }
168
152
  });
169
153
 
@@ -183,9 +167,6 @@ program
183
167
  const startTime = Date.now();
184
168
 
185
169
  try {
186
- // Load config file if it exists
187
- const config = loadConfig(directory);
188
-
189
170
  // Define defaults
190
171
  const defaults = {
191
172
  maxDepth: 5,
@@ -198,27 +179,23 @@ program
198
179
  },
199
180
  };
200
181
 
201
- // Merge config with defaults
202
- const mergedConfig = mergeConfigWithDefaults(config, defaults);
203
-
204
- // Override with CLI options (CLI takes precedence)
205
- const finalOptions = {
206
- rootDir: directory,
207
- maxDepth: options.maxDepth ? parseInt(options.maxDepth) : mergedConfig.maxDepth,
208
- maxContextBudget: options.maxContext ? parseInt(options.maxContext) : mergedConfig.maxContextBudget,
209
- include: options.include?.split(',') || mergedConfig.include,
210
- exclude: options.exclude?.split(',') || mergedConfig.exclude,
211
- };
182
+ // Load and merge config with CLI options
183
+ const finalOptions = loadMergedConfig(directory, defaults, {
184
+ maxDepth: options.maxDepth ? parseInt(options.maxDepth) : undefined,
185
+ maxContextBudget: options.maxContext ? parseInt(options.maxContext) : undefined,
186
+ include: options.include?.split(','),
187
+ exclude: options.exclude?.split(','),
188
+ });
212
189
 
213
190
  const { analyzeContext, generateSummary } = await import('@aiready/context-analyzer');
214
191
 
215
192
  const results = await analyzeContext(finalOptions);
216
193
 
217
- const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
194
+ const elapsedTime = getElapsedTime(startTime);
218
195
  const summary = generateSummary(results);
219
196
 
220
- const outputFormat = options.output || mergedConfig.output?.format || 'console';
221
- const outputFile = options.outputFile || mergedConfig.output?.file;
197
+ const outputFormat = options.output || finalOptions.output?.format || 'console';
198
+ const outputFile = options.outputFile || finalOptions.output?.file;
222
199
 
223
200
  if (outputFormat === 'json') {
224
201
  const outputData = {
@@ -226,12 +203,7 @@ program
226
203
  summary: { ...summary, executionTime: parseFloat(elapsedTime) },
227
204
  };
228
205
 
229
- if (outputFile) {
230
- writeFileSync(outputFile, JSON.stringify(outputData, null, 2));
231
- console.log(chalk.green(`✅ Results saved to ${outputFile}`));
232
- } else {
233
- console.log(JSON.stringify(outputData, null, 2));
234
- }
206
+ handleJSONOutput(outputData, outputFile, `✅ Results saved to ${outputFile}`);
235
207
  } else {
236
208
  console.log(`Context Analysis Complete (${elapsedTime}s)`);
237
209
  console.log(`Files analyzed: ${summary.totalFiles}`);
@@ -240,8 +212,7 @@ program
240
212
  console.log(`Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(1)}%`);
241
213
  }
242
214
  } catch (error) {
243
- console.error(chalk.red('Context analysis failed:'), error);
244
- process.exit(1);
215
+ handleCLIError(error, 'Context analysis');
245
216
  }
246
217
  });
247
218