@sicilianwildcat/aiready 0.1.1 → 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.
- package/dist/cli.js +120 -11
- 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
|
|
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.
|
|
217
|
-
const text = await provider.chat(
|
|
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}`,
|
|
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
|
-
|
|
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
|
-
|
|
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,15 +681,42 @@ var OllamaProvider = class {
|
|
|
605
681
|
{ role: "user", content: user }
|
|
606
682
|
]
|
|
607
683
|
});
|
|
608
|
-
|
|
684
|
+
const text = response.choices[0]?.message?.content ?? "";
|
|
685
|
+
this.totalTokens += estimateChatTokens(system, user, text);
|
|
686
|
+
return text;
|
|
609
687
|
}
|
|
610
688
|
};
|
|
689
|
+
var NON_CHAT_MODEL_SUBSTRINGS = [
|
|
690
|
+
"codex",
|
|
691
|
+
// coding agent — different API surface
|
|
692
|
+
"image",
|
|
693
|
+
// image generation
|
|
694
|
+
"tts",
|
|
695
|
+
// text to speech
|
|
696
|
+
"transcribe",
|
|
697
|
+
// audio to text
|
|
698
|
+
"whisper",
|
|
699
|
+
// audio transcription
|
|
700
|
+
"computer-use",
|
|
701
|
+
// computer use agent
|
|
702
|
+
"search-preview",
|
|
703
|
+
// search-augmented interface
|
|
704
|
+
"realtime"
|
|
705
|
+
// realtime audio API
|
|
706
|
+
];
|
|
707
|
+
function isChatCompatibleOpenAIModel(id) {
|
|
708
|
+
const isChatFamily = id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3");
|
|
709
|
+
if (!isChatFamily || id.includes(":")) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
return !NON_CHAT_MODEL_SUBSTRINGS.some((s) => id.includes(s));
|
|
713
|
+
}
|
|
611
714
|
async function listOpenAIModels(apiKey) {
|
|
612
715
|
try {
|
|
613
716
|
const baseURL = process.env["OPENAI_BASE_URL"];
|
|
614
717
|
const client = new import_openai.default({ apiKey, ...baseURL ? { baseURL } : {} });
|
|
615
718
|
const response = await client.models.list();
|
|
616
|
-
const chatModels = response.data.filter((m) =>
|
|
719
|
+
const chatModels = response.data.filter((m) => isChatCompatibleOpenAIModel(m.id)).sort((a, b) => b.created - a.created).map((m) => ({ id: m.id, label: m.id }));
|
|
617
720
|
return chatModels.length > 0 ? chatModels : OPENAI_FALLBACK_MODELS;
|
|
618
721
|
} catch {
|
|
619
722
|
return OPENAI_FALLBACK_MODELS;
|
|
@@ -700,7 +803,7 @@ async function promptModelId(provider, apiKey) {
|
|
|
700
803
|
}
|
|
701
804
|
const models = await listOpenAIModels(apiKey);
|
|
702
805
|
return (0, import_prompts.select)({
|
|
703
|
-
message: "Select model:",
|
|
806
|
+
message: "Select model: (chat-compatible models only)",
|
|
704
807
|
choices: models.map((m) => ({ name: m.label, value: m.id }))
|
|
705
808
|
});
|
|
706
809
|
}
|
|
@@ -724,7 +827,13 @@ async function runAudit(target, opts) {
|
|
|
724
827
|
const files = loadRepo(targetDir);
|
|
725
828
|
const mappings = await mapFiles(files.mdFiles, provider);
|
|
726
829
|
const scored = await scoreRepo(files, mappings, provider);
|
|
727
|
-
|
|
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
|
+
}
|
|
728
837
|
if (scored.overall < opts.minScore) {
|
|
729
838
|
process.exit(1);
|
|
730
839
|
}
|