open-research 0.1.23 → 0.1.25
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/dist/cli.js +85 -100
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
10
|
import React5 from "react";
|
|
11
|
-
import
|
|
11
|
+
import path21 from "path";
|
|
12
12
|
import { Command } from "commander";
|
|
13
13
|
import { render } from "ink";
|
|
14
14
|
|
|
@@ -811,7 +811,7 @@ function formatDateTime(value) {
|
|
|
811
811
|
}
|
|
812
812
|
|
|
813
813
|
// src/lib/cli/version.ts
|
|
814
|
-
var PACKAGE_VERSION = "0.1.
|
|
814
|
+
var PACKAGE_VERSION = "0.1.25";
|
|
815
815
|
function getPackageVersion() {
|
|
816
816
|
return PACKAGE_VERSION;
|
|
817
817
|
}
|
|
@@ -871,7 +871,7 @@ async function ensureOpenResearchConfig(options) {
|
|
|
871
871
|
}
|
|
872
872
|
|
|
873
873
|
// src/tui/app.tsx
|
|
874
|
-
import
|
|
874
|
+
import path20 from "path";
|
|
875
875
|
import {
|
|
876
876
|
startTransition,
|
|
877
877
|
useDeferredValue,
|
|
@@ -5414,7 +5414,7 @@ ${skill.prompt}`).join("\n\n");
|
|
|
5414
5414
|
"- Be transparent. Show the user what you're doing and why.",
|
|
5415
5415
|
"- When unsure, ask. Use ask_user rather than guessing.",
|
|
5416
5416
|
"- For large outputs, redirect to a file and read selectively.",
|
|
5417
|
-
"- Always wrap file paths in backticks
|
|
5417
|
+
"- Always wrap file paths in backticks: `notes/brief.md`, `experiments/analysis.py`. Include line references as `src/file.ts:42`. This makes them clickable.",
|
|
5418
5418
|
"",
|
|
5419
5419
|
`## Workspace
|
|
5420
5420
|
Root: ${process.cwd()}
|
|
@@ -6617,67 +6617,7 @@ function truncate3(value, max = 96) {
|
|
|
6617
6617
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
6618
6618
|
|
|
6619
6619
|
// src/tui/markdown.ts
|
|
6620
|
-
|
|
6621
|
-
import fs21 from "fs";
|
|
6622
|
-
var FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
6623
|
-
".py",
|
|
6624
|
-
".ts",
|
|
6625
|
-
".tsx",
|
|
6626
|
-
".js",
|
|
6627
|
-
".jsx",
|
|
6628
|
-
".r",
|
|
6629
|
-
".R",
|
|
6630
|
-
".tex",
|
|
6631
|
-
".bib",
|
|
6632
|
-
".md",
|
|
6633
|
-
".txt",
|
|
6634
|
-
".json",
|
|
6635
|
-
".yaml",
|
|
6636
|
-
".yml",
|
|
6637
|
-
".toml",
|
|
6638
|
-
".csv",
|
|
6639
|
-
".tsv",
|
|
6640
|
-
".sh",
|
|
6641
|
-
".bash",
|
|
6642
|
-
".zsh",
|
|
6643
|
-
".sql",
|
|
6644
|
-
".html",
|
|
6645
|
-
".css",
|
|
6646
|
-
".xml",
|
|
6647
|
-
".pdf",
|
|
6648
|
-
".png",
|
|
6649
|
-
".jpg",
|
|
6650
|
-
".svg",
|
|
6651
|
-
".gif",
|
|
6652
|
-
".cfg",
|
|
6653
|
-
".ini",
|
|
6654
|
-
".env",
|
|
6655
|
-
".lock",
|
|
6656
|
-
".log"
|
|
6657
|
-
]);
|
|
6658
|
-
function looksLikeFilePath(text) {
|
|
6659
|
-
if (text.length < 3 || text.length > 200) return false;
|
|
6660
|
-
if (text.includes(" ") || text.includes("\n")) return false;
|
|
6661
|
-
const ext = path20.extname(text).toLowerCase();
|
|
6662
|
-
if (FILE_EXTENSIONS.has(ext)) return true;
|
|
6663
|
-
if (text.includes("/") && !text.startsWith("http")) return true;
|
|
6664
|
-
return false;
|
|
6665
|
-
}
|
|
6666
|
-
function resolveFilePath(filePath) {
|
|
6667
|
-
if (path20.isAbsolute(filePath)) return filePath;
|
|
6668
|
-
return path20.resolve(process.cwd(), filePath);
|
|
6669
|
-
}
|
|
6670
|
-
function fileLink(displayText, filePath) {
|
|
6671
|
-
const absPath = resolveFilePath(filePath);
|
|
6672
|
-
try {
|
|
6673
|
-
fs21.statSync(absPath);
|
|
6674
|
-
} catch {
|
|
6675
|
-
return displayText;
|
|
6676
|
-
}
|
|
6677
|
-
const uri = `file://${absPath}`;
|
|
6678
|
-
return `\x1B]8;;${uri}\x1B\\${displayText}\x1B]8;;\x1B\\`;
|
|
6679
|
-
}
|
|
6680
|
-
function renderMarkdown(text) {
|
|
6620
|
+
function renderMarkdown(text, options = {}) {
|
|
6681
6621
|
if (!text || !text.trim()) return text;
|
|
6682
6622
|
if (!/[*_`#\[\]>~\-]/.test(text) && !text.includes("```")) return text;
|
|
6683
6623
|
const lines = text.split("\n");
|
|
@@ -6713,13 +6653,9 @@ function renderMarkdown(text) {
|
|
|
6713
6653
|
if (headingMatch) {
|
|
6714
6654
|
const level = headingMatch[1].length;
|
|
6715
6655
|
const content = renderInline(headingMatch[2]);
|
|
6716
|
-
if (level === 1)
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
output2.push(source_default.bold.white(content));
|
|
6720
|
-
} else {
|
|
6721
|
-
output2.push(source_default.bold(content));
|
|
6722
|
-
}
|
|
6656
|
+
if (level === 1) output2.push(source_default.bold.cyan(content));
|
|
6657
|
+
else if (level === 2) output2.push(source_default.bold.white(content));
|
|
6658
|
+
else output2.push(source_default.bold(content));
|
|
6723
6659
|
continue;
|
|
6724
6660
|
}
|
|
6725
6661
|
if (/^[-*_]{3,}\s*$/.test(line.trim())) {
|
|
@@ -6733,17 +6669,12 @@ function renderMarkdown(text) {
|
|
|
6733
6669
|
}
|
|
6734
6670
|
const ulMatch = line.match(/^(\s*)[*+-]\s+(.+)$/);
|
|
6735
6671
|
if (ulMatch) {
|
|
6736
|
-
|
|
6737
|
-
const content = renderInline(ulMatch[2]);
|
|
6738
|
-
output2.push(`${indent}${source_default.gray("\u2022")} ${content}`);
|
|
6672
|
+
output2.push(`${ulMatch[1]}${source_default.gray("\u2022")} ${renderInline(ulMatch[2])}`);
|
|
6739
6673
|
continue;
|
|
6740
6674
|
}
|
|
6741
6675
|
const olMatch = line.match(/^(\s*)(\d+)[.)]\s+(.+)$/);
|
|
6742
6676
|
if (olMatch) {
|
|
6743
|
-
|
|
6744
|
-
const num = olMatch[2];
|
|
6745
|
-
const content = renderInline(olMatch[3]);
|
|
6746
|
-
output2.push(`${indent}${source_default.gray(num + ".")} ${content}`);
|
|
6677
|
+
output2.push(`${olMatch[1]}${source_default.gray(olMatch[2] + ".")} ${renderInline(olMatch[3])}`);
|
|
6747
6678
|
continue;
|
|
6748
6679
|
}
|
|
6749
6680
|
output2.push(renderInline(line));
|
|
@@ -6759,12 +6690,7 @@ function renderMarkdown(text) {
|
|
|
6759
6690
|
}
|
|
6760
6691
|
function renderInline(text) {
|
|
6761
6692
|
let result = text;
|
|
6762
|
-
result = result.replace(/`([^`]+)`/g, (_, code) =>
|
|
6763
|
-
if (looksLikeFilePath(code)) {
|
|
6764
|
-
return fileLink(source_default.cyan.underline(code), code);
|
|
6765
|
-
}
|
|
6766
|
-
return source_default.cyan(code);
|
|
6767
|
-
});
|
|
6693
|
+
result = result.replace(/`([^`]+)`/g, (_, code) => source_default.cyan(code));
|
|
6768
6694
|
result = result.replace(/\*\*\*(.+?)\*\*\*/g, (_, t) => source_default.bold.italic(t));
|
|
6769
6695
|
result = result.replace(/___(.+?)___/g, (_, t) => source_default.bold.italic(t));
|
|
6770
6696
|
result = result.replace(/\*\*(.+?)\*\*/g, (_, t) => source_default.bold(t));
|
|
@@ -7144,12 +7070,14 @@ function App({
|
|
|
7144
7070
|
const [cursorToEnd, setCursorToEnd] = useState4(0);
|
|
7145
7071
|
const [screen, setScreen] = useState4("main");
|
|
7146
7072
|
const [resumeSessions, setResumeSessions] = useState4([]);
|
|
7073
|
+
const [ctrlCPending, setCtrlCPending] = useState4(false);
|
|
7147
7074
|
const sessionId = useMemo3(() => crypto.randomUUID(), []);
|
|
7148
7075
|
const deferredMessages = useDeferredValue(messages);
|
|
7149
7076
|
const deferredPendingUpdates = useDeferredValue(pendingUpdates);
|
|
7150
7077
|
const activityFrame = useAnimatedFrame(busy);
|
|
7151
7078
|
const [agentQuestion, setAgentQuestion] = useState4(null);
|
|
7152
7079
|
const previewRef = useRef2(null);
|
|
7080
|
+
const ctrlCTimerRef = useRef2(null);
|
|
7153
7081
|
const isHome = deferredMessages.length === 0 && !busy;
|
|
7154
7082
|
const hasWorkspace = workspacePath !== null;
|
|
7155
7083
|
const hasAuth = authStatus === "connected";
|
|
@@ -7202,6 +7130,13 @@ function App({
|
|
|
7202
7130
|
cancelled = true;
|
|
7203
7131
|
};
|
|
7204
7132
|
}, [homeDir]);
|
|
7133
|
+
useEffect2(() => {
|
|
7134
|
+
return () => {
|
|
7135
|
+
if (ctrlCTimerRef.current) {
|
|
7136
|
+
clearTimeout(ctrlCTimerRef.current);
|
|
7137
|
+
}
|
|
7138
|
+
};
|
|
7139
|
+
}, []);
|
|
7205
7140
|
const [selectedSuggestion, setSelectedSuggestion] = useState4(-1);
|
|
7206
7141
|
const atMention = useMemo3(() => extractAtMention(input2), [input2]);
|
|
7207
7142
|
const suggestions = useMemo3(() => {
|
|
@@ -7733,7 +7668,58 @@ ${msg.text}
|
|
|
7733
7668
|
addSystemMessage(`Rejected: ${next.summary}`);
|
|
7734
7669
|
});
|
|
7735
7670
|
}
|
|
7671
|
+
function clearCtrlCPending() {
|
|
7672
|
+
if (ctrlCTimerRef.current) {
|
|
7673
|
+
clearTimeout(ctrlCTimerRef.current);
|
|
7674
|
+
ctrlCTimerRef.current = null;
|
|
7675
|
+
}
|
|
7676
|
+
setCtrlCPending(false);
|
|
7677
|
+
}
|
|
7678
|
+
function armCtrlCExitWindow() {
|
|
7679
|
+
if (ctrlCTimerRef.current) {
|
|
7680
|
+
clearTimeout(ctrlCTimerRef.current);
|
|
7681
|
+
}
|
|
7682
|
+
setCtrlCPending(true);
|
|
7683
|
+
ctrlCTimerRef.current = setTimeout(() => {
|
|
7684
|
+
ctrlCTimerRef.current = null;
|
|
7685
|
+
setCtrlCPending(false);
|
|
7686
|
+
}, 3e3);
|
|
7687
|
+
}
|
|
7688
|
+
function returnToMainScreen() {
|
|
7689
|
+
setScreen("main");
|
|
7690
|
+
setComposerFocused(true);
|
|
7691
|
+
}
|
|
7736
7692
|
useInput4((key, inputKey) => {
|
|
7693
|
+
if (inputKey.ctrl && key === "c") {
|
|
7694
|
+
if (busy) {
|
|
7695
|
+
clearCtrlCPending();
|
|
7696
|
+
if (abortRef.current) {
|
|
7697
|
+
abortRef.current.abort();
|
|
7698
|
+
addSystemMessage("Interrupting agent...");
|
|
7699
|
+
}
|
|
7700
|
+
return;
|
|
7701
|
+
}
|
|
7702
|
+
if (screen !== "main") {
|
|
7703
|
+
clearCtrlCPending();
|
|
7704
|
+
returnToMainScreen();
|
|
7705
|
+
return;
|
|
7706
|
+
}
|
|
7707
|
+
if (planningState.status === "charter-review") {
|
|
7708
|
+
clearCtrlCPending();
|
|
7709
|
+
rejectCharter();
|
|
7710
|
+
return;
|
|
7711
|
+
}
|
|
7712
|
+
if (ctrlCPending) {
|
|
7713
|
+
clearCtrlCPending();
|
|
7714
|
+
app.exit();
|
|
7715
|
+
return;
|
|
7716
|
+
}
|
|
7717
|
+
armCtrlCExitWindow();
|
|
7718
|
+
return;
|
|
7719
|
+
}
|
|
7720
|
+
if (ctrlCPending) {
|
|
7721
|
+
clearCtrlCPending();
|
|
7722
|
+
}
|
|
7737
7723
|
if (inputKey.shift && inputKey.tab) {
|
|
7738
7724
|
setAgentMode((prev) => {
|
|
7739
7725
|
const modes = ["manual-review", "auto-approve", "auto-research"];
|
|
@@ -8132,6 +8118,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8132
8118
|
addSystemMessage("Charter cancelled. Planning reset.");
|
|
8133
8119
|
}
|
|
8134
8120
|
const statusParts = [];
|
|
8121
|
+
if (ctrlCPending) statusParts.push("Press Ctrl+C again to exit.");
|
|
8135
8122
|
if (hasAuth) statusParts.push("connected");
|
|
8136
8123
|
else statusParts.push("no auth");
|
|
8137
8124
|
if (hasWorkspace) statusParts.push(`${workspaceFiles.length} files`);
|
|
@@ -8139,7 +8126,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8139
8126
|
if (skills2.length > 0) statusParts.push(`${skills2.length} skills`);
|
|
8140
8127
|
statusParts.push(agentMode);
|
|
8141
8128
|
if (deferredPendingUpdates.length > 0) statusParts.push(`${deferredPendingUpdates.length} pending`);
|
|
8142
|
-
const statusColor = busy ? "yellow" : !hasAuth ? "red" : deferredPendingUpdates.length > 0 ? "magenta" : "green";
|
|
8129
|
+
const statusColor = busy ? "yellow" : ctrlCPending ? "yellow" : !hasAuth ? "red" : deferredPendingUpdates.length > 0 ? "magenta" : "green";
|
|
8143
8130
|
const configItems = useMemo3(() => [
|
|
8144
8131
|
{
|
|
8145
8132
|
key: "defaults.model",
|
|
@@ -8194,8 +8181,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8194
8181
|
await saveOpenResearchConfig(updated, { homeDir });
|
|
8195
8182
|
}
|
|
8196
8183
|
function handleConfigClose() {
|
|
8197
|
-
|
|
8198
|
-
setComposerFocused(true);
|
|
8184
|
+
returnToMainScreen();
|
|
8199
8185
|
}
|
|
8200
8186
|
if (screen === "resume") {
|
|
8201
8187
|
return /* @__PURE__ */ jsx5(
|
|
@@ -8213,12 +8199,10 @@ ${error.stack}` : String(error)}` }
|
|
|
8213
8199
|
} catch (err) {
|
|
8214
8200
|
addSystemMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8215
8201
|
}
|
|
8216
|
-
|
|
8217
|
-
setComposerFocused(true);
|
|
8202
|
+
returnToMainScreen();
|
|
8218
8203
|
},
|
|
8219
8204
|
onCancel: () => {
|
|
8220
|
-
|
|
8221
|
-
setComposerFocused(true);
|
|
8205
|
+
returnToMainScreen();
|
|
8222
8206
|
}
|
|
8223
8207
|
}
|
|
8224
8208
|
);
|
|
@@ -8371,7 +8355,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8371
8355
|
statusParts,
|
|
8372
8356
|
statusColor,
|
|
8373
8357
|
tokenDisplay,
|
|
8374
|
-
workspaceName: hasWorkspace ?
|
|
8358
|
+
workspaceName: hasWorkspace ? path20.basename(workspacePath) : process.cwd(),
|
|
8375
8359
|
mode: agentMode,
|
|
8376
8360
|
planningStatus: planningState.status
|
|
8377
8361
|
}
|
|
@@ -8383,7 +8367,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8383
8367
|
var program = new Command();
|
|
8384
8368
|
program.name("open-research").version(getPackageVersion()).description("Local-first research CLI powered by ChatGPT/Codex auth.").argument("[workspacePath]", "Optional workspace path to open").action(async (workspacePath) => {
|
|
8385
8369
|
await ensureOpenResearchConfig();
|
|
8386
|
-
const target = workspacePath ?
|
|
8370
|
+
const target = workspacePath ? path21.resolve(workspacePath) : process.cwd();
|
|
8387
8371
|
const project = await loadWorkspaceProject(target);
|
|
8388
8372
|
const auth2 = await loadStoredAuth();
|
|
8389
8373
|
render(
|
|
@@ -8394,12 +8378,13 @@ program.name("open-research").version(getPackageVersion()).description("Local-fi
|
|
|
8394
8378
|
screen: "home",
|
|
8395
8379
|
pendingUpdates: []
|
|
8396
8380
|
}
|
|
8397
|
-
})
|
|
8381
|
+
}),
|
|
8382
|
+
{ exitOnCtrlC: false }
|
|
8398
8383
|
);
|
|
8399
8384
|
});
|
|
8400
8385
|
program.command("init").argument("[workspacePath]").description("Initialize an Open Research workspace.").action(async (workspacePath) => {
|
|
8401
8386
|
await ensureOpenResearchConfig();
|
|
8402
|
-
const target =
|
|
8387
|
+
const target = path21.resolve(workspacePath ?? process.cwd());
|
|
8403
8388
|
const project = await initWorkspace({ workspaceDir: target });
|
|
8404
8389
|
console.log(`Initialized workspace: ${target}`);
|
|
8405
8390
|
console.log(`Title: ${project.title}`);
|
|
@@ -8468,8 +8453,8 @@ skills.command("create").argument("[name]").description("Scaffold a new user ski
|
|
|
8468
8453
|
});
|
|
8469
8454
|
skills.command("edit").argument("<name>").description("Open a user skill in $EDITOR.").action(async (name) => {
|
|
8470
8455
|
await ensureOpenResearchConfig();
|
|
8471
|
-
const skillDir =
|
|
8472
|
-
openInEditor(
|
|
8456
|
+
const skillDir = path21.join(getOpenResearchSkillsDir(), name);
|
|
8457
|
+
openInEditor(path21.join(skillDir, "SKILL.md"));
|
|
8473
8458
|
const validation = await validateSkillDirectory({ skillDir });
|
|
8474
8459
|
if (!validation.ok) {
|
|
8475
8460
|
console.error(validation.errors.join("\n"));
|
|
@@ -8480,9 +8465,9 @@ skills.command("edit").argument("<name>").description("Open a user skill in $EDI
|
|
|
8480
8465
|
});
|
|
8481
8466
|
skills.command("validate").argument("[nameOrPath]").description("Validate one user skill.").action(async (nameOrPath) => {
|
|
8482
8467
|
await ensureOpenResearchConfig();
|
|
8483
|
-
const skillDir = nameOrPath ?
|
|
8468
|
+
const skillDir = nameOrPath ? path21.isAbsolute(nameOrPath) ? nameOrPath : path21.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
|
|
8484
8469
|
const stat = await import("fs/promises").then(
|
|
8485
|
-
(
|
|
8470
|
+
(fs21) => fs21.stat(skillDir).catch(() => null)
|
|
8486
8471
|
);
|
|
8487
8472
|
if (!stat) {
|
|
8488
8473
|
throw new Error(`Skill path not found: ${skillDir}`);
|
package/package.json
CHANGED