@longtable/cli 0.1.2 → 0.1.4

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 CHANGED
@@ -1,107 +1,113 @@
1
1
  # @longtable/cli
2
2
 
3
- Researcher-facing Long Table CLI built on top of the legacy `@diverga/*` package surface.
3
+ Long Table researcher-facing CLI입니다.
4
4
 
5
- 현재 공개 설치 경로는 `@longtable/cli`입니다.
5
+ 패키지의 핵심은 명령을 많이 외우게 하는 것이 아니라, 아래 두 단계를 명확하게 만드는 것입니다.
6
6
 
7
- ## Install
7
+ 1. `longtable init`
8
+ 2. `longtable start`
9
+
10
+ 중요한 점:
8
11
 
9
- 공개 설치:
12
+ - 이 두 명령은 **터미널에서 실행하는 셸 명령**입니다.
13
+ - Codex 채팅창에 `longtable start`를 입력하는 것이 아닙니다.
14
+ - `longtable start`로 프로젝트 작업공간을 만든 뒤, 그 디렉토리에서 `codex`를 여는 것이 기본 경로입니다.
15
+
16
+ ## Install
10
17
 
11
18
  ```bash
12
19
  npm install -g @longtable/cli
13
20
  ```
14
21
 
15
- 로컬 preview:
22
+ ## 1. Global setup
16
23
 
17
24
  ```bash
18
- npm install
19
- npm run build
20
- node packages/longtable/dist/cli.js --help
25
+ longtable init --flow interview
21
26
  ```
22
27
 
23
- ## Recommended flow
28
+ 여기서는 연구자 프로필과 기본 선호를 묻습니다.
24
29
 
25
- Run setup once and install Codex prompt aliases:
30
+ - 연구 분야
31
+ - 연구자 역할
32
+ - challenge 강도
33
+ - 저자성/서사 관련 기본값
34
+ - Long Table이 보통 어디서부터 시작해야 하는지
35
+
36
+ ## 2. Project start
26
37
 
27
38
  ```bash
28
- longtable init --flow interview --install-prompts
39
+ longtable start
29
40
  ```
30
41
 
31
- `longtable init` now uses an arrow-key terminal menu instead of plain number-entry prompts and supports:
42
+ 여기서는 실제 작업공간을 만듭니다.
43
+
44
+ - 프로젝트 이름
45
+ - 프로젝트 디렉토리 위치
46
+ - 현재 세션 목표
47
+ - 현재 blocker
48
+ - 필요한 관점
49
+ - disagreement 가시성
32
50
 
33
- - `--flow quickstart`
34
- - `--flow interview`
51
+ 그리고 Long Table은:
35
52
 
36
- Then you can work in two ways.
53
+ - 프로젝트 디렉토리 생성
54
+ - `.longtable/` 메모리 파일 생성
55
+ - 프로젝트용 `AGENTS.md` 생성
37
56
 
38
- From the terminal:
57
+ 수행합니다.
58
+
59
+ ## Recommended flow
39
60
 
40
61
  ```bash
41
- longtable ask --prompt "연구를 시작하고 싶어. 지금 어디서부터 좁혀야 할지 모르겠어."
42
- longtable review --prompt "Review this claim critically."
43
- longtable review --prompt "BJET 편집자 관점에서 봐줘." --role editor
44
- longtable review --prompt "방법론적으로 어디가 취약한지 말해줘." --panel --show-conflicts
62
+ longtable init --flow interview
63
+ longtable start
64
+ cd "<project-path>"
65
+ codex
45
66
  ```
46
67
 
47
- Inside Codex after prompt aliases are installed:
68
+ 이게 현재 가장 신뢰할 있는 Long Table 사용 경로입니다.
48
69
 
49
- - `/prompts:longtable`
50
- - `/prompts:longtable-init`
51
- - `/prompts:longtable-explore`
52
- - `/prompts:longtable-review`
53
- - `/prompts:longtable-critique`
54
- - `/prompts:longtable-draft`
55
- - `/prompts:longtable-commit`
56
- - `/prompts:longtable-status`
70
+ ## After the project starts
57
71
 
58
- ## Core commands
72
+ 프로젝트 디렉토리 안에서는 보통 그냥 `codex`를 열고 자연어로 연구를 시작하면 됩니다.
59
73
 
60
- ```bash
61
- longtable ask --prompt "Help me open this research problem carefully."
62
- longtable init
63
- longtable show --json
64
- longtable install --json
65
- longtable explore --prompt "Help me stay exploratory."
66
- longtable review --prompt "Review this claim critically."
67
- longtable commit --prompt "Help me make this decision carefully."
68
- ```
69
-
70
- ## Codex overlay
74
+ CLI를 계속 쓰고 싶다면 아래 명령은 보조 경로입니다.
71
75
 
72
- Install Codex prompt aliases:
76
+ ## Advanced commands
73
77
 
74
78
  ```bash
75
- longtable codex install-prompts
79
+ longtable roles
80
+ longtable ask --cwd "<project-path>" --prompt "연구 질문을 어디서부터 좁혀야 할지 모르겠어."
81
+ longtable review --cwd "<project-path>" --prompt "방법론적으로 어디가 취약한지 말해줘." --role methods_critic
76
82
  ```
77
83
 
78
- If you finish onboarding inside Codex with `/prompts:longtable-init`, you can persist the collected answers with:
84
+ ## Roles
79
85
 
80
86
  ```bash
81
- longtable codex persist-init --flow interview --provider codex --field education --career-stage doctoral --experience intermediate --project-type "journal article" --checkpoint balanced --topic "AI adoption in workplace settings" --blocker "기업 맥락으로 범위를 다시 잡아야 하는데 어디서부터 포함/제외 기준을 세울지 모르겠어." --entry-mode explore --weakest-domain methodology --panel-preference show_on_conflict --install-prompts
87
+ longtable roles
82
88
  ```
83
89
 
84
- Or from a JSON block:
90
+ 예:
85
91
 
86
- ```bash
87
- pbpaste | longtable codex persist-init --stdin --install-prompts
88
- ```
92
+ - `editor`
93
+ - `reviewer`
94
+ - `methods_critic`
95
+ - `voice_keeper`
89
96
 
90
- Check whether setup and aliases are present:
97
+ ## Codex prompt-file integration
91
98
 
