@longtable/cli 0.1.14 → 0.1.16

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.
Files changed (3) hide show
  1. package/README.md +29 -0
  2. package/dist/cli.js +387 -4
  3. package/package.json +8 -8
package/README.md CHANGED
@@ -19,15 +19,25 @@ The basic contract is:
19
19
  npm install -g @longtable/cli
20
20
  ```
21
21
 
22
+ The npm install only installs the CLI. It does not write Codex skills, MCP
23
+ config, hooks, tmux state, or provider runtime files without explicit setup
24
+ approval.
25
+
22
26
  ## Primary Flow
23
27
 
24
28
  ```bash
29
+ longtable setup --provider codex
25
30
  longtable init --flow interview
26
31
  longtable start
27
32
  cd "<project-path>"
28
33
  codex
29
34
  ```
30
35
 
36
+ `longtable setup --provider codex` is the permission-first setup route. It asks
37
+ which runtime surfaces LongTable may enable and explains why each choice matters:
38
+ CLI only, skills, skills + MCP, skills + MCP + sentinel, intervention posture,
39
+ tmux HUD/console, and team discussion mode.
40
+
31
41
  Return later:
32
42
 
33
43
  ```bash
@@ -81,6 +91,9 @@ longtable resume --cwd "<project-path>"
81
91
  longtable roles
82
92
  longtable ask --cwd "<project-path>" --prompt "..."
83
93
  longtable panel --prompt "..."
94
+ longtable sentinel --prompt "Should I define a new measurement construct?"
95
+ longtable hud --watch
96
+ longtable team --tmux --prompt "Review this measurement plan."
84
97
  longtable codex install-skills
85
98
  longtable claude install-skills
