@longtable/cli 0.1.7 → 0.1.9
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 +55 -91
- package/dist/cli.js +70 -39
- package/dist/persona-router.d.ts +1 -1
- package/dist/persona-router.js +6 -6
- package/dist/project-session.d.ts +13 -2
- package/dist/project-session.js +232 -150
- package/dist/prompt-aliases.js +32 -32
- package/package.json +10 -7
package/README.md
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
# @longtable/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Researcher-facing CLI for LongTable.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
LongTable is designed around a simple contract:
|
|
6
6
|
|
|
7
|
-
1.
|
|
8
|
-
2.
|
|
9
|
-
|
|
10
|
-
중요한 점:
|
|
11
|
-
|
|
12
|
-
- 이 두 명령은 **터미널에서 실행하는 셸 명령**입니다.
|
|
13
|
-
- Codex 채팅창에 `longtable start`를 입력하는 것이 아닙니다.
|
|
14
|
-
- `longtable start`로 프로젝트 작업공간을 만든 뒤, 그 디렉토리에서 `codex`를 여는 것이 기본 경로입니다.
|
|
7
|
+
1. seed the researcher profile once
|
|
8
|
+
2. create a workspace for each project
|
|
9
|
+
3. continue the research conversation inside that workspace
|
|
15
10
|
|
|
16
11
|
## Install
|
|
17
12
|
|
|
@@ -19,47 +14,7 @@ Long Table의 researcher-facing CLI입니다.
|
|
|
19
14
|
npm install -g @longtable/cli
|
|
20
15
|
```
|
|
21
16
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
longtable init --flow interview
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
여기서는 연구자 프로필과 기본 선호를 묻습니다.
|
|
29
|
-
|
|
30
|
-
- 연구 분야
|
|
31
|
-
- 연구자 역할
|
|
32
|
-
- challenge 강도
|
|
33
|
-
- 저자성/서사 관련 기본값
|
|
34
|
-
- Long Table이 보통 어디서부터 시작해야 하는지
|
|
35
|
-
|
|
36
|
-
## 2. Project start
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
longtable start
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
여기서는 실제 작업공간을 만듭니다.
|
|
43
|
-
|
|
44
|
-
- 프로젝트 이름
|
|
45
|
-
- 프로젝트 디렉토리 위치
|
|
46
|
-
- 현재 세션 목표
|
|
47
|
-
- 현재 blocker
|
|
48
|
-
- 필요한 관점
|
|
49
|
-
- disagreement 가시성
|
|
50
|
-
|
|
51
|
-
그리고 Long Table은:
|
|
52
|
-
|
|
53
|
-
- 프로젝트 디렉토리 생성
|
|
54
|
-
- `.longtable/` 메모리 파일 생성
|
|
55
|
-
- 프로젝트용 `AGENTS.md` 생성
|
|
56
|
-
- `START-HERE.md` 생성
|
|
57
|
-
- `NEXT-STEPS.md` 생성
|
|
58
|
-
- `SESSION-SNAPSHOT.md` 생성
|
|
59
|
-
|
|
60
|
-
을 수행합니다.
|
|
61
|
-
|
|
62
|
-
## Recommended flow
|
|
17
|
+
## Primary Flow
|
|
63
18
|
|
|
64
19
|
```bash
|
|
65
20
|
longtable init --flow interview
|
|
@@ -68,9 +23,7 @@ cd "<project-path>"
|
|
|
68
23
|
codex
|
|
69
24
|
```
|
|
70
25
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
돌아와서 다시 이어갈 때는:
|
|
26
|
+
Return later:
|
|
74
27
|
|
|
75
28
|
```bash
|
|
76
29
|
cd "<project-path>"
|
|
@@ -78,58 +31,69 @@ longtable resume
|
|
|
78
31
|
codex
|
|
79
32
|
```
|
|
80
33
|
|
|
81
|
-
##
|
|
82
|
-
|
|
83
|
-
프로젝트 디렉토리 안에서는 보통 그냥 `codex`를 열고 자연어로 연구를 시작하면 됩니다.
|
|
84
|
-
|
|
85
|
-
Codex 안에서 더 명시적으로 부르고 싶다면, 아래처럼 짧은 문법도 사용할 수 있습니다.
|
|
34
|
+
## What `longtable start` Creates
|
|
86
35
|
|
|
87
36
|
```text
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
37
|
+
<project>/
|
|
38
|
+
AGENTS.md
|
|
39
|
+
CURRENT.md
|
|
40
|
+
.longtable/
|
|
41
|
+
project.json
|
|
42
|
+
current-session.json
|
|
43
|
+
state.json
|
|
44
|
+
sessions/
|
|
93
45
|
```
|
|
94
46
|
|
|
95
|
-
|
|
47
|
+
## Artifact Contract
|
|
96
48
|
|
|
97
|
-
|
|
49
|
+
- `AGENTS.md`: runtime guidance for Codex
|
|
50
|
+
- `CURRENT.md`: human-facing current view regenerated from state
|
|
51
|
+
- `.longtable/project.json`: stable project identity
|
|
52
|
+
- `.longtable/current-session.json`: current session cursor
|
|
53
|
+
- `.longtable/state.json`: layered memory state
|
|
54
|
+
- `.longtable/sessions/`: historical snapshots
|
|
98
55
|
|
|
99
|
-
|
|
100
|
-
longtable roles
|
|
101
|
-
longtable resume --cwd "<project-path>"
|
|
102
|
-
longtable ask --cwd "<project-path>" --prompt "연구 질문을 어디서부터 좁혀야 할지 모르겠어."
|
|
103
|
-
longtable review --cwd "<project-path>" --prompt "방법론적으로 어디가 취약한지 말해줘." --role methods_critic
|
|
104
|
-
```
|
|
56
|
+
## Why This Shape
|
|
105
57
|
|
|
106
|
-
|
|
58
|
+
The CLI tries to keep the root simple for novice researchers while preserving enough structure for power users and downstream tooling.
|
|
107
59
|
|
|
108
|
-
|
|
109
|
-
longtable roles
|
|
110
|
-
```
|
|
60
|
+
The memory model distinguishes:
|
|
111
61
|
|
|
112
|
-
|
|
62
|
+
- explicit state
|
|
63
|
+
- working state
|
|
64
|
+
- inferred hypotheses
|
|
65
|
+
- open tensions
|
|
66
|
+
- narrative traces
|
|
113
67
|
|
|
114
|
-
|
|
115
|
-
- `reviewer`
|
|
116
|
-
- `methods_critic`
|
|
117
|
-
- `voice_keeper`
|
|
68
|
+
This is how LongTable avoids turning tacit knowledge into fake certainty.
|
|
118
69
|
|
|
119
|
-
##
|
|
70
|
+
## Commands
|
|
120
71
|
|
|
121
72
|
```bash
|
|
122
|
-
longtable
|
|
123
|
-
longtable
|
|
73
|
+
longtable init
|
|
74
|
+
longtable start
|
|
75
|
+
longtable resume --cwd "<project-path>"
|
|
76
|
+
longtable roles
|
|
77
|
+
longtable ask --cwd "<project-path>" --prompt "..."
|
|
124
78
|
```
|
|
125
79
|
|
|
126
|
-
|
|
127
|
-
사용자에게 약속하는 주 경로는 아닙니다.
|
|
80
|
+
## Inside Codex
|
|
128
81
|
|
|
129
|
-
|
|
82
|
+
Natural language should be the default.
|
|
130
83
|
|
|
131
|
-
|
|
132
|
-
- `longtable start`
|
|
133
|
-
- 프로젝트 디렉토리에서 `codex`
|
|
84
|
+
Explicit short forms are available when needed:
|
|
134
85
|
|
|
135
|
-
|
|
86
|
+
```text
|
|
87
|
+
lt explore: Where should I narrow the question first?
|
|
88
|
+
lt review: What is weak in this claim?
|
|
89
|
+
lt panel: Show me the disagreement before I commit.
|
|
90
|
+
lt methods: Where is the design vulnerable?
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Validation
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm install
|
|
97
|
+
npm run typecheck
|
|
98
|
+
npm run build
|
|
99
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
4
|
import { emitKeypressEvents } from "node:readline";
|
|
4
5
|
import { createInterface } from "node:readline/promises";
|
|
5
6
|
import { stdin as input, stdout as output, cwd, exit } from "node:process";
|
|
6
|
-
import { resolve } from "node:path";
|
|
7
|
+
import { dirname, resolve } from "node:path";
|
|
7
8
|
import { homedir } from "node:os";
|
|
8
|
-
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@
|
|
9
|
-
import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@
|
|
9
|
+
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@longtable/setup";
|
|
10
|
+
import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@longtable/provider-codex";
|
|
10
11
|
import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodexPromptAliases, resolveCodexPromptsDir } from "./prompt-aliases.js";
|
|
11
12
|
import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router.js";
|
|
12
13
|
import { PERSONA_DEFINITIONS } from "./personas.js";
|
|
13
|
-
import { createOrUpdateProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary } from "./project-session.js";
|
|
14
|
+
import { createOrUpdateProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
|
|
14
15
|
const VALID_MODES = new Set([
|
|
15
16
|
"explore",
|
|
16
17
|
"review",
|
|
@@ -154,11 +155,11 @@ function buildSetupFlowChoices() {
|
|
|
154
155
|
];
|
|
155
156
|
}
|
|
156
157
|
function renderSetupHeader(flow) {
|
|
157
|
-
const title = flow === "interview" ? "
|
|
158
|
+
const title = flow === "interview" ? "LongTable Setup Interview" : "LongTable Quickstart";
|
|
158
159
|
const subtitle = flow === "interview"
|
|
159
160
|
? "We will ask about your research persona, challenge preferences, and authorship defaults."
|
|
160
|
-
: "We will capture the minimum profile needed to start using
|
|
161
|
-
return [renderBrandBanner("
|
|
161
|
+
: "We will capture the minimum profile needed to start using LongTable.";
|
|
162
|
+
return [renderBrandBanner("LongTable", "Research workspace setup"), "", renderSectionCard(title, [subtitle])].join("\n");
|
|
162
163
|
}
|
|
163
164
|
function renderQuestionHeader(index, total, section, prompt) {
|
|
164
165
|
return [
|
|
@@ -175,7 +176,7 @@ function questionSection(questionId) {
|
|
|
175
176
|
return "Interaction style";
|
|
176
177
|
}
|
|
177
178
|
if (questionId === "weakestDomain" || questionId === "panelPreference") {
|
|
178
|
-
return "How
|
|
179
|
+
return "How LongTable should challenge you";
|
|
179
180
|
}
|
|
180
181
|
return "Authorship and voice";
|
|
181
182
|
}
|
|
@@ -242,6 +243,29 @@ function resolveInteractiveProjectPath(parentOrPath, projectName) {
|
|
|
242
243
|
}
|
|
243
244
|
return resolve(normalized);
|
|
244
245
|
}
|
|
246
|
+
async function verifyWritableWorkspaceParent(projectPath) {
|
|
247
|
+
const parentDir = dirname(resolve(projectPath));
|
|
248
|
+
const probePrefix = resolve(parentDir, ".longtable-permission-check-");
|
|
249
|
+
try {
|
|
250
|
+
const created = await mkdtemp(probePrefix);
|
|
251
|
+
await rm(created, { recursive: true, force: true });
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
255
|
+
throw new Error([
|
|
256
|
+
`LongTable could not create a project workspace under: ${parentDir}`,
|
|
257
|
+
"Reason: your current shell process cannot write to that parent directory.",
|
|
258
|
+
"",
|
|
259
|
+
"What to try next:",
|
|
260
|
+
`- test it directly: mkdir -p "${resolve(parentDir, "_longtable_write_test")}"`,
|
|
261
|
+
"- if that fails too, this is an OS/disk permission problem rather than a LongTable command problem",
|
|
262
|
+
"- try a known-writable path such as ~/Research",
|
|
263
|
+
"- or choose a different existing parent directory when LongTable asks where the project should live",
|
|
264
|
+
"",
|
|
265
|
+
`Original error: ${message}`
|
|
266
|
+
].join("\n"));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
245
269
|
async function promptChoiceByNumber(rl, prompt, choices) {
|
|
246
270
|
while (true) {
|
|
247
271
|
const answer = await rl.question(`${prompt}\n${renderChoices(choices)}\nSelect one number: `);
|
|
@@ -457,7 +481,7 @@ async function collectInteractiveAnswers(initialFlow) {
|
|
|
457
481
|
const rl = createInterface({ input, output });
|
|
458
482
|
try {
|
|
459
483
|
const flow = initialFlow ??
|
|
460
|
-
(await promptChoice(rl, "How would you like to set up
|
|
484
|
+
(await promptChoice(rl, "How would you like to set up LongTable?", buildSetupFlowChoices()));
|
|
461
485
|
console.log("");
|
|
462
486
|
console.log(renderSetupHeader(flow));
|
|
463
487
|
console.log("");
|
|
@@ -535,11 +559,11 @@ async function collectProjectInterview(setup, args) {
|
|
|
535
559
|
try {
|
|
536
560
|
if (needsInteractivePrompts) {
|
|
537
561
|
console.log("");
|
|
538
|
-
console.log(renderBrandBanner("
|
|
562
|
+
console.log(renderBrandBanner("LongTable", "Project workspace interview"));
|
|
539
563
|
console.log("");
|
|
540
|
-
console.log(renderSectionCard("
|
|
564
|
+
console.log(renderSectionCard("LongTable Project Start", [
|
|
541
565
|
"We will create a project workspace and a session memory seed for today's work.",
|
|
542
|
-
"At the end,
|
|
566
|
+
"At the end, LongTable will tell you exactly which directory to open in Codex."
|
|
543
567
|
]));
|
|
544
568
|
console.log("");
|
|
545
569
|
}
|
|
@@ -551,7 +575,7 @@ async function collectProjectInterview(setup, args) {
|
|
|
551
575
|
const suggestedPath = resolveInteractiveProjectPath(suggestedParentDir, projectName);
|
|
552
576
|
const projectPath = (typeof args.path === "string" && args.path.trim()
|
|
553
577
|
? normalizeUserPath(args.path.trim())
|
|
554
|
-
: resolveInteractiveProjectPath((await promptText(rl, renderQuestionHeader(2, 6, "Project interview", `Which parent directory should contain this project?\
|
|
578
|
+
: resolveInteractiveProjectPath((await promptText(rl, renderQuestionHeader(2, 6, "Project interview", `Which parent directory should contain this project?\nLongTable will create this folder:\n${suggestedPath}`), true)), projectName));
|
|
555
579
|
const currentGoal = (typeof args.goal === "string" && args.goal.trim()) ||
|
|
556
580
|
(await promptText(rl, renderQuestionHeader(3, 6, "Current session", "What are you trying to accomplish in this session?"), true));
|
|
557
581
|
const currentBlocker = (typeof args.blocker === "string" && args.blocker.trim()) ||
|
|
@@ -564,7 +588,7 @@ async function collectProjectInterview(setup, args) {
|
|
|
564
588
|
{
|
|
565
589
|
id: "synthesis_only",
|
|
566
590
|
label: "Synthesis only",
|
|
567
|
-
description: "Show one
|
|
591
|
+
description: "Show one LongTable answer unless I ask for more."
|
|
568
592
|
},
|
|
569
593
|
{
|
|
570
594
|
id: "show_on_conflict",
|
|
@@ -685,7 +709,7 @@ async function runInit(args) {
|
|
|
685
709
|
console.log("- Start here: `longtable start`.");
|
|
686
710
|
console.log("- If you want a direct natural-language entry: `longtable ask --prompt \"...\"`.");
|
|
687
711
|
console.log("- Codex prompt files are available as an experimental integration, not the primary path.");
|
|
688
|
-
console.log("- Suggested next action: create a project workspace and let
|
|
712
|
+
console.log("- Suggested next action: create a project workspace and let LongTable interview the current session.");
|
|
689
713
|
}
|
|
690
714
|
}
|
|
691
715
|
async function runShow(args) {
|
|
@@ -743,7 +767,7 @@ async function runCodexPersistInit(args) {
|
|
|
743
767
|
console.log("- Start here: `longtable start`.");
|
|
744
768
|
console.log("- If you want a direct natural-language entry: `longtable ask --prompt \"...\"`.");
|
|
745
769
|
console.log("- Codex prompt files are available as an experimental integration, not the primary path.");
|
|
746
|
-
console.log("- Suggested next action: create a project workspace and let
|
|
770
|
+
console.log("- Suggested next action: create a project workspace and let LongTable interview the current session.");
|
|
747
771
|
}
|
|
748
772
|
}
|
|
749
773
|
async function resolvePrompt(prompt) {
|
|
@@ -755,7 +779,7 @@ async function resolvePrompt(prompt) {
|
|
|
755
779
|
}
|
|
756
780
|
const rl = createInterface({ input, output });
|
|
757
781
|
try {
|
|
758
|
-
return (await rl.question("What should
|
|
782
|
+
return (await rl.question("What should LongTable help with?\n> ")).trim();
|
|
759
783
|
}
|
|
760
784
|
finally {
|
|
761
785
|
rl.close();
|
|
@@ -818,7 +842,7 @@ async function buildProjectAwarePrompt(prompt, workingDirectory) {
|
|
|
818
842
|
return { prompt, projectContextFound: false };
|
|
819
843
|
}
|
|
820
844
|
const lines = [
|
|
821
|
-
"
|
|
845
|
+
"LongTable project context",
|
|
822
846
|
`project: ${context.project.projectName}`,
|
|
823
847
|
`current session goal: ${context.session.currentGoal}`,
|
|
824
848
|
...(context.session.currentBlocker ? [`current blocker: ${context.session.currentBlocker}`] : []),
|
|
@@ -912,8 +936,8 @@ async function runRoles(args) {
|
|
|
912
936
|
console.log(JSON.stringify(payload, null, 2));
|
|
913
937
|
return;
|
|
914
938
|
}
|
|
915
|
-
console.log("
|
|
916
|
-
console.log("These are perspectives
|
|
939
|
+
console.log("LongTable roles");
|
|
940
|
+
console.log("These are perspectives LongTable can consult when relevant.");
|
|
917
941
|
console.log("Inside Codex, explicit forms like `lt editor: ...` and `lt methods: ...` are stronger than plain natural language.");
|
|
918
942
|
console.log("");
|
|
919
943
|
for (const persona of payload) {
|
|
@@ -927,9 +951,10 @@ async function runStart(args) {
|
|
|
927
951
|
const setupPath = typeof args.setup === "string" ? args.setup : undefined;
|
|
928
952
|
const existingSetup = await loadOptionalSetup(setupPath);
|
|
929
953
|
if (!existingSetup) {
|
|
930
|
-
throw new Error("
|
|
954
|
+
throw new Error("LongTable global setup is missing. Run `longtable init --flow interview` first.");
|
|
931
955
|
}
|
|
932
956
|
const interview = await collectProjectInterview(existingSetup, args);
|
|
957
|
+
await verifyWritableWorkspaceParent(interview.projectPath);
|
|
933
958
|
const context = await createOrUpdateProjectWorkspace({
|
|
934
959
|
projectName: interview.projectName,
|
|
935
960
|
projectPath: interview.projectPath,
|
|
@@ -943,8 +968,12 @@ async function runStart(args) {
|
|
|
943
968
|
console.log(JSON.stringify({
|
|
944
969
|
project: context.project,
|
|
945
970
|
session: context.session,
|
|
946
|
-
|
|
947
|
-
|
|
971
|
+
files: {
|
|
972
|
+
project: context.projectFilePath,
|
|
973
|
+
session: context.sessionFilePath,
|
|
974
|
+
state: context.stateFilePath,
|
|
975
|
+
current: context.currentFilePath
|
|
976
|
+
}
|
|
948
977
|
}, null, 2));
|
|
949
978
|
return;
|
|
950
979
|
}
|
|
@@ -954,7 +983,7 @@ async function runStart(args) {
|
|
|
954
983
|
`1. cd "${context.project.projectPath}"`,
|
|
955
984
|
"2. run `codex` in that directory",
|
|
956
985
|
"3. begin with your current goal in natural language",
|
|
957
|
-
"4. if you return later,
|
|
986
|
+
"4. if you return later, open `CURRENT.md` or run `longtable resume`",
|
|
958
987
|
"",
|
|
959
988
|
`Suggested first message: ${context.session.currentBlocker ? `"I want to work on ${context.session.currentGoal}. My current blocker is ${context.session.currentBlocker}."` : `"I want to work on ${context.session.currentGoal}."`}`,
|
|
960
989
|
"",
|
|
@@ -965,22 +994,24 @@ async function runResume(args) {
|
|
|
965
994
|
const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
|
|
966
995
|
const context = await loadProjectContextFromDirectory(workingDirectory);
|
|
967
996
|
if (!context) {
|
|
968
|
-
throw new Error("No
|
|
997
|
+
throw new Error("No LongTable project workspace was found here. Run `longtable start` first or pass --cwd.");
|
|
969
998
|
}
|
|
999
|
+
await syncCurrentWorkspaceView(context);
|
|
970
1000
|
const payload = {
|
|
971
1001
|
project: context.project,
|
|
972
1002
|
session: context.session,
|
|
973
1003
|
files: {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1004
|
+
current: resolve(context.project.projectPath, "CURRENT.md"),
|
|
1005
|
+
project: context.projectFilePath,
|
|
1006
|
+
session: context.sessionFilePath,
|
|
1007
|
+
state: context.stateFilePath
|
|
977
1008
|
}
|
|
978
1009
|
};
|
|
979
1010
|
if (args.json === true) {
|
|
980
1011
|
console.log(JSON.stringify(payload, null, 2));
|
|
981
1012
|
return;
|
|
982
1013
|
}
|
|
983
|
-
console.log(renderSectionCard("
|
|
1014
|
+
console.log(renderSectionCard("LongTable Resume", [
|
|
984
1015
|
`Project: ${context.project.projectName}`,
|
|
985
1016
|
`Path: ${context.project.projectPath}`,
|
|
986
1017
|
`Current goal: ${context.session.currentGoal}`,
|
|
@@ -988,19 +1019,17 @@ async function runResume(args) {
|
|
|
988
1019
|
`Requested perspectives: ${context.session.requestedPerspectives.length > 0 ? context.session.requestedPerspectives.join(", ") : "auto"}`,
|
|
989
1020
|
`Disagreement: ${context.session.disagreementPreference}`,
|
|
990
1021
|
"",
|
|
991
|
-
"
|
|
992
|
-
`- ${payload.files.
|
|
993
|
-
`- ${payload.files.nextSteps}`,
|
|
994
|
-
`- ${payload.files.sessionSnapshot}`,
|
|
1022
|
+
"Current file:",
|
|
1023
|
+
`- ${payload.files.current}`,
|
|
995
1024
|
"",
|
|
996
|
-
`Suggested restart message: ${context.session.currentBlocker ? `
|
|
1025
|
+
`Suggested restart message: "${context.session.resumeHint ?? (context.session.currentBlocker ? `I want to continue ${context.session.currentGoal}. The unresolved blocker is ${context.session.currentBlocker}.` : `I want to continue ${context.session.currentGoal}.`)}"`
|
|
997
1026
|
]));
|
|
998
1027
|
}
|
|
999
1028
|
async function runCodexSubcommand(subcommand, args) {
|
|
1000
1029
|
const customDir = typeof args.dir === "string" ? args.dir : undefined;
|
|
1001
1030
|
if (subcommand === "install-prompts") {
|
|
1002
1031
|
const installed = await installCodexPromptAliases(customDir);
|
|
1003
|
-
console.log(`Installed ${installed.length}
|
|
1032
|
+
console.log(`Installed ${installed.length} LongTable prompt aliases in ${resolveCodexPromptsDir(customDir)}`);
|
|
1004
1033
|
console.log("Note: prompt-file discovery depends on the Codex build. Treat this as an experimental integration.");
|
|
1005
1034
|
for (const prompt of installed) {
|
|
1006
1035
|
console.log(`- /prompts:${prompt.name}`);
|
|
@@ -1013,7 +1042,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
1013
1042
|
}
|
|
1014
1043
|
if (subcommand === "remove-prompts") {
|
|
1015
1044
|
const removed = await removeCodexPromptAliases(customDir);
|
|
1016
|
-
console.log(`Removed ${removed.length}
|
|
1045
|
+
console.log(`Removed ${removed.length} LongTable prompt aliases from ${resolveCodexPromptsDir(customDir)}`);
|
|
1017
1046
|
return;
|
|
1018
1047
|
}
|
|
1019
1048
|
if (subcommand === "status") {
|
|
@@ -1032,7 +1061,7 @@ async function runCodexSubcommand(subcommand, args) {
|
|
|
1032
1061
|
console.log(JSON.stringify(status, null, 2));
|
|
1033
1062
|
return;
|
|
1034
1063
|
}
|
|
1035
|
-
console.log("
|
|
1064
|
+
console.log("LongTable Codex status");
|
|
1036
1065
|
console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
|
|
1037
1066
|
console.log(`- codex runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
|
|
1038
1067
|
console.log(`- prompt aliases dir: ${status.promptsDir}`);
|
|
@@ -1101,7 +1130,9 @@ main().catch((error) => {
|
|
|
1101
1130
|
if (message === "Setup cancelled.") {
|
|
1102
1131
|
exit(1);
|
|
1103
1132
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1133
|
+
if (message.startsWith("Unknown command:") || message.startsWith("Invalid ")) {
|
|
1134
|
+
console.error("");
|
|
1135
|
+
console.error(usage());
|
|
1136
|
+
}
|
|
1106
1137
|
exit(1);
|
|
1107
1138
|
});
|
package/dist/persona-router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CanonicalPersona } from "./personas.js";
|
|
2
|
-
import type { InteractionMode } from "@
|
|
2
|
+
import type { InteractionMode } from "@longtable/core";
|
|
3
3
|
export type OutputLanguage = "ko" | "en";
|
|
4
4
|
export interface PersonaRoutingResult {
|
|
5
5
|
outputLanguage: OutputLanguage;
|
package/dist/persona-router.js
CHANGED
|
@@ -118,8 +118,8 @@ export function renderDisclosure(roles, language) {
|
|
|
118
118
|
}
|
|
119
119
|
const labels = roles.map((role) => getPersonaDefinition(role).label);
|
|
120
120
|
return language === "ko"
|
|
121
|
-
? `
|
|
122
|
-
: `
|
|
121
|
+
? `LongTable consulted: ${labels.join(", ")}`
|
|
122
|
+
: `LongTable consulted: ${labels.join(", ")}`;
|
|
123
123
|
}
|
|
124
124
|
export function buildPersonaGuidance(options) {
|
|
125
125
|
const directive = parseInvocationDirective(options.prompt);
|
|
@@ -129,8 +129,8 @@ export function buildPersonaGuidance(options) {
|
|
|
129
129
|
const disclosure = renderDisclosure(routing.consultedRoles, routing.outputLanguage);
|
|
130
130
|
const lines = [];
|
|
131
131
|
lines.push(routing.outputLanguage === "ko"
|
|
132
|
-
? `
|
|
133
|
-
: `
|
|
132
|
+
? `LongTable mode: ${options.mode[0].toUpperCase()}${options.mode.slice(1)}`
|
|
133
|
+
: `LongTable mode: ${options.mode[0].toUpperCase()}${options.mode.slice(1)}`);
|
|
134
134
|
if (disclosure) {
|
|
135
135
|
lines.push(disclosure);
|
|
136
136
|
}
|
|
@@ -141,8 +141,8 @@ export function buildPersonaGuidance(options) {
|
|
|
141
141
|
}
|
|
142
142
|
if (options.panel || directive.panel) {
|
|
143
143
|
lines.push(routing.outputLanguage === "ko"
|
|
144
|
-
? "Return format: 1)
|
|
145
|
-
: "Return format: 1)
|
|
144
|
+
? "Return format: 1) LongTable synthesis 2) panel opinions by role 3) decision prompt to the researcher."
|
|
145
|
+
: "Return format: 1) LongTable synthesis 2) panel opinions by role 3) decision prompt to the researcher.");
|
|
146
146
|
}
|
|
147
147
|
if (options.showConflicts || directive.showConflicts) {
|
|
148
148
|
lines.push(routing.outputLanguage === "ko"
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { SetupPersistedOutput } from "@
|
|
1
|
+
import type { SetupPersistedOutput } from "@longtable/setup";
|
|
2
2
|
export type ProjectDisagreementPreference = "synthesis_only" | "show_on_conflict" | "always_visible";
|
|
3
3
|
export interface LongTableProjectRecord {
|
|
4
4
|
schemaVersion: 1;
|
|
5
|
-
product: "
|
|
5
|
+
product: "LongTable";
|
|
6
6
|
projectName: string;
|
|
7
7
|
projectPath: string;
|
|
8
8
|
createdAt: string;
|
|
9
|
+
contractVersion?: "workspace-v2";
|
|
10
|
+
locale?: string;
|
|
9
11
|
globalSetupSummary: {
|
|
10
12
|
field: string;
|
|
11
13
|
careerStage: string;
|
|
@@ -20,20 +22,29 @@ export interface LongTableSessionRecord {
|
|
|
20
22
|
schemaVersion: 1;
|
|
21
23
|
id: string;
|
|
22
24
|
createdAt: string;
|
|
25
|
+
lastUpdatedAt?: string;
|
|
23
26
|
projectName: string;
|
|
24
27
|
projectPath: string;
|
|
25
28
|
currentGoal: string;
|
|
26
29
|
currentBlocker?: string;
|
|
30
|
+
nextAction?: string;
|
|
31
|
+
openQuestions?: string[];
|
|
27
32
|
requestedPerspectives: string[];
|
|
28
33
|
disagreementPreference: ProjectDisagreementPreference;
|
|
34
|
+
activeModes?: string[];
|
|
35
|
+
resumeHint?: string;
|
|
36
|
+
locale?: string;
|
|
29
37
|
}
|
|
30
38
|
export interface LongTableProjectContext {
|
|
31
39
|
project: LongTableProjectRecord;
|
|
32
40
|
session: LongTableSessionRecord;
|
|
33
41
|
projectFilePath: string;
|
|
34
42
|
sessionFilePath: string;
|
|
43
|
+
stateFilePath: string;
|
|
44
|
+
currentFilePath: string;
|
|
35
45
|
metaDir: string;
|
|
36
46
|
}
|
|
47
|
+
export declare function syncCurrentWorkspaceView(context: LongTableProjectContext): Promise<string>;
|
|
37
48
|
export declare function createOrUpdateProjectWorkspace(options: {
|
|
38
49
|
projectName: string;
|
|
39
50
|
projectPath: string;
|
package/dist/project-session.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
3
4
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { createEmptyResearchState } from "@
|
|
5
|
+
import { createEmptyResearchState } from "@longtable/memory";
|
|
6
|
+
const CURRENT_FILE_NAME = "CURRENT.md";
|
|
7
|
+
const LEGACY_ROOT_FILES = ["LONGTABLE.md", "START-HERE.md", "NEXT-STEPS.md", "SESSION-SNAPSHOT.md"];
|
|
5
8
|
function nowIso() {
|
|
6
9
|
return new Date().toISOString();
|
|
7
10
|
}
|
|
@@ -16,160 +19,168 @@ function slugify(input) {
|
|
|
16
19
|
function resolveMetaDir(projectPath) {
|
|
17
20
|
return join(projectPath, ".longtable");
|
|
18
21
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
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
|
-
"- For factual, current, or external claims, attach source links or local file references whenever possible.",
|
|
38
|
-
"- If you cannot source a statement, label it as an inference or estimate.",
|
|
39
|
-
"- Do not expose internal tool logs or process commentary in researcher-facing answers.",
|
|
40
|
-
"",
|
|
41
|
-
"## Session files",
|
|
42
|
-
"- `.longtable/project.json` contains project-level metadata.",
|
|
43
|
-
"- `.longtable/current-session.json` contains the current session goal and blocker."
|
|
44
|
-
];
|
|
45
|
-
return lines.join("\n");
|
|
22
|
+
function resolveStateFilePath(projectPath) {
|
|
23
|
+
return join(resolveMetaDir(projectPath), "state.json");
|
|
46
24
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
"# Start Here",
|
|
50
|
-
"",
|
|
51
|
-
`Project: ${project.projectName}`,
|
|
52
|
-
"",
|
|
53
|
-
"This workspace was created by Long Table for a single research project.",
|
|
54
|
-
"",
|
|
55
|
-
"## What to do now",
|
|
56
|
-
"1. Open Codex in this directory.",
|
|
57
|
-
"2. Start with your current goal, not a shell command.",
|
|
58
|
-
"3. Let Long Table keep disagreement visible when it matters.",
|
|
59
|
-
"4. If you ask for factual or current external information, expect sources or an explicit inference label.",
|
|
60
|
-
"",
|
|
61
|
-
"## Suggested first message",
|
|
62
|
-
session.currentBlocker
|
|
63
|
-
? `"I want to work on ${session.currentGoal}. My current blocker is: ${session.currentBlocker}."`
|
|
64
|
-
: `"I want to work on ${session.currentGoal}."`,
|
|
65
|
-
"",
|
|
66
|
-
"## Explicit Long Table invocation inside Codex",
|
|
67
|
-
"- `lt explore: <question>`",
|
|
68
|
-
"- `lt review: <claim or plan>`",
|
|
69
|
-
"- `lt panel: <claim or plan>`",
|
|
70
|
-
"- `lt editor: <draft or positioning question>`",
|
|
71
|
-
"- `lt reviewer: <claim or section>`",
|
|
72
|
-
"- `lt methods: <design, measure, or analysis plan>`",
|
|
73
|
-
"- `lt commit: <decision that needs commitment>`",
|
|
74
|
-
"",
|
|
75
|
-
"## What Long Table already knows in this directory",
|
|
76
|
-
`- Current goal: ${session.currentGoal}`,
|
|
77
|
-
...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
|
|
78
|
-
`- Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
79
|
-
`- Disagreement visibility: ${session.disagreementPreference}`,
|
|
80
|
-
"",
|
|
81
|
-
"## Files",
|
|
82
|
-
"- `AGENTS.md` tells Codex how to behave in this workspace.",
|
|
83
|
-
"- `.longtable/current-session.json` stores the current session goal and blocker.",
|
|
84
|
-
"- `.longtable/project.json` stores the project-level record.",
|
|
85
|
-
"- `NEXT-STEPS.md` stores the immediate next actions.",
|
|
86
|
-
"- `SESSION-SNAPSHOT.md` stores the short resume snapshot."
|
|
87
|
-
];
|
|
88
|
-
return lines.join("\n");
|
|
25
|
+
function resolveCurrentFilePath(projectPath) {
|
|
26
|
+
return join(projectPath, CURRENT_FILE_NAME);
|
|
89
27
|
}
|
|
90
|
-
function
|
|
91
|
-
|
|
28
|
+
function normalizeLocale(locale) {
|
|
29
|
+
if ((locale ?? "").toLowerCase().startsWith("ko")) {
|
|
30
|
+
return "ko";
|
|
31
|
+
}
|
|
32
|
+
return "en";
|
|
33
|
+
}
|
|
34
|
+
function resolveUserLocale() {
|
|
35
|
+
const envLocale = process.env.LC_ALL ??
|
|
36
|
+
process.env.LC_MESSAGES ??
|
|
37
|
+
process.env.LANG ??
|
|
38
|
+
"";
|
|
39
|
+
if (envLocale && !["c", "c.utf-8", "posix"].includes(envLocale.toLowerCase())) {
|
|
40
|
+
return normalizeLocale(envLocale);
|
|
41
|
+
}
|
|
42
|
+
if (process.platform === "darwin") {
|
|
43
|
+
try {
|
|
44
|
+
const appleLocale = execSync("defaults read -g AppleLocale", { encoding: "utf8" }).trim();
|
|
45
|
+
if (appleLocale) {
|
|
46
|
+
return normalizeLocale(appleLocale);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Ignore and fall back to Intl below.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return normalizeLocale(Intl.DateTimeFormat().resolvedOptions().locale);
|
|
54
|
+
}
|
|
55
|
+
function buildFirstQuestion(session) {
|
|
56
|
+
return session.currentBlocker
|
|
92
57
|
? `What would reduce the uncertainty around "${session.currentBlocker}" first?`
|
|
93
58
|
: `What is the first concrete question that would move "${session.currentGoal}" forward?`;
|
|
59
|
+
}
|
|
60
|
+
function buildOpenQuestions(session) {
|
|
61
|
+
const firstQuestion = buildFirstQuestion(session);
|
|
62
|
+
return session.currentBlocker
|
|
63
|
+
? [
|
|
64
|
+
firstQuestion,
|
|
65
|
+
`What evidence would let you decide whether "${session.currentBlocker}" is a knowledge gap, a coding rule gap, or a data gap?`
|
|
66
|
+
]
|
|
67
|
+
: [
|
|
68
|
+
firstQuestion,
|
|
69
|
+
`What would count as a good first outcome for "${session.currentGoal}" in this session?`
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
function buildNextAction(session) {
|
|
73
|
+
return session.currentBlocker
|
|
74
|
+
? `Open with the blocker, then ask LongTable to surface the first high-leverage uncertainty around "${session.currentBlocker}".`
|
|
75
|
+
: "Open with your current goal in one sentence, then ask LongTable for the first concrete research move.";
|
|
76
|
+
}
|
|
77
|
+
function buildResumeHint(session) {
|
|
78
|
+
return session.currentBlocker
|
|
79
|
+
? `I want to continue ${session.currentGoal}. The unresolved blocker is ${session.currentBlocker}.`
|
|
80
|
+
: `I want to continue ${session.currentGoal}.`;
|
|
81
|
+
}
|
|
82
|
+
function buildCurrentGuide(project, session) {
|
|
83
|
+
const locale = normalizeLocale(session.locale ?? project.locale);
|
|
84
|
+
const openQuestions = session.openQuestions && session.openQuestions.length > 0
|
|
85
|
+
? session.openQuestions
|
|
86
|
+
: buildOpenQuestions(session);
|
|
87
|
+
const nextAction = session.nextAction ?? buildNextAction(session);
|
|
88
|
+
const resumeHint = session.resumeHint ?? buildResumeHint(session);
|
|
89
|
+
const suggestedPrompt = `lt explore: ${openQuestions[0]}`;
|
|
90
|
+
if (locale === "ko") {
|
|
91
|
+
return [
|
|
92
|
+
"# CURRENT",
|
|
93
|
+
"",
|
|
94
|
+
`Project: ${project.projectName}`,
|
|
95
|
+
"",
|
|
96
|
+
"이 파일은 `.longtable/current-session.json`과 `.longtable/state.json`에서 재생성되는 현재 작업 뷰입니다.",
|
|
97
|
+
"",
|
|
98
|
+
"## 지금 초점",
|
|
99
|
+
`- 현재 목표: ${session.currentGoal}`,
|
|
100
|
+
...(session.currentBlocker ? [`- 현재 blocker: ${session.currentBlocker}`] : []),
|
|
101
|
+
`- 다음 액션: ${nextAction}`,
|
|
102
|
+
`- 관점: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
103
|
+
`- disagreement: ${session.disagreementPreference}`,
|
|
104
|
+
"",
|
|
105
|
+
"## 열린 질문",
|
|
106
|
+
...openQuestions.map((question) => `- ${question}`),
|
|
107
|
+
"",
|
|
108
|
+
"## 다시 시작 문장",
|
|
109
|
+
`- "${resumeHint}"`,
|
|
110
|
+
"",
|
|
111
|
+
"## 빠른 시작",
|
|
112
|
+
"- 이 디렉토리에서 `codex`를 엽니다.",
|
|
113
|
+
`- 첫 메시지는 보통 \`${suggestedPrompt}\` 정도면 충분합니다.`,
|
|
114
|
+
"",
|
|
115
|
+
"## 증거 규칙",
|
|
116
|
+
"- 외부 사실이나 현재 정보는 source를 붙이거나 inference로 낮춥니다."
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
94
119
|
return [
|
|
95
|
-
"#
|
|
120
|
+
"# CURRENT",
|
|
96
121
|
"",
|
|
97
122
|
`Project: ${project.projectName}`,
|
|
98
123
|
"",
|
|
99
|
-
"
|
|
100
|
-
"1. Open Codex in this directory.",
|
|
101
|
-
"2. State your current goal in one sentence.",
|
|
102
|
-
"3. Ask Long Table to surface disagreement before closing on a plan.",
|
|
124
|
+
"This file is regenerated from `.longtable/current-session.json` and `.longtable/state.json`.",
|
|
103
125
|
"",
|
|
104
|
-
"##
|
|
105
|
-
`-
|
|
106
|
-
`-
|
|
107
|
-
`-
|
|
108
|
-
""
|
|
109
|
-
|
|
110
|
-
"- Read `SESSION-SNAPSHOT.md` first.",
|
|
111
|
-
"- Continue from the unresolved blocker rather than restating the whole project.",
|
|
126
|
+
"## Focus Now",
|
|
127
|
+
`- Current goal: ${session.currentGoal}`,
|
|
128
|
+
...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
|
|
129
|
+
`- Next action: ${nextAction}`,
|
|
130
|
+
`- Perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
131
|
+
`- Disagreement: ${session.disagreementPreference}`,
|
|
112
132
|
"",
|
|
113
|
-
"##
|
|
114
|
-
|
|
115
|
-
].join("\n");
|
|
116
|
-
}
|
|
117
|
-
function buildSessionSnapshot(project, session) {
|
|
118
|
-
return [
|
|
119
|
-
"# Session Snapshot",
|
|
133
|
+
"## Open Questions",
|
|
134
|
+
...openQuestions.map((question) => `- ${question}`),
|
|
120
135
|
"",
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
...(session.currentBlocker ? [`Current blocker: ${session.currentBlocker}`] : []),
|
|
124
|
-
`Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
125
|
-
`Disagreement visibility: ${session.disagreementPreference}`,
|
|
136
|
+
"## Restart Prompt",
|
|
137
|
+
`- "${resumeHint}"`,
|
|
126
138
|
"",
|
|
127
|
-
"##
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
: `"I want to continue ${session.currentGoal}."`,
|
|
139
|
+
"## Quick Start",
|
|
140
|
+
"- Open `codex` in this directory.",
|
|
141
|
+
`- A good first message is usually \`${suggestedPrompt}\`.`,
|
|
131
142
|
"",
|
|
132
|
-
"##
|
|
133
|
-
"-
|
|
134
|
-
"- If factual or external claims matter, Long Table should cite sources or mark them as inference."
|
|
143
|
+
"## Evidence Rule",
|
|
144
|
+
"- External or current claims should carry a source link or be labeled as inference."
|
|
135
145
|
].join("\n");
|
|
136
146
|
}
|
|
137
147
|
function buildProjectAgentsMd(project, session) {
|
|
138
148
|
return [
|
|
139
149
|
"# AGENTS.md",
|
|
140
150
|
"",
|
|
141
|
-
"This directory is a
|
|
151
|
+
"This directory is a LongTable research workspace.",
|
|
142
152
|
"",
|
|
143
|
-
"##
|
|
144
|
-
`- Project name: ${project.projectName}`,
|
|
145
|
-
`- Current goal: ${session.currentGoal}`,
|
|
146
|
-
...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
|
|
147
|
-
`- Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
148
|
-
`- Disagreement visibility: ${session.disagreementPreference}`,
|
|
149
|
-
"",
|
|
150
|
-
"## Research-facing behavior",
|
|
153
|
+
"## Runtime Contract",
|
|
151
154
|
"- Treat researcher interaction as the primary task.",
|
|
152
|
-
"-
|
|
155
|
+
"- Read `.longtable/current-session.json` before giving substantial guidance.",
|
|
156
|
+
"- Use `.longtable/project.json` as stable project context.",
|
|
157
|
+
"- Use `.longtable/state.json` as layered working memory.",
|
|
158
|
+
"- Prefer `currentGoal`, `currentBlocker`, `nextAction`, and `openQuestions` over generic assumptions.",
|
|
159
|
+
"- Treat `AGENTS.md` as runtime guidance, not as the researcher-facing resume artifact.",
|
|
160
|
+
"",
|
|
161
|
+
"## Invocation Rules",
|
|
162
|
+
"- If the user message starts with `lt `, `longtable `, `long table `, or `롱테이블 ` followed by a directive and `:`, treat it as an explicit LongTable invocation.",
|
|
153
163
|
"- Supported explicit directives are: explore, review, critique, draft, commit, panel, status, editor, reviewer, methods, theory, measurement, ethics, voice, venue.",
|
|
154
|
-
"- For explicit
|
|
155
|
-
"- For general research requests in this workspace, prefer
|
|
164
|
+
"- For explicit LongTable invocations, do not begin by scanning the workspace. Use the current session files first and answer as LongTable immediately.",
|
|
165
|
+
"- For general research requests in this workspace, prefer LongTable behavior before generic coding behavior.",
|
|
166
|
+
"",
|
|
167
|
+
"## Research Behavior",
|
|
156
168
|
"- Begin exploratory work with clarifying or tension questions before recommending a direction.",
|
|
157
|
-
"- If you foreground role perspectives, disclose them with `
|
|
169
|
+
"- If you foreground role perspectives, disclose them with `LongTable consulted: ...`.",
|
|
158
170
|
"- Keep one accountable synthesis, but do not hide meaningful disagreement.",
|
|
159
|
-
"- For factual, current, or external claims, provide source links or file references when possible.",
|
|
160
|
-
"- If a statement cannot be sourced, label it as an inference or estimate instead of presenting it as a fact.",
|
|
161
171
|
...(session.disagreementPreference === "always_visible"
|
|
162
|
-
? ["-
|
|
172
|
+
? ["- Panel disagreement should be visible by default rather than hidden behind a single synthesis."]
|
|
163
173
|
: []),
|
|
174
|
+
"- For factual, current, or external claims, provide source links or file references when possible.",
|
|
175
|
+
"- If a statement cannot be sourced, label it as an inference or estimate instead of presenting it as a fact.",
|
|
164
176
|
"- Do not expose internal tool logs, file-search traces, or process commentary in the researcher-facing answer.",
|
|
165
177
|
"",
|
|
166
|
-
"## Session memory",
|
|
167
|
-
"- Read `.longtable/current-session.json` before giving substantial guidance.",
|
|
168
|
-
"- Use `.longtable/project.json` as the project-level context.",
|
|
169
|
-
"- Prefer the current goal and blocker over generic assumptions.",
|
|
170
|
-
"- Do not recursively search unrelated files unless the researcher explicitly asks for file analysis or code changes.",
|
|
171
|
-
"",
|
|
172
178
|
"## Scope",
|
|
179
|
+
`- Project: ${project.projectName}`,
|
|
180
|
+
`- Current goal: ${session.currentGoal}`,
|
|
181
|
+
...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
|
|
182
|
+
`- Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
183
|
+
`- Disagreement visibility: ${session.disagreementPreference}`,
|
|
173
184
|
"- These instructions apply to this directory and its children."
|
|
174
185
|
].join("\n");
|
|
175
186
|
}
|
|
@@ -180,12 +191,18 @@ function buildStateSeed(project, session, setup) {
|
|
|
180
191
|
careerStage: setup.profileSeed.careerStage,
|
|
181
192
|
experienceLevel: setup.profileSeed.experienceLevel,
|
|
182
193
|
projectName: project.projectName,
|
|
183
|
-
currentGoal: session.currentGoal,
|
|
184
194
|
disagreementPreference: session.disagreementPreference,
|
|
185
195
|
requestedPerspectives: session.requestedPerspectives
|
|
186
196
|
};
|
|
197
|
+
state.workingState = {
|
|
198
|
+
currentGoal: session.currentGoal,
|
|
199
|
+
...(session.currentBlocker ? { currentBlocker: session.currentBlocker } : {}),
|
|
200
|
+
...(session.nextAction ? { nextAction: session.nextAction } : {}),
|
|
201
|
+
openQuestions: session.openQuestions ?? [],
|
|
202
|
+
activeModes: session.activeModes ?? [],
|
|
203
|
+
...(session.resumeHint ? { resumeHint: session.resumeHint } : {})
|
|
204
|
+
};
|
|
187
205
|
if (session.currentBlocker) {
|
|
188
|
-
state.explicitState.currentBlocker = session.currentBlocker;
|
|
189
206
|
state.openTensions.push(session.currentBlocker);
|
|
190
207
|
}
|
|
191
208
|
if (setup.profileSeed.humanAuthorshipSignal) {
|
|
@@ -213,24 +230,42 @@ function buildStateSeed(project, session, setup) {
|
|
|
213
230
|
}
|
|
214
231
|
return JSON.stringify(state, null, 2);
|
|
215
232
|
}
|
|
233
|
+
async function removeLegacyRootFiles(projectPath) {
|
|
234
|
+
await Promise.all(LEGACY_ROOT_FILES.map((file) => rm(join(projectPath, file), { force: true })));
|
|
235
|
+
}
|
|
236
|
+
export async function syncCurrentWorkspaceView(context) {
|
|
237
|
+
const body = buildCurrentGuide(context.project, context.session);
|
|
238
|
+
await writeFile(context.currentFilePath, body, "utf8");
|
|
239
|
+
return context.currentFilePath;
|
|
240
|
+
}
|
|
216
241
|
export async function createOrUpdateProjectWorkspace(options) {
|
|
217
242
|
const projectPath = resolve(options.projectPath);
|
|
218
243
|
const metaDir = resolveMetaDir(projectPath);
|
|
219
244
|
const sessionsDir = join(metaDir, "sessions");
|
|
220
245
|
const projectFilePath = join(metaDir, "project.json");
|
|
221
246
|
const sessionFilePath = join(metaDir, "current-session.json");
|
|
247
|
+
const stateFilePath = resolveStateFilePath(projectPath);
|
|
248
|
+
const currentFilePath = resolveCurrentFilePath(projectPath);
|
|
222
249
|
const sessionId = slugify(`${options.projectName}-${Date.now()}`);
|
|
250
|
+
const locale = resolveUserLocale();
|
|
251
|
+
const timestamp = nowIso();
|
|
223
252
|
await mkdir(projectPath, { recursive: true });
|
|
224
253
|
await mkdir(metaDir, { recursive: true });
|
|
225
254
|
await mkdir(sessionsDir, { recursive: true });
|
|
226
255
|
const project = existsSync(projectFilePath)
|
|
227
|
-
?
|
|
256
|
+
? {
|
|
257
|
+
...JSON.parse(await readFile(projectFilePath, "utf8")),
|
|
258
|
+
contractVersion: "workspace-v2",
|
|
259
|
+
locale
|
|
260
|
+
}
|
|
228
261
|
: {
|
|
229
262
|
schemaVersion: 1,
|
|
230
|
-
product: "
|
|
263
|
+
product: "LongTable",
|
|
231
264
|
projectName: options.projectName,
|
|
232
265
|
projectPath,
|
|
233
|
-
createdAt:
|
|
266
|
+
createdAt: timestamp,
|
|
267
|
+
contractVersion: "workspace-v2",
|
|
268
|
+
locale,
|
|
234
269
|
globalSetupSummary: {
|
|
235
270
|
field: options.setup.profileSeed.field,
|
|
236
271
|
careerStage: options.setup.profileSeed.careerStage,
|
|
@@ -250,30 +285,67 @@ export async function createOrUpdateProjectWorkspace(options) {
|
|
|
250
285
|
const session = {
|
|
251
286
|
schemaVersion: 1,
|
|
252
287
|
id: sessionId,
|
|
253
|
-
createdAt:
|
|
288
|
+
createdAt: timestamp,
|
|
289
|
+
lastUpdatedAt: timestamp,
|
|
254
290
|
projectName: project.projectName,
|
|
255
291
|
projectPath,
|
|
256
292
|
currentGoal: options.currentGoal,
|
|
257
293
|
...(options.currentBlocker ? { currentBlocker: options.currentBlocker } : {}),
|
|
294
|
+
nextAction: buildNextAction({
|
|
295
|
+
schemaVersion: 1,
|
|
296
|
+
id: sessionId,
|
|
297
|
+
createdAt: timestamp,
|
|
298
|
+
projectName: project.projectName,
|
|
299
|
+
projectPath,
|
|
300
|
+
currentGoal: options.currentGoal,
|
|
301
|
+
...(options.currentBlocker ? { currentBlocker: options.currentBlocker } : {}),
|
|
302
|
+
requestedPerspectives: options.requestedPerspectives,
|
|
303
|
+
disagreementPreference: options.disagreementPreference
|
|
304
|
+
}),
|
|
305
|
+
openQuestions: buildOpenQuestions({
|
|
306
|
+
schemaVersion: 1,
|
|
307
|
+
id: sessionId,
|
|
308
|
+
createdAt: timestamp,
|
|
309
|
+
projectName: project.projectName,
|
|
310
|
+
projectPath,
|
|
311
|
+
currentGoal: options.currentGoal,
|
|
312
|
+
...(options.currentBlocker ? { currentBlocker: options.currentBlocker } : {}),
|
|
313
|
+
requestedPerspectives: options.requestedPerspectives,
|
|
314
|
+
disagreementPreference: options.disagreementPreference
|
|
315
|
+
}),
|
|
258
316
|
requestedPerspectives: options.requestedPerspectives,
|
|
259
|
-
disagreementPreference: options.disagreementPreference
|
|
317
|
+
disagreementPreference: options.disagreementPreference,
|
|
318
|
+
activeModes: ["explore"],
|
|
319
|
+
resumeHint: buildResumeHint({
|
|
320
|
+
schemaVersion: 1,
|
|
321
|
+
id: sessionId,
|
|
322
|
+
createdAt: timestamp,
|
|
323
|
+
projectName: project.projectName,
|
|
324
|
+
projectPath,
|
|
325
|
+
currentGoal: options.currentGoal,
|
|
326
|
+
...(options.currentBlocker ? { currentBlocker: options.currentBlocker } : {}),
|
|
327
|
+
requestedPerspectives: options.requestedPerspectives,
|
|
328
|
+
disagreementPreference: options.disagreementPreference
|
|
329
|
+
}),
|
|
330
|
+
locale
|
|
260
331
|
};
|
|
261
|
-
|
|
262
|
-
await writeFile(sessionFilePath, JSON.stringify(session, null, 2), "utf8");
|
|
263
|
-
await writeFile(join(sessionsDir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf8");
|
|
264
|
-
await writeFile(join(metaDir, "state.json"), buildStateSeed(project, session, options.setup), "utf8");
|
|
265
|
-
await writeFile(join(projectPath, "LONGTABLE.md"), buildWorkspaceGuide(project, session), "utf8");
|
|
266
|
-
await writeFile(join(projectPath, "START-HERE.md"), buildStartHereGuide(project, session), "utf8");
|
|
267
|
-
await writeFile(join(projectPath, "NEXT-STEPS.md"), buildNextStepsGuide(project, session), "utf8");
|
|
268
|
-
await writeFile(join(projectPath, "SESSION-SNAPSHOT.md"), buildSessionSnapshot(project, session), "utf8");
|
|
269
|
-
await writeFile(join(projectPath, "AGENTS.md"), buildProjectAgentsMd(project, session), "utf8");
|
|
270
|
-
return {
|
|
332
|
+
const context = {
|
|
271
333
|
project,
|
|
272
334
|
session,
|
|
273
335
|
projectFilePath,
|
|
274
336
|
sessionFilePath,
|
|
337
|
+
stateFilePath,
|
|
338
|
+
currentFilePath,
|
|
275
339
|
metaDir
|
|
276
340
|
};
|
|
341
|
+
await writeFile(projectFilePath, JSON.stringify(project, null, 2), "utf8");
|
|
342
|
+
await writeFile(sessionFilePath, JSON.stringify(session, null, 2), "utf8");
|
|
343
|
+
await writeFile(join(sessionsDir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf8");
|
|
344
|
+
await writeFile(stateFilePath, buildStateSeed(project, session, options.setup), "utf8");
|
|
345
|
+
await writeFile(join(projectPath, "AGENTS.md"), buildProjectAgentsMd(project, session), "utf8");
|
|
346
|
+
await syncCurrentWorkspaceView(context);
|
|
347
|
+
await removeLegacyRootFiles(projectPath);
|
|
348
|
+
return context;
|
|
277
349
|
}
|
|
278
350
|
export async function loadProjectContextFromDirectory(startPath) {
|
|
279
351
|
let current = resolve(startPath);
|
|
@@ -282,11 +354,22 @@ export async function loadProjectContextFromDirectory(startPath) {
|
|
|
282
354
|
const projectFilePath = join(metaDir, "project.json");
|
|
283
355
|
const sessionFilePath = join(metaDir, "current-session.json");
|
|
284
356
|
if (existsSync(projectFilePath) && existsSync(sessionFilePath)) {
|
|
357
|
+
const project = JSON.parse(await readFile(projectFilePath, "utf8"));
|
|
358
|
+
const session = JSON.parse(await readFile(sessionFilePath, "utf8"));
|
|
285
359
|
return {
|
|
286
|
-
project
|
|
287
|
-
session:
|
|
360
|
+
project,
|
|
361
|
+
session: {
|
|
362
|
+
...session,
|
|
363
|
+
locale: session.locale ?? project.locale ?? resolveUserLocale(),
|
|
364
|
+
openQuestions: session.openQuestions ?? buildOpenQuestions(session),
|
|
365
|
+
nextAction: session.nextAction ?? buildNextAction(session),
|
|
366
|
+
resumeHint: session.resumeHint ?? buildResumeHint(session),
|
|
367
|
+
activeModes: session.activeModes ?? ["explore"]
|
|
368
|
+
},
|
|
288
369
|
projectFilePath,
|
|
289
370
|
sessionFilePath,
|
|
371
|
+
stateFilePath: resolveStateFilePath(project.projectPath),
|
|
372
|
+
currentFilePath: resolveCurrentFilePath(project.projectPath),
|
|
290
373
|
metaDir
|
|
291
374
|
};
|
|
292
375
|
}
|
|
@@ -300,7 +383,7 @@ export async function loadProjectContextFromDirectory(startPath) {
|
|
|
300
383
|
export function renderProjectWorkspaceSummary(context) {
|
|
301
384
|
return [
|
|
302
385
|
"┌──────────────────────────────────────────────┐",
|
|
303
|
-
"│
|
|
386
|
+
"│ LongTable Project Workspace │",
|
|
304
387
|
"└──────────────────────────────────────────────┘",
|
|
305
388
|
`Project: ${context.project.projectName}`,
|
|
306
389
|
`Path: ${context.project.projectPath}`,
|
|
@@ -312,9 +395,8 @@ export function renderProjectWorkspaceSummary(context) {
|
|
|
312
395
|
"Created files:",
|
|
313
396
|
`- ${context.projectFilePath}`,
|
|
314
397
|
`- ${context.sessionFilePath}`,
|
|
315
|
-
`- ${
|
|
316
|
-
`- ${
|
|
317
|
-
`- ${join(context.project.projectPath, "SESSION-SNAPSHOT.md")}`,
|
|
398
|
+
`- ${context.stateFilePath}`,
|
|
399
|
+
`- ${context.currentFilePath}`,
|
|
318
400
|
`- ${join(context.project.projectPath, "AGENTS.md")}`
|
|
319
401
|
].join("\n");
|
|
320
402
|
}
|
package/dist/prompt-aliases.js
CHANGED
|
@@ -9,26 +9,26 @@ function promptSpec() {
|
|
|
9
9
|
return [
|
|
10
10
|
{
|
|
11
11
|
name: "longtable",
|
|
12
|
-
description: "Single-entry
|
|
12
|
+
description: "Single-entry LongTable router for research conversations",
|
|
13
13
|
argumentHint: "<natural language research request>",
|
|
14
14
|
body: [
|
|
15
|
-
"You are
|
|
15
|
+
"You are LongTable.",
|
|
16
16
|
"Classify the user's request into one of these modes: explore, review, critique, draft, commit, panel, or status.",
|
|
17
17
|
"If the request is ambiguous, ask one short clarifying question before closing.",
|
|
18
|
-
"Always begin with `
|
|
19
|
-
"Always disclose consulted roles with `
|
|
18
|
+
"Always begin with `LongTable mode: <Mode>`.",
|
|
19
|
+
"Always disclose consulted roles with `LongTable consulted: ...` when any role is foregrounded.",
|
|
20
20
|
"In explore mode, ask at least two clarifying or tension questions before any recommendation.",
|
|
21
|
-
"In panel mode, return 1)
|
|
21
|
+
"In panel mode, return 1) LongTable synthesis 2) visible panel opinions by role 3) conflict summary if needed 4) a decision prompt for the researcher.",
|
|
22
22
|
"Do not expose internal tool logs, file searches, or process notes in the researcher-facing answer.",
|
|
23
23
|
"Treat any slash-command arguments as the current research object."
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
name: "longtable-init",
|
|
28
|
-
description: "Run
|
|
28
|
+
description: "Run LongTable researcher onboarding inside Codex",
|
|
29
29
|
argumentHint: "[project context or current uncertainty]",
|
|
30
30
|
body: [
|
|
31
|
-
"You are
|
|
31
|
+
"You are LongTable onboarding inside Codex.",
|
|
32
32
|
"First ask whether the researcher wants Quickstart or Interview setup.",
|
|
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.",
|
|
@@ -45,11 +45,11 @@ function promptSpec() {
|
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
name: "longtable-explore",
|
|
48
|
-
description: "
|
|
48
|
+
description: "LongTable explore mode for open research questions",
|
|
49
49
|
argumentHint: "<topic or research problem>",
|
|
50
50
|
body: [
|
|
51
|
-
"You are
|
|
52
|
-
"Always begin with `
|
|
51
|
+
"You are LongTable in explore mode.",
|
|
52
|
+
"Always begin with `LongTable mode: Explore`.",
|
|
53
53
|
"Ask at least two clarifying or tension questions before any recommendation.",
|
|
54
54
|
"Keep unresolved tensions visible.",
|
|
55
55
|
"Do not rush to synthesis.",
|
|
@@ -59,11 +59,11 @@ function promptSpec() {
|
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
name: "longtable-review",
|
|
62
|
-
description: "
|
|
62
|
+
description: "LongTable review mode for critical evaluation",
|
|
63
63
|
argumentHint: "<claim, paragraph, design, or plan>",
|
|
64
64
|
body: [
|
|
65
|
-
"You are
|
|
66
|
-
"Always begin with `
|
|
65
|
+
"You are LongTable in review mode.",
|
|
66
|
+
"Always begin with `LongTable mode: Review`.",
|
|
67
67
|
"Surface why this may be wrong before synthesis.",
|
|
68
68
|
"Preserve the researcher's own language where possible.",
|
|
69
69
|
"Treat any slash-command arguments as the object to review."
|
|
@@ -71,12 +71,12 @@ function promptSpec() {
|
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
name: "longtable-panel",
|
|
74
|
-
description: "
|
|
74
|
+
description: "LongTable panel mode with visible role disagreement",
|
|
75
75
|
argumentHint: "<claim, plan, or draft for multi-role review>",
|
|
76
76
|
body: [
|
|
77
|
-
"You are
|
|
78
|
-
"Always begin with `
|
|
79
|
-
"Return 1) a
|
|
77
|
+
"You are LongTable in panel mode.",
|
|
78
|
+
"Always begin with `LongTable mode: Panel`.",
|
|
79
|
+
"Return 1) a LongTable synthesis 2) visible panel opinions by role 3) a decision prompt for the researcher.",
|
|
80
80
|
"If roles disagree, do not collapse them too early.",
|
|
81
81
|
"Disclose which roles were consulted.",
|
|
82
82
|
"Treat any slash-command arguments as the object under discussion."
|
|
@@ -84,10 +84,10 @@ function promptSpec() {
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
name: "longtable-editor",
|
|
87
|
-
description: "
|
|
87
|
+
description: "LongTable editor view",
|
|
88
88
|
argumentHint: "<claim, draft, or paper positioning>",
|
|
89
89
|
body: [
|
|
90
|
-
"You are
|
|
90
|
+
"You are LongTable with the Journal Editor role foregrounded.",
|
|
91
91
|
"Prioritize venue fit, framing clarity, contribution shape, and likely editorial concerns.",
|
|
92
92
|
"Disclose that the editor role was consulted.",
|
|
93
93
|
"Treat any slash-command arguments as the editorial object."
|
|
@@ -95,10 +95,10 @@ function promptSpec() {
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
name: "longtable-reviewer",
|
|
98
|
-
description: "
|
|
98
|
+
description: "LongTable reviewer view",
|
|
99
99
|
argumentHint: "<claim, method, or manuscript section>",
|
|
100
100
|
body: [
|
|
101
|
-
"You are
|
|
101
|
+
"You are LongTable with the Reviewer role foregrounded.",
|
|
102
102
|
"Prioritize likely objections, missing evidence, weak claims, and points needing clarification.",
|
|
103
103
|
"Disclose that the reviewer role was consulted.",
|
|
104
104
|
"Treat any slash-command arguments as the review object."
|
|
@@ -106,10 +106,10 @@ function promptSpec() {
|
|
|
106
106
|
},
|
|
107
107
|
{
|
|
108
108
|
name: "longtable-methods",
|
|
109
|
-
description: "
|
|
109
|
+
description: "LongTable methods-critic view",
|
|
110
110
|
argumentHint: "<study design, measure, or analysis plan>",
|
|
111
111
|
body: [
|
|
112
|
-
"You are
|
|
112
|
+
"You are LongTable with the Methods Critic role foregrounded.",
|
|
113
113
|
"Prioritize design fit, methodological defensibility, and mismatches between question, measure, and analysis.",
|
|
114
114
|
"Disclose that the methods critic role was consulted.",
|
|
115
115
|
"Treat any slash-command arguments as the methodological object."
|
|
@@ -117,10 +117,10 @@ function promptSpec() {
|
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
name: "longtable-critique",
|
|
120
|
-
description: "
|
|
120
|
+
description: "LongTable critique mode for stronger counterarguments",
|
|
121
121
|
argumentHint: "<claim or draft to challenge>",
|
|
122
122
|
body: [
|
|
123
|
-
"You are
|
|
123
|
+
"You are LongTable in critique mode.",
|
|
124
124
|
"Prioritize counterarguments, blind spots, and hidden assumptions.",
|
|
125
125
|
"Do not smooth over uncertainty.",
|
|
126
126
|
"Treat any slash-command arguments as the object to challenge."
|
|
@@ -128,10 +128,10 @@ function promptSpec() {
|
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
name: "longtable-draft",
|
|
131
|
-
description: "
|
|
131
|
+
description: "LongTable draft mode with narrative-trace preservation",
|
|
132
132
|
argumentHint: "<draft goal or section request>",
|
|
133
133
|
body: [
|
|
134
|
-
"You are
|
|
134
|
+
"You are LongTable in draft mode.",
|
|
135
135
|
"Preserve narrative trace and avoid generic fluency.",
|
|
136
136
|
"Keep the researcher's voice recognizable.",
|
|
137
137
|
"Treat any slash-command arguments as the drafting target."
|
|
@@ -139,10 +139,10 @@ function promptSpec() {
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
name: "longtable-commit",
|
|
142
|
-
description: "
|
|
142
|
+
description: "LongTable commit mode for explicit human decisions",
|
|
143
143
|
argumentHint: "<decision or choice that needs commitment>",
|
|
144
144
|
body: [
|
|
145
|
-
"You are
|
|
145
|
+
"You are LongTable in commit mode.",
|
|
146
146
|
"Before making any recommendation, ask for the human commitment that is actually at stake.",
|
|
147
147
|
"Make the trade-offs explicit.",
|
|
148
148
|
"Treat any slash-command arguments as the decision under consideration."
|
|
@@ -150,11 +150,11 @@ function promptSpec() {
|
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
152
|
name: "longtable-status",
|
|
153
|
-
description: "Inspect
|
|
153
|
+
description: "Inspect LongTable setup and Codex alias status",
|
|
154
154
|
argumentHint: "[optional concern]",
|
|
155
155
|
body: [
|
|
156
|
-
"You are
|
|
157
|
-
"Inspect whether setup and runtime artifacts appear to exist under `~/.
|
|
156
|
+
"You are LongTable status mode.",
|
|
157
|
+
"Inspect whether setup and runtime artifacts appear to exist under `~/.longtable/` and whether LongTable prompt aliases appear to be installed under `~/.codex/prompts/`.",
|
|
158
158
|
"Summarize what is configured, what is missing, and the next minimal action.",
|
|
159
159
|
"Treat any slash-command arguments as the user's concern."
|
|
160
160
|
]
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "Researcher-facing
|
|
5
|
+
"description": "Researcher-facing LongTable CLI",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"longtable": "./bin/longtable"
|
|
17
|
+
},
|
|
15
18
|
"directories": {
|
|
16
19
|
"bin": "./bin"
|
|
17
20
|
},
|
|
@@ -25,9 +28,9 @@
|
|
|
25
28
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
26
29
|
},
|
|
27
30
|
"dependencies": {
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
30
|
-
"@
|
|
31
|
+
"@longtable/memory": "0.1.9",
|
|
32
|
+
"@longtable/provider-codex": "0.1.9",
|
|
33
|
+
"@longtable/setup": "0.1.9"
|
|
31
34
|
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"@types/node": "^22.10.1",
|
|
@@ -44,9 +47,9 @@
|
|
|
44
47
|
"license": "MIT",
|
|
45
48
|
"repository": {
|
|
46
49
|
"type": "git",
|
|
47
|
-
"url": "git+https://github.com/HosungYou/
|
|
50
|
+
"url": "git+https://github.com/HosungYou/LongTable.git"
|
|
48
51
|
},
|
|
49
|
-
"homepage": "https://github.com/HosungYou/
|
|
52
|
+
"homepage": "https://github.com/HosungYou/LongTable#readme",
|
|
50
53
|
"publishConfig": {
|
|
51
54
|
"access": "public"
|
|
52
55
|
},
|