@sicilianwildcat/aiready 0.1.2 → 0.1.3

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 (2) hide show
  1. package/dist/cli.js +93 -9
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -173,6 +173,19 @@ function loadRepo(targetDir) {
173
173
  }
174
174
 
175
175
  // src/audit/mapper.ts
176
+ var TRIAGE_SYSTEM = `You are a repository analyst. Given a list of files with short previews,
177
+ identify which files could plausibly serve as harness artifacts for AI
178
+ coding agents.
179
+
180
+ Harness artifacts include: agent instructions, project progress tracking,
181
+ architecture documentation, constraints or rules, session state,
182
+ design decisions.
183
+
184
+ Exclude: changelogs, license files, generated API docs, package readmes,
185
+ dependency documentation, test fixtures, lock files.
186
+
187
+ Respond with valid JSON only:
188
+ { "relevant": ["path/to/file.md", ...] }`;
176
189
  var MAPPER_SYSTEM = `You are classifying repository markdown files by AI agent harness subsystem.
177
190
 
178
191
  There are exactly 5 subsystems:
@@ -194,6 +207,20 @@ var VALID_SUBSYSTEMS = /* @__PURE__ */ new Set([
194
207
  "memory",
195
208
  "constraints"
196
209
  ]);
210
+ function firstLines(content, lineCount) {
211
+ return content.split("\n").slice(0, lineCount).join("\n");
212
+ }
213
+ function parseTriageResponse(text) {
214
+ try {
215
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
216
+ if (!jsonMatch) return [];
217
+ const parsed = JSON.parse(jsonMatch[0]);
218
+ if (!Array.isArray(parsed.relevant)) return [];
219
+ return parsed.relevant.filter((p) => typeof p === "string");
220
+ } catch {
221
+ return [];
222
+ }
223
+ }
197
224
  function parseMapperResponse(text) {
198
225
  try {
199
226
  const jsonMatch = text.match(/\{[\s\S]*\}/);
@@ -210,15 +237,39 @@ function parseMapperResponse(text) {
210
237
  return [];
211
238
  }
212
239
  }
213
- async function mapFiles(mdFiles, provider) {
214
- if (mdFiles.length === 0) return [];
240
+ async function triageFiles(mdFiles, provider) {
215
241
  const fileList = mdFiles.map((f) => `- path: ${f.path}
216
- preview: ${JSON.stringify(f.preview)}`).join("\n");
217
- const text = await provider.chat(MAPPER_SYSTEM, `Classify these repository files:
242
+ preview: ${JSON.stringify(firstLines(f.fullContent, 5))}`).join("\n");
243
+ const text = await provider.chat(
244
+ TRIAGE_SYSTEM,
245
+ `Identify harness-relevant files:
218
246
 
219
- ${fileList}`, { fast: true });
247
+ ${fileList}`,
248
+ { fast: true }
249
+ );
250
+ return parseTriageResponse(text);
251
+ }
252
+ async function classifyFiles(relevantFiles, provider) {
253
+ const fileList = relevantFiles.map((f) => `- path: ${f.path}
254
+ preview: ${JSON.stringify(firstLines(f.fullContent, 50))}`).join("\n");
255
+ const text = await provider.chat(
256
+ MAPPER_SYSTEM,
257
+ `Classify these repository files:
258
+
259
+ ${fileList}`,
260
+ { fast: true }
261
+ );
220
262
  return parseMapperResponse(text);
221
263
  }
264
+ async function mapFiles(mdFiles, provider) {
265
+ if (mdFiles.length === 0) return [];
266
+ const relevantPaths = await triageFiles(mdFiles, provider);
267
+ if (relevantPaths.length === 0) return [];
268
+ const pathSet = new Set(relevantPaths);
269
+ const relevantFiles = mdFiles.filter((f) => pathSet.has(f.path));
270
+ if (relevantFiles.length === 0) return [];
271
+ return classifyFiles(relevantFiles, provider);
272
+ }
222
273
 
223
274
  // src/audit/cross-ref.ts
