@longtable/cli 0.1.59 → 0.1.60

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 CHANGED
@@ -10,7 +10,7 @@ import { homedir } from "node:os";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { collectHardStopBlockers } from "@longtable/core";
12
12
  import { classifyCheckpointTrigger } from "@longtable/checkpoints";
13
- import { assessSearchSourceCapabilities, buildResearchSearchIntent, parsePublisherTarget, probePublisherAccess, publisherConfigs, runResearchSearch, SEARCH_SOURCES, summarizeConfiguredPublisherAccess } from "./search/index.js";
13
+ import { assessSearchSourceCapabilities, assessScholarResearchReadiness, buildResearchSearchIntent, buildScholarResearchSmokeFixture, parsePublisherTarget, probePublisherAccess, publisherConfigs, runResearchSearch, SEARCH_SOURCES, writeScholarResearchRunScaffold, summarizeConfiguredPublisherAccess } from "./search/index.js";
14
14
  import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupOutput, saveSetupAndRuntimeConfig, serializeSetupOutput, writeRuntimeConfig } from "@longtable/setup";
15
15
  import { buildCodexSkillSpecs, buildCodexThinWrappedPrompt, installCodexSkills, listInstalledCodexSkills, renderQuestionRecordPrompt, removeCodexSkills, resolveCodexSkillsDir, runCodexThinWrapper } from "@longtable/provider-codex";
16
16
  import { buildClaudeSkillSpecs, installClaudeSkills, listInstalledClaudeSkills, renderQuestionRecordInput, removeClaudeSkills, resolveClaudeSkillsDir } from "@longtable/provider-claude";
