@longtable/cli 0.1.14 → 0.1.15
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/README.md +19 -0
- package/dist/cli.js +77 -38
- package/dist/panel.d.ts +3 -1
- package/dist/panel.js +10 -4
- package/dist/project-session.d.ts +2 -0
- package/dist/project-session.js +6 -0
- package/dist/prompt-aliases.js +1 -1
- package/package.json +9 -7
- package/scripts/postinstall.mjs +85 -0
package/README.md
CHANGED
|
@@ -19,6 +19,25 @@ The basic contract is:
|
|
|
19
19
|
npm install -g @longtable/cli
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
For global npm installs, LongTable also bootstraps provider-native skill files:
|
|
23
|
+
|
|
24
|
+
- Codex skills are written to `~/.codex/skills/`.
|
|
25
|
+
- Claude Code skills are written when the `claude` command is detected.
|
|
26
|
+
- `LONGTABLE_POSTINSTALL_PROVIDERS=all npm install -g @longtable/cli` installs
|
|
27
|
+
both provider skill sets.
|
|
28
|
+
- `LONGTABLE_SKIP_POSTINSTALL=1 npm install -g @longtable/cli` skips the skill
|
|
29
|
+
bootstrap.
|
|
30
|
+
|
|
31
|
+
MCP config writes are intentionally opt-in:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
longtable mcp install --provider codex --write
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
During `longtable init --flow interview`, LongTable asks whether to configure
|
|
38
|
+
MCP and whether provider-native team/subagent surfaces should be used when they
|
|
39
|
+
are available. The stable fallback remains LongTable's sequential panel.
|
|
40
|
+
|
|
22
41
|
## Primary Flow
|
|
23
42
|
|
|
24
43
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ const ANSI = {
|
|
|
40
40
|
green: "\u001B[32m"
|
|
41
41
|
};
|
|
42
42
|
const LONGTABLE_MCP_SERVER_NAME = "longtable-state";
|
|
43
|
-
const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.
|
|
43
|
+
const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.15";
|
|
44
44
|
const LONGTABLE_MCP_MARKER_START = "# LongTable state MCP START";
|
|
45
45
|
const LONGTABLE_MCP_MARKER_END = "# LongTable state MCP END";
|
|
46
46
|
function style(text, prefix) {
|
|
@@ -76,7 +76,7 @@ function usage() {
|
|
|
76
76
|
" Run `longtable ...` in your terminal, not inside the Codex chat box.",
|
|
77
77
|
" After `longtable start`, move into the created project directory and open `codex` there.",
|
|
78
78
|
"",
|
|
79
|
-
" longtable init [--flow quickstart|interview] [--provider codex|claude] [--field <field>] [--career-stage <stage>] [--experience novice|intermediate|advanced] [--checkpoint low|balanced|high] [--authorship-signal <text>] [--entry-mode explore|review|critique|draft|commit] [--weakest-domain theory|methodology|measurement|analysis|writing] [--panel-preference synthesis_only|show_on_conflict|always_visible] [--json] [--no-install] [--install-skills] [--install-prompts]",
|
|
79
|
+
" longtable init [--flow quickstart|interview] [--provider codex|claude] [--field <field>] [--career-stage <stage>] [--experience novice|intermediate|advanced] [--checkpoint low|balanced|high] [--authorship-signal <text>] [--entry-mode explore|review|critique|draft|commit] [--weakest-domain theory|methodology|measurement|analysis|writing] [--panel-preference synthesis_only|show_on_conflict|always_visible] [--agent-team native_when_available|sequential_panel_only] [--mcp skip|print_config|write_provider|write_all] [--json] [--no-install] [--install-skills] [--install-prompts]",
|
|
80
80
|
" longtable start [--path <dir>] [--name <project>] [--goal <text>] [--blocker <text>] [--perspectives <role[,role]>] [--disagreement synthesis_only|show_on_conflict|always_visible] [--setup <path>] [--json]",
|
|
81
81
|
" longtable resume [--cwd <path>] [--json]",
|
|
82
82
|
" longtable doctor [--cwd <path>] [--fix] [--json] [--codex-dir <path>] [--claude-dir <path>] [--codex-prompts-dir <path>] [--codex-runtime-path <file>] [--claude-runtime-path <file>]",
|
|
@@ -197,9 +197,12 @@ function questionSection(questionId) {
|
|
|
197
197
|
if (questionId === "preferredCheckpointIntensity" || questionId === "preferredEntryMode") {
|
|
198
198
|
return "Interaction style";
|
|
199
199
|
}
|
|
200
|
-
if (questionId === "weakestDomain" || questionId === "panelPreference") {
|
|
200
|
+
if (questionId === "weakestDomain" || questionId === "panelPreference" || questionId === "agentTeamPreference") {
|
|
201
201
|
return "How LongTable should challenge you";
|
|
202
202
|
}
|
|
203
|
+
if (questionId === "mcpPreference") {
|
|
204
|
+
return "Provider integration";
|
|
205
|
+
}
|
|
203
206
|
return "Authorship and voice";
|
|
204
207
|
}
|
|
205
208
|
function formatModeLabel(mode) {
|
|
@@ -496,6 +499,12 @@ function toSetupAnswers(args) {
|
|
|
496
499
|
: undefined,
|
|
497
500
|
panelPreference: typeof args["panel-preference"] === "string"
|
|
498
501
|
? String(args["panel-preference"])
|
|
502
|
+
: undefined,
|
|
503
|
+
agentTeamPreference: typeof args["agent-team"] === "string"
|
|
504
|
+
? String(args["agent-team"])
|
|
505
|
+
: undefined,
|
|
506
|
+
mcpPreference: typeof args.mcp === "string"
|
|
507
|
+
? String(args.mcp)
|
|
499
508
|
: undefined
|
|
500
509
|
};
|
|
501
510
|
}
|
|
@@ -543,6 +552,11 @@ async function collectInteractiveAnswers(initialFlow) {
|
|
|
543
552
|
answers.weakestDomain = value;
|
|
544
553
|
if (question.id === "panelPreference")
|
|
545
554
|
answers.panelPreference = value;
|
|
555
|
+
if (question.id === "agentTeamPreference") {
|
|
556
|
+
answers.agentTeamPreference = value;
|
|
557
|
+
}
|
|
558
|
+
if (question.id === "mcpPreference")
|
|
559
|
+
answers.mcpPreference = value;
|
|
546
560
|
}
|
|
547
561
|
return {
|
|
548
562
|
flow,
|
|
@@ -657,6 +671,12 @@ function normalizePersistAnswers(raw) {
|
|
|
657
671
|
: {}),
|
|
658
672
|
...(raw.panelPreference
|
|
659
673
|
? { panelPreference: raw.panelPreference }
|
|
674
|
+
: {}),
|
|
675
|
+
...(raw.agentTeamPreference
|
|
676
|
+
? { agentTeamPreference: raw.agentTeamPreference }
|
|
677
|
+
: {}),
|
|
678
|
+
...(raw.mcpPreference
|
|
679
|
+
? { mcpPreference: raw.mcpPreference }
|
|
660
680
|
: {})
|
|
661
681
|
}
|
|
662
682
|
};
|
|
@@ -685,6 +705,15 @@ async function readPersistAnswers(args) {
|
|
|
685
705
|
}
|
|
686
706
|
throw new Error("persist-init requires either --answers-json, --stdin, or the full set of setup flags.");
|
|
687
707
|
}
|
|
708
|
+
async function applySetupMcpPreference(provider, preference) {
|
|
709
|
+
if (!preference || preference === "skip") {
|
|
710
|
+
return undefined;
|
|
711
|
+
}
|
|
712
|
+
return installMcpConfig({
|
|
713
|
+
provider: preference === "write_all" ? "all" : provider,
|
|
714
|
+
write: preference === "write_provider" || preference === "write_all"
|
|
715
|
+
});
|
|
716
|
+
}
|
|
688
717
|
async function runInit(args) {
|
|
689
718
|
const json = args.json === true;
|
|
690
719
|
const installRuntime = args["no-install"] !== true;
|
|
@@ -717,15 +746,17 @@ async function runInit(args) {
|
|
|
717
746
|
if (provider === "claude" && installSkills) {
|
|
718
747
|
installedSkills = await installClaudeSkills(listRoleDefinitions(), skillsDir);
|
|
719
748
|
}
|
|
749
|
+
const mcpInstall = await applySetupMcpPreference(provider, answers.mcpPreference);
|
|
720
750
|
if (json) {
|
|
721
|
-
if (installedPrompts.length === 0 && installedSkills.length === 0) {
|
|
751
|
+
if (installedPrompts.length === 0 && installedSkills.length === 0 && !mcpInstall) {
|
|
722
752
|
console.log(serializeSetupOutput(outputValue));
|
|
723
753
|
return;
|
|
724
754
|
}
|
|
725
755
|
console.log(JSON.stringify({
|
|
726
756
|
setup: outputValue,
|
|
727
757
|
installedPrompts: installedPrompts.map((prompt) => prompt.name),
|
|
728
|
-
installedSkills: installedSkills.map((skill) => skill.name)
|
|
758
|
+
installedSkills: installedSkills.map((skill) => skill.name),
|
|
759
|
+
mcpInstall
|
|
729
760
|
}, null, 2));
|
|
730
761
|
return;
|
|
731
762
|
}
|
|
@@ -750,6 +781,10 @@ async function runInit(args) {
|
|
|
750
781
|
}
|
|
751
782
|
console.log(" Use these by naming LongTable naturally, e.g. `lt panel: ...`.");
|
|
752
783
|
}
|
|
784
|
+
if (mcpInstall) {
|
|
785
|
+
console.log("");
|
|
786
|
+
console.log(renderMcpInstallSummary(mcpInstall));
|
|
787
|
+
}
|
|
753
788
|
if (provider === "codex") {
|
|
754
789
|
console.log("");
|
|
755
790
|
console.log("Next step:");
|
|
@@ -883,40 +918,43 @@ function renderMcpInstallSummary(result) {
|
|
|
883
918
|
}
|
|
884
919
|
return lines.join("\n").trimEnd();
|
|
885
920
|
}
|
|
921
|
+
async function installMcpConfig(args) {
|
|
922
|
+
const serverName = typeof args.name === "string" && args.name.trim()
|
|
923
|
+
? args.name.trim()
|
|
924
|
+
: LONGTABLE_MCP_SERVER_NAME;
|
|
925
|
+
const packageSpec = resolveMcpPackageSpec(args);
|
|
926
|
+
const command = typeof args.command === "string" && args.command.trim() ? args.command.trim() : "npx";
|
|
927
|
+
const mcpArgs = command === "npx" ? ["-y", packageSpec] : [packageSpec];
|
|
928
|
+
const providers = resolveMcpProviders(args.provider);
|
|
929
|
+
const write = args.write === true;
|
|
930
|
+
const targets = [];
|
|
931
|
+
for (const provider of providers) {
|
|
932
|
+
if (provider === "codex") {
|
|
933
|
+
const path = resolveCodexMcpConfigPath(args);
|
|
934
|
+
const block = renderCodexMcpBlock(serverName, command, mcpArgs);
|
|
935
|
+
const content = write ? await writeCodexMcpConfig(path, block, serverName) : block;
|
|
936
|
+
targets.push({ provider, path, format: "toml", content });
|
|
937
|
+
}
|
|
938
|
+
if (provider === "claude") {
|
|
939
|
+
const path = resolveClaudeMcpSettingsPath(args);
|
|
940
|
+
const content = write
|
|
941
|
+
? await writeClaudeMcpSettings(path, serverName, command, mcpArgs)
|
|
942
|
+
: renderClaudeMcpJson(serverName, command, mcpArgs);
|
|
943
|
+
targets.push({ provider, path, format: "json", content });
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return {
|
|
947
|
+
serverName,
|
|
948
|
+
packageSpec,
|
|
949
|
+
command,
|
|
950
|
+
args: mcpArgs,
|
|
951
|
+
write,
|
|
952
|
+
targets
|
|
953
|
+
};
|
|
954
|
+
}
|
|
886
955
|
async function runMcpSubcommand(subcommand, args) {
|
|
887
956
|
if (!subcommand || subcommand === "install" || subcommand === "print-config") {
|
|
888
|
-
const
|
|
889
|
-
? args.name.trim()
|
|
890
|
-
: LONGTABLE_MCP_SERVER_NAME;
|
|
891
|
-
const packageSpec = resolveMcpPackageSpec(args);
|
|
892
|
-
const command = typeof args.command === "string" && args.command.trim() ? args.command.trim() : "npx";
|
|
893
|
-
const mcpArgs = command === "npx" ? ["-y", packageSpec] : [packageSpec];
|
|
894
|
-
const providers = resolveMcpProviders(args.provider);
|
|
895
|
-
const write = args.write === true;
|
|
896
|
-
const targets = [];
|
|
897
|
-
for (const provider of providers) {
|
|
898
|
-
if (provider === "codex") {
|
|
899
|
-
const path = resolveCodexMcpConfigPath(args);
|
|
900
|
-
const block = renderCodexMcpBlock(serverName, command, mcpArgs);
|
|
901
|
-
const content = write ? await writeCodexMcpConfig(path, block, serverName) : block;
|
|
902
|
-
targets.push({ provider, path, format: "toml", content });
|
|
903
|
-
}
|
|
904
|
-
if (provider === "claude") {
|
|
905
|
-
const path = resolveClaudeMcpSettingsPath(args);
|
|
906
|
-
const content = write
|
|
907
|
-
? await writeClaudeMcpSettings(path, serverName, command, mcpArgs)
|
|
908
|
-
: renderClaudeMcpJson(serverName, command, mcpArgs);
|
|
909
|
-
targets.push({ provider, path, format: "json", content });
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
const result = {
|
|
913
|
-
serverName,
|
|
914
|
-
packageSpec,
|
|
915
|
-
command,
|
|
916
|
-
args: mcpArgs,
|
|
917
|
-
write,
|
|
918
|
-
targets
|
|
919
|
-
};
|
|
957
|
+
const result = await installMcpConfig(args);
|
|
920
958
|
if (args.json === true) {
|
|
921
959
|
console.log(JSON.stringify(result, null, 2));
|
|
922
960
|
return;
|
|
@@ -1449,7 +1487,8 @@ async function runPanelCommand(args) {
|
|
|
1449
1487
|
mode,
|
|
1450
1488
|
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
1451
1489
|
provider,
|
|
1452
|
-
visibility
|
|
1490
|
+
visibility,
|
|
1491
|
+
agentTeamPreference: setup?.profileSeed.agentTeamPreference
|
|
1453
1492
|
});
|
|
1454
1493
|
if (projectAware.projectContextFound) {
|
|
1455
1494
|
const context = await loadProjectContextFromDirectory(workingDirectory);
|
package/dist/panel.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CheckpointSensitivity, InteractionMode, InvocationIntent, InvocationRecord, PanelPlan, PanelResult, PanelVisibility, QuestionRecord, ProviderKind, RoleKey } from "@longtable/core";
|
|
1
|
+
import type { CheckpointSensitivity, InteractionMode, InvocationIntent, InvocationSurface, InvocationRecord, PanelPlan, PanelResult, PanelVisibility, QuestionRecord, ProviderKind, RoleKey } from "@longtable/core";
|
|
2
2
|
import { type CanonicalPersona } from "./personas.js";
|
|
3
3
|
export interface BuildPanelPlanOptions {
|
|
4
4
|
prompt: string;
|
|
@@ -7,6 +7,7 @@ export interface BuildPanelPlanOptions {
|
|
|
7
7
|
roles?: CanonicalPersona[];
|
|
8
8
|
provider?: ProviderKind;
|
|
9
9
|
visibility?: PanelVisibility;
|
|
10
|
+
agentTeamPreference?: "native_when_available" | "sequential_panel_only";
|
|
10
11
|
}
|
|
11
12
|
export interface PanelFallback {
|
|
12
13
|
intent: InvocationIntent;
|
|
@@ -22,6 +23,7 @@ export declare function buildInvocationIntent(options: {
|
|
|
22
23
|
mode?: InteractionMode;
|
|
23
24
|
roles: RoleKey[];
|
|
24
25
|
provider?: ProviderKind;
|
|
26
|
+
requestedSurface?: InvocationSurface;
|
|
25
27
|
visibility?: PanelVisibility;
|
|
26
28
|
checkpointSensitivity?: CheckpointSensitivity;
|
|
27
29
|
rationale?: string[];
|
package/dist/panel.js
CHANGED
|
@@ -67,6 +67,9 @@ export function buildPanelPlan(options) {
|
|
|
67
67
|
const routedRoles = routePersonas(options.prompt).consultedRoles;
|
|
68
68
|
const roles = resolvePanelRoles(options);
|
|
69
69
|
const createdAt = nowIso();
|
|
70
|
+
const preferredSurface = options.agentTeamPreference === "native_when_available"
|
|
71
|
+
? "native_parallel"
|
|
72
|
+
: "sequential_fallback";
|
|
70
73
|
return {
|
|
71
74
|
id: createId("panel_plan"),
|
|
72
75
|
createdAt,
|
|
@@ -74,12 +77,14 @@ export function buildPanelPlan(options) {
|
|
|
74
77
|
prompt: options.prompt,
|
|
75
78
|
members: roles.map((role) => memberForRole(role, explicitRoles, routedRoles)),
|
|
76
79
|
visibility: options.visibility ?? "always_visible",
|
|
77
|
-
preferredSurface
|
|
80
|
+
preferredSurface,
|
|
78
81
|
fallbackSurface: "sequential_fallback",
|
|
79
82
|
checkpointSensitivity: highestSensitivity(roles),
|
|
80
83
|
rationale: [
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
preferredSurface === "native_parallel"
|
|
85
|
+
? "Setup prefers native provider team/subagent surfaces when available."
|
|
86
|
+
: "Setup prefers LongTable's provider-neutral sequential panel surface.",
|
|
87
|
+
"Sequential fallback remains the stable execution path for both Claude Code and Codex.",
|
|
83
88
|
roles.length === explicitRoles.length && explicitRoles.length > 0
|
|
84
89
|
? "The panel is constrained by explicitly requested roles."
|
|
85
90
|
: "The panel combines default research-review roles with prompt-triggered roles."
|
|
@@ -94,7 +99,7 @@ export function buildInvocationIntent(options) {
|
|
|
94
99
|
prompt: options.prompt,
|
|
95
100
|
roles: options.roles,
|
|
96
101
|
provider: options.provider,
|
|
97
|
-
requestedSurface: "sequential_fallback",
|
|
102
|
+
requestedSurface: options.requestedSurface ?? "sequential_fallback",
|
|
98
103
|
visibility: options.visibility ?? "always_visible",
|
|
99
104
|
checkpointSensitivity: options.checkpointSensitivity ?? "medium",
|
|
100
105
|
rationale: options.rationale ?? ["Panel invocation requested."]
|
|
@@ -230,6 +235,7 @@ export function buildPanelFallback(options) {
|
|
|
230
235
|
mode: plan.mode,
|
|
231
236
|
roles: plan.members.map((member) => member.role),
|
|
232
237
|
provider: options.provider,
|
|
238
|
+
requestedSurface: plan.preferredSurface,
|
|
233
239
|
visibility: plan.visibility,
|
|
234
240
|
checkpointSensitivity: plan.checkpointSensitivity,
|
|
235
241
|
rationale: plan.rationale
|
|
@@ -17,6 +17,8 @@ export interface LongTableProjectRecord {
|
|
|
17
17
|
humanAuthorshipSignal?: string;
|
|
18
18
|
weakestDomain?: string;
|
|
19
19
|
defaultPanelPreference?: ProjectDisagreementPreference;
|
|
20
|
+
agentTeamPreference?: string;
|
|
21
|
+
mcpPreference?: string;
|
|
20
22
|
};
|
|
21
23
|
}
|
|
22
24
|
export interface LongTableSessionRecord {
|
package/dist/project-session.js
CHANGED
|
@@ -917,6 +917,12 @@ export async function createOrUpdateProjectWorkspace(options) {
|
|
|
917
917
|
: {}),
|
|
918
918
|
...(options.setup.profileSeed.panelPreference
|
|
919
919
|
? { defaultPanelPreference: options.setup.profileSeed.panelPreference }
|
|
920
|
+
: {}),
|
|
921
|
+
...(options.setup.profileSeed.agentTeamPreference
|
|
922
|
+
? { agentTeamPreference: options.setup.profileSeed.agentTeamPreference }
|
|
923
|
+
: {}),
|
|
924
|
+
...(options.setup.profileSeed.mcpPreference
|
|
925
|
+
? { mcpPreference: options.setup.profileSeed.mcpPreference }
|
|
920
926
|
: {})
|
|
921
927
|
}
|
|
922
928
|
};
|
package/dist/prompt-aliases.js
CHANGED
|
@@ -35,7 +35,7 @@ function promptSpec() {
|
|
|
35
35
|
"Do not move to the next question until the researcher answers the current one.",
|
|
36
36
|
"Quickstart covers: provider, field, career stage, experience level, checkpoint intensity, and human authorship signal.",
|
|
37
37
|
"Interview also covers: preferred entry mode, weakest domain, and panel visibility preference.",
|
|
38
|
-
"After collecting all answers, summarize the proposed setup and then output both: 1) the exact `longtable codex persist-init ... --install-skills` command and 2) a strict JSON object with keys provider, flow, field, careerStage, experienceLevel, preferredCheckpointIntensity, and optional humanAuthorshipSignal, preferredEntryMode, weakestDomain, panelPreference.",
|
|
38
|
+
"After collecting all answers, summarize the proposed setup and then output both: 1) the exact `longtable codex persist-init ... --install-skills` command and 2) a strict JSON object with keys provider, flow, field, careerStage, experienceLevel, preferredCheckpointIntensity, and optional humanAuthorshipSignal, preferredEntryMode, weakestDomain, panelPreference, agentTeamPreference, mcpPreference.",
|
|
39
39
|
"If the user prefers paste-based setup, tell them they can pipe the JSON into `longtable codex persist-init --stdin --install-skills`.",
|
|
40
40
|
"If the researcher asks you to stay inside Codex, keep the conversation in numbered form and do not prematurely close.",
|
|
41
41
|
"Frame the setup like a short researcher interview, not a bare config form.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Researcher-facing LongTable CLI",
|
|
6
6
|
"type": "module",
|
|
@@ -21,19 +21,21 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"bin",
|
|
24
|
+
"scripts/postinstall.mjs",
|
|
24
25
|
"README.md"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
28
|
+
"postinstall": "node ./scripts/postinstall.mjs",
|
|
27
29
|
"build": "tsc -p tsconfig.json",
|
|
28
30
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
29
31
|
},
|
|
30
32
|
"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.
|
|
33
|
+
"@longtable/checkpoints": "0.1.15",
|
|
34
|
+
"@longtable/core": "0.1.15",
|
|
35
|
+
"@longtable/memory": "0.1.15",
|
|
36
|
+
"@longtable/provider-claude": "0.1.15",
|
|
37
|
+
"@longtable/provider-codex": "0.1.15",
|
|
38
|
+
"@longtable/setup": "0.1.15"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
39
41
|
"@types/node": "^22.10.1",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
function envFlag(name) {
|
|
8
|
+
const value = process.env[name];
|
|
9
|
+
if (!value) return false;
|
|
10
|
+
return !["0", "false", "no", "off"].includes(value.toLowerCase());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isGlobalInstall() {
|
|
14
|
+
return process.env.npm_config_global === "true" || process.env.npm_config_location === "global";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function commandOnPath(command) {
|
|
18
|
+
try {
|
|
19
|
+
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function requestedProviders() {
|
|
27
|
+
const raw = process.env.LONGTABLE_POSTINSTALL_PROVIDERS?.trim().toLowerCase();
|
|
28
|
+
if (!raw || raw === "auto") {
|
|
29
|
+
const providers = ["codex"];
|
|
30
|
+
if (commandOnPath("claude")) providers.push("claude");
|
|
31
|
+
return providers;
|
|
32
|
+
}
|
|
33
|
+
if (raw === "all") return ["codex", "claude"];
|
|
34
|
+
return raw
|
|
35
|
+
.split(",")
|
|
36
|
+
.map((entry) => entry.trim())
|
|
37
|
+
.filter((entry) => entry === "codex" || entry === "claude");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
if (!isGlobalInstall() || envFlag("LONGTABLE_SKIP_POSTINSTALL")) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const distEntrypoint = fileURLToPath(new URL("../dist/personas.js", import.meta.url));
|
|
46
|
+
if (!existsSync(distEntrypoint)) {
|
|
47
|
+
console.warn("[longtable] Skipped provider skill bootstrap because package build output was not found.");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const providers = requestedProviders();
|
|
52
|
+
if (providers.length === 0) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { listRoleDefinitions } = await import("../dist/personas.js");
|
|
57
|
+
const roles = listRoleDefinitions();
|
|
58
|
+
const installed = [];
|
|
59
|
+
|
|
60
|
+
if (providers.includes("codex")) {
|
|
61
|
+
const { installCodexSkills, resolveCodexSkillsDir } = await import("@longtable/provider-codex");
|
|
62
|
+
const skills = await installCodexSkills(roles);
|
|
63
|
+
installed.push(`Codex skills: ${skills.length} (${resolveCodexSkillsDir()})`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (providers.includes("claude")) {
|
|
67
|
+
const { installClaudeSkills, resolveClaudeSkillsDir } = await import("@longtable/provider-claude");
|
|
68
|
+
const skills = await installClaudeSkills(roles);
|
|
69
|
+
installed.push(`Claude skills: ${skills.length} (${resolveClaudeSkillsDir()})`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (installed.length > 0) {
|
|
73
|
+
console.log("[longtable] Provider skills installed during global npm install:");
|
|
74
|
+
for (const line of installed) {
|
|
75
|
+
console.log(`[longtable] - ${line}`);
|
|
76
|
+
}
|
|
77
|
+
console.log("[longtable] Run `longtable doctor` to verify setup.");
|
|
78
|
+
console.log("[longtable] MCP config is opt-in: run `longtable mcp install --provider codex --write` when you want provider config files updated.");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
main().catch((error) => {
|
|
83
|
+
console.warn(`[longtable] Postinstall provider skill bootstrap failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
84
|
+
console.warn("[longtable] You can repair this later with `longtable doctor --fix`.");
|
|
85
|
+
});
|