@longtable/cli 0.1.1 → 0.1.2

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
@@ -25,16 +25,20 @@ node packages/longtable/dist/cli.js --help
25
25
  Run setup once and install Codex prompt aliases:
26
26
 
27
27
  ```bash
28
- longtable init --install-prompts
28
+ longtable init --flow interview --install-prompts
29
29
  ```
30
30
 
31
- `longtable init` now uses an arrow-key terminal menu instead of plain number-entry prompts.
31
+ `longtable init` now uses an arrow-key terminal menu instead of plain number-entry prompts and supports:
32
+
33
+ - `--flow quickstart`
34
+ - `--flow interview`
32
35
 
33
36
  Then you can work in two ways.
34
37
 
35
38
  From the terminal:
36
39
 
37
40
  ```bash
41
+ longtable ask --prompt "연구를 시작하고 싶어. 지금 어디서부터 좁혀야 할지 모르겠어."
38
42
  longtable review --prompt "Review this claim critically."
39
43
  longtable review --prompt "BJET 편집자 관점에서 봐줘." --role editor
40
44
  longtable review --prompt "방법론적으로 어디가 취약한지 말해줘." --panel --show-conflicts
@@ -42,6 +46,7 @@ longtable review --prompt "방법론적으로 어디가 취약한지 말해줘."
42
46
 
43
47
  Inside Codex after prompt aliases are installed:
44
48
 
49
+ - `/prompts:longtable`
45
50
  - `/prompts:longtable-init`
46
51
  - `/prompts:longtable-explore`
47
52
  - `/prompts:longtable-review`
@@ -53,6 +58,7 @@ Inside Codex after prompt aliases are installed:
53
58
  ## Core commands
54
59
 
55
60
  ```bash
61
+ longtable ask --prompt "Help me open this research problem carefully."
56
62
  longtable init
57
63
  longtable show --json
58
64
  longtable install --json
@@ -72,7 +78,7 @@ longtable codex install-prompts
72
78
  If you finish onboarding inside Codex with `/prompts:longtable-init`, you can persist the collected answers with:
73
79
 
74
80
  ```bash
75
- longtable codex persist-init --provider codex --field education --career-stage doctoral --experience intermediate --project-type "journal article" --checkpoint balanced --install-prompts
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
76
82
  ```
77
83
 
78
84
  Or from a JSON block:
