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