92
99
  ```bash
100
+ longtable codex install-prompts
93
101
  longtable codex status
94
102
  ```
95
103
 
96
- Remove the aliases:
97
-
98
- ```bash
99
- longtable codex remove-prompts
100
- ```
104
+ 경로는 현재 실험적입니다.
105
+ 사용자에게 약속하는 주 경로는 아닙니다.
101
106
 
102
- ## Why this package exists
107
+ 기본 경로는 여전히:
103
108
 
104
- Long Table is the product name.
105
- `Diverga` remains an internal compatibility layer for some lower-level packages and runtime paths.
109
+ - `longtable init`
110
+ - `longtable start`
111
+ - 프로젝트 디렉토리에서 `codex`
106
112
 
107
- This package exists so researchers can install and run `longtable` directly while that compatibility layer remains underneath.
113
+ 입니다.
package/dist/cli.js CHANGED
@@ -3,10 +3,13 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { emitKeypressEvents } from "node:readline";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stdin as input, stdout as output, cwd, exit } from "node:process";
6
+ import { resolve } from "node:path";
6
7
  import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@diverga/setup";
7
8
  import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@diverga/provider-codex";
8
9
  import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodexPromptAliases, resolveCodexPromptsDir } from "./prompt-aliases.js";
9
10
  import { buildPersonaGuidance } from "./persona-router.js";