package/dist/cli.js CHANGED
@@ -27,9 +27,10 @@ const VALID_STAGES = new Set([
27
27
  function usage() {
28
28
  return [
29
29
  "Usage:",
30
- " longtable init [--provider codex|claude] [--field <field>] [--career-stage <stage>] [--experience novice|intermediate|advanced] [--project-type <type>] [--checkpoint low|balanced|high] [--authorship-signal <text>] [--json] [--no-install] [--install-prompts]",
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]",
31
31
  " longtable show [--json] [--path <file>]",
32
32
  " longtable install [--json] [--path <file>] [--runtime-path <file>]",
33
+ " longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
33
34
  " longtable explore|review|critique|draft|commit|submit [--prompt <text>] [--role <role[,role]>] [--panel] [--show-conflicts] [--show-deliberation] [--print] [--json] [--stage <stage>] [--setup <path>] [--cwd <path>]",
34
35
  " longtable codex persist-init [--answers-json <json> | --stdin | full setup flags] [--install-prompts] [--json]",
35
36
  " longtable codex install-prompts [--dir <path>]",
@@ -37,7 +38,8 @@ function usage() {
37
38
  " longtable codex status [--dir <path>] [--json]",
38
39
  "",
39
40
  "Examples:",
40
- " longtable init --install-prompts",
41
+ " longtable init --flow interview --install-prompts",
42
+ " longtable ask --prompt \"연구를 시작하고 싶어. 지금 어디서부터 좁혀야 할지 모르겠어.\"",
41
43
  " longtable review --prompt \"Review this claim critically.\" --panel --show-conflicts",
42
44
  " longtable review --role editor --prompt \"BJET 편집자 관점에서 봐줘.\"",
43
45
  " printf '{\"provider\":\"codex\",...}' | longtable codex persist-init --stdin --install-prompts",
@@ -49,7 +51,7 @@ function parseArgs(argv) {
49
51
  const values = {};
50
52
  let subcommand = maybeSubcommand;
51
53
  const modeCommand = command && VALID_MODES.has(command);
52
- const directCommand = command && ["init", "show", "install", "codex"].includes(command);
54
+ const directCommand = command && ["init", "show", "install", "codex", "ask"].includes(command);
53
55
  let startIndex = 1;
54
56
  if (modeCommand) {
55
57
  subcommand = undefined;
@@ -91,6 +93,50 @@ function renderChoices(choices) {
91
93
  .map((choice, index) => `${index + 1}. ${choice.label} — ${choice.description}`)
92
94
  .join("\n");
93
95
  }
96
+ function buildSetupFlowChoices() {
97
+ return [
98
+ {
99
+ id: "quickstart",
100
+ label: "Quickstart",
101
+ description: "Minimal setup for the fastest first win."
102
+ },
103
+ {
104
+ id: "interview",
105
+ label: "Interview",
106
+ description: "A more detailed researcher profile interview for better first guidance."
107
+ }
108
+ ];
109
+ }
110
+ function renderSetupHeader(flow) {
111
+ const title = flow === "interview" ? "Long Table Setup Interview" : "Long Table Quickstart";
112
+ const subtitle = flow === "interview"
113
+ ? "We will ask about your research persona, current topic, blocker, and preferred kind of challenge."
114
+ : "We will capture the minimum profile needed to start using Long Table.";
115
+ return [
116
+ "┌──────────────────────────────────────────────┐",
117
+ `│ ${title.padEnd(44, " ")}│`,
118
+ "└──────────────────────────────────────────────┘",
119
+ subtitle
120
+ ].join("\n");
121
+ }
122
+ function renderQuestionHeader(index, total, section, prompt) {
123
+ return [``, `[${index}/${total}] ${section}`, prompt].join("\n");
124
+ }
125
+ function questionSection(questionId) {
126
+ if (questionId === "field" || questionId === "careerStage" || questionId === "experienceLevel") {
127
+ return "Researcher profile";
128
+ }
129
+ if (questionId === "currentProjectType" || questionId === "currentResearchTopic" || questionId === "currentBlocker") {
130
+ return "Current work";
131
+ }
132
+ if (questionId === "preferredCheckpointIntensity" || questionId === "preferredEntryMode") {
133
+ return "Interaction style";
134
+ }
135
+ if (questionId === "weakestDomain" || questionId === "panelPreference") {
136
+ return "How Long Table should challenge you";
137
+ }
138
+ return "Authorship and voice";
139
+ }
94
140
  function moveCursorUp(lines) {
95
141
  return lines > 0 ? `\u001B[${lines}A` : "";
96
142
  }
@@ -125,6 +171,18 @@ async function promptChoiceByNumber(rl, prompt, choices) {
125
171
  return choice.id;
126
172
  }
127
173
  }
174
+ async function promptText(rl, prompt, required) {
175
+ while (true) {
176
+ const answer = (await rl.question(`${prompt}\n> `)).trim();
177
+ if (!required) {
178
+ return answer || undefined;
179
+ }
180
+ if (answer) {
181
+ return answer;
182
+ }
183
+ console.log("This answer cannot be empty.");
184
+ }
185
+ }
128
186
  async function promptChoiceWithArrows(rl, prompt, choices) {
129
187
  const stream = input;
130
188
  if (!stream.isTTY || !output.isTTY) {
@@ -198,6 +256,9 @@ function hasCompleteFlagInput(args) {
198
256
  const required = ["provider", "field", "career-stage", "experience", "project-type", "checkpoint"];
199
257
  return required.every((key) => typeof args[key] === "string" && String(args[key]).trim().length > 0);
200
258
  }
259
+ function resolveSetupFlow(args) {
260
+ return String(args.flow) === "interview" ? "interview" : "quickstart";
261
+ }
201
262
  function toSetupAnswers(args) {
202
263
  return {
203
264
  field: String(args.field),
@@ -207,19 +268,43 @@ function toSetupAnswers(args) {
207
268
  preferredCheckpointIntensity: String(args.checkpoint),
208
269
  humanAuthorshipSignal: typeof args["authorship-signal"] === "string" && args["authorship-signal"].trim().length > 0
209
270
  ? args["authorship-signal"].trim()
271
+ : 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
+ preferredEntryMode: typeof args["entry-mode"] === "string" && VALID_MODES.has(String(args["entry-mode"]))
275
+ ? String(args["entry-mode"])
276
+ : undefined,
277
+ weakestDomain: typeof args["weakest-domain"] === "string"
278
+ ? String(args["weakest-domain"])
279
+ : undefined,
280
+ panelPreference: typeof args["panel-preference"] === "string"
281
+ ? String(args["panel-preference"])
210
282
  : undefined
211
283
  };
212
284
  }
213
285
  async function collectInteractiveAnswers() {
214
286
  const rl = createInterface({ input, output });
215
287
  try {
288
+ const flow = (await promptChoice(rl, "How would you like to set up Long Table?", buildSetupFlowChoices()));
289
+ console.log("");
290
+ console.log(renderSetupHeader(flow));
291
+ console.log("");
216
292
  const provider = await promptChoice(rl, "Which provider do you want to configure?", buildProviderChoices());
217
293
  const answers = {};
218
- for (const question of buildQuickSetupFlow()) {
219
- if (!question.choices) {
294
+ const questions = buildQuickSetupFlow(flow);
295
+ for (let index = 0; index < questions.length; index += 1) {
296
+ const question = questions[index];
297
+ const prompt = renderQuestionHeader(index + 1, questions.length, questionSection(question.id), question.prompt);
298
+ let value;
299
+ if (question.kind === "text") {
300
+ value = await promptText(rl, prompt, question.required);
301
+ }
302
+ else if (question.choices) {
303
+ value = await promptChoice(rl, prompt, question.choices);
304
+ }
305
+ if (!value) {
220
306
  continue;
221
307
  }
222
- const value = await promptChoice(rl, question.prompt, question.choices);
223
308
  if (question.id === "field")
224
309
  answers.field = value;
225
310
  if (question.id === "careerStage")
@@ -234,8 +319,19 @@ async function collectInteractiveAnswers() {
234
319
  if (question.id === "humanAuthorshipSignal" && value !== "other") {
235
320
  answers.humanAuthorshipSignal = value;
236
321
  }
322
+ if (question.id === "currentResearchTopic")
323
+ answers.currentResearchTopic = value;
324
+ if (question.id === "currentBlocker")
325
+ answers.currentBlocker = value;
326
+ if (question.id === "preferredEntryMode")
327
+ answers.preferredEntryMode = value;
328
+ if (question.id === "weakestDomain")
329
+ answers.weakestDomain = value;
330
+ if (question.id === "panelPreference")
331
+ answers.panelPreference = value;
237
332
  }
238
333
  return {
334
+ flow,
239
335
  provider,
240
336
  answers: answers
241
337
  };
@@ -246,6 +342,7 @@ async function collectInteractiveAnswers() {
246
342
  }
247
343
  function normalizePersistAnswers(raw) {
248
344
  return {
345
+ flow: raw.flow === "interview" ? "interview" : "quickstart",
249
346
  provider: raw.provider === "claude" ? "claude" : "codex",
250
347
  answers: {
251
348
  field: raw.field,
@@ -255,13 +352,32 @@ function normalizePersistAnswers(raw) {
255
352
  preferredCheckpointIntensity: raw.preferredCheckpointIntensity,
256
353
  ...(raw.humanAuthorshipSignal?.trim()
257
354
  ? { humanAuthorshipSignal: raw.humanAuthorshipSignal.trim() }
355
+ : {}),
356
+ ...(raw.currentResearchTopic?.trim()
357
+ ? { currentResearchTopic: raw.currentResearchTopic.trim() }
358
+ : {}),
359
+ ...(raw.currentBlocker?.trim()
360
+ ? { currentBlocker: raw.currentBlocker.trim() }
361
+ : {}),
362
+ ...(raw.preferredEntryMode
363
+ ? { preferredEntryMode: raw.preferredEntryMode }
364
+ : {}),
365
+ ...(raw.weakestDomain
366
+ ? { weakestDomain: raw.weakestDomain }
367
+ : {}),
368
+ ...(raw.panelPreference
369
+ ? { panelPreference: raw.panelPreference }
258
370
  : {})
259
371
  }
260
372
  };
261
373
  }
262
374
  async function readPersistAnswers(args) {
263
375
  if (typeof args["answers-json"] === "string") {
264
- return normalizePersistAnswers(JSON.parse(args["answers-json"]));
376
+ const normalized = normalizePersistAnswers(JSON.parse(args["answers-json"]));
377
+ return {
378
+ ...normalized,
379
+ flow: typeof args.flow === "string" ? resolveSetupFlow(args) : normalized.flow
380
+ };
265
381
  }
266
382
  if (args.stdin === true) {
267
383
  const raw = readFileSync(0, "utf8").trim();
@@ -272,6 +388,7 @@ async function readPersistAnswers(args) {
272
388
  }
273
389
  if (hasCompleteFlagInput(args)) {
274
390
  return {
391
+ flow: resolveSetupFlow(args),
275
392
  provider: String(args.provider) === "claude" ? "claude" : "codex",
276
393
  answers: toSetupAnswers(args)
277
394
  };
@@ -285,13 +402,14 @@ async function runInit(args) {
285
402
  const customPath = typeof args.path === "string" ? args.path : undefined;
286
403
  const runtimePath = typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined;
287
404
  const promptsDir = typeof args.dir === "string" ? args.dir : undefined;
288
- const { provider, answers } = hasCompleteFlagInput(args)
405
+ const { flow, provider, answers } = hasCompleteFlagInput(args)
289
406
  ? {
407
+ flow: resolveSetupFlow(args),
290
408
  provider: String(args.provider) === "claude" ? "claude" : "codex",
291
409
  answers: toSetupAnswers(args)
292
410
  }
293
411
  : await collectInteractiveAnswers();
294
- const outputValue = createPersistedSetupOutput(answers, provider);
412
+ const outputValue = createPersistedSetupOutput(answers, provider, flow);
295
413
  const result = await saveSetupAndRuntimeConfig(outputValue, {
296
414
  setupPath: customPath,
297
415
  runtimePath
@@ -316,6 +434,20 @@ async function runInit(args) {
316
434
  console.log(`- /prompts:${prompt.name}`);
317
435
  }
318
436
  }
437
+ if (provider === "codex") {
438
+ console.log("");
439
+ 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
+ }
450
+ }
319
451
  }
320
452
  async function runShow(args) {
321
453
  const outputValue = await loadSetupOutput(typeof args.path === "string" ? args.path : undefined);
@@ -337,8 +469,8 @@ async function runInstall(args) {
337
469
  console.log(renderInstallSummary(result));
338
470
  }
339
471
  async function runCodexPersistInit(args) {
340
- const { provider, answers } = await readPersistAnswers(args);
341
- const outputValue = createPersistedSetupOutput(answers, provider);
472
+ const { flow, provider, answers } = await readPersistAnswers(args);
473
+ const outputValue = createPersistedSetupOutput(answers, provider, flow);
342
474
  const result = await saveSetupAndRuntimeConfig(outputValue, {
343
475
  setupPath: typeof args.path === "string" ? args.path : undefined,
344
476
  runtimePath: typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined
@@ -365,6 +497,20 @@ async function runCodexPersistInit(args) {
365
497
  console.log(`- /prompts:${prompt.name}`);
366
498
  }
367
499
  }
500
+ if (provider === "codex") {
501
+ console.log("");
502
+ 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
+ }
513
+ }
368
514
  }
369
515
  async function resolvePrompt(prompt) {
370
516
  if (prompt?.trim()) {
@@ -381,6 +527,57 @@ async function resolvePrompt(prompt) {
381
527
  rl.close();
382
528
  }
383
529
  }
530
+ function inferModeFromPrompt(prompt) {
531
+ const normalized = prompt.toLowerCase();
532
+ if (normalized.includes("status") ||
533
+ normalized.includes("설정") ||
534
+ normalized.includes("상태") ||
535
+ normalized.includes("롱테이블 상태")) {
536
+ return "status";
537
+ }
538
+ if (normalized.includes("패널") ||
539
+ normalized.includes("의견 충돌") ||
540
+ normalized.includes("conflict") ||
541
+ normalized.includes("disagree") ||
542
+ normalized.includes("panel")) {
543
+ return "panel";
544
+ }
545
+ if (normalized.includes("결정") ||
546
+ normalized.includes("고를") ||
547
+ normalized.includes("선택") ||
548
+ normalized.includes("commit")) {
549
+ return "commit";
550
+ }
551
+ if (normalized.includes("초안") ||
552
+ normalized.includes("써줘") ||
553
+ normalized.includes("문단") ||
554
+ normalized.includes("draft") ||
555
+ normalized.includes("write")) {
556
+ return "draft";
557
+ }
558
+ if (normalized.includes("비판") ||
559
+ normalized.includes("반론") ||
560
+ normalized.includes("critique") ||
561
+ normalized.includes("challenge")) {
562
+ return "critique";
563
+ }
564
+ if (normalized.includes("검토") ||
565
+ normalized.includes("review") ||
566
+ normalized.includes("편집자") ||
567
+ normalized.includes("리뷰어") ||
568
+ normalized.includes("judge")) {
569
+ return "review";
570
+ }
571
+ return "explore";
572
+ }
573
+ async function loadOptionalSetup(path) {
574
+ try {
575
+ return await loadSetupOutput(path);
576
+ }
577
+ catch {
578
+ return null;
579
+ }
580
+ }
384
581
  async function runModeCommand(mode, args) {
385
582
  const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
386
583
  if (!prompt) {
@@ -390,10 +587,16 @@ async function runModeCommand(mode, args) {
390
587
  if (stage && !VALID_STAGES.has(stage)) {
391
588
  throw new Error(`Invalid stage: ${stage}`);
392
589
  }
590
+ const setup = await loadOptionalSetup(typeof args.setup === "string" ? args.setup : undefined);
591
+ const panelPreference = setup?.profileSeed.panelPreference;
592
+ const panelRequested = args.panel === true ||
593
+ panelPreference === "always_visible" ||
594
+ (panelPreference === "show_on_conflict" && args["show-conflicts"] === true);
393
595
  const { guidedPrompt } = buildPersonaGuidance({
596
+ mode,
394
597
  prompt,
395
598
  roleFlag: typeof args.role === "string" ? args.role : undefined,
396
- panel: args.panel === true,
599
+ panel: panelRequested,
397
600
  showConflicts: args["show-conflicts"] === true,
398
601
  showDeliberation: args["show-deliberation"] === true
399
602
  });
@@ -418,6 +621,27 @@ async function runModeCommand(mode, args) {
418
621
  });
419
622
  exit(exitCode);
420
623
  }
624
+ async function runAsk(args) {
625
+ const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
626
+ if (!prompt) {
627
+ throw new Error("A prompt is required.");
628
+ }
629
+ const inferred = inferModeFromPrompt(prompt);
630
+ if (inferred === "status") {
631
+ await runCodexSubcommand("status", args);
632
+ return;
633
+ }
634
+ const mode = inferred === "panel" ? "review" : inferred;
635
+ const delegatedArgs = {
636
+ ...args,
637
+ prompt
638
+ };
639
+ if (inferred === "panel" && delegatedArgs.panel !== true) {
640
+ delegatedArgs.panel = true;
641
+ delegatedArgs["show-conflicts"] = true;
642
+ }
643
+ await runModeCommand(mode, delegatedArgs);
644
+ }
421
645
  async function runCodexSubcommand(subcommand, args) {
422
646
  const customDir = typeof args.dir === "string" ? args.dir : undefined;
423
647
  if (subcommand === "install-prompts") {
@@ -489,6 +713,10 @@ async function main() {
489
713
  await runInstall(values);
490
714
  return;
491
715
  }
716
+ if (command === "ask") {
717
+ await runAsk(values);
718
+ return;
719
+ }
492
720
  if (command === "codex") {
493
721
  await runCodexSubcommand(subcommand, values);
494
722
  return;
@@ -500,7 +728,11 @@ async function main() {
500
728
  throw new Error(`Unknown command: ${command}`);
501
729
  }
502
730
  main().catch((error) => {
503
- console.error(error instanceof Error ? error.message : String(error));
731
+ const message = error instanceof Error ? error.message : String(error);
732
+ console.error(message);
733
+ if (message === "Setup cancelled.") {
734
+ exit(1);
735
+ }
504
736
  console.error("");
505
737
  console.error(usage());
506
738
  exit(1);
@@ -1,4 +1,5 @@
1
1
  import { type CanonicalPersona } from "./personas.js";
2
+ import type { InteractionMode } from "@diverga/core";
2
3
  export type OutputLanguage = "ko" | "en";
3
4
  export interface PersonaRoutingResult {
4
5
  outputLanguage: OutputLanguage;
@@ -12,6 +13,7 @@ export declare function parseRoleFlag(value?: string): CanonicalPersona[];
12
13
  export declare function routePersonas(prompt: string, explicitRoleFlag?: string): PersonaRoutingResult;
13
14
  export declare function renderDisclosure(roles: CanonicalPersona[], language: OutputLanguage): string | null;
14
15
  export declare function buildPersonaGuidance(options: {
16
+ mode: InteractionMode;
15
17
  prompt: string;
16
18
  roleFlag?: string;
17
19
  panel?: boolean;
@@ -63,6 +63,9 @@ export function buildPersonaGuidance(options) {
63
63
  const routing = routePersonas(options.prompt, options.roleFlag);
64
64
  const disclosure = renderDisclosure(routing.consultedRoles, routing.outputLanguage);
65
65
  const lines = [];
66
+ lines.push(routing.outputLanguage === "ko"
67
+ ? `Long Table mode: ${options.mode[0].toUpperCase()}${options.mode.slice(1)}`
68
+ : `Long Table mode: ${options.mode[0].toUpperCase()}${options.mode.slice(1)}`);
66
69
  if (disclosure) {
67
70
  lines.push(disclosure);
68
71
  }
@@ -86,6 +89,9 @@ export function buildPersonaGuidance(options) {
86
89
  ? "Include a short deliberation trace showing why the roles diverged."
87
90
  : "Include a short deliberation trace showing why the roles diverged.");
88
91
  }
92
+ lines.push(routing.outputLanguage === "ko"
93
+ ? "Do not show internal file-search logs, tool traces, or process commentary in the researcher-facing answer."
94
+ : "Do not show internal file-search logs, tool traces, or process commentary in the researcher-facing answer.");
89
95
  lines.push(options.prompt.trim());
90
96
  return {
91
97
  guidedPrompt: lines.join("\n\n"),
@@ -7,19 +7,38 @@ export function resolveCodexPromptsDir(customDir) {
7
7
  }
8
8
  function promptSpec() {
9
9
  return [
10
+ {
11
+ name: "longtable",
12
+ description: "Single-entry Long Table router for research conversations",
13
+ argumentHint: "<natural language research request>",
14
+ body: [
15
+ "You are Long Table.",
16
+ "Classify the user's request into one of these modes: explore, review, critique, draft, commit, panel, or status.",
17
+ "If the request is ambiguous, ask one short clarifying question before closing.",
18
+ "Always begin with `Long Table mode: <Mode>`.",
19
+ "Always disclose consulted roles with `Long Table consulted: ...` when any role is foregrounded.",
20
+ "In explore mode, ask at least two clarifying or tension questions before any recommendation.",
21
+ "In panel mode, return 1) Long Table synthesis 2) visible panel opinions by role 3) conflict summary if needed 4) a decision prompt for the researcher.",
22
+ "Do not expose internal tool logs, file searches, or process notes in the researcher-facing answer.",
23
+ "Treat any slash-command arguments as the current research object."
24
+ ]
25
+ },
10
26
  {
11
27
  name: "longtable-init",
12
28
  description: "Run Long Table researcher onboarding inside Codex",
13
29
  argumentHint: "[project context or current uncertainty]",
14
30
  body: [
15
31
  "You are Long Table onboarding inside Codex.",
32
+ "First ask whether the researcher wants Quickstart or Interview setup.",
16
33
  "Ask exactly one setup question at a time.",
17
34
  "Use numbered choices when possible and include a 'None of the above' option when needed.",
18
35
  "Do not move to the next question until the researcher answers the current one.",
19
- "Cover these fields: provider, field, career stage, experience level, current project type, checkpoint intensity, and human authorship signal.",
20
- "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, field, careerStage, experienceLevel, currentProjectType, preferredCheckpointIntensity, and optional humanAuthorshipSignal.",
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.",
21
39
  "If the user prefers paste-based setup, tell them they can pipe the JSON into `longtable codex persist-init --stdin --install-prompts`.",
22
40
  "If the researcher asks you to stay inside Codex, keep the conversation in numbered form and do not prematurely close.",
41
+ "Frame the setup like a short researcher interview, not a bare config form.",
23
42
  "Treat any slash-command arguments as context for why setup is being done now."
24
43
  ]
25
44
  },
@@ -29,9 +48,11 @@ function promptSpec() {
29
48
  argumentHint: "<topic or research problem>",
30
49
  body: [
31
50
  "You are Long Table in explore mode.",
51
+ "Always begin with `Long Table mode: Explore`.",
32
52
  "Ask at least two clarifying or tension questions before any recommendation.",
33
53
  "Keep unresolved tensions visible.",
34
54
  "Do not rush to synthesis.",
55
+ "Do not expose internal process notes or file-search summaries.",
35
56
  "Treat any slash-command arguments as the current research object."
36
57
  ]
37
58
  },
@@ -41,6 +62,7 @@ function promptSpec() {
41
62
  argumentHint: "<claim, paragraph, design, or plan>",
42
63
  body: [
43
64
  "You are Long Table in review mode.",
65
+ "Always begin with `Long Table mode: Review`.",
44
66
  "Surface why this may be wrong before synthesis.",
45
67
  "Preserve the researcher's own language where possible.",
46
68
  "Treat any slash-command arguments as the object to review."
@@ -52,6 +74,7 @@ function promptSpec() {
52
74
  argumentHint: "<claim, plan, or draft for multi-role review>",
53
75
  body: [
54
76
  "You are Long Table in panel mode.",
77
+ "Always begin with `Long Table mode: Panel`.",
55
78
  "Return 1) a Long Table synthesis 2) visible panel opinions by role 3) a decision prompt for the researcher.",
56
79
  "If roles disagree, do not collapse them too early.",
57
80
  "Disclose which roles were consulted.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "Researcher-facing Long Table CLI on top of the legacy Diverga package surface",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@diverga/provider-codex": "0.1.1",
29
- "@diverga/setup": "0.1.1"
29
+ "@diverga/setup": "0.1.2"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.10.1",