@@ -154,6 +154,9 @@ function usage() {
154
154
  " longtable access doctor [--doi <doi>] [--publisher auto|elsevier|springer_nature|wiley|taylor_francis|all] [--json]",
155
155
  " longtable access probe --doi <doi> [--publisher auto|elsevier|springer_nature|wiley|taylor_francis] [--json]",
156
156
  " longtable search --query <text> [--intent literature|theory|measurement|citation|metadata|venue] [--field <text>] [--source all|crossref,arxiv,openalex,semantic_scholar,pubmed,eric,doaj,unpaywall] [--must <term[,term]>] [--exclude <term[,term]>] [--limit <n>] [--allow-partial] [--publisher-access] [--record] [--cwd <path>] [--json]",
157
+ " longtable scholar-research doctor [--json]",
158
+ " longtable scholar-research scaffold [--cwd <path>] [--run-id <id>] [--json]",
159
+ " longtable scholar-research smoke-fixture [--json]",
157
160
  " longtable sentinel --prompt <text> [--cwd <path>] [--json] [--record]",
158
161
  " longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
159
162
  " longtable clarify --prompt <task-context> [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json] [--force]",
@@ -201,7 +204,7 @@ function parseArgs(argv) {
201
204
  const values = {};
202
205
  let subcommand = maybeSubcommand;
203
206
  const modeCommand = command && VALID_MODES.has(command);
204
- const directCommand = command && ["init", "setup", "start", "resume", "doctor", "status", "audit", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "clear-question", "prune-questions", "panel", "handoff", "decide", "sentinel", "access", "search", "spec"].includes(command);
207
+ const directCommand = command && ["init", "setup", "start", "resume", "doctor", "status", "audit", "roles", "show", "install", "mcp", "codex", "claude", "ask", "clarify", "question", "clear-question", "prune-questions", "panel", "handoff", "decide", "sentinel", "access", "search", "scholar-research", "spec"].includes(command);
205
208
  let startIndex = 1;
206
209
  if (modeCommand) {
207
210
  subcommand = undefined;
@@ -210,7 +213,7 @@ function parseArgs(argv) {
210
213
  else if (command === "codex" || command === "claude" || command === "mcp") {
211
214
  startIndex = 2;
212
215
  }
213
- else if ((command === "access" || command === "search" || command === "spec" || command === "panel") && maybeSubcommand && !maybeSubcommand.startsWith("--")) {
216
+ else if ((command === "access" || command === "search" || command === "scholar-research" || command === "spec" || command === "panel") && maybeSubcommand && !maybeSubcommand.startsWith("--")) {
214
217
  subcommand = maybeSubcommand;
215
218
  startIndex = 2;
216
219
  }
@@ -991,15 +994,25 @@ async function collectProjectInterview(setup, args) {
991
994
  ]));
992
995
  console.log("");
993
996
  }
994
- const projectName = (typeof args.name === "string" && args.name.trim()) ||
997
+ const projectNameInput = (typeof args.name === "string" && args.name.trim()) ||
995
998
  (await promptText(renderQuestionHeader(1, 2, "Workspace", "What should this project be called?"), true));
999
+ if (!projectNameInput) {
1000
+ throw new Error("LongTable start needs a project name.");
1001
+ }
1002
+ const projectName = projectNameInput;
996
1003
  const suggestedParentDir = typeof args.path === "string" && args.path.trim()
997
1004
  ? normalizeUserPath(args.path.trim())
998
1005
  : homedir();
999
1006
  const suggestedPath = resolveInteractiveProjectPath(suggestedParentDir, projectName);
1000
- const projectPath = (typeof args.path === "string" && args.path.trim()
1007
+ const projectPathInput = typeof args.path === "string" && args.path.trim()
1001
1008
  ? normalizeUserPath(args.path.trim())
1002
- : resolveInteractiveProjectPath((await promptText(renderQuestionHeader(2, 2, "Workspace", `Which parent directory should contain this project?\nLongTable will create this folder:\n${suggestedPath}`), true)), projectName));
1009
+ : await promptText(renderQuestionHeader(2, 2, "Workspace", `Which parent directory should contain this project?\nLongTable will create this folder:\n${suggestedPath}`), true);
1010
+ if (!projectPathInput) {
1011
+ throw new Error("LongTable start needs a project path.");
1012
+ }
1013
+ const projectPath = typeof args.path === "string" && args.path.trim()
1014
+ ? projectPathInput
1015
+ : resolveInteractiveProjectPath(projectPathInput, projectName);
1003
1016
  const adaptive = skipResearchInterview
1004
1017
  ? {}
1005
1018
  : await collectAdaptiveStartInterview({
@@ -1637,6 +1650,7 @@ async function collectDoctorStatus(args) {
1637
1650
  return {
1638
1651
  setupPath,
1639
1652
  setupExists: existsSync(setupPath),
1653
+ scholarResearch: assessScholarResearchReadiness(env),
1640
1654
  providers: {
1641
1655
  codex: {
1642
1656
  command: "codex",
@@ -1700,6 +1714,8 @@ function renderDoctorStatus(status) {
1700
1714
  const lines = [
1701
1715
  "LongTable doctor",
1702
1716
  `- setup: ${status.setupExists ? "present" : "missing"} (${status.setupPath})`,
1717
+ `- scholar-research connectors: ${status.scholarResearch.connectors.filter((connector) => connector.status === "ready").length}/${status.scholarResearch.connectors.length} ready`,
1718
+ `- scholar-research safety: paywall bypass ${status.scholarResearch.safety.paywallBypassAllowed ? "allowed" : "disabled"}, institution login automation ${status.scholarResearch.safety.institutionLoginAutomationAllowed ? "allowed" : "disabled"}`,
1703
1719
  "",
1704
1720
  ...renderProviderDoctorBlock("Codex", status.providers.codex),
1705
1721
  `- legacy prompt files: ${status.providers.codex.legacyPromptFilesInstalled.length}`,
@@ -2083,12 +2099,14 @@ function buildRoleAuditEntry(provider, spec) {
2083
2099
  }
2084
2100
  function runRoleAudit() {
2085
2101
  const baseSkillNames = new Set([
2102
+ "critical-interview",
2086
2103
  "longtable",
2087
2104
  "longtable-start",
2088
2105
  "longtable-interview",
2089
2106
  "longtable-panel",
2090
2107
  "longtable-explore",
2091
- "longtable-review"
2108
+ "longtable-review",
2109
+ "scholar-research"
2092
2110
  ]);
2093
2111
  const roles = [
2094
2112
  ...buildCodexSkillSpecs(listRoleDefinitions(), "full")
@@ -2829,6 +2847,9 @@ async function runPanelCommand(args) {
2829
2847
  console.log(renderPanelSummary(fallback.plan));
2830
2848
  console.log("");
2831
2849
  if (finalNativeRun) {
2850
+ if (!nativeRunContext) {
2851
+ throw new Error("Native panel worker run finished without a run context.");
2852
+ }
2832
2853
  console.log("LongTable native panel worker run created");
2833
2854
  console.log(`- run: ${finalNativeRun.id}`);
2834
2855
  console.log(`- status: ${finalNativeRun.status}`);
@@ -3455,6 +3476,52 @@ async function runSearch(subcommand, args) {
3455
3476
  }
3456
3477
  console.log(renderEvidenceRunSummary(run, recordedPath));
3457
3478
  }
3479
+ async function runScholarResearch(subcommand, args) {
3480
+ const json = args.json === true;
3481
+ if (subcommand === "doctor" || subcommand === "status" || !subcommand) {
3482
+ const readiness = assessScholarResearchReadiness(env);
3483
+ if (json) {
3484
+ console.log(JSON.stringify(readiness, null, 2));
3485
+ return;
3486
+ }
3487
+ console.log("LongTable scholar-research doctor");
3488
+ for (const connector of readiness.connectors) {
3489
+ const missing = connector.missingEnv.length > 0 ? ` (missing ${connector.missingEnv.join(", ")})` : "";
3490
+ console.log(`- ${connector.name}: ${connector.status}${missing}`);
3491
+ }
3492
+ console.log("- safety: paywall bypass disabled; institution-login automation disabled");
3493
+ return;
3494
+ }
3495
+ if (subcommand === "scaffold") {
3496
+ const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
3497
+ const runId = typeof args["run-id"] === "string" ? args["run-id"] : undefined;
3498
+ const scaffold = await writeScholarResearchRunScaffold({
3499
+ cwd: workingDirectory,
3500
+ ...(runId ? { runId } : {})
3501
+ });
3502
+ if (json) {
3503
+ console.log(JSON.stringify(scaffold, null, 2));
3504
+ return;
3505
+ }
3506
+ console.log("LongTable scholar-research scaffold");
3507
+ console.log(`- run: ${scaffold.runId}`);
3508
+ console.log(`- dir: ${scaffold.runDir}`);
3509
+ return;
3510
+ }
3511
+ if (subcommand === "smoke-fixture") {
3512
+ const fixture = buildScholarResearchSmokeFixture();
3513
+ if (json) {
3514
+ console.log(JSON.stringify({ fixture }, null, 2));
3515
+ return;
3516
+ }
3517
+ console.log("LongTable scholar-research smoke fixture");
3518
+ for (const item of fixture) {
3519
+ console.log(`- ${item.id}: ${item.category} - ${item.label}`);
3520
+ }
3521
+ return;
3522
+ }
3523
+ throw new Error(`Unknown scholar-research subcommand: ${subcommand}`);
3524
+ }
3458
3525
  async function requireWorkspaceContext(args) {
3459
3526
  const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
3460
3527
  const context = await loadProjectContextFromDirectory(workingDirectory);
@@ -4588,6 +4655,10 @@ async function main() {
4588
4655
  await runSearch(subcommand, values);
4589
4656
  return;
4590
4657
  }
4658
+ if (command === "scholar-research") {
4659
+ await runScholarResearch(subcommand, values);
4660
+ return;
4661
+ }
4591
4662
  if (command === "spec") {
4592
4663
  await runSpec(subcommand, values);
4593
4664
  return;
package/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ export * from "./panel.js";
6
6
  export * from "./project-session.js";
7
7
  export * from "./question-obligations.js";
8
8
  export * from "./hard-stop.js";
9
+ export * from "./search/index.js";
package/dist/index.js CHANGED
@@ -6,3 +6,4 @@ export * from "./panel.js";
6
6
  export * from "./project-session.js";
7
7
  export * from "./question-obligations.js";
8
8
  export * from "./hard-stop.js";
9
+ export * from "./search/index.js";
@@ -1,6 +1 @@
1
- export * from "./types.js";
2
- export * from "./query.js";
3
- export * from "./sources.js";
4
- export * from "./rank.js";
5
- export * from "./run.js";
6
- export * from "./publisher-access.js";
1
+ export * from "@longtable/scholar-research";
@@ -1,6 +1 @@
1
- export * from "./types.js";
2
- export * from "./query.js";
3
- export * from "./sources.js";
4
- export * from "./rank.js";
5
- export * from "./run.js";
6
- export * from "./publisher-access.js";
1
+ export * from "@longtable/scholar-research";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/cli",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "private": false,
5
5
  "description": "Researcher-facing LongTable CLI",
6
6
  "type": "module",
@@ -29,12 +29,13 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@clack/prompts": "^1.2.0",
32
- "@longtable/checkpoints": "0.1.59",
33
- "@longtable/core": "0.1.59",
34
- "@longtable/memory": "0.1.59",
35
- "@longtable/provider-claude": "0.1.59",
36
- "@longtable/provider-codex": "0.1.59",
37
- "@longtable/setup": "0.1.59"
32
+ "@longtable/checkpoints": "0.1.60",
33
+ "@longtable/core": "0.1.60",
34
+ "@longtable/memory": "0.1.60",
35
+ "@longtable/provider-claude": "0.1.60",
36
+ "@longtable/provider-codex": "0.1.60",
37
+ "@longtable/scholar-research": "0.1.60",
38
+ "@longtable/setup": "0.1.60"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/node": "^22.10.1",
@@ -1,21 +0,0 @@
1
- import { type CrossrefTdmDiscovery, type EvidenceCard, type Publisher, type PublisherAccessRecord, type PublisherProbeInput, type PublisherProbeTarget, type SearchFetch } from "./types.js";
2
- interface PublisherConfig {
3
- publisher: Publisher;
4
- label: string;
5
- requiredEnv: string[];
6
- optionalEnv: string[];
7
- setupHint: string;
8
- }
9
- export declare function normalizeDoi(value: string): string;
10
- export declare function parsePublisherTarget(value?: string | boolean): PublisherProbeTarget;
11
- export declare function discoverCrossrefTdm(doi: string, env?: Record<string, string | undefined>, httpFetch?: SearchFetch): Promise<CrossrefTdmDiscovery>;
12
- export declare function publisherConfigs(): PublisherConfig[];
13
- export declare function probePublisherAccess(input: PublisherProbeInput): Promise<PublisherAccessRecord>;
14
- export declare function summarizeConfiguredPublisherAccess(env?: Record<string, string | undefined>): PublisherAccessRecord[];
15
- export declare function enrichCardsWithPublisherAccess(input: {
16
- cards: EvidenceCard[];
17
- env?: Record<string, string | undefined>;
18
- fetch?: SearchFetch;
19
- limit?: number;
20
- }): Promise<EvidenceCard[]>;
21
- export {};