@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 +154 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/project-session.d.ts +1 -0
- package/dist/project-session.js +3 -0
- package/package.json +7 -7
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
package/dist/index.js
CHANGED
|
@@ -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[]>;
|
package/dist/project-session.js
CHANGED
|
@@ -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.
|
|
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.
|
|
32
|
-
"@longtable/core": "0.1.
|
|
33
|
-
"@longtable/memory": "0.1.
|
|
34
|
-
"@longtable/provider-claude": "0.1.
|
|
35
|
-
"@longtable/provider-codex": "0.1.
|
|
36
|
-
"@longtable/setup": "0.1.
|
|
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",
|