@longtable/cli 0.1.51 → 0.1.53
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 +41 -1
- package/dist/cli.js +115 -92
- package/dist/codex-hooks.js +2 -4
- package/dist/debate.js +21 -21
- package/dist/hard-stop.d.ts +2 -0
- package/dist/hard-stop.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/longtable-codex-native-hook.js +46 -18
- package/dist/panel.js +1 -1
- package/dist/persona-router.d.ts +1 -1
- package/dist/persona-router.js +2 -2
- package/dist/project-session.d.ts +13 -1
- package/dist/project-session.js +48 -2
- package/dist/question-obligations.js +2 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -4,7 +4,8 @@ Researcher-facing CLI for LongTable.
|
|
|
4
4
|
|
|
5
5
|
LongTable keeps scholarly project state in `.longtable/` and exposes generated
|
|
6
6
|
provider skills for Codex and Claude Code. The CLI installs setup, state,
|
|
7
|
-
checkpoint, search, and diagnostic tooling. It does not replace the
|
|
7
|
+
checkpoint, search, panel, and diagnostic tooling. It does not replace the
|
|
8
|
+
provider.
|
|
8
9
|
|
|
9
10
|
## Install
|
|
10
11
|
|
|
@@ -74,14 +75,53 @@ longtable question --prompt "<decision context>"
|
|
|
74
75
|
longtable decide --question <id> --answer <value>
|
|
75
76
|
longtable spec read --cwd "<project-path>"
|
|
76
77
|
longtable search --query "<topic>"
|
|
78
|
+
longtable panel --prompt "review this measurement plan" --json
|
|
77
79
|
```
|
|
78
80
|
|
|
79
81
|
`longtable start` remains available for scripted workspace creation with
|
|
80
82
|
`--no-interview --json`, but it is not the primary research-start surface.
|
|
81
83
|
|
|
84
|
+
## Panel Orchestration
|
|
85
|
+
|
|
86
|
+
Panel orchestration is for moments where disagreement matters: methods risk,
|
|
87
|
+
measurement validity, theory fit, literature positioning, and claims that need
|
|
88
|
+
challenge before they become project memory.
|
|
89
|
+
|
|
90
|
+
The CLI creates a provider-neutral `PanelPlan` and returns a planned
|
|
91
|
+
`PanelResult`. When native subagents are unavailable, LongTable uses a stable
|
|
92
|
+
sequential fallback prompt. That keeps the same research semantics available in
|
|
93
|
+
Codex and Claude Code without making either provider's native question or agent
|
|
94
|
+
tool the source of truth.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
longtable panel --prompt "Review this measurement plan." --role editor,measurement_auditor --json
|
|
98
|
+
longtable panel --visibility always_visible --prompt "Keep unresolved disagreement visible." --json
|
|
99
|
+
longtable ask --prompt "lt debate: Review this design before I commit it." --json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Team-style requests route through panel. Explicit debate-language requests write
|
|
103
|
+
panel debate records under `.longtable/panel/`; LongTable team execution is
|
|
104
|
+
disabled for new work.
|
|
105
|
+
|
|
82
106
|
## Development
|
|
83
107
|
|
|
84
108
|
```bash
|
|
85
109
|
npm run build --workspace @longtable/cli
|
|
86
110
|
npm run typecheck --workspace @longtable/cli
|
|
87
111
|
```
|
|
112
|
+
|
|
113
|
+
## Codex hard-stop diagnostics
|
|
114
|
+
|
|
115
|
+
Codex `Stop` blocks only active LongTable hard-stop blockers: unresolved
|
|
116
|
+
Research Specification question, scope, construct, method, evidence, or protected
|
|
117
|
+
decision commitments. Use:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
longtable codex hook-doctor --json
|
|
121
|
+
longtable codex status --json
|
|
122
|
+
longtable doctor --json
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
to inspect hook coverage/trust plus `stopWouldBlock`, `activeBlockers`, stale
|
|
126
|
+
pending-question counts, and next actions. Tmux remains an optional terminal
|
|
127
|
+
transport; LongTable state and hooks own the behavior.
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { stdin as input, stdout as output, cwd, env, exit } from "node:process";
|
|
|
8
8
|
import { dirname, join, resolve } from "node:path";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { collectHardStopBlockers } from "@longtable/core";
|
|
11
12
|
import { classifyCheckpointTrigger } from "@longtable/checkpoints";
|
|
12
13
|
import { assessSearchSourceCapabilities, buildResearchSearchIntent, parsePublisherTarget, probePublisherAccess, publisherConfigs, runResearchSearch, SEARCH_SOURCES, summarizeConfiguredPublisherAccess } from "./search/index.js";
|
|
13
14
|
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupOutput, saveSetupAndRuntimeConfig, serializeSetupOutput, writeRuntimeConfig } from "@longtable/setup";
|
|
@@ -19,7 +20,7 @@ import { PERSONA_DEFINITIONS, listRoleDefinitions } from "./personas.js";
|
|
|
19
20
|
import { buildPanelFallback, renderPanelSummary } from "./panel.js";
|
|
20
21
|
import { LONGTABLE_MANAGED_HOOK_EVENTS, codexHooksEnabled, enableCodexHooksFeature, getMissingManagedCodexHookEvents, getMissingManagedCodexHookTrustState, mergeCodexHookTrustState, mergeManagedCodexHooksConfig, removeCodexHookTrustState, removeManagedCodexHooks } from "./codex-hooks.js";
|
|
21
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";
|
|
22
|
-
import { buildTeamDebate
|
|
23
|
+
import { buildTeamDebate } from "./debate.js";
|
|
23
24
|
import { createPromptRenderer } from "./prompt-renderer.js";
|
|
24
25
|
const VALID_MODES = new Set([
|
|
25
26
|
"explore",
|
|
@@ -153,7 +154,6 @@ function usage() {
|
|
|
153
154
|
" longtable access probe --doi <doi> [--publisher auto|elsevier|springer_nature|wiley|taylor_francis] [--json]",
|
|
154
155
|
" longtable search --query <text> [--intent literature|theory|measurement|citation|metadata|venue] [--field <text>] [--source all|crossref,arxiv,openalex,semantic_scholar,pubmed,eric,doaj,unpaywall] [--must <term[,term]>] [--exclude <term[,term]>] [--limit <n>] [--allow-partial] [--publisher-access] [--record] [--cwd <path>] [--json]",
|
|
155
156
|
" longtable sentinel --prompt <text> [--cwd <path>] [--json] [--record]",
|
|
156
|
-
" longtable team --prompt <text> [--role <role[,role]>] [--debate] [--rounds 3|5] [--cwd <path>] [--json]",
|
|
157
157
|
" longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
|
|
158
158
|
" longtable clarify --prompt <task-context> [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json] [--force]",
|
|
159
159
|
" longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
|
|
@@ -169,6 +169,7 @@ function usage() {
|
|
|
169
169
|
" longtable codex install-hooks [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
170
170
|
" longtable codex remove-hooks [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
171
171
|
" longtable codex status [--surface compact|full] [--dir <path>] [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
172
|
+
" longtable codex hook-doctor [--cwd <path>] [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
172
173
|
" longtable claude install-skills [--surface compact|full] [--dir <path>]",
|
|
173
174
|
" longtable claude remove-skills [--dir <path>]",
|
|
174
175
|
" longtable claude status [--surface compact|full] [--dir <path>] [--json]",
|
|
@@ -1527,6 +1528,22 @@ function setupForProvider(setup, provider) {
|
|
|
1527
1528
|
}
|
|
1528
1529
|
};
|
|
1529
1530
|
}
|
|
1531
|
+
async function collectHardStopDiagnostics(startPath) {
|
|
1532
|
+
const empty = {
|
|
1533
|
+
stopWouldBlock: false,
|
|
1534
|
+
activeBlockers: [],
|
|
1535
|
+
staleOrUnrelatedPendingQuestionCount: 0,
|
|
1536
|
+
stalePendingQuestionCount: 0,
|
|
1537
|
+
stalePendingObligationCount: 0,
|
|
1538
|
+
nextActions: []
|
|
1539
|
+
};
|
|
1540
|
+
const context = await loadProjectContextFromDirectory(startPath);
|
|
1541
|
+
if (!context) {
|
|
1542
|
+
return empty;
|
|
1543
|
+
}
|
|
1544
|
+
const state = await loadWorkspaceState(context);
|
|
1545
|
+
return collectHardStopBlockers(state);
|
|
1546
|
+
}
|
|
1530
1547
|
async function collectDoctorStatus(args) {
|
|
1531
1548
|
const roles = listRoleDefinitions();
|
|
1532
1549
|
const skillSurface = parseSkillSurface(args);
|
|
@@ -1579,11 +1596,13 @@ async function collectDoctorStatus(args) {
|
|
|
1579
1596
|
: [];
|
|
1580
1597
|
const expectedCodexSkills = buildCodexSkillSpecs(roles, skillSurface).map((skill) => skill.name);
|
|
1581
1598
|
const expectedClaudeSkills = buildClaudeSkillSpecs(roles, skillSurface).map((skill) => skill.name);
|
|
1582
|
-
const
|
|
1599
|
+
const workspacePath = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
1600
|
+
const [codexSkills, claudeSkills, codexAliases, workspace, hardStop] = await Promise.all([
|
|
1583
1601
|
listInstalledCodexSkills(roles, codexDir, skillSurface),
|
|
1584
1602
|
listInstalledClaudeSkills(roles, claudeDir, skillSurface),
|
|
1585
1603
|
listInstalledCodexPromptAliases(codexPromptsDir),
|
|
1586
|
-
inspectProjectWorkspace(
|
|
1604
|
+
inspectProjectWorkspace(workspacePath),
|
|
1605
|
+
collectHardStopDiagnostics(workspacePath)
|
|
1587
1606
|
]);
|
|
1588
1607
|
const installedCodexSkills = codexSkills.map((skill) => skill.name);
|
|
1589
1608
|
const installedClaudeSkills = claudeSkills.map((skill) => skill.name);
|
|
@@ -1614,7 +1633,12 @@ async function collectDoctorStatus(args) {
|
|
|
1614
1633
|
hooksExists: existsSync(codexHooksPath),
|
|
1615
1634
|
codexHooksEnabled: codexHooksEnabled(codexMcpConfig),
|
|
1616
1635
|
missingManagedHookEvents,
|
|
1617
|
-
missingManagedHookTrustState
|
|
1636
|
+
missingManagedHookTrustState,
|
|
1637
|
+
stopWouldBlock: hardStop.stopWouldBlock,
|
|
1638
|
+
activeBlockers: hardStop.activeBlockers,
|
|
1639
|
+
stalePendingQuestionCount: hardStop.stalePendingQuestionCount,
|
|
1640
|
+
stalePendingObligationCount: hardStop.stalePendingObligationCount,
|
|
1641
|
+
nextActions: hardStop.nextActions
|
|
1618
1642
|
},
|
|
1619
1643
|
claude: {
|
|
1620
1644
|
command: "claude",
|
|
@@ -1627,7 +1651,8 @@ async function collectDoctorStatus(args) {
|
|
|
1627
1651
|
missingSkills: missingNames(expectedClaudeSkills, installedClaudeSkills)
|
|
1628
1652
|
}
|
|
1629
1653
|
},
|
|
1630
|
-
workspace
|
|
1654
|
+
workspace,
|
|
1655
|
+
hardStop
|
|
1631
1656
|
};
|
|
1632
1657
|
}
|
|
1633
1658
|
function renderProviderDoctorBlock(label, provider) {
|
|
@@ -1665,6 +1690,9 @@ function renderDoctorStatus(status) {
|
|
|
1665
1690
|
`- hooks feature: ${status.providers.codex.codexHooksEnabled ? "enabled" : "missing"}`,
|
|
1666
1691
|
`- managed hook coverage: ${status.providers.codex.missingManagedHookEvents.length === 0 ? "complete" : `missing ${status.providers.codex.missingManagedHookEvents.join(", ")}`}`,
|
|
1667
1692
|
`- managed hook trust: ${status.providers.codex.missingManagedHookTrustState.length === 0 ? "current" : `missing/stale ${status.providers.codex.missingManagedHookTrustState.length}`}`,
|
|
1693
|
+
`- Stop would block now: ${status.hardStop.stopWouldBlock ? "yes" : "no"}`,
|
|
1694
|
+
`- active hard-stop blockers: ${status.hardStop.activeBlockers.length}`,
|
|
1695
|
+
`- stale/unrelated pending questions: ${status.hardStop.staleOrUnrelatedPendingQuestionCount}`,
|
|
1668
1696
|
"",
|
|
1669
1697
|
...renderProviderDoctorBlock("Claude", status.providers.claude),
|
|
1670
1698
|
"",
|
|
@@ -1675,7 +1703,7 @@ function renderDoctorStatus(status) {
|
|
|
1675
1703
|
}
|
|
1676
1704
|
else {
|
|
1677
1705
|
const workspace = status.workspace;
|
|
1678
|
-
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)`, `- obligations: ${workspace.counts?.pendingObligations ?? 0} pending`, `- decisions: ${workspace.counts?.decisions ?? 0}`);
|
|
1706
|
+
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)`, `- obligations: ${workspace.counts?.pendingObligations ?? 0} pending`, `- Stop hard-stop: ${workspace.hardStop?.stopWouldBlock ? "would block" : "clear"}`, `- stale/unrelated pending: ${workspace.hardStop?.stalePendingQuestionCount ?? 0} questions, ${workspace.hardStop?.stalePendingObligationCount ?? 0} obligations`, `- decisions: ${workspace.counts?.decisions ?? 0}`);
|
|
1679
1707
|
if ((workspace.recentInvocations ?? []).length > 0) {
|
|
1680
1708
|
lines.push("- recent invocations:");
|
|
1681
1709
|
for (const invocation of workspace.recentInvocations ?? []) {
|
|
@@ -1689,12 +1717,25 @@ function renderDoctorStatus(status) {
|
|
|
1689
1717
|
lines.push(` - ${question.id}: ${question.question} (${question.options.join("/")})`);
|
|
1690
1718
|
}
|
|
1691
1719
|
}
|
|
1720
|
+
if (status.hardStop.activeBlockers.length > 0) {
|
|
1721
|
+
lines.push("- active hard-stop blockers:");
|
|
1722
|
+
for (const blocker of status.hardStop.activeBlockers) {
|
|
1723
|
+
lines.push(` - ${blocker.id}: ${blocker.scope} (${blocker.type})`);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1692
1726
|
if ((workspace.pendingObligations ?? []).length > 0) {
|
|
1693
1727
|
lines.push("- pending obligations:");
|
|
1694
1728
|
for (const obligation of workspace.pendingObligations ?? []) {
|
|
1695
1729
|
lines.push(` - ${obligation.id}: ${obligation.prompt}`);
|
|
1696
1730
|
}
|
|
1697
1731
|
}
|
|
1732
|
+
if ((workspace.hardStop?.activeBlockers ?? []).length > 0) {
|
|
1733
|
+
lines.push("- hard-stop blockers:");
|
|
1734
|
+
for (const blocker of workspace.hardStop?.activeBlockers ?? []) {
|
|
1735
|
+
lines.push(` - ${blocker.id} [${blocker.scope}]: ${blocker.prompt}`);
|
|
1736
|
+
lines.push(` next: ${blocker.commandHint}`);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1698
1739
|
if ((workspace.answerWarnings ?? []).length > 0) {
|
|
1699
1740
|
lines.push("- answer warnings:");
|
|
1700
1741
|
for (const warning of workspace.answerWarnings ?? []) {
|
|
@@ -1736,10 +1777,16 @@ function renderDoctorStatus(status) {
|
|
|
1736
1777
|
if (!status.workspace.found) {
|
|
1737
1778
|
nextActions.push("longtable start");
|
|
1738
1779
|
}
|
|
1780
|
+
nextActions.push(...status.hardStop.nextActions);
|
|
1739
1781
|
const firstQuestion = status.workspace.pendingQuestions?.[0];
|
|
1740
|
-
if (firstQuestion) {
|
|
1782
|
+
if (firstQuestion && status.hardStop.nextActions.length === 0) {
|
|
1741
1783
|
nextActions.push(`longtable decide --question ${firstQuestion.id} --answer <value>`);
|
|
1742
1784
|
}
|
|
1785
|
+
for (const action of status.workspace.hardStop?.nextActions ?? []) {
|
|
1786
|
+
if (!nextActions.includes(action)) {
|
|
1787
|
+
nextActions.push(action);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1743
1790
|
if (nextActions.length > 0) {
|
|
1744
1791
|
lines.push("", "Next actions:");
|
|
1745
1792
|
for (const action of nextActions) {
|
|
@@ -2216,10 +2263,10 @@ function inferCollaborationRoute(prompt) {
|
|
|
2216
2263
|
if (explicitDebate) {
|
|
2217
2264
|
return "debate";
|
|
2218
2265
|
}
|
|
2219
|
-
const
|
|
2266
|
+
const explicitPanelTeam = /\bagent team\b|\bresearch team\b|\bteam review\b|\bteam-style\b|\buse a team\b/i.test(prompt) ||
|
|
2220
2267
|
/에이전트\s*팀|연구\s*팀|팀\s*(리뷰|검토)|팀으로/.test(prompt);
|
|
2221
|
-
if (
|
|
2222
|
-
return "
|
|
2268
|
+
if (explicitPanelTeam) {
|
|
2269
|
+
return "panel";
|
|
2223
2270
|
}
|
|
2224
2271
|
const panelCue = /\bpanel\b|\bmulti[- ]?role\b|\bmultiple perspectives\b|\brole disagreement\b|\bdisagreement\b|\bconflict\b/i.test(prompt) ||
|
|
2225
2272
|
/패널|여러\s*관점|복수\s*관점|역할.*불일치|불일치|충돌/.test(prompt);
|
|
@@ -2234,10 +2281,10 @@ function inferCollaborationRoute(prompt) {
|
|
|
2234
2281
|
trigger.signal.artifactStakes === "study_protocol" ||
|
|
2235
2282
|
trigger.requiresQuestionBeforeClosure;
|
|
2236
2283
|
if (panelCue && trigger.signal.artifactStakes === "external_submission") {
|
|
2237
|
-
return "
|
|
2284
|
+
return "panel";
|
|
2238
2285
|
}
|
|
2239
2286
|
if (highStakes) {
|
|
2240
|
-
return "
|
|
2287
|
+
return "panel";
|
|
2241
2288
|
}
|
|
2242
2289
|
return "panel";
|
|
2243
2290
|
}
|
|
@@ -2813,7 +2860,7 @@ async function runAccess(subcommand, args) {
|
|
|
2813
2860
|
await runAccessSetup(args);
|
|
2814
2861
|
return;
|
|
2815
2862
|
}
|
|
2816
|
-
if (subcommand === "status") {
|
|
2863
|
+
if (subcommand === "status" || subcommand === "hook-doctor") {
|
|
2817
2864
|
await runAccessStatus(args);
|
|
2818
2865
|
return;
|
|
2819
2866
|
}
|
|
@@ -3338,10 +3385,10 @@ async function runAsk(args) {
|
|
|
3338
3385
|
(directive.panel || delegatedArgs.panel === true
|
|
3339
3386
|
? "panel"
|
|
3340
3387
|
: inferCollaborationRoute(effectivePrompt) ?? (inferred === "panel" ? "panel" : null));
|
|
3341
|
-
if (collaborationRoute === "
|
|
3342
|
-
await
|
|
3388
|
+
if (collaborationRoute === "debate") {
|
|
3389
|
+
await runPanelDebateCommand({
|
|
3343
3390
|
...delegatedArgs,
|
|
3344
|
-
debate:
|
|
3391
|
+
debate: true
|
|
3345
3392
|
});
|
|
3346
3393
|
return;
|
|
3347
3394
|
}
|
|
@@ -3360,21 +3407,21 @@ function localId(prefix) {
|
|
|
3360
3407
|
async function writeJsonFile(path, value) {
|
|
3361
3408
|
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
3362
3409
|
}
|
|
3363
|
-
async function
|
|
3364
|
-
await mkdir(
|
|
3365
|
-
await writeFile(join(
|
|
3366
|
-
await writeJsonFile(join(
|
|
3367
|
-
await writeJsonFile(join(
|
|
3410
|
+
async function writePanelDebateArtifacts(bundle, panelDir, prompt) {
|
|
3411
|
+
await mkdir(panelDir, { recursive: true });
|
|
3412
|
+
await writeFile(join(panelDir, "prompt.txt"), prompt, "utf8");
|
|
3413
|
+
await writeJsonFile(join(panelDir, "plan.json"), bundle.plan);
|
|
3414
|
+
await writeJsonFile(join(panelDir, "run.json"), bundle.run);
|
|
3368
3415
|
for (const round of bundle.run.rounds) {
|
|
3369
3416
|
await mkdir(round.artifactDir, { recursive: true });
|
|
3370
3417
|
await writeJsonFile(join(round.artifactDir, "round.json"), round);
|
|
3371
3418
|
for (const contribution of round.contributions) {
|
|
3372
|
-
await writeJsonFile(join(
|
|
3419
|
+
await writeJsonFile(join(panelDir, contribution.artifactPath), contribution);
|
|
3373
3420
|
}
|
|
3374
3421
|
}
|
|
3375
|
-
await writeJsonFile(join(
|
|
3376
|
-
await writeJsonFile(join(
|
|
3377
|
-
await writeJsonFile(join(
|
|
3422
|
+
await writeJsonFile(join(panelDir, "synthesis.json"), bundle.run.synthesis);
|
|
3423
|
+
await writeJsonFile(join(panelDir, "checkpoint.json"), bundle.questionRecord);
|
|
3424
|
+
await writeJsonFile(join(panelDir, "invocation.json"), bundle.invocationRecord);
|
|
3378
3425
|
}
|
|
3379
3426
|
function sentinelSummary(prompt, workingDirectory) {
|
|
3380
3427
|
const trigger = classifyCheckpointTrigger(prompt, {
|
|
@@ -3453,19 +3500,15 @@ async function runSentinel(args) {
|
|
|
3453
3500
|
console.log(context ? `- recorded in: ${context.stateFilePath}` : "- record skipped: no LongTable workspace found");
|
|
3454
3501
|
}
|
|
3455
3502
|
}
|
|
3456
|
-
async function
|
|
3503
|
+
async function runPanelDebateCommand(args) {
|
|
3457
3504
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
3458
3505
|
const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
|
|
3459
3506
|
if (!prompt) {
|
|
3460
3507
|
throw new Error("A prompt is required.");
|
|
3461
3508
|
}
|
|
3462
|
-
const
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
if (!Number.isInteger(rounds) || rounds !== expectedRounds) {
|
|
3466
|
-
throw new Error(isDebate
|
|
3467
|
-
? "LongTable team debate v1 supports `--rounds 5` only."
|
|
3468
|
-
: "LongTable team v1 supports `--rounds 3` cross-review only.");
|
|
3509
|
+
const rounds = typeof args.rounds === "string" ? Number(args.rounds) : 5;
|
|
3510
|
+
if (!Number.isInteger(rounds) || rounds !== 5) {
|
|
3511
|
+
throw new Error("LongTable panel debate v1 supports `--rounds 5` only.");
|
|
3469
3512
|
}
|
|
3470
3513
|
const setup = await loadOptionalSetup(typeof args.setup === "string" ? args.setup : undefined);
|
|
3471
3514
|
const projectContext = await loadProjectContextFromDirectory(workingDirectory);
|
|
@@ -3473,78 +3516,47 @@ async function runTeam(args) {
|
|
|
3473
3516
|
await assertWorkspaceNotBlocked(projectContext);
|
|
3474
3517
|
}
|
|
3475
3518
|
const projectAware = await buildProjectAwarePrompt(prompt, workingDirectory);
|
|
3476
|
-
const
|
|
3477
|
-
const
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
teamDir,
|
|
3482
|
-
prompt: projectAware.prompt,
|
|
3483
|
-
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
3484
|
-
provider: setup?.providerSelection.provider,
|
|
3485
|
-
visibility: "always_visible",
|
|
3486
|
-
roundCount: rounds
|
|
3487
|
-
});
|
|
3488
|
-
await writeTeamDebateArtifacts(debate, teamDir, prompt);
|
|
3489
|
-
const canRecordWorkspace = projectAware.projectContextFound && projectContext && existsSync(projectContext.stateFilePath);
|
|
3490
|
-
if (canRecordWorkspace) {
|
|
3491
|
-
await appendInvocationRecordToWorkspace(projectContext, debate.invocationRecord, [debate.questionRecord]);
|
|
3492
|
-
}
|
|
3493
|
-
if (args.json === true) {
|
|
3494
|
-
console.log(JSON.stringify({
|
|
3495
|
-
teamId,
|
|
3496
|
-
teamDir,
|
|
3497
|
-
plan: debate.plan,
|
|
3498
|
-
run: debate.run,
|
|
3499
|
-
questionRecord: debate.questionRecord,
|
|
3500
|
-
invocationRecord: debate.invocationRecord,
|
|
3501
|
-
execution: {
|
|
3502
|
-
status: "completed",
|
|
3503
|
-
surface: debate.run.surface,
|
|
3504
|
-
projectContextFound: projectAware.projectContextFound,
|
|
3505
|
-
invocationLogged: canRecordWorkspace
|
|
3506
|
-
}
|
|
3507
|
-
}, null, 2));
|
|
3508
|
-
return;
|
|
3509
|
-
}
|
|
3510
|
-
console.log(renderTeamDebateSummary(debate.run));
|
|
3511
|
-
console.log(`- checkpoint: ${debate.questionRecord.id}`);
|
|
3512
|
-
return;
|
|
3513
|
-
}
|
|
3514
|
-
const team = buildTeamReview({
|
|
3515
|
-
teamId,
|
|
3516
|
-
teamDir,
|
|
3519
|
+
const panelId = localId("panel_debate");
|
|
3520
|
+
const panelDir = join(workingDirectory, ".longtable", "panel", panelId);
|
|
3521
|
+
const debate = buildTeamDebate({
|
|
3522
|
+
teamId: panelId,
|
|
3523
|
+
teamDir: panelDir,
|
|
3517
3524
|
prompt: projectAware.prompt,
|
|
3518
3525
|
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
3519
3526
|
provider: setup?.providerSelection.provider,
|
|
3520
3527
|
visibility: "always_visible",
|
|
3521
3528
|
roundCount: rounds
|
|
3522
3529
|
});
|
|
3523
|
-
await
|
|
3530
|
+
await writePanelDebateArtifacts(debate, panelDir, prompt);
|
|
3524
3531
|
const canRecordWorkspace = projectAware.projectContextFound && projectContext && existsSync(projectContext.stateFilePath);
|
|
3525
3532
|
if (canRecordWorkspace) {
|
|
3526
|
-
await appendInvocationRecordToWorkspace(projectContext,
|
|
3533
|
+
await appendInvocationRecordToWorkspace(projectContext, debate.invocationRecord, [debate.questionRecord]);
|
|
3527
3534
|
}
|
|
3528
3535
|
if (args.json === true) {
|
|
3529
3536
|
console.log(JSON.stringify({
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
plan:
|
|
3533
|
-
run:
|
|
3534
|
-
questionRecord:
|
|
3535
|
-
invocationRecord:
|
|
3537
|
+
panelId,
|
|
3538
|
+
panelDir,
|
|
3539
|
+
plan: debate.plan,
|
|
3540
|
+
run: debate.run,
|
|
3541
|
+
questionRecord: debate.questionRecord,
|
|
3542
|
+
invocationRecord: debate.invocationRecord,
|
|
3536
3543
|
execution: {
|
|
3537
3544
|
status: "completed",
|
|
3538
|
-
surface:
|
|
3539
|
-
interactionDepth: team.run.interactionDepth,
|
|
3545
|
+
surface: debate.run.surface,
|
|
3540
3546
|
projectContextFound: projectAware.projectContextFound,
|
|
3541
3547
|
invocationLogged: canRecordWorkspace
|
|
3542
3548
|
}
|
|
3543
3549
|
}, null, 2));
|
|
3544
3550
|
return;
|
|
3545
3551
|
}
|
|
3546
|
-
console.log(
|
|
3547
|
-
console.log(`-
|
|
3552
|
+
console.log("LongTable Panel Debate");
|
|
3553
|
+
console.log(`- panel: ${panelId}`);
|
|
3554
|
+
console.log(`- interaction depth: ${debate.run.interactionDepth}`);
|
|
3555
|
+
console.log(`- rounds: ${debate.run.roundCount}`);
|
|
3556
|
+
console.log(`- checkpoint: ${debate.questionRecord.id}`);
|
|
3557
|
+
}
|
|
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.");
|
|
3548
3560
|
}
|
|
3549
3561
|
async function runDecide(args) {
|
|
3550
3562
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
@@ -3768,7 +3780,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3768
3780
|
console.log(renderCodexHookInstallSummary(result));
|
|
3769
3781
|
return;
|
|
3770
3782
|
}
|
|
3771
|
-
if (subcommand === "status") {
|
|
3783
|
+
if (subcommand === "status" || subcommand === "hook-doctor") {
|
|
3772
3784
|
const aliases = await listInstalledCodexPromptAliases(customDir);
|
|
3773
3785
|
const skills = await listInstalledCodexSkills(roles, customDir, skillSurface);
|
|
3774
3786
|
const setupPath = resolveDefaultSetupPath(typeof args.path === "string" ? args.path : undefined).path;
|
|
@@ -3777,6 +3789,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3777
3789
|
const configContent = existsSync(configPath) ? await readFile(configPath, "utf8") : "";
|
|
3778
3790
|
const hooksPath = resolveCodexHooksPath(args);
|
|
3779
3791
|
const hooksContent = existsSync(hooksPath) ? await readFile(hooksPath, "utf8") : "";
|
|
3792
|
+
const workspace = await inspectProjectWorkspace(typeof args.cwd === "string" ? args.cwd : cwd());
|
|
3780
3793
|
const status = {
|
|
3781
3794
|
setupPath,
|
|
3782
3795
|
setupExists: existsSync(setupPath),
|
|
@@ -3796,13 +3809,21 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3796
3809
|
: [...LONGTABLE_MANAGED_HOOK_EVENTS],
|
|
3797
3810
|
missingManagedHookTrustState: hooksContent
|
|
3798
3811
|
? getMissingManagedCodexHookTrustState(configContent, hooksPath, hooksContent)
|
|
3799
|
-
: []
|
|
3812
|
+
: [],
|
|
3813
|
+
workspaceHardStop: workspace.hardStop ?? {
|
|
3814
|
+
stopWouldBlock: false,
|
|
3815
|
+
activeBlockers: [],
|
|
3816
|
+
staleOrUnrelatedPendingQuestionCount: 0,
|
|
3817
|
+
stalePendingQuestionCount: 0,
|
|
3818
|
+
stalePendingObligationCount: 0,
|
|
3819
|
+
nextActions: []
|
|
3820
|
+
}
|
|
3800
3821
|
};
|
|
3801
3822
|
if (args.json === true) {
|
|
3802
3823
|
console.log(JSON.stringify(status, null, 2));
|
|
3803
3824
|
return;
|
|
3804
3825
|
}
|
|
3805
|
-
console.log("LongTable Codex status");
|
|
3826
|
+
console.log(subcommand === "hook-doctor" ? "LongTable Codex hook doctor" : "LongTable Codex status");
|
|
3806
3827
|
console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
|
|
3807
3828
|
console.log(`- codex runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
|
|
3808
3829
|
console.log(`- skills dir: ${status.skillsDir}`);
|
|
@@ -3832,6 +3853,9 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3832
3853
|
console.log(`- hooks file: ${status.hooksExists ? "present" : "missing"} (${status.hooksPath})`);
|
|
3833
3854
|
console.log(`- managed hook coverage: ${status.missingManagedHookEvents.length === 0 ? "complete" : `missing ${status.missingManagedHookEvents.join(", ")}`}`);
|
|
3834
3855
|
console.log(`- managed hook trust: ${status.missingManagedHookTrustState.length === 0 ? "current" : `missing/stale ${status.missingManagedHookTrustState.length}`}`);
|
|
3856
|
+
console.log(`- Stop hard-stop: ${status.workspaceHardStop.stopWouldBlock ? "would block" : "clear"}`);
|
|
3857
|
+
console.log(`- active hard-stop blockers: ${status.workspaceHardStop.activeBlockers.length}`);
|
|
3858
|
+
console.log(`- stale/unrelated pending: ${status.workspaceHardStop.stalePendingQuestionCount} questions, ${status.workspaceHardStop.stalePendingObligationCount} obligations`);
|
|
3835
3859
|
return;
|
|
3836
3860
|
}
|
|
3837
3861
|
throw new Error("Unknown codex subcommand.");
|
|
@@ -3977,8 +4001,7 @@ async function main() {
|
|
|
3977
4001
|
return;
|
|
3978
4002
|
}
|
|
3979
4003
|
if (command === "team") {
|
|
3980
|
-
|
|
3981
|
-
return;
|
|
4004
|
+
throw disabledTeamCommandError();
|
|
3982
4005
|
}
|
|
3983
4006
|
if (command === "decide") {
|
|
3984
4007
|
await runDecide(values);
|
package/dist/codex-hooks.js
CHANGED
|
@@ -48,14 +48,12 @@ export function buildManagedCodexHooksConfig(packageRoot) {
|
|
|
48
48
|
],
|
|
49
49
|
PreToolUse: [
|
|
50
50
|
buildCommandHook(command, {
|
|
51
|
-
matcher: "Bash"
|
|
52
|
-
statusMessage: "Running LongTable checkpoint guard"
|
|
51
|
+
matcher: "Bash"
|
|
53
52
|
})
|
|
54
53
|
],
|
|
55
54
|
PostToolUse: [
|
|
56
55
|
buildCommandHook(command, {
|
|
57
|
-
matcher: "Bash"
|
|
58
|
-
statusMessage: "Reviewing LongTable post-tool state"
|
|
56
|
+
matcher: "Bash"
|
|
59
57
|
})
|
|
60
58
|
],
|
|
61
59
|
UserPromptSubmit: [
|
package/dist/debate.js
CHANGED
|
@@ -166,7 +166,7 @@ function convergenceContribution(roundId, plan, role, label, artifactPath) {
|
|
|
166
166
|
function buildSynthesis(plan, artifactPath, kind) {
|
|
167
167
|
const labels = plan.members.map((member) => member.label);
|
|
168
168
|
const highSensitivity = plan.checkpointSensitivity === "high";
|
|
169
|
-
const runLabel = kind === "debate" ? "debate" : "
|
|
169
|
+
const runLabel = kind === "debate" ? "panel debate" : "panel review";
|
|
170
170
|
return {
|
|
171
171
|
artifactPath,
|
|
172
172
|
summary: `The ${runLabel} completed across ${labels.join(", ")}. It should slow closure by turning role disagreement into an explicit researcher decision.`,
|
|
@@ -201,8 +201,8 @@ export function createTeamDebateQuestionRecord(run, provider) {
|
|
|
201
201
|
status: "pending",
|
|
202
202
|
prompt: {
|
|
203
203
|
id: createId("question_prompt"),
|
|
204
|
-
checkpointKey: "
|
|
205
|
-
title: isDebate ? "
|
|
204
|
+
checkpointKey: "panel_debate_next_decision",
|
|
205
|
+
title: isDebate ? "Panel debate follow-up decision" : "Panel review follow-up decision",
|
|
206
206
|
question: run.synthesis.recommendedCheckpoint,
|
|
207
207
|
type: "single_choice",
|
|
208
208
|
options: [
|
|
@@ -238,11 +238,11 @@ export function createTeamDebateQuestionRecord(run, provider) {
|
|
|
238
238
|
? "Role rebuttals and convergence should connect to an explicit researcher decision."
|
|
239
239
|
: "Cross-review created role disagreement that should connect to an explicit researcher decision.",
|
|
240
240
|
rationale: [
|
|
241
|
-
"
|
|
241
|
+
"LongTable panel orchestration is a research harness surface, not a substitute for researcher judgment.",
|
|
242
242
|
isDebate
|
|
243
243
|
? "The fixed debate rounds created disagreement that should connect to an explicit researcher decision."
|
|
244
244
|
: "The cross-review round created disagreement that should connect to an explicit researcher decision.",
|
|
245
|
-
`
|
|
245
|
+
`LongTable panel run: ${run.id}.`
|
|
246
246
|
],
|
|
247
247
|
preferredSurfaces: provider === "claude"
|
|
248
248
|
? ["native_structured", "numbered"]
|
|
@@ -255,8 +255,8 @@ function buildTeamBundle(options, kind) {
|
|
|
255
255
|
const expectedRounds = kind === "debate" ? 5 : 3;
|
|
256
256
|
if (roundCount !== expectedRounds) {
|
|
257
257
|
throw new Error(kind === "debate"
|
|
258
|
-
? "LongTable debate v1 supports fixed 5-round debate only."
|
|
259
|
-
: "LongTable
|
|
258
|
+
? "LongTable panel debate v1 supports fixed 5-round debate only."
|
|
259
|
+
: "LongTable panel review v1 supports fixed 3-round cross-review only.");
|
|
260
260
|
}
|
|
261
261
|
const createdAt = nowIso();
|
|
262
262
|
const plan = buildPanelPlan({
|
|
@@ -267,7 +267,7 @@ function buildTeamBundle(options, kind) {
|
|
|
267
267
|
visibility: options.visibility ?? "always_visible"
|
|
268
268
|
});
|
|
269
269
|
const rounds = [];
|
|
270
|
-
const round1Id = createId("
|
|
270
|
+
const round1Id = createId("panel_round");
|
|
271
271
|
const independentContributions = plan.members.map((member) => independentContribution(round1Id, plan, member.role, member.label, join("round-1-independent", `${member.role}.json`)));
|
|
272
272
|
rounds.push({
|
|
273
273
|
id: round1Id,
|
|
@@ -278,7 +278,7 @@ function buildTeamBundle(options, kind) {
|
|
|
278
278
|
artifactDir: join(options.teamDir, "round-1-independent"),
|
|
279
279
|
contributions: independentContributions
|
|
280
280
|
});
|
|
281
|
-
const round2Id = createId("
|
|
281
|
+
const round2Id = createId("panel_round");
|
|
282
282
|
const crossContributions = plan.members.flatMap((member) => plan.members
|
|
283
283
|
.filter((target) => target.role !== member.role)
|
|
284
284
|
.map((target) => crossReviewContribution(round2Id, plan, member.role, member.label, target.role, target.label, independentContributions.find((contribution) => contribution.role === target.role), join("round-2-cross-review", `${member.role}-on-${target.role}.json`))));
|
|
@@ -292,7 +292,7 @@ function buildTeamBundle(options, kind) {
|
|
|
292
292
|
contributions: crossContributions
|
|
293
293
|
});
|
|
294
294
|
if (kind === "debate") {
|
|
295
|
-
const round3Id = createId("
|
|
295
|
+
const round3Id = createId("panel_round");
|
|
296
296
|
rounds.push({
|
|
297
297
|
id: round3Id,
|
|
298
298
|
index: 3,
|
|
@@ -302,7 +302,7 @@ function buildTeamBundle(options, kind) {
|
|
|
302
302
|
artifactDir: join(options.teamDir, "round-3-rebuttal"),
|
|
303
303
|
contributions: plan.members.map((member) => rebuttalContribution(round3Id, member.role, member.label, join("round-3-rebuttal", `${member.role}.json`)))
|
|
304
304
|
});
|
|
305
|
-
const round4Id = createId("
|
|
305
|
+
const round4Id = createId("panel_round");
|
|
306
306
|
rounds.push({
|
|
307
307
|
id: round4Id,
|
|
308
308
|
index: 4,
|
|
@@ -315,22 +315,22 @@ function buildTeamBundle(options, kind) {
|
|
|
315
315
|
}
|
|
316
316
|
const synthesis = buildSynthesis(plan, "synthesis.json", kind);
|
|
317
317
|
const run = {
|
|
318
|
-
id: createId("
|
|
318
|
+
id: createId("panel_debate_run"),
|
|
319
319
|
teamId: options.teamId,
|
|
320
320
|
createdAt,
|
|
321
321
|
updatedAt: createdAt,
|
|
322
322
|
prompt: options.prompt,
|
|
323
323
|
roles: plan.members,
|
|
324
324
|
status: "completed",
|
|
325
|
-
surface: "
|
|
325
|
+
surface: "file_backed_panel_debate",
|
|
326
326
|
interactionDepth: kind === "debate" ? "debated" : "cross_reviewed",
|
|
327
|
-
roundPolicy: kind === "debate" ? "fixed" : "
|
|
327
|
+
roundPolicy: kind === "debate" ? "fixed" : "panel_cross_review",
|
|
328
328
|
roundCount,
|
|
329
329
|
artifactRoot: options.teamDir,
|
|
330
330
|
rounds: [
|
|
331
331
|
...rounds,
|
|
332
332
|
{
|
|
333
|
-
id: createId("
|
|
333
|
+
id: createId("panel_round"),
|
|
334
334
|
index: roundCount,
|
|
335
335
|
kind: "synthesis",
|
|
336
336
|
title: "Coordinator synthesis and checkpoint",
|
|
@@ -353,12 +353,12 @@ function buildTeamBundle(options, kind) {
|
|
|
353
353
|
checkpointSensitivity: plan.checkpointSensitivity,
|
|
354
354
|
rationale: [
|
|
355
355
|
kind === "debate"
|
|
356
|
-
? "Autonomous debate requested through LongTable
|
|
357
|
-
: "
|
|
356
|
+
? "Autonomous debate requested through LongTable panel orchestration."
|
|
357
|
+
: "Cross-role review requested through LongTable panel orchestration.",
|
|
358
358
|
"File-backed rounds keep disagreement inspectable before researcher closure."
|
|
359
359
|
]
|
|
360
360
|
});
|
|
361
|
-
intent.kind = kind === "debate" ? "
|
|
361
|
+
intent.kind = kind === "debate" ? "panel_debate" : "panel";
|
|
362
362
|
intent.requestedSurface = run.surface;
|
|
363
363
|
const invocationRecord = {
|
|
364
364
|
id: createId("invocation_record"),
|
|
@@ -371,7 +371,7 @@ function buildTeamBundle(options, kind) {
|
|
|
371
371
|
interactionDepth: run.interactionDepth,
|
|
372
372
|
panelPlan: plan,
|
|
373
373
|
teamDebateRun: run,
|
|
374
|
-
degradationReason: "File-backed
|
|
374
|
+
degradationReason: "File-backed panel artifacts are the canonical execution record."
|
|
375
375
|
};
|
|
376
376
|
return {
|
|
377
377
|
plan,
|
|
@@ -389,8 +389,8 @@ export function buildTeamDebate(options) {
|
|
|
389
389
|
}
|
|
390
390
|
export function renderTeamDebateSummary(run) {
|
|
391
391
|
return [
|
|
392
|
-
"LongTable
|
|
393
|
-
`-
|
|
392
|
+
"LongTable Panel Debate",
|
|
393
|
+
`- panel: ${run.teamId}`,
|
|
394
394
|
`- surface: ${run.surface}`,
|
|
395
395
|
`- interaction depth: ${run.interactionDepth}`,
|
|
396
396
|
`- rounds: ${run.roundCount} ${run.roundPolicy}`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { collectHardStopBlockers } from "@longtable/core";
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { collectHardStopBlockers } from "@longtable/core";
|
|
2
3
|
import { createWorkspaceFollowUpQuestions, loadProjectContextFromDirectory, loadWorkspaceState, pendingQuestionObligations } from "./index.js";
|
|
3
4
|
function safeString(value) {
|
|
4
5
|
return typeof value === "string" ? value : "";
|
|
@@ -66,6 +67,9 @@ function formatQuestionOptions(question) {
|
|
|
66
67
|
function pendingRequiredQuestions(state) {
|
|
67
68
|
return (state.questionLog ?? []).filter((question) => question.status === "pending" && question.prompt.required);
|
|
68
69
|
}
|
|
70
|
+
function hardStopBlockers(state) {
|
|
71
|
+
return collectHardStopBlockers(state).activeBlockers;
|
|
72
|
+
}
|
|
69
73
|
function pendingObligations(state) {
|
|
70
74
|
return pendingQuestionObligations(state);
|
|
71
75
|
}
|
|
@@ -221,7 +225,7 @@ function mutatesLongTableResearchState(command) {
|
|
|
221
225
|
return false;
|
|
222
226
|
}
|
|
223
227
|
return /\.longtable(?:\/|\b)|\bCURRENT\.md\b/.test(normalized)
|
|
224
|
-
|| /\blongtable\s+(?:start|question|clear-question|prune-questions|ask|clarify|panel
|
|
228
|
+
|| /\blongtable\s+(?:start|question|clear-question|prune-questions|ask|clarify|panel)\b/.test(normalized);
|
|
225
229
|
}
|
|
226
230
|
async function loadLongTableRuntime(startPath) {
|
|
227
231
|
const context = await loadProjectContextFromDirectory(startPath);
|
|
@@ -283,6 +287,22 @@ function buildGeneratedQuestionsContext(questions, created) {
|
|
|
283
287
|
lines.push("Do not choose or record answers for these checkpoints unless the researcher explicitly provides the selections.");
|
|
284
288
|
return lines.join("\n");
|
|
285
289
|
}
|
|
290
|
+
function buildHardStopBlockerContext(blocker) {
|
|
291
|
+
return [
|
|
292
|
+
`LongTable hard-stop blocker ${blocker.id} affects ${blocker.scope.replace(/_/g, " ")}.`,
|
|
293
|
+
blocker.prompt,
|
|
294
|
+
blocker.reason,
|
|
295
|
+
`Next action: ${blocker.commandHints[0] ?? "decide, clear, or defer with rationale"}`
|
|
296
|
+
].join("\n");
|
|
297
|
+
}
|
|
298
|
+
function buildStopBlockerReason(blocker, count) {
|
|
299
|
+
const suffix = count > 1 ? ` (${count} active blockers total)` : "";
|
|
300
|
+
return [
|
|
301
|
+
`LongTable hard-stop ${blocker.id}${suffix}: ${blocker.scope.replace(/_/g, " ")}.`,
|
|
302
|
+
compactContextValue(blocker.prompt, 120),
|
|
303
|
+
`Required next action: ${blocker.commandHints[0] ?? "decide, clear, or defer with rationale"}.`
|
|
304
|
+
].join(" ");
|
|
305
|
+
}
|
|
286
306
|
function buildPendingObligationContext(obligation) {
|
|
287
307
|
return [
|
|
288
308
|
`Pending LongTable research obligation: ${obligation.prompt}`,
|
|
@@ -298,6 +318,21 @@ function buildSeparatePendingObligationNotice(obligation) {
|
|
|
298
318
|
"This is not part of the active interview. Keep it visible only when the researcher is settling or saving the research direction."
|
|
299
319
|
].join("\n");
|
|
300
320
|
}
|
|
321
|
+
function buildHardStopContext(runtime) {
|
|
322
|
+
const verdict = collectHardStopBlockers(runtime.state);
|
|
323
|
+
const blocker = verdict.activeBlockers[0];
|
|
324
|
+
if (!blocker) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
return [
|
|
328
|
+
`Hard-stop Researcher Checkpoint is still pending: ${blocker.id}`,
|
|
329
|
+
`Affected Research Specification area: ${blocker.scope}`,
|
|
330
|
+
`Question/obligation: ${blocker.prompt}`,
|
|
331
|
+
`Reason: ${blocker.reason}`,
|
|
332
|
+
`Required next action: ${blocker.commandHint}; or clear/defer it with an explicit rationale.`,
|
|
333
|
+
verdict.activeBlockers.length > 1 ? `Additional hard-stop blockers: ${verdict.activeBlockers.length - 1}` : ""
|
|
334
|
+
].filter(Boolean).join("\n");
|
|
335
|
+
}
|
|
301
336
|
function buildActiveInterviewContext(hook) {
|
|
302
337
|
const turnCount = hook.turns?.length ?? 0;
|
|
303
338
|
return [
|
|
@@ -421,13 +456,9 @@ function preToolUseOutput(runtime, payload) {
|
|
|
421
456
|
if (!stateChangingCommand) {
|
|
422
457
|
return null;
|
|
423
458
|
}
|
|
424
|
-
const
|
|
425
|
-
if (
|
|
426
|
-
return buildBlockOutput("PreToolUse", "A
|
|
427
|
-
}
|
|
428
|
-
const blockingObligation = pendingObligations(runtime.state)[0];
|
|
429
|
-
if (blockingObligation && mutatesLongTableResearchState(command)) {
|
|
430
|
-
return buildBlockOutput("PreToolUse", "A LongTable research obligation is still pending before a research-state Bash command.", buildPendingObligationContext(blockingObligation));
|
|
459
|
+
const hardStopContext = buildHardStopContext(runtime);
|
|
460
|
+
if (hardStopContext && mutatesLongTableResearchState(command)) {
|
|
461
|
+
return buildBlockOutput("PreToolUse", "A LongTable hard-stop is pending before a research-state Bash command.", hardStopContext);
|
|
431
462
|
}
|
|
432
463
|
return null;
|
|
433
464
|
}
|
|
@@ -438,21 +469,18 @@ function postToolUseOutput(runtime, payload) {
|
|
|
438
469
|
const command = readCommandText(payload);
|
|
439
470
|
const exitCode = readExitCode(payload);
|
|
440
471
|
const output = readCombinedOutput(payload);
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
return buildBlockOutput("PostToolUse", "A research-state Bash command completed while LongTable still had an unresolved checkpoint or obligation.", blockingQuestion
|
|
445
|
-
? buildPendingQuestionContext(blockingQuestion)
|
|
446
|
-
: buildPendingObligationContext(blockingObligation));
|
|
472
|
+
const hardStopContext = buildHardStopContext(runtime);
|
|
473
|
+
if (hardStopContext && mutatesLongTableResearchState(command)) {
|
|
474
|
+
return buildBlockOutput("PostToolUse", "A research-state Bash command completed while LongTable still had an unresolved hard-stop.", hardStopContext);
|
|
447
475
|
}
|
|
448
|
-
if (exitCode !== null && exitCode !== 0 && output) {
|
|
449
|
-
return buildBlockOutput("PostToolUse", "
|
|
476
|
+
if (exitCode !== null && exitCode !== 0 && output && mutatesLongTableResearchState(command)) {
|
|
477
|
+
return buildBlockOutput("PostToolUse", "A LongTable-relevant Bash command returned a non-zero exit code and should be reviewed before LongTable continues.", "Review the command output and explain what failed before retrying or continuing.");
|
|
450
478
|
}
|
|
451
479
|
return null;
|
|
452
480
|
}
|
|
453
481
|
function stopOutput(runtime) {
|
|
454
|
-
|
|
455
|
-
return null;
|
|
482
|
+
const hardStopContext = buildHardStopContext(runtime);
|
|
483
|
+
return hardStopContext ? buildStopBlockOutput(hardStopContext) : null;
|
|
456
484
|
}
|
|
457
485
|
export async function dispatchCodexHook(payload, cwdOverride) {
|
|
458
486
|
const hookEventName = readHookEventName(payload);
|
package/dist/panel.js
CHANGED
|
@@ -183,7 +183,7 @@ export function createPlannedInvocationRecord(options) {
|
|
|
183
183
|
interactionDepth: "independent",
|
|
184
184
|
panelPlan: options.plan,
|
|
185
185
|
panelResult: options.result,
|
|
186
|
-
degradationReason: "
|
|
186
|
+
degradationReason: "Sequential fallback is the stable LongTable panel surface."
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
function roleInstruction(member) {
|
package/dist/persona-router.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface LongTableInvocationDirective {
|
|
|
12
12
|
explicit: boolean;
|
|
13
13
|
cleanedPrompt: string;
|
|
14
14
|
mode?: InteractionMode | "panel" | "status";
|
|
15
|
-
collaboration?: "panel" | "
|
|
15
|
+
collaboration?: "panel" | "debate";
|
|
16
16
|
roles: CanonicalPersona[];
|
|
17
17
|
panel: boolean;
|
|
18
18
|
showConflicts: boolean;
|
package/dist/persona-router.js
CHANGED
|
@@ -8,8 +8,8 @@ const DIRECTIVE_MAP = [
|
|
|
8
8
|
{ key: "draft", mode: "draft" },
|
|
9
9
|
{ key: "commit", mode: "commit" },
|
|
10
10
|
{ key: "panel", mode: "panel", collaboration: "panel", panel: true, showConflicts: true },
|
|
11
|
-
{ key: "team", mode: "
|
|
12
|
-
{ key: "debate", mode: "
|
|
11
|
+
{ key: "team", mode: "panel", collaboration: "panel", panel: true, showConflicts: true },
|
|
12
|
+
{ key: "debate", mode: "panel", collaboration: "debate", panel: true, showConflicts: true },
|
|
13
13
|
{ key: "status", mode: "status" },
|
|
14
14
|
{ key: "editor", mode: "review", roles: ["editor"] },
|
|
15
15
|
{ key: "reviewer", mode: "review", roles: ["reviewer"] },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { DecisionRecord, EvidenceRecord, InvocationRecord, LongTableQuestionObligation, ProviderKind, QuestionOption, QuestionCommitmentFamily, QuestionEpistemicBasis, QuestionGenerationResult, QuestionOpportunity, QuestionSurface, QuestionPromptType, QuestionRecord, ResearchSpecificationChange, ResearchSpecificationPatch, ResearchSpecificationPatchSource, ResearchSpecificationRevision, ResearchState } from "@longtable/core";
|
|
1
|
+
import type { DecisionRecord, EvidenceRecord, InvocationRecord, LongTableQuestionObligation, ProviderKind, QuestionOption, HardStopScope, QuestionCommitmentFamily, QuestionEpistemicBasis, QuestionGenerationResult, QuestionOpportunity, QuestionSurface, QuestionPromptType, QuestionRecord, ResearchSpecificationChange, ResearchSpecificationPatch, ResearchSpecificationPatchSource, ResearchSpecificationRevision, ResearchState } from "@longtable/core";
|
|
2
2
|
import type { SetupPersistedOutput } from "@longtable/setup";
|
|
3
|
+
import { type HardStopVerdict } from "@longtable/core";
|
|
3
4
|
export type ProjectDisagreementPreference = "synthesis_only" | "show_on_conflict" | "always_visible";
|
|
4
5
|
export type StartInterviewSignal = "phenomenon" | "audience" | "artifact" | "evidence" | "assumption" | "decision_risk" | "voice";
|
|
5
6
|
export interface StartInterviewTurn {
|
|
@@ -211,6 +212,8 @@ export interface LongTableWorkspaceInspection {
|
|
|
211
212
|
questions: number;
|
|
212
213
|
pendingQuestions: number;
|
|
213
214
|
pendingObligations: number;
|
|
215
|
+
stalePendingQuestions?: number;
|
|
216
|
+
stalePendingObligations?: number;
|
|
214
217
|
answeredQuestions: number;
|
|
215
218
|
decisions: number;
|
|
216
219
|
interviewTurns?: number;
|
|
@@ -218,6 +221,7 @@ export interface LongTableWorkspaceInspection {
|
|
|
218
221
|
specPatches?: number;
|
|
219
222
|
specRevisions?: number;
|
|
220
223
|
};
|
|
224
|
+
hardStop?: HardStopVerdict;
|
|
221
225
|
recentInvocations?: Array<{
|
|
222
226
|
id: string;
|
|
223
227
|
kind: string;
|
|
@@ -234,6 +238,8 @@ export interface LongTableWorkspaceInspection {
|
|
|
234
238
|
question: string;
|
|
235
239
|
commitmentFamily?: QuestionCommitmentFamily;
|
|
236
240
|
epistemicBasis?: QuestionEpistemicBasis;
|
|
241
|
+
hardStop?: boolean;
|
|
242
|
+
hardStopScope?: string;
|
|
237
243
|
options: string[];
|
|
238
244
|
required: boolean;
|
|
239
245
|
}>;
|
|
@@ -242,6 +248,8 @@ export interface LongTableWorkspaceInspection {
|
|
|
242
248
|
kind: string;
|
|
243
249
|
prompt: string;
|
|
244
250
|
reason: string;
|
|
251
|
+
hardStop?: boolean;
|
|
252
|
+
hardStopScope?: string;
|
|
245
253
|
questionId?: string;
|
|
246
254
|
}>;
|
|
247
255
|
recentDecisions?: Array<{
|
|
@@ -379,6 +387,8 @@ export declare function createWorkspaceFollowUpQuestions(options: {
|
|
|
379
387
|
prompt: string;
|
|
380
388
|
provider?: ProviderKind;
|
|
381
389
|
required?: boolean;
|
|
390
|
+
hardStop?: boolean;
|
|
391
|
+
hardStopScope?: HardStopScope;
|
|
382
392
|
force?: boolean;
|
|
383
393
|
auto?: boolean;
|
|
384
394
|
requiredOnly?: boolean;
|
|
@@ -401,6 +411,8 @@ export declare function createWorkspaceQuestion(options: {
|
|
|
401
411
|
displayReason?: string;
|
|
402
412
|
provider?: ProviderKind;
|
|
403
413
|
required?: boolean;
|
|
414
|
+
hardStop?: boolean;
|
|
415
|
+
hardStopScope?: HardStopScope;
|
|
404
416
|
commitmentFamily?: QuestionCommitmentFamily;
|
|
405
417
|
epistemicBasis?: QuestionEpistemicBasis;
|
|
406
418
|
}): Promise<{
|
package/dist/project-session.js
CHANGED
|
@@ -4,6 +4,7 @@ import { execSync } from "node:child_process";
|
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { appendDecisionRecord as appendDecisionToResearchState, appendInvocationRecord as appendInvocationToResearchState, appendQuestionRecords, createEmptyResearchState } from "@longtable/memory";
|
|
6
6
|
import { classifyCheckpointTrigger } from "@longtable/checkpoints";
|
|
7
|
+
import { collectHardStopBlockers } from "@longtable/core";
|
|
7
8
|
import { ensureRequiredQuestionObligation, pendingQuestionObligations, resolveQuestionObligationByQuestionId } from "./question-obligations.js";
|
|
8
9
|
const CURRENT_FILE_NAME = "CURRENT.md";
|
|
9
10
|
const LEGACY_ROOT_FILES = ["LONGTABLE.md", "START-HERE.md", "NEXT-STEPS.md", "SESSION-SNAPSHOT.md"];
|
|
@@ -765,6 +766,7 @@ function summarizeWorkspaceInspection(context, state) {
|
|
|
765
766
|
const pendingQuestions = questions.filter((record) => record.status === "pending");
|
|
766
767
|
const answeredQuestions = questions.filter((record) => record.status === "answered");
|
|
767
768
|
const pendingObligations = visiblePendingObligations(state);
|
|
769
|
+
const hardStop = collectHardStopBlockers(state);
|
|
768
770
|
return {
|
|
769
771
|
found: true,
|
|
770
772
|
rootPath: context.project.projectPath,
|
|
@@ -803,6 +805,8 @@ function summarizeWorkspaceInspection(context, state) {
|
|
|
803
805
|
questions: questions.length,
|
|
804
806
|
pendingQuestions: pendingQuestions.length,
|
|
805
807
|
pendingObligations: pendingObligations.length,
|
|
808
|
+
stalePendingQuestions: hardStop.stalePendingQuestionCount,
|
|
809
|
+
stalePendingObligations: hardStop.stalePendingObligationCount,
|
|
806
810
|
answeredQuestions: answeredQuestions.length,
|
|
807
811
|
decisions: (state.decisionLog ?? []).length,
|
|
808
812
|
interviewTurns: (state.interviewTurns ?? []).length,
|
|
@@ -810,6 +814,7 @@ function summarizeWorkspaceInspection(context, state) {
|
|
|
810
814
|
specPatches: (state.specPatches ?? []).length,
|
|
811
815
|
specRevisions: (state.specRevisions ?? []).length
|
|
812
816
|
},
|
|
817
|
+
hardStop,
|
|
813
818
|
recentInvocations: recentInvocationRecords(state, 5).map((record) => ({
|
|
814
819
|
id: record.id,
|
|
815
820
|
kind: record.intent.kind,
|
|
@@ -826,6 +831,8 @@ function summarizeWorkspaceInspection(context, state) {
|
|
|
826
831
|
question: record.prompt.question,
|
|
827
832
|
...(record.commitmentFamily ? { commitmentFamily: record.commitmentFamily } : {}),
|
|
828
833
|
...(record.epistemicBasis ? { epistemicBasis: record.epistemicBasis } : {}),
|
|
834
|
+
...(typeof record.hardStop === "boolean" ? { hardStop: record.hardStop } : {}),
|
|
835
|
+
...(record.hardStopScope ? { hardStopScope: record.hardStopScope } : {}),
|
|
829
836
|
options: formatQuestionOptionValues(record),
|
|
830
837
|
required: record.prompt.required
|
|
831
838
|
})),
|
|
@@ -834,6 +841,8 @@ function summarizeWorkspaceInspection(context, state) {
|
|
|
834
841
|
kind: obligation.kind,
|
|
835
842
|
prompt: obligation.prompt,
|
|
836
843
|
reason: obligation.reason,
|
|
844
|
+
...(typeof obligation.hardStop === "boolean" ? { hardStop: obligation.hardStop } : {}),
|
|
845
|
+
...(obligation.hardStopScope ? { hardStopScope: obligation.hardStopScope } : {}),
|
|
837
846
|
...(obligation.questionId ? { questionId: obligation.questionId } : {})
|
|
838
847
|
})),
|
|
839
848
|
recentDecisions: (state.decisionLog ?? []).slice(-5).reverse().map((record) => ({
|
|
@@ -2266,7 +2275,7 @@ const COMMITMENT_FAMILY_BY_CHECKPOINT = [
|
|
|
2266
2275
|
[/research_question|research_direction|scope|boundary|inclusion|exclusion/, "scope"],
|
|
2267
2276
|
[/theory|construct|conceptual/, "construct"],
|
|
2268
2277
|
[/measurement|coding|codebook|extraction/, "coding"],
|
|
2269
|
-
[/method|analysis|panel_disagreement|team_debate|review/, "method"],
|
|
2278
|
+
[/method|analysis|panel_disagreement|panel_debate|team_debate|review/, "method"],
|
|
2270
2279
|
[/evidence|scholarly_access|source_authority/, "evidence"],
|
|
2271
2280
|
[/knowledge_gap|tacit_assumption|epistemic/, "epistemic_authority"]
|
|
2272
2281
|
];
|
|
@@ -2305,12 +2314,45 @@ function inferEpistemicBasis(input) {
|
|
|
2305
2314
|
return "mixed";
|
|
2306
2315
|
return unique[0];
|
|
2307
2316
|
}
|
|
2317
|
+
function inferHardStopScope(input, commitmentFamily) {
|
|
2318
|
+
if (commitmentFamily === "product_policy")
|
|
2319
|
+
return undefined;
|
|
2320
|
+
if (commitmentFamily === "scope")
|
|
2321
|
+
return "scope";
|
|
2322
|
+
if (commitmentFamily === "construct" || commitmentFamily === "coding")
|
|
2323
|
+
return "construct";
|
|
2324
|
+
if (commitmentFamily === "method")
|
|
2325
|
+
return "method";
|
|
2326
|
+
if (commitmentFamily === "evidence")
|
|
2327
|
+
return "evidence";
|
|
2328
|
+
if (commitmentFamily === "epistemic_authority")
|
|
2329
|
+
return "protected_decision";
|
|
2330
|
+
const text = compactMetadataText([input.checkpointKey, input.title, input.question, input.prompt, input.rationale]);
|
|
2331
|
+
if (textMatchesAny(text, [/product_runtime|checkpoint policy|hook ux|setup|install|cli|npm|release|git|github|docs?|readme|package|workflow/])) {
|
|
2332
|
+
return undefined;
|
|
2333
|
+
}
|
|
2334
|
+
if (textMatchesAny(text, [/protected_decision|closure/]))
|
|
2335
|
+
return "protected_decision";
|
|
2336
|
+
if (textMatchesAny(text, [/research_question|research direction|question_freeze/]))
|
|
2337
|
+
return "research_question";
|
|
2338
|
+
if (textMatchesAny(text, [/scope|boundary|inclusion|exclusion/]))
|
|
2339
|
+
return "scope";
|
|
2340
|
+
if (textMatchesAny(text, [/construct|theory|frame|ontology|measurement|coding|validity/]))
|
|
2341
|
+
return "construct";
|
|
2342
|
+
if (textMatchesAny(text, [/method|design|sample|analysis|strategy|model/]))
|
|
2343
|
+
return "method";
|
|
2344
|
+
if (textMatchesAny(text, [/evidence|access|source|corpus|pdf|full[-_ ]?text|scholarly/]))
|
|
2345
|
+
return "evidence";
|
|
2346
|
+
return undefined;
|
|
2347
|
+
}
|
|
2308
2348
|
function resolveQuestionRecordMetadata(input) {
|
|
2309
2349
|
const commitmentFamily = input.commitmentFamily ?? inferCommitmentFamily(input);
|
|
2310
2350
|
const epistemicBasis = input.epistemicBasis ?? inferEpistemicBasis(input);
|
|
2351
|
+
const hardStopScope = inferHardStopScope(input, commitmentFamily);
|
|
2311
2352
|
return {
|
|
2312
2353
|
...(commitmentFamily ? { commitmentFamily } : {}),
|
|
2313
|
-
...(epistemicBasis ? { epistemicBasis } : {})
|
|
2354
|
+
...(epistemicBasis ? { epistemicBasis } : {}),
|
|
2355
|
+
...(hardStopScope ? { hardStop: true, hardStopScope } : {})
|
|
2314
2356
|
};
|
|
2315
2357
|
}
|
|
2316
2358
|
function hasFollowUpPrompt(record, prompt) {
|
|
@@ -2371,6 +2413,8 @@ export async function createWorkspaceFollowUpQuestions(options) {
|
|
|
2371
2413
|
updatedAt: createdAt,
|
|
2372
2414
|
status: "pending",
|
|
2373
2415
|
...metadata,
|
|
2416
|
+
...(typeof options.hardStop === "boolean" ? { hardStop: options.hardStop } : {}),
|
|
2417
|
+
...(options.hardStopScope ? { hardStopScope: options.hardStopScope } : {}),
|
|
2374
2418
|
prompt: {
|
|
2375
2419
|
id: createId("question_prompt"),
|
|
2376
2420
|
checkpointKey,
|
|
@@ -2425,6 +2469,8 @@ export async function createWorkspaceQuestion(options) {
|
|
|
2425
2469
|
updatedAt: createdAt,
|
|
2426
2470
|
status: "pending",
|
|
2427
2471
|
...metadata,
|
|
2472
|
+
...(typeof options.hardStop === "boolean" ? { hardStop: options.hardStop } : {}),
|
|
2473
|
+
...(options.hardStopScope ? { hardStopScope: options.hardStopScope } : {}),
|
|
2428
2474
|
prompt: {
|
|
2429
2475
|
id: createId("question_prompt"),
|
|
2430
2476
|
checkpointKey,
|
|
@@ -17,6 +17,8 @@ export function createRequiredQuestionObligation(question) {
|
|
|
17
17
|
updatedAt: timestamp,
|
|
18
18
|
prompt: question.prompt.question,
|
|
19
19
|
reason: question.prompt.displayReason ?? "A required LongTable checkpoint is pending.",
|
|
20
|
+
...(question.hardStop !== undefined ? { hardStop: question.hardStop } : {}),
|
|
21
|
+
...(question.hardStopScope ? { hardStopScope: question.hardStopScope } : {}),
|
|
20
22
|
questionId: question.id
|
|
21
23
|
};
|
|
22
24
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
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.53",
|
|
33
|
+
"@longtable/core": "0.1.53",
|
|
34
|
+
"@longtable/memory": "0.1.53",
|
|
35
|
+
"@longtable/provider-claude": "0.1.53",
|
|
36
|
+
"@longtable/provider-codex": "0.1.53",
|
|
37
|
+
"@longtable/setup": "0.1.53"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.1",
|