@longtable/cli 0.1.10 → 0.1.11

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, readFileSync, statSync } from "node:fs";
3
- import { mkdtemp, rm } from "node:fs/promises";
3
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
4
4
  import { execSync } from "node:child_process";
5
5
  import { emitKeypressEvents } from "node:readline";
6
6
  import { createInterface } from "node:readline/promises";
@@ -39,6 +39,10 @@ const ANSI = {
39
39
  cyan: "\u001B[36m",
40
40
  green: "\u001B[32m"
41
41
  };
42
+ const LONGTABLE_MCP_SERVER_NAME = "longtable-state";
43
+ const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.11";
44
+ const LONGTABLE_MCP_MARKER_START = "# LongTable state MCP START";
45
+ const LONGTABLE_MCP_MARKER_END = "# LongTable state MCP END";
42
46
  function style(text, prefix) {
43
47
  return `${prefix}${text}${ANSI.reset}`;
44
48
  }
@@ -80,6 +84,7 @@ function usage() {
80
84
  " longtable roles [--json]",
81
85
  " longtable show [--json] [--path <file>]",
82
86
  " longtable install [--json] [--path <file>] [--runtime-path <file>]",
87
+ " longtable mcp install [--provider codex|claude|all] [--write] [--json] [--codex-config <path>] [--claude-settings <path>] [--package <spec>]",
83
88
  " longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
84
89
  " longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
85
90
  " longtable panel [--prompt <text>] [--role <role[,role]>] [--mode review|critique|draft|commit] [--visibility synthesis_only|show_on_conflict|always_visible] [--print] [--json] [--setup <path>] [--cwd <path>]",
@@ -94,6 +99,7 @@ function usage() {
94
99
  " longtable claude install-skills [--dir <path>]",
95
100
  " longtable claude remove-skills [--dir <path>]",
96
101
  " longtable claude status [--dir <path>] [--json]",
102
+ " longtable mcp install --provider all",
97
103
  "",
98
104
  "Examples:",
99
105
  " longtable init --flow interview --provider codex --install-skills",
@@ -113,13 +119,13 @@ function parseArgs(argv) {
113
119
  const values = {};
114
120
  let subcommand = maybeSubcommand;
115
121
  const modeCommand = command && VALID_MODES.has(command);
116
- const directCommand = command && ["init", "start", "resume", "doctor", "status", "roles", "show", "install", "codex", "claude", "ask", "question", "panel", "decide"].includes(command);
122
+ const directCommand = command && ["init", "start", "resume", "doctor", "status", "roles", "show", "install", "mcp", "codex", "claude", "ask", "question", "panel", "decide"].includes(command);
117
123
  let startIndex = 1;
118
124
  if (modeCommand) {
119
125
  subcommand = undefined;
120
126
  startIndex = 1;
121
127
  }
122
- else if (command === "codex" || command === "claude") {
128
+ else if (command === "codex" || command === "claude" || command === "mcp") {
123
129
  startIndex = 2;
124
130
  }
125
131
  else if (directCommand) {
@@ -778,6 +784,147 @@ async function runInstall(args) {
778
784
  }
779
785
  console.log(renderInstallSummary(result));
780
786
  }
787
+ function resolveMcpProviders(value) {
788
+ if (value === "codex" || value === "claude") {
789
+ return [value];
790
+ }
791
+ return ["codex", "claude"];
792
+ }
793
+ function resolveMcpPackageSpec(args) {
794
+ return typeof args.package === "string" && args.package.trim()
795
+ ? args.package.trim()
796
+ : `@longtable/mcp@${LONGTABLE_MCP_PACKAGE_VERSION}`;
797
+ }
798
+ function resolveCodexMcpConfigPath(args) {
799
+ return resolve(normalizeUserPath(typeof args["codex-config"] === "string" && args["codex-config"].trim()
800
+ ? args["codex-config"].trim()
801
+ : "~/.codex/config.toml"));
802
+ }
803
+ function resolveClaudeMcpSettingsPath(args) {
804
+ return resolve(normalizeUserPath(typeof args["claude-settings"] === "string" && args["claude-settings"].trim()
805
+ ? args["claude-settings"].trim()
806
+ : "~/.claude/settings.json"));
807
+ }
808
+ function escapeTomlString(value) {
809
+ return JSON.stringify(value);
810
+ }
811
+ function renderCodexMcpBlock(serverName, command, mcpArgs) {
812
+ return [
813
+ LONGTABLE_MCP_MARKER_START,
814
+ `[mcp_servers.${serverName}]`,
815
+ `command = ${escapeTomlString(command)}`,
816
+ `args = [${mcpArgs.map((arg) => escapeTomlString(arg)).join(", ")}]`,
817
+ LONGTABLE_MCP_MARKER_END
818
+ ].join("\n");
819
+ }
820
+ function replaceMarkedCodexMcpBlock(existing, block, serverName) {
821
+ const markerPattern = new RegExp(`${LONGTABLE_MCP_MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${LONGTABLE_MCP_MARKER_END.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`, "m");
822
+ const serverPattern = new RegExp(`\\n?\\[mcp_servers\\.${serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\][\\s\\S]*?(?=\\n\\[|$)`, "m");
823
+ const trimmed = existing.replace(markerPattern, "").replace(serverPattern, "").trimEnd();
824
+ return trimmed ? `${trimmed}\n\n${block}\n` : `${block}\n`;
825
+ }
826
+ async function writeCodexMcpConfig(path, block, serverName) {
827
+ const existing = existsSync(path) ? await readFile(path, "utf8") : "";
828
+ const updated = replaceMarkedCodexMcpBlock(existing, block, serverName);
829
+ await mkdir(dirname(path), { recursive: true });
830
+ await writeFile(path, updated, "utf8");
831
+ return updated;
832
+ }
833
+ function renderClaudeMcpJson(serverName, command, mcpArgs) {
834
+ return JSON.stringify({
835
+ mcpServers: {
836
+ [serverName]: {
837
+ command,
838
+ args: mcpArgs
839
+ }
840
+ }
841
+ }, null, 2);
842
+ }
843
+ async function writeClaudeMcpSettings(path, serverName, command, mcpArgs) {
844
+ let settings = {};
845
+ if (existsSync(path)) {
846
+ const raw = await readFile(path, "utf8");
847
+ settings = raw.trim() ? JSON.parse(raw) : {};
848
+ }
849
+ const existingServers = typeof settings.mcpServers === "object" && settings.mcpServers !== null && !Array.isArray(settings.mcpServers)
850
+ ? settings.mcpServers
851
+ : {};
852
+ settings.mcpServers = {
853
+ ...existingServers,
854
+ [serverName]: {
855
+ command,
856
+ args: mcpArgs
857
+ }
858
+ };
859
+ const updated = JSON.stringify(settings, null, 2);
860
+ await mkdir(dirname(path), { recursive: true });
861
+ await writeFile(path, `${updated}\n`, "utf8");
862
+ return `${updated}\n`;
863
+ }
864
+ function renderMcpInstallSummary(result) {
865
+ const lines = [
866
+ "LongTable MCP transport",
867
+ `- server: ${result.serverName}`,
868
+ `- package: ${result.packageSpec}`,
869
+ `- command: ${result.command} ${result.args.join(" ")}`,
870
+ `- mode: ${result.write ? "wrote provider config" : "printed config only"}`,
871
+ ""
872
+ ];
873
+ for (const target of result.targets) {
874
+ lines.push(`${target.provider} (${target.path})`);
875
+ lines.push("```" + target.format);
876
+ lines.push(target.content.trimEnd());
877
+ lines.push("```");
878
+ lines.push("");
879
+ }
880
+ if (!result.write) {
881
+ lines.push("Run again with `--write` to update these provider config files.");
882
+ }
883
+ return lines.join("\n").trimEnd();
884
+ }
885
+ async function runMcpSubcommand(subcommand, args) {
886
+ if (!subcommand || subcommand === "install" || subcommand === "print-config") {
887
+ const serverName = typeof args.name === "string" && args.name.trim()
888
+ ? args.name.trim()
889
+ : LONGTABLE_MCP_SERVER_NAME;
890
+ const packageSpec = resolveMcpPackageSpec(args);
891
+ const command = typeof args.command === "string" && args.command.trim() ? args.command.trim() : "npx";
892
+ const mcpArgs = command === "npx" ? ["-y", packageSpec] : [packageSpec];
893
+ const providers = resolveMcpProviders(args.provider);
894
+ const write = args.write === true;
895
+ const targets = [];
896
+ for (const provider of providers) {
897
+ if (provider === "codex") {
898
+ const path = resolveCodexMcpConfigPath(args);
899
+ const block = renderCodexMcpBlock(serverName, command, mcpArgs);
900
+ const content = write ? await writeCodexMcpConfig(path, block, serverName) : block;
901
+ targets.push({ provider, path, format: "toml", content });
902
+ }
903
+ if (provider === "claude") {
904
+ const path = resolveClaudeMcpSettingsPath(args);
905
+ const content = write
906
+ ? await writeClaudeMcpSettings(path, serverName, command, mcpArgs)
907
+ : renderClaudeMcpJson(serverName, command, mcpArgs);
908
+ targets.push({ provider, path, format: "json", content });
909
+ }
910
+ }
911
+ const result = {
912
+ serverName,
913
+ packageSpec,
914
+ command,
915
+ args: mcpArgs,
916
+ write,
917
+ targets
918
+ };
919
+ if (args.json === true) {
920
+ console.log(JSON.stringify(result, null, 2));
921
+ return;
922
+ }
923
+ console.log(renderMcpInstallSummary(result));
924
+ return;
925
+ }
926
+ throw new Error("Unknown mcp subcommand.");
927
+ }
781
928
  function commandOnPath(command) {
782
929
  try {
783
930
  execSync(`command -v ${command}`, { stdio: "ignore" });
@@ -1728,6 +1875,10 @@ async function main() {
1728
1875
  await runInstall(values);
1729
1876
  return;
1730
1877
  }
1878
+ if (command === "mcp") {
1879
+ await runMcpSubcommand(subcommand, values);
1880
+ return;
1881
+ }
1731
1882
  if (command === "ask") {
1732
1883
  await runAsk(values);
1733
1884
  return;
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from "./prompt-aliases.js";
2
2
  export * from "./personas.js";
3
3
  export * from "./persona-router.js";
4
4
  export * from "./panel.js";
5
+ export * from "./project-session.js";
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ export * from "./prompt-aliases.js";
2
2
  export * from "./personas.js";
3
3
  export * from "./persona-router.js";
4
4
  export * from "./panel.js";
5
+ export * from "./project-session.js";
@@ -99,6 +99,7 @@ export interface LongTableWorkspaceInspection {
99
99
  timestamp: string;
100
100
  }>;
101
101
  }
102
+ export declare function loadWorkspaceState(context: LongTableProjectContext): Promise<ResearchState>;
102
103
  export declare function syncCurrentWorkspaceView(context: LongTableProjectContext): Promise<string>;
103
104
  export declare function appendInvocationRecordToWorkspace(context: LongTableProjectContext, invocation: InvocationRecord, questions?: QuestionRecord[]): Promise<ResearchState>;
104
105
  export declare function listBlockingWorkspaceQuestions(context: LongTableProjectContext): Promise<QuestionRecord[]>;
@@ -205,6 +205,9 @@ async function loadResearchState(stateFilePath) {
205
205
  ...(parsed.studyContract ? { studyContract: parsed.studyContract } : {})
206
206
  };
207
207
  }
208
+ export async function loadWorkspaceState(context) {
209
+ return loadResearchState(context.stateFilePath);
210
+ }
208
211
  function recentInvocationRecords(state, limit = 3) {
209
212
  return (state.invocationLog ?? []).slice(-limit).reverse();
210
213
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "private": false,
5
5
  "description": "Researcher-facing LongTable CLI",
6
6
  "type": "module",
@@ -28,12 +28,12 @@
28
28
  "typecheck": "tsc -p tsconfig.json --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@longtable/checkpoints": "0.1.10",
32
- "@longtable/core": "0.1.10",
33
- "@longtable/memory": "0.1.10",
34
- "@longtable/provider-claude": "0.1.10",
35
- "@longtable/provider-codex": "0.1.10",
36
- "@longtable/setup": "0.1.10"
31
+ "@longtable/checkpoints": "0.1.11",
32
+ "@longtable/core": "0.1.11",
33
+ "@longtable/memory": "0.1.11",
34
+ "@longtable/provider-claude": "0.1.11",
35
+ "@longtable/provider-codex": "0.1.11",
36
+ "@longtable/setup": "0.1.11"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.10.1",