11
+ import { PERSONA_DEFINITIONS } from "./personas.js";
12
+ import { createOrUpdateProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary } from "./project-session.js";
10
13
  const VALID_MODES = new Set([
11
14
  "explore",
12
15
  "review",
@@ -27,7 +30,12 @@ const VALID_STAGES = new Set([
27
30
  function usage() {
28
31
  return [
29
32
  "Usage:",
30
- " longtable init [--flow quickstart|interview] [--provider codex|claude] [--field <field>] [--career-stage <stage>] [--experience novice|intermediate|advanced] [--project-type <type>] [--checkpoint low|balanced|high] [--authorship-signal <text>] [--topic <text>] [--blocker <text>] [--entry-mode explore|review|critique|draft|commit] [--weakest-domain theory|methodology|measurement|analysis|writing] [--panel-preference synthesis_only|show_on_conflict|always_visible] [--json] [--no-install] [--install-prompts]",
33
+ " Run `longtable ...` in your terminal, not inside the Codex chat box.",
34
+ " After `longtable start`, move into the created project directory and open `codex` there.",
35
+ "",
36
+ " longtable init [--flow quickstart|interview] [--provider codex|claude] [--field <field>] [--career-stage <stage>] [--experience novice|intermediate|advanced] [--checkpoint low|balanced|high] [--authorship-signal <text>] [--entry-mode explore|review|critique|draft|commit] [--weakest-domain theory|methodology|measurement|analysis|writing] [--panel-preference synthesis_only|show_on_conflict|always_visible] [--json] [--no-install] [--install-prompts]",
37
+ " longtable start [--path <dir>] [--name <project>] [--goal <text>] [--blocker <text>] [--perspectives <role[,role]>] [--disagreement synthesis_only|show_on_conflict|always_visible] [--setup <path>] [--json]",
38
+ " longtable roles [--json]",
31
39
  " longtable show [--json] [--path <file>]",
32
40
  " longtable install [--json] [--path <file>] [--runtime-path <file>]",
33
41
  " longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
@@ -39,9 +47,11 @@ function usage() {
39
47
  "",
40
48
  "Examples:",
41
49
  " longtable init --flow interview --install-prompts",
50
+ " longtable start",
51
+ " longtable start --path ~/Research/My-Project --name \"AI Adoption Meta-Analysis\" --goal \"Narrow the review question\"",
52
+ " cd \"<project-path>\" && codex",
53
+ " longtable roles",
42
54
  " longtable ask --prompt \"연구를 시작하고 싶어. 지금 어디서부터 좁혀야 할지 모르겠어.\"",
43
- " longtable review --prompt \"Review this claim critically.\" --panel --show-conflicts",
44
- " longtable review --role editor --prompt \"BJET 편집자 관점에서 봐줘.\"",
45
55
  " printf '{\"provider\":\"codex\",...}' | longtable codex persist-init --stdin --install-prompts",
46
56
  " longtable codex install-prompts"
47
57
  ].join("\n");
@@ -51,7 +61,7 @@ function parseArgs(argv) {
51
61
  const values = {};
52
62
  let subcommand = maybeSubcommand;
53
63
  const modeCommand = command && VALID_MODES.has(command);
54
- const directCommand = command && ["init", "show", "install", "codex", "ask"].includes(command);
64
+ const directCommand = command && ["init", "start", "roles", "show", "install", "codex", "ask"].includes(command);
55
65
  let startIndex = 1;
56
66
  if (modeCommand) {
57
67
  subcommand = undefined;
@@ -110,7 +120,7 @@ function buildSetupFlowChoices() {
110
120
  function renderSetupHeader(flow) {
111
121
  const title = flow === "interview" ? "Long Table Setup Interview" : "Long Table Quickstart";
112
122
  const subtitle = flow === "interview"
113
- ? "We will ask about your research persona, current topic, blocker, and preferred kind of challenge."
123
+ ? "We will ask about your research persona, challenge preferences, and authorship defaults."
114
124
  : "We will capture the minimum profile needed to start using Long Table.";
115
125
  return [
116
126
  "┌──────────────────────────────────────────────┐",
@@ -126,9 +136,6 @@ function questionSection(questionId) {
126
136
  if (questionId === "field" || questionId === "careerStage" || questionId === "experienceLevel") {
127
137
  return "Researcher profile";
128
138
  }
129
- if (questionId === "currentProjectType" || questionId === "currentResearchTopic" || questionId === "currentBlocker") {
130
- return "Current work";
131
- }
132
139
  if (questionId === "preferredCheckpointIntensity" || questionId === "preferredEntryMode") {
133
140
  return "Interaction style";
134
141
  }
@@ -137,6 +144,9 @@ function questionSection(questionId) {
137
144
  }
138
145
  return "Authorship and voice";
139
146
  }
147
+ function formatModeLabel(mode) {
148
+ return `${mode[0].toUpperCase()}${mode.slice(1)}`;
149
+ }
140
150
  function moveCursorUp(lines) {
141
151
  return lines > 0 ? `\u001B[${lines}A` : "";
142
152
  }
@@ -252,8 +262,84 @@ async function promptChoiceWithArrows(rl, prompt, choices) {
252
262
  async function promptChoice(rl, prompt, choices) {
253
263
  return promptChoiceWithArrows(rl, prompt, choices);
254
264
  }
265
+ async function promptMultiChoice(rl, prompt, choices) {
266
+ const stream = input;
267
+ if (!stream.isTTY || !output.isTTY) {
268
+ const answer = await rl.question(`${prompt}\nType comma-separated ids or leave blank for auto.\n> `);
269
+ return answer
270
+ .split(",")
271
+ .map((part) => part.trim())
272
+ .filter(Boolean);
273
+ }
274
+ const previousRawMode = stream.isRaw;
275
+ let selectedIndex = 0;
276
+ const selected = new Set();
277
+ const renderedLineCount = choices.length + 2;
278
+ return await new Promise((resolvePromise, reject) => {
279
+ function draw(first = false) {
280
+ if (!first) {
281
+ output.write(moveCursorUp(renderedLineCount));
282
+ }
283
+ const lines = [prompt, "Use ↑/↓, Space to toggle, and Enter to confirm."];
284
+ for (let index = 0; index < choices.length; index += 1) {
285
+ const choice = choices[index];
286
+ const pointer = index === selectedIndex ? ">" : " ";
287
+ const marker = selected.has(choice.id) ? "[x]" : "[ ]";
288
+ lines.push(`${pointer} ${marker} ${choice.label} - ${choice.description}`);
289
+ }
290
+ for (const line of lines) {
291
+ output.write(clearLine());
292
+ output.write(`${line}\n`);
293
+ }
294
+ }
295
+ function cleanup() {
296
+ stream.off("keypress", onKeypress);
297
+ if (stream.isTTY) {
298
+ stream.setRawMode(previousRawMode ?? false);
299
+ }
300
+ output.write("\u001B[?25h");
301
+ }
302
+ function onKeypress(_, key) {
303
+ if (key.ctrl && key.name === "c") {
304
+ cleanup();
305
+ reject(new Error("Setup cancelled."));
306
+ return;
307
+ }
308
+ if (key.name === "up") {
309
+ selectedIndex = selectedIndex === 0 ? choices.length - 1 : selectedIndex - 1;
310
+ draw();
311
+ return;
312
+ }
313
+ if (key.name === "down") {
314
+ selectedIndex = selectedIndex === choices.length - 1 ? 0 : selectedIndex + 1;
315
+ draw();
316
+ return;
317
+ }
318
+ if (key.name === "space") {
319
+ const id = choices[selectedIndex].id;
320
+ if (selected.has(id)) {
321
+ selected.delete(id);
322
+ }
323
+ else {
324
+ selected.add(id);
325
+ }
326
+ draw();
327
+ return;
328
+ }
329
+ if (key.name === "return") {
330
+ cleanup();
331
+ resolvePromise([...selected]);
332
+ }
333
+ }
334
+ emitKeypressEvents(stream);
335
+ stream.setRawMode(true);
336
+ output.write("\u001B[?25l");
337
+ draw(true);
338
+ stream.on("keypress", onKeypress);
339
+ });
340
+ }
255
341
  function hasCompleteFlagInput(args) {
256
- const required = ["provider", "field", "career-stage", "experience", "project-type", "checkpoint"];
342
+ const required = ["provider", "field", "career-stage", "experience", "checkpoint"];
257
343
  return required.every((key) => typeof args[key] === "string" && String(args[key]).trim().length > 0);
258
344
  }
259
345
  function resolveSetupFlow(args) {
@@ -264,13 +350,13 @@ function toSetupAnswers(args) {
264
350
  field: String(args.field),
265
351
  careerStage: String(args["career-stage"]),
266
352
  experienceLevel: String(args.experience),
267
- currentProjectType: String(args["project-type"]),
353
+ currentProjectType: typeof args["project-type"] === "string" && args["project-type"].trim().length > 0
354
+ ? String(args["project-type"])
355
+ : "unspecified research task",
268
356
  preferredCheckpointIntensity: String(args.checkpoint),
269
357
  humanAuthorshipSignal: typeof args["authorship-signal"] === "string" && args["authorship-signal"].trim().length > 0
270
358
  ? args["authorship-signal"].trim()
271
359
  : undefined,
272
- currentResearchTopic: typeof args.topic === "string" && args.topic.trim().length > 0 ? args.topic.trim() : undefined,
273
- currentBlocker: typeof args.blocker === "string" && args.blocker.trim().length > 0 ? args.blocker.trim() : undefined,
274
360
  preferredEntryMode: typeof args["entry-mode"] === "string" && VALID_MODES.has(String(args["entry-mode"]))
275
361
  ? String(args["entry-mode"])
276
362
  : undefined,
@@ -282,15 +368,18 @@ function toSetupAnswers(args) {
282
368
  : undefined
283
369
  };
284
370
  }
285
- async function collectInteractiveAnswers() {
371
+ async function collectInteractiveAnswers(initialFlow) {
286
372
  const rl = createInterface({ input, output });
287
373
  try {
288
- const flow = (await promptChoice(rl, "How would you like to set up Long Table?", buildSetupFlowChoices()));
374
+ const flow = initialFlow ??
375
+ (await promptChoice(rl, "How would you like to set up Long Table?", buildSetupFlowChoices()));
289
376
  console.log("");
290
377
  console.log(renderSetupHeader(flow));
291
378
  console.log("");
292
379
  const provider = await promptChoice(rl, "Which provider do you want to configure?", buildProviderChoices());
293
- const answers = {};
380
+ const answers = {
381
+ currentProjectType: "unspecified research task"
382
+ };
294
383
  const questions = buildQuickSetupFlow(flow);
295
384
  for (let index = 0; index < questions.length; index += 1) {
296
385
  const question = questions[index];
@@ -311,18 +400,12 @@ async function collectInteractiveAnswers() {
311
400
  answers.careerStage = value;
312
401
  if (question.id === "experienceLevel")
313
402
  answers.experienceLevel = value;
314
- if (question.id === "currentProjectType")
315
- answers.currentProjectType = value;
316
403
  if (question.id === "preferredCheckpointIntensity") {
317
404
  answers.preferredCheckpointIntensity = value;
318
405
  }
319
406
  if (question.id === "humanAuthorshipSignal" && value !== "other") {
320
407
  answers.humanAuthorshipSignal = value;
321
408
  }
322
- if (question.id === "currentResearchTopic")
323
- answers.currentResearchTopic = value;
324
- if (question.id === "currentBlocker")
325
- answers.currentBlocker = value;
326
409
  if (question.id === "preferredEntryMode")
327
410
  answers.preferredEntryMode = value;
328
411
  if (question.id === "weakestDomain")
@@ -340,6 +423,84 @@ async function collectInteractiveAnswers() {
340
423
  rl.close();
341
424
  }
342
425
  }
426
+ function perspectiveChoices() {
427
+ return PERSONA_DEFINITIONS.map((persona) => ({
428
+ id: persona.key,
429
+ label: persona.label,
430
+ description: persona.shortDescription
431
+ }));
432
+ }
433
+ function normalizePerspectiveList(value) {
434
+ if (!value?.trim()) {
435
+ return [];
436
+ }
437
+ return value
438
+ .split(",")
439
+ .map((part) => part.trim())
440
+ .filter(Boolean);
441
+ }
442
+ async function collectProjectInterview(setup, args) {
443
+ const needsInteractivePrompts = !(typeof args.name === "string" && args.name.trim()) ||
444
+ !(typeof args.path === "string" && args.path.trim()) ||
445
+ !(typeof args.goal === "string" && args.goal.trim()) ||
446
+ typeof args.blocker !== "string" ||
447
+ normalizePerspectiveList(typeof args.perspectives === "string" ? args.perspectives : undefined).length === 0 ||
448
+ !(typeof args.disagreement === "string" && args.disagreement.trim());
449
+ const rl = createInterface({ input, output });
450
+ try {
451
+ if (needsInteractivePrompts) {
452
+ console.log("");
453
+ console.log("┌──────────────────────────────────────────────┐");
454
+ console.log("│ Long Table Project Start │");
455
+ console.log("└──────────────────────────────────────────────┘");
456
+ console.log("We will create a project workspace and a session memory seed for today's work.");
457
+ console.log("");
458
+ }
459
+ const projectName = (typeof args.name === "string" && args.name.trim()) ||
460
+ (await promptText(rl, renderQuestionHeader(1, 6, "Project interview", "What should this project be called?"), true));
461
+ const suggestedPath = typeof args.path === "string" && args.path.trim()
462
+ ? args.path.trim()
463
+ : resolve(projectName.replace(/\s+/g, "-"));
464
+ const projectPath = (typeof args.path === "string" && args.path.trim()) ||
465
+ (await promptText(rl, renderQuestionHeader(2, 6, "Project interview", `Where should this project live on your machine?\nSuggested path: ${suggestedPath}`), true));
466
+ const currentGoal = (typeof args.goal === "string" && args.goal.trim()) ||
467
+ (await promptText(rl, renderQuestionHeader(3, 6, "Current session", "What are you trying to accomplish in this session?"), true));
468
+ const currentBlocker = (typeof args.blocker === "string" && args.blocker.trim()) ||
469
+ (await promptText(rl, renderQuestionHeader(4, 6, "Current session", "What is the main blocker or uncertainty right now?"), false));
470
+ const requestedPerspectives = normalizePerspectiveList(typeof args.perspectives === "string" ? args.perspectives : undefined).length > 0
471
+ ? normalizePerspectiveList(typeof args.perspectives === "string" ? args.perspectives : undefined)
472
+ : await promptMultiChoice(rl, renderQuestionHeader(5, 6, "Perspectives", "Which perspectives do you already know you want at the table? Leave everything unchecked for auto."), perspectiveChoices());
473
+ const disagreementPreference = (typeof args.disagreement === "string" && args.disagreement.trim()) ||
474
+ (await promptChoice(rl, renderQuestionHeader(6, 6, "Disagreement", "How visible should disagreement between perspectives be in this project by default?"), [
475
+ {
476
+ id: "synthesis_only",
477
+ label: "Synthesis only",
478
+ description: "Show one Long Table answer unless I ask for more."
479
+ },
480
+ {
481
+ id: "show_on_conflict",
482
+ label: "Show on conflict",
483
+ description: "Surface disagreement when the perspectives materially diverge."
484
+ },
485
+ {
486
+ id: "always_visible",
487
+ label: "Always visible",
488
+ description: "Keep panel opinions visible by default."
489
+ }
490
+ ]));
491
+ return {
492
+ projectName: projectName.trim(),
493
+ projectPath: projectPath.trim(),
494
+ currentGoal: currentGoal.trim(),
495
+ ...(currentBlocker?.trim() ? { currentBlocker: currentBlocker.trim() } : {}),
496
+ requestedPerspectives,
497
+ disagreementPreference: disagreementPreference
498
+ };
499
+ }
500
+ finally {
501
+ rl.close();
502
+ }
503
+ }
343
504
  function normalizePersistAnswers(raw) {
344
505
  return {
345
506
  flow: raw.flow === "interview" ? "interview" : "quickstart",
@@ -348,17 +509,11 @@ function normalizePersistAnswers(raw) {
348
509
  field: raw.field,
349
510
  careerStage: raw.careerStage,
350
511
  experienceLevel: raw.experienceLevel,
351
- currentProjectType: raw.currentProjectType,
512
+ currentProjectType: "unspecified research task",
352
513
  preferredCheckpointIntensity: raw.preferredCheckpointIntensity,
353
514
  ...(raw.humanAuthorshipSignal?.trim()
354
515
  ? { humanAuthorshipSignal: raw.humanAuthorshipSignal.trim() }
355
516
  : {}),
356
- ...(raw.currentResearchTopic?.trim()
357
- ? { currentResearchTopic: raw.currentResearchTopic.trim() }
358
- : {}),
359
- ...(raw.currentBlocker?.trim()
360
- ? { currentBlocker: raw.currentBlocker.trim() }
361
- : {}),
362
517
  ...(raw.preferredEntryMode
363
518
  ? { preferredEntryMode: raw.preferredEntryMode }
364
519
  : {}),
@@ -408,7 +563,7 @@ async function runInit(args) {
408
563
  provider: String(args.provider) === "claude" ? "claude" : "codex",
409
564
  answers: toSetupAnswers(args)
410
565
  }
411
- : await collectInteractiveAnswers();
566
+ : await collectInteractiveAnswers(typeof args.flow === "string" ? resolveSetupFlow(args) : undefined);
412
567
  const outputValue = createPersistedSetupOutput(answers, provider, flow);
413
568
  const result = await saveSetupAndRuntimeConfig(outputValue, {
414
569
  setupPath: customPath,
@@ -429,24 +584,19 @@ async function runInit(args) {
429
584
  }
430
585
  if (installedPrompts.length > 0) {
431
586
  console.log("");
432
- console.log("Installed Codex prompt aliases:");
587
+ console.log("Installed Codex prompt files:");
433
588
  for (const prompt of installedPrompts) {
434
589
  console.log(`- /prompts:${prompt.name}`);
435
590
  }
591
+ console.log(" Note: whether Codex exposes these as slash commands depends on your Codex build.");
436
592
  }
437
593
  if (provider === "codex") {
438
594
  console.log("");
439
595
  console.log("Next step:");
440
- console.log("- Open Codex and start with `/prompts:longtable`.");
441
- if (answers.currentBlocker) {
442
- console.log(`- Suggested first message: ${answers.currentBlocker}`);
443
- }
444
- else if (answers.currentResearchTopic) {
445
- console.log(`- Suggested first message: Help me open the problem around ${answers.currentResearchTopic}.`);
446
- }
447
- else {
448
- console.log("- Suggested first message: I want to start my research and need help opening the problem.");
449
- }
596
+ console.log("- Start here: `longtable start`.");
597
+ console.log("- If you want a direct natural-language entry: `longtable ask --prompt \"...\"`.");
598
+ console.log("- Codex prompt files are available as an experimental integration, not the primary path.");
599
+ console.log("- Suggested next action: create a project workspace and let Long Table interview the current session.");
450
600
  }
451
601
  }
452
602
  async function runShow(args) {
@@ -492,24 +642,19 @@ async function runCodexPersistInit(args) {
492
642
  console.log(renderInstallSummary(result));
493
643
  if (installedPrompts.length > 0) {
494
644
  console.log("");
495
- console.log("Installed Codex prompt aliases:");
645
+ console.log("Installed Codex prompt files:");
496
646
  for (const prompt of installedPrompts) {
497
647
  console.log(`- /prompts:${prompt.name}`);
498
648
  }
649
+ console.log(" Note: whether Codex exposes these as slash commands depends on your Codex build.");
499
650
  }
500
651
  if (provider === "codex") {
501
652
  console.log("");
502
653
  console.log("Next step:");
503
- console.log("- Start Codex and use `/prompts:longtable` for the most natural entry.");
504
- if (answers.currentBlocker) {
505
- console.log(`- Suggested first message: ${answers.currentBlocker}`);
506
- }
507
- else if (answers.currentResearchTopic) {
508
- console.log(`- Suggested first message: Help me think through ${answers.currentResearchTopic}.`);
509
- }
510
- else {
511
- console.log("- Suggested first message: I want to start my research and I am not sure where to open the problem.");
512
- }
654
+ console.log("- Start here: `longtable start`.");
655
+ console.log("- If you want a direct natural-language entry: `longtable ask --prompt \"...\"`.");
656
+ console.log("- Codex prompt files are available as an experimental integration, not the primary path.");
657
+ console.log("- Suggested next action: create a project workspace and let Long Table interview the current session.");
513
658
  }
514
659
  }
515
660
  async function resolvePrompt(prompt) {
@@ -578,7 +723,25 @@ async function loadOptionalSetup(path) {
578
723
  return null;
579
724
  }
580
725
  }
726
+ async function buildProjectAwarePrompt(prompt, workingDirectory) {
727
+ const context = await loadProjectContextFromDirectory(workingDirectory);
728
+ if (!context) {
729
+ return { prompt, projectContextFound: false };
730
+ }
731
+ const lines = [
732
+ "Long Table project context",
733
+ `project: ${context.project.projectName}`,
734
+ `current session goal: ${context.session.currentGoal}`,
735
+ ...(context.session.currentBlocker ? [`current blocker: ${context.session.currentBlocker}`] : []),
736
+ `requested perspectives: ${context.session.requestedPerspectives.length > 0 ? context.session.requestedPerspectives.join(", ") : "auto"}`,
737
+ `disagreement preference: ${context.session.disagreementPreference}`,
738
+ "",
739
+ prompt
740
+ ];
741
+ return { prompt: lines.join("\n"), projectContextFound: true };
742
+ }
581
743
  async function runModeCommand(mode, args) {
744
+ const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
582
745
  const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
583
746
  if (!prompt) {
584
747
  throw new Error("A prompt is required.");
@@ -588,13 +751,14 @@ async function runModeCommand(mode, args) {
588
751
  throw new Error(`Invalid stage: ${stage}`);
589
752
  }
590
753
  const setup = await loadOptionalSetup(typeof args.setup === "string" ? args.setup : undefined);
754
+ const projectAware = await buildProjectAwarePrompt(prompt, workingDirectory);
591
755
  const panelPreference = setup?.profileSeed.panelPreference;
592
756
  const panelRequested = args.panel === true ||
593
757
  panelPreference === "always_visible" ||
594
758
  (panelPreference === "show_on_conflict" && args["show-conflicts"] === true);
595
759
  const { guidedPrompt } = buildPersonaGuidance({
596
760
  mode,
597
- prompt,
761
+ prompt: projectAware.prompt,
598
762
  roleFlag: typeof args.role === "string" ? args.role : undefined,
599
763
  panel: panelRequested,
600
764
  showConflicts: args["show-conflicts"] === true,
@@ -606,7 +770,7 @@ async function runModeCommand(mode, args) {
606
770
  mode,
607
771
  researchStage: stage,
608
772
  setupPath: typeof args.setup === "string" ? args.setup : undefined,
609
- workingDirectory: typeof args.cwd === "string" ? args.cwd : cwd()
773
+ workingDirectory
610
774
  });
611
775
  console.log(wrapped.wrappedPrompt);
612
776
  return;
@@ -616,7 +780,7 @@ async function runModeCommand(mode, args) {
616
780
  mode,
617
781
  researchStage: stage,
618
782
  setupPath: typeof args.setup === "string" ? args.setup : undefined,
619
- workingDirectory: typeof args.cwd === "string" ? args.cwd : cwd(),
783
+ workingDirectory,
620
784
  json: args.json === true
621
785
  });
622
786
  exit(exitCode);
@@ -642,11 +806,67 @@ async function runAsk(args) {
642
806
  }
643
807
  await runModeCommand(mode, delegatedArgs);
644
808
  }
809
+ async function runRoles(args) {
810
+ const payload = PERSONA_DEFINITIONS.map((persona) => ({
811
+ key: persona.key,
812
+ label: persona.label,
813
+ description: persona.shortDescription,
814
+ triggerMode: persona.triggerMode,
815
+ exampleTriggers: persona.synonyms.slice(0, 4)
816
+ }));
817
+ if (args.json === true) {
818
+ console.log(JSON.stringify(payload, null, 2));
819
+ return;
820
+ }
821
+ console.log("Long Table roles");
822
+ console.log("These are perspectives Long Table can consult when relevant.");
823
+ console.log("");
824
+ for (const persona of payload) {
825
+ console.log(`- ${persona.label} (${persona.key})`);
826
+ console.log(` ${persona.description}`);
827
+ console.log(` Trigger: ${persona.triggerMode === "auto-callable" ? "auto-callable when your language strongly implies it" : "explicit request only"}`);
828
+ console.log(` Examples: ${persona.exampleTriggers.join(", ")}`);
829
+ }
830
+ }
831
+ async function runStart(args) {
832
+ const setupPath = typeof args.setup === "string" ? args.setup : undefined;
833
+ const existingSetup = await loadOptionalSetup(setupPath);
834
+ if (!existingSetup) {
835
+ throw new Error("Long Table global setup is missing. Run `longtable init --flow interview` first.");
836
+ }
837
+ const interview = await collectProjectInterview(existingSetup, args);
838
+ const context = await createOrUpdateProjectWorkspace({
839
+ projectName: interview.projectName,
840
+ projectPath: interview.projectPath,
841
+ currentGoal: interview.currentGoal,
842
+ currentBlocker: interview.currentBlocker,
843
+ requestedPerspectives: interview.requestedPerspectives,
844
+ disagreementPreference: interview.disagreementPreference,
845
+ setup: existingSetup
846
+ });
847
+ if (args.json === true) {
848
+ console.log(JSON.stringify({
849
+ project: context.project,
850
+ session: context.session,
851
+ projectFilePath: context.projectFilePath,
852
+ sessionFilePath: context.sessionFilePath
853
+ }, null, 2));
854
+ return;
855
+ }
856
+ console.log(renderProjectWorkspaceSummary(context));
857
+ console.log("");
858
+ console.log("Next step:");
859
+ console.log(`- cd "${context.project.projectPath}"`);
860
+ console.log("- start Codex in that directory if you want a plain Codex session that inherits Long Table project instructions");
861
+ console.log(`- or run: longtable ask --cwd "${context.project.projectPath}" --prompt "${context.session.currentGoal.replaceAll("\"", "\\\"")}"`);
862
+ console.log("- the workspace now includes `.longtable/` memory files and a project-scoped `AGENTS.md`");
863
+ }
645
864
  async function runCodexSubcommand(subcommand, args) {
646
865
  const customDir = typeof args.dir === "string" ? args.dir : undefined;
647
866
  if (subcommand === "install-prompts") {
648
867
  const installed = await installCodexPromptAliases(customDir);
649
868
  console.log(`Installed ${installed.length} Long Table prompt aliases in ${resolveCodexPromptsDir(customDir)}`);
869
+ console.log("Note: prompt-file discovery depends on the Codex build. Treat this as an experimental integration.");
650
870
  for (const prompt of installed) {
651
871
  console.log(`- /prompts:${prompt.name}`);
652
872
  }
@@ -681,6 +901,7 @@ async function runCodexSubcommand(subcommand, args) {
681
901
  console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
682
902
  console.log(`- codex runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
683
903
  console.log(`- prompt aliases dir: ${status.promptsDir}`);
904
+ console.log("- prompt-file integration: experimental (your Codex build may not expose these as slash commands)");
684
905
  if (aliases.length === 0) {
685
906
  console.log("- prompt aliases: none");
686
907
  }
@@ -705,6 +926,14 @@ async function main() {
705
926
  await runInit(values);
706
927
  return;
707
928
  }
929
+ if (command === "start") {
930
+ await runStart(values);
931
+ return;
932
+ }
933
+ if (command === "roles") {
934
+ await runRoles(values);
935
+ return;
936
+ }
708
937
  if (command === "show") {
709
938
  await runShow(values);
710
939
  return;
@@ -0,0 +1,47 @@
1
+ import type { SetupPersistedOutput } from "@diverga/setup";
2
+ export type ProjectDisagreementPreference = "synthesis_only" | "show_on_conflict" | "always_visible";
3
+ export interface LongTableProjectRecord {
4
+ schemaVersion: 1;
5
+ product: "Long Table";
6
+ projectName: string;
7
+ projectPath: string;
8
+ createdAt: string;
9
+ globalSetupSummary: {
10
+ field: string;
11
+ careerStage: string;
12
+ experienceLevel: string;
13
+ checkpointIntensity: string;
14
+ humanAuthorshipSignal?: string;
15
+ weakestDomain?: string;
16
+ defaultPanelPreference?: ProjectDisagreementPreference;
17
+ };
18
+ }
19
+ export interface LongTableSessionRecord {
20
+ schemaVersion: 1;
21
+ id: string;
22
+ createdAt: string;
23
+ projectName: string;
24
+ projectPath: string;
25
+ currentGoal: string;
26
+ currentBlocker?: string;
27
+ requestedPerspectives: string[];
28
+ disagreementPreference: ProjectDisagreementPreference;
29
+ }
30
+ export interface LongTableProjectContext {
31
+ project: LongTableProjectRecord;
32
+ session: LongTableSessionRecord;
33
+ projectFilePath: string;
34
+ sessionFilePath: string;
35
+ metaDir: string;
36
+ }
37
+ export declare function createOrUpdateProjectWorkspace(options: {
38
+ projectName: string;
39
+ projectPath: string;
40
+ currentGoal: string;
41
+ currentBlocker?: string;
42
+ requestedPerspectives: string[];
43
+ disagreementPreference: ProjectDisagreementPreference;
44
+ setup: SetupPersistedOutput;
45
+ }): Promise<LongTableProjectContext>;
46
+ export declare function loadProjectContextFromDirectory(startPath: string): Promise<LongTableProjectContext | null>;
47
+ export declare function renderProjectWorkspaceSummary(context: LongTableProjectContext): string;
@@ -0,0 +1,207 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { createEmptyResearchState } from "@diverga/memory";
5
+ function nowIso() {
6
+ return new Date().toISOString();
7
+ }
8
+ function slugify(input) {
9
+ return input
10
+ .trim()
11
+ .toLowerCase()
12
+ .replace(/[^a-z0-9가-힣]+/g, "-")
13
+ .replace(/^-+|-+$/g, "")
14
+ .slice(0, 80);
15
+ }
16
+ function resolveMetaDir(projectPath) {
17
+ return join(projectPath, ".longtable");
18
+ }
19
+ function buildWorkspaceGuide(project, session) {
20
+ const lines = [
21
+ "# Long Table Workspace",
22
+ "",
23
+ "This directory is a Long Table research workspace.",
24
+ "",
25
+ "## Current project",
26
+ `- Project: ${project.projectName}`,
27
+ `- Goal right now: ${session.currentGoal}`,
28
+ ...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
29
+ `- Disagreement visibility: ${session.disagreementPreference}`,
30
+ `- Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
31
+ "",
32
+ "## How Codex should behave here",
33
+ "- Treat this as a researcher-facing Long Table session, not a generic coding task.",
34
+ "- Ask clarifying or tension questions before closing too early.",
35
+ "- If you foreground specific roles, disclose them with `Long Table consulted: ...`.",
36
+ "- Keep one accountable synthesis, but keep disagreement visible by default when it matters.",
37
+ "- Do not expose internal tool logs or process commentary in researcher-facing answers.",
38
+ "",
39
+ "## Session files",
40
+ "- `.longtable/project.json` contains project-level metadata.",
41
+ "- `.longtable/current-session.json` contains the current session goal and blocker."
42
+ ];
43
+ return lines.join("\n");
44
+ }
45
+ function buildProjectAgentsMd(project, session) {
46
+ return [
47
+ "# AGENTS.md",
48
+ "",
49
+ "This directory is a Long Table research workspace.",
50
+ "",
51
+ "## Purpose",
52
+ `- Project name: ${project.projectName}`,
53
+ `- Current goal: ${session.currentGoal}`,
54
+ ...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
55
+ `- Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
56
+ `- Disagreement visibility: ${session.disagreementPreference}`,
57
+ "",
58
+ "## Research-facing behavior",
59
+ "- Treat researcher interaction as the primary task.",
60
+ "- Begin exploratory work with clarifying or tension questions before recommending a direction.",
61
+ "- If you foreground role perspectives, disclose them with `Long Table consulted: ...`.",
62
+ "- Keep one accountable synthesis, but do not hide meaningful disagreement.",
63
+ "- Do not expose internal tool logs, file-search traces, or process commentary in the researcher-facing answer.",
64
+ "",
65
+ "## Session memory",
66
+ "- Read `.longtable/current-session.json` before giving substantial guidance.",
67
+ "- Use `.longtable/project.json` as the project-level context.",
68
+ "- Prefer the current goal and blocker over generic assumptions.",
69
+ "",
70
+ "## Scope",
71
+ "- These instructions apply to this directory and its children."
72
+ ].join("\n");
73
+ }
74
+ function buildStateSeed(project, session, setup) {
75
+ const state = createEmptyResearchState();
76
+ state.explicitState = {
77
+ field: setup.profileSeed.field,
78
+ careerStage: setup.profileSeed.careerStage,
79
+ experienceLevel: setup.profileSeed.experienceLevel,
80
+ projectName: project.projectName,
81
+ currentGoal: session.currentGoal,
82
+ disagreementPreference: session.disagreementPreference,
83
+ requestedPerspectives: session.requestedPerspectives
84
+ };
85
+ if (session.currentBlocker) {
86
+ state.explicitState.currentBlocker = session.currentBlocker;
87
+ state.openTensions.push(session.currentBlocker);
88
+ }
89
+ if (setup.profileSeed.humanAuthorshipSignal) {
90
+ state.explicitState.humanAuthorshipSignal = setup.profileSeed.humanAuthorshipSignal;
91
+ }
92
+ state.narrativeTraces.push({
93
+ id: "project-session-goal",
94
+ timestamp: nowIso(),
95
+ source: "longtable-start",
96
+ traceType: "judgment",
97
+ summary: `Current session goal: ${session.currentGoal}.`,
98
+ visibility: "explicit",
99
+ importance: "high"
100
+ });
101
+ if (session.currentBlocker) {
102
+ state.narrativeTraces.push({
103
+ id: "project-session-blocker",
104
+ timestamp: nowIso(),
105
+ source: "longtable-start",
106
+ traceType: "tension",
107
+ summary: `Current session blocker: ${session.currentBlocker}.`,
108
+ visibility: "explicit",
109
+ importance: "high"
110
+ });
111
+ }
112
+ return JSON.stringify(state, null, 2);
113
+ }
114
+ export async function createOrUpdateProjectWorkspace(options) {
115
+ const projectPath = resolve(options.projectPath);
116
+ const metaDir = resolveMetaDir(projectPath);
117
+ const sessionsDir = join(metaDir, "sessions");
118
+ const projectFilePath = join(metaDir, "project.json");
119
+ const sessionFilePath = join(metaDir, "current-session.json");
120
+ const sessionId = slugify(`${options.projectName}-${Date.now()}`);
121
+ await mkdir(projectPath, { recursive: true });
122
+ await mkdir(metaDir, { recursive: true });
123
+ await mkdir(sessionsDir, { recursive: true });
124
+ const project = existsSync(projectFilePath)
125
+ ? JSON.parse(await readFile(projectFilePath, "utf8"))
126
+ : {
127
+ schemaVersion: 1,
128
+ product: "Long Table",
129
+ projectName: options.projectName,
130
+ projectPath,
131
+ createdAt: nowIso(),
132
+ globalSetupSummary: {
133
+ field: options.setup.profileSeed.field,
134
+ careerStage: options.setup.profileSeed.careerStage,
135
+ experienceLevel: options.setup.profileSeed.experienceLevel,
136
+ checkpointIntensity: options.setup.profileSeed.preferredCheckpointIntensity,
137
+ ...(options.setup.profileSeed.humanAuthorshipSignal
138
+ ? { humanAuthorshipSignal: options.setup.profileSeed.humanAuthorshipSignal }
139
+ : {}),
140
+ ...(options.setup.profileSeed.weakestDomain
141
+ ? { weakestDomain: options.setup.profileSeed.weakestDomain }
142
+ : {}),
143
+ ...(options.setup.profileSeed.panelPreference
144
+ ? { defaultPanelPreference: options.setup.profileSeed.panelPreference }
145
+ : {})
146
+ }
147
+ };
148
+ const session = {
149
+ schemaVersion: 1,
150
+ id: sessionId,
151
+ createdAt: nowIso(),
152
+ projectName: project.projectName,
153
+ projectPath,
154
+ currentGoal: options.currentGoal,
155
+ ...(options.currentBlocker ? { currentBlocker: options.currentBlocker } : {}),
156
+ requestedPerspectives: options.requestedPerspectives,
157
+ disagreementPreference: options.disagreementPreference
158
+ };
159
+ await writeFile(projectFilePath, JSON.stringify(project, null, 2), "utf8");
160
+ await writeFile(sessionFilePath, JSON.stringify(session, null, 2), "utf8");
161
+ await writeFile(join(sessionsDir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf8");
162
+ await writeFile(join(metaDir, "state.json"), buildStateSeed(project, session, options.setup), "utf8");
163
+ await writeFile(join(projectPath, "LONGTABLE.md"), buildWorkspaceGuide(project, session), "utf8");
164
+ await writeFile(join(projectPath, "AGENTS.md"), buildProjectAgentsMd(project, session), "utf8");
165
+ return {
166
+ project,
167
+ session,
168
+ projectFilePath,
169
+ sessionFilePath,
170
+ metaDir
171
+ };
172
+ }
173
+ export async function loadProjectContextFromDirectory(startPath) {
174
+ let current = resolve(startPath);
175
+ while (true) {
176
+ const metaDir = resolveMetaDir(current);
177
+ const projectFilePath = join(metaDir, "project.json");
178
+ const sessionFilePath = join(metaDir, "current-session.json");
179
+ if (existsSync(projectFilePath) && existsSync(sessionFilePath)) {
180
+ return {
181
+ project: JSON.parse(await readFile(projectFilePath, "utf8")),
182
+ session: JSON.parse(await readFile(sessionFilePath, "utf8")),
183
+ projectFilePath,
184
+ sessionFilePath,
185
+ metaDir
186
+ };
187
+ }
188
+ const parent = dirname(current);
189
+ if (parent === current) {
190
+ return null;
191
+ }
192
+ current = parent;
193
+ }
194
+ }
195
+ export function renderProjectWorkspaceSummary(context) {
196
+ return [
197
+ "Long Table project workspace",
198
+ `project: ${context.project.projectName}`,
199
+ `path: ${context.project.projectPath}`,
200
+ `goal: ${context.session.currentGoal}`,
201
+ ...(context.session.currentBlocker ? [`blocker: ${context.session.currentBlocker}`] : []),
202
+ `perspectives: ${context.session.requestedPerspectives.length > 0 ? context.session.requestedPerspectives.join(", ") : "auto"}`,
203
+ `disagreement: ${context.session.disagreementPreference}`,
204
+ `project file: ${context.projectFilePath}`,
205
+ `session file: ${context.sessionFilePath}`
206
+ ].join("\n");
207
+ }
@@ -33,12 +33,13 @@ function promptSpec() {
33
33
  "Ask exactly one setup question at a time.",
34
34
  "Use numbered choices when possible and include a 'None of the above' option when needed.",
35
35
  "Do not move to the next question until the researcher answers the current one.",
36
- "Quickstart covers: provider, field, career stage, experience level, current project type, checkpoint intensity, and human authorship signal.",
37
- "Interview also covers: current research topic, current blocker, preferred entry mode, weakest domain, and panel visibility preference.",
38
- "After collecting all answers, summarize the proposed setup and then output both: 1) the exact `longtable codex persist-init ... --install-prompts` command and 2) a strict JSON object with keys provider, flow, field, careerStage, experienceLevel, currentProjectType, preferredCheckpointIntensity, and optional humanAuthorshipSignal, currentResearchTopic, currentBlocker, preferredEntryMode, weakestDomain, panelPreference.",
36
+ "Quickstart covers: provider, field, career stage, experience level, checkpoint intensity, and human authorship signal.",
37
+ "Interview also covers: preferred entry mode, weakest domain, and panel visibility preference.",
38
+ "After collecting all answers, summarize the proposed setup and then output both: 1) the exact `longtable codex persist-init ... --install-prompts` command and 2) a strict JSON object with keys provider, flow, field, careerStage, experienceLevel, preferredCheckpointIntensity, and optional humanAuthorshipSignal, preferredEntryMode, weakestDomain, panelPreference.",
39
39
  "If the user prefers paste-based setup, tell them they can pipe the JSON into `longtable codex persist-init --stdin --install-prompts`.",
40
40
  "If the researcher asks you to stay inside Codex, keep the conversation in numbered form and do not prematurely close.",
41
41
  "Frame the setup like a short researcher interview, not a bare config form.",
42
+ "Do not pretend that this is the full project-start interview. The real project-start interview happens in `longtable start`.",
42
43
  "Treat any slash-command arguments as context for why setup is being done now."
43
44
  ]
44
45
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "description": "Researcher-facing Long Table CLI on top of the legacy Diverga package surface",
6
6
  "type": "module",
@@ -25,8 +25,9 @@
25
25
  "typecheck": "tsc -p tsconfig.json --noEmit"
26
26
  },
27
27
  "dependencies": {
28
+ "@diverga/memory": "0.1.0",
28
29
  "@diverga/provider-codex": "0.1.1",
29
- "@diverga/setup": "0.1.2"
30
+ "@diverga/setup": "0.1.4"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@types/node": "^22.10.1",