@longtable/cli 0.1.5 → 0.1.6

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
@@ -72,6 +72,16 @@ codex
72
72
 
73
73
  프로젝트 디렉토리 안에서는 보통 그냥 `codex`를 열고 자연어로 연구를 시작하면 됩니다.
74
74
 
75
+ Codex 안에서 더 명시적으로 부르고 싶다면, 아래처럼 짧은 문법도 사용할 수 있습니다.
76
+
77
+ ```text
78
+ lt explore: 연구 질문을 어디서부터 좁혀야 할지 모르겠어.
79
+ lt review: 이 주장 어디가 약한지 봐줘.
80
+ lt panel: 이 방향에 대한 의견 충돌도 같이 보여줘.
81
+ lt editor: BJET 편집자 관점에서 봐줘.
82
+ lt methods: 방법론적으로 어디가 취약한지 말해줘.
83
+ ```
84
+
75
85
  CLI를 계속 쓰고 싶다면 아래 명령은 보조 경로입니다.
76
86
 
77
87
  ## Advanced commands
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { resolve } from "node:path";
7
7
  import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@diverga/setup";
8
8
  import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@diverga/provider-codex";
9
9
  import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodexPromptAliases, resolveCodexPromptsDir } from "./prompt-aliases.js";
10
- import { buildPersonaGuidance } from "./persona-router.js";
10
+ import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router.js";
11
11
  import { PERSONA_DEFINITIONS } from "./personas.js";
12
12
  import { createOrUpdateProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary } from "./project-session.js";