86
99
  ```
@@ -151,6 +164,22 @@ Default panel roles include:
151
164
 
152
165
  Use `--role` to constrain the panel when the research problem is already clear.
153
166
 
167
+ ## Sentinel, HUD, And Tmux Team
168
+
169
+ `longtable sentinel` is an explicit gap/tacit check for prompts that may contain
170
+ measurement, theory, method, evidence, authorship, or tacit-assumption risks.
171
+ Use `--record` inside a LongTable workspace to store the finding as an
172
+ unconfirmed inferred hypothesis.
173
+
174
+ `longtable hud --watch` renders a compact view of the current project goal,
175
+ blocker, pending checkpoints, recent decisions, and invocation counts.
176
+ `longtable hud --tmux` opens that view in a tmux pane.
177
+
178
+ `longtable team --tmux` opens role-specific panes for research discussion and
179
+ writes logs under `.longtable/team/<id>/`. This is panel discussion, not merely
180
+ parallel execution: role panes are prompted to state claims, objections, open
181
+ questions, and likely disagreement.
182
+
154
183
  ## Evidence And Search Direction
155
184
 
156
185
  LongTable should not behave like a generic web scraper. Research search should
package/dist/cli.js CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
4
- import { execSync } from "node:child_process";
4
+ import { execFileSync, execSync } from "node:child_process";
5
5
  import { emitKeypressEvents } from "node:readline";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  import { stdin as input, stdout as output, cwd, exit } from "node:process";
8
- import { dirname, resolve } from "node:path";
8
+ import { dirname, join, resolve } from "node:path";
9
9
  import { homedir } from "node:os";
10
+ import { classifyCheckpointTrigger } from "@longtable/checkpoints";
10
11
  import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput, writeRuntimeConfig } from "@longtable/setup";
11
12
  import { buildCodexSkillSpecs, buildCodexThinWrappedPrompt, installCodexSkills, listInstalledCodexSkills, renderQuestionRecordPrompt, removeCodexSkills, resolveCodexSkillsDir, runCodexThinWrapper } from "@longtable/provider-codex";
12
13
  import { buildClaudeSkillSpecs, installClaudeSkills, listInstalledClaudeSkills, renderQuestionRecordInput, removeClaudeSkills, resolveClaudeSkillsDir } from "@longtable/provider-claude";
@@ -14,7 +15,7 @@ import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodex
14
15
  import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router.js";
15
16
  import { PERSONA_DEFINITIONS, listRoleDefinitions } from "./personas.js";
16
17
  import { buildPanelFallback, renderPanelSummary } from "./panel.js";
17
- import { appendInvocationRecordToWorkspace, assertWorkspaceNotBlocked, answerWorkspaceQuestion, createWorkspaceClarificationCard, createWorkspaceQuestion, createOrUpdateProjectWorkspace, inspectProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
18
+ import { appendInvocationRecordToWorkspace, assertWorkspaceNotBlocked, answerWorkspaceQuestion, createWorkspaceClarificationCard, createWorkspaceQuestion, createOrUpdateProjectWorkspace, inspectProjectWorkspace, loadWorkspaceState, loadProjectContextFromDirectory, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
18
19
  const VALID_MODES = new Set([
19
20
  "explore",
20
21
  "review",
@@ -77,6 +78,7 @@ function usage() {
77
78
  " After `longtable start`, move into the created project directory and open `codex` there.",
78
79
  "",
79
80
  " 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]",
81
+ " longtable setup [--provider codex|claude] [--json] [--dir <path>] [--skills-dir <path>] [--runtime-path <file>] [--setup-path <file>]",
80
82
  " 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
83
  " longtable resume [--cwd <path>] [--json]",
82
84
  " longtable doctor [--cwd <path>] [--fix] [--json] [--codex-dir <path>] [--claude-dir <path>] [--codex-prompts-dir <path>] [--codex-runtime-path <file>] [--claude-runtime-path <file>]",
@@ -85,6 +87,9 @@ function usage() {
85
87
  " longtable show [--json] [--path <file>]",
86
88
  " longtable install [--json] [--path <file>] [--runtime-path <file>]",
87
89
  " longtable mcp install [--provider codex|claude|all] [--write] [--json] [--codex-config <path>] [--claude-settings <path>] [--package <spec>]",
90
+ " longtable hud [--watch] [--tmux] [--preset minimal|full] [--cwd <path>] [--json]",
91
+ " longtable sentinel --prompt <text> [--cwd <path>] [--json] [--record]",
92
+ " longtable team --prompt <text> [--role <role[,role]>] [--tmux] [--cwd <path>] [--json]",
88
93
  " longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
89
94
  " longtable clarify --prompt <task-context> [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json] [--force]",
90
95
  " longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
@@ -120,7 +125,7 @@ function parseArgs(argv) {
120
125
  const values = {};
121
126
  let subcommand = maybeSubcommand;
122
127
  const modeCommand = command && VALID_MODES.has(command);
123
- const directCommand = command && ["init", "start", "resume", "doctor", "status", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "panel", "decide"].includes(command);
128
+ const directCommand = command && ["init", "setup", "start", "resume", "doctor", "status", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "panel", "decide", "hud", "sentinel", "team"].includes(command);
124
129
  let startIndex = 1;
125
130
  if (modeCommand) {
126
131
  subcommand = undefined;
@@ -554,6 +559,172 @@ async function collectInteractiveAnswers(initialFlow) {
554
559
  rl.close();
555
560
  }
556
561
  }
562
+ function buildPermissionSetupChoices() {
563
+ return {
564
+ surfaces: [
565
+ {
566
+ id: "cli_only",
567
+ label: "CLI only",
568
+ description: "Why: least invasive. Tradeoff: no natural in-provider LongTable entrypoints."
569
+ },
570
+ {
571
+ id: "skills",
572
+ label: "Skills",
573
+ description: "Why: enables natural LongTable skill routing. Tradeoff: writes provider skill files."
574
+ },
575
+ {
576
+ id: "skills_mcp",
577
+ label: "Skills + MCP",
578
+ description: "Why: adds structured state access. Tradeoff: writes provider config for MCP transport."
579
+ },
580
+ {
581
+ id: "skills_mcp_sentinel",
582
+ label: "Skills + MCP + Sentinel",
583
+ description: "Why: prepares advisory gap/tacit monitoring. Tradeoff: LongTable may nudge research turns."
584
+ }
585
+ ],
586
+ intervention: [
587
+ {
588
+ id: "advisory",
589
+ label: "Advisory",
590
+ description: "Why: notices gaps without blocking. Tradeoff: you may still miss hard commitments."
591
+ },
592
+ {
593
+ id: "balanced",
594
+ label: "Balanced",
595
+ description: "Why: blocks clear theory, measurement, method, or evidence commitments. Tradeoff: occasional stops."
596
+ },
597
+ {
598
+ id: "strong",
599
+ label: "Strong",
600
+ description: "Why: maximizes judgment protection. Tradeoff: more interruption before closure."
601
+ }
602
+ ],
603
+ tmux: [
604
+ {
605
+ id: "standard",
606
+ label: "Standard chat",
607
+ description: "Why: portable default. Tradeoff: checkpoints and gaps are less persistently visible."
608
+ },
609
+ {
610
+ id: "hud",
611
+ label: "Research HUD",
612
+ description: "Why: keeps goals, blockers, and pending checkpoints visible. Requires tmux."
613
+ },
614
+ {
615
+ id: "console",
616
+ label: "Research console",
617
+ description: "Why: enables a richer tmux layout for HUD and team discussion. Requires tmux."
618
+ }
619
+ ],
620
+ team: [
621
+ {
622
+ id: "off",
623
+ label: "Off",
624
+ description: "Why: simplest. Tradeoff: panel disagreement stays inside one LongTable response."
625
+ },
626
+ {
627
+ id: "panel",
628
+ label: "Structured panel",
629
+ description: "Why: role disagreement is visible without tmux. Tradeoff: not parallel."
630
+ },
631
+ {
632
+ id: "tmux_team",
633
+ label: "Tmux team discussion",
634
+ description: "Why: opens role panes for parallel debate. Tradeoff: terminal complexity and cleanup."
635
+ }
636
+ ]
637
+ };
638
+ }
639
+ function checkpointIntensityFromIntervention(choice) {
640
+ if (choice === "strong")
641
+ return "high";
642
+ if (choice === "advisory")
643
+ return "low";
644
+ return "balanced";
645
+ }
646
+ async function runSetup(args) {
647
+ const json = args.json === true;
648
+ const rl = createInterface({ input, output });
649
+ try {
650
+ const provider = (typeof args.provider === "string"
651
+ ? (args.provider === "claude" ? "claude" : "codex")
652
+ : await promptChoice(rl, "Which provider should LongTable configure?", buildProviderChoices()));
653
+ const choices = buildPermissionSetupChoices();
654
+ const surfaces = await promptChoice(rl, [
655
+ "Which LongTable runtime surfaces should be enabled?",
656
+ "This is a permission choice because skills, MCP, and sentinel support write provider-facing runtime files."
657
+ ].join("\n"), choices.surfaces);
658
+ const intervention = await promptChoice(rl, "How strongly may LongTable interrupt research decisions?", choices.intervention);
659
+ const tmuxMode = await promptChoice(rl, "Should LongTable recommend a tmux-based research interface?", choices.tmux);
660
+ const teamMode = await promptChoice(rl, "Should LongTable enable agent/team discussion mode?", choices.team);
661
+ const outputValue = createPersistedSetupOutput({
662
+ field: "unspecified",
663
+ careerStage: "unspecified",
664
+ experienceLevel: "advanced",
665
+ preferredCheckpointIntensity: checkpointIntensityFromIntervention(intervention),
666
+ preferredEntryMode: "explore",
667
+ panelPreference: teamMode === "off" ? "show_on_conflict" : "always_visible"
668
+ }, provider, "quickstart");
669
+ outputValue.initialState.explicitState = {
670
+ ...outputValue.initialState.explicitState,
671
+ runtimeSurfaces: surfaces,
672
+ interventionPosture: intervention,
673
+ tmuxMode,
674
+ teamMode
675
+ };
676
+ if (surfaces === "skills_mcp_sentinel") {
677
+ outputValue.initialState.inferredHypotheses.push({
678
+ hypothesis: "Researcher approved advisory Gap/Tacit Sentinel setup.",
679
+ confidence: 0.95,
680
+ evidence: ["Selected Skills + MCP + Sentinel during permission-first setup."],
681
+ status: "confirmed"
682
+ });
683
+ }
684
+ const result = await saveSetupAndRuntimeConfig(outputValue, {
685
+ setupPath: typeof args["setup-path"] === "string" ? args["setup-path"] : undefined,
686
+ runtimePath: typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined
687
+ });
688
+ const installedSkills = surfaces === "cli_only"
689
+ ? []
690
+ : provider === "codex"
691
+ ? await installCodexSkills(listRoleDefinitions(), typeof args["skills-dir"] === "string" ? args["skills-dir"] : typeof args.dir === "string" ? args.dir : undefined)
692
+ : await installClaudeSkills(listRoleDefinitions(), typeof args["skills-dir"] === "string" ? args["skills-dir"] : typeof args.dir === "string" ? args.dir : undefined);
693
+ const mcpRequested = surfaces === "skills_mcp" || surfaces === "skills_mcp_sentinel";
694
+ if (mcpRequested && !json) {
695
+ console.log("");
696
+ console.log("MCP setup is approved. To write provider config now, run:");
697
+ console.log(`- longtable mcp install --provider ${provider} --write`);
698
+ }
699
+ if (json) {
700
+ console.log(JSON.stringify({
701
+ setup: outputValue,
702
+ runtime: result,
703
+ installedSkills: installedSkills.map((skill) => skill.name),
704
+ mcpRequested,
705
+ tmuxMode,
706
+ teamMode
707
+ }, null, 2));
708
+ return;
709
+ }
710
+ console.log("");
711
+ console.log(renderSetupSummary(outputValue));
712
+ console.log("");
713
+ console.log(renderInstallSummary(result));
714
+ console.log(`Installed skills: ${installedSkills.length}`);
715
+ if (tmuxMode !== "standard") {
716
+ console.log("");
717
+ console.log("Tmux recommendation:");
718
+ console.log("- macOS: brew install tmux");
719
+ console.log("- Ubuntu/Debian: sudo apt install tmux");
720
+ console.log("- Start HUD in an existing tmux session: longtable hud --tmux");
721
+ console.log("- Start a discussion team: longtable team --tmux --prompt \"...\"");
722
+ }
723
+ }
724
+ finally {
725
+ rl.close();
726
+ }
727
+ }
557
728
  function perspectiveChoices() {
558
729
  return PERSONA_DEFINITIONS.map((persona) => ({
559
730
  id: persona.key,
@@ -1770,6 +1941,202 @@ async function runAsk(args) {
1770
1941
  }
1771
1942
  await runModeCommand(mode, delegatedArgs);
1772
1943
  }
1944
+ function localId(prefix) {
1945
+ return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
1946
+ }
1947
+ function shellEscape(value) {
1948
+ return `'${value.replaceAll("'", "'\\''")}'`;
1949
+ }
1950
+ function sentinelSummary(prompt, workingDirectory) {
1951
+ const trigger = classifyCheckpointTrigger(prompt, {
1952
+ fallbackMode: "explore",
1953
+ unresolvedTensions: []
1954
+ });
1955
+ const normalized = prompt.toLowerCase();
1956
+ const signals = [];
1957
+ if (/measure|measurement|scale|validity|reliability|측정|척도|타당도|신뢰도/.test(normalized)) {
1958
+ signals.push("measurement gap or commitment");
1959
+ }
1960
+ if (/theory|theoretical|framework|construct|이론|프레임워크|개념/.test(normalized)) {
1961
+ signals.push("theory or construct commitment");
1962
+ }
1963
+ if (/method|design|sample|participant|방법|설계|표본|참여자/.test(normalized)) {
1964
+ signals.push("method/design gap");
1965
+ }
1966
+ if (/citation|reference|source|evidence|doi|문헌|인용|근거|출처/.test(normalized)) {
1967
+ signals.push("evidence gap");
1968
+ }
1969
+ if (/voice|authorship|narrative|저자성|서사|문체|목소리/.test(normalized)) {
1970
+ signals.push("authorship or narrative-trace risk");
1971
+ }
1972
+ if (/assumption|implicit|tacit|암묵|전제|가정/.test(normalized)) {
1973
+ signals.push("tacit assumption risk");
1974
+ }
1975
+ return {
1976
+ cwd: workingDirectory,
1977
+ checkpoint: trigger.signal.checkpointKey,
1978
+ family: trigger.family,
1979
+ confidence: trigger.confidence,
1980
+ requiresQuestionBeforeClosure: trigger.requiresQuestionBeforeClosure,
1981
+ signals: signals.length > 0 ? signals : ["no specific gap/tacit signal beyond checkpoint classifier"],
1982
+ rationale: trigger.rationale
1983
+ };
1984
+ }
1985
+ async function runSentinel(args) {
1986
+ const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
1987
+ const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
1988
+ if (!prompt) {
1989
+ throw new Error("A prompt is required.");
1990
+ }
1991
+ const summary = sentinelSummary(prompt, workingDirectory);
1992
+ const context = await loadProjectContextFromDirectory(workingDirectory);
1993
+ if (args.record === true && context) {
1994
+ const state = await loadWorkspaceState(context);
1995
+ state.inferredHypotheses.push({
1996
+ hypothesis: `Sentinel detected: ${summary.signals.join(", ")}.`,
1997
+ confidence: summary.confidence === "high" ? 0.85 : summary.confidence === "medium" ? 0.65 : 0.4,
1998
+ evidence: [`Prompt: ${prompt}`],
1999
+ status: "unconfirmed"
2000
+ });
2001
+ if (summary.requiresQuestionBeforeClosure) {
2002
+ state.openTensions.push(`Pending sentinel risk: ${summary.checkpoint}`);
2003
+ }
2004
+ await writeFile(context.stateFilePath, JSON.stringify(state, null, 2), "utf8");
2005
+ await syncCurrentWorkspaceView(context);
2006
+ }
2007
+ if (args.json === true) {
2008
+ console.log(JSON.stringify(summary, null, 2));
2009
+ return;
2010
+ }
2011
+ console.log("LongTable Sentinel");
2012
+ console.log(`- checkpoint: ${summary.checkpoint}`);
2013
+ console.log(`- family: ${summary.family}`);
2014
+ console.log(`- confidence: ${summary.confidence}`);
2015
+ console.log(`- question before closure: ${summary.requiresQuestionBeforeClosure ? "yes" : "no"}`);
2016
+ console.log("- detected signals:");
2017
+ for (const signal of summary.signals) {
2018
+ console.log(` - ${signal}`);
2019
+ }
2020
+ if (args.record === true) {
2021
+ console.log(context ? `- recorded in: ${context.stateFilePath}` : "- record skipped: no LongTable workspace found");
2022
+ }
2023
+ }
2024
+ function renderHudText(inspection, preset) {
2025
+ if (!inspection.found) {
2026
+ return [
2027
+ "LongTable HUD",
2028
+ "- workspace: not found",
2029
+ "- run `longtable start` for durable research state"
2030
+ ].join("\n");
2031
+ }
2032
+ const lines = [
2033
+ "LongTable HUD",
2034
+ `- project: ${inspection.project?.name}`,
2035
+ `- goal: ${inspection.session?.currentGoal}`,
2036
+ ...(inspection.session?.currentBlocker ? [`- blocker: ${inspection.session.currentBlocker}`] : []),
2037
+ `- questions: ${inspection.counts?.pendingQuestions ?? 0} pending / ${inspection.counts?.questions ?? 0} total`,
2038
+ `- decisions: ${inspection.counts?.decisions ?? 0}`,
2039
+ `- invocations: ${inspection.counts?.invocations ?? 0}`
2040
+ ];
2041
+ if (preset !== "minimal") {
2042
+ lines.push("- pending checkpoints:");
2043
+ for (const question of inspection.pendingQuestions ?? []) {
2044
+ lines.push(` - ${question.required ? "required" : "advisory"}: ${question.question}`);
2045
+ }
2046
+ lines.push("- recent decisions:");
2047
+ for (const decision of inspection.recentDecisions ?? []) {
2048
+ lines.push(` - ${decision.summary}`);
2049
+ }
2050
+ }
2051
+ return lines.join("\n");
2052
+ }
2053
+ async function runHud(args) {
2054
+ const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
2055
+ const preset = typeof args.preset === "string" ? args.preset : "full";
2056
+ if (args.tmux === true) {
2057
+ if (!process.env.TMUX) {
2058
+ throw new Error("`longtable hud --tmux` must be run inside an existing tmux session.");
2059
+ }
2060
+ const launcher = process.argv[1] ?? "longtable";
2061
+ const command = `node ${shellEscape(launcher)} hud --watch --preset ${shellEscape(preset)} --cwd ${shellEscape(workingDirectory)}`;
2062
+ execFileSync("tmux", ["split-window", "-v", "-l", "10", command], { stdio: "inherit" });
2063
+ return;
2064
+ }
2065
+ while (true) {
2066
+ const inspection = await inspectProjectWorkspace(workingDirectory);
2067
+ if (args.json === true) {
2068
+ console.log(JSON.stringify(inspection, null, 2));
2069
+ return;
2070
+ }
2071
+ if (args.watch === true) {
2072
+ process.stdout.write("\u001Bc");
2073
+ }
2074
+ console.log(renderHudText(inspection, preset));
2075
+ if (args.watch !== true) {
2076
+ return;
2077
+ }
2078
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 1500));
2079
+ }
2080
+ }
2081
+ async function runTeam(args) {
2082
+ const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
2083
+ const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
2084
+ if (!prompt) {
2085
+ throw new Error("A prompt is required.");
2086
+ }
2087
+ const fallback = buildPanelFallback({
2088
+ prompt,
2089
+ mode: "review",
2090
+ roleFlag: typeof args.role === "string" ? args.role : undefined,
2091
+ provider: "codex",
2092
+ visibility: "always_visible"
2093
+ });
2094
+ const teamId = localId("team");
2095
+ const teamDir = join(workingDirectory, ".longtable", "team", teamId);
2096
+ await mkdir(teamDir, { recursive: true });
2097
+ await writeFile(join(teamDir, "prompt.txt"), prompt, "utf8");
2098
+ await writeFile(join(teamDir, "plan.json"), JSON.stringify(fallback.plan, null, 2), "utf8");
2099
+ if (args.json === true) {
2100
+ console.log(JSON.stringify({ teamId, teamDir, plan: fallback.plan }, null, 2));
2101
+ return;
2102
+ }
2103
+ if (args.tmux !== true) {
2104
+ console.log(renderPanelSummary(fallback.plan));
2105
+ console.log("");
2106
+ console.log("Run with `--tmux` to launch role panes for parallel discussion.");
2107
+ return;
2108
+ }
2109
+ const sessionName = `longtable-${teamId.replaceAll("_", "-")}`;
2110
+ const shell = process.env.SHELL || "/bin/sh";
2111
+ const launcher = process.argv[1] ?? "longtable";
2112
+ const leaderCommand = [
2113
+ `echo ${shellEscape(`LongTable team ${teamId}`)}`,
2114
+ `echo ${shellEscape(`Logs: ${teamDir}`)}`,
2115
+ "echo 'Role panes are running. Review logs, then run:'",
2116
+ `echo ${shellEscape(`longtable panel --role ${fallback.plan.members.map((member) => member.role).join(",")} --prompt ${JSON.stringify(prompt)}`)}`,
2117
+ `exec ${shellEscape(shell)}`
2118
+ ].join("; ");
2119
+ execFileSync("tmux", ["new-session", "-d", "-s", sessionName, "-c", workingDirectory, leaderCommand], { stdio: "inherit" });
2120
+ for (const member of fallback.plan.members) {
2121
+ const rolePrompt = [
2122
+ `LongTable team discussion role: ${member.label} (${member.role}).`,
2123
+ "Give claims, objections, open questions, and evidence needs. Address likely disagreement with other roles.",
2124
+ "",
2125
+ prompt
2126
+ ].join("\n");
2127
+ const logPath = join(teamDir, `${member.role}.log`);
2128
+ const command = [
2129
+ `node ${shellEscape(launcher)} review --role ${shellEscape(member.role)} --prompt ${shellEscape(rolePrompt)} --cwd ${shellEscape(workingDirectory)} 2>&1 | tee ${shellEscape(logPath)}`,
2130
+ `echo ${shellEscape(`Role log written to ${logPath}`)}`,
2131
+ `exec ${shellEscape(shell)}`
2132
+ ].join("; ");
2133
+ execFileSync("tmux", ["split-window", "-t", sessionName, "-c", workingDirectory, command], { stdio: "inherit" });
2134
+ execFileSync("tmux", ["select-layout", "-t", sessionName, "tiled"], { stdio: "ignore" });
2135
+ }
2136
+ console.log(`LongTable tmux team launched: ${sessionName}`);
2137
+ console.log(`Attach with: tmux attach -t ${sessionName}`);
2138
+ console.log(`Logs: ${teamDir}`);
2139
+ }
1773
2140
  async function runDecide(args) {
1774
2141
  const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
1775
2142
  const answer = typeof args.answer === "string" ? args.answer.trim() : "";
@@ -2056,6 +2423,10 @@ async function main() {
2056
2423
  await runInit(values);
2057
2424
  return;
2058
2425
  }
2426
+ if (command === "setup") {
2427
+ await runSetup(values);
2428
+ return;
2429
+ }
2059
2430
  if (command === "start") {
2060
2431
  await runStart(values);
2061
2432
  return;
@@ -2100,6 +2471,18 @@ async function main() {
2100
2471
  await runPanelCommand(values);
2101
2472
  return;
2102
2473
  }
2474
+ if (command === "hud") {
2475
+ await runHud(values);
2476
+ return;
2477
+ }
2478
+ if (command === "sentinel") {
2479
+ await runSentinel(values);
2480
+ return;
2481
+ }
2482
+ if (command === "team") {
2483
+ await runTeam(values);
2484
+ return;
2485
+ }
2103
2486
  if (command === "decide") {
2104
2487
  await runDecide(values);
2105
2488
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "private": false,
5
5
  "description": "Researcher-facing LongTable CLI",
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  }
14
14
  },
15
15
  "bin": {
16
- "longtable": "./bin/longtable"
16
+ "longtable": "bin/longtable"
17
17
  },
18
18
  "directories": {
19
19
  "bin": "./bin"
@@ -28,12 +28,12 @@
28
28
  "typecheck": "tsc -p tsconfig.json --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@longtable/checkpoints": "0.1.14",
32
- "@longtable/core": "0.1.14",
33
- "@longtable/memory": "0.1.14",
34
- "@longtable/provider-claude": "0.1.14",
35
- "@longtable/provider-codex": "0.1.14",
36
- "@longtable/setup": "0.1.14"
31
+ "@longtable/checkpoints": "0.1.16",
32
+ "@longtable/core": "0.1.16",
33
+ "@longtable/memory": "0.1.16",
34
+ "@longtable/provider-claude": "0.1.16",
35
+ "@longtable/provider-codex": "0.1.16",
36
+ "@longtable/setup": "0.1.16"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.10.1",