@longtable/cli 0.1.0
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 +91 -0
- package/bin/longtable +5 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +353 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/persona-router.d.ts +23 -0
- package/dist/persona-router.js +94 -0
- package/dist/personas.d.ts +12 -0
- package/dist/personas.js +87 -0
- package/dist/prompt-aliases.d.ts +9 -0
- package/dist/prompt-aliases.js +188 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @longtable/cli
|
|
2
|
+
|
|
3
|
+
Researcher-facing Long Table CLI built on top of the legacy `@diverga/*` package surface.
|
|
4
|
+
|
|
5
|
+
중요한 현재 상태:
|
|
6
|
+
|
|
7
|
+
- `@longtable/cli`는 repo 안에서는 구현되어 있음
|
|
8
|
+
- npm의 `@longtable` scope가 아직 준비되지 않아 실제 publish는 아직 안 됨
|
|
9
|
+
- 지금 공개 설치 경로는 여전히 `@diverga/setup`, `@diverga/provider-codex`
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
로컬 preview:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm run build
|
|
18
|
+
node packages/longtable/dist/cli.js --help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
예상 공개 설치 경로:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g @longtable/cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Recommended flow
|
|
28
|
+
|
|
29
|
+
Run setup once and install Codex prompt aliases:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
longtable init --install-prompts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then you can work in two ways.
|
|
36
|
+
|
|
37
|
+
From the terminal:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
longtable review --prompt "Review this claim critically."
|
|
41
|
+
longtable review --prompt "BJET 편집자 관점에서 봐줘." --role editor
|
|
42
|
+
longtable review --prompt "방법론적으로 어디가 취약한지 말해줘." --panel --show-conflicts
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Inside Codex after prompt aliases are installed:
|
|
46
|
+
|
|
47
|
+
- `/prompts:longtable-init`
|
|
48
|
+
- `/prompts:longtable-explore`
|
|
49
|
+
- `/prompts:longtable-review`
|
|
50
|
+
- `/prompts:longtable-critique`
|
|
51
|
+
- `/prompts:longtable-draft`
|
|
52
|
+
- `/prompts:longtable-commit`
|
|
53
|
+
- `/prompts:longtable-status`
|
|
54
|
+
|
|
55
|
+
## Core commands
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
longtable init
|
|
59
|
+
longtable show --json
|
|
60
|
+
longtable install --json
|
|
61
|
+
longtable explore --prompt "Help me stay exploratory."
|
|
62
|
+
longtable review --prompt "Review this claim critically."
|
|
63
|
+
longtable commit --prompt "Help me make this decision carefully."
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Codex overlay
|
|
67
|
+
|
|
68
|
+
Install Codex prompt aliases:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
longtable codex install-prompts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Check whether setup and aliases are present:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
longtable codex status
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Remove the aliases:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
longtable codex remove-prompts
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Why this package exists
|
|
87
|
+
|
|
88
|
+
Long Table is the product name.
|
|
89
|
+
`Diverga` is still the legacy technical identifier for package names, CLI binaries, and runtime paths.
|
|
90
|
+
|
|
91
|
+
This package creates a researcher-facing command surface now, without forcing a breaking rename of the published package chain yet.
|
package/bin/longtable
ADDED
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
4
|
+
import { stdin as input, stdout as output, cwd, exit } from "node:process";
|
|
5
|
+
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@diverga/setup";
|
|
6
|
+
import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@diverga/provider-codex";
|
|
7
|
+
import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodexPromptAliases, resolveCodexPromptsDir } from "./prompt-aliases.js";
|
|
8
|
+
import { buildPersonaGuidance } from "./persona-router.js";
|
|
9
|
+
const VALID_MODES = new Set([
|
|
10
|
+
"explore",
|
|
11
|
+
"review",
|
|
12
|
+
"critique",
|
|
13
|
+
"draft",
|
|
14
|
+
"commit",
|
|
15
|
+
"submit"
|
|
16
|
+
]);
|
|
17
|
+
const VALID_STAGES = new Set([
|
|
18
|
+
"problem_framing",
|
|
19
|
+
"theory_selection",
|
|
20
|
+
"method_design",
|
|
21
|
+
"measurement_design",
|
|
22
|
+
"analysis_planning",
|
|
23
|
+
"writing",
|
|
24
|
+
"submission"
|
|
25
|
+
]);
|
|
26
|
+
function usage() {
|
|
27
|
+
return [
|
|
28
|
+
"Usage:",
|
|
29
|
+
" 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 show [--json] [--path <file>]",
|
|
31
|
+
" longtable install [--json] [--path <file>] [--runtime-path <file>]",
|
|
32
|
+
" 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>]",
|
|
33
|
+
" longtable codex install-prompts [--dir <path>]",
|
|
34
|
+
" longtable codex remove-prompts [--dir <path>]",
|
|
35
|
+
" longtable codex status [--dir <path>] [--json]",
|
|
36
|
+
"",
|
|
37
|
+
"Examples:",
|
|
38
|
+
" longtable init --install-prompts",
|
|
39
|
+
" longtable review --prompt \"Review this claim critically.\" --panel --show-conflicts",
|
|
40
|
+
" longtable review --role editor --prompt \"BJET 편집자 관점에서 봐줘.\"",
|
|
41
|
+
" longtable codex install-prompts"
|
|
42
|
+
].join("\n");
|
|
43
|
+
}
|
|
44
|
+
function parseArgs(argv) {
|
|
45
|
+
const [command, maybeSubcommand] = argv;
|
|
46
|
+
const values = {};
|
|
47
|
+
let subcommand = maybeSubcommand;
|
|
48
|
+
const modeCommand = command && VALID_MODES.has(command);
|
|
49
|
+
const directCommand = command && ["init", "show", "install", "codex"].includes(command);
|
|
50
|
+
let startIndex = 1;
|
|
51
|
+
if (modeCommand) {
|
|
52
|
+
subcommand = undefined;
|
|
53
|
+
startIndex = 1;
|
|
54
|
+
}
|
|
55
|
+
else if (command === "codex") {
|
|
56
|
+
startIndex = 2;
|
|
57
|
+
}
|
|
58
|
+
else if (directCommand) {
|
|
59
|
+
subcommand = undefined;
|
|
60
|
+
startIndex = 1;
|
|
61
|
+
}
|
|
62
|
+
else if (!command || command === "--help") {
|
|
63
|
+
return { values };
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
startIndex = 1;
|
|
67
|
+
subcommand = undefined;
|
|
68
|
+
}
|
|
69
|
+
const tokens = argv.slice(startIndex);
|
|
70
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
71
|
+
const token = tokens[index];
|
|
72
|
+
if (!token.startsWith("--")) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const key = token.slice(2);
|
|
76
|
+
const next = tokens[index + 1];
|
|
77
|
+
if (!next || next.startsWith("--")) {
|
|
78
|
+
values[key] = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
values[key] = next;
|
|
82
|
+
index += 1;
|
|
83
|
+
}
|
|
84
|
+
return { command, subcommand, values };
|
|
85
|
+
}
|
|
86
|
+
function renderChoices(choices) {
|
|
87
|
+
return choices
|
|
88
|
+
.map((choice, index) => `${index + 1}. ${choice.label} — ${choice.description}`)
|
|
89
|
+
.join("\n");
|
|
90
|
+
}
|
|
91
|
+
async function promptChoice(rl, prompt, choices) {
|
|
92
|
+
while (true) {
|
|
93
|
+
const answer = await rl.question(`${prompt}\n${renderChoices(choices)}\nSelect one number: `);
|
|
94
|
+
const numeric = Number(answer.trim());
|
|
95
|
+
if (!Number.isInteger(numeric) || numeric < 1 || numeric > choices.length) {
|
|
96
|
+
console.log("Invalid selection. Enter one of the listed numbers.");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const choice = choices[numeric - 1];
|
|
100
|
+
if (choice.fallbackToText) {
|
|
101
|
+
const freeText = await rl.question("Type your custom value: ");
|
|
102
|
+
if (!freeText.trim()) {
|
|
103
|
+
console.log("Custom value cannot be empty.");
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
return freeText.trim();
|
|
107
|
+
}
|
|
108
|
+
return choice.id;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function hasCompleteFlagInput(args) {
|
|
112
|
+
const required = ["provider", "field", "career-stage", "experience", "project-type", "checkpoint"];
|
|
113
|
+
return required.every((key) => typeof args[key] === "string" && String(args[key]).trim().length > 0);
|
|
114
|
+
}
|
|
115
|
+
function toSetupAnswers(args) {
|
|
116
|
+
return {
|
|
117
|
+
field: String(args.field),
|
|
118
|
+
careerStage: String(args["career-stage"]),
|
|
119
|
+
experienceLevel: String(args.experience),
|
|
120
|
+
currentProjectType: String(args["project-type"]),
|
|
121
|
+
preferredCheckpointIntensity: String(args.checkpoint),
|
|
122
|
+
humanAuthorshipSignal: typeof args["authorship-signal"] === "string" && args["authorship-signal"].trim().length > 0
|
|
123
|
+
? args["authorship-signal"].trim()
|
|
124
|
+
: undefined
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function collectInteractiveAnswers() {
|
|
128
|
+
const rl = createInterface({ input, output });
|
|
129
|
+
try {
|
|
130
|
+
const provider = await promptChoice(rl, "Which provider do you want to configure?", buildProviderChoices());
|
|
131
|
+
const answers = {};
|
|
132
|
+
for (const question of buildQuickSetupFlow()) {
|
|
133
|
+
if (!question.choices) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const value = await promptChoice(rl, question.prompt, question.choices);
|
|
137
|
+
if (question.id === "field")
|
|
138
|
+
answers.field = value;
|
|
139
|
+
if (question.id === "careerStage")
|
|
140
|
+
answers.careerStage = value;
|
|
141
|
+
if (question.id === "experienceLevel")
|
|
142
|
+
answers.experienceLevel = value;
|
|
143
|
+
if (question.id === "currentProjectType")
|
|
144
|
+
answers.currentProjectType = value;
|
|
145
|
+
if (question.id === "preferredCheckpointIntensity") {
|
|
146
|
+
answers.preferredCheckpointIntensity = value;
|
|
147
|
+
}
|
|
148
|
+
if (question.id === "humanAuthorshipSignal" && value !== "other") {
|
|
149
|
+
answers.humanAuthorshipSignal = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
provider,
|
|
154
|
+
answers: answers
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
rl.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function runInit(args) {
|
|
162
|
+
const json = args.json === true;
|
|
163
|
+
const installRuntime = args["no-install"] !== true;
|
|
164
|
+
const installPrompts = args["install-prompts"] === true;
|
|
165
|
+
const customPath = typeof args.path === "string" ? args.path : undefined;
|
|
166
|
+
const runtimePath = typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined;
|
|
167
|
+
const promptsDir = typeof args.dir === "string" ? args.dir : undefined;
|
|
168
|
+
const { provider, answers } = hasCompleteFlagInput(args)
|
|
169
|
+
? {
|
|
170
|
+
provider: String(args.provider) === "claude" ? "claude" : "codex",
|
|
171
|
+
answers: toSetupAnswers(args)
|
|
172
|
+
}
|
|
173
|
+
: await collectInteractiveAnswers();
|
|
174
|
+
const outputValue = createPersistedSetupOutput(answers, provider);
|
|
175
|
+
const result = await saveSetupAndRuntimeConfig(outputValue, {
|
|
176
|
+
setupPath: customPath,
|
|
177
|
+
runtimePath
|
|
178
|
+
});
|
|
179
|
+
let installedPrompts = [];
|
|
180
|
+
if (provider === "codex" && installPrompts) {
|
|
181
|
+
installedPrompts = await installCodexPromptAliases(promptsDir);
|
|
182
|
+
}
|
|
183
|
+
if (json) {
|
|
184
|
+
console.log(serializeSetupOutput(outputValue));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
console.log(renderSetupSummary(outputValue));
|
|
188
|
+
if (installRuntime) {
|
|
189
|
+
console.log("");
|
|
190
|
+
console.log(renderInstallSummary(result));
|
|
191
|
+
}
|
|
192
|
+
if (installedPrompts.length > 0) {
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log("Installed Codex prompt aliases:");
|
|
195
|
+
for (const prompt of installedPrompts) {
|
|
196
|
+
console.log(`- /prompts:${prompt.name}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function runShow(args) {
|
|
201
|
+
const outputValue = await loadSetupOutput(typeof args.path === "string" ? args.path : undefined);
|
|
202
|
+
if (args.json === true) {
|
|
203
|
+
console.log(serializeSetupOutput(outputValue));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
console.log(renderSetupSummary(outputValue));
|
|
207
|
+
}
|
|
208
|
+
async function runInstall(args) {
|
|
209
|
+
const result = await installRuntimeConfigFromStoredSetup({
|
|
210
|
+
setupPath: typeof args.path === "string" ? args.path : undefined,
|
|
211
|
+
runtimePath: typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined
|
|
212
|
+
});
|
|
213
|
+
if (args.json === true) {
|
|
214
|
+
console.log(JSON.stringify(result, null, 2));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
console.log(renderInstallSummary(result));
|
|
218
|
+
}
|
|
219
|
+
async function resolvePrompt(prompt) {
|
|
220
|
+
if (prompt?.trim()) {
|
|
221
|
+
return prompt.trim();
|
|
222
|
+
}
|
|
223
|
+
if (!process.stdin.isTTY) {
|
|
224
|
+
return readFileSync(0, "utf8").trim();
|
|
225
|
+
}
|
|
226
|
+
const rl = createInterface({ input, output });
|
|
227
|
+
try {
|
|
228
|
+
return (await rl.question("What should Long Table help with?\n> ")).trim();
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
rl.close();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function runModeCommand(mode, args) {
|
|
235
|
+
const prompt = await resolvePrompt(typeof args.prompt === "string" ? args.prompt : undefined);
|
|
236
|
+
if (!prompt) {
|
|
237
|
+
throw new Error("A prompt is required.");
|
|
238
|
+
}
|
|
239
|
+
const stage = typeof args.stage === "string" ? args.stage : undefined;
|
|
240
|
+
if (stage && !VALID_STAGES.has(stage)) {
|
|
241
|
+
throw new Error(`Invalid stage: ${stage}`);
|
|
242
|
+
}
|
|
243
|
+
const { guidedPrompt } = buildPersonaGuidance({
|
|
244
|
+
prompt,
|
|
245
|
+
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
246
|
+
panel: args.panel === true,
|
|
247
|
+
showConflicts: args["show-conflicts"] === true,
|
|
248
|
+
showDeliberation: args["show-deliberation"] === true
|
|
249
|
+
});
|
|
250
|
+
if (args.print === true) {
|
|
251
|
+
const wrapped = await buildCodexThinWrappedPrompt({
|
|
252
|
+
prompt: guidedPrompt,
|
|
253
|
+
mode,
|
|
254
|
+
researchStage: stage,
|
|
255
|
+
setupPath: typeof args.setup === "string" ? args.setup : undefined,
|
|
256
|
+
workingDirectory: typeof args.cwd === "string" ? args.cwd : cwd()
|
|
257
|
+
});
|
|
258
|
+
console.log(wrapped.wrappedPrompt);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const exitCode = await runCodexThinWrapper({
|
|
262
|
+
prompt: guidedPrompt,
|
|
263
|
+
mode,
|
|
264
|
+
researchStage: stage,
|
|
265
|
+
setupPath: typeof args.setup === "string" ? args.setup : undefined,
|
|
266
|
+
workingDirectory: typeof args.cwd === "string" ? args.cwd : cwd(),
|
|
267
|
+
json: args.json === true
|
|
268
|
+
});
|
|
269
|
+
exit(exitCode);
|
|
270
|
+
}
|
|
271
|
+
async function runCodexSubcommand(subcommand, args) {
|
|
272
|
+
const customDir = typeof args.dir === "string" ? args.dir : undefined;
|
|
273
|
+
if (subcommand === "install-prompts") {
|
|
274
|
+
const installed = await installCodexPromptAliases(customDir);
|
|
275
|
+
console.log(`Installed ${installed.length} Long Table prompt aliases in ${resolveCodexPromptsDir(customDir)}`);
|
|
276
|
+
for (const prompt of installed) {
|
|
277
|
+
console.log(`- /prompts:${prompt.name}`);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (subcommand === "remove-prompts") {
|
|
282
|
+
const removed = await removeCodexPromptAliases(customDir);
|
|
283
|
+
console.log(`Removed ${removed.length} Long Table prompt aliases from ${resolveCodexPromptsDir(customDir)}`);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (subcommand === "status") {
|
|
287
|
+
const aliases = await listInstalledCodexPromptAliases(customDir);
|
|
288
|
+
const setupPath = resolveDefaultSetupPath(typeof args.path === "string" ? args.path : undefined).path;
|
|
289
|
+
const runtimePath = resolveDefaultRuntimeConfigPath("codex", typeof args["runtime-path"] === "string" ? args["runtime-path"] : undefined).path;
|
|
290
|
+
const status = {
|
|
291
|
+
setupPath,
|
|
292
|
+
setupExists: existsSync(setupPath),
|
|
293
|
+
runtimePath,
|
|
294
|
+
runtimeExists: existsSync(runtimePath),
|
|
295
|
+
promptsDir: resolveCodexPromptsDir(customDir),
|
|
296
|
+
promptAliasesInstalled: aliases.map((alias) => alias.name)
|
|
297
|
+
};
|
|
298
|
+
if (args.json === true) {
|
|
299
|
+
console.log(JSON.stringify(status, null, 2));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
console.log("Long Table Codex status");
|
|
303
|
+
console.log(`- setup: ${status.setupExists ? "present" : "missing"} (${setupPath})`);
|
|
304
|
+
console.log(`- codex runtime artifact: ${status.runtimeExists ? "present" : "missing"} (${runtimePath})`);
|
|
305
|
+
console.log(`- prompt aliases dir: ${status.promptsDir}`);
|
|
306
|
+
if (aliases.length === 0) {
|
|
307
|
+
console.log("- prompt aliases: none");
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
console.log("- prompt aliases:");
|
|
311
|
+
for (const alias of aliases) {
|
|
312
|
+
console.log(` - /prompts:${alias.name}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
throw new Error("Unknown codex subcommand.");
|
|
318
|
+
}
|
|
319
|
+
async function main() {
|
|
320
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
321
|
+
const { command, subcommand, values } = parsed;
|
|
322
|
+
if (!command || command === "--help" || values.help === true) {
|
|
323
|
+
console.log(usage());
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (command === "init") {
|
|
327
|
+
await runInit(values);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (command === "show") {
|
|
331
|
+
await runShow(values);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (command === "install") {
|
|
335
|
+
await runInstall(values);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (command === "codex") {
|
|
339
|
+
await runCodexSubcommand(subcommand, values);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (VALID_MODES.has(command)) {
|
|
343
|
+
await runModeCommand(command, values);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
throw new Error(`Unknown command: ${command}`);
|
|
347
|
+
}
|
|
348
|
+
main().catch((error) => {
|
|
349
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
350
|
+
console.error("");
|
|
351
|
+
console.error(usage());
|
|
352
|
+
exit(1);
|
|
353
|
+
});
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type CanonicalPersona } from "./personas.js";
|
|
2
|
+
export type OutputLanguage = "ko" | "en";
|
|
3
|
+
export interface PersonaRoutingResult {
|
|
4
|
+
outputLanguage: OutputLanguage;
|
|
5
|
+
explicitRoles: CanonicalPersona[];
|
|
6
|
+
implicitRoles: CanonicalPersona[];
|
|
7
|
+
consultedRoles: CanonicalPersona[];
|
|
8
|
+
ambiguousSignal: string | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function detectOutputLanguage(input: string): OutputLanguage;
|
|
11
|
+
export declare function parseRoleFlag(value?: string): CanonicalPersona[];
|
|
12
|
+
export declare function routePersonas(prompt: string, explicitRoleFlag?: string): PersonaRoutingResult;
|
|
13
|
+
export declare function renderDisclosure(roles: CanonicalPersona[], language: OutputLanguage): string | null;
|
|
14
|
+
export declare function buildPersonaGuidance(options: {
|
|
15
|
+
prompt: string;
|
|
16
|
+
roleFlag?: string;
|
|
17
|
+
panel?: boolean;
|
|
18
|
+
showConflicts?: boolean;
|
|
19
|
+
showDeliberation?: boolean;
|
|
20
|
+
}): {
|
|
21
|
+
guidedPrompt: string;
|
|
22
|
+
routing: PersonaRoutingResult;
|
|
23
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getPersonaDefinition, parsePersonaKey, PERSONA_DEFINITIONS } from "./personas.js";
|
|
2
|
+
const AUTO_CALL_LIMIT = 3;
|
|
3
|
+
function unique(items) {
|
|
4
|
+
return [...new Set(items)];
|
|
5
|
+
}
|
|
6
|
+
export function detectOutputLanguage(input) {
|
|
7
|
+
return /[가-힣]/.test(input) ? "ko" : "en";
|
|
8
|
+
}
|
|
9
|
+
export function parseRoleFlag(value) {
|
|
10
|
+
if (!value?.trim()) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return unique(value
|
|
14
|
+
.split(",")
|
|
15
|
+
.map((part) => parsePersonaKey(part))
|
|
16
|
+
.filter((part) => part !== null));
|
|
17
|
+
}
|
|
18
|
+
export function routePersonas(prompt, explicitRoleFlag) {
|
|
19
|
+
const normalizedPrompt = prompt.toLowerCase();
|
|
20
|
+
const explicitRoles = parseRoleFlag(explicitRoleFlag);
|
|
21
|
+
const naturalLanguageExplicit = PERSONA_DEFINITIONS.filter((persona) => persona.synonyms.some((synonym) => normalizedPrompt.includes(synonym.toLowerCase()))).map((persona) => persona.key);
|
|
22
|
+
const mergedExplicit = unique([...explicitRoles, ...naturalLanguageExplicit]);
|
|
23
|
+
const implicitRoles = PERSONA_DEFINITIONS.filter((persona) => {
|
|
24
|
+
if (persona.triggerMode !== "auto-callable") {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (mergedExplicit.includes(persona.key)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return persona.synonyms.some((synonym) => normalizedPrompt.includes(synonym.toLowerCase()));
|
|
31
|
+
})
|
|
32
|
+
.map((persona) => persona.key)
|
|
33
|
+
.slice(0, AUTO_CALL_LIMIT);
|
|
34
|
+
let ambiguousSignal = null;
|
|
35
|
+
if (normalizedPrompt.includes("review") ||
|
|
36
|
+
normalizedPrompt.includes("봐줘") ||
|
|
37
|
+
normalizedPrompt.includes("검토") ||
|
|
38
|
+
normalizedPrompt.includes("판단")) {
|
|
39
|
+
const hasEditor = mergedExplicit.includes("editor") || implicitRoles.includes("editor");
|
|
40
|
+
const hasReviewer = mergedExplicit.includes("reviewer") || implicitRoles.includes("reviewer");
|
|
41
|
+
if (!hasEditor && !hasReviewer) {
|
|
42
|
+
ambiguousSignal = "editor_or_reviewer";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
outputLanguage: detectOutputLanguage(prompt),
|
|
47
|
+
explicitRoles: mergedExplicit,
|
|
48
|
+
implicitRoles,
|
|
49
|
+
consultedRoles: unique([...mergedExplicit, ...implicitRoles]),
|
|
50
|
+
ambiguousSignal
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function renderDisclosure(roles, language) {
|
|
54
|
+
if (roles.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const labels = roles.map((role) => getPersonaDefinition(role).label);
|
|
58
|
+
return language === "ko"
|
|
59
|
+
? `Long Table consulted: ${labels.join(", ")}`
|
|
60
|
+
: `Long Table consulted: ${labels.join(", ")}`;
|
|
61
|
+
}
|
|
62
|
+
export function buildPersonaGuidance(options) {
|
|
63
|
+
const routing = routePersonas(options.prompt, options.roleFlag);
|
|
64
|
+
const disclosure = renderDisclosure(routing.consultedRoles, routing.outputLanguage);
|
|
65
|
+
const lines = [];
|
|
66
|
+
if (disclosure) {
|
|
67
|
+
lines.push(disclosure);
|
|
68
|
+
}
|
|
69
|
+
if (routing.ambiguousSignal === "editor_or_reviewer") {
|
|
70
|
+
lines.push(routing.outputLanguage === "ko"
|
|
71
|
+
? "Ambiguity note: 편집자 관점인지 리뷰어 관점인지 애매합니다. 먼저 둘 중 무엇을 우선할지 짧게 확인하세요."
|
|
72
|
+
: "Ambiguity note: it is unclear whether the user wants an editor view or reviewer view. Ask briefly before closing.");
|
|
73
|
+
}
|
|
74
|
+
if (options.panel) {
|
|
75
|
+
lines.push(routing.outputLanguage === "ko"
|
|
76
|
+
? "Return format: 1) Long Table synthesis 2) panel opinions by role 3) decision prompt to the researcher."
|
|
77
|
+
: "Return format: 1) Long Table synthesis 2) panel opinions by role 3) decision prompt to the researcher.");
|
|
78
|
+
}
|
|
79
|
+
if (options.showConflicts) {
|
|
80
|
+
lines.push(routing.outputLanguage === "ko"
|
|
81
|
+
? "If roles disagree, show the conflict explicitly instead of forcing one answer."
|
|
82
|
+
: "If roles disagree, show the conflict explicitly instead of forcing one answer.");
|
|
83
|
+
}
|
|
84
|
+
if (options.showDeliberation) {
|
|
85
|
+
lines.push(routing.outputLanguage === "ko"
|
|
86
|
+
? "Include a short deliberation trace showing why the roles diverged."
|
|
87
|
+
: "Include a short deliberation trace showing why the roles diverged.");
|
|
88
|
+
}
|
|
89
|
+
lines.push(options.prompt.trim());
|
|
90
|
+
return {
|
|
91
|
+
guidedPrompt: lines.join("\n\n"),
|
|
92
|
+
routing
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const CANONICAL_PERSONAS: readonly ["editor", "reviewer", "theory_critic", "methods_critic", "measurement_auditor", "ethics_reviewer", "voice_keeper", "venue_strategist"];
|
|
2
|
+
export type CanonicalPersona = (typeof CANONICAL_PERSONAS)[number];
|
|
3
|
+
export interface PersonaDefinition {
|
|
4
|
+
key: CanonicalPersona;
|
|
5
|
+
label: string;
|
|
6
|
+
shortDescription: string;
|
|
7
|
+
triggerMode: "auto-callable" | "explicit-only";
|
|
8
|
+
synonyms: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare const PERSONA_DEFINITIONS: readonly PersonaDefinition[];
|
|
11
|
+
export declare function parsePersonaKey(value: string): CanonicalPersona | null;
|
|
12
|
+
export declare function getPersonaDefinition(key: CanonicalPersona): PersonaDefinition;
|
package/dist/personas.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export const CANONICAL_PERSONAS = [
|
|
2
|
+
"editor",
|
|
3
|
+
"reviewer",
|
|
4
|
+
"theory_critic",
|
|
5
|
+
"methods_critic",
|
|
6
|
+
"measurement_auditor",
|
|
7
|
+
"ethics_reviewer",
|
|
8
|
+
"voice_keeper",
|
|
9
|
+
"venue_strategist"
|
|
10
|
+
];
|
|
11
|
+
export const PERSONA_DEFINITIONS = [
|
|
12
|
+
{
|
|
13
|
+
key: "editor",
|
|
14
|
+
label: "Journal Editor",
|
|
15
|
+
shortDescription: "Assesses venue fit, framing strength, and editorial salience.",
|
|
16
|
+
triggerMode: "auto-callable",
|
|
17
|
+
synonyms: ["editor", "journal editor", "editorial", "편집자", "저널 편집자", "에디터"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
key: "reviewer",
|
|
21
|
+
label: "Reviewer",
|
|
22
|
+
shortDescription: "Surfaces likely peer-review objections and requests for clarification.",
|
|
23
|
+
triggerMode: "auto-callable",
|
|
24
|
+
synonyms: ["reviewer", "peer reviewer", "심사자", "리뷰어", "심사위원"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: "theory_critic",
|
|
28
|
+
label: "Theory Critic",
|
|
29
|
+
shortDescription: "Checks conceptual coherence, anchor theory fit, and overreach.",
|
|
30
|
+
triggerMode: "auto-callable",
|
|
31
|
+
synonyms: ["theory", "theoretical", "conceptual", "이론", "이론적", "개념적"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "methods_critic",
|
|
35
|
+
label: "Methods Critic",
|
|
36
|
+
shortDescription: "Challenges design logic, methodological defensibility, and alignment.",
|
|
37
|
+
triggerMode: "auto-callable",
|
|
38
|
+
synonyms: ["method", "methods", "methodology", "research design", "방법론", "방법", "연구 설계"]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "measurement_auditor",
|
|
42
|
+
label: "Measurement Auditor",
|
|
43
|
+
shortDescription: "Looks for construct validity, scale choice, and evidence quality issues.",
|
|
44
|
+
triggerMode: "auto-callable",
|
|
45
|
+
synonyms: [
|
|
46
|
+
"measurement",
|
|
47
|
+
"measure",
|
|
48
|
+
"scale",
|
|
49
|
+
"validity",
|
|
50
|
+
"reliability",
|
|
51
|
+
"측정",
|
|
52
|
+
"척도",
|
|
53
|
+
"타당도",
|
|
54
|
+
"신뢰도"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: "ethics_reviewer",
|
|
59
|
+
label: "Ethics Reviewer",
|
|
60
|
+
shortDescription: "Flags consent, IRB, representation, and trust harms.",
|
|
61
|
+
triggerMode: "auto-callable",
|
|
62
|
+
synonyms: ["ethics", "ethical", "irb", "윤리", "윤리적", "irb"]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: "voice_keeper",
|
|
66
|
+
label: "Voice Keeper",
|
|
67
|
+
shortDescription: "Protects narrative trace, authorship, and the researcher's own voice.",
|
|
68
|
+
triggerMode: "auto-callable",
|
|
69
|
+
synonyms: ["voice", "tone", "narrative", "authorship", "목소리", "서사", "저자성", "문체"]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "venue_strategist",
|
|
73
|
+
label: "Venue Strategist",
|
|
74
|
+
shortDescription: "Compares venue expectations and suggests positioning tradeoffs.",
|
|
75
|
+
triggerMode: "explicit-only",
|
|
76
|
+
synonyms: ["venue", "journal fit", "conference fit", "저널 적합성", "학회 적합성", "투고처"]
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
export function parsePersonaKey(value) {
|
|
80
|
+
const normalized = value.trim().toLowerCase().replace(/\s+/g, "_");
|
|
81
|
+
return CANONICAL_PERSONAS.includes(normalized)
|
|
82
|
+
? normalized
|
|
83
|
+
: null;
|
|
84
|
+
}
|
|
85
|
+
export function getPersonaDefinition(key) {
|
|
86
|
+
return PERSONA_DEFINITIONS.find((persona) => persona.key === key);
|
|
87
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface InstalledPromptAlias {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveCodexPromptsDir(customDir?: string): string;
|
|
7
|
+
export declare function installCodexPromptAliases(customDir?: string): Promise<InstalledPromptAlias[]>;
|
|
8
|
+
export declare function removeCodexPromptAliases(customDir?: string): Promise<string[]>;
|
|
9
|
+
export declare function listInstalledCodexPromptAliases(customDir?: string): Promise<InstalledPromptAlias[]>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
export function resolveCodexPromptsDir(customDir) {
|
|
6
|
+
return customDir ? resolve(customDir) : join(homedir(), ".codex", "prompts");
|
|
7
|
+
}
|
|
8
|
+
function promptSpec() {
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
name: "longtable-init",
|
|
12
|
+
description: "Run Long Table researcher onboarding inside Codex",
|
|
13
|
+
argumentHint: "[project context or current uncertainty]",
|
|
14
|
+
body: [
|
|
15
|
+
"You are Long Table onboarding inside Codex.",
|
|
16
|
+
"Ask exactly one setup question at a time.",
|
|
17
|
+
"Use numbered choices when possible and include a 'None of the above' option when needed.",
|
|
18
|
+
"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 tell the researcher the exact `longtable init ...` command to persist it.",
|
|
21
|
+
"If the researcher asks you to stay inside Codex, keep the conversation in numbered form and do not prematurely close.",
|
|
22
|
+
"Treat any slash-command arguments as context for why setup is being done now."
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "longtable-explore",
|
|
27
|
+
description: "Long Table explore mode for open research questions",
|
|
28
|
+
argumentHint: "<topic or research problem>",
|
|
29
|
+
body: [
|
|
30
|
+
"You are Long Table in explore mode.",
|
|
31
|
+
"Ask at least two clarifying or tension questions before any recommendation.",
|
|
32
|
+
"Keep unresolved tensions visible.",
|
|
33
|
+
"Do not rush to synthesis.",
|
|
34
|
+
"Treat any slash-command arguments as the current research object."
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "longtable-review",
|
|
39
|
+
description: "Long Table review mode for critical evaluation",
|
|
40
|
+
argumentHint: "<claim, paragraph, design, or plan>",
|
|
41
|
+
body: [
|
|
42
|
+
"You are Long Table in review mode.",
|
|
43
|
+
"Surface why this may be wrong before synthesis.",
|
|
44
|
+
"Preserve the researcher's own language where possible.",
|
|
45
|
+
"Treat any slash-command arguments as the object to review."
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "longtable-panel",
|
|
50
|
+
description: "Long Table panel mode with visible role disagreement",
|
|
51
|
+
argumentHint: "<claim, plan, or draft for multi-role review>",
|
|
52
|
+
body: [
|
|
53
|
+
"You are Long Table in panel mode.",
|
|
54
|
+
"Return 1) a Long Table synthesis 2) visible panel opinions by role 3) a decision prompt for the researcher.",
|
|
55
|
+
"If roles disagree, do not collapse them too early.",
|
|
56
|
+
"Disclose which roles were consulted.",
|
|
57
|
+
"Treat any slash-command arguments as the object under discussion."
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "longtable-editor",
|
|
62
|
+
description: "Long Table editor view",
|
|
63
|
+
argumentHint: "<claim, draft, or paper positioning>",
|
|
64
|
+
body: [
|
|
65
|
+
"You are Long Table with the Journal Editor role foregrounded.",
|
|
66
|
+
"Prioritize venue fit, framing clarity, contribution shape, and likely editorial concerns.",
|
|
67
|
+
"Disclose that the editor role was consulted.",
|
|
68
|
+
"Treat any slash-command arguments as the editorial object."
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "longtable-reviewer",
|
|
73
|
+
description: "Long Table reviewer view",
|
|
74
|
+
argumentHint: "<claim, method, or manuscript section>",
|
|
75
|
+
body: [
|
|
76
|
+
"You are Long Table with the Reviewer role foregrounded.",
|
|
77
|
+
"Prioritize likely objections, missing evidence, weak claims, and points needing clarification.",
|
|
78
|
+
"Disclose that the reviewer role was consulted.",
|
|
79
|
+
"Treat any slash-command arguments as the review object."
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "longtable-methods",
|
|
84
|
+
description: "Long Table methods-critic view",
|
|
85
|
+
argumentHint: "<study design, measure, or analysis plan>",
|
|
86
|
+
body: [
|
|
87
|
+
"You are Long Table with the Methods Critic role foregrounded.",
|
|
88
|
+
"Prioritize design fit, methodological defensibility, and mismatches between question, measure, and analysis.",
|
|
89
|
+
"Disclose that the methods critic role was consulted.",
|
|
90
|
+
"Treat any slash-command arguments as the methodological object."
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "longtable-critique",
|
|
95
|
+
description: "Long Table critique mode for stronger counterarguments",
|
|
96
|
+
argumentHint: "<claim or draft to challenge>",
|
|
97
|
+
body: [
|
|
98
|
+
"You are Long Table in critique mode.",
|
|
99
|
+
"Prioritize counterarguments, blind spots, and hidden assumptions.",
|
|
100
|
+
"Do not smooth over uncertainty.",
|
|
101
|
+
"Treat any slash-command arguments as the object to challenge."
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "longtable-draft",
|
|
106
|
+
description: "Long Table draft mode with narrative-trace preservation",
|
|
107
|
+
argumentHint: "<draft goal or section request>",
|
|
108
|
+
body: [
|
|
109
|
+
"You are Long Table in draft mode.",
|
|
110
|
+
"Preserve narrative trace and avoid generic fluency.",
|
|
111
|
+
"Keep the researcher's voice recognizable.",
|
|
112
|
+
"Treat any slash-command arguments as the drafting target."
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "longtable-commit",
|
|
117
|
+
description: "Long Table commit mode for explicit human decisions",
|
|
118
|
+
argumentHint: "<decision or choice that needs commitment>",
|
|
119
|
+
body: [
|
|
120
|
+
"You are Long Table in commit mode.",
|
|
121
|
+
"Before making any recommendation, ask for the human commitment that is actually at stake.",
|
|
122
|
+
"Make the trade-offs explicit.",
|
|
123
|
+
"Treat any slash-command arguments as the decision under consideration."
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "longtable-status",
|
|
128
|
+
description: "Inspect Long Table setup and Codex alias status",
|
|
129
|
+
argumentHint: "[optional concern]",
|
|
130
|
+
body: [
|
|
131
|
+
"You are Long Table status mode.",
|
|
132
|
+
"Inspect whether setup and runtime artifacts appear to exist under `~/.diverga/` and whether Long Table prompt aliases appear to be installed under `~/.codex/prompts/`.",
|
|
133
|
+
"Summarize what is configured, what is missing, and the next minimal action.",
|
|
134
|
+
"Treat any slash-command arguments as the user's concern."
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
function renderPromptFile(description, argumentHint, body) {
|
|
140
|
+
return [
|
|
141
|
+
"---",
|
|
142
|
+
`description: \"${description}\"`,
|
|
143
|
+
`argument-hint: \"${argumentHint}\"`,
|
|
144
|
+
"---",
|
|
145
|
+
...body
|
|
146
|
+
].join("\n");
|
|
147
|
+
}
|
|
148
|
+
export async function installCodexPromptAliases(customDir) {
|
|
149
|
+
const promptsDir = resolveCodexPromptsDir(customDir);
|
|
150
|
+
await mkdir(promptsDir, { recursive: true });
|
|
151
|
+
const installed = [];
|
|
152
|
+
for (const spec of promptSpec()) {
|
|
153
|
+
const path = join(promptsDir, `${spec.name}.md`);
|
|
154
|
+
await writeFile(path, renderPromptFile(spec.description, spec.argumentHint, spec.body), "utf8");
|
|
155
|
+
installed.push({
|
|
156
|
+
name: spec.name,
|
|
157
|
+
path,
|
|
158
|
+
description: spec.description
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return installed;
|
|
162
|
+
}
|
|
163
|
+
export async function removeCodexPromptAliases(customDir) {
|
|
164
|
+
const promptsDir = resolveCodexPromptsDir(customDir);
|
|
165
|
+
const removed = [];
|
|
166
|
+
for (const spec of promptSpec()) {
|
|
167
|
+
const path = join(promptsDir, `${spec.name}.md`);
|
|
168
|
+
if (existsSync(path)) {
|
|
169
|
+
await rm(path);
|
|
170
|
+
removed.push(path);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return removed;
|
|
174
|
+
}
|
|
175
|
+
export async function listInstalledCodexPromptAliases(customDir) {
|
|
176
|
+
const promptsDir = resolveCodexPromptsDir(customDir);
|
|
177
|
+
if (!existsSync(promptsDir)) {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
const files = new Set(await readdir(promptsDir));
|
|
181
|
+
return promptSpec()
|
|
182
|
+
.filter((spec) => files.has(`${spec.name}.md`))
|
|
183
|
+
.map((spec) => ({
|
|
184
|
+
name: spec.name,
|
|
185
|
+
path: join(promptsDir, `${spec.name}.md`),
|
|
186
|
+
description: spec.description
|
|
187
|
+
}));
|
|
188
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longtable/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Researcher-facing Long Table CLI on top of the legacy Diverga package surface",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"directories": {
|
|
16
|
+
"bin": "./bin"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"bin",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.json",
|
|
25
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@diverga/provider-codex": "0.1.1",
|
|
29
|
+
"@diverga/setup": "0.1.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.10.1",
|
|
33
|
+
"typescript": "^5.6.0"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"long-table",
|
|
37
|
+
"research",
|
|
38
|
+
"codex",
|
|
39
|
+
"onboarding",
|
|
40
|
+
"checkpoints"
|
|
41
|
+
],
|
|
42
|
+
"author": "Hosung You",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/HosungYou/Diverga-Refactoring.git"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/HosungYou/Diverga-Refactoring#readme",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|