@longtable/cli 0.1.55 → 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +368 -20
- package/dist/panel-runtime.d.ts +20 -0
- package/dist/panel-runtime.js +525 -0
- package/dist/panel.d.ts +4 -1
- package/dist/panel.js +34 -7
- package/dist/project-session.d.ts +36 -1
- package/dist/project-session.js +451 -18
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -18,8 +18,9 @@ import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodex
|
|
|
18
18
|
import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router.js";
|
|
19
19
|
import { PERSONA_DEFINITIONS, listRoleDefinitions } from "./personas.js";
|
|
20
20
|
import { buildPanelFallback, renderPanelSummary } from "./panel.js";
|
|
21
|
+
import { createPanelWorkerRun, launchPanelWorkerRun, panelWorkerRunPath, readPanelWorkerRun, refreshPanelWorkerRun, requestPanelWorkerStop, resumePanelWorkerRun, waitForPanelWorkerRun } from "./panel-runtime.js";
|
|
21
22
|
import { LONGTABLE_MANAGED_HOOK_EVENTS, codexHooksEnabled, enableCodexHooksFeature, getMissingManagedCodexHookEvents, getMissingManagedCodexHookTrustState, mergeCodexHookTrustState, mergeManagedCodexHooksConfig, removeCodexHookTrustState, removeManagedCodexHooks } from "./codex-hooks.js";
|
|
22
|
-
import { appendInvocationRecordToWorkspace, applyResearchSpecificationPatch, assertWorkspaceNotBlocked, answerWorkspaceQuestion, buildQuestionOpportunitySpecs, clearWorkspaceQuestion, createWorkspaceFollowUpQuestions, createWorkspaceQuestion, createOrUpdateProjectWorkspace, diffResearchSpecifications, inspectProjectWorkspace, loadWorkspaceState, loadProjectContextFromDirectory, findUnincorporatedResearchEvidence, proposeResearchSpecificationPatch, pruneWorkspaceQuestions, readResearchSpecificationHistory, repairWorkspaceStateConsistency, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
|
|
23
|
+
import { appendInvocationRecordToWorkspace, applyResearchSpecificationPatch, assertWorkspaceNotBlocked, answerWorkspaceQuestion, buildQuestionOpportunitySpecs, clearWorkspaceQuestion, createWorkspaceFollowUpQuestions, createWorkspaceQuestion, createOrUpdateProjectWorkspace, createWorkspaceHandoff, diffResearchSpecifications, inspectProjectWorkspace, loadWorkspaceState, loadProjectContextFromDirectory, findUnincorporatedResearchEvidence, proposeResearchSpecificationPatch, pruneWorkspaceQuestions, readResearchSpecificationHistory, recordPanelResultInWorkspace, repairWorkspaceStateConsistency, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
|
|
23
24
|
import { buildTeamDebate } from "./debate.js";
|
|
24
25
|
import { createPromptRenderer } from "./prompt-renderer.js";
|
|
25
26
|
const VALID_MODES = new Set([
|
|
@@ -158,7 +159,13 @@ function usage() {
|
|
|
158
159
|
" longtable clarify --prompt <task-context> [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json] [--force]",
|
|
159
160
|
" longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
|
|
160
161
|
" longtable clear-question --question <id> --reason <text> [--cwd <path>] [--json]",
|
|
161
|
-
" longtable
|
|
162
|
+
" longtable repair-state [--cwd <path>] [--dry-run] [--json]",
|
|
163
|
+
" longtable panel [--prompt <text>] [--role <role[,role]>] [--mode review|critique|draft|commit] [--visibility synthesis_only|show_on_conflict|always_visible] [--provider codex|claude] [--native-workers|--native-subagents] [--wait [ms]] [--print] [--json] [--setup <path>] [--cwd <path>]",
|
|
164
|
+
" longtable panel status --run <panel_run_id> [--wait [ms]] [--cwd <path>] [--json]",
|
|
165
|
+
" longtable panel stop --run <panel_run_id> [--cwd <path>] [--json]",
|
|
166
|
+
" longtable panel resume --run <panel_run_id> [--wait [ms]] [--cwd <path>] [--json]",
|
|
167
|
+
" longtable panel record [--invocation <id>] --result-file <json> [--surface sequential_fallback|native_subagents|native_workers] [--cwd <path>] [--json]",
|
|
168
|
+
" longtable handoff [--cwd <path>] [--output <file>] [--print] [--json]",
|
|
162
169
|
" longtable decide [--question <id>] --answer <value-or-text> [--rationale <text>] [--provider codex|claude] [--cwd <path>] [--json]",
|
|
163
170
|
" 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>]",
|
|
164
171
|
" longtable codex persist-init [--answers-json <json> | --stdin | full setup flags] [--install-skills] [--install-prompts] [--json]",
|
|
@@ -194,7 +201,7 @@ function parseArgs(argv) {
|
|
|
194
201
|
const values = {};
|
|
195
202
|
let subcommand = maybeSubcommand;
|
|
196
203
|
const modeCommand = command && VALID_MODES.has(command);
|
|
197
|
-
const directCommand = command && ["init", "setup", "start", "resume", "doctor", "status", "audit", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "clear-question", "prune-questions", "panel", "
|
|
204
|
+
const directCommand = command && ["init", "setup", "start", "resume", "doctor", "status", "audit", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "clear-question", "repair-state", "prune-questions", "panel", "handoff", "decide", "sentinel", "access", "search", "spec"].includes(command);
|
|
198
205
|
let startIndex = 1;
|
|
199
206
|
if (modeCommand) {
|
|
200
207
|
subcommand = undefined;
|
|
@@ -203,7 +210,7 @@ function parseArgs(argv) {
|
|
|
203
210
|
else if (command === "codex" || command === "claude" || command === "mcp") {
|
|
204
211
|
startIndex = 2;
|
|
205
212
|
}
|
|
206
|
-
else if ((command === "access" || command === "search" || command === "spec") && maybeSubcommand && !maybeSubcommand.startsWith("--")) {
|
|
213
|
+
else if ((command === "access" || command === "search" || command === "spec" || command === "panel") && maybeSubcommand && !maybeSubcommand.startsWith("--")) {
|
|
207
214
|
subcommand = maybeSubcommand;
|
|
208
215
|
startIndex = 2;
|
|
209
216
|
}
|
|
@@ -1777,6 +1784,10 @@ function renderDoctorStatus(status) {
|
|
|
1777
1784
|
if (!status.workspace.found) {
|
|
1778
1785
|
nextActions.push("longtable start");
|
|
1779
1786
|
}
|
|
1787
|
+
if ((status.workspace.answerWarnings ?? []).some((warning) => warning.issue.includes("legacy answer shape"))) {
|
|
1788
|
+
const root = status.workspace.rootPath ? ` --cwd "${status.workspace.rootPath}"` : "";
|
|
1789
|
+
nextActions.push(`longtable repair-state${root}`);
|
|
1790
|
+
}
|
|
1780
1791
|
nextActions.push(...status.hardStop.nextActions);
|
|
1781
1792
|
const firstQuestion = status.workspace.pendingQuestions?.[0];
|
|
1782
1793
|
if (firstQuestion && status.hardStop.nextActions.length === 0) {
|
|
@@ -2302,6 +2313,132 @@ function parsePanelMode(value) {
|
|
|
2302
2313
|
}
|
|
2303
2314
|
return "review";
|
|
2304
2315
|
}
|
|
2316
|
+
function requireRunId(args) {
|
|
2317
|
+
if (typeof args.run !== "string" || args.run.trim().length === 0) {
|
|
2318
|
+
throw new Error("A panel run id is required. Pass --run <panel_run_id>.");
|
|
2319
|
+
}
|
|
2320
|
+
return args.run.trim();
|
|
2321
|
+
}
|
|
2322
|
+
function commandAvailable(command) {
|
|
2323
|
+
try {
|
|
2324
|
+
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
2325
|
+
return true;
|
|
2326
|
+
}
|
|
2327
|
+
catch {
|
|
2328
|
+
return false;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
function parseWaitMs(value) {
|
|
2332
|
+
if (value === undefined || value === false) {
|
|
2333
|
+
return undefined;
|
|
2334
|
+
}
|
|
2335
|
+
if (value === true) {
|
|
2336
|
+
return 30_000;
|
|
2337
|
+
}
|
|
2338
|
+
const trimmed = value.trim();
|
|
2339
|
+
const multiplier = trimmed.endsWith("s") ? 1000 : 1;
|
|
2340
|
+
const numeric = Number.parseInt(trimmed.endsWith("s") ? trimmed.slice(0, -1) : trimmed, 10);
|
|
2341
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
2342
|
+
throw new Error(`Invalid --wait value: ${value}. Use milliseconds or a value like 30s.`);
|
|
2343
|
+
}
|
|
2344
|
+
return numeric * multiplier;
|
|
2345
|
+
}
|
|
2346
|
+
function panelWorkerNextCommands(context, runId) {
|
|
2347
|
+
const cwdFlag = `--cwd "${context.project.projectPath}"`;
|
|
2348
|
+
return {
|
|
2349
|
+
status: `longtable panel status ${cwdFlag} --run ${runId}`,
|
|
2350
|
+
stop: `longtable panel stop ${cwdFlag} --run ${runId}`,
|
|
2351
|
+
resume: `longtable panel resume ${cwdFlag} --run ${runId}`
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
async function recordTerminalNativeWorkerRun(context, run) {
|
|
2355
|
+
if ((run.status !== "completed" && run.status !== "blocked") || !existsSync(run.aggregateResultPath)) {
|
|
2356
|
+
return null;
|
|
2357
|
+
}
|
|
2358
|
+
const aggregate = JSON.parse(await readFile(run.aggregateResultPath, "utf8"));
|
|
2359
|
+
return recordPanelResultInWorkspace({
|
|
2360
|
+
context,
|
|
2361
|
+
result: {
|
|
2362
|
+
...aggregate,
|
|
2363
|
+
invocationId: run.invocationId,
|
|
2364
|
+
surface: "native_workers",
|
|
2365
|
+
status: aggregate.status ?? run.status
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
function summarizePanelRecordOutput(result) {
|
|
2370
|
+
if (!result) {
|
|
2371
|
+
return null;
|
|
2372
|
+
}
|
|
2373
|
+
return {
|
|
2374
|
+
invocationId: result.invocation.id,
|
|
2375
|
+
status: result.invocation.status,
|
|
2376
|
+
surface: result.invocation.surface,
|
|
2377
|
+
evidenceRecordIds: result.evidenceRecords.map((record) => record.id)
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
async function runPanelStatusCommand(args) {
|
|
2381
|
+
const context = await requireWorkspaceContext(args);
|
|
2382
|
+
const runId = requireRunId(args);
|
|
2383
|
+
const waitMs = parseWaitMs(args.wait);
|
|
2384
|
+
const initial = await refreshPanelWorkerRun(await readPanelWorkerRun(context.project.projectPath, runId));
|
|
2385
|
+
const refreshed = waitMs ? await waitForPanelWorkerRun(initial.run, waitMs) : initial.run;
|
|
2386
|
+
const recordedPanelResult = await recordTerminalNativeWorkerRun(context, refreshed);
|
|
2387
|
+
const nextCommands = panelWorkerNextCommands(context, refreshed.id);
|
|
2388
|
+
if (args.json === true) {
|
|
2389
|
+
console.log(JSON.stringify({ ...refreshed, nextCommands, recordedPanelResult: summarizePanelRecordOutput(recordedPanelResult) }, null, 2));
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2392
|
+
console.log("LongTable panel run status");
|
|
2393
|
+
console.log(`- run: ${refreshed.id}`);
|
|
2394
|
+
console.log(`- status: ${refreshed.status}`);
|
|
2395
|
+
for (const worker of refreshed.workers) {
|
|
2396
|
+
console.log(`- ${worker.label} (${worker.role}): ${worker.status}`);
|
|
2397
|
+
}
|
|
2398
|
+
console.log(`- stop: ${nextCommands.stop}`);
|
|
2399
|
+
console.log(`- resume: ${nextCommands.resume}`);
|
|
2400
|
+
if (recordedPanelResult) {
|
|
2401
|
+
console.log(`- recorded evidence: ${recordedPanelResult.evidenceRecords.length}`);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
async function runPanelStopCommand(args) {
|
|
2405
|
+
const context = await requireWorkspaceContext(args);
|
|
2406
|
+
const runId = requireRunId(args);
|
|
2407
|
+
const stopped = await requestPanelWorkerStop(await readPanelWorkerRun(context.project.projectPath, runId));
|
|
2408
|
+
const nextCommands = panelWorkerNextCommands(context, stopped.id);
|
|
2409
|
+
if (args.json === true) {
|
|
2410
|
+
console.log(JSON.stringify({ ...stopped, nextCommands }, null, 2));
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
console.log("LongTable panel run stop requested");
|
|
2414
|
+
console.log(`- run: ${stopped.id}`);
|
|
2415
|
+
console.log(`- status: ${stopped.status}`);
|
|
2416
|
+
for (const worker of stopped.workers) {
|
|
2417
|
+
console.log(`- ${worker.label} (${worker.role}): ${worker.status}`);
|
|
2418
|
+
}
|
|
2419
|
+
console.log(`- resume: ${nextCommands.resume}`);
|
|
2420
|
+
}
|
|
2421
|
+
async function runPanelResumeCommand(args) {
|
|
2422
|
+
const context = await requireWorkspaceContext(args);
|
|
2423
|
+
const runId = requireRunId(args);
|
|
2424
|
+
const waitMs = parseWaitMs(args.wait);
|
|
2425
|
+
const { run } = await refreshPanelWorkerRun(await readPanelWorkerRun(context.project.projectPath, runId));
|
|
2426
|
+
const resumed = run.status === "completed" ? run : await launchPanelWorkerRun(await resumePanelWorkerRun(run));
|
|
2427
|
+
const finalRun = waitMs ? await waitForPanelWorkerRun(resumed, waitMs) : resumed;
|
|
2428
|
+
const recordedPanelResult = await recordTerminalNativeWorkerRun(context, finalRun);
|
|
2429
|
+
const nextCommands = panelWorkerNextCommands(context, finalRun.id);
|
|
2430
|
+
if (args.json === true) {
|
|
2431
|
+
console.log(JSON.stringify({ ...finalRun, nextCommands, recordedPanelResult: summarizePanelRecordOutput(recordedPanelResult) }, null, 2));
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
console.log("LongTable panel run resume requested");
|
|
2435
|
+
console.log(`- run: ${finalRun.id}`);
|
|
2436
|
+
console.log(`- status: ${finalRun.status}`);
|
|
2437
|
+
console.log(`- status command: ${nextCommands.status}`);
|
|
2438
|
+
if (recordedPanelResult) {
|
|
2439
|
+
console.log(`- recorded evidence: ${recordedPanelResult.evidenceRecords.length}`);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2305
2442
|
async function loadOptionalSetup(path) {
|
|
2306
2443
|
try {
|
|
2307
2444
|
return await loadSetupOutput(path);
|
|
@@ -2388,7 +2525,9 @@ async function runPanelCommand(args) {
|
|
|
2388
2525
|
await assertWorkspaceNotBlocked(existingContext);
|
|
2389
2526
|
}
|
|
2390
2527
|
const projectAware = await buildProjectAwarePrompt(prompt, workingDirectory);
|
|
2391
|
-
const provider =
|
|
2528
|
+
const provider = args.provider === "codex" || args.provider === "claude"
|
|
2529
|
+
? args.provider
|
|
2530
|
+
: setup?.providerSelection.provider;
|
|
2392
2531
|
const visibility = parsePanelVisibility(typeof args.visibility === "string" ? args.visibility : undefined) ??
|
|
2393
2532
|
parsePanelVisibility(setup?.profileSeed.panelPreference) ??
|
|
2394
2533
|
"always_visible";
|
|
@@ -2398,7 +2537,9 @@ async function runPanelCommand(args) {
|
|
|
2398
2537
|
mode,
|
|
2399
2538
|
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
2400
2539
|
provider,
|
|
2401
|
-
visibility
|
|
2540
|
+
visibility,
|
|
2541
|
+
nativeSubagents: args["native-subagents"] === true,
|
|
2542
|
+
nativeWorkers: args["native-workers"] === true
|
|
2402
2543
|
});
|
|
2403
2544
|
if (projectAware.projectContextFound) {
|
|
2404
2545
|
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
@@ -2406,6 +2547,30 @@ async function runPanelCommand(args) {
|
|
|
2406
2547
|
await appendInvocationRecordToWorkspace(context, fallback.invocationRecord, [fallback.questionRecord]);
|
|
2407
2548
|
}
|
|
2408
2549
|
}
|
|
2550
|
+
if (args.print === true) {
|
|
2551
|
+
console.log(fallback.prompt);
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
const waitMs = parseWaitMs(args.wait);
|
|
2555
|
+
const nativeWorkersRequested = args["native-workers"] === true && fallback.plan.preferredSurface === "native_workers";
|
|
2556
|
+
const nativeRunContext = nativeWorkersRequested
|
|
2557
|
+
? await loadProjectContextFromDirectory(workingDirectory)
|
|
2558
|
+
: null;
|
|
2559
|
+
const nativeRun = nativeWorkersRequested && nativeRunContext
|
|
2560
|
+
? await launchPanelWorkerRun(await createPanelWorkerRun({
|
|
2561
|
+
workingDirectory: nativeRunContext.project.projectPath,
|
|
2562
|
+
fallback,
|
|
2563
|
+
initialStatus: "planned",
|
|
2564
|
+
diagnostics: [
|
|
2565
|
+
commandAvailable("tmux") ? "tmux:available" : "tmux:unavailable",
|
|
2566
|
+
commandAvailable("codex") ? "codex:available" : "codex:unavailable"
|
|
2567
|
+
]
|
|
2568
|
+
}))
|
|
2569
|
+
: null;
|
|
2570
|
+
const finalNativeRun = nativeRun && waitMs ? await waitForPanelWorkerRun(nativeRun, waitMs) : nativeRun;
|
|
2571
|
+
const recordedPanelResult = finalNativeRun && nativeRunContext
|
|
2572
|
+
? await recordTerminalNativeWorkerRun(nativeRunContext, finalNativeRun)
|
|
2573
|
+
: null;
|
|
2409
2574
|
if (args.json === true) {
|
|
2410
2575
|
console.log(JSON.stringify({
|
|
2411
2576
|
intent: fallback.intent,
|
|
@@ -2414,22 +2579,51 @@ async function runPanelCommand(args) {
|
|
|
2414
2579
|
invocationRecord: fallback.invocationRecord,
|
|
2415
2580
|
questionRecord: fallback.questionRecord,
|
|
2416
2581
|
execution: {
|
|
2417
|
-
status: "planned",
|
|
2582
|
+
status: finalNativeRun?.status ?? "planned",
|
|
2418
2583
|
stableSurface: "sequential_fallback",
|
|
2419
|
-
|
|
2584
|
+
preferredSurface: fallback.plan.preferredSurface,
|
|
2585
|
+
nativeParallel: fallback.plan.preferredSurface === "native_workers"
|
|
2586
|
+
? "longtable_native_workers"
|
|
2587
|
+
: fallback.plan.preferredSurface === "native_subagents"
|
|
2588
|
+
? "session_dependent"
|
|
2589
|
+
: "not_requested",
|
|
2420
2590
|
projectContextFound: projectAware.projectContextFound,
|
|
2421
|
-
invocationLogged: projectAware.projectContextFound
|
|
2591
|
+
invocationLogged: projectAware.projectContextFound,
|
|
2592
|
+
nativeRunCreated: Boolean(finalNativeRun),
|
|
2593
|
+
waitMs,
|
|
2594
|
+
degradedReason: nativeWorkersRequested && !finalNativeRun
|
|
2595
|
+
? "native workers require an existing LongTable workspace; sequential fallback prompt returned"
|
|
2596
|
+
: finalNativeRun?.status === "degraded"
|
|
2597
|
+
? "native workers require local tmux and codex commands; sequential fallback remains available"
|
|
2598
|
+
: undefined
|
|
2422
2599
|
},
|
|
2600
|
+
nativeRun: finalNativeRun,
|
|
2601
|
+
recordedPanelResult: summarizePanelRecordOutput(recordedPanelResult),
|
|
2423
2602
|
fallbackPrompt: fallback.prompt
|
|
2424
2603
|
}, null, 2));
|
|
2425
2604
|
return;
|
|
2426
2605
|
}
|
|
2427
|
-
if (args.print === true) {
|
|
2428
|
-
console.log(fallback.prompt);
|
|
2429
|
-
return;
|
|
2430
|
-
}
|
|
2431
2606
|
console.log(renderPanelSummary(fallback.plan));
|
|
2432
2607
|
console.log("");
|
|
2608
|
+
if (finalNativeRun) {
|
|
2609
|
+
console.log("LongTable native panel worker run created");
|
|
2610
|
+
console.log(`- run: ${finalNativeRun.id}`);
|
|
2611
|
+
console.log(`- status: ${finalNativeRun.status}`);
|
|
2612
|
+
console.log(`- state: ${panelWorkerRunPath(nativeRunContext.project.projectPath, finalNativeRun.id)}`);
|
|
2613
|
+
const nextCommands = panelWorkerNextCommands(nativeRunContext, finalNativeRun.id);
|
|
2614
|
+
console.log(`- next status: ${nextCommands.status}`);
|
|
2615
|
+
console.log(`- stop: ${nextCommands.stop}`);
|
|
2616
|
+
console.log(`- resume: ${nextCommands.resume}`);
|
|
2617
|
+
if (recordedPanelResult) {
|
|
2618
|
+
console.log(`- recorded evidence: ${recordedPanelResult.evidenceRecords.length}`);
|
|
2619
|
+
}
|
|
2620
|
+
if (finalNativeRun.status === "degraded") {
|
|
2621
|
+
console.log("- degraded: native workers are unavailable; use the sequential fallback prompt below.");
|
|
2622
|
+
console.log("");
|
|
2623
|
+
console.log(fallback.prompt);
|
|
2624
|
+
}
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2433
2627
|
const exitCode = await runCodexThinWrapper({
|
|
2434
2628
|
prompt: fallback.prompt,
|
|
2435
2629
|
mode,
|
|
@@ -2439,6 +2633,106 @@ async function runPanelCommand(args) {
|
|
|
2439
2633
|
});
|
|
2440
2634
|
exit(exitCode);
|
|
2441
2635
|
}
|
|
2636
|
+
function parseInvocationSurface(value) {
|
|
2637
|
+
if (value === undefined || value === true) {
|
|
2638
|
+
return undefined;
|
|
2639
|
+
}
|
|
2640
|
+
const allowed = [
|
|
2641
|
+
"native_parallel",
|
|
2642
|
+
"native_subagents",
|
|
2643
|
+
"native_workers",
|
|
2644
|
+
"generated_skill",
|
|
2645
|
+
"prompt_alias",
|
|
2646
|
+
"sequential_fallback",
|
|
2647
|
+
"file_backed_panel_debate",
|
|
2648
|
+
"file_backed_debate",
|
|
2649
|
+
"mcp_transport"
|
|
2650
|
+
];
|
|
2651
|
+
if (allowed.includes(value)) {
|
|
2652
|
+
return value;
|
|
2653
|
+
}
|
|
2654
|
+
throw new Error(`Invalid panel result surface: ${value}.`);
|
|
2655
|
+
}
|
|
2656
|
+
function parseInvocationStatus(value) {
|
|
2657
|
+
if (value === undefined || value === true) {
|
|
2658
|
+
return undefined;
|
|
2659
|
+
}
|
|
2660
|
+
const allowed = ["planned", "running", "completed", "blocked", "degraded", "error"];
|
|
2661
|
+
if (allowed.includes(value)) {
|
|
2662
|
+
return value;
|
|
2663
|
+
}
|
|
2664
|
+
throw new Error(`Invalid panel result status: ${value}.`);
|
|
2665
|
+
}
|
|
2666
|
+
async function readPanelResultRecordInput(args) {
|
|
2667
|
+
const resultFile = typeof args["result-file"] === "string" ? args["result-file"] : undefined;
|
|
2668
|
+
const baseDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
2669
|
+
const fromFile = resultFile
|
|
2670
|
+
? JSON.parse(await readFile(resolve(baseDirectory, resultFile), "utf8"))
|
|
2671
|
+
: {};
|
|
2672
|
+
const result = {
|
|
2673
|
+
...fromFile,
|
|
2674
|
+
...(typeof args.invocation === "string" ? { invocationId: args.invocation } : {}),
|
|
2675
|
+
...(typeof args.synthesis === "string" ? { synthesis: args.synthesis } : {}),
|
|
2676
|
+
...(typeof args["conflict-summary"] === "string" ? { conflictSummary: args["conflict-summary"] } : {}),
|
|
2677
|
+
...(typeof args["decision-prompt"] === "string" ? { decisionPrompt: args["decision-prompt"] } : {}),
|
|
2678
|
+
...(parseInvocationSurface(args.surface) ? { surface: parseInvocationSurface(args.surface) } : {}),
|
|
2679
|
+
...(parseInvocationStatus(args.status) ? { status: parseInvocationStatus(args.status) } : {})
|
|
2680
|
+
};
|
|
2681
|
+
if (!result.synthesis &&
|
|
2682
|
+
!result.conflictSummary &&
|
|
2683
|
+
!result.decisionPrompt &&
|
|
2684
|
+
(!result.memberResults || result.memberResults.length === 0)) {
|
|
2685
|
+
throw new Error("Panel result content is required. Pass --result-file with synthesis, conflictSummary, decisionPrompt, or memberResults.");
|
|
2686
|
+
}
|
|
2687
|
+
return result;
|
|
2688
|
+
}
|
|
2689
|
+
async function runPanelRecordCommand(args) {
|
|
2690
|
+
const context = await requireWorkspaceContext(args);
|
|
2691
|
+
const resultInput = await readPanelResultRecordInput(args);
|
|
2692
|
+
const result = await recordPanelResultInWorkspace({
|
|
2693
|
+
context,
|
|
2694
|
+
result: resultInput
|
|
2695
|
+
});
|
|
2696
|
+
if (args.json === true) {
|
|
2697
|
+
console.log(JSON.stringify({
|
|
2698
|
+
invocation: result.invocation,
|
|
2699
|
+
evidenceRecords: result.evidenceRecords,
|
|
2700
|
+
files: {
|
|
2701
|
+
state: context.stateFilePath,
|
|
2702
|
+
current: context.currentFilePath
|
|
2703
|
+
}
|
|
2704
|
+
}, null, 2));
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
console.log("LongTable panel result recorded");
|
|
2708
|
+
console.log(`- invocation: ${result.invocation.id}`);
|
|
2709
|
+
console.log(`- status: ${result.invocation.status}`);
|
|
2710
|
+
console.log(`- surface: ${result.invocation.surface}`);
|
|
2711
|
+
console.log(`- evidence records: ${result.evidenceRecords.length}`);
|
|
2712
|
+
console.log(`- unincorporated evidence: longtable spec unincorporated --cwd "${context.project.projectPath}"`);
|
|
2713
|
+
console.log(`- next handoff: longtable handoff --cwd "${context.project.projectPath}"`);
|
|
2714
|
+
}
|
|
2715
|
+
async function runHandoff(args) {
|
|
2716
|
+
const context = await requireWorkspaceContext(args);
|
|
2717
|
+
const result = await createWorkspaceHandoff({
|
|
2718
|
+
context,
|
|
2719
|
+
outputPath: typeof args.output === "string" ? args.output : undefined
|
|
2720
|
+
});
|
|
2721
|
+
if (args.json === true) {
|
|
2722
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
if (args.print === true) {
|
|
2726
|
+
console.log(result.content);
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
console.log("LongTable handoff created");
|
|
2730
|
+
console.log(`- file: ${result.path}`);
|
|
2731
|
+
console.log(`- latest invocation: ${result.latestInvocationId ?? "none"}`);
|
|
2732
|
+
console.log(`- unincorporated evidence: ${result.sourceEvidenceIds.length}`);
|
|
2733
|
+
console.log(`- pending decisions: ${result.pendingQuestionIds.length}`);
|
|
2734
|
+
console.log("Use this handoff directly in Codex/Claude, or paste it into OMX `$ralplan` when OMX is installed.");
|
|
2735
|
+
}
|
|
2442
2736
|
function parseLimit(value) {
|
|
2443
2737
|
if (typeof value !== "string") {
|
|
2444
2738
|
return undefined;
|
|
@@ -3142,6 +3436,39 @@ async function runClearQuestion(args) {
|
|
|
3142
3436
|
console.log(`- state: ${context.stateFilePath}`);
|
|
3143
3437
|
console.log(`- current: ${context.currentFilePath}`);
|
|
3144
3438
|
}
|
|
3439
|
+
async function runRepairState(args) {
|
|
3440
|
+
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
3441
|
+
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
3442
|
+
if (!context) {
|
|
3443
|
+
throw new Error("No LongTable project workspace was found here. Run this inside a project or pass --cwd.");
|
|
3444
|
+
}
|
|
3445
|
+
const result = await repairWorkspaceStateConsistency({
|
|
3446
|
+
context,
|
|
3447
|
+
dryRun: args["dry-run"] === true
|
|
3448
|
+
});
|
|
3449
|
+
if (args.json === true) {
|
|
3450
|
+
console.log(JSON.stringify({
|
|
3451
|
+
dryRun: args["dry-run"] === true,
|
|
3452
|
+
repaired: result.repaired,
|
|
3453
|
+
files: {
|
|
3454
|
+
state: context.stateFilePath,
|
|
3455
|
+
current: context.currentFilePath
|
|
3456
|
+
}
|
|
3457
|
+
}, null, 2));
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
console.log(args["dry-run"] === true ? "LongTable state repair preview" : "LongTable state repaired");
|
|
3461
|
+
if (result.repaired.length === 0) {
|
|
3462
|
+
console.log("- no repairs needed");
|
|
3463
|
+
}
|
|
3464
|
+
else {
|
|
3465
|
+
for (const item of result.repaired) {
|
|
3466
|
+
console.log(`- ${item}`);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
console.log(`- state: ${context.stateFilePath}`);
|
|
3470
|
+
console.log(`- current: ${context.currentFilePath}`);
|
|
3471
|
+
}
|
|
3145
3472
|
async function runPruneQuestions(args) {
|
|
3146
3473
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
3147
3474
|
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
@@ -3555,9 +3882,6 @@ async function runPanelDebateCommand(args) {
|
|
|
3555
3882
|
console.log(`- rounds: ${debate.run.roundCount}`);
|
|
3556
3883
|
console.log(`- checkpoint: ${debate.questionRecord.id}`);
|
|
3557
3884
|
}
|
|
3558
|
-
function disabledTeamCommandError() {
|
|
3559
|
-
return new Error("`longtable team` is disabled. Use `longtable panel --prompt <text>` for visible multi-role review, or `longtable ask --prompt \"lt debate: <text>\"` when debate is explicitly requested.");
|
|
3560
|
-
}
|
|
3561
3885
|
async function runDecide(args) {
|
|
3562
3886
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
3563
3887
|
const answer = typeof args.answer === "string" ? args.answer.trim() : "";
|
|
@@ -3732,7 +4056,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3732
4056
|
const installed = await installCodexSkills(roles, customDir, skillSurface);
|
|
3733
4057
|
console.log(`Installed ${installed.length} LongTable Codex skills in ${resolveCodexSkillsDir(customDir)} (${skillSurface} surface)`);
|
|
3734
4058
|
console.log("Use them inside Codex with natural-language triggers such as `lt explore: ...` or `lt panel: ...`.");
|
|
3735
|
-
console.log("Use `$longtable` as the general router; compact installs expose
|
|
4059
|
+
console.log("Use `$longtable` as the general router; compact installs expose `$longtable-panel` plus the most common role shortcuts.");
|
|
3736
4060
|
for (const skill of installed) {
|
|
3737
4061
|
console.log(`- ${skill.name}`);
|
|
3738
4062
|
}
|
|
@@ -3936,6 +4260,10 @@ async function main() {
|
|
|
3936
4260
|
await runResume(values);
|
|
3937
4261
|
return;
|
|
3938
4262
|
}
|
|
4263
|
+
if (command === "handoff") {
|
|
4264
|
+
await runHandoff(values);
|
|
4265
|
+
return;
|
|
4266
|
+
}
|
|
3939
4267
|
if (command === "doctor" || command === "status") {
|
|
3940
4268
|
await runDoctor(values);
|
|
3941
4269
|
return;
|
|
@@ -3988,11 +4316,34 @@ async function main() {
|
|
|
3988
4316
|
await runClearQuestion(values);
|
|
3989
4317
|
return;
|
|
3990
4318
|
}
|
|
4319
|
+
if (command === "repair-state") {
|
|
4320
|
+
await runRepairState(values);
|
|
4321
|
+
return;
|
|
4322
|
+
}
|
|
3991
4323
|
if (command === "prune-questions") {
|
|
3992
4324
|
await runPruneQuestions(values);
|
|
3993
4325
|
return;
|
|
3994
4326
|
}
|
|
3995
4327
|
if (command === "panel") {
|
|
4328
|
+
if (subcommand === "record") {
|
|
4329
|
+
await runPanelRecordCommand(values);
|
|
4330
|
+
return;
|
|
4331
|
+
}
|
|
4332
|
+
if (subcommand === "status") {
|
|
4333
|
+
await runPanelStatusCommand(values);
|
|
4334
|
+
return;
|
|
4335
|
+
}
|
|
4336
|
+
if (subcommand === "stop") {
|
|
4337
|
+
await runPanelStopCommand(values);
|
|
4338
|
+
return;
|
|
4339
|
+
}
|
|
4340
|
+
if (subcommand === "resume") {
|
|
4341
|
+
await runPanelResumeCommand(values);
|
|
4342
|
+
return;
|
|
4343
|
+
}
|
|
4344
|
+
if (subcommand) {
|
|
4345
|
+
throw new Error(`Unknown panel subcommand: ${subcommand}`);
|
|
4346
|
+
}
|
|
3996
4347
|
await runPanelCommand(values);
|
|
3997
4348
|
return;
|
|
3998
4349
|
}
|
|
@@ -4000,9 +4351,6 @@ async function main() {
|
|
|
4000
4351
|
await runSentinel(values);
|
|
4001
4352
|
return;
|
|
4002
4353
|
}
|
|
4003
|
-
if (command === "team") {
|
|
4004
|
-
throw disabledTeamCommandError();
|
|
4005
|
-
}
|
|
4006
4354
|
if (command === "decide") {
|
|
4007
4355
|
await runDecide(values);
|
|
4008
4356
|
return;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PanelMemberResult, PanelWorkerRun, PanelWorkerRunStatus } from "@longtable/core";
|
|
2
|
+
import type { PanelFallback } from "./panel.js";
|
|
3
|
+
export declare function panelWorkerRunDirectory(workingDirectory: string, runId: string): string;
|
|
4
|
+
export declare function panelWorkerRunPath(workingDirectory: string, runId: string): string;
|
|
5
|
+
export declare function createPanelWorkerRun(options: {
|
|
6
|
+
workingDirectory: string;
|
|
7
|
+
fallback: PanelFallback;
|
|
8
|
+
initialStatus?: PanelWorkerRunStatus;
|
|
9
|
+
diagnostics?: string[];
|
|
10
|
+
}): Promise<PanelWorkerRun>;
|
|
11
|
+
export declare function readPanelWorkerRun(workingDirectory: string, runId: string): Promise<PanelWorkerRun>;
|
|
12
|
+
export declare function writePanelWorkerRun(run: PanelWorkerRun): Promise<void>;
|
|
13
|
+
export declare function launchPanelWorkerRun(run: PanelWorkerRun): Promise<PanelWorkerRun>;
|
|
14
|
+
export declare function refreshPanelWorkerRun(run: PanelWorkerRun): Promise<{
|
|
15
|
+
run: PanelWorkerRun;
|
|
16
|
+
memberResults: PanelMemberResult[];
|
|
17
|
+
}>;
|
|
18
|
+
export declare function requestPanelWorkerStop(run: PanelWorkerRun): Promise<PanelWorkerRun>;
|
|
19
|
+
export declare function resumePanelWorkerRun(run: PanelWorkerRun): Promise<PanelWorkerRun>;
|
|
20
|
+
export declare function waitForPanelWorkerRun(run: PanelWorkerRun, timeoutMs: number): Promise<PanelWorkerRun>;
|