224
275
  function extractNpmRunCommands(content) {
@@ -485,6 +536,7 @@ function report(scored, opts) {
485
536
  if (opts.json) {
486
537
  const out = {
487
538
  overall: scored.overall,
539
+ ...opts.tokenUsage !== void 0 ? { token_usage: opts.tokenUsage } : {},
488
540
  subsystems: {
489
541
  identity: { score: scored.identity.score, gaps: scored.identity.gaps, files: scored.identity.files },
490
542
  verification: { score: scored.verification.score, gaps: scored.verification.gaps, files: scored.verification.files },
@@ -541,16 +593,28 @@ var OPENAI_FALLBACK_MODELS = [
541
593
  { id: "gpt-3.5-turbo", label: "GPT-3.5 Turbo" }
542
594
  ];
543
595
 
596
+ // src/utils/tokens.ts
597
+ function estimateTokens(text) {
598
+ return Math.ceil(text.length / 4);
599
+ }
600
+
544
601
  // src/utils/llm.ts
602
+ function estimateChatTokens(system, user, response) {
603
+ return estimateTokens(system) + estimateTokens(user) + estimateTokens(response);
604
+ }
545
605
  var ANTHROPIC_FAST = "claude-haiku-4-5-20251001";
546
606
  var ANTHROPIC_QUALITY = "claude-sonnet-4-6";
547
607
  var AnthropicProvider = class {
548
608
  client;
549
609
  modelId;
610
+ totalTokens = 0;
550
611
  constructor(apiKey, modelId) {
551
612
  this.client = new import_sdk.default({ apiKey });
552
613
  this.modelId = modelId;
553
614
  }
615
+ getTotalTokens() {
616
+ return this.totalTokens;
617
+ }
554
618
  async chat(system, user, opts) {
555
619
  const model = this.modelId ?? (opts?.fast ? ANTHROPIC_FAST : ANTHROPIC_QUALITY);
556
620
  const maxTokens = opts?.fast ? 1024 : 2048;
@@ -561,7 +625,9 @@ var AnthropicProvider = class {
561
625
  messages: [{ role: "user", content: user }]
562
626
  });
563
627
  const block = response.content.find((b) => b.type === "text");
564
- return block && block.type === "text" ? block.text : "";
628
+ const text = block && block.type === "text" ? block.text : "";
629
+ this.totalTokens += estimateChatTokens(system, user, text);
630
+ return text;
565
631
  }
566
632
  };
567
633
  var OPENAI_FAST = "gpt-4o-mini";
@@ -569,11 +635,15 @@ var OPENAI_QUALITY = "gpt-4o";
569
635
  var OpenAIProvider = class {
570
636
  client;
571
637
  modelId;
638
+ totalTokens = 0;
572
639
  constructor(apiKey, modelId) {
573
640
  const baseURL = process.env["OPENAI_BASE_URL"];
574
641
  this.client = new import_openai.default({ apiKey, ...baseURL ? { baseURL } : {} });
575
642
  this.modelId = modelId;
576
643
  }
644
+ getTotalTokens() {
645
+ return this.totalTokens;
646
+ }
577
647
  async chat(system, user, opts) {
578
648
  const model = this.modelId ?? (opts?.fast ? OPENAI_FAST : OPENAI_QUALITY);
579
649
  const response = await this.client.chat.completions.create({
@@ -583,12 +653,15 @@ var OpenAIProvider = class {
583
653
  { role: "user", content: user }
584
654
  ]
585
655
  });
586
- return response.choices[0]?.message?.content ?? "";
656
+ const text = response.choices[0]?.message?.content ?? "";
657
+ this.totalTokens += estimateChatTokens(system, user, text);
658
+ return text;
587
659
  }
588
660
  };
589
661
  var OllamaProvider = class {
590
662
  client;
591
663
  model;
664
+ totalTokens = 0;
592
665
  constructor() {
593
666
  const host = process.env["OLLAMA_HOST"] ?? "http://localhost:11434";
594
667
  this.model = process.env["OLLAMA_MODEL"] ?? "llama3";
@@ -597,6 +670,9 @@ var OllamaProvider = class {
597
670
  baseURL: `${host}/v1`
598
671
  });
599
672
  }
673
+ getTotalTokens() {
674
+ return this.totalTokens;
675
+ }
600
676
  async chat(system, user) {
601
677
  const response = await this.client.chat.completions.create({
602
678
  model: this.model,
@@ -605,7 +681,9 @@ var OllamaProvider = class {
605
681
  { role: "user", content: user }
606
682
  ]
607
683
  });
608
- return response.choices[0]?.message?.content ?? "";
684
+ const text = response.choices[0]?.message?.content ?? "";
685
+ this.totalTokens += estimateChatTokens(system, user, text);
686
+ return text;
609
687
  }
610
688
  };
611
689
  var NON_CHAT_MODEL_SUBSTRINGS = [
@@ -749,7 +827,13 @@ async function runAudit(target, opts) {
749
827
  const files = loadRepo(targetDir);
750
828
  const mappings = await mapFiles(files.mdFiles, provider);
751
829
  const scored = await scoreRepo(files, mappings, provider);
752
- report(scored, { json: opts.json });
830
+ const totalTokens = provider.getTotalTokens();
831
+ report(scored, { json: opts.json, tokenUsage: totalTokens });
832
+ if (!opts.json) {
833
+ const display = totalTokens >= 1e3 ? `~${Math.ceil(totalTokens / 1e3)}k` : `~${totalTokens}`;
834
+ console.log(`
835
+ Tokens used: ${display}`);
836
+ }
753
837
  if (scored.overall < opts.minScore) {
754
838
  process.exit(1);
755
839
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sicilianwildcat/aiready",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Audit repositories for AI agent readiness",
5
5
  "license": "MIT",
6
6
  "author": "harikrishn4a",