const fs = require("fs"); const path = require("path"); const { spawn } = require("child_process"); const root = "C:\\project\\FT546WMS-SHANDOW"; const outDir = "C:\\project\\FT546WMS-shadow-export-0828"; const config = path.join(root, ".cursorrules-mcp.json"); const database = "FT546-wms"; fs.mkdirSync(outDir, { recursive: true }); function redact(value) { return String(value).replace(/password\s*[:=]\s*[^\s,}]+/gi, "password=[redacted]"); } function createClient() { const child = spawn("powershell.exe", [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\project\\sqlserver-mcp-server\\start.ps1", "-ConfigPath", config ], { stdio: ["pipe", "pipe", "pipe"] }); let buffer = ""; let nextId = 1; const pending = new Map(); child.stdout.on("data", (chunk) => { buffer += chunk.toString(); const lines = buffer.split(/\r?\n/); buffer = lines.pop(); for (const line of lines) { if (!line.trim()) continue; try { const message = JSON.parse(line); const resolve = pending.get(message.id); if (resolve) { pending.delete(message.id); resolve(message); } } catch { // The MCP protocol stream is line-delimited JSON. } } }); function call(method, params) { return new Promise((resolve, reject) => { const id = nextId++; pending.set(id, (message) => message.error ? reject(new Error(message.error.message || "MCP error")) : resolve(message.result)); child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n"); }); } return { child, call }; } function parseResult(result) { const item = (result.content || []).find((entry) => entry.type === "text"); if (!item) throw new Error("MCP returned no result text"); try { return JSON.parse(item.text); } catch { throw new Error(item.text.replace(/password\s*[:=]\s*[^\s,}]+/gi, "password=[redacted]").replace(/\r?\n/g, " ")); } } async function main() { const client = createClient(); try { await client.call("initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "ft546-shadow-followup", version: "1.0" } }); client.child.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n"); const start = "2026-08-18T00:00:00"; const end = new Date().toISOString().slice(0, 19); const warehouse = "01"; const callRead = async (name, query) => ({ name, result: parseResult(await client.call("tools/call", { name: "sqlserver_read_data", arguments: { databaseName: database, query, response_format: "json" } })) }); const queries = [ ["inventory_counts", `SELECT 'legacy_bas_stock' AS source_name, COUNT_BIG(*) AS row_count FROM dbo.bas_stock s INNER JOIN dbo.bas_cargo_space c ON c.cargo_space_id = s.cargo_space_id AND c.warehouse_id = '${warehouse}' UNION ALL SELECT 'shadow_inventory_item_snapshot', COUNT_BIG(*) FROM alg.inventory_item_snapshot WHERE warehouse_id = '${warehouse}'`], ["legacy_missing_from_shadow", `SELECT TOP (1000) c.warehouse_id, s.stock_id, s.cargo_space_id, s.tray_code, s.spare4 AS legacy_project_no, s.product_id AS legacy_product_no, s.lot_no, s.in_date, s.quantity, s.out_quantity FROM dbo.bas_stock s INNER JOIN dbo.bas_cargo_space c ON c.cargo_space_id = s.cargo_space_id AND c.warehouse_id = '${warehouse}' LEFT JOIN alg.inventory_item_snapshot i ON i.warehouse_id = c.warehouse_id AND i.stock_id = s.stock_id WHERE i.stock_id IS NULL ORDER BY s.stock_id`], ["shadow_missing_from_legacy", `SELECT TOP (1000) i.warehouse_id, i.stock_id, i.space_id, i.tray_code, i.project_no, i.product_no, i.lot_no, i.inbound_time_raw, i.quantity, i.out_quantity FROM alg.inventory_item_snapshot i LEFT JOIN dbo.bas_stock s ON s.stock_id = i.stock_id WHERE i.warehouse_id = '${warehouse}' AND s.stock_id IS NULL ORDER BY i.stock_id`], ["matched_field_difference_counts", `SELECT COUNT_BIG(*) AS matched_stock_count, SUM(CASE WHEN ISNULL(LTRIM(RTRIM(s.cargo_space_id)), '') <> ISNULL(LTRIM(RTRIM(i.space_id)), '') THEN 1 ELSE 0 END) AS space_mismatch_count, SUM(CASE WHEN ISNULL(LTRIM(RTRIM(s.tray_code)), '') <> ISNULL(LTRIM(RTRIM(i.tray_code)), '') THEN 1 ELSE 0 END) AS tray_mismatch_count, SUM(CASE WHEN ISNULL(LTRIM(RTRIM(s.spare4)), '') <> ISNULL(LTRIM(RTRIM(i.project_no)), '') THEN 1 ELSE 0 END) AS project_mismatch_count, SUM(CASE WHEN ISNULL(LTRIM(RTRIM(s.product_id)), '') <> ISNULL(LTRIM(RTRIM(i.product_no)), '') THEN 1 ELSE 0 END) AS product_mismatch_count, SUM(CASE WHEN ISNULL(LTRIM(RTRIM(s.lot_no)), '') <> ISNULL(LTRIM(RTRIM(i.lot_no)), '') THEN 1 ELSE 0 END) AS lot_mismatch_count, SUM(CASE WHEN ISNULL(s.quantity, 0) <> ISNULL(i.quantity, 0) THEN 1 ELSE 0 END) AS quantity_mismatch_count, SUM(CASE WHEN ISNULL(s.out_quantity, 0) <> ISNULL(i.out_quantity, 0) THEN 1 ELSE 0 END) AS out_quantity_mismatch_count FROM dbo.bas_stock s INNER JOIN dbo.bas_cargo_space c ON c.cargo_space_id = s.cargo_space_id AND c.warehouse_id = '${warehouse}' INNER JOIN alg.inventory_item_snapshot i ON i.warehouse_id = c.warehouse_id AND i.stock_id = s.stock_id`], ["field_difference_rows", `SELECT TOP (1000) s.stock_id, s.cargo_space_id AS legacy_space_id, i.space_id AS shadow_space_id, s.tray_code AS legacy_tray_code, i.tray_code AS shadow_tray_code, s.spare4 AS legacy_project_no, i.project_no AS shadow_project_no, s.product_id AS legacy_product_no, i.product_no AS shadow_product_no, s.lot_no AS legacy_lot_no, i.lot_no AS shadow_lot_no, s.quantity AS legacy_quantity, i.quantity AS shadow_quantity, s.out_quantity AS legacy_out_quantity, i.out_quantity AS shadow_out_quantity FROM dbo.bas_stock s INNER JOIN dbo.bas_cargo_space c ON c.cargo_space_id = s.cargo_space_id AND c.warehouse_id = '${warehouse}' INNER JOIN alg.inventory_item_snapshot i ON i.warehouse_id = c.warehouse_id AND i.stock_id = s.stock_id WHERE ISNULL(LTRIM(RTRIM(s.cargo_space_id)), '') <> ISNULL(LTRIM(RTRIM(i.space_id)), '') OR ISNULL(LTRIM(RTRIM(s.tray_code)), '') <> ISNULL(LTRIM(RTRIM(i.tray_code)), '') OR ISNULL(LTRIM(RTRIM(s.spare4)), '') <> ISNULL(LTRIM(RTRIM(i.project_no)), '') OR ISNULL(LTRIM(RTRIM(s.product_id)), '') <> ISNULL(LTRIM(RTRIM(i.product_no)), '') OR ISNULL(LTRIM(RTRIM(s.lot_no)), '') <> ISNULL(LTRIM(RTRIM(i.lot_no)), '') OR ISNULL(s.quantity, 0) <> ISNULL(i.quantity, 0) OR ISNULL(s.out_quantity, 0) <> ISNULL(i.out_quantity, 0) ORDER BY s.stock_id`], ["request_match_counts", `SELECT COUNT_BIG(*) AS compare_count, SUM(CASE WHEN j_by_id.job_id IS NOT NULL THEN 1 ELSE 0 END) AS matched_by_job_id, SUM(CASE WHEN j_by_id.job_id IS NULL AND j_by_task.job_id IS NOT NULL THEN 1 ELSE 0 END) AS matched_by_task_no, SUM(CASE WHEN j_by_id.job_id IS NULL AND j_by_task.job_id IS NULL THEN 1 ELSE 0 END) AS unmatched_count FROM alg.decision_log d OUTER APPLY (SELECT LTRIM(RTRIM(SUBSTRING(d.reason, CHARINDEX('request=', d.reason) + 8, CHARINDEX(CHAR(59), d.reason + CHAR(59), CHARINDEX('request=', d.reason)) - (CHARINDEX('request=', d.reason) + 8)))) AS request_id) r LEFT JOIN dbo.mydo_job j_by_id ON j_by_id.warehouse_id = '${warehouse}' AND CONVERT(varchar(255), j_by_id.job_id) = r.request_id LEFT JOIN dbo.mydo_job j_by_task ON j_by_task.warehouse_id = '${warehouse}' AND CONVERT(varchar(255), j_by_task.task_no) = r.request_id WHERE d.decision_type = 'OUTBOUND_COMPARE' AND d.created_at >= '${start}' AND d.created_at < '${end}'`], ["request_match_details", `SELECT TOP (300) d.created_at, d.result_code, r.request_id, j_by_id.job_id AS job_id_match, j_by_id.task_no AS job_id_match_task_no, j_by_id.tray_code AS job_id_match_tray_code, j_by_id.status AS job_id_match_status, j_by_id.in_or_out AS job_id_match_in_or_out, j_by_task.job_id AS task_no_match_job_id, j_by_task.task_no AS task_no_match_task_no, j_by_task.tray_code AS task_no_match_tray_code, j_by_task.status AS task_no_match_status, d.reason FROM alg.decision_log d OUTER APPLY (SELECT LTRIM(RTRIM(SUBSTRING(d.reason, CHARINDEX('request=', d.reason) + 8, CHARINDEX(CHAR(59), d.reason + CHAR(59), CHARINDEX('request=', d.reason)) - (CHARINDEX('request=', d.reason) + 8)))) AS request_id) r LEFT JOIN dbo.mydo_job j_by_id ON j_by_id.warehouse_id = '${warehouse}' AND CONVERT(varchar(255), j_by_id.job_id) = r.request_id LEFT JOIN dbo.mydo_job j_by_task ON j_by_task.warehouse_id = '${warehouse}' AND CONVERT(varchar(255), j_by_task.task_no) = r.request_id WHERE d.decision_type = 'OUTBOUND_COMPARE' AND d.created_at >= '${start}' AND d.created_at < '${end}' ORDER BY d.created_at`] ]; const results = []; for (const [name, query] of queries) results.push(await callRead(name, query)); const output = { export_type: "shadow-followup", database, warehouse_id: warehouse, export_start_utc: start, export_end_utc: end, results }; fs.writeFileSync(path.join(outDir, "shadow-followup.sql"), queries.map(([name, query]) => `-- ${name}\n${query}`).join(";\n\n") + ";\n", "utf8"); fs.writeFileSync(path.join(outDir, "shadow-followup.txt"), JSON.stringify(output, null, 2) + "\n", "utf8"); console.log("FOLLOWUP_EXPORT_OK shadow-followup.txt result_sets=" + results.length); } finally { client.child.kill(); } } main().catch((error) => { console.error("FOLLOWUP_EXPORT_FAILED " + redact(error.message)); process.exitCode = 1; });