speclore 0.1.7 → 0.1.9
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/index.js +31 -35
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +31 -35
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +10 -14
- package/dist/mcp/server.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -371,7 +371,7 @@ var init_version = __esm({
|
|
|
371
371
|
function readModuleConfig(config) {
|
|
372
372
|
const boundaries = [];
|
|
373
373
|
for (const [name, mod] of Object.entries(config.project.modules)) {
|
|
374
|
-
logger.debug(`Reading module config: ${name}
|
|
374
|
+
logger.debug(`Reading module config: ${name} -> ${mod.path}`);
|
|
375
375
|
boundaries.push({
|
|
376
376
|
name,
|
|
377
377
|
responsibility: mod.responsibility,
|
|
@@ -951,7 +951,7 @@ var init_cost_tracker = __esm({
|
|
|
951
951
|
totalTokens: promptTokens + completionTokens,
|
|
952
952
|
estimatedCostUsd: cost
|
|
953
953
|
});
|
|
954
|
-
logger.debug(`AI usage: ${model}
|
|
954
|
+
logger.debug(`AI usage: ${model} - ${promptTokens}+${completionTokens} tokens, ~$${cost.toFixed(4)}`);
|
|
955
955
|
return cost;
|
|
956
956
|
}
|
|
957
957
|
/**
|
|
@@ -1503,8 +1503,7 @@ var init_pdf_reader = __esm({
|
|
|
1503
1503
|
const uint8 = new Uint8Array(buffer);
|
|
1504
1504
|
const doc = await pdfjsLib.getDocument({
|
|
1505
1505
|
data: uint8,
|
|
1506
|
-
useWorkerFetch: false
|
|
1507
|
-
isEvalSupported: false
|
|
1506
|
+
useWorkerFetch: false
|
|
1508
1507
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1509
1508
|
}).promise;
|
|
1510
1509
|
const textParts = [];
|
|
@@ -1514,7 +1513,6 @@ var init_pdf_reader = __esm({
|
|
|
1514
1513
|
const pageText = textContent.items.map((item) => item.str).join("");
|
|
1515
1514
|
if (pageText) textParts.push(pageText);
|
|
1516
1515
|
}
|
|
1517
|
-
doc.destroy();
|
|
1518
1516
|
const text = textParts.join("\n");
|
|
1519
1517
|
return [{
|
|
1520
1518
|
id: source.replace(/\\/g, "/").replace(/\.pdf$/i, ""),
|
|
@@ -2871,8 +2869,7 @@ async function extractPdfText(dataBuffer) {
|
|
|
2871
2869
|
const uint8 = new Uint8Array(dataBuffer);
|
|
2872
2870
|
const doc = await pdfjsLib.getDocument({
|
|
2873
2871
|
data: uint8,
|
|
2874
|
-
useWorkerFetch: false
|
|
2875
|
-
isEvalSupported: false
|
|
2872
|
+
useWorkerFetch: false
|
|
2876
2873
|
}).promise;
|
|
2877
2874
|
const textParts = [];
|
|
2878
2875
|
for (let i = 1; i <= doc.numPages; i++) {
|
|
@@ -2881,7 +2878,6 @@ async function extractPdfText(dataBuffer) {
|
|
|
2881
2878
|
const pageText = textContent.items.map((item) => item.str).join("");
|
|
2882
2879
|
if (pageText) textParts.push(pageText);
|
|
2883
2880
|
}
|
|
2884
|
-
doc.destroy();
|
|
2885
2881
|
return textParts.join("\n");
|
|
2886
2882
|
}
|
|
2887
2883
|
async function parsePdfBuffer(dataBuffer, idHint = "document") {
|
|
@@ -3770,7 +3766,7 @@ async function generateConstraints(projectRoot2, features, _context, config) {
|
|
|
3770
3766
|
try {
|
|
3771
3767
|
await writer.write(content);
|
|
3772
3768
|
writtenFiles.push(writer.configFile);
|
|
3773
|
-
logger.info(`Constraint written via plugin: ${writer.toolName}
|
|
3769
|
+
logger.info(`Constraint written via plugin: ${writer.toolName} -> ${writer.configFile}`);
|
|
3774
3770
|
} catch (err) {
|
|
3775
3771
|
logger.warn(`Writer plugin '${tool}' failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3776
3772
|
}
|
|
@@ -3995,7 +3991,7 @@ function resolveFromMappingFile(projectRoot2, _feature, scenario) {
|
|
|
3995
3991
|
if (mapping.scenarios && scenario.name in mapping.scenarios) {
|
|
3996
3992
|
const entry = mapping.scenarios[scenario.name];
|
|
3997
3993
|
if (entry) {
|
|
3998
|
-
logger.debug(`Mapping file hit: ${scenario.name}
|
|
3994
|
+
logger.debug(`Mapping file hit: ${scenario.name} -> ${entry.testMethod}`);
|
|
3999
3995
|
return {
|
|
4000
3996
|
name: scenario.name,
|
|
4001
3997
|
status: "passed",
|
|
@@ -4026,7 +4022,7 @@ function resolveFromTag(projectRoot2, _feature, scenario) {
|
|
|
4026
4022
|
while ((match = tagRegex.exec(content)) !== null) {
|
|
4027
4023
|
if (match[1].trim() === scenario.name) {
|
|
4028
4024
|
const methodMatch = findTestMethodNearby(content, match.index);
|
|
4029
|
-
logger.debug(`Tag hit: ${scenario.name}
|
|
4025
|
+
logger.debug(`Tag hit: ${scenario.name} -> ${methodMatch}`);
|
|
4030
4026
|
return {
|
|
4031
4027
|
name: scenario.name,
|
|
4032
4028
|
status: "passed",
|
|
@@ -4470,7 +4466,7 @@ function acquireLock(specLoreDir) {
|
|
|
4470
4466
|
try {
|
|
4471
4467
|
writeFileSync16(lockPath, JSON.stringify(lockInfo), { flag: "wx", encoding: "utf-8" });
|
|
4472
4468
|
} catch {
|
|
4473
|
-
logger.debug("Lock write failed
|
|
4469
|
+
logger.debug("Lock write failed - another process may have acquired it");
|
|
4474
4470
|
return false;
|
|
4475
4471
|
}
|
|
4476
4472
|
logger.debug(`Lock acquired by PID ${process.pid}`);
|
|
@@ -5034,10 +5030,10 @@ async function startMcpServer() {
|
|
|
5034
5030
|
mkdirSync16(specLoreDir, { recursive: true });
|
|
5035
5031
|
const locked = acquireLock(specLoreDir);
|
|
5036
5032
|
if (!locked) {
|
|
5037
|
-
logger.warn("Could not acquire lock
|
|
5033
|
+
logger.warn("Could not acquire lock - another SpecLore instance may be running.");
|
|
5038
5034
|
}
|
|
5039
5035
|
} catch {
|
|
5040
|
-
logger.warn("Could not acquire lock
|
|
5036
|
+
logger.warn("Could not acquire lock - .speclore/ directory may not exist.");
|
|
5041
5037
|
}
|
|
5042
5038
|
const transport = new StdioServerTransport();
|
|
5043
5039
|
await server.connect(transport);
|
|
@@ -5377,16 +5373,16 @@ function registerSetupCommand(program) {
|
|
|
5377
5373
|
if (opts.verbose) logger.setLevel("debug");
|
|
5378
5374
|
const projectRoot2 = process.cwd();
|
|
5379
5375
|
logger.info("SpecLore Setup");
|
|
5380
|
-
logger.info("
|
|
5376
|
+
logger.info("---------------");
|
|
5381
5377
|
const tools = detectAITools(projectRoot2);
|
|
5382
5378
|
if (tools.length === 0) {
|
|
5383
5379
|
logger.warn("No supported AI tools detected.");
|
|
5384
5380
|
logger.info("");
|
|
5385
5381
|
logger.info(" To enable MCP integration, open your AI client in this project first,");
|
|
5386
5382
|
logger.info(" then re-run setup. Or use manual configuration:");
|
|
5387
|
-
logger.info(" speclore mcp add cursor
|
|
5388
|
-
logger.info(" speclore mcp add claude
|
|
5389
|
-
logger.info(" speclore mcp add qoder
|
|
5383
|
+
logger.info(" speclore mcp add cursor - configure for Cursor");
|
|
5384
|
+
logger.info(" speclore mcp add claude - configure for Claude Code");
|
|
5385
|
+
logger.info(" speclore mcp add qoder - configure for Qoder");
|
|
5390
5386
|
logger.info("");
|
|
5391
5387
|
} else {
|
|
5392
5388
|
logger.info(`Detected AI tools: ${tools.map((t) => t.tool).join(", ")}`);
|
|
@@ -5406,18 +5402,18 @@ function registerSetupCommand(program) {
|
|
|
5406
5402
|
logger.info("");
|
|
5407
5403
|
logger.info("Setup complete! Next steps:");
|
|
5408
5404
|
logger.info("");
|
|
5409
|
-
logger.info(" Option A
|
|
5410
|
-
logger.info(' speclore spec "your requirement"
|
|
5411
|
-
logger.info(" speclore code
|
|
5412
|
-
logger.info(" speclore verify
|
|
5405
|
+
logger.info(" Option A - CLI workflow (terminal users):");
|
|
5406
|
+
logger.info(' speclore spec "your requirement" -> generate .feature');
|
|
5407
|
+
logger.info(" speclore code -> generate constraints + tests");
|
|
5408
|
+
logger.info(" speclore verify -> run acceptance");
|
|
5413
5409
|
logger.info("");
|
|
5414
|
-
logger.info(" Option B
|
|
5410
|
+
logger.info(" Option B - AI client workflow (recommended):");
|
|
5415
5411
|
logger.info(" Open Cursor / Qoder / Claude Code and start chatting.");
|
|
5416
|
-
logger.info(" MCP is already configured
|
|
5412
|
+
logger.info(" MCP is already configured - AI handles the full pipeline.");
|
|
5417
5413
|
logger.info("");
|
|
5418
|
-
logger.info(" Optional
|
|
5419
|
-
logger.info(" speclore init
|
|
5420
|
-
logger.info(" (Not required
|
|
5414
|
+
logger.info(" Optional - Pre-scan project context:");
|
|
5415
|
+
logger.info(" speclore init -> scan modules/entities/APIs for better AI context");
|
|
5416
|
+
logger.info(" (Not required - context is auto-built on first spec/code call)");
|
|
5421
5417
|
});
|
|
5422
5418
|
}
|
|
5423
5419
|
|
|
@@ -5431,7 +5427,7 @@ function registerInitCommand(program) {
|
|
|
5431
5427
|
if (opts.verbose) logger.setLevel("debug");
|
|
5432
5428
|
const projectRoot2 = process.cwd();
|
|
5433
5429
|
logger.info("SpecLore Init");
|
|
5434
|
-
logger.info("
|
|
5430
|
+
logger.info("--------------");
|
|
5435
5431
|
const config = loadConfig(projectRoot2);
|
|
5436
5432
|
logger.info(`Project: ${config.project.name || "(unnamed)"}`);
|
|
5437
5433
|
const context = buildContext(projectRoot2, config);
|
|
@@ -5838,7 +5834,7 @@ function registerMcpConfigCommands(program) {
|
|
|
5838
5834
|
if (!client) return;
|
|
5839
5835
|
logger.info(`Configuring MCP for ${clientLabel(client)}...`);
|
|
5840
5836
|
writeMcpForClient(projectRoot2, client);
|
|
5841
|
-
logger.info(
|
|
5837
|
+
logger.info(`[OK] ${clientLabel(client)} MCP configured at ${getMcpPath(projectRoot2, client)}`);
|
|
5842
5838
|
});
|
|
5843
5839
|
mcpCmd.command("remove <client>").description("Remove SpecLore MCP config from a specific AI client (cursor | claude | qoder)").action((clientArg) => {
|
|
5844
5840
|
const projectRoot2 = process.cwd();
|
|
@@ -5851,7 +5847,7 @@ function registerMcpConfigCommands(program) {
|
|
|
5851
5847
|
}
|
|
5852
5848
|
const removed = removeMcpServerEntry(mcpPath, "speclore");
|
|
5853
5849
|
if (removed) {
|
|
5854
|
-
logger.info(
|
|
5850
|
+
logger.info(`[OK] Removed speclore from ${clientLabel(client)}: ${mcpPath}`);
|
|
5855
5851
|
} else {
|
|
5856
5852
|
logger.info(`${clientLabel(client)}: speclore entry not found in ${mcpPath}`);
|
|
5857
5853
|
}
|
|
@@ -5859,24 +5855,24 @@ function registerMcpConfigCommands(program) {
|
|
|
5859
5855
|
mcpCmd.command("list").description("Show current SpecLore MCP configuration status for all clients").action(() => {
|
|
5860
5856
|
const projectRoot2 = process.cwd();
|
|
5861
5857
|
logger.info("SpecLore MCP Configuration Status");
|
|
5862
|
-
logger.info("
|
|
5858
|
+
logger.info("------------------------------");
|
|
5863
5859
|
let anyFound = false;
|
|
5864
5860
|
for (const client of VALID_CLIENTS) {
|
|
5865
5861
|
const mcpPath = getMcpPath(projectRoot2, client);
|
|
5866
5862
|
if (!existsSync27(mcpPath)) {
|
|
5867
|
-
logger.info(`
|
|
5863
|
+
logger.info(` [-] ${clientLabel(client)}: no config file (${mcpPath})`);
|
|
5868
5864
|
continue;
|
|
5869
5865
|
}
|
|
5870
5866
|
try {
|
|
5871
5867
|
const config = JSON.parse(readFileSync20(mcpPath, "utf-8"));
|
|
5872
5868
|
if (config.mcpServers?.["speclore"]) {
|
|
5873
|
-
logger.info(`
|
|
5869
|
+
logger.info(` [OK] ${clientLabel(client)}: configured -> ${mcpPath}`);
|
|
5874
5870
|
anyFound = true;
|
|
5875
5871
|
} else {
|
|
5876
|
-
logger.info(`
|
|
5872
|
+
logger.info(` [-] ${clientLabel(client)}: config file exists but speclore not registered -> ${mcpPath}`);
|
|
5877
5873
|
}
|
|
5878
5874
|
} catch {
|
|
5879
|
-
logger.info(`
|
|
5875
|
+
logger.info(` [!] ${clientLabel(client)}: failed to parse ${mcpPath}`);
|
|
5880
5876
|
}
|
|
5881
5877
|
}
|
|
5882
5878
|
if (!anyFound) {
|