@longtable/cli 0.1.8 → 0.1.10
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 +121 -76
- package/dist/cli.js +711 -66
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/panel.d.ts +40 -0
- package/dist/panel.js +268 -0
- package/dist/persona-router.d.ts +1 -1
- package/dist/persona-router.js +6 -6
- package/dist/personas.d.ts +5 -0
- package/dist/personas.js +44 -8
- package/dist/project-session.d.ts +94 -2
- package/dist/project-session.js +593 -150
- package/dist/prompt-aliases.js +34 -34
- package/package.json +13 -7
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
4
5
|
import { emitKeypressEvents } from "node:readline";
|
|
5
6
|
import { createInterface } from "node:readline/promises";
|
|
6
7
|
import { stdin as input, stdout as output, cwd, exit } from "node:process";
|
|
7
8
|
import { dirname, resolve } from "node:path";
|
|
8
9
|
import { homedir } from "node:os";
|
|
9
|
-
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@
|
|
10
|
-
import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@
|
|
10
|
+
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput, writeRuntimeConfig } from "@longtable/setup";
|
|
11
|
+
import { buildCodexSkillSpecs, buildCodexThinWrappedPrompt, installCodexSkills, listInstalledCodexSkills, renderQuestionRecordPrompt, removeCodexSkills, resolveCodexSkillsDir, runCodexThinWrapper } from "@longtable/provider-codex";
|
|
12
|
+
import { buildClaudeSkillSpecs, installClaudeSkills, listInstalledClaudeSkills, renderQuestionRecordInput, removeClaudeSkills, resolveClaudeSkillsDir } from "@longtable/provider-claude";
|
|
11
13
|
import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodexPromptAliases, resolveCodexPromptsDir } from "./prompt-aliases.js";
|
|
12
14
|
import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router.js";
|
|
13
|
-
import { PERSONA_DEFINITIONS } from "./personas.js";
|
|
14
|
-
import {
|
|
15
|
+
import { PERSONA_DEFINITIONS, listRoleDefinitions } from "./personas.js";
|
|
16
|
+
import { buildPanelFallback, renderPanelSummary } from "./panel.js";
|
|
17
|
+
import { appendInvocationRecordToWorkspace, assertWorkspaceNotBlocked, answerWorkspaceQuestion, createWorkspaceQuestion, createOrUpdateProjectWorkspace, inspectProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
|
|
15
18
|
const VALID_MODES = new Set([
|
|
16
19
|
"explore",
|
|
17
20
|
"review",
|
|
@@ -69,28 +72,40 @@ function usage() {
|
|
|
69
72
|
" Run `longtable ...` in your terminal, not inside the Codex chat box.",
|
|
70
73
|
" After `longtable start`, move into the created project directory and open `codex` there.",
|
|
71
74
|
"",
|
|
72
|
-
" 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-prompts]",
|
|
75
|
+
" 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]",
|
|
73
76
|
" longtable start [--path <dir>] [--name <project>] [--goal <text>] [--blocker <text>] [--perspectives <role[,role]>] [--disagreement synthesis_only|show_on_conflict|always_visible] [--setup <path>] [--json]",
|
|
74
77
|
" longtable resume [--cwd <path>] [--json]",
|
|
78
|
+
" longtable doctor [--cwd <path>] [--fix] [--json] [--codex-dir <path>] [--claude-dir <path>] [--codex-prompts-dir <path>] [--codex-runtime-path <file>] [--claude-runtime-path <file>]",
|
|
79
|
+
" longtable status [--cwd <path>] [--fix] [--json] [--codex-dir <path>] [--claude-dir <path>] [--codex-prompts-dir <path>] [--codex-runtime-path <file>] [--claude-runtime-path <file>]",
|
|
75
80
|
" longtable roles [--json]",
|
|
76
81
|
" longtable show [--json] [--path <file>]",
|
|
77
82
|
" longtable install [--json] [--path <file>] [--runtime-path <file>]",
|
|
78
83
|
" longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
|
|
84
|
+
" longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
|
|
85
|
+
" 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>]",
|
|
86
|
+
" longtable decide [--question <id>] --answer <value-or-text> [--rationale <text>] [--provider codex|claude] [--cwd <path>] [--json]",
|
|
79
87
|
" longtable explore|review|critique|draft|commit|submit [--prompt <text>] [--role <role[,role]>] [--panel] [--show-conflicts] [--show-deliberation] [--print] [--json] [--stage <stage>] [--setup <path>] [--cwd <path>]",
|
|
80
|
-
" longtable codex persist-init [--answers-json <json> | --stdin | full setup flags] [--install-prompts] [--json]",
|
|
88
|
+
" longtable codex persist-init [--answers-json <json> | --stdin | full setup flags] [--install-skills] [--install-prompts] [--json]",
|
|
89
|
+
" longtable codex install-skills [--dir <path>]",
|
|
90
|
+
" longtable codex remove-skills [--dir <path>]",
|
|
81
91
|
" longtable codex install-prompts [--dir <path>]",
|
|
82
92
|
" longtable codex remove-prompts [--dir <path>]",
|
|
83
93
|
" longtable codex status [--dir <path>] [--json]",
|
|
94
|
+
" longtable claude install-skills [--dir <path>]",
|
|
95
|
+
" longtable claude remove-skills [--dir <path>]",
|
|
96
|
+
" longtable claude status [--dir <path>] [--json]",
|
|
84
97
|
"",
|
|
85
98
|
"Examples:",
|
|
86
|
-
" longtable init --flow interview --install-
|
|
99
|
+
" longtable init --flow interview --provider codex --install-skills",
|
|
87
100
|
" longtable start",
|
|
88
101
|
" longtable start --path ~/Research/My-Project --name \"AI Adoption Meta-Analysis\" --goal \"Narrow the review question\"",
|
|
89
102
|
" cd \"<project-path>\" && codex",
|
|
103
|
+
" longtable doctor",
|
|
90
104
|
" longtable roles",
|
|
91
105
|
" longtable ask --prompt \"연구를 시작하고 싶어. 지금 어디서부터 좁혀야 할지 모르겠어.\"",
|
|
92
|
-
" printf '{\"provider\":\"codex\",...}' | longtable codex persist-init --stdin --install-
|
|
93
|
-
" longtable codex install-
|
|
106
|
+
" printf '{\"provider\":\"codex\",...}' | longtable codex persist-init --stdin --install-skills",
|
|
107
|
+
" longtable codex install-skills",
|
|
108
|
+
" longtable claude install-skills"
|
|
94
109
|
].join("\n");
|
|
95
110
|
}
|
|
96
111
|
function parseArgs(argv) {
|
|
@@ -98,13 +113,13 @@ function parseArgs(argv) {
|
|
|
98
113
|
const values = {};
|
|
99
114
|
let subcommand = maybeSubcommand;
|
|
100
115
|
const modeCommand = command && VALID_MODES.has(command);
|
|
101
|
-
const directCommand = command && ["init", "start", "resume", "roles", "show", "install", "codex", "ask"].includes(command);
|
|
116
|
+
const directCommand = command && ["init", "start", "resume", "doctor", "status", "roles", "show", "install", "codex", "claude", "ask", "question", "panel", "decide"].includes(command);
|
|
102
117
|
let startIndex = 1;
|
|
103
118
|
if (modeCommand) {
|
|
104
119
|
subcommand = undefined;
|
|
105
120
|
startIndex = 1;
|
|
106
121
|
}
|
|
107
|
-
else if (command === "codex") {
|
|
122
|
+
else if (command === "codex" || command === "claude") {
|
|
108
123
|
startIndex = 2;
|
|
109
124
|
}
|
|
110
125
|
else if (directCommand) {
|
|
@@ -155,11 +170,11 @@ function buildSetupFlowChoices() {
|
|
|
155
170
|
];
|
|
156
171
|
}
|
|
157
172
|
function renderSetupHeader(flow) {
|
|
158
|
-
const title = flow === "interview" ? "
|
|
173
|
+
const title = flow === "interview" ? "LongTable Setup Interview" : "LongTable Quickstart";
|
|
159
174
|
const subtitle = flow === "interview"
|
|
160
175
|
? "We will ask about your research persona, challenge preferences, and authorship defaults."
|
|
161
|
-
: "We will capture the minimum profile needed to start using
|
|
162
|
-
return [renderBrandBanner("
|
|
176
|
+
: "We will capture the minimum profile needed to start using LongTable.";
|
|
177
|
+
return [renderBrandBanner("LongTable", "Research workspace setup"), "", renderSectionCard(title, [subtitle])].join("\n");
|
|
163
178
|
}
|
|
164
179
|
function renderQuestionHeader(index, total, section, prompt) {
|
|
165
180
|
return [
|
|
@@ -176,7 +191,7 @@ function questionSection(questionId) {
|
|
|
176
191
|
return "Interaction style";
|
|
177
192
|
}
|
|
178
193
|
if (questionId === "weakestDomain" || questionId === "panelPreference") {
|
|
179
|
-
return "How
|
|
194
|
+
return "How LongTable should challenge you";
|
|
180
195
|
}
|
|
181
196
|
return "Authorship and voice";
|
|
182
197
|
}
|
|
@@ -253,14 +268,14 @@ async function verifyWritableWorkspaceParent(projectPath) {
|
|
|
253
268
|
catch (error) {
|
|
254
269
|
const message = error instanceof Error ? error.message : String(error);
|
|
255
270
|
throw new Error([
|
|
256
|
-
`
|
|
271
|
+
`LongTable could not create a project workspace under: ${parentDir}`,
|
|
257
272
|
"Reason: your current shell process cannot write to that parent directory.",
|
|
258
273
|
"",
|
|
259
274
|
"What to try next:",
|
|
260
275
|
`- test it directly: mkdir -p "${resolve(parentDir, "_longtable_write_test")}"`,
|
|
261
|
-
"- if that fails too, this is an OS/disk permission problem rather than a
|
|
276
|
+
"- if that fails too, this is an OS/disk permission problem rather than a LongTable command problem",
|
|
262
277
|
"- try a known-writable path such as ~/Research",
|
|
263
|
-
"- or choose a different existing parent directory when
|
|
278
|
+
"- or choose a different existing parent directory when LongTable asks where the project should live",
|
|
264
279
|
"",
|
|
265
280
|
`Original error: ${message}`
|
|
266
281
|
].join("\n"));
|
|
@@ -481,7 +496,7 @@ async function collectInteractiveAnswers(initialFlow) {
|
|
|
481
496
|
const rl = createInterface({ input, output });
|
|
482
497
|
try {
|
|
483
498
|
const flow = initialFlow ??
|
|
484
|
-
(await promptChoice(rl, "How would you like to set up
|
|
499
|
+
(await promptChoice(rl, "How would you like to set up LongTable?", buildSetupFlowChoices()));
|
|
485
500
|
console.log("");
|
|
486
501
|
console.log(renderSetupHeader(flow));
|
|
487
502
|
console.log("");
|
|
@@ -559,11 +574,11 @@ async function collectProjectInterview(setup, args) {
|
|
|
559
574
|
try {
|
|
560
575
|
if (needsInteractivePrompts) {
|
|
561
576
|
console.log("");
|
|
562
|
-
console.log(renderBrandBanner("
|
|
577
|
+
console.log(renderBrandBanner("LongTable", "Project workspace interview"));
|
|
563
578
|
console.log("");
|
|
564
|
-
console.log(renderSectionCard("
|
|
579
|
+
console.log(renderSectionCard("LongTable Project Start", [
|
|
565
580
|
"We will create a project workspace and a session memory seed for today's work.",
|
|
566
|
-
"At the end,
|
|
581
|
+
"At the end, LongTable will tell you exactly which directory to open in Codex."
|
|
567
582
|
]));
|
|
568
583
|
console.log("");
|
|
569
584
|
}
|
|
@@ -575,7 +590,7 @@ async function collectProjectInterview(setup, args) {
|
|
|
575
590
|
const suggestedPath = resolveInteractiveProjectPath(suggestedParentDir, projectName);
|
|
576
591
|
const projectPath = (typeof args.path === "string" && args.path.trim()
|
|
577
592
|
? normalizeUserPath(args.path.trim())
|
|
578
|
-
: resolveInteractiveProjectPath((await promptText(rl, renderQuestionHeader(2, 6, "Project interview", `Which parent directory should contain this project?\
|
|
593
|
+
: resolveInteractiveProjectPath((await promptText(rl, renderQuestionHeader(2, 6, "Project interview", `Which parent directory should contain this project?\nLongTable will create this folder:\n${suggestedPath}`), true)), projectName));
|
|
579
594
|
const currentGoal = (typeof args.goal === "string" && args.goal.trim()) ||
|
|
580
595
|
(await promptText(rl, renderQuestionHeader(3, 6, "Current session", "What are you trying to accomplish in this session?"), true));
|
|
581
596
|
const currentBlocker = (typeof args.blocker === "string" && args.blocker.trim()) ||
|
|
@@ -588,7 +603,7 @@ async function collectProjectInterview(setup, args) {
|
|
|
588
603
|
{
|
|
589
604
|
id: "synthesis_only",
|
|
590
605
|
label: "Synthesis only",
|
|
591
|
-
description: "Show one
|
|
606
|
+
description: "Show one LongTable answer unless I ask for more."
|
|
592
607
|
},
|
|
593
608
|
{
|
|
594
609
|
id: "show_on_conflict",
|
|
@@ -667,9 +682,11 @@ async function runInit(args) {
|
|
|
667
682
|
const json = args.json === true;
|
|
668
683
|
const installRuntime = args["no-install"] !== true;
|
|
669
684
|
const installPrompts = args["install-prompts"] === true;
|
|
685
|
+
const installSkills = args["install-skills"] === true;
|
|
670
686
|
const customPath = typeof args.path === "string" ? args.path : undefined;
|
|
671
687
|
const runtimePath = typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined;
|
|
672
688
|
const promptsDir = typeof args.dir === "string" ? args.dir : undefined;
|
|
689
|
+
const skillsDir = typeof args["skills-dir"] === "string" ? args["skills-dir"] : promptsDir;
|
|
673
690
|
const { flow, provider, answers } = hasCompleteFlagInput(args)
|
|
674
691
|
? {
|
|
675
692
|
flow: resolveSetupFlow(args),
|
|
@@ -686,8 +703,23 @@ async function runInit(args) {
|
|
|
686
703
|
if (provider === "codex" && installPrompts) {
|
|
687
704
|
installedPrompts = await installCodexPromptAliases(promptsDir);
|
|
688
705
|
}
|
|
706
|
+
let installedSkills = [];
|
|
707
|
+
if (provider === "codex" && installSkills) {
|
|
708
|
+
installedSkills = await installCodexSkills(listRoleDefinitions(), skillsDir);
|
|
709
|
+
}
|
|
710
|
+
if (provider === "claude" && installSkills) {
|
|
711
|
+
installedSkills = await installClaudeSkills(listRoleDefinitions(), skillsDir);
|
|
712
|
+
}
|
|
689
713
|
if (json) {
|
|
690
|
-
|
|
714
|
+
if (installedPrompts.length === 0 && installedSkills.length === 0) {
|
|
715
|
+
console.log(serializeSetupOutput(outputValue));
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
console.log(JSON.stringify({
|
|
719
|
+
setup: outputValue,
|
|
720
|
+
installedPrompts: installedPrompts.map((prompt) => prompt.name),
|
|
721
|
+
installedSkills: installedSkills.map((skill) => skill.name)
|
|
722
|
+
}, null, 2));
|
|
691
723
|
return;
|
|
692
724
|
}
|
|
693
725
|
console.log(renderSetupSummary(outputValue));
|
|
@@ -699,17 +731,32 @@ async function runInit(args) {
|
|
|
699
731
|
console.log("");
|
|
700
732
|
console.log("Installed Codex prompt files:");
|
|
701
733
|
for (const prompt of installedPrompts) {
|
|
702
|
-
console.log(`-
|
|
734
|
+
console.log(`- ${prompt.name}`);
|
|
735
|
+
}
|
|
736
|
+
console.log(" Note: prompt files are legacy and may not be exposed by your Codex build.");
|
|
737
|
+
}
|
|
738
|
+
if (installedSkills.length > 0) {
|
|
739
|
+
console.log("");
|
|
740
|
+
console.log(`Installed ${provider === "codex" ? "Codex" : "Claude"} skill files:`);
|
|
741
|
+
for (const skill of installedSkills) {
|
|
742
|
+
console.log(`- ${skill.name}`);
|
|
703
743
|
}
|
|
704
|
-
console.log("
|
|
744
|
+
console.log(" Use these by naming LongTable naturally, e.g. `lt panel: ...`.");
|
|
705
745
|
}
|
|
706
746
|
if (provider === "codex") {
|
|
707
747
|
console.log("");
|
|
708
748
|
console.log("Next step:");
|
|
709
749
|
console.log("- Start here: `longtable start`.");
|
|
710
750
|
console.log("- If you want a direct natural-language entry: `longtable ask --prompt \"...\"`.");
|
|
711
|
-
console.log("- Codex
|
|
712
|
-
console.log("- Suggested next action: create a project workspace and let
|
|
751
|
+
console.log("- Codex skills are the preferred native surface. Prompt files are legacy and may not expose slash commands.");
|
|
752
|
+
console.log("- Suggested next action: create a project workspace and let LongTable interview the current session.");
|
|
753
|
+
}
|
|
754
|
+
if (provider === "claude") {
|
|
755
|
+
console.log("");
|
|
756
|
+
console.log("Next step:");
|
|
757
|
+
console.log("- Start here: `longtable start`.");
|
|
758
|
+
console.log("- In Claude Code, use natural language such as `lt explore: ...` or `lt panel: ...`.");
|
|
759
|
+
console.log("- Claude skills are adapter output from LongTable roles, not the source of truth.");
|
|
713
760
|
}
|
|
714
761
|
}
|
|
715
762
|
async function runShow(args) {
|
|
@@ -731,6 +778,300 @@ async function runInstall(args) {
|
|
|
731
778
|
}
|
|
732
779
|
console.log(renderInstallSummary(result));
|
|
733
780
|
}
|
|
781
|
+
function commandOnPath(command) {
|
|
782
|
+
try {
|
|
783
|
+
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
catch {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function missingNames(expected, installed) {
|
|
791
|
+
const installedSet = new Set(installed);
|
|
792
|
+
return expected.filter((name) => !installedSet.has(name));
|
|
793
|
+
}
|
|
794
|
+
function setupForProvider(setup, provider) {
|
|
795
|
+
return {
|
|
796
|
+
...setup,
|
|
797
|
+
providerSelection: provider === "claude"
|
|
798
|
+
? {
|
|
799
|
+
provider,
|
|
800
|
+
checkpointProtocol: "native_structured",
|
|
801
|
+
supportsStructuredQuestions: true
|
|
802
|
+
}
|
|
803
|
+
: {
|
|
804
|
+
provider,
|
|
805
|
+
checkpointProtocol: "numbered",
|
|
806
|
+
supportsStructuredQuestions: false
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
async function collectDoctorStatus(args) {
|
|
811
|
+
const roles = listRoleDefinitions();
|
|
812
|
+
const setupOverride = typeof args.setup === "string"
|
|
813
|
+
? args.setup
|
|
814
|
+
: typeof args.path === "string"
|
|
815
|
+
? args.path
|
|
816
|
+
: undefined;
|
|
817
|
+
const setupPath = resolveDefaultSetupPath(setupOverride).path;
|
|
818
|
+
const codexRuntimeOverride = typeof args["codex-runtime-path"] === "string"
|
|
819
|
+
? args["codex-runtime-path"]
|
|
820
|
+
: undefined;
|
|
821
|
+
const claudeRuntimeOverride = typeof args["claude-runtime-path"] === "string"
|
|
822
|
+
? args["claude-runtime-path"]
|
|
823
|
+
: undefined;
|
|
824
|
+
const codexDir = typeof args["codex-dir"] === "string"
|
|
825
|
+
? args["codex-dir"]
|
|
826
|
+
: typeof args.dir === "string"
|
|
827
|
+
? args.dir
|
|
828
|
+
: undefined;
|
|
829
|
+
const codexPromptsDir = typeof args["codex-prompts-dir"] === "string"
|
|
830
|
+
? args["codex-prompts-dir"]
|
|
831
|
+
: typeof args["prompts-dir"] === "string"
|
|
832
|
+
? args["prompts-dir"]
|
|
833
|
+
: typeof args.dir === "string"
|
|
834
|
+
? args.dir
|
|
835
|
+
: undefined;
|
|
836
|
+
const claudeDir = typeof args["claude-dir"] === "string"
|
|
837
|
+
? args["claude-dir"]
|
|
838
|
+
: typeof args.dir === "string"
|
|
839
|
+
? args.dir
|
|
840
|
+
: undefined;
|
|
841
|
+
const codexRuntimePath = resolveDefaultRuntimeConfigPath("codex", codexRuntimeOverride).path;
|
|
842
|
+
const claudeRuntimePath = resolveDefaultRuntimeConfigPath("claude", claudeRuntimeOverride).path;
|
|
843
|
+
const expectedCodexSkills = buildCodexSkillSpecs(roles).map((skill) => skill.name);
|
|
844
|
+
const expectedClaudeSkills = buildClaudeSkillSpecs(roles).map((skill) => skill.name);
|
|
845
|
+
const [codexSkills, claudeSkills, codexAliases, workspace] = await Promise.all([
|
|
846
|
+
listInstalledCodexSkills(roles, codexDir),
|
|
847
|
+
listInstalledClaudeSkills(roles, claudeDir),
|
|
848
|
+
listInstalledCodexPromptAliases(codexPromptsDir),
|
|
849
|
+
inspectProjectWorkspace(typeof args.cwd === "string" ? args.cwd : cwd())
|
|
850
|
+
]);
|
|
851
|
+
const installedCodexSkills = codexSkills.map((skill) => skill.name);
|
|
852
|
+
const installedClaudeSkills = claudeSkills.map((skill) => skill.name);
|
|
853
|
+
return {
|
|
854
|
+
setupPath,
|
|
855
|
+
setupExists: existsSync(setupPath),
|
|
856
|
+
providers: {
|
|
857
|
+
codex: {
|
|
858
|
+
command: "codex",
|
|
859
|
+
commandOnPath: commandOnPath("codex"),
|
|
860
|
+
runtimePath: codexRuntimePath,
|
|
861
|
+
runtimeExists: existsSync(codexRuntimePath),
|
|
862
|
+
skillsDir: resolveCodexSkillsDir(codexDir),
|
|
863
|
+
expectedSkills: expectedCodexSkills,
|
|
864
|
+
installedSkills: installedCodexSkills,
|
|
865
|
+
missingSkills: missingNames(expectedCodexSkills, installedCodexSkills),
|
|
866
|
+
promptsDir: resolveCodexPromptsDir(codexPromptsDir),
|
|
867
|
+
legacyPromptFilesInstalled: codexAliases.map((alias) => alias.name)
|
|
868
|
+
},
|
|
869
|
+
claude: {
|
|
870
|
+
command: "claude",
|
|
871
|
+
commandOnPath: commandOnPath("claude"),
|
|
872
|
+
runtimePath: claudeRuntimePath,
|
|
873
|
+
runtimeExists: existsSync(claudeRuntimePath),
|
|
874
|
+
skillsDir: resolveClaudeSkillsDir(claudeDir),
|
|
875
|
+
expectedSkills: expectedClaudeSkills,
|
|
876
|
+
installedSkills: installedClaudeSkills,
|
|
877
|
+
missingSkills: missingNames(expectedClaudeSkills, installedClaudeSkills)
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
workspace
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function renderProviderDoctorBlock(label, provider) {
|
|
884
|
+
const expectedCount = provider.expectedSkills.length;
|
|
885
|
+
const installedCount = provider.installedSkills.length;
|
|
886
|
+
return [
|
|
887
|
+
`${label}:`,
|
|
888
|
+
`- command: ${provider.commandOnPath ? "present" : "missing"} (${provider.command})`,
|
|
889
|
+
`- runtime artifact: ${provider.runtimeExists ? "present" : "missing"} (${provider.runtimePath})`,
|
|
890
|
+
`- skills: ${installedCount}/${expectedCount} installed (${provider.skillsDir})`,
|
|
891
|
+
...(provider.missingSkills.length > 0
|
|
892
|
+
? [`- missing skills: ${provider.missingSkills.join(", ")}`]
|
|
893
|
+
: ["- missing skills: none"])
|
|
894
|
+
];
|
|
895
|
+
}
|
|
896
|
+
function renderDoctorStatus(status) {
|
|
897
|
+
const lines = [
|
|
898
|
+
"LongTable doctor",
|
|
899
|
+
`- setup: ${status.setupExists ? "present" : "missing"} (${status.setupPath})`,
|
|
900
|
+
"",
|
|
901
|
+
...renderProviderDoctorBlock("Codex", status.providers.codex),
|
|
902
|
+
`- legacy prompt files: ${status.providers.codex.legacyPromptFilesInstalled.length}`,
|
|
903
|
+
...(status.providers.codex.legacyPromptFilesInstalled.length > 0
|
|
904
|
+
? [`- legacy prompt names: ${status.providers.codex.legacyPromptFilesInstalled.join(", ")}`]
|
|
905
|
+
: []),
|
|
906
|
+
"",
|
|
907
|
+
...renderProviderDoctorBlock("Claude", status.providers.claude),
|
|
908
|
+
"",
|
|
909
|
+
"Workspace:"
|
|
910
|
+
];
|
|
911
|
+
if (!status.workspace.found) {
|
|
912
|
+
lines.push("- project: not found from current directory");
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
const workspace = status.workspace;
|
|
916
|
+
lines.push(`- project: ${workspace.project?.name ?? "unknown"}`, `- root: ${workspace.rootPath ?? "unknown"}`, `- goal: ${workspace.session?.currentGoal ?? "unknown"}`, `- invocations: ${workspace.counts?.invocations ?? 0}`, `- questions: ${workspace.counts?.questions ?? 0} (${workspace.counts?.pendingQuestions ?? 0} pending, ${workspace.counts?.answeredQuestions ?? 0} answered)`, `- decisions: ${workspace.counts?.decisions ?? 0}`);
|
|
917
|
+
if ((workspace.recentInvocations ?? []).length > 0) {
|
|
918
|
+
lines.push("- recent invocations:");
|
|
919
|
+
for (const invocation of workspace.recentInvocations ?? []) {
|
|
920
|
+
const roles = invocation.roles.length > 0 ? invocation.roles.join(",") : "auto";
|
|
921
|
+
lines.push(` - ${invocation.kind}/${invocation.mode} via ${invocation.surface}: ${roles} (${invocation.status})`);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if ((workspace.pendingQuestions ?? []).length > 0) {
|
|
925
|
+
lines.push("- pending questions:");
|
|
926
|
+
for (const question of workspace.pendingQuestions ?? []) {
|
|
927
|
+
lines.push(` - ${question.id}: ${question.question} (${question.options.join("/")})`);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
const nextActions = [];
|
|
932
|
+
const canFix = status.providers.codex.missingSkills.length > 0 ||
|
|
933
|
+
status.providers.claude.missingSkills.length > 0 ||
|
|
934
|
+
status.providers.codex.legacyPromptFilesInstalled.length > 0 ||
|
|
935
|
+
(status.setupExists &&
|
|
936
|
+
(!status.providers.codex.runtimeExists || !status.providers.claude.runtimeExists));
|
|
937
|
+
if (canFix) {
|
|
938
|
+
nextActions.push("longtable doctor --fix");
|
|
939
|
+
}
|
|
940
|
+
if (!status.setupExists) {
|
|
941
|
+
nextActions.push("longtable init --flow interview --provider codex --install-skills");
|
|
942
|
+
}
|
|
943
|
+
if (!status.workspace.found) {
|
|
944
|
+
nextActions.push("longtable start");
|
|
945
|
+
}
|
|
946
|
+
const firstQuestion = status.workspace.pendingQuestions?.[0];
|
|
947
|
+
if (firstQuestion) {
|
|
948
|
+
nextActions.push(`longtable decide --question ${firstQuestion.id} --answer <value>`);
|
|
949
|
+
}
|
|
950
|
+
if (nextActions.length > 0) {
|
|
951
|
+
lines.push("", "Next actions:");
|
|
952
|
+
for (const action of nextActions) {
|
|
953
|
+
lines.push(`- ${action}`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return lines.join("\n");
|
|
957
|
+
}
|
|
958
|
+
function renderRepairSummary(repair) {
|
|
959
|
+
const lines = ["LongTable doctor repair"];
|
|
960
|
+
if (repair.installedCodexSkills.length > 0) {
|
|
961
|
+
lines.push(`- installed Codex skills: ${repair.installedCodexSkills.length}`);
|
|
962
|
+
}
|
|
963
|
+
if (repair.installedClaudeSkills.length > 0) {
|
|
964
|
+
lines.push(`- installed Claude skills: ${repair.installedClaudeSkills.length}`);
|
|
965
|
+
}
|
|
966
|
+
if (repair.removedLegacyPromptFiles.length > 0) {
|
|
967
|
+
lines.push(`- removed legacy prompt files: ${repair.removedLegacyPromptFiles.length}`);
|
|
968
|
+
}
|
|
969
|
+
if (repair.writtenRuntimeConfigs.length > 0) {
|
|
970
|
+
lines.push("- wrote runtime configs:");
|
|
971
|
+
for (const target of repair.writtenRuntimeConfigs) {
|
|
972
|
+
lines.push(` - ${target.provider}: ${target.path}`);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
if (repair.skipped.length > 0) {
|
|
976
|
+
lines.push("- skipped:");
|
|
977
|
+
for (const item of repair.skipped) {
|
|
978
|
+
lines.push(` - ${item}`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (lines.length === 1) {
|
|
982
|
+
lines.push("- no repairs needed");
|
|
983
|
+
}
|
|
984
|
+
return lines.join("\n");
|
|
985
|
+
}
|
|
986
|
+
async function repairDoctorStatus(args, status) {
|
|
987
|
+
const roles = listRoleDefinitions();
|
|
988
|
+
const codexDir = typeof args["codex-dir"] === "string"
|
|
989
|
+
? args["codex-dir"]
|
|
990
|
+
: typeof args.dir === "string"
|
|
991
|
+
? args.dir
|
|
992
|
+
: undefined;
|
|
993
|
+
const codexPromptsDir = typeof args["codex-prompts-dir"] === "string"
|
|
994
|
+
? args["codex-prompts-dir"]
|
|
995
|
+
: typeof args["prompts-dir"] === "string"
|
|
996
|
+
? args["prompts-dir"]
|
|
997
|
+
: typeof args.dir === "string"
|
|
998
|
+
? args.dir
|
|
999
|
+
: undefined;
|
|
1000
|
+
const claudeDir = typeof args["claude-dir"] === "string"
|
|
1001
|
+
? args["claude-dir"]
|
|
1002
|
+
: typeof args.dir === "string"
|
|
1003
|
+
? args.dir
|
|
1004
|
+
: undefined;
|
|
1005
|
+
const setupOverride = typeof args.setup === "string"
|
|
1006
|
+
? args.setup
|
|
1007
|
+
: typeof args.path === "string"
|
|
1008
|
+
? args.path
|
|
1009
|
+
: undefined;
|
|
1010
|
+
const codexRuntimeOverride = typeof args["codex-runtime-path"] === "string"
|
|
1011
|
+
? args["codex-runtime-path"]
|
|
1012
|
+
: undefined;
|
|
1013
|
+
const claudeRuntimeOverride = typeof args["claude-runtime-path"] === "string"
|
|
1014
|
+
? args["claude-runtime-path"]
|
|
1015
|
+
: undefined;
|
|
1016
|
+
const repair = {
|
|
1017
|
+
installedCodexSkills: [],
|
|
1018
|
+
installedClaudeSkills: [],
|
|
1019
|
+
removedLegacyPromptFiles: [],
|
|
1020
|
+
writtenRuntimeConfigs: [],
|
|
1021
|
+
skipped: []
|
|
1022
|
+
};
|
|
1023
|
+
if (status.providers.codex.missingSkills.length > 0) {
|
|
1024
|
+
repair.installedCodexSkills = (await installCodexSkills(roles, codexDir)).map((skill) => skill.name);
|
|
1025
|
+
}
|
|
1026
|
+
if (status.providers.claude.missingSkills.length > 0) {
|
|
1027
|
+
repair.installedClaudeSkills = (await installClaudeSkills(roles, claudeDir)).map((skill) => skill.name);
|
|
1028
|
+
}
|
|
1029
|
+
if (status.providers.codex.legacyPromptFilesInstalled.length > 0) {
|
|
1030
|
+
repair.removedLegacyPromptFiles = await removeCodexPromptAliases(codexPromptsDir);
|
|
1031
|
+
}
|
|
1032
|
+
if (!status.setupExists) {
|
|
1033
|
+
repair.skipped.push("runtime configs require a researcher setup; run `longtable init --flow interview --provider codex` first");
|
|
1034
|
+
return repair;
|
|
1035
|
+
}
|
|
1036
|
+
const setup = await loadSetupOutput(setupOverride);
|
|
1037
|
+
if (!status.providers.codex.runtimeExists) {
|
|
1038
|
+
const target = await writeRuntimeConfig(setupForProvider(setup, "codex"), status.setupPath, codexRuntimeOverride);
|
|
1039
|
+
repair.writtenRuntimeConfigs.push({
|
|
1040
|
+
provider: target.provider,
|
|
1041
|
+
path: target.path,
|
|
1042
|
+
format: target.format
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
if (!status.providers.claude.runtimeExists) {
|
|
1046
|
+
const target = await writeRuntimeConfig(setupForProvider(setup, "claude"), status.setupPath, claudeRuntimeOverride);
|
|
1047
|
+
repair.writtenRuntimeConfigs.push({
|
|
1048
|
+
provider: target.provider,
|
|
1049
|
+
path: target.path,
|
|
1050
|
+
format: target.format
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
return repair;
|
|
1054
|
+
}
|
|
1055
|
+
async function runDoctor(args) {
|
|
1056
|
+
const status = await collectDoctorStatus(args);
|
|
1057
|
+
if (args.fix === true) {
|
|
1058
|
+
const repair = await repairDoctorStatus(args, status);
|
|
1059
|
+
const updatedStatus = await collectDoctorStatus(args);
|
|
1060
|
+
if (args.json === true) {
|
|
1061
|
+
console.log(JSON.stringify({ repair, status: updatedStatus }, null, 2));
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
console.log(renderRepairSummary(repair));
|
|
1065
|
+
console.log("");
|
|
1066
|
+
console.log(renderDoctorStatus(updatedStatus));
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
if (args.json === true) {
|
|
1070
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
console.log(renderDoctorStatus(status));
|
|
1074
|
+
}
|
|
734
1075
|
async function runCodexPersistInit(args) {
|
|
735
1076
|
const { flow, provider, answers } = await readPersistAnswers(args);
|
|
736
1077
|
const outputValue = createPersistedSetupOutput(answers, provider, flow);
|
|
@@ -742,11 +1083,16 @@ async function runCodexPersistInit(args) {
|
|
|
742
1083
|
if (provider === "codex" && args["install-prompts"] === true) {
|
|
743
1084
|
installedPrompts = await installCodexPromptAliases(typeof args.dir === "string" ? args.dir : undefined);
|
|
744
1085
|
}
|
|
1086
|
+
let installedSkills = [];
|
|
1087
|
+
if (provider === "codex" && args["install-skills"] === true) {
|
|
1088
|
+
installedSkills = await installCodexSkills(listRoleDefinitions(), typeof args.dir === "string" ? args.dir : undefined);
|
|
1089
|
+
}
|
|
745
1090
|
if (args.json === true) {
|
|
746
1091
|
console.log(JSON.stringify({
|
|
747
1092
|
setup: outputValue,
|
|
748
1093
|
install: result,
|
|
749
|
-
installedPrompts: installedPrompts.map((prompt) => prompt.name)
|
|
1094
|
+
installedPrompts: installedPrompts.map((prompt) => prompt.name),
|
|
1095
|
+
installedSkills: installedSkills.map((skill) => skill.name)
|
|
750
1096
|
}, null, 2));
|
|
751
1097
|
return;
|
|
752
1098
|
}
|
|
@@ -757,17 +1103,25 @@ async function runCodexPersistInit(args) {
|
|
|
757
1103
|
console.log("");
|
|
758
1104
|
console.log("Installed Codex prompt files:");
|
|
759
1105
|
for (const prompt of installedPrompts) {
|
|
760
|
-
console.log(`-
|
|
1106
|
+
console.log(`- ${prompt.name}`);
|
|
1107
|
+
}
|
|
1108
|
+
console.log(" Note: prompt files are legacy and may not be exposed by your Codex build.");
|
|
1109
|
+
}
|
|
1110
|
+
if (installedSkills.length > 0) {
|
|
1111
|
+
console.log("");
|
|
1112
|
+
console.log("Installed Codex skill files:");
|
|
1113
|
+
for (const skill of installedSkills) {
|
|
1114
|
+
console.log(`- ${skill.name}`);
|
|
761
1115
|
}
|
|
762
|
-
console.log("
|
|
1116
|
+
console.log(" Use these inside Codex by naming LongTable naturally, e.g. `lt panel: ...`.");
|
|
763
1117
|
}
|
|
764
1118
|
if (provider === "codex") {
|
|
765
1119
|
console.log("");
|
|
766
1120
|
console.log("Next step:");
|
|
767
1121
|
console.log("- Start here: `longtable start`.");
|
|
768
1122
|
console.log("- If you want a direct natural-language entry: `longtable ask --prompt \"...\"`.");
|
|
769
|
-
console.log("- Codex
|
|
770
|
-
console.log("- Suggested next action: create a project workspace and let
|
|
1123
|
+
console.log("- Codex skills are the preferred native surface. Prompt files are legacy and may not expose slash commands.");
|
|
1124
|
+
console.log("- Suggested next action: create a project workspace and let LongTable interview the current session.");
|
|
771
1125
|
}
|
|
772
1126
|
}
|
|
773
1127
|
async function resolvePrompt(prompt) {
|
|
@@ -779,7 +1133,7 @@ async function resolvePrompt(prompt) {
|
|
|
779
1133
|
}
|
|
780
1134
|
const rl = createInterface({ input, output });
|
|
781
1135
|
try {
|
|
782
|
-
return (await rl.question("What should
|
|
1136
|
+
return (await rl.question("What should LongTable help with?\n> ")).trim();
|
|
783
1137
|
}
|
|
784
1138
|
finally {
|
|
785
1139
|
rl.close();
|
|
@@ -828,6 +1182,20 @@ function inferModeFromPrompt(prompt) {
|
|
|
828
1182
|
}
|
|
829
1183
|
return "explore";
|
|
830
1184
|
}
|
|
1185
|
+
function parsePanelVisibility(value) {
|
|
1186
|
+
if (value === "synthesis_only" ||
|
|
1187
|
+
value === "show_on_conflict" ||
|
|
1188
|
+
value === "always_visible") {
|
|
1189
|
+
return value;
|
|
1190
|
+
}
|
|
1191
|
+
return undefined;
|
|
1192
|
+
}
|
|
1193
|
+
function parsePanelMode(value) {
|
|
1194
|
+
if (value && VALID_MODES.has(value) && value !== "explore" && value !== "submit") {
|
|
1195
|
+
return value;
|
|
1196
|
+
}
|
|
1197
|
+
return "review";
|
|
1198
|
+
}
|
|
831
1199
|
async function loadOptionalSetup(path) {
|
|
832
1200
|
try {
|
|
833
1201
|
return await loadSetupOutput(path);
|
|
@@ -842,7 +1210,7 @@ async function buildProjectAwarePrompt(prompt, workingDirectory) {
|
|
|
842
1210
|
return { prompt, projectContextFound: false };
|
|
843
1211
|
}
|
|
844
1212
|
const lines = [
|
|
845
|
-
"
|
|
1213
|
+
"LongTable project context",
|
|
846
1214
|
`project: ${context.project.projectName}`,
|
|
847
1215
|
`current session goal: ${context.session.currentGoal}`,
|
|
848
1216
|
...(context.session.currentBlocker ? [`current blocker: ${context.session.currentBlocker}`] : []),
|
|
@@ -864,6 +1232,10 @@ async function runModeCommand(mode, args) {
|
|
|
864
1232
|
throw new Error(`Invalid stage: ${stage}`);
|
|
865
1233
|
}
|
|
866
1234
|
const setup = await loadOptionalSetup(typeof args.setup === "string" ? args.setup : undefined);
|
|
1235
|
+
const projectContext = await loadProjectContextFromDirectory(workingDirectory);
|
|
1236
|
+
if (projectContext) {
|
|
1237
|
+
await assertWorkspaceNotBlocked(projectContext);
|
|
1238
|
+
}
|
|
867
1239
|
const projectAware = await buildProjectAwarePrompt(prompt, workingDirectory);
|
|
868
1240
|
const panelPreference = setup?.profileSeed.panelPreference;
|
|
869
1241
|
const panelRequested = args.panel === true ||
|
|
@@ -898,6 +1270,125 @@ async function runModeCommand(mode, args) {
|
|
|
898
1270
|
});
|
|
899
1271
|
exit(exitCode);
|
|
900
1272
|
}
|
|
1273
|
+
async function runPanelCommand(args) {
|
|
1274
|
+
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
1275
|
+
const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
|
|
1276
|
+
if (!prompt) {
|
|
1277
|
+
throw new Error("A prompt is required.");
|
|
1278
|
+
}
|
|
1279
|
+
const setup = await loadOptionalSetup(typeof args.setup === "string" ? args.setup : undefined);
|
|
1280
|
+
const existingContext = await loadProjectContextFromDirectory(workingDirectory);
|
|
1281
|
+
if (existingContext) {
|
|
1282
|
+
await assertWorkspaceNotBlocked(existingContext);
|
|
1283
|
+
}
|
|
1284
|
+
const projectAware = await buildProjectAwarePrompt(prompt, workingDirectory);
|
|
1285
|
+
const provider = setup?.providerSelection.provider;
|
|
1286
|
+
const visibility = parsePanelVisibility(typeof args.visibility === "string" ? args.visibility : undefined) ??
|
|
1287
|
+
parsePanelVisibility(setup?.profileSeed.panelPreference) ??
|
|
1288
|
+
"always_visible";
|
|
1289
|
+
const mode = parsePanelMode(typeof args.mode === "string" ? args.mode : undefined);
|
|
1290
|
+
const fallback = buildPanelFallback({
|
|
1291
|
+
prompt: projectAware.prompt,
|
|
1292
|
+
mode,
|
|
1293
|
+
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
1294
|
+
provider,
|
|
1295
|
+
visibility
|
|
1296
|
+
});
|
|
1297
|
+
if (projectAware.projectContextFound) {
|
|
1298
|
+
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
1299
|
+
if (context) {
|
|
1300
|
+
await appendInvocationRecordToWorkspace(context, fallback.invocationRecord, [fallback.questionRecord]);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
if (args.json === true) {
|
|
1304
|
+
console.log(JSON.stringify({
|
|
1305
|
+
intent: fallback.intent,
|
|
1306
|
+
plan: fallback.plan,
|
|
1307
|
+
result: fallback.result,
|
|
1308
|
+
invocationRecord: fallback.invocationRecord,
|
|
1309
|
+
questionRecord: fallback.questionRecord,
|
|
1310
|
+
execution: {
|
|
1311
|
+
status: "planned",
|
|
1312
|
+
stableSurface: "sequential_fallback",
|
|
1313
|
+
nativeParallel: "not_required_for_option_a",
|
|
1314
|
+
projectContextFound: projectAware.projectContextFound,
|
|
1315
|
+
invocationLogged: projectAware.projectContextFound
|
|
1316
|
+
},
|
|
1317
|
+
fallbackPrompt: fallback.prompt
|
|
1318
|
+
}, null, 2));
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
if (args.print === true) {
|
|
1322
|
+
console.log(fallback.prompt);
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
console.log(renderPanelSummary(fallback.plan));
|
|
1326
|
+
console.log("");
|
|
1327
|
+
const exitCode = await runCodexThinWrapper({
|
|
1328
|
+
prompt: fallback.prompt,
|
|
1329
|
+
mode,
|
|
1330
|
+
setupPath: typeof args.setup === "string" ? args.setup : undefined,
|
|
1331
|
+
workingDirectory,
|
|
1332
|
+
json: false
|
|
1333
|
+
});
|
|
1334
|
+
exit(exitCode);
|
|
1335
|
+
}
|
|
1336
|
+
async function runQuestion(args) {
|
|
1337
|
+
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
1338
|
+
const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
|
|
1339
|
+
if (!prompt) {
|
|
1340
|
+
throw new Error("A decision context is required. Pass --prompt <text>.");
|
|
1341
|
+
}
|
|
1342
|
+
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
1343
|
+
if (!context) {
|
|
1344
|
+
throw new Error("No LongTable project workspace was found here. Run this inside a project or pass --cwd.");
|
|
1345
|
+
}
|
|
1346
|
+
const provider = args.provider === "claude" ? "claude" : args.provider === "codex" ? "codex" : undefined;
|
|
1347
|
+
const required = args.required === true ? true : args.advisory === true ? false : undefined;
|
|
1348
|
+
const result = await createWorkspaceQuestion({
|
|
1349
|
+
context,
|
|
1350
|
+
prompt,
|
|
1351
|
+
title: typeof args.title === "string" ? args.title : undefined,
|
|
1352
|
+
question: typeof args.text === "string" ? args.text : undefined,
|
|
1353
|
+
provider,
|
|
1354
|
+
required
|
|
1355
|
+
});
|
|
1356
|
+
const transport = provider === "claude"
|
|
1357
|
+
? renderQuestionRecordInput(result.question)
|
|
1358
|
+
: renderQuestionRecordPrompt(result.question);
|
|
1359
|
+
if (args.json === true) {
|
|
1360
|
+
console.log(JSON.stringify({
|
|
1361
|
+
question: result.question,
|
|
1362
|
+
transport,
|
|
1363
|
+
files: {
|
|
1364
|
+
state: context.stateFilePath,
|
|
1365
|
+
current: context.currentFilePath
|
|
1366
|
+
},
|
|
1367
|
+
nextAction: `longtable decide --question ${result.question.id} --answer <value>`
|
|
1368
|
+
}, null, 2));
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
if (args.print === true) {
|
|
1372
|
+
if (provider === "claude") {
|
|
1373
|
+
console.log(JSON.stringify(transport, null, 2));
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
console.log("prompt" in transport ? transport.prompt : JSON.stringify(transport, null, 2));
|
|
1377
|
+
}
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const optionValues = [
|
|
1381
|
+
...result.question.prompt.options.map((option) => option.value),
|
|
1382
|
+
...(result.question.prompt.allowOther ? ["other"] : [])
|
|
1383
|
+
];
|
|
1384
|
+
console.log(result.question.prompt.required ? "LongTable required Researcher Checkpoint recorded" : "LongTable advisory Researcher Checkpoint recorded");
|
|
1385
|
+
console.log(`- question: ${result.question.id}`);
|
|
1386
|
+
console.log(`- checkpoint: ${result.question.prompt.checkpointKey ?? "manual"}`);
|
|
1387
|
+
console.log(`- prompt: ${result.question.prompt.question}`);
|
|
1388
|
+
console.log(`- options: ${optionValues.join("/")}`);
|
|
1389
|
+
console.log(`- answer: longtable decide --question ${result.question.id} --answer <value>`);
|
|
1390
|
+
console.log(`- current: ${context.currentFilePath}`);
|
|
1391
|
+
}
|
|
901
1392
|
async function runAsk(args) {
|
|
902
1393
|
const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
|
|
903
1394
|
if (!prompt) {
|
|
@@ -907,7 +1398,7 @@ async function runAsk(args) {
|
|
|
907
1398
|
const effectivePrompt = directive.cleanedPrompt;
|
|
908
1399
|
const inferred = directive.mode ?? inferModeFromPrompt(effectivePrompt);
|
|
909
1400
|
if (inferred === "status") {
|
|
910
|
-
await
|
|
1401
|
+
await runDoctor(args);
|
|
911
1402
|
return;
|
|
912
1403
|
}
|
|
913
1404
|
const mode = inferred === "panel" ? "review" : inferred;
|
|
@@ -918,32 +1409,76 @@ async function runAsk(args) {
|
|
|
918
1409
|
if (directive.roles.length > 0 && typeof delegatedArgs.role !== "string") {
|
|
919
1410
|
delegatedArgs.role = directive.roles.join(",");
|
|
920
1411
|
}
|
|
921
|
-
if (
|
|
922
|
-
|
|
923
|
-
|
|
1412
|
+
if (inferred === "panel" || directive.panel || delegatedArgs.panel === true) {
|
|
1413
|
+
await runPanelCommand({
|
|
1414
|
+
...delegatedArgs,
|
|
1415
|
+
visibility: "always_visible"
|
|
1416
|
+
});
|
|
1417
|
+
return;
|
|
924
1418
|
}
|
|
925
1419
|
await runModeCommand(mode, delegatedArgs);
|
|
926
1420
|
}
|
|
1421
|
+
async function runDecide(args) {
|
|
1422
|
+
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
1423
|
+
const answer = typeof args.answer === "string" ? args.answer.trim() : "";
|
|
1424
|
+
if (!answer) {
|
|
1425
|
+
throw new Error("A decision answer is required. Pass --answer <value-or-text>.");
|
|
1426
|
+
}
|
|
1427
|
+
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
1428
|
+
if (!context) {
|
|
1429
|
+
throw new Error("No LongTable project workspace was found here. Run this inside a project or pass --cwd.");
|
|
1430
|
+
}
|
|
1431
|
+
const provider = args.provider === "claude" ? "claude" : args.provider === "codex" ? "codex" : undefined;
|
|
1432
|
+
const result = await answerWorkspaceQuestion({
|
|
1433
|
+
context,
|
|
1434
|
+
questionId: typeof args.question === "string" ? args.question : undefined,
|
|
1435
|
+
answer,
|
|
1436
|
+
rationale: typeof args.rationale === "string" ? args.rationale : undefined,
|
|
1437
|
+
provider
|
|
1438
|
+
});
|
|
1439
|
+
if (args.json === true) {
|
|
1440
|
+
console.log(JSON.stringify({
|
|
1441
|
+
question: result.question,
|
|
1442
|
+
decision: result.decision,
|
|
1443
|
+
files: {
|
|
1444
|
+
state: context.stateFilePath,
|
|
1445
|
+
current: context.currentFilePath
|
|
1446
|
+
}
|
|
1447
|
+
}, null, 2));
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
console.log("LongTable decision recorded");
|
|
1451
|
+
console.log(`- question: ${result.question.id}`);
|
|
1452
|
+
console.log(`- decision: ${result.decision.id}`);
|
|
1453
|
+
console.log(`- answer: ${result.decision.selectedOption ?? answer}`);
|
|
1454
|
+
console.log(`- state: ${context.stateFilePath}`);
|
|
1455
|
+
console.log(`- current: ${context.currentFilePath}`);
|
|
1456
|
+
}
|
|
927
1457
|
async function runRoles(args) {
|
|
928
1458
|
const payload = PERSONA_DEFINITIONS.map((persona) => ({
|
|
929
1459
|
key: persona.key,
|
|
930
1460
|
label: persona.label,
|
|
931
1461
|
description: persona.shortDescription,
|
|
932
1462
|
triggerMode: persona.triggerMode,
|
|
1463
|
+
defaultPanelMember: persona.defaultPanelMember,
|
|
1464
|
+
checkpointSensitivity: persona.checkpointSensitivity,
|
|
1465
|
+
supportedModes: persona.supportedModes,
|
|
933
1466
|
exampleTriggers: persona.synonyms.slice(0, 4)
|
|
934
1467
|
}));
|
|
935
1468
|
if (args.json === true) {
|
|
936
1469
|
console.log(JSON.stringify(payload, null, 2));
|
|
937
1470
|
return;
|
|
938
1471
|
}
|
|
939
|
-
console.log("
|
|
940
|
-
console.log("These are perspectives
|
|
1472
|
+
console.log("LongTable roles");
|
|
1473
|
+
console.log("These are perspectives LongTable can consult when relevant.");
|
|
941
1474
|
console.log("Inside Codex, explicit forms like `lt editor: ...` and `lt methods: ...` are stronger than plain natural language.");
|
|
942
1475
|
console.log("");
|
|
943
1476
|
for (const persona of payload) {
|
|
944
1477
|
console.log(`- ${persona.label} (${persona.key})`);
|
|
945
1478
|
console.log(` ${persona.description}`);
|
|
946
1479
|
console.log(` Trigger: ${persona.triggerMode === "auto-callable" ? "auto-callable when your language strongly implies it" : "explicit request only"}`);
|
|
1480
|
+
console.log(` Panel: ${persona.defaultPanelMember ? "default member" : "contextual member"}`);
|
|
1481
|
+
console.log(` Checkpoint sensitivity: ${persona.checkpointSensitivity}`);
|
|
947
1482
|
console.log(` Examples: ${persona.exampleTriggers.join(", ")}`);
|
|
948
1483
|
}
|
|
949
1484
|
}
|
|
@@ -951,7 +1486,7 @@ async function runStart(args) {
|
|
|
951
1486
|
const setupPath = typeof args.setup === "string" ? args.setup : undefined;
|
|
952
1487
|
const existingSetup = await loadOptionalSetup(setupPath);
|
|
953
1488
|
if (!existingSetup) {
|
|
954
|
-
throw new Error("
|
|
1489
|
+
throw new Error("LongTable global setup is missing. Run `longtable init --flow interview` first.");
|
|
955
1490
|
}
|
|
956
1491
|
const interview = await collectProjectInterview(existingSetup, args);
|
|
957
1492
|
await verifyWritableWorkspaceParent(interview.projectPath);
|
|
@@ -968,8 +1503,12 @@ async function runStart(args) {
|
|
|
968
1503
|
console.log(JSON.stringify({
|
|
969
1504
|
project: context.project,
|
|
970
1505
|
session: context.session,
|
|
971
|
-
|
|
972
|
-
|
|
1506
|
+
files: {
|
|
1507
|
+
project: context.projectFilePath,
|
|
1508
|
+
session: context.sessionFilePath,
|
|
1509
|
+
state: context.stateFilePath,
|
|
1510
|
+
current: context.currentFilePath
|
|
1511
|
+
}
|
|
973
1512
|
}, null, 2));
|
|
974
1513
|
return;
|
|
975
1514
|
}
|
|
@@ -979,7 +1518,7 @@ async function runStart(args) {
|
|
|
979
1518
|
`1. cd "${context.project.projectPath}"`,
|
|
980
1519
|
"2. run `codex` in that directory",
|
|
981
1520
|
"3. begin with your current goal in natural language",
|
|
982
|
-
"4. if you return later,
|
|
1521
|
+
"4. if you return later, open `CURRENT.md` or run `longtable resume`",
|
|
983
1522
|
"",
|
|
984
1523
|
`Suggested first message: ${context.session.currentBlocker ? `"I want to work on ${context.session.currentGoal}. My current blocker is ${context.session.currentBlocker}."` : `"I want to work on ${context.session.currentGoal}."`}`,
|
|
985
1524
|
"",
|
|
@@ -990,22 +1529,24 @@ async function runResume(args) {
|
|
|
990
1529
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
991
1530
|
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
992
1531
|
if (!context) {
|
|
993
|
-
throw new Error("No
|
|
1532
|
+
throw new Error("No LongTable project workspace was found here. Run `longtable start` first or pass --cwd.");
|
|
994
1533
|
}
|
|
1534
|
+
await syncCurrentWorkspaceView(context);
|
|
995
1535
|
const payload = {
|
|
996
1536
|
project: context.project,
|
|
997
1537
|
session: context.session,
|
|
998
1538
|
files: {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1539
|
+
current: resolve(context.project.projectPath, "CURRENT.md"),
|
|
1540
|
+
project: context.projectFilePath,
|
|
1541
|
+
session: context.sessionFilePath,
|
|
1542
|
+
state: context.stateFilePath
|
|
1002
1543
|
}
|
|
1003
1544
|
};
|
|
1004
1545
|
if (args.json === true) {
|
|
1005
1546
|
console.log(JSON.stringify(payload, null, 2));
|
|
1006
1547
|
return;
|
|
1007
1548
|
}
|
|
1008
|
-
console.log(renderSectionCard("
|
|
1549
|
+
console.log(renderSectionCard("LongTable Resume", [
|
|
1009
1550
|
`Project: ${context.project.projectName}`,
|
|
1010
1551
|
`Path: ${context.project.projectPath}`,
|
|
1011
1552
|
`Current goal: ${context.session.currentGoal}`,
|
|
@@ -1013,22 +1554,36 @@ async function runResume(args) {
|
|
|
1013
1554
|
`Requested perspectives: ${context.session.requestedPerspectives.length > 0 ? context.session.requestedPerspectives.join(", ") : "auto"}`,
|
|
1014
1555
|
`Disagreement: ${context.session.disagreementPreference}`,
|
|
1015
1556
|
"",
|
|
1016
|
-
"
|
|
1017
|
-
`- ${payload.files.
|
|
1018
|
-
`- ${payload.files.nextSteps}`,
|
|
1019
|
-
`- ${payload.files.sessionSnapshot}`,
|
|
1557
|
+
"Current file:",
|
|
1558
|
+
`- ${payload.files.current}`,
|
|
1020
1559
|
"",
|
|
1021
|
-
`Suggested restart message: ${context.session.currentBlocker ? `
|
|
1560
|
+
`Suggested restart message: "${context.session.resumeHint ?? (context.session.currentBlocker ? `I want to continue ${context.session.currentGoal}. The unresolved blocker is ${context.session.currentBlocker}.` : `I want to continue ${context.session.currentGoal}.`)}"`
|
|
1022
1561
|
]));
|
|
1023
1562
|
}
|
|
1024
1563
|
async function runCodexSubcommand(subcommand, args) {
|
|
1025
1564
|
const customDir = typeof args.dir === "string" ? args.dir : undefined;
|
|
1565
|
+
const roles = listRoleDefinitions();
|
|
1566
|
+
if (subcommand === "install-skills") {
|
|
1567
|
+
const installed = await installCodexSkills(roles, customDir);
|
|
1568
|
+
console.log(`Installed ${installed.length} LongTable Codex skills in ${resolveCodexSkillsDir(customDir)}`);
|
|
1569
|
+
console.log("Use them inside Codex with natural-language triggers such as `lt explore: ...` or `lt panel: ...`.");
|
|
1570
|
+
console.log("If you want an explicit trigger, use `$longtable` when your Codex build exposes skills that way.");
|
|
1571
|
+
for (const skill of installed) {
|
|
1572
|
+
console.log(`- ${skill.name}`);
|
|
1573
|
+
}
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
if (subcommand === "remove-skills") {
|
|
1577
|
+
const removed = await removeCodexSkills(roles, customDir);
|
|
1578
|
+
console.log(`Removed ${removed.length} LongTable Codex skills from ${resolveCodexSkillsDir(customDir)}`);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1026
1581
|
if (subcommand === "install-prompts") {
|
|
1027
1582
|
const installed = await installCodexPromptAliases(customDir);
|
|
1028
|
-
console.log(`Installed ${installed.length}
|
|
1029
|
-
console.log("Note:
|
|
1583
|
+
console.log(`Installed ${installed.length} legacy LongTable prompt files in ${resolveCodexPromptsDir(customDir)}`);
|
|
1584
|
+
console.log("Note: current Codex builds may not expose these files as slash commands. Prefer `longtable codex install-skills`.");
|
|
1030
1585
|
for (const prompt of installed) {
|
|
1031
|
-
console.log(`-
|
|
1586
|
+
console.log(`- ${prompt.name}`);
|
|
1032
1587
|
}
|
|
1033
1588
|
return;
|
|
1034
1589
|
}
|
|
@@ -1038,11 +1593,12 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
1038
1593
|
}
|
|
1039
1594
|
if (subcommand === "remove-prompts") {
|
|
1040
1595
|
const removed = await removeCodexPromptAliases(customDir);
|
|
1041
|
-
console.log(`Removed ${removed.length}
|
|
1596
|
+
console.log(`Removed ${removed.length} legacy LongTable prompt files from ${resolveCodexPromptsDir(customDir)}`);
|
|
1042
1597
|
return;
|
|
1043
1598
|
}
|
|
1044
1599
|
if (subcommand === "status") {
|
|
1045
1600
|
const aliases = await listInstalledCodexPromptAliases(customDir);
|
|
1601
|
+
const skills = await listInstalledCodexSkills(roles, customDir);
|
|
1046
1602
|
const setupPath = resolveDefaultSetupPath(typeof args.path === "string" ? args.path : undefined).path;
|
|
1047
1603
|
const runtimePath = resolveDefaultRuntimeConfigPath("codex", typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined).path;
|
|
1048
1604
|
const status = {
|
|
@@ -1050,31 +1606,93 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
1050
1606
|
setupExists: existsSync(setupPath),
|
|
1051
1607
|
runtimePath,
|
|
1052
1608
|
runtimeExists: existsSync(runtimePath),
|
|
1609
|
+
skillsDir: resolveCodexSkillsDir(customDir),
|
|
1610
|
+
skillsInstalled: skills.map((skill) => skill.name),
|
|
1053
1611
|
promptsDir: resolveCodexPromptsDir(customDir),
|
|
1054
|
-
|
|
1612
|
+
legacyPromptFilesInstalled: aliases.map((alias) => alias.name)
|
|
1055
1613
|
};
|
|
1056
1614
|
if (args.json === true) {
|
|
1057
1615
|
console.log(JSON.stringify(status, null, 2));
|
|
1058
1616
|
return;
|
|
1059
1617
|
}
|
|
1060
|
-
console.log("
|
|
1618
|
+
console.log("LongTable Codex status");
|
|
1061
1619
|
console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
|
|
1062
1620
|
console.log(`- codex runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
|
|
1621
|
+
console.log(`- skills dir: ${status.skillsDir}`);
|
|
1622
|
+
if (skills.length === 0) {
|
|
1623
|
+
console.log("- skills: none");
|
|
1624
|
+
}
|
|
1625
|
+
else {
|
|
1626
|
+
console.log("- skills:");
|
|
1627
|
+
for (const skill of skills) {
|
|
1628
|
+
console.log(` - ${skill.name}`);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1063
1631
|
console.log(`- prompt aliases dir: ${status.promptsDir}`);
|
|
1064
|
-
console.log("- prompt
|
|
1632
|
+
console.log("- prompt files: legacy; current Codex builds may not expose these as slash commands");
|
|
1065
1633
|
if (aliases.length === 0) {
|
|
1066
|
-
console.log("- prompt
|
|
1634
|
+
console.log("- legacy prompt files: none");
|
|
1067
1635
|
}
|
|
1068
1636
|
else {
|
|
1069
|
-
console.log("- prompt
|
|
1637
|
+
console.log("- legacy prompt files:");
|
|
1070
1638
|
for (const alias of aliases) {
|
|
1071
|
-
console.log(` -
|
|
1639
|
+
console.log(` - ${alias.name}`);
|
|
1072
1640
|
}
|
|
1073
1641
|
}
|
|
1074
1642
|
return;
|
|
1075
1643
|
}
|
|
1076
1644
|
throw new Error("Unknown codex subcommand.");
|
|
1077
1645
|
}
|
|
1646
|
+
async function runClaudeSubcommand(subcommand, args) {
|
|
1647
|
+
const customDir = typeof args.dir === "string" ? args.dir : undefined;
|
|
1648
|
+
const roles = listRoleDefinitions();
|
|
1649
|
+
if (subcommand === "install-skills") {
|
|
1650
|
+
const installed = await installClaudeSkills(roles, customDir);
|
|
1651
|
+
console.log(`Installed ${installed.length} LongTable Claude skills in ${resolveClaudeSkillsDir(customDir)}`);
|
|
1652
|
+
console.log("Use them inside Claude Code with natural-language triggers such as `lt explore: ...` or `lt panel: ...`.");
|
|
1653
|
+
for (const skill of installed) {
|
|
1654
|
+
console.log(`- ${skill.name}`);
|
|
1655
|
+
}
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
if (subcommand === "remove-skills") {
|
|
1659
|
+
const removed = await removeClaudeSkills(roles, customDir);
|
|
1660
|
+
console.log(`Removed ${removed.length} LongTable Claude skills from ${resolveClaudeSkillsDir(customDir)}`);
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (subcommand === "status") {
|
|
1664
|
+
const skills = await listInstalledClaudeSkills(roles, customDir);
|
|
1665
|
+
const setupPath = resolveDefaultSetupPath(typeof args.path === "string" ? args.path : undefined).path;
|
|
1666
|
+
const runtimePath = resolveDefaultRuntimeConfigPath("claude", typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined).path;
|
|
1667
|
+
const status = {
|
|
1668
|
+
setupPath,
|
|
1669
|
+
setupExists: existsSync(setupPath),
|
|
1670
|
+
runtimePath,
|
|
1671
|
+
runtimeExists: existsSync(runtimePath),
|
|
1672
|
+
skillsDir: resolveClaudeSkillsDir(customDir),
|
|
1673
|
+
skillsInstalled: skills.map((skill) => skill.name)
|
|
1674
|
+
};
|
|
1675
|
+
if (args.json === true) {
|
|
1676
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
console.log("LongTable Claude status");
|
|
1680
|
+
console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
|
|
1681
|
+
console.log(`- claude runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
|
|
1682
|
+
console.log(`- skills dir: ${status.skillsDir}`);
|
|
1683
|
+
if (skills.length === 0) {
|
|
1684
|
+
console.log("- skills: none");
|
|
1685
|
+
}
|
|
1686
|
+
else {
|
|
1687
|
+
console.log("- skills:");
|
|
1688
|
+
for (const skill of skills) {
|
|
1689
|
+
console.log(` - ${skill.name}`);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
throw new Error("Unknown claude subcommand.");
|
|
1695
|
+
}
|
|
1078
1696
|
async function main() {
|
|
1079
1697
|
const parsed = parseArgs(process.argv.slice(2));
|
|
1080
1698
|
const { command, subcommand, values } = parsed;
|
|
@@ -1094,6 +1712,10 @@ async function main() {
|
|
|
1094
1712
|
await runResume(values);
|
|
1095
1713
|
return;
|
|
1096
1714
|
}
|
|
1715
|
+
if (command === "doctor" || command === "status") {
|
|
1716
|
+
await runDoctor(values);
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1097
1719
|
if (command === "roles") {
|
|
1098
1720
|
await runRoles(values);
|
|
1099
1721
|
return;
|
|
@@ -1110,11 +1732,34 @@ async function main() {
|
|
|
1110
1732
|
await runAsk(values);
|
|
1111
1733
|
return;
|
|
1112
1734
|
}
|
|
1735
|
+
if (command === "question") {
|
|
1736
|
+
await runQuestion(values);
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
if (command === "panel") {
|
|
1740
|
+
await runPanelCommand(values);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
if (command === "decide") {
|
|
1744
|
+
await runDecide(values);
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1113
1747
|
if (command === "codex") {
|
|
1114
1748
|
await runCodexSubcommand(subcommand, values);
|
|
1115
1749
|
return;
|
|
1116
1750
|
}
|
|
1751
|
+
if (command === "claude") {
|
|
1752
|
+
await runClaudeSubcommand(subcommand, values);
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1117
1755
|
if (VALID_MODES.has(command)) {
|
|
1756
|
+
if (values.panel === true) {
|
|
1757
|
+
await runPanelCommand({
|
|
1758
|
+
...values,
|
|
1759
|
+
mode: command
|
|
1760
|
+
});
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1118
1763
|
await runModeCommand(command, values);
|
|
1119
1764
|
return;
|
|
1120
1765
|
}
|