open-research 0.1.21 → 0.1.23
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 +155 -26
- 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 path22 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.23";
|
|
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 path21 from "path";
|
|
875
875
|
import {
|
|
876
876
|
startTransition,
|
|
877
877
|
useDeferredValue,
|
|
@@ -1384,6 +1384,8 @@ var source_default = chalk;
|
|
|
1384
1384
|
// src/tui/text-input.tsx
|
|
1385
1385
|
import { jsx } from "react/jsx-runtime";
|
|
1386
1386
|
var PASTE_MARKER = "\uFFFC";
|
|
1387
|
+
var BRACKETED_PASTE_START = "[200~";
|
|
1388
|
+
var BRACKETED_PASTE_END = "[201~";
|
|
1387
1389
|
function expandPasteMarkers(value, pasteMap) {
|
|
1388
1390
|
let result = "";
|
|
1389
1391
|
let pasteIdx = 0;
|
|
@@ -1428,6 +1430,7 @@ function TextInput({
|
|
|
1428
1430
|
const cursorOffsetRef = useRef(originalValue.length);
|
|
1429
1431
|
const pasteMapRef = useRef(/* @__PURE__ */ new Map());
|
|
1430
1432
|
const pasteCounterRef = useRef(0);
|
|
1433
|
+
const bracketedPasteBufferRef = useRef(null);
|
|
1431
1434
|
useEffect(() => {
|
|
1432
1435
|
valueRef.current = originalValue;
|
|
1433
1436
|
}, [originalValue]);
|
|
@@ -1498,6 +1501,28 @@ function TextInput({
|
|
|
1498
1501
|
return result;
|
|
1499
1502
|
}
|
|
1500
1503
|
const renderedPlaceholder = showCursor && focus && placeholder.length > 0 ? source_default.inverse(placeholder[0]) + source_default.grey(placeholder.slice(1)) : placeholder ? source_default.grey(placeholder) : void 0;
|
|
1504
|
+
function sanitizeInput(raw) {
|
|
1505
|
+
return raw.replace(/\x1b\[[?>=!]*[0-9;]*[a-zA-Z~]/g, "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?/g, "").replace(/\[20[01]~/g, "").replace(/\d+;\d+;\d+[~u]/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "");
|
|
1506
|
+
}
|
|
1507
|
+
function insertCleanText(raw, currentValue, currentCursor) {
|
|
1508
|
+
const clean = sanitizeInput(raw);
|
|
1509
|
+
if (!clean) {
|
|
1510
|
+
return { nextValue: currentValue, nextCursor: currentCursor };
|
|
1511
|
+
}
|
|
1512
|
+
const lineCount = (clean.match(/\n/g) || []).length;
|
|
1513
|
+
if (lineCount >= 2) {
|
|
1514
|
+
const id = ++pasteCounterRef.current;
|
|
1515
|
+
pasteMapRef.current.set(id, { text: clean, lineCount, id });
|
|
1516
|
+
return {
|
|
1517
|
+
nextValue: currentValue.slice(0, currentCursor) + PASTE_MARKER + currentValue.slice(currentCursor),
|
|
1518
|
+
nextCursor: currentCursor + 1
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
return {
|
|
1522
|
+
nextValue: currentValue.slice(0, currentCursor) + clean + currentValue.slice(currentCursor),
|
|
1523
|
+
nextCursor: currentCursor + clean.length
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1501
1526
|
useInput(
|
|
1502
1527
|
(input2, key) => {
|
|
1503
1528
|
const currentValue = valueRef.current;
|
|
@@ -1517,7 +1542,7 @@ function TextInput({
|
|
|
1517
1542
|
onTab?.();
|
|
1518
1543
|
return;
|
|
1519
1544
|
}
|
|
1520
|
-
if (key.return && key.shift || key.return && key.meta) {
|
|
1545
|
+
if (key.return && key.shift || key.return && key.meta || input2 === "27;2;13~" || input2.includes("27;2;13")) {
|
|
1521
1546
|
const inserted = currentValue.slice(0, currentCursor) + "\n" + currentValue.slice(currentCursor);
|
|
1522
1547
|
cursorOffsetRef.current = currentCursor + 1;
|
|
1523
1548
|
valueRef.current = inserted;
|
|
@@ -1537,10 +1562,12 @@ function TextInput({
|
|
|
1537
1562
|
nextValue = currentValue.slice(0, boundary) + currentValue.slice(currentCursor);
|
|
1538
1563
|
nextCursor = boundary;
|
|
1539
1564
|
} else if (key.ctrl && input2 === "u") {
|
|
1540
|
-
|
|
1541
|
-
|
|
1565
|
+
const lineStart = currentValue.lastIndexOf("\n", currentCursor - 1) + 1;
|
|
1566
|
+
nextValue = currentValue.slice(0, lineStart) + currentValue.slice(currentCursor);
|
|
1567
|
+
nextCursor = lineStart;
|
|
1542
1568
|
} else if (key.ctrl && input2 === "k") {
|
|
1543
|
-
|
|
1569
|
+
const lineEnd = currentValue.indexOf("\n", currentCursor);
|
|
1570
|
+
nextValue = currentValue.slice(0, currentCursor) + (lineEnd === -1 ? "" : currentValue.slice(lineEnd));
|
|
1544
1571
|
} else if (key.meta && key.delete) {
|
|
1545
1572
|
const boundary = nextWordBoundary(currentValue, currentCursor);
|
|
1546
1573
|
nextValue = currentValue.slice(0, currentCursor) + currentValue.slice(boundary);
|
|
@@ -1573,18 +1600,54 @@ function TextInput({
|
|
|
1573
1600
|
} else if (key.rightArrow) {
|
|
1574
1601
|
if (showCursor) nextCursor++;
|
|
1575
1602
|
} else if (!key.ctrl && !key.meta) {
|
|
1576
|
-
const
|
|
1577
|
-
if (
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1603
|
+
const hasPasteMarkers = input2.includes(BRACKETED_PASTE_START) || input2.includes(BRACKETED_PASTE_END) || bracketedPasteBufferRef.current !== null;
|
|
1604
|
+
if (hasPasteMarkers) {
|
|
1605
|
+
let remaining = input2;
|
|
1606
|
+
let workingValue = currentValue;
|
|
1607
|
+
let workingCursor = currentCursor;
|
|
1608
|
+
while (remaining.length > 0) {
|
|
1609
|
+
if (bracketedPasteBufferRef.current === null) {
|
|
1610
|
+
const startIndex = remaining.indexOf(BRACKETED_PASTE_START);
|
|
1611
|
+
if (startIndex === -1) {
|
|
1612
|
+
const inserted2 = insertCleanText(remaining, workingValue, workingCursor);
|
|
1613
|
+
workingValue = inserted2.nextValue;
|
|
1614
|
+
workingCursor = inserted2.nextCursor;
|
|
1615
|
+
remaining = "";
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
1618
|
+
const prefix = remaining.slice(0, startIndex);
|
|
1619
|
+
if (prefix) {
|
|
1620
|
+
const inserted2 = insertCleanText(prefix, workingValue, workingCursor);
|
|
1621
|
+
workingValue = inserted2.nextValue;
|
|
1622
|
+
workingCursor = inserted2.nextCursor;
|
|
1623
|
+
}
|
|
1624
|
+
bracketedPasteBufferRef.current = "";
|
|
1625
|
+
remaining = remaining.slice(startIndex + BRACKETED_PASTE_START.length);
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
const endIndex = remaining.indexOf(BRACKETED_PASTE_END);
|
|
1629
|
+
if (endIndex === -1) {
|
|
1630
|
+
bracketedPasteBufferRef.current += remaining;
|
|
1631
|
+
remaining = "";
|
|
1632
|
+
break;
|
|
1633
|
+
}
|
|
1634
|
+
bracketedPasteBufferRef.current += remaining.slice(0, endIndex);
|
|
1635
|
+
const inserted = insertCleanText(
|
|
1636
|
+
bracketedPasteBufferRef.current,
|
|
1637
|
+
workingValue,
|
|
1638
|
+
workingCursor
|
|
1639
|
+
);
|
|
1640
|
+
workingValue = inserted.nextValue;
|
|
1641
|
+
workingCursor = inserted.nextCursor;
|
|
1642
|
+
bracketedPasteBufferRef.current = null;
|
|
1643
|
+
remaining = remaining.slice(endIndex + BRACKETED_PASTE_END.length);
|
|
1587
1644
|
}
|
|
1645
|
+
nextValue = workingValue;
|
|
1646
|
+
nextCursor = workingCursor;
|
|
1647
|
+
} else {
|
|
1648
|
+
const inserted = insertCleanText(input2, currentValue, currentCursor);
|
|
1649
|
+
nextValue = inserted.nextValue;
|
|
1650
|
+
nextCursor = inserted.nextCursor;
|
|
1588
1651
|
}
|
|
1589
1652
|
}
|
|
1590
1653
|
nextCursor = Math.max(0, Math.min(nextCursor, nextValue.length));
|
|
@@ -5351,6 +5414,7 @@ ${skill.prompt}`).join("\n\n");
|
|
|
5351
5414
|
"- Be transparent. Show the user what you're doing and why.",
|
|
5352
5415
|
"- When unsure, ask. Use ask_user rather than guessing.",
|
|
5353
5416
|
"- For large outputs, redirect to a file and read selectively.",
|
|
5417
|
+
"- Always wrap file paths in backticks when mentioning them, e.g. `notes/brief.md` or `experiments/analysis.py`. This makes them clickable in the terminal.",
|
|
5354
5418
|
"",
|
|
5355
5419
|
`## Workspace
|
|
5356
5420
|
Root: ${process.cwd()}
|
|
@@ -6553,6 +6617,66 @@ function truncate3(value, max = 96) {
|
|
|
6553
6617
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
6554
6618
|
|
|
6555
6619
|
// src/tui/markdown.ts
|
|
6620
|
+
import path20 from "path";
|
|
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
|
+
}
|
|
6556
6680
|
function renderMarkdown(text) {
|
|
6557
6681
|
if (!text || !text.trim()) return text;
|
|
6558
6682
|
if (!/[*_`#\[\]>~\-]/.test(text) && !text.includes("```")) return text;
|
|
@@ -6635,7 +6759,12 @@ function renderMarkdown(text) {
|
|
|
6635
6759
|
}
|
|
6636
6760
|
function renderInline(text) {
|
|
6637
6761
|
let result = text;
|
|
6638
|
-
result = result.replace(/`([^`]+)`/g, (_, code) =>
|
|
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
|
+
});
|
|
6639
6768
|
result = result.replace(/\*\*\*(.+?)\*\*\*/g, (_, t) => source_default.bold.italic(t));
|
|
6640
6769
|
result = result.replace(/___(.+?)___/g, (_, t) => source_default.bold.italic(t));
|
|
6641
6770
|
result = result.replace(/\*\*(.+?)\*\*/g, (_, t) => source_default.bold(t));
|
|
@@ -8242,7 +8371,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8242
8371
|
statusParts,
|
|
8243
8372
|
statusColor,
|
|
8244
8373
|
tokenDisplay,
|
|
8245
|
-
workspaceName: hasWorkspace ?
|
|
8374
|
+
workspaceName: hasWorkspace ? path21.basename(workspacePath) : process.cwd(),
|
|
8246
8375
|
mode: agentMode,
|
|
8247
8376
|
planningStatus: planningState.status
|
|
8248
8377
|
}
|
|
@@ -8254,7 +8383,7 @@ ${error.stack}` : String(error)}` }
|
|
|
8254
8383
|
var program = new Command();
|
|
8255
8384
|
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) => {
|
|
8256
8385
|
await ensureOpenResearchConfig();
|
|
8257
|
-
const target = workspacePath ?
|
|
8386
|
+
const target = workspacePath ? path22.resolve(workspacePath) : process.cwd();
|
|
8258
8387
|
const project = await loadWorkspaceProject(target);
|
|
8259
8388
|
const auth2 = await loadStoredAuth();
|
|
8260
8389
|
render(
|
|
@@ -8270,7 +8399,7 @@ program.name("open-research").version(getPackageVersion()).description("Local-fi
|
|
|
8270
8399
|
});
|
|
8271
8400
|
program.command("init").argument("[workspacePath]").description("Initialize an Open Research workspace.").action(async (workspacePath) => {
|
|
8272
8401
|
await ensureOpenResearchConfig();
|
|
8273
|
-
const target =
|
|
8402
|
+
const target = path22.resolve(workspacePath ?? process.cwd());
|
|
8274
8403
|
const project = await initWorkspace({ workspaceDir: target });
|
|
8275
8404
|
console.log(`Initialized workspace: ${target}`);
|
|
8276
8405
|
console.log(`Title: ${project.title}`);
|
|
@@ -8339,8 +8468,8 @@ skills.command("create").argument("[name]").description("Scaffold a new user ski
|
|
|
8339
8468
|
});
|
|
8340
8469
|
skills.command("edit").argument("<name>").description("Open a user skill in $EDITOR.").action(async (name) => {
|
|
8341
8470
|
await ensureOpenResearchConfig();
|
|
8342
|
-
const skillDir =
|
|
8343
|
-
openInEditor(
|
|
8471
|
+
const skillDir = path22.join(getOpenResearchSkillsDir(), name);
|
|
8472
|
+
openInEditor(path22.join(skillDir, "SKILL.md"));
|
|
8344
8473
|
const validation = await validateSkillDirectory({ skillDir });
|
|
8345
8474
|
if (!validation.ok) {
|
|
8346
8475
|
console.error(validation.errors.join("\n"));
|
|
@@ -8351,9 +8480,9 @@ skills.command("edit").argument("<name>").description("Open a user skill in $EDI
|
|
|
8351
8480
|
});
|
|
8352
8481
|
skills.command("validate").argument("[nameOrPath]").description("Validate one user skill.").action(async (nameOrPath) => {
|
|
8353
8482
|
await ensureOpenResearchConfig();
|
|
8354
|
-
const skillDir = nameOrPath ?
|
|
8483
|
+
const skillDir = nameOrPath ? path22.isAbsolute(nameOrPath) ? nameOrPath : path22.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
|
|
8355
8484
|
const stat = await import("fs/promises").then(
|
|
8356
|
-
(
|
|
8485
|
+
(fs22) => fs22.stat(skillDir).catch(() => null)
|
|
8357
8486
|
);
|
|
8358
8487
|
if (!stat) {
|
|
8359
8488
|
throw new Error(`Skill path not found: ${skillDir}`);
|
package/package.json
CHANGED