vaspera-pm 2.3.0 → 2.3.1

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/README.md CHANGED
@@ -182,7 +182,7 @@ npx vaspera-pm install # Configures Claude Code
182
182
 
183
183
  Features:
184
184
  - Team collaboration & sharing
185
- - Usage tracking & quotas (Free: 5, Starter: 100, Pro: 500/month)
185
+ - Usage tracking & quotas (Free: 100, Starter: 500, Pro: 2,500/month)
186
186
  - Jira/Linear/GitHub/ADO integrations
187
187
  - Historical drift tracking
188
188
  - No Anthropic key needed (we proxy requests)
package/dist/cli.js CHANGED
@@ -25390,26 +25390,102 @@ function formatCodeIndexForAI(index, importanceScores) {
25390
25390
  }
25391
25391
  return lines.join("\n");
25392
25392
  }
25393
- async function crossValidateWithAST(docsRequirements, codeIndex, docsContent, semanticHints, importanceScores) {
25393
+ async function crossValidateWithAST(docsRequirements, codeIndex, docsContent, semanticHints, importanceScores, onProgress) {
25394
25394
  const codeIndexText = formatCodeIndexForAI(codeIndex, importanceScores);
25395
- let semanticHintsText = "";
25396
- if (semanticHints && semanticHints.size > 0) {
25397
- const hintLines = ["## SEMANTIC MATCH HINTS", ""];
25398
- hintLines.push("Pre-computed semantic matches between requirements and code (use these as starting points):");
25399
- hintLines.push("");
25400
- for (const [reqId, match] of semanticHints) {
25401
- if (match.matches.length > 0) {
25402
- hintLines.push(`### ${reqId}`);
25403
- hintLines.push(`Top matches (${match.confidence}% confidence):`);
25404
- for (const m of match.matches.slice(0, 3)) {
25405
- hintLines.push(` - ${m.type}: ${m.name} [${m.filePath}:${m.lineNumber}] (${Math.round(m.score * 100)}%)`);
25406
- }
25407
- hintLines.push("");
25408
- }
25395
+ const estimatedPromptSize = JSON.stringify(docsRequirements).length + codeIndexText.length + docsContent.substring(0, 3e4).length;
25396
+ const needsBatching = docsRequirements.length > MAX_REQUIREMENTS_PER_BATCH || estimatedPromptSize > MAX_CODE_INDEX_CHARS_PER_BATCH * 2;
25397
+ if (needsBatching) {
25398
+ if (onProgress) {
25399
+ const batchCount = Math.ceil(docsRequirements.length / MAX_REQUIREMENTS_PER_BATCH);
25400
+ onProgress("verification", 66, `Large codebase detected: splitting into ${batchCount} batches of ${MAX_REQUIREMENTS_PER_BATCH} requirements`);
25409
25401
  }
25410
- semanticHintsText = hintLines.join("\n");
25402
+ return crossValidateWithASTBatched(docsRequirements, codeIndex, codeIndexText, docsContent, semanticHints, importanceScores, onProgress);
25403
+ }
25404
+ return crossValidateWithASTSingle(docsRequirements, codeIndexText, docsContent, semanticHints);
25405
+ }
25406
+ async function crossValidateWithASTSingle(docsRequirements, codeIndexText, docsContent, semanticHints) {
25407
+ const semanticHintsText = buildSemanticHintsText(semanticHints, docsRequirements.map((r) => r.id));
25408
+ const systemPrompt = buildVerificationSystemPrompt(true);
25409
+ const docsReqJson = JSON.stringify(docsRequirements, null, 2);
25410
+ const userMessage = `## REQUIREMENTS TO VERIFY
25411
+ ${docsReqJson}
25412
+
25413
+ ## COMPLETE CODE INDEX
25414
+ ${codeIndexText}
25415
+ ${semanticHintsText ? `
25416
+ ${semanticHintsText}` : ""}
25417
+ ## DOCUMENTATION (for context)
25418
+ ${docsContent.substring(0, 3e4)}
25419
+
25420
+ For each requirement, find matching code in the index and determine verification status.
25421
+ Code entries include [file:line] references - use these in your evidence.
25422
+ ${semanticHintsText ? "Use the semantic match hints above as starting points for your verification." : ""}`;
25423
+ const result = await createJsonCompletion({
25424
+ systemPrompt,
25425
+ userMessage,
25426
+ model: "balanced",
25427
+ maxTokens: 16384
25428
+ });
25429
+ return parseVerificationResult(result);
25430
+ }
25431
+ async function crossValidateWithASTBatched(docsRequirements, codeIndex, codeIndexText, docsContent, semanticHints, importanceScores, onProgress) {
25432
+ const batches = [];
25433
+ for (let i = 0; i < docsRequirements.length; i += MAX_REQUIREMENTS_PER_BATCH) {
25434
+ batches.push(docsRequirements.slice(i, i + MAX_REQUIREMENTS_PER_BATCH));
25435
+ }
25436
+ const allRequirements = [];
25437
+ let totalInputTokens = 0;
25438
+ let totalOutputTokens = 0;
25439
+ const docsContextLimit = Math.min(2e4, Math.floor(3e4 / batches.length * 2));
25440
+ for (let batchIdx = 0; batchIdx < batches.length; batchIdx++) {
25441
+ const batch = batches[batchIdx];
25442
+ const isFirstBatch = batchIdx === 0;
25443
+ const batchIds = batch.map((r) => r.id);
25444
+ if (onProgress) {
25445
+ const batchPercent = 65 + Math.round(batchIdx / batches.length * 15);
25446
+ onProgress("verification", batchPercent, `Verifying batch ${batchIdx + 1}/${batches.length} (${batch.length} requirements)...`);
25447
+ }
25448
+ const batchSemanticHints = buildSemanticHintsText(semanticHints, batchIds);
25449
+ const systemPrompt = buildVerificationSystemPrompt(isFirstBatch);
25450
+ const docsReqJson = JSON.stringify(batch, null, 2);
25451
+ const batchLabel = `[Batch ${batchIdx + 1}/${batches.length}]`;
25452
+ const userMessage = `${batchLabel} Verify these ${batch.length} requirements against the code index.
25453
+
25454
+ ## REQUIREMENTS TO VERIFY
25455
+ ${docsReqJson}
25456
+
25457
+ ## COMPLETE CODE INDEX
25458
+ ${codeIndexText}
25459
+ ${batchSemanticHints ? `
25460
+ ${batchSemanticHints}` : ""}
25461
+ ## DOCUMENTATION (for context)
25462
+ ${docsContent.substring(0, docsContextLimit)}
25463
+
25464
+ For each requirement, find matching code in the index and determine verification status.
25465
+ Code entries include [file:line] references - use these in your evidence.
25466
+ ${batchSemanticHints ? "Use the semantic match hints above as starting points for your verification." : ""}`;
25467
+ const result = await createJsonCompletion({
25468
+ systemPrompt,
25469
+ userMessage,
25470
+ model: "balanced",
25471
+ maxTokens: 16384
25472
+ });
25473
+ const parsed = parseVerificationResult(result);
25474
+ allRequirements.push(...parsed.requirements);
25475
+ totalInputTokens += parsed.tokensUsed.input;
25476
+ totalOutputTokens += parsed.tokensUsed.output;
25411
25477
  }
25412
- const systemPrompt = `You are a specification verification expert. Compare documentation requirements against a structured code index.
25478
+ return {
25479
+ requirements: allRequirements,
25480
+ tokensUsed: { input: totalInputTokens, output: totalOutputTokens }
25481
+ };
25482
+ }
25483
+ function buildVerificationSystemPrompt(includeUndocumented) {
25484
+ const undocSection = includeUndocumented ? `
25485
+ For code features not matching any requirement:
25486
+ UNDOCUMENTED: Feature exists in code but has no corresponding requirement.` : "";
25487
+ const returnFormat = includeUndocumented ? 'Return JSON: {"requirements": [...], "undocumented": [...]}' : 'Return JSON: {"requirements": [...]}';
25488
+ return `You are a specification verification expert. Compare documentation requirements against a structured code index.
25413
25489
 
25414
25490
  The code index shows ALL functions, routes, classes, types, and CONFIG ENTRIES in the codebase with their EXACT locations.
25415
25491
  This is a complete inventory - if something is not listed, it does not exist.
@@ -25426,14 +25502,11 @@ For each requirement, find matching code OR config entries and determine:
25426
25502
  VERIFIED: The requirement is implemented in code OR config. Cite the specific file:line.
25427
25503
  DRIFT: The requirement is partially implemented or differs from spec. Explain the difference.
25428
25504
  UNIMPLEMENTED: No matching code or config found in the index. This is a gap.
25429
-
25430
- For code features not matching any requirement:
25431
- UNDOCUMENTED: Feature exists in code but has no corresponding requirement.
25432
-
25505
+ ${undocSection}
25433
25506
  CRITICAL: Provide file:line references for ALL matches. The code index includes exact locations.
25434
25507
  For config files, use the format "package.json:5" or "action.yml:12".
25435
25508
 
25436
- Return JSON: {"requirements": [...], "undocumented": [...]}
25509
+ ${returnFormat}
25437
25510
 
25438
25511
  Each requirement entry:
25439
25512
  {
@@ -25452,30 +25525,32 @@ Each requirement entry:
25452
25525
  "recommendation": "update_docs|update_code|needs_discussion"
25453
25526
  }
25454
25527
  }`;
25455
- const docsReqJson = JSON.stringify(docsRequirements, null, 2);
25456
- const userMessage = `## REQUIREMENTS TO VERIFY
25457
- ${docsReqJson}
25458
-
25459
- ## COMPLETE CODE INDEX
25460
- ${codeIndexText}
25461
- ${semanticHintsText ? `
25462
- ${semanticHintsText}` : ""}
25463
- ## DOCUMENTATION (for context)
25464
- ${docsContent.substring(0, 3e4)}
25465
-
25466
- For each requirement, find matching code in the index and determine verification status.
25467
- Code entries include [file:line] references - use these in your evidence.
25468
- ${semanticHintsText ? "Use the semantic match hints above as starting points for your verification." : ""}`;
25469
- const result = await createJsonCompletion({
25470
- systemPrompt,
25471
- userMessage,
25472
- model: "balanced",
25473
- maxTokens: 16384
25474
- });
25528
+ }
25529
+ function buildSemanticHintsText(semanticHints, requirementIds) {
25530
+ if (!semanticHints || semanticHints.size === 0) return "";
25531
+ const hintLines = ["## SEMANTIC MATCH HINTS", ""];
25532
+ hintLines.push("Pre-computed semantic matches between requirements and code (use these as starting points):");
25533
+ hintLines.push("");
25534
+ let hasHints = false;
25535
+ for (const reqId of requirementIds) {
25536
+ const match = semanticHints.get(reqId);
25537
+ if (match && match.matches.length > 0) {
25538
+ hasHints = true;
25539
+ hintLines.push(`### ${reqId}`);
25540
+ hintLines.push(`Top matches (${match.confidence}% confidence):`);
25541
+ for (const m of match.matches.slice(0, 3)) {
25542
+ hintLines.push(` - ${m.type}: ${m.name} [${m.filePath}:${m.lineNumber}] (${Math.round(m.score * 100)}%)`);
25543
+ }
25544
+ hintLines.push("");
25545
+ }
25546
+ }
25547
+ return hasHints ? hintLines.join("\n") : "";
25548
+ }
25549
+ function parseVerificationResult(result) {
25475
25550
  const allRequirements = [];
25476
25551
  const reqs = Array.isArray(result.data) ? result.data : result.data.requirements || [];
25477
25552
  allRequirements.push(...reqs);
25478
- if (result.data.undocumented) {
25553
+ if (!Array.isArray(result.data) && result.data.undocumented) {
25479
25554
  for (const undoc of result.data.undocumented) {
25480
25555
  allRequirements.push({
25481
25556
  id: undoc.id,
@@ -25643,7 +25718,8 @@ async function verifyWithAST(docsContent, codeFiles, options = {}) {
25643
25718
  codeIndex,
25644
25719
  docsContent,
25645
25720
  semanticHints,
25646
- importanceScores
25721
+ importanceScores,
25722
+ onProgress
25647
25723
  );
25648
25724
  progress("verification", 80, `Cross-validation complete: ${verified.requirements.length} items analyzed`);
25649
25725
  progress("finishing", 85, "Adjusting drift severity based on file importance...");
@@ -25947,7 +26023,7 @@ function formatDriftReportMarkdown(report) {
25947
26023
  }
25948
26024
  return lines.join("\n");
25949
26025
  }
25950
- var CHUNK_SIZE_THRESHOLD2, TARGET_CHUNK_SIZE2, CHUNK_OVERLAP2, DEFAULT_PARSE_CONCURRENCY, MAX_PARSE_CONCURRENCY, MAX_FUNCTIONS_THRESHOLD, MAX_TYPES_THRESHOLD, MAX_CONSTANTS_THRESHOLD, MAX_FILE_SIZE_BYTES, LARGE_FILE_WARNING_BYTES;
26026
+ var CHUNK_SIZE_THRESHOLD2, TARGET_CHUNK_SIZE2, CHUNK_OVERLAP2, MAX_REQUIREMENTS_PER_BATCH, MAX_CODE_INDEX_CHARS_PER_BATCH, DEFAULT_PARSE_CONCURRENCY, MAX_PARSE_CONCURRENCY, MAX_FUNCTIONS_THRESHOLD, MAX_TYPES_THRESHOLD, MAX_CONSTANTS_THRESHOLD, MAX_FILE_SIZE_BYTES, LARGE_FILE_WARNING_BYTES;
25951
26027
  var init_verification = __esm({
25952
26028
  "src/verification/index.ts"() {
25953
26029
  "use strict";
@@ -25963,6 +26039,8 @@ var init_verification = __esm({
25963
26039
  CHUNK_SIZE_THRESHOLD2 = 4e4;
25964
26040
  TARGET_CHUNK_SIZE2 = 35e3;
25965
26041
  CHUNK_OVERLAP2 = 3e3;
26042
+ MAX_REQUIREMENTS_PER_BATCH = 25;
26043
+ MAX_CODE_INDEX_CHARS_PER_BATCH = 8e4;
25966
26044
  DEFAULT_PARSE_CONCURRENCY = 8;
25967
26045
  MAX_PARSE_CONCURRENCY = 16;
25968
26046
  MAX_FUNCTIONS_THRESHOLD = 5e3;