speclock 5.2.6 → 5.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/src/mcp/server.js CHANGED
@@ -65,6 +65,8 @@ import {
65
65
  parseUnifiedDiff,
66
66
  } from "../core/engine.js";
67
67
  import { generateContext, generateContextPack } from "../core/context.js";
68
+ import { syncRules, getSyncFormats } from "../core/rules-sync.js";
69
+ import { getReplay, listSessions, formatReplay } from "../core/replay.js";
68
70
  import {
69
71
  readBrain,
70
72
  readEvents,
@@ -120,7 +122,7 @@ const PROJECT_ROOT =
120
122
  args.project || process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
121
123
 
122
124
  // --- MCP Server ---
123
- const VERSION = "5.2.6";
125
+ const VERSION = "5.3.1";
124
126
  const AUTHOR = "Sandeep Roy";
125
127
 
126
128
  const server = new McpServer(
@@ -1895,6 +1897,133 @@ server.tool(
1895
1897
  }
1896
1898
  );
1897
1899
 
1900
+ // --- Universal Rules Sync (v5.3) ---
1901
+
1902
+ // Tool 36: speclock_sync_rules
1903
+ server.tool(
1904
+ "speclock_sync_rules",
1905
+ "Sync SpecLock constraints to AI tool rules files. Generates .cursorrules, CLAUDE.md, AGENTS.md, .windsurfrules, copilot-instructions.md, GEMINI.md, and .aider.conf.yml from your SpecLock constraints. One source of truth for all AI tools. Use --format for a specific tool, or sync all at once.",
1906
+ {
1907
+ format: z.enum(["cursor", "claude", "agents", "windsurf", "copilot", "gemini", "codex", "aider", "all"]).optional().default("all").describe("Target format. 'all' syncs to every supported AI tool."),
1908
+ dryRun: z.boolean().optional().default(false).describe("Preview output without writing files"),
1909
+ append: z.boolean().optional().default(false).describe("Append to existing CLAUDE.md instead of overwriting (only for claude format)"),
1910
+ },
1911
+ async ({ format, dryRun, append }) => {
1912
+ const options = { dryRun };
1913
+ if (format && format !== "all") options.format = format;
1914
+ if (append) options.append = true;
1915
+
1916
+ const result = syncRules(PROJECT_ROOT, options);
1917
+
1918
+ if (result.errors.length > 0 && result.synced.length === 0) {
1919
+ return { content: [{ type: "text", text: `Sync failed: ${result.errors.join(", ")}` }], isError: true };
1920
+ }
1921
+
1922
+ const lines = [
1923
+ `## Rules Sync ${dryRun ? "(Preview)" : "Complete"}`,
1924
+ ``,
1925
+ `Constraints synced: ${result.lockCount} lock(s), ${result.decisionCount || 0} decision(s)`,
1926
+ ``,
1927
+ ];
1928
+
1929
+ for (const s of result.synced) {
1930
+ if (dryRun && s.content) {
1931
+ lines.push(`### ${s.name} → ${s.file}`);
1932
+ lines.push("```");
1933
+ lines.push(s.content);
1934
+ lines.push("```");
1935
+ lines.push("");
1936
+ } else {
1937
+ lines.push(`- **${s.name}** → \`${s.file}\` (${s.size} bytes)`);
1938
+ }
1939
+ }
1940
+
1941
+ if (!dryRun && result.synced.length > 0) {
1942
+ lines.push(``, `${result.synced.length} file(s) written. Your AI tools will now see SpecLock constraints.`);
1943
+ lines.push(``, `Tip: Commit these files to git so they're always in sync.`);
1944
+ }
1945
+
1946
+ if (result.errors.length > 0) {
1947
+ lines.push(``, `Warnings: ${result.errors.join(", ")}`);
1948
+ }
1949
+
1950
+ return { content: [{ type: "text", text: lines.join("\n") }] };
1951
+ }
1952
+ );
1953
+
1954
+ // Tool 37: speclock_replay
1955
+ server.tool(
1956
+ "speclock_replay",
1957
+ "Replay a session's activity log — shows exactly what AI agents tried and what SpecLock caught. Returns chronological event list with ALLOW/WARN/BLOCK verdicts, changes logged, locks added/removed, and session stats. Like a flight recorder for your AI coding sessions.",
1958
+ {
1959
+ sessionId: z.string().optional().describe("Specific session ID to replay. Omit to replay most recent session."),
1960
+ limit: z.number().optional().default(50).describe("Max events to return"),
1961
+ },
1962
+ async ({ sessionId, limit }) => {
1963
+ const replay = getReplay(PROJECT_ROOT, { sessionId, limit });
1964
+
1965
+ if (!replay.found) {
1966
+ return { content: [{ type: "text", text: replay.error }], isError: true };
1967
+ }
1968
+
1969
+ const formatted = formatReplay(replay);
1970
+ return { content: [{ type: "text", text: `## Incident Replay\n\n\`\`\`\n${formatted}\n\`\`\`` }] };
1971
+ }
1972
+ );
1973
+
1974
+ // Tool 38: speclock_list_sessions
1975
+ server.tool(
1976
+ "speclock_list_sessions",
1977
+ "List available sessions for replay. Shows session IDs, tools used, timestamps, and event counts.",
1978
+ {
1979
+ limit: z.number().optional().default(10).describe("Max sessions to list"),
1980
+ },
1981
+ async ({ limit }) => {
1982
+ const result = listSessions(PROJECT_ROOT, limit);
1983
+
1984
+ if (result.sessions.length === 0) {
1985
+ return { content: [{ type: "text", text: "No sessions recorded yet." }] };
1986
+ }
1987
+
1988
+ const lines = [`## Sessions (${result.total} total)`, ""];
1989
+ for (const s of result.sessions) {
1990
+ const current = s.isCurrent ? " **[ACTIVE]**" : "";
1991
+ lines.push(`- **${s.id}** — ${s.tool} — ${s.startedAt.substring(0, 16)} — ${s.events} events${current}`);
1992
+ if (s.summary && s.summary !== "(no summary)") {
1993
+ lines.push(` _${s.summary.substring(0, 80)}_`);
1994
+ }
1995
+ }
1996
+ lines.push("", "Use `speclock_replay` with a session ID to see full activity log.");
1997
+
1998
+ return { content: [{ type: "text", text: lines.join("\n") }] };
1999
+ }
2000
+ );
2001
+
2002
+ // Tool 39: speclock_list_sync_formats
2003
+ server.tool(
2004
+ "speclock_list_sync_formats",
2005
+ "List all available AI tool formats that SpecLock can sync constraints to. Shows format key, tool name, output file path, and description.",
2006
+ {},
2007
+ async () => {
2008
+ const formats = getSyncFormats();
2009
+ const lines = [
2010
+ `## Available Sync Formats`,
2011
+ ``,
2012
+ `| Format | Tool | Output File | Description |`,
2013
+ `|--------|------|-------------|-------------|`,
2014
+ ];
2015
+
2016
+ for (const f of formats) {
2017
+ lines.push(`| ${f.key} | ${f.name} | \`${f.file}\` | ${f.description} |`);
2018
+ }
2019
+
2020
+ lines.push(``);
2021
+ lines.push(`Use \`speclock_sync_rules\` with a specific format or "all" to sync.`);
2022
+
2023
+ return { content: [{ type: "text", text: lines.join("\n") }] };
2024
+ }
2025
+ );
2026
+
1898
2027
  // --- Smithery sandbox export ---
1899
2028
  export default function createSandboxServer() {
1900
2029
  return server;