@longtable/cli 0.1.56 → 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 +43 -1
- package/dist/project-session.d.ts +1 -0
- package/dist/project-session.js +141 -17
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -159,6 +159,7 @@ function usage() {
|
|
|
159
159
|
" longtable clarify --prompt <task-context> [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json] [--force]",
|
|
160
160
|
" longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
|
|
161
161
|
" longtable clear-question --question <id> --reason <text> [--cwd <path>] [--json]",
|
|
162
|
+
" longtable repair-state [--cwd <path>] [--dry-run] [--json]",
|
|
162
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>]",
|
|
163
164
|
" longtable panel status --run <panel_run_id> [--wait [ms]] [--cwd <path>] [--json]",
|
|
164
165
|
" longtable panel stop --run <panel_run_id> [--cwd <path>] [--json]",
|
|
@@ -200,7 +201,7 @@ function parseArgs(argv) {
|
|
|
200
201
|
const values = {};
|
|
201
202
|
let subcommand = maybeSubcommand;
|
|
202
203
|
const modeCommand = command && VALID_MODES.has(command);
|
|
203
|
-
const directCommand = command && ["init", "setup", "start", "resume", "doctor", "status", "audit", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "clear-question", "prune-questions", "panel", "handoff", "decide", "sentinel", "access", "search", "spec"].includes(command);
|
|
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);
|
|
204
205
|
let startIndex = 1;
|
|
205
206
|
if (modeCommand) {
|
|
206
207
|
subcommand = undefined;
|
|
@@ -1783,6 +1784,10 @@ function renderDoctorStatus(status) {
|
|
|
1783
1784
|
if (!status.workspace.found) {
|
|
1784
1785
|
nextActions.push("longtable start");
|
|
1785
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
|
+
}
|
|
1786
1791
|
nextActions.push(...status.hardStop.nextActions);
|
|
1787
1792
|
const firstQuestion = status.workspace.pendingQuestions?.[0];
|
|
1788
1793
|
if (firstQuestion && status.hardStop.nextActions.length === 0) {
|
|
@@ -3431,6 +3436,39 @@ async function runClearQuestion(args) {
|
|
|
3431
3436
|
console.log(`- state: ${context.stateFilePath}`);
|
|
3432
3437
|
console.log(`- current: ${context.currentFilePath}`);
|
|
3433
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
|
+
}
|
|
3434
3472
|
async function runPruneQuestions(args) {
|
|
3435
3473
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
3436
3474
|
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
@@ -4278,6 +4316,10 @@ async function main() {
|
|
|
4278
4316
|
await runClearQuestion(values);
|
|
4279
4317
|
return;
|
|
4280
4318
|
}
|
|
4319
|
+
if (command === "repair-state") {
|
|
4320
|
+
await runRepairState(values);
|
|
4321
|
+
return;
|
|
4322
|
+
}
|
|
4281
4323
|
if (command === "prune-questions") {
|
|
4282
4324
|
await runPruneQuestions(values);
|
|
4283
4325
|
return;
|
|
@@ -490,6 +490,7 @@ export declare function pruneWorkspaceQuestions(options: {
|
|
|
490
490
|
}>;
|
|
491
491
|
export declare function repairWorkspaceStateConsistency(options: {
|
|
492
492
|
context: LongTableProjectContext;
|
|
493
|
+
dryRun?: boolean;
|
|
493
494
|
}): Promise<{
|
|
494
495
|
state: ResearchState;
|
|
495
496
|
repaired: string[];
|
package/dist/project-session.js
CHANGED
|
@@ -497,6 +497,98 @@ function formatQuestionMetadata(record) {
|
|
|
497
497
|
].filter(Boolean);
|
|
498
498
|
return parts.length > 0 ? ` [${parts.join("; ")}]` : "";
|
|
499
499
|
}
|
|
500
|
+
const QUESTION_SURFACES = new Set([
|
|
501
|
+
"native_structured",
|
|
502
|
+
"mcp_elicitation",
|
|
503
|
+
"numbered",
|
|
504
|
+
"terminal_selector",
|
|
505
|
+
"web_form"
|
|
506
|
+
]);
|
|
507
|
+
function asStringArray(value) {
|
|
508
|
+
if (Array.isArray(value)) {
|
|
509
|
+
return value.filter((entry) => typeof entry === "string");
|
|
510
|
+
}
|
|
511
|
+
return typeof value === "string" ? [value] : [];
|
|
512
|
+
}
|
|
513
|
+
function legacyQuestionAnswerRecord(record) {
|
|
514
|
+
return asRecord(record.answer);
|
|
515
|
+
}
|
|
516
|
+
function labelForQuestionAnswerValue(record, value) {
|
|
517
|
+
const option = record.prompt.options.find((candidate) => candidate.value === value || candidate.label === value);
|
|
518
|
+
if (option) {
|
|
519
|
+
return option.label;
|
|
520
|
+
}
|
|
521
|
+
if (value === "other") {
|
|
522
|
+
return record.prompt.otherLabel ?? "Other";
|
|
523
|
+
}
|
|
524
|
+
return value;
|
|
525
|
+
}
|
|
526
|
+
function selectedValuesForQuestion(record) {
|
|
527
|
+
const answer = legacyQuestionAnswerRecord(record);
|
|
528
|
+
if (!answer) {
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
const directValues = asStringArray(answer.selectedValues);
|
|
532
|
+
if (directValues.length > 0) {
|
|
533
|
+
return directValues;
|
|
534
|
+
}
|
|
535
|
+
const legacyValues = [
|
|
536
|
+
...asStringArray(answer.selectedValue),
|
|
537
|
+
...asStringArray(answer.selected),
|
|
538
|
+
...asStringArray(answer.selectedOption),
|
|
539
|
+
...asStringArray(answer.answer),
|
|
540
|
+
...asStringArray(answer.value)
|
|
541
|
+
];
|
|
542
|
+
return uniqueStrings(legacyValues);
|
|
543
|
+
}
|
|
544
|
+
function selectedLabelsForQuestion(record, selectedValues) {
|
|
545
|
+
const answer = legacyQuestionAnswerRecord(record);
|
|
546
|
+
const directLabels = asStringArray(answer?.selectedLabels);
|
|
547
|
+
if (directLabels.length > 0) {
|
|
548
|
+
return directLabels;
|
|
549
|
+
}
|
|
550
|
+
return selectedValues.map((value) => labelForQuestionAnswerValue(record, value));
|
|
551
|
+
}
|
|
552
|
+
function legacyAnswerShapeWarnings(questions) {
|
|
553
|
+
return questions.flatMap((record) => {
|
|
554
|
+
if (record.status !== "answered" || !legacyQuestionAnswerRecord(record)) {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
const selectedValues = asStringArray(legacyQuestionAnswerRecord(record)?.selectedValues);
|
|
558
|
+
const selectedLabels = asStringArray(legacyQuestionAnswerRecord(record)?.selectedLabels);
|
|
559
|
+
if (selectedValues.length > 0 && selectedLabels.length > 0) {
|
|
560
|
+
return [];
|
|
561
|
+
}
|
|
562
|
+
return [{
|
|
563
|
+
questionId: record.id,
|
|
564
|
+
...(record.decisionRecordId ? { decisionRecordId: record.decisionRecordId } : {}),
|
|
565
|
+
issue: "Answered question uses a legacy answer shape that is missing selectedValues or selectedLabels.",
|
|
566
|
+
suggestion: "Run `longtable repair-state --cwd <project-path>` to normalize the answer without changing the recorded selection."
|
|
567
|
+
}];
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
function numericOtherAnswerWarnings(questions) {
|
|
571
|
+
return questions.flatMap((record) => {
|
|
572
|
+
if (record.status !== "answered" || !selectedValuesForQuestion(record).includes("other")) {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
const answer = legacyQuestionAnswerRecord(record);
|
|
576
|
+
const raw = typeof answer?.otherText === "string"
|
|
577
|
+
? answer.otherText
|
|
578
|
+
: selectedLabelsForQuestion(record, selectedValuesForQuestion(record))[0] ?? "";
|
|
579
|
+
if (!/^\d+$/.test(raw.trim())) {
|
|
580
|
+
return [];
|
|
581
|
+
}
|
|
582
|
+
const index = Number(raw.trim()) - 1;
|
|
583
|
+
const option = record.prompt.options[index];
|
|
584
|
+
return [{
|
|
585
|
+
questionId: record.id,
|
|
586
|
+
...(record.decisionRecordId ? { decisionRecordId: record.decisionRecordId } : {}),
|
|
587
|
+
issue: `Numeric answer "${raw.trim()}" was stored as other text.`,
|
|
588
|
+
...(option ? { suggestion: `Use "${option.value}" (${option.label}) for this checkpoint option.` } : {})
|
|
589
|
+
}];
|
|
590
|
+
});
|
|
591
|
+
}
|
|
500
592
|
function compactLine(value, limit = 160) {
|
|
501
593
|
const compacted = value.replace(/\s+/g, " ").trim();
|
|
502
594
|
return compacted.length > limit ? `${compacted.slice(0, limit - 1)}…` : compacted;
|
|
@@ -854,22 +946,10 @@ function summarizeWorkspaceInspection(context, state) {
|
|
|
854
946
|
...(record.selectedOption ? { selectedOption: record.selectedOption } : {}),
|
|
855
947
|
timestamp: record.timestamp
|
|
856
948
|
})),
|
|
857
|
-
answerWarnings:
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
if (!/^\d+$/.test(raw.trim())) {
|
|
862
|
-
return [];
|
|
863
|
-
}
|
|
864
|
-
const index = Number(raw.trim()) - 1;
|
|
865
|
-
const option = record.prompt.options[index];
|
|
866
|
-
return [{
|
|
867
|
-
questionId: record.id,
|
|
868
|
-
...(record.decisionRecordId ? { decisionRecordId: record.decisionRecordId } : {}),
|
|
869
|
-
issue: `Numeric answer "${raw.trim()}" was stored as other text.`,
|
|
870
|
-
...(option ? { suggestion: `Use "${option.value}" (${option.label}) for this checkpoint option.` } : {})
|
|
871
|
-
}];
|
|
872
|
-
})
|
|
949
|
+
answerWarnings: [
|
|
950
|
+
...(legacyAnswerShapeWarnings(questions) ?? []),
|
|
951
|
+
...(numericOtherAnswerWarnings(questions) ?? [])
|
|
952
|
+
]
|
|
873
953
|
};
|
|
874
954
|
}
|
|
875
955
|
function buildProjectAgentsMd(project, session) {
|
|
@@ -3242,7 +3322,51 @@ export async function repairWorkspaceStateConsistency(options) {
|
|
|
3242
3322
|
})
|
|
3243
3323
|
};
|
|
3244
3324
|
}
|
|
3245
|
-
|
|
3325
|
+
const timestamp = nowIso();
|
|
3326
|
+
const repairedQuestionLog = (updated.questionLog ?? []).map((record) => {
|
|
3327
|
+
if (record.status !== "answered") {
|
|
3328
|
+
return record;
|
|
3329
|
+
}
|
|
3330
|
+
const answer = legacyQuestionAnswerRecord(record);
|
|
3331
|
+
if (!answer) {
|
|
3332
|
+
return record;
|
|
3333
|
+
}
|
|
3334
|
+
const selectedValues = selectedValuesForQuestion(record);
|
|
3335
|
+
if (selectedValues.length === 0) {
|
|
3336
|
+
return record;
|
|
3337
|
+
}
|
|
3338
|
+
const selectedLabels = selectedLabelsForQuestion(record, selectedValues);
|
|
3339
|
+
const needsRepair = asStringArray(answer.selectedValues).length === 0 ||
|
|
3340
|
+
asStringArray(answer.selectedLabels).length === 0 ||
|
|
3341
|
+
typeof answer.promptId !== "string" ||
|
|
3342
|
+
!QUESTION_SURFACES.has(answer.surface);
|
|
3343
|
+
if (!needsRepair) {
|
|
3344
|
+
return record;
|
|
3345
|
+
}
|
|
3346
|
+
repaired.push(`normalized legacy answer shape for question ${record.id}`);
|
|
3347
|
+
const normalizedAnswer = {
|
|
3348
|
+
...answer,
|
|
3349
|
+
promptId: typeof answer.promptId === "string" ? answer.promptId : record.prompt.id,
|
|
3350
|
+
selectedValues,
|
|
3351
|
+
selectedLabels,
|
|
3352
|
+
...(typeof answer.otherText === "string" && answer.otherText.trim() ? { otherText: answer.otherText } : {}),
|
|
3353
|
+
...(typeof answer.rationale === "string" && answer.rationale.trim() ? { rationale: answer.rationale } : {}),
|
|
3354
|
+
...(answer.provider === "codex" || answer.provider === "claude" ? { provider: answer.provider } : {}),
|
|
3355
|
+
surface: QUESTION_SURFACES.has(answer.surface) ? answer.surface : "numbered"
|
|
3356
|
+
};
|
|
3357
|
+
return {
|
|
3358
|
+
...record,
|
|
3359
|
+
updatedAt: timestamp,
|
|
3360
|
+
answer: normalizedAnswer
|
|
3361
|
+
};
|
|
3362
|
+
});
|
|
3363
|
+
if (repairedQuestionLog.some((record, index) => record !== (updated.questionLog ?? [])[index])) {
|
|
3364
|
+
updated = {
|
|
3365
|
+
...updated,
|
|
3366
|
+
questionLog: repairedQuestionLog
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
if (repaired.length > 0 && !options.dryRun) {
|
|
3246
3370
|
await writeFile(options.context.stateFilePath, JSON.stringify(updated, null, 2), "utf8");
|
|
3247
3371
|
await syncCurrentWorkspaceView(options.context);
|
|
3248
3372
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.57",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Researcher-facing LongTable CLI",
|
|
6
6
|
"type": "module",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@clack/prompts": "^1.2.0",
|
|
32
|
-
"@longtable/checkpoints": "0.1.
|
|
33
|
-
"@longtable/core": "0.1.
|
|
34
|
-
"@longtable/memory": "0.1.
|
|
35
|
-
"@longtable/provider-claude": "0.1.
|
|
36
|
-
"@longtable/provider-codex": "0.1.
|
|
37
|
-
"@longtable/setup": "0.1.
|
|
32
|
+
"@longtable/checkpoints": "0.1.57",
|
|
33
|
+
"@longtable/core": "0.1.57",
|
|
34
|
+
"@longtable/memory": "0.1.57",
|
|
35
|
+
"@longtable/provider-claude": "0.1.57",
|
|
36
|
+
"@longtable/provider-codex": "0.1.57",
|
|
37
|
+
"@longtable/setup": "0.1.57"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.1",
|