@longtable/cli 0.1.50 → 0.1.52
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 +45 -242
- package/dist/cli.js +83 -22
- package/dist/codex-hooks.js +2 -4
- 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 +48 -20
- package/dist/project-session.d.ts +13 -1
- package/dist/project-session.js +59 -11
- package/dist/prompt-aliases.js +3 -3
- package/dist/question-obligations.js +2 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -2,16 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Researcher-facing CLI for LongTable.
|
|
4
4
|
|
|
5
|
-
LongTable
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The basic contract is:
|
|
10
|
-
|
|
11
|
-
1. approve provider/runtime support once
|
|
12
|
-
2. start each project inside the provider with `$longtable-interview`
|
|
13
|
-
3. create or resume a workspace from that interview
|
|
14
|
-
4. preserve decisions, tensions, and evidence as durable project state
|
|
5
|
+
LongTable keeps scholarly project state in `.longtable/` and exposes generated
|
|
6
|
+
provider skills for Codex and Claude Code. The CLI installs setup, state,
|
|
7
|
+
checkpoint, search, and diagnostic tooling. It does not replace the provider.
|
|
15
8
|
|
|
16
9
|
## Install
|
|
17
10
|
|
|
@@ -19,55 +12,35 @@ The basic contract is:
|
|
|
19
12
|
npm install -g @longtable/cli
|
|
20
13
|
```
|
|
21
14
|
|
|
22
|
-
The npm install only installs the CLI. It does not write Codex skills, MCP
|
|
23
|
-
config, hooks, or provider runtime files without explicit setup approval.
|
|
24
|
-
|
|
25
15
|
## Primary Flow
|
|
26
16
|
|
|
27
|
-
Start Codex from the research folder. The provider uses the shell working
|
|
28
|
-
directory at process start as the session workspace.
|
|
29
|
-
|
|
30
17
|
```bash
|
|
31
18
|
longtable setup --provider codex
|
|
32
19
|
cd "<research-folder>"
|
|
33
20
|
codex
|
|
34
21
|
```
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
Changing directories after Codex is already running does not change that
|
|
38
|
-
session's workspace root or rerun LongTable's `SessionStart` hook.
|
|
39
|
-
|
|
40
|
-
Examples:
|
|
23
|
+
Then invoke:
|
|
41
24
|
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
codex -C "/Users/yourname/Research/My-Research-Project"
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
```powershell
|
|
48
|
-
# Windows PowerShell
|
|
49
|
-
codex -C "C:\Users\YourName\Documents\Research\My-Research-Project"
|
|
25
|
+
```text
|
|
26
|
+
$longtable-start
|
|
50
27
|
```
|
|
51
28
|
|
|
52
|
-
|
|
29
|
+
`$longtable-start` creates or resumes the workspace, asks open research-start
|
|
30
|
+
questions, and stores a Research Specification when there is enough material.
|
|
53
31
|
|
|
54
|
-
|
|
55
|
-
where LongTable may install support, which runtime surfaces it may enable, how
|
|
56
|
-
strongly it may interrupt research decisions, and whether to show the
|
|
57
|
-
provider-native interview launch steps. `longtable init` remains only as a
|
|
58
|
-
deprecated compatibility alias.
|
|
32
|
+
After a Research Specification exists, use:
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
cd "<project-path>"
|
|
64
|
-
codex
|
|
34
|
+
```text
|
|
35
|
+
$longtable-interview
|
|
65
36
|
```
|
|
66
37
|
|
|
67
|
-
|
|
68
|
-
|
|
38
|
+
`$longtable-interview` is post-start. It uses option-first follow-up choices for
|
|
39
|
+
spec revisions, checkpoint resolution, evidence boundaries, coding rules, method
|
|
40
|
+
choices, and protected decisions. If no usable Research Specification exists, it
|
|
41
|
+
must route to `$longtable-start`.
|
|
69
42
|
|
|
70
|
-
##
|
|
43
|
+
## Workspace Artifacts
|
|
71
44
|
|
|
72
45
|
```text
|
|
73
46
|
<project>/
|
|
@@ -80,221 +53,51 @@ summary without starting a provider session.
|
|
|
80
53
|
sessions/
|
|
81
54
|
```
|
|
82
55
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- `
|
|
87
|
-
- `.longtable/project.json`: stable project identity
|
|
88
|
-
- `.longtable/current-session.json`: current session cursor
|
|
89
|
-
- `.longtable/state.json`: layered memory state, including First Research
|
|
90
|
-
Shape and Research Specification when the interview has produced them
|
|
91
|
-
- `.longtable/sessions/`: historical snapshots
|
|
92
|
-
|
|
93
|
-
`$longtable-interview` first stabilizes a short First Research Shape. When the
|
|
94
|
-
conversation is substantive enough, it should also preserve a Research
|
|
95
|
-
Specification covering scope, construct ontology, theory framing,
|
|
96
|
-
measurement/coding, method options, evidence/access requirements, epistemic
|
|
97
|
-
alignment, protected decisions, open questions, and next actions. `CURRENT.md`
|
|
98
|
-
renders that specification so later agents do not need to reconstruct the full
|
|
99
|
-
interview from memory.
|
|
100
|
-
|
|
101
|
-
## Why This Shape
|
|
102
|
-
|
|
103
|
-
The CLI tries to keep the root simple for novice researchers while preserving enough structure for power users and downstream tooling.
|
|
56
|
+
- `CURRENT.md`: human-readable current project state
|
|
57
|
+
- `.longtable/state.json`: durable memory, questions, decisions, interview
|
|
58
|
+
turns, evidence records, First Research Shape, and Research Specification
|
|
59
|
+
- `QuestionRecord -> DecisionRecord`: the durable checkpoint lifecycle
|
|
104
60
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
- working state
|
|
109
|
-
- inferred hypotheses
|
|
110
|
-
- open tensions
|
|
111
|
-
- narrative traces
|
|
112
|
-
|
|
113
|
-
This is how LongTable avoids turning tacit knowledge into fake certainty.
|
|
61
|
+
Provider UI is transport. LongTable supports MCP/native structured elicitation,
|
|
62
|
+
interactive TTY selector surfaces, and numbered/plain-text fallback. Tmux is not
|
|
63
|
+
required for LongTable core behavior.
|
|
114
64
|
|
|
115
65
|
## Commands
|
|
116
66
|
|
|
117
67
|
```bash
|
|
118
|
-
longtable setup
|
|
68
|
+
longtable setup --provider codex
|
|
69
|
+
longtable doctor
|
|
70
|
+
longtable status --cwd "<project-path>"
|
|
119
71
|
longtable resume --cwd "<project-path>"
|
|
120
72
|
longtable roles
|
|
121
|
-
longtable
|
|
122
|
-
longtable
|
|
123
|
-
longtable
|
|
124
|
-
longtable
|
|
125
|
-
longtable team --debate --prompt "Review this measurement plan." --role editor,measurement_auditor --json
|
|
126
|
-
longtable codex install-skills
|
|
127
|
-
longtable claude install-skills
|
|
73
|
+
longtable question --prompt "<decision context>"
|
|
74
|
+
longtable decide --question <id> --answer <value>
|
|
75
|
+
longtable spec read --cwd "<project-path>"
|
|
76
|
+
longtable search --query "<topic>"
|
|
128
77
|
```
|
|
129
78
|
|
|
130
|
-
|
|
79
|
+
`longtable start` remains available for scripted workspace creation with
|
|
80
|
+
`--no-interview --json`, but it is not the primary research-start surface.
|
|
131
81
|
|
|
132
|
-
|
|
133
|
-
longtable panel --prompt "review this methods section" --json
|
|
134
|
-
longtable review --role methods_critic,measurement_auditor --panel --prompt "review this methods section" --json
|
|
135
|
-
longtable ask --prompt "lt panel: show the disagreement before I commit" --json
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Inside Codex
|
|
139
|
-
|
|
140
|
-
Natural language should be the default.
|
|
141
|
-
|
|
142
|
-
Codex UI Researcher Checkpoints are a core LongTable feature when enabled:
|
|
82
|
+
## Development
|
|
143
83
|
|
|
144
84
|
```bash
|
|
145
|
-
|
|
85
|
+
npm run build --workspace @longtable/cli
|
|
86
|
+
npm run typecheck --workspace @longtable/cli
|
|
146
87
|
```
|
|
147
88
|
|
|
148
|
-
|
|
149
|
-
for form-style checkpoint prompts. Without it, LongTable keeps the same
|
|
150
|
-
`QuestionRecord` pending and falls back to numbered text.
|
|
151
|
-
|
|
152
|
-
## Runtime Boundary
|
|
153
|
-
|
|
154
|
-
LongTable is not a replacement wrapper for Codex. Markdown docs and generated
|
|
155
|
-
skills are soft policy; hooks, MCP elicitation, CLI gates, and `.longtable/`
|
|
156
|
-
state are the enforcement layers.
|
|
157
|
-
|
|
158
|
-
LongTable should ask and stop before acting when the next step would change or
|
|
159
|
-
settle one of four high-risk research commitments:
|
|
160
|
-
|
|
161
|
-
1. Research question or scope
|
|
162
|
-
2. Theory frame or construct map
|
|
163
|
-
3. Measurement, coding, or extraction standard
|
|
164
|
-
4. Method design or analysis strategy
|
|
165
|
-
|
|
166
|
-
Low-risk reversible work should continue with visible assumptions instead of a
|
|
167
|
-
hook interruption. If human knowledge, AI inference, and durable project state
|
|
168
|
-
conflict, LongTable should prefer the most explicit durable state; if that state
|
|
169
|
-
is not explicit enough, it should ask the researcher for clarity.
|
|
170
|
-
|
|
171
|
-
Explicit short forms are available when needed:
|
|
172
|
-
|
|
173
|
-
```text
|
|
174
|
-
lt explore: Where should I narrow the question first?
|
|
175
|
-
lt review: What is weak in this claim?
|
|
176
|
-
lt panel: Show me the disagreement before I commit.
|
|
177
|
-
lt methods: Where is the design vulnerable?
|
|
178
|
-
```
|
|
89
|
+
## Codex hard-stop diagnostics
|
|
179
90
|
|
|
180
|
-
|
|
91
|
+
Codex `Stop` blocks only active LongTable hard-stop blockers: unresolved
|
|
92
|
+
Research Specification question, scope, construct, method, evidence, or protected
|
|
93
|
+
decision commitments. Use:
|
|
181
94
|
|
|
182
95
|
```bash
|
|
183
|
-
longtable codex
|
|
184
|
-
longtable
|
|
96
|
+
longtable codex hook-doctor --json
|
|
97
|
+
longtable codex status --json
|
|
98
|
+
longtable doctor --json
|
|
185
99
|
```
|
|
186
100
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
`longtable-voice`. `$longtable` remains the general router and can still invoke
|
|
191
|
-
editor, ethics, venue, panel, explore, or review behavior when the request calls
|
|
192
|
-
for it.
|
|
193
|
-
|
|
194
|
-
Power users can install the legacy full surface explicitly:
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
longtable codex install-skills --surface full
|
|
198
|
-
longtable claude install-skills --surface full
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Do not depend on `/prompts`; current Codex builds may reject it.
|
|
202
|
-
|
|
203
|
-
## Panel Orchestration
|
|
204
|
-
|
|
205
|
-
Panel orchestration is for moments where disagreement matters: methods risk,
|
|
206
|
-
measurement validity, theory fit, literature positioning, and claims that need
|
|
207
|
-
challenge before they become project memory.
|
|
208
|
-
|
|
209
|
-
The CLI creates a provider-neutral `PanelPlan` and returns a planned
|
|
210
|
-
`PanelResult`. When native subagents are unavailable, LongTable uses a stable
|
|
211
|
-
sequential fallback prompt. That keeps the same research semantics available in
|
|
212
|
-
Codex and Claude Code without making either provider's native question or agent
|
|
213
|
-
tool the source of truth.
|
|
214
|
-
|
|
215
|
-
Inside a LongTable project workspace, panel planning also appends an
|
|
216
|
-
`InvocationRecord` to `.longtable/state.json`, creates a pending follow-up
|
|
217
|
-
`QuestionRecord`, and refreshes `CURRENT.md`.
|
|
218
|
-
|
|
219
|
-
Panel output should remain inspectable. A panel or debate result is expected to
|
|
220
|
-
show the consulted roles, each role's main claim or objection, the disagreement
|
|
221
|
-
map, decision options, a defensible recommendation when one exists, and the
|
|
222
|
-
exact researcher-facing question before a high-stakes decision is treated as
|
|
223
|
-
settled.
|
|
224
|
-
|
|
225
|
-
Default panel roles include:
|
|
226
|
-
|
|
227
|
-
- `reviewer`
|
|
228
|
-
- `methods_critic`
|
|
229
|
-
- `measurement_auditor`
|
|
230
|
-
- `theory_critic`
|
|
231
|
-
|
|
232
|
-
Use `--role` to constrain the panel when the research problem is already clear.
|
|
233
|
-
|
|
234
|
-
## Sentinel And Agent Team
|
|
235
|
-
|
|
236
|
-
`longtable sentinel` is an explicit gap/tacit check for prompts that may contain
|
|
237
|
-
measurement, theory, method, evidence, authorship, or tacit-assumption risks.
|
|
238
|
-
Use `--record` inside a LongTable workspace to store the finding as an
|
|
239
|
-
unconfirmed inferred hypothesis.
|
|
240
|
-
|
|
241
|
-
The Codex hook stays quiet for advisory-only questions. Required hook context is
|
|
242
|
-
reserved for durable Researcher Checkpoints, especially when a prompt would
|
|
243
|
-
change the research question/scope, theory frame, measurement/coding standard,
|
|
244
|
-
method design, or analysis strategy. Low-risk reversible work should proceed
|
|
245
|
-
with visible assumptions rather than a noisy hook interruption.
|
|
246
|
-
|
|
247
|
-
`longtable team` creates a file-backed agent-team review under
|
|
248
|
-
`.longtable/team/<id>/`: independent review, cross-review, and
|
|
249
|
-
synthesis/checkpoint. Use it when roles should inspect each other's concerns
|
|
250
|
-
before LongTable proposes a researcher decision.
|
|
251
|
-
|
|
252
|
-
`longtable team --debate` creates a fixed five-round debate record under
|
|
253
|
-
`.longtable/team/<id>/`: independent review, cross-review, rebuttal,
|
|
254
|
-
convergence, and synthesis/checkpoint. The file-backed artifact directory is
|
|
255
|
-
the source of truth.
|
|
256
|
-
|
|
257
|
-
See `docs/AGENT-TEAM-README.md` in the repository for a user-facing guide to
|
|
258
|
-
panel, team, and debate surfaces.
|
|
259
|
-
|
|
260
|
-
## Evidence And Search Direction
|
|
261
|
-
|
|
262
|
-
LongTable should not behave like a generic web scraper. Research search should
|
|
263
|
-
start from scholarly routes when the user needs literature discovery, citation
|
|
264
|
-
verification, publication metadata, or evidence-backed research decisions.
|
|
265
|
-
|
|
266
|
-
`longtable search` routes research queries through arXiv, Crossref, OpenAlex,
|
|
267
|
-
Semantic Scholar, PubMed/NCBI, ERIC, DOAJ, and Unpaywall, then normalizes,
|
|
268
|
-
deduplicates, ranks, and labels results as evidence cards. Some sources work
|
|
269
|
-
without keys, some require a contact email, and some need API keys for reliable
|
|
270
|
-
use.
|
|
271
|
-
|
|
272
|
-
Scholarly access is configured separately through `longtable access setup`.
|
|
273
|
-
It records readiness for metadata, OA full text, institutional access,
|
|
274
|
-
publisher API/TDM credentials, and manual PDFs without storing secrets.
|
|
275
|
-
Publisher probes cover Elsevier, Springer Nature, Wiley, and Taylor & Francis.
|
|
276
|
-
|
|
277
|
-
Citation support should be checked explicitly. A reference can be useful as
|
|
278
|
-
background while still failing to support the specific claim attached to it.
|
|
279
|
-
|
|
280
|
-
```bash
|
|
281
|
-
longtable access setup
|
|
282
|
-
longtable access probe --doi "10.1016/example" --publisher elsevier
|
|
283
|
-
longtable search --query "trust calibration measurement" --intent measurement
|
|
284
|
-
longtable search --query "trust calibration measurement" --publisher-access --json
|
|
285
|
-
longtable search --query "trust calibration citation support" --intent citation --record
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
See:
|
|
289
|
-
|
|
290
|
-
- [Research Search](https://github.com/HosungYou/LongTable/blob/main/docs/RESEARCH-SEARCH.md)
|
|
291
|
-
- [Evidence Policy](https://github.com/HosungYou/LongTable/blob/main/docs/EVIDENCE-POLICY.md)
|
|
292
|
-
- [LongTable Command Surface](https://github.com/HosungYou/LongTable/blob/main/docs/LONGTABLE-COMMAND-SURFACE.md)
|
|
293
|
-
|
|
294
|
-
## Validation
|
|
295
|
-
|
|
296
|
-
```bash
|
|
297
|
-
npm install
|
|
298
|
-
npm run typecheck
|
|
299
|
-
npm run build
|
|
300
|
-
```
|
|
101
|
+
to inspect hook coverage/trust plus `stopWouldBlock`, `activeBlockers`, stale
|
|
102
|
+
pending-question counts, and next actions. Tmux remains an optional terminal
|
|
103
|
+
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";
|
|
@@ -112,16 +113,16 @@ function renderBrandBanner(title, subtitle) {
|
|
|
112
113
|
}
|
|
113
114
|
function renderInterviewLaunchSteps(provider) {
|
|
114
115
|
const command = provider === "codex" ? "codex" : "claude";
|
|
115
|
-
return renderSectionCard("LongTable
|
|
116
|
+
return renderSectionCard("LongTable Start", [
|
|
116
117
|
"Setup is permission and runtime calibration, not the research interview.",
|
|
117
118
|
"The first research conversation now happens inside the provider so the model can listen, reflect, and ask one natural-language follow-up at a time.",
|
|
118
119
|
"",
|
|
119
120
|
"Next:",
|
|
120
121
|
"1. cd \"<research-folder>\"",
|
|
121
122
|
`2. run \`${command}\``,
|
|
122
|
-
"3. invoke `$longtable-
|
|
123
|
+
"3. invoke `$longtable-start`",
|
|
123
124
|
"",
|
|
124
|
-
"The interview will create or resume `.longtable/`, may store a short First Research Shape handle, and uses option UI for the final Research Specification confirmation."
|
|
125
|
+
"The start interview will create or resume `.longtable/`, may store a short First Research Shape handle, and uses option UI for the final Research Specification confirmation."
|
|
125
126
|
]);
|
|
126
127
|
}
|
|
127
128
|
function renderProgressBar(current, total) {
|
|
@@ -133,7 +134,7 @@ function usage() {
|
|
|
133
134
|
return [
|
|
134
135
|
"Usage:",
|
|
135
136
|
" Run `longtable ...` in your terminal, not inside the Codex chat box.",
|
|
136
|
-
" LongTable research starts inside Codex or Claude with `$longtable-
|
|
137
|
+
" LongTable research starts inside Codex or Claude with `$longtable-start` after setup.",
|
|
137
138
|
"",
|
|
138
139
|
" longtable setup [--provider codex|claude] [--install-scope user|project|none] [--surfaces cli_only|skills|skills_mcp|skills_mcp_sentinel] [--intervention advisory|balanced|strong] [--checkpoint-ui off|interactive|strong] [--workspace create|later] [--project-dir <path>] [--json] [--dir <path>] [--skills-dir <path>] [--runtime-path <file>] [--setup-path <file>]",
|
|
139
140
|
" longtable init [deprecated alias for setup; full legacy flags still supported for automation]",
|
|
@@ -169,6 +170,7 @@ function usage() {
|
|
|
169
170
|
" longtable codex install-hooks [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
170
171
|
" longtable codex remove-hooks [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
171
172
|
" longtable codex status [--surface compact|full] [--dir <path>] [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
173
|
+
" longtable codex hook-doctor [--cwd <path>] [--codex-config <path>] [--hooks-path <path>] [--json]",
|
|
172
174
|
" longtable claude install-skills [--surface compact|full] [--dir <path>]",
|
|
173
175
|
" longtable claude remove-skills [--dir <path>]",
|
|
174
176
|
" longtable claude status [--surface compact|full] [--dir <path>] [--json]",
|
|
@@ -178,7 +180,7 @@ function usage() {
|
|
|
178
180
|
"Examples:",
|
|
179
181
|
" longtable setup --provider codex",
|
|
180
182
|
" cd \"<research-folder>\" && codex",
|
|
181
|
-
" $longtable-
|
|
183
|
+
" $longtable-start",
|
|
182
184
|
" longtable start --no-interview --path ~/Research/My-Project --name \"AI Adoption Meta-Analysis\" --goal \"Narrow the review question\"",
|
|
183
185
|
" longtable doctor",
|
|
184
186
|
" longtable roles",
|
|
@@ -525,12 +527,12 @@ function buildPermissionSetupChoices() {
|
|
|
525
527
|
{
|
|
526
528
|
id: "create",
|
|
527
529
|
label: "Show interview launch steps",
|
|
528
|
-
description: "Why: research should start inside the provider. What you get: setup finishes with Codex/Claude + $longtable-
|
|
530
|
+
description: "Why: research should start inside the provider. What you get: setup finishes with Codex/Claude + $longtable-start steps. Tradeoff: workspace creation waits for the in-provider interview."
|
|
529
531
|
},
|
|
530
532
|
{
|
|
531
533
|
id: "later",
|
|
532
534
|
label: "No, prepare runtime only",
|
|
533
|
-
description: "Why: keeps setup short. What you get: runtime support without project state. Tradeoff: no durable research memory until `$longtable-
|
|
535
|
+
description: "Why: keeps setup short. What you get: runtime support without project state. Tradeoff: no durable research memory until `$longtable-start` creates or resumes a workspace."
|
|
534
536
|
}
|
|
535
537
|
]
|
|
536
538
|
};
|
|
@@ -652,7 +654,7 @@ async function runSetup(args) {
|
|
|
652
654
|
interventionPosture: effectiveIntervention,
|
|
653
655
|
checkpointUiMode: checkpointUi,
|
|
654
656
|
workspaceCreationPreference: workspacePreference,
|
|
655
|
-
officialStartSurface: "$longtable-
|
|
657
|
+
officialStartSurface: "$longtable-start",
|
|
656
658
|
setupPosture: "permission_first",
|
|
657
659
|
teamMode: "panel"
|
|
658
660
|
};
|
|
@@ -698,9 +700,9 @@ async function runSetup(args) {
|
|
|
698
700
|
mcpInstall,
|
|
699
701
|
workspacePreference,
|
|
700
702
|
nextStep: {
|
|
701
|
-
surface: "$longtable-
|
|
703
|
+
surface: "$longtable-start",
|
|
702
704
|
command: provider === "codex" ? "codex" : "claude",
|
|
703
|
-
description: "Open the provider in the research folder and invoke `$longtable-
|
|
705
|
+
description: "Open the provider in the research folder and invoke `$longtable-start`."
|
|
704
706
|
}
|
|
705
707
|
}, null, 2));
|
|
706
708
|
return;
|
|
@@ -734,7 +736,7 @@ async function runSetup(args) {
|
|
|
734
736
|
console.log(renderInterviewLaunchSteps(provider));
|
|
735
737
|
if (workspacePreference === "create") {
|
|
736
738
|
console.log("");
|
|
737
|
-
console.log("Workspace launch requested. Open the provider in your research folder and run `$longtable-
|
|
739
|
+
console.log("Workspace launch requested. Open the provider in your research folder and run `$longtable-start`; the interview will create `.longtable/` there.");
|
|
738
740
|
}
|
|
739
741
|
}
|
|
740
742
|
function perspectiveChoices() {
|
|
@@ -1527,6 +1529,22 @@ function setupForProvider(setup, provider) {
|
|
|
1527
1529
|
}
|
|
1528
1530
|
};
|
|
1529
1531
|
}
|
|
1532
|
+
async function collectHardStopDiagnostics(startPath) {
|
|
1533
|
+
const empty = {
|
|
1534
|
+
stopWouldBlock: false,
|
|
1535
|
+
activeBlockers: [],
|
|
1536
|
+
staleOrUnrelatedPendingQuestionCount: 0,
|
|
1537
|
+
stalePendingQuestionCount: 0,
|
|
1538
|
+
stalePendingObligationCount: 0,
|
|
1539
|
+
nextActions: []
|
|
1540
|
+
};
|
|
1541
|
+
const context = await loadProjectContextFromDirectory(startPath);
|
|
1542
|
+
if (!context) {
|
|
1543
|
+
return empty;
|
|
1544
|
+
}
|
|
1545
|
+
const state = await loadWorkspaceState(context);
|
|
1546
|
+
return collectHardStopBlockers(state);
|
|
1547
|
+
}
|
|
1530
1548
|
async function collectDoctorStatus(args) {
|
|
1531
1549
|
const roles = listRoleDefinitions();
|
|
1532
1550
|
const skillSurface = parseSkillSurface(args);
|
|
@@ -1579,11 +1597,13 @@ async function collectDoctorStatus(args) {
|
|
|
1579
1597
|
: [];
|
|
1580
1598
|
const expectedCodexSkills = buildCodexSkillSpecs(roles, skillSurface).map((skill) => skill.name);
|
|
1581
1599
|
const expectedClaudeSkills = buildClaudeSkillSpecs(roles, skillSurface).map((skill) => skill.name);
|
|
1582
|
-
const
|
|
1600
|
+
const workspacePath = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
1601
|
+
const [codexSkills, claudeSkills, codexAliases, workspace, hardStop] = await Promise.all([
|
|
1583
1602
|
listInstalledCodexSkills(roles, codexDir, skillSurface),
|
|
1584
1603
|
listInstalledClaudeSkills(roles, claudeDir, skillSurface),
|
|
1585
1604
|
listInstalledCodexPromptAliases(codexPromptsDir),
|
|
1586
|
-
inspectProjectWorkspace(
|
|
1605
|
+
inspectProjectWorkspace(workspacePath),
|
|
1606
|
+
collectHardStopDiagnostics(workspacePath)
|
|
1587
1607
|
]);
|
|
1588
1608
|
const installedCodexSkills = codexSkills.map((skill) => skill.name);
|
|
1589
1609
|
const installedClaudeSkills = claudeSkills.map((skill) => skill.name);
|
|
@@ -1614,7 +1634,12 @@ async function collectDoctorStatus(args) {
|
|
|
1614
1634
|
hooksExists: existsSync(codexHooksPath),
|
|
1615
1635
|
codexHooksEnabled: codexHooksEnabled(codexMcpConfig),
|
|
1616
1636
|
missingManagedHookEvents,
|
|
1617
|
-
missingManagedHookTrustState
|
|
1637
|
+
missingManagedHookTrustState,
|
|
1638
|
+
stopWouldBlock: hardStop.stopWouldBlock,
|
|
1639
|
+
activeBlockers: hardStop.activeBlockers,
|
|
1640
|
+
stalePendingQuestionCount: hardStop.stalePendingQuestionCount,
|
|
1641
|
+
stalePendingObligationCount: hardStop.stalePendingObligationCount,
|
|
1642
|
+
nextActions: hardStop.nextActions
|
|
1618
1643
|
},
|
|
1619
1644
|
claude: {
|
|
1620
1645
|
command: "claude",
|
|
@@ -1627,7 +1652,8 @@ async function collectDoctorStatus(args) {
|
|
|
1627
1652
|
missingSkills: missingNames(expectedClaudeSkills, installedClaudeSkills)
|
|
1628
1653
|
}
|
|
1629
1654
|
},
|
|
1630
|
-
workspace
|
|
1655
|
+
workspace,
|
|
1656
|
+
hardStop
|
|
1631
1657
|
};
|
|
1632
1658
|
}
|
|
1633
1659
|
function renderProviderDoctorBlock(label, provider) {
|
|
@@ -1665,6 +1691,9 @@ function renderDoctorStatus(status) {
|
|
|
1665
1691
|
`- hooks feature: ${status.providers.codex.codexHooksEnabled ? "enabled" : "missing"}`,
|
|
1666
1692
|
`- managed hook coverage: ${status.providers.codex.missingManagedHookEvents.length === 0 ? "complete" : `missing ${status.providers.codex.missingManagedHookEvents.join(", ")}`}`,
|
|
1667
1693
|
`- managed hook trust: ${status.providers.codex.missingManagedHookTrustState.length === 0 ? "current" : `missing/stale ${status.providers.codex.missingManagedHookTrustState.length}`}`,
|
|
1694
|
+
`- Stop would block now: ${status.hardStop.stopWouldBlock ? "yes" : "no"}`,
|
|
1695
|
+
`- active hard-stop blockers: ${status.hardStop.activeBlockers.length}`,
|
|
1696
|
+
`- stale/unrelated pending questions: ${status.hardStop.staleOrUnrelatedPendingQuestionCount}`,
|
|
1668
1697
|
"",
|
|
1669
1698
|
...renderProviderDoctorBlock("Claude", status.providers.claude),
|
|
1670
1699
|
"",
|
|
@@ -1675,7 +1704,7 @@ function renderDoctorStatus(status) {
|
|
|
1675
1704
|
}
|
|
1676
1705
|
else {
|
|
1677
1706
|
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}`);
|
|
1707
|
+
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
1708
|
if ((workspace.recentInvocations ?? []).length > 0) {
|
|
1680
1709
|
lines.push("- recent invocations:");
|
|
1681
1710
|
for (const invocation of workspace.recentInvocations ?? []) {
|
|
@@ -1689,12 +1718,25 @@ function renderDoctorStatus(status) {
|
|
|
1689
1718
|
lines.push(` - ${question.id}: ${question.question} (${question.options.join("/")})`);
|
|
1690
1719
|
}
|
|
1691
1720
|
}
|
|
1721
|
+
if (status.hardStop.activeBlockers.length > 0) {
|
|
1722
|
+
lines.push("- active hard-stop blockers:");
|
|
1723
|
+
for (const blocker of status.hardStop.activeBlockers) {
|
|
1724
|
+
lines.push(` - ${blocker.id}: ${blocker.scope} (${blocker.type})`);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1692
1727
|
if ((workspace.pendingObligations ?? []).length > 0) {
|
|
1693
1728
|
lines.push("- pending obligations:");
|
|
1694
1729
|
for (const obligation of workspace.pendingObligations ?? []) {
|
|
1695
1730
|
lines.push(` - ${obligation.id}: ${obligation.prompt}`);
|
|
1696
1731
|
}
|
|
1697
1732
|
}
|
|
1733
|
+
if ((workspace.hardStop?.activeBlockers ?? []).length > 0) {
|
|
1734
|
+
lines.push("- hard-stop blockers:");
|
|
1735
|
+
for (const blocker of workspace.hardStop?.activeBlockers ?? []) {
|
|
1736
|
+
lines.push(` - ${blocker.id} [${blocker.scope}]: ${blocker.prompt}`);
|
|
1737
|
+
lines.push(` next: ${blocker.commandHint}`);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1698
1740
|
if ((workspace.answerWarnings ?? []).length > 0) {
|
|
1699
1741
|
lines.push("- answer warnings:");
|
|
1700
1742
|
for (const warning of workspace.answerWarnings ?? []) {
|
|
@@ -1736,10 +1778,16 @@ function renderDoctorStatus(status) {
|
|
|
1736
1778
|
if (!status.workspace.found) {
|
|
1737
1779
|
nextActions.push("longtable start");
|
|
1738
1780
|
}
|
|
1781
|
+
nextActions.push(...status.hardStop.nextActions);
|
|
1739
1782
|
const firstQuestion = status.workspace.pendingQuestions?.[0];
|
|
1740
|
-
if (firstQuestion) {
|
|
1783
|
+
if (firstQuestion && status.hardStop.nextActions.length === 0) {
|
|
1741
1784
|
nextActions.push(`longtable decide --question ${firstQuestion.id} --answer <value>`);
|
|
1742
1785
|
}
|
|
1786
|
+
for (const action of status.workspace.hardStop?.nextActions ?? []) {
|
|
1787
|
+
if (!nextActions.includes(action)) {
|
|
1788
|
+
nextActions.push(action);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1743
1791
|
if (nextActions.length > 0) {
|
|
1744
1792
|
lines.push("", "Next actions:");
|
|
1745
1793
|
for (const action of nextActions) {
|
|
@@ -2009,6 +2057,7 @@ function buildRoleAuditEntry(provider, spec) {
|
|
|
2009
2057
|
function runRoleAudit() {
|
|
2010
2058
|
const baseSkillNames = new Set([
|
|
2011
2059
|
"longtable",
|
|
2060
|
+
"longtable-start",
|
|
2012
2061
|
"longtable-interview",
|
|
2013
2062
|
"longtable-panel",
|
|
2014
2063
|
"longtable-explore",
|
|
@@ -2812,7 +2861,7 @@ async function runAccess(subcommand, args) {
|
|
|
2812
2861
|
await runAccessSetup(args);
|
|
2813
2862
|
return;
|
|
2814
2863
|
}
|
|
2815
|
-
if (subcommand === "status") {
|
|
2864
|
+
if (subcommand === "status" || subcommand === "hook-doctor") {
|
|
2816
2865
|
await runAccessStatus(args);
|
|
2817
2866
|
return;
|
|
2818
2867
|
}
|
|
@@ -3624,7 +3673,7 @@ async function runStart(args) {
|
|
|
3624
3673
|
"1. longtable setup --provider codex",
|
|
3625
3674
|
"2. cd \"<research-folder>\"",
|
|
3626
3675
|
"3. codex",
|
|
3627
|
-
"4. $longtable-
|
|
3676
|
+
"4. $longtable-start",
|
|
3628
3677
|
"",
|
|
3629
3678
|
"For automation, pass `--no-interview --json` with `--name`, `--path`, and `--goal`."
|
|
3630
3679
|
]));
|
|
@@ -3767,7 +3816,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3767
3816
|
console.log(renderCodexHookInstallSummary(result));
|
|
3768
3817
|
return;
|
|
3769
3818
|
}
|
|
3770
|
-
if (subcommand === "status") {
|
|
3819
|
+
if (subcommand === "status" || subcommand === "hook-doctor") {
|
|
3771
3820
|
const aliases = await listInstalledCodexPromptAliases(customDir);
|
|
3772
3821
|
const skills = await listInstalledCodexSkills(roles, customDir, skillSurface);
|
|
3773
3822
|
const setupPath = resolveDefaultSetupPath(typeof args.path === "string" ? args.path : undefined).path;
|
|
@@ -3776,6 +3825,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3776
3825
|
const configContent = existsSync(configPath) ? await readFile(configPath, "utf8") : "";
|
|
3777
3826
|
const hooksPath = resolveCodexHooksPath(args);
|
|
3778
3827
|
const hooksContent = existsSync(hooksPath) ? await readFile(hooksPath, "utf8") : "";
|
|
3828
|
+
const workspace = await inspectProjectWorkspace(typeof args.cwd === "string" ? args.cwd : cwd());
|
|
3779
3829
|
const status = {
|
|
3780
3830
|
setupPath,
|
|
3781
3831
|
setupExists: existsSync(setupPath),
|
|
@@ -3795,13 +3845,21 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3795
3845
|
: [...LONGTABLE_MANAGED_HOOK_EVENTS],
|
|
3796
3846
|
missingManagedHookTrustState: hooksContent
|
|
3797
3847
|
? getMissingManagedCodexHookTrustState(configContent, hooksPath, hooksContent)
|
|
3798
|
-
: []
|
|
3848
|
+
: [],
|
|
3849
|
+
workspaceHardStop: workspace.hardStop ?? {
|
|
3850
|
+
stopWouldBlock: false,
|
|
3851
|
+
activeBlockers: [],
|
|
3852
|
+
staleOrUnrelatedPendingQuestionCount: 0,
|
|
3853
|
+
stalePendingQuestionCount: 0,
|
|
3854
|
+
stalePendingObligationCount: 0,
|
|
3855
|
+
nextActions: []
|
|
3856
|
+
}
|
|
3799
3857
|
};
|
|
3800
3858
|
if (args.json === true) {
|
|
3801
3859
|
console.log(JSON.stringify(status, null, 2));
|
|
3802
3860
|
return;
|
|
3803
3861
|
}
|
|
3804
|
-
console.log("LongTable Codex status");
|
|
3862
|
+
console.log(subcommand === "hook-doctor" ? "LongTable Codex hook doctor" : "LongTable Codex status");
|
|
3805
3863
|
console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
|
|
3806
3864
|
console.log(`- codex runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
|
|
3807
3865
|
console.log(`- skills dir: ${status.skillsDir}`);
|
|
@@ -3831,6 +3889,9 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
3831
3889
|
console.log(`- hooks file: ${status.hooksExists ? "present" : "missing"} (${status.hooksPath})`);
|
|
3832
3890
|
console.log(`- managed hook coverage: ${status.missingManagedHookEvents.length === 0 ? "complete" : `missing ${status.missingManagedHookEvents.join(", ")}`}`);
|
|
3833
3891
|
console.log(`- managed hook trust: ${status.missingManagedHookTrustState.length === 0 ? "current" : `missing/stale ${status.missingManagedHookTrustState.length}`}`);
|
|
3892
|
+
console.log(`- Stop hard-stop: ${status.workspaceHardStop.stopWouldBlock ? "would block" : "clear"}`);
|
|
3893
|
+
console.log(`- active hard-stop blockers: ${status.workspaceHardStop.activeBlockers.length}`);
|
|
3894
|
+
console.log(`- stale/unrelated pending: ${status.workspaceHardStop.stalePendingQuestionCount} questions, ${status.workspaceHardStop.stalePendingObligationCount} obligations`);
|
|
3834
3895
|
return;
|
|
3835
3896
|
}
|
|
3836
3897
|
throw new Error("Unknown codex subcommand.");
|
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: [
|
|
@@ -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
|
}
|
|
@@ -168,15 +172,15 @@ function looksLikeExplicitInterviewPrompt(prompt) {
|
|
|
168
172
|
if (!normalized) {
|
|
169
173
|
return false;
|
|
170
174
|
}
|
|
171
|
-
if (/\$longtable-interview\b/i.test(normalized)) {
|
|
175
|
+
if (/\$longtable-(?:start|interview)\b/i.test(normalized)) {
|
|
172
176
|
return true;
|
|
173
177
|
}
|
|
174
178
|
if (looksLikeLongTableProductOrToolingPrompt(normalized)) {
|
|
175
179
|
return false;
|
|
176
180
|
}
|
|
177
|
-
return /\bLongTable\b.*\
|
|
181
|
+
return /\bLongTable\b.*\b(?:start|interview)\b/i.test(normalized)
|
|
178
182
|
|| /\bfirst research shape\b/i.test(normalized)
|
|
179
|
-
||
|
|
183
|
+
|| /롱테이블.*(?:시작|인터뷰)|LongTable.*인터뷰|First Research Shape/i.test(normalized);
|
|
180
184
|
}
|
|
181
185
|
function looksLikeResearchStateConfirmationPrompt(prompt) {
|
|
182
186
|
if (looksLikeLongTableProductOrToolingPrompt(prompt) && !looksLikeExplicitInterviewPrompt(prompt)) {
|
|
@@ -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);
|
|
@@ -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"];
|
|
@@ -339,7 +340,7 @@ function buildCurrentGuide(project, session, recentInvocations = [], pendingQues
|
|
|
339
340
|
"",
|
|
340
341
|
"## 빠른 시작",
|
|
341
342
|
"- 이 디렉토리에서 `codex`를 엽니다.",
|
|
342
|
-
`- 첫 메시지는 보통 \`${session.firstResearchShape ? suggestedPrompt : "$longtable-
|
|
343
|
+
`- 첫 메시지는 보통 \`${session.firstResearchShape ? suggestedPrompt : "$longtable-start"}\` 정도면 충분합니다.`,
|
|
343
344
|
"",
|
|
344
345
|
"## 증거 규칙",
|
|
345
346
|
"- 외부 사실이나 현재 정보는 source를 붙이거나 inference로 낮춥니다."
|
|
@@ -416,7 +417,7 @@ function buildCurrentGuide(project, session, recentInvocations = [], pendingQues
|
|
|
416
417
|
"",
|
|
417
418
|
"## Quick Start",
|
|
418
419
|
"- Open `codex` in this directory.",
|
|
419
|
-
`- A good first message is usually \`${session.firstResearchShape ? suggestedPrompt : "$longtable-
|
|
420
|
+
`- A good first message is usually \`${session.firstResearchShape ? suggestedPrompt : "$longtable-start"}\`.`,
|
|
420
421
|
"",
|
|
421
422
|
"## Evidence Rule",
|
|
422
423
|
"- External or current claims should carry a source link or be labeled as inference."
|
|
@@ -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) => ({
|
|
@@ -878,7 +887,8 @@ function buildProjectAgentsMd(project, session) {
|
|
|
878
887
|
"- Treat `AGENTS.md` as runtime guidance, not as the researcher-facing resume artifact.",
|
|
879
888
|
"",
|
|
880
889
|
"## Invocation Rules",
|
|
881
|
-
"- If the user message starts with `$longtable-
|
|
890
|
+
"- If the user message starts with `$longtable-start`, run the LongTable research-start flow before generic research advice.",
|
|
891
|
+
"- If the user message starts with `$longtable-interview`, use post-start structured interview only when a usable Research Specification exists; otherwise route to `$longtable-start`.",
|
|
882
892
|
"- If the user message starts with `lt `, `longtable `, `long table `, or `롱테이블 ` followed by a directive and `:`, treat it as an explicit LongTable invocation.",
|
|
883
893
|
"- Supported explicit directives are: interview, explore, review, critique, draft, commit, panel, status, editor, reviewer, methods, theory, measurement, ethics, voice, venue.",
|
|
884
894
|
"- For explicit LongTable invocations, do not begin by scanning the workspace. Use the current session files first and answer as LongTable immediately.",
|
|
@@ -886,13 +896,14 @@ function buildProjectAgentsMd(project, session) {
|
|
|
886
896
|
"",
|
|
887
897
|
"## Research Behavior",
|
|
888
898
|
"- Begin exploratory work with clarifying or tension questions before recommending a direction.",
|
|
889
|
-
"- For `$longtable-
|
|
890
|
-
"- Do not summarize `$longtable-
|
|
899
|
+
"- For `$longtable-start`, ask one natural-language question at a time, reflect with `LongTable hears: ...`, record turns when MCP is available, and avoid early reader/reviewer or theory/method/measurement classification.",
|
|
900
|
+
"- Do not summarize `$longtable-start` because a fixed number of turns has passed; wait for content-based readiness around research object, focal uncertainty, boundary, evidence/material, protected decision, and next action.",
|
|
891
901
|
"- First Research Shape is a short handle/resume index, not the default closure point.",
|
|
892
902
|
"- After the First Research Shape, create a Research Specification when the interview has enough detail to preserve scope, construct ontology, theory framing, coding rules, method options, evidence/access requirements, epistemic alignment, protected decisions, open questions, and next actions.",
|
|
893
903
|
"- If a confirmed First Research Shape exists without a Research Specification, continue directly into the next Research Specification question instead of asking shape-level continue/revise/restart questions.",
|
|
894
904
|
"- If the researcher chooses `ask_one_more` or `revise_section` at Research Specification confirmation, answer that gap and return to the Research Specification Preview before ending the interview.",
|
|
895
|
-
"- Do not let unrelated pending Researcher Checkpoints interrupt `$longtable-
|
|
905
|
+
"- Do not let unrelated pending Researcher Checkpoints interrupt `$longtable-start`; mention them only as separate unresolved checkpoints unless the researcher is confirming, saving, or recording a research decision.",
|
|
906
|
+
"- For `$longtable-interview` after a Research Specification exists, use option-first follow-up choices with Other/free-text or one open-question escape hatch.",
|
|
896
907
|
"- Use structured options at the final Research Specification confirmation, at explicit short-handle stop points, or at true checkpoint boundaries.",
|
|
897
908
|
"- If you foreground role perspectives, disclose them with `LongTable consulted: ...`.",
|
|
898
909
|
"- Keep one accountable synthesis, but do not hide meaningful disagreement.",
|
|
@@ -1195,7 +1206,7 @@ export async function beginLongTableInterview(options) {
|
|
|
1195
1206
|
turns: [],
|
|
1196
1207
|
qualityNotes: [],
|
|
1197
1208
|
rationale: [
|
|
1198
|
-
"Official LongTable research start surface is provider-native `$longtable-
|
|
1209
|
+
"Official LongTable research start surface is provider-native `$longtable-start`, not the CLI start questionnaire.",
|
|
1199
1210
|
"The hook keeps early research ambiguity open until a first research handle can be summarized."
|
|
1200
1211
|
]
|
|
1201
1212
|
};
|
|
@@ -1203,7 +1214,7 @@ export async function beginLongTableInterview(options) {
|
|
|
1203
1214
|
updated.workingState = {
|
|
1204
1215
|
...updated.workingState,
|
|
1205
1216
|
activeInterviewHookId: hook.id,
|
|
1206
|
-
interviewSurface: "$longtable-
|
|
1217
|
+
interviewSurface: "$longtable-start",
|
|
1207
1218
|
...(options.openingQuestion ? { interviewOpeningQuestion: options.openingQuestion } : {}),
|
|
1208
1219
|
...(options.seedAnswer ? { interviewSeedAnswer: options.seedAnswer } : {})
|
|
1209
1220
|
};
|
|
@@ -1335,7 +1346,7 @@ export async function summarizeLongTableInterview(options) {
|
|
|
1335
1346
|
updated.narrativeTraces.push({
|
|
1336
1347
|
id: createId("narrative_trace"),
|
|
1337
1348
|
timestamp,
|
|
1338
|
-
source: "$longtable-
|
|
1349
|
+
source: "$longtable-start",
|
|
1339
1350
|
traceType: "judgment",
|
|
1340
1351
|
summary: `First Research Shape: ${shape.handle}.`,
|
|
1341
1352
|
visibility: "explicit",
|
|
@@ -1457,7 +1468,7 @@ export async function summarizeLongTableResearchSpecification(options) {
|
|
|
1457
1468
|
updated.narrativeTraces.push({
|
|
1458
1469
|
id: createId("narrative_trace"),
|
|
1459
1470
|
timestamp,
|
|
1460
|
-
source: "$longtable-
|
|
1471
|
+
source: "$longtable-start",
|
|
1461
1472
|
traceType: "judgment",
|
|
1462
1473
|
summary: `Research Specification draft: ${specification.title}.`,
|
|
1463
1474
|
visibility: "explicit",
|
|
@@ -2303,12 +2314,45 @@ function inferEpistemicBasis(input) {
|
|
|
2303
2314
|
return "mixed";
|
|
2304
2315
|
return unique[0];
|
|
2305
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
|
+
}
|
|
2306
2348
|
function resolveQuestionRecordMetadata(input) {
|
|
2307
2349
|
const commitmentFamily = input.commitmentFamily ?? inferCommitmentFamily(input);
|
|
2308
2350
|
const epistemicBasis = input.epistemicBasis ?? inferEpistemicBasis(input);
|
|
2351
|
+
const hardStopScope = inferHardStopScope(input, commitmentFamily);
|
|
2309
2352
|
return {
|
|
2310
2353
|
...(commitmentFamily ? { commitmentFamily } : {}),
|
|
2311
|
-
...(epistemicBasis ? { epistemicBasis } : {})
|
|
2354
|
+
...(epistemicBasis ? { epistemicBasis } : {}),
|
|
2355
|
+
...(hardStopScope ? { hardStop: true, hardStopScope } : {})
|
|
2312
2356
|
};
|
|
2313
2357
|
}
|
|
2314
2358
|
function hasFollowUpPrompt(record, prompt) {
|
|
@@ -2369,6 +2413,8 @@ export async function createWorkspaceFollowUpQuestions(options) {
|
|
|
2369
2413
|
updatedAt: createdAt,
|
|
2370
2414
|
status: "pending",
|
|
2371
2415
|
...metadata,
|
|
2416
|
+
...(typeof options.hardStop === "boolean" ? { hardStop: options.hardStop } : {}),
|
|
2417
|
+
...(options.hardStopScope ? { hardStopScope: options.hardStopScope } : {}),
|
|
2372
2418
|
prompt: {
|
|
2373
2419
|
id: createId("question_prompt"),
|
|
2374
2420
|
checkpointKey,
|
|
@@ -2423,6 +2469,8 @@ export async function createWorkspaceQuestion(options) {
|
|
|
2423
2469
|
updatedAt: createdAt,
|
|
2424
2470
|
status: "pending",
|
|
2425
2471
|
...metadata,
|
|
2472
|
+
...(typeof options.hardStop === "boolean" ? { hardStop: options.hardStop } : {}),
|
|
2473
|
+
...(options.hardStopScope ? { hardStopScope: options.hardStopScope } : {}),
|
|
2426
2474
|
prompt: {
|
|
2427
2475
|
id: createId("question_prompt"),
|
|
2428
2476
|
checkpointKey,
|
package/dist/prompt-aliases.js
CHANGED
|
@@ -29,7 +29,7 @@ function promptSpec() {
|
|
|
29
29
|
argumentHint: "[project context or current uncertainty]",
|
|
30
30
|
body: [
|
|
31
31
|
"You are LongTable setup guidance inside Codex.",
|
|
32
|
-
"Treat `longtable init` as deprecated. Prefer `longtable setup --provider codex` for runtime permissions, then `$longtable-
|
|
32
|
+
"Treat `longtable init` as deprecated. Prefer `longtable setup --provider codex` for runtime permissions, then `$longtable-start` inside Codex for the project interview.",
|
|
33
33
|
"Ask exactly one setup question at a time.",
|
|
34
34
|
"Use numbered choices and include a concise Why / What you get / Tradeoff for each option.",
|
|
35
35
|
"Do not move to the next question until the researcher answers the current one.",
|
|
@@ -37,10 +37,10 @@ function promptSpec() {
|
|
|
37
37
|
"Do not ask for field, career stage, experience level, authorship signal, weakest domain, or panel preference during runtime setup.",
|
|
38
38
|
"Project interview covers: the first research handle, early uncertainty, first inspectable material, and final structured confirmation.",
|
|
39
39
|
"After collecting runtime answers, summarize the proposed setup and output the exact `longtable setup --provider codex ...` command.",
|
|
40
|
-
"If the researcher is ready to create a project workspace, tell them to open Codex in the research folder and invoke `$longtable-
|
|
40
|
+
"If the researcher is ready to create a project workspace, tell them to open Codex in the research folder and invoke `$longtable-start`.",
|
|
41
41
|
"If the researcher asks you to stay inside Codex, keep the conversation in numbered form and do not prematurely close.",
|
|
42
42
|
"Frame setup as permission and intervention calibration, not a researcher profile interview.",
|
|
43
|
-
"Do not pretend setup is the full project-start interview. The project-start interview happens in `$longtable-
|
|
43
|
+
"Do not pretend setup is the full project-start interview. The project-start interview happens in `$longtable-start`.",
|
|
44
44
|
"Treat any slash-command arguments as context for why setup is being done now."
|
|
45
45
|
]
|
|
46
46
|
},
|
|
@@ -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.52",
|
|
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.52",
|
|
33
|
+
"@longtable/core": "0.1.52",
|
|
34
|
+
"@longtable/memory": "0.1.52",
|
|
35
|
+
"@longtable/provider-claude": "0.1.52",
|
|
36
|
+
"@longtable/provider-codex": "0.1.52",
|
|
37
|
+
"@longtable/setup": "0.1.52"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.1",
|