@aiready/core 0.21.18 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -18,6 +18,8 @@ import {
18
18
  ModelTier,
19
19
  ModelTierSchema,
20
20
  ParseError,
21
+ ReadinessRating,
22
+ RecommendationPriority,
21
23
  SIZE_ADJUSTED_THRESHOLDS,
22
24
  Severity,
23
25
  SeveritySchema,
@@ -39,7 +41,7 @@ import {
39
41
  getToolWeight,
40
42
  normalizeToolName,
41
43
  parseWeightString
42
- } from "./chunk-COHIBX3Q.mjs";
44
+ } from "./chunk-SWTDBVYJ.mjs";
43
45
 
44
46
  // src/types/contract.ts
45
47
  function validateSpokeOutput(toolName, output) {
@@ -100,51 +102,61 @@ function validateWithSchema(schema, data) {
100
102
  }
101
103
 
102
104
  // src/registry.ts
103
- var ToolRegistry = class {
104
- static getProviders() {
105
- const g = globalThis;
106
- if (!g.__AIRE_TOOL_REGISTRY__) {
107
- g.__AIRE_TOOL_REGISTRY__ = /* @__PURE__ */ new Map();
108
- }
109
- return g.__AIRE_TOOL_REGISTRY__;
105
+ var ToolRegistry = class _ToolRegistry {
106
+ /**
107
+ * Create a new ToolRegistry instance
108
+ *
109
+ * @param id Optional identifier for the registry (e.g. for debugging)
110
+ */
111
+ constructor(id = "default") {
112
+ this.providers = /* @__PURE__ */ new Map();
113
+ this.id = `registry-${id}-${Math.random().toString(36).substring(2, 9)}`;
110
114
  }
111
115
  /**
112
116
  * Register a new tool provider.
117
+ *
118
+ * @param provider The tool provider to register
113
119
  */
114
- static register(provider) {
115
- console.log(
116
- `[ToolRegistry#${this.instanceId}] Registering tool: ${provider.id} (${provider.alias.join(", ")})`
117
- );
118
- this.getProviders().set(provider.id, provider);
120
+ register(provider) {
121
+ this.providers.set(provider.id, provider);
119
122
  }
120
123
  /**
121
124
  * Get a provider by its canonical ID.
125
+ *
126
+ * @param id The tool ID
127
+ * @returns The provider if found
122
128
  */
123
- static get(id) {
124
- return this.getProviders().get(id);
129
+ get(id) {
130
+ return this.providers.get(id);
125
131
  }
126
132
  /**
127
133
  * Get a provider by name or alias.
134
+ *
135
+ * @param nameOrAlias The tool name or alias string
136
+ * @returns The provider if found
128
137
  */
129
- static find(nameOrAlias) {
130
- const providers = this.getProviders();
131
- const exact = providers.get(nameOrAlias);
138
+ find(nameOrAlias) {
139
+ const exact = this.providers.get(nameOrAlias);
132
140
  if (exact) return exact;
133
- for (const p of providers.values()) {
141
+ for (const p of this.providers.values()) {
134
142
  if (p.alias.includes(nameOrAlias)) return p;
135
143
  }
136
144
  return void 0;
137
145
  }
138
146
  /**
139
147
  * Get all registered tool providers.
148
+ *
149
+ * @returns Array of all registered tool providers
140
150
  */
141
- static getAll() {
142
- return Array.from(this.getProviders().values());
151
+ getAll() {
152
+ return Array.from(this.providers.values());
143
153
  }
144
154
  /**
145
155
  * Get all available tool IDs from the ToolName enum.
156
+ *
157
+ * @returns Array of valid ToolName identifiers
146
158
  */
147
- static getAvailableIds() {
159
+ getAvailableIds() {
148
160
  return Object.values(ToolName).filter(
149
161
  (v) => typeof v === "string"
150
162
  );
@@ -152,11 +164,54 @@ var ToolRegistry = class {
152
164
  /**
153
165
  * Clear the registry (primarily for testing).
154
166
  */
167
+ clear() {
168
+ this.providers.clear();
169
+ }
170
+ // --- Static Compatibility Layer ---
171
+ static getGlobalRegistry() {
172
+ const g = globalThis;
173
+ if (!g.__AIRE_TOOL_REGISTRY_INSTANCE__) {
174
+ g.__AIRE_TOOL_REGISTRY_INSTANCE__ = new _ToolRegistry("global");
175
+ }
176
+ return g.__AIRE_TOOL_REGISTRY_INSTANCE__;
177
+ }
178
+ /**
179
+ * Static register (Singleton compatibility)
180
+ */
181
+ static register(provider) {
182
+ this.getGlobalRegistry().register(provider);
183
+ }
184
+ /**
185
+ * Static get (Singleton compatibility)
186
+ */
187
+ static get(id) {
188
+ return this.getGlobalRegistry().get(id);
189
+ }
190
+ /**
191
+ * Static find (Singleton compatibility)
192
+ */
193
+ static find(nameOrAlias) {
194
+ return this.getGlobalRegistry().find(nameOrAlias);
195
+ }
196
+ /**
197
+ * Static getAll (Singleton compatibility)
198
+ */
199
+ static getAll() {
200
+ return this.getGlobalRegistry().getAll();
201
+ }
202
+ /**
203
+ * Static getAvailableIds (Singleton compatibility)
204
+ */
205
+ static getAvailableIds() {
206
+ return this.getGlobalRegistry().getAvailableIds();
207
+ }
208
+ /**
209
+ * Static clear (Singleton compatibility)
210
+ */
155
211
  static clear() {
156
- this.getProviders().clear();
212
+ this.getGlobalRegistry().clear();
157
213
  }
158
214
  };
159
- ToolRegistry.instanceId = globalThis.Math.random();
160
215
 
161
216
  // src/utils/file-scanner.ts
162
217
  import { glob } from "glob";
@@ -268,7 +323,8 @@ async function scanFiles(options) {
268
323
  });
269
324
  const gitignoreFiles = await glob("**/.gitignore", {
270
325
  cwd: rootDir,
271
- ignore: finalExclude,
326
+ ignore: (exclude || []).concat(["**/node_modules/**", "**/.git/**"]),
327
+ // Minimal ignore for gitignore discovery
272
328
  absolute: true
273
329
  });
274
330
  if (gitignoreFiles.length > 0) {
@@ -335,7 +391,7 @@ async function scanEntries(options) {
335
391
  });
336
392
  const gitignoreFiles = await glob("**/.gitignore", {
337
393
  cwd: rootDir,
338
- ignore: finalExclude,
394
+ ignore: (exclude || []).concat(["**/node_modules/**", "**/.git/**"]),
339
395
  absolute: true
340
396
  });
341
397
  if (gitignoreFiles.length > 0) {
@@ -359,8 +415,9 @@ async function scanEntries(options) {
359
415
  }
360
416
  }
361
417
  const filteredDirs = dirs.filter((d) => {
362
- const rel = relative(rootDir || ".", d).replace(/\\/g, "/").replace(/\/$/, "");
418
+ let rel = relative(rootDir || ".", d).replace(/\\/g, "/");
363
419
  if (rel === "") return true;
420
+ if (!rel.endsWith("/")) rel += "/";
364
421
  return !ig.ignores(rel);
365
422
  });
366
423
  return { files, dirs: filteredDirs };
@@ -425,7 +482,8 @@ function getElapsedTime(startTime) {
425
482
  return ((Date.now() - startTime) / 1e3).toFixed(2);
426
483
  }
427
484
  function getScoreBar(val) {
428
- return "\u2588".repeat(Math.round(val / 10)).padEnd(10, "\u2591");
485
+ const clamped = Math.max(0, Math.min(100, val));
486
+ return "\u2588".repeat(Math.round(clamped / 10)).padEnd(10, "\u2591");
429
487
  }
430
488
  function getSafetyIcon(rating) {
431
489
  switch (rating) {
@@ -686,7 +744,7 @@ var TypeScriptParser = class {
686
744
  extractFromDeclaration(declaration, importedNames, code, parentNode) {
687
745
  const exports = [];
688
746
  const metadata = this.analyzeMetadata(parentNode || declaration, code);
689
- if (declaration.type === "FunctionDeclaration" && declaration.id) {
747
+ if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
690
748
  exports.push({
691
749
  name: declaration.id.name,
692
750
  type: "function",
@@ -714,11 +772,21 @@ var TypeScriptParser = class {
714
772
  const body = declaration.body.body;
715
773
  const methods = body.filter((m) => m.type === "MethodDefinition");
716
774
  const properties = body.filter((m) => m.type === "PropertyDefinition");
775
+ const constructor = methods.find(
776
+ (m) => m.kind === "constructor"
777
+ );
778
+ const parameters = constructor ? constructor.value.params.map((p) => {
779
+ if (p.type === "Identifier") return p.name;
780
+ if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier")
781
+ return p.parameter.name;
782
+ return "unknown";
783
+ }) : [];
717
784
  exports.push({
718
785
  name: declaration.id.name,
719
786
  type: "class",
720
787
  methodCount: methods.length,
721
788
  propertyCount: properties.length,
789
+ parameters,
722
790
  loc: declaration.loc ? {
723
791
  start: {
724
792
  line: declaration.loc.start.line,
@@ -2165,6 +2233,9 @@ var GoParser = class {
2165
2233
 
2166
2234
  // src/parsers/parser-factory.ts
2167
2235
  var ParserFactory = class _ParserFactory {
2236
+ /**
2237
+ * Create a new ParserFactory instance
2238
+ */
2168
2239
  constructor() {
2169
2240
  this.parsers = /* @__PURE__ */ new Map();
2170
2241
  this.extensionMap = new Map(
@@ -2177,7 +2248,9 @@ var ParserFactory = class _ParserFactory {
2177
2248
  this.registerParser(new GoParser());
2178
2249
  }
2179
2250
  /**
2180
- * Get singleton instance
2251
+ * Get the global singleton instance
2252
+ *
2253
+ * @returns The singleton ParserFactory instance
2181
2254
  */
2182
2255
  static getInstance() {
2183
2256
  if (!_ParserFactory.instance) {
@@ -2628,15 +2701,17 @@ var DEFAULT_COST_CONFIG = {
2628
2701
  daysPerMonth: 30
2629
2702
  };
2630
2703
  function calculateMonthlyCost(tokenWaste, config = {}) {
2704
+ const multiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
2631
2705
  const budget = calculateTokenBudget({
2632
- totalContextTokens: tokenWaste * 2.5,
2706
+ totalContextTokens: tokenWaste * multiplier,
2633
2707
  wastedTokens: {
2634
2708
  duplication: tokenWaste * 0.7,
2635
2709
  fragmentation: tokenWaste * 0.3,
2636
- chattiness: 0
2710
+ chattiness: 0.1 * tokenWaste
2711
+ // Added baseline chattiness
2637
2712
  }
2638
2713
  });
2639
- const preset = getModelPreset("claude-4.6");
2714
+ const preset = getModelPreset("claude-3.5-sonnet");
2640
2715
  return estimateCostFromBudget(budget, preset, config);
2641
2716
  }
2642
2717
  function calculateTokenBudget(params) {
@@ -3118,7 +3193,7 @@ function calculatePatternEntropy(files) {
3118
3193
  }
3119
3194
  const dirGroups = /* @__PURE__ */ new Map();
3120
3195
  for (const file of files) {
3121
- const parts = file.path.split("/").slice(0, 4).join("/") || "root";
3196
+ const parts = file.path.split("/").slice(0, -1).join("/") || "root";
3122
3197
  dirGroups.set(parts, (dirGroups.get(parts) || 0) + 1);
3123
3198
  }
3124
3199
  const counts = Array.from(dirGroups.values());
@@ -3225,6 +3300,8 @@ function calculateAiSignalClarity(params) {
3225
3300
  deepCallbacks,
3226
3301
  ambiguousNames,
3227
3302
  undocumentedExports,
3303
+ largeFiles = 0,
3304
+ // Default to 0 to prevent NaN
3228
3305
  totalSymbols,
3229
3306
  totalExports
3230
3307
  } = params;
@@ -3241,28 +3318,35 @@ function calculateAiSignalClarity(params) {
3241
3318
  const overloadSignal = {
3242
3319
  name: "Symbol Overloading",
3243
3320
  count: overloadedSymbols,
3244
- riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
3321
+ riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.2),
3245
3322
  description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
3246
3323
  };
3324
+ const largeFileSignal = {
3325
+ name: "Large Files",
3326
+ count: largeFiles,
3327
+ riskContribution: Math.round(Math.min(5, largeFiles) * 5),
3328
+ // up to 25 points
3329
+ description: `${largeFiles} large files \u2014 pushing AI context limits`
3330
+ };
3247
3331
  const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
3248
3332
  const magicSignal = {
3249
3333
  name: "Magic Literals",
3250
3334
  count: magicLiterals,
3251
- riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
3335
+ riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.15),
3252
3336
  description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
3253
3337
  };
3254
3338
  const trapRatio = booleanTraps / Math.max(1, totalSymbols);
3255
3339
  const trapSignal = {
3256
3340
  name: "Boolean Traps",
3257
3341
  count: booleanTraps,
3258
- riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
3342
+ riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.15),
3259
3343
  description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
3260
3344
  };
3261
3345
  const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
3262
3346
  const sideEffectSignal = {
3263
3347
  name: "Implicit Side Effects",
3264
3348
  count: implicitSideEffects,
3265
- riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
3349
+ riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.1),
3266
3350
  description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
3267
3351
  };
3268
3352
  const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
@@ -3276,18 +3360,19 @@ function calculateAiSignalClarity(params) {
3276
3360
  const ambiguousSignal = {
3277
3361
  name: "Ambiguous Names",
3278
3362
  count: ambiguousNames,
3279
- riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
3363
+ riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.05),
3280
3364
  description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
3281
3365
  };
3282
3366
  const undocRatio = undocumentedExports / Math.max(1, totalExports);
3283
3367
  const undocSignal = {
3284
3368
  name: "Undocumented Exports",
3285
3369
  count: undocumentedExports,
3286
- riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
3370
+ riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.05),
3287
3371
  description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
3288
3372
  };
3289
3373
  const signals = [
3290
3374
  overloadSignal,
3375
+ largeFileSignal,
3291
3376
  magicSignal,
3292
3377
  trapSignal,
3293
3378
  sideEffectSignal,
@@ -3310,6 +3395,10 @@ function calculateAiSignalClarity(params) {
3310
3395
  );
3311
3396
  const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant issues detected";
3312
3397
  const recommendations = [];
3398
+ if (largeFileSignal.riskContribution > 5)
3399
+ recommendations.push(
3400
+ `Split ${largeFiles} large files (> 500 lines) into smaller, single-responsibility modules`
3401
+ );
3313
3402
  if (overloadSignal.riskContribution > 5)
3314
3403
  recommendations.push(
3315
3404
  `Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
@@ -3945,6 +4034,46 @@ function getRepoMetadata(directory) {
3945
4034
  }
3946
4035
  return metadata;
3947
4036
  }
4037
+
4038
+ // src/utils/github-utils.ts
4039
+ function emitAnnotation(params) {
4040
+ const { level, file, line, col, title, message } = params;
4041
+ const parts = [];
4042
+ if (file) parts.push(`file=${file}`);
4043
+ if (line) parts.push(`line=${line}`);
4044
+ if (col) parts.push(`col=${col}`);
4045
+ if (title) parts.push(`title=${title}`);
4046
+ const metadata = parts.length > 0 ? ` ${parts.join(",")}` : "";
4047
+ console.log(`::${level}${metadata}::${message.replace(/\n/g, "%0A")}`);
4048
+ }
4049
+ function severityToAnnotationLevel(severity) {
4050
+ switch (severity.toLowerCase()) {
4051
+ case "critical":
4052
+ case "high-risk":
4053
+ case "blind-risk":
4054
+ return "error";
4055
+ case "major":
4056
+ case "moderate-risk":
4057
+ return "warning";
4058
+ case "minor":
4059
+ case "info":
4060
+ case "safe":
4061
+ default:
4062
+ return "notice";
4063
+ }
4064
+ }
4065
+ function emitIssuesAsAnnotations(issues) {
4066
+ issues.forEach((issue) => {
4067
+ emitAnnotation({
4068
+ level: severityToAnnotationLevel(issue.severity || "info"),
4069
+ file: issue.file || issue.fileName || "",
4070
+ line: issue.line || issue.location?.start?.line,
4071
+ col: issue.column || issue.location?.start?.column,
4072
+ title: `${issue.tool || "AIReady"}: ${issue.type || "Issue"}`,
4073
+ message: issue.message || issue.description || "No description provided"
4074
+ });
4075
+ });
4076
+ }
3948
4077
  export {
3949
4078
  AnalysisResultSchema,
3950
4079
  AnalysisStatus,
@@ -3973,6 +4102,8 @@ export {
3973
4102
  ParseError,
3974
4103
  ParserFactory,
3975
4104
  PythonParser,
4105
+ ReadinessRating,
4106
+ RecommendationPriority,
3976
4107
  SEVERITY_TIME_ESTIMATES,
3977
4108
  SIZE_ADJUSTED_THRESHOLDS,
3978
4109
  Severity,
@@ -4009,6 +4140,8 @@ export {
4009
4140
  calculateTestabilityIndex,
4010
4141
  calculateTokenBudget,
4011
4142
  clearHistory,
4143
+ emitAnnotation,
4144
+ emitIssuesAsAnnotations,
4012
4145
  emitProgress,
4013
4146
  estimateCostFromBudget,
4014
4147
  estimateTokens,
@@ -4062,6 +4195,7 @@ export {
4062
4195
  scanEntries,
4063
4196
  scanFiles,
4064
4197
  setupParser,
4198
+ severityToAnnotationLevel,
4065
4199
  validateSpokeOutput,
4066
4200
  validateWithSchema
4067
4201
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.21.18",
3
+ "version": "0.22.0",
4
4
  "description": "Shared utilities for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",