13
13
  const VALID_MODES = new Set([
@@ -803,7 +803,9 @@ async function runAsk(args) {
803
803
  if (!prompt) {
804
804
  throw new Error("A prompt is required.");
805
805
  }
806
- const inferred = inferModeFromPrompt(prompt);
806
+ const directive = parseInvocationDirective(prompt);
807
+ const effectivePrompt = directive.cleanedPrompt;
808
+ const inferred = directive.mode ?? inferModeFromPrompt(effectivePrompt);
807
809
  if (inferred === "status") {
808
810
  await runCodexSubcommand("status", args);
809
811
  return;
@@ -811,9 +813,12 @@ async function runAsk(args) {
811
813
  const mode = inferred === "panel" ? "review" : inferred;
812
814
  const delegatedArgs = {
813
815
  ...args,
814
- prompt
816
+ prompt: effectivePrompt
815
817
  };
816
- if (inferred === "panel" && delegatedArgs.panel !== true) {
818
+ if (directive.roles.length > 0 && typeof delegatedArgs.role !== "string") {
819
+ delegatedArgs.role = directive.roles.join(",");
820
+ }
821
+ if ((inferred === "panel" || directive.panel) && delegatedArgs.panel !== true) {
817
822
  delegatedArgs.panel = true;
818
823
  delegatedArgs["show-conflicts"] = true;
819
824
  }
@@ -8,8 +8,17 @@ export interface PersonaRoutingResult {
8
8
  consultedRoles: CanonicalPersona[];
9
9
  ambiguousSignal: string | null;
10
10
  }
11
+ export interface LongTableInvocationDirective {
12
+ explicit: boolean;
13
+ cleanedPrompt: string;
14
+ mode?: InteractionMode | "panel" | "status";
15
+ roles: CanonicalPersona[];
16
+ panel: boolean;
17
+ showConflicts: boolean;
18
+ }
11
19
  export declare function detectOutputLanguage(input: string): OutputLanguage;
12
20
  export declare function parseRoleFlag(value?: string): CanonicalPersona[];
21
+ export declare function parseInvocationDirective(prompt: string): LongTableInvocationDirective;
13
22
  export declare function routePersonas(prompt: string, explicitRoleFlag?: string): PersonaRoutingResult;
14
23
  export declare function renderDisclosure(roles: CanonicalPersona[], language: OutputLanguage): string | null;
15
24
  export declare function buildPersonaGuidance(options: {
@@ -1,5 +1,24 @@
1
1
  import { getPersonaDefinition, parsePersonaKey, PERSONA_DEFINITIONS } from "./personas.js";
2
2
  const AUTO_CALL_LIMIT = 3;
3
+ const INVOCATION_PREFIX = /^(?:lt|longtable|long table|롱테이블)\s+/i;
4
+ const DIRECTIVE_MAP = [
5
+ { key: "explore", mode: "explore" },
6
+ { key: "review", mode: "review" },
7
+ { key: "critique", mode: "critique" },
8
+ { key: "draft", mode: "draft" },
9
+ { key: "commit", mode: "commit" },
10
+ { key: "panel", mode: "panel", panel: true, showConflicts: true },
11
+ { key: "status", mode: "status" },
12
+ { key: "editor", mode: "review", roles: ["editor"] },
13
+ { key: "reviewer", mode: "review", roles: ["reviewer"] },
14
+ { key: "methods", mode: "review", roles: ["methods_critic"] },
15
+ { key: "method", mode: "review", roles: ["methods_critic"] },
16
+ { key: "theory", mode: "review", roles: ["theory_critic"] },
17
+ { key: "measurement", mode: "review", roles: ["measurement_auditor"] },
18
+ { key: "ethics", mode: "review", roles: ["ethics_reviewer"] },
19
+ { key: "voice", mode: "review", roles: ["voice_keeper"] },
20
+ { key: "venue", mode: "review", roles: ["venue_strategist"] }
21
+ ];
3
22
  function unique(items) {
4
23
  return [...new Set(items)];
5
24
  }
@@ -15,6 +34,49 @@ export function parseRoleFlag(value) {
15
34
  .map((part) => parsePersonaKey(part))
16
35
  .filter((part) => part !== null));
17
36
  }
37
+ export function parseInvocationDirective(prompt) {
38
+ const trimmed = prompt.trim();
39
+ if (!INVOCATION_PREFIX.test(trimmed)) {
40
+ return {
41
+ explicit: false,
42
+ cleanedPrompt: prompt,
43
+ roles: [],
44
+ panel: false,
45
+ showConflicts: false
46
+ };
47
+ }
48
+ const withoutPrefix = trimmed.replace(INVOCATION_PREFIX, "");
49
+ const colonIndex = withoutPrefix.indexOf(":");
50
+ if (colonIndex === -1) {
51
+ return {
52
+ explicit: false,
53
+ cleanedPrompt: prompt,
54
+ roles: [],
55
+ panel: false,
56
+ showConflicts: false
57
+ };
58
+ }
59
+ const directiveKey = withoutPrefix.slice(0, colonIndex).trim().toLowerCase();
60
+ const body = withoutPrefix.slice(colonIndex + 1).trim();
61
+ const directive = DIRECTIVE_MAP.find((entry) => entry.key === directiveKey);
62
+ if (!directive) {
63
+ return {
64
+ explicit: false,
65
+ cleanedPrompt: body || prompt,
66
+ roles: [],
67
+ panel: false,
68
+ showConflicts: false
69
+ };
70
+ }
71
+ return {
72
+ explicit: true,
73
+ cleanedPrompt: body || prompt,
74
+ mode: directive.mode,
75
+ roles: directive.roles ?? [],
76
+ panel: directive.panel === true,
77
+ showConflicts: directive.showConflicts === true
78
+ };
79
+ }
18
80
  export function routePersonas(prompt, explicitRoleFlag) {
19
81
  const normalizedPrompt = prompt.toLowerCase();
20
82
  const explicitRoles = parseRoleFlag(explicitRoleFlag);
@@ -60,7 +122,10 @@ export function renderDisclosure(roles, language) {
60
122
  : `Long Table consulted: ${labels.join(", ")}`;
61
123
  }
62
124
  export function buildPersonaGuidance(options) {
63
- const routing = routePersonas(options.prompt, options.roleFlag);
125
+ const directive = parseInvocationDirective(options.prompt);
126
+ const effectivePrompt = directive.cleanedPrompt;
127
+ const mergedRoleFlag = [options.roleFlag, directive.roles.join(",")].filter(Boolean).join(",");
128
+ const routing = routePersonas(effectivePrompt, mergedRoleFlag || undefined);
64
129
  const disclosure = renderDisclosure(routing.consultedRoles, routing.outputLanguage);
65
130
  const lines = [];
66
131
  lines.push(routing.outputLanguage === "ko"
@@ -74,12 +139,12 @@ export function buildPersonaGuidance(options) {
74
139
  ? "Ambiguity note: 편집자 관점인지 리뷰어 관점인지 애매합니다. 먼저 둘 중 무엇을 우선할지 짧게 확인하세요."
75
140
  : "Ambiguity note: it is unclear whether the user wants an editor view or reviewer view. Ask briefly before closing.");
76
141
  }
77
- if (options.panel) {
142
+ if (options.panel || directive.panel) {
78
143
  lines.push(routing.outputLanguage === "ko"
79
144
  ? "Return format: 1) Long Table synthesis 2) panel opinions by role 3) decision prompt to the researcher."
80
145
  : "Return format: 1) Long Table synthesis 2) panel opinions by role 3) decision prompt to the researcher.");
81
146
  }
82
- if (options.showConflicts) {
147
+ if (options.showConflicts || directive.showConflicts) {
83
148
  lines.push(routing.outputLanguage === "ko"
84
149
  ? "If roles disagree, show the conflict explicitly instead of forcing one answer."
85
150
  : "If roles disagree, show the conflict explicitly instead of forcing one answer.");
@@ -92,7 +157,7 @@ export function buildPersonaGuidance(options) {
92
157
  lines.push(routing.outputLanguage === "ko"
93
158
  ? "Do not show internal file-search logs, tool traces, or process commentary in the researcher-facing answer."
94
159
  : "Do not show internal file-search logs, tool traces, or process commentary in the researcher-facing answer.");
95
- lines.push(options.prompt.trim());
160
+ lines.push(effectivePrompt.trim());
96
161
  return {
97
162
  guidedPrompt: lines.join("\n\n"),
98
163
  routing
@@ -60,6 +60,15 @@ function buildStartHereGuide(project, session) {
60
60
  ? `"I want to work on ${session.currentGoal}. My current blocker is: ${session.currentBlocker}."`
61
61
  : `"I want to work on ${session.currentGoal}."`,
62
62
  "",
63
+ "## Explicit Long Table invocation inside Codex",
64
+ "- `lt explore: <question>`",
65
+ "- `lt review: <claim or plan>`",
66
+ "- `lt panel: <claim or plan>`",
67
+ "- `lt editor: <draft or positioning question>`",
68
+ "- `lt reviewer: <claim or section>`",
69
+ "- `lt methods: <design, measure, or analysis plan>`",
70
+ "- `lt commit: <decision that needs commitment>`",
71
+ "",
63
72
  "## What Long Table already knows in this directory",
64
73
  `- Current goal: ${session.currentGoal}`,
65
74
  ...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
@@ -88,6 +97,10 @@ function buildProjectAgentsMd(project, session) {
88
97
  "",
89
98
  "## Research-facing behavior",
90
99
  "- Treat researcher interaction as the primary task.",
100
+ "- If the user message starts with `lt `, `longtable `, `long table `, or `롱테이블 ` followed by a directive and `:`, treat it as an explicit Long Table invocation.",
101
+ "- Supported explicit directives are: explore, review, critique, draft, commit, panel, status, editor, reviewer, methods, theory, measurement, ethics, voice, venue.",
102
+ "- For explicit Long Table invocations, do not begin by scanning the workspace. Use the current session files first and answer as Long Table immediately.",
103
+ "- For general research requests in this workspace, prefer Long Table behavior before generic coding behavior.",
91
104
  "- Begin exploratory work with clarifying or tension questions before recommending a direction.",
92
105
  "- If you foreground role perspectives, disclose them with `Long Table consulted: ...`.",
93
106
  "- Keep one accountable synthesis, but do not hide meaningful disagreement.",
@@ -100,6 +113,7 @@ function buildProjectAgentsMd(project, session) {
100
113
  "- Read `.longtable/current-session.json` before giving substantial guidance.",
101
114
  "- Use `.longtable/project.json` as the project-level context.",
102
115
  "- Prefer the current goal and blocker over generic assumptions.",
116
+ "- Do not recursively search unrelated files unless the researcher explicitly asks for file analysis or code changes.",
103
117
  "",
104
118
  "## Scope",
105
119
  "- These instructions apply to this directory and its children."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "description": "Researcher-facing Long Table CLI on top of the legacy Diverga package surface",
6
6
  "type": "module",