@longtable/cli 0.1.28 → 0.1.30

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 CHANGED
@@ -200,11 +200,18 @@ deduplicates, ranks, and labels results as evidence cards. Some sources work
200
200
  without keys, some require a contact email, and some need API keys for reliable
201
201
  use.
202
202
 
203
+ Publisher access is configured separately through environment variables and
204
+ DOI probes. `longtable search setup` checks Elsevier, Springer Nature, Wiley,
205
+ and Taylor & Francis credentials or TDM tokens without storing secrets.
206
+
203
207
  Citation support should be checked explicitly. A reference can be useful as
204
208
  background while still failing to support the specific claim attached to it.
205
209
 
206
210
  ```bash
211
+ longtable search setup
212
+ longtable search probe --doi "10.1016/example" --publisher elsevier
207
213
  longtable search --query "trust calibration measurement" --intent measurement
214
+ longtable search --query "trust calibration measurement" --publisher-access --json
208
215
  longtable search --query "trust calibration citation support" --intent citation --record
209
216
  ```
210
217
 
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { stdin as input, stdout as output, cwd, env, exit } from "node:process";
8
8
  import { dirname, join, resolve } from "node:path";
9
9
  import { homedir } from "node:os";
10
10
  import { classifyCheckpointTrigger } from "@longtable/checkpoints";
11
- import { assessSearchSourceCapabilities, buildResearchSearchIntent, runResearchSearch } from "@longtable/research-search";
11
+ import { assessSearchSourceCapabilities, buildResearchSearchIntent, buildSearchCapabilitySnapshot, parsePublisherTarget, probePublisherAccess, publisherConfigs, runResearchSearch, searchCapabilitySnapshotPath, summarizeConfiguredPublisherAccess } from "./search/index.js";
12
12
  import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupOutput, saveSetupAndRuntimeConfig, serializeSetupOutput, writeRuntimeConfig } from "@longtable/setup";
13
13
  import { buildCodexSkillSpecs, buildCodexThinWrappedPrompt, installCodexSkills, listInstalledCodexSkills, renderQuestionRecordPrompt, removeCodexSkills, resolveCodexSkillsDir, runCodexThinWrapper } from "@longtable/provider-codex";
14
14
  import { buildClaudeSkillSpecs, installClaudeSkills, listInstalledClaudeSkills, renderQuestionRecordInput, removeClaudeSkills, resolveClaudeSkillsDir } from "@longtable/provider-claude";
@@ -43,7 +43,7 @@ const ANSI = {
43
43
  green: "\u001B[32m"
44
44
  };
45
45
  const LONGTABLE_MCP_SERVER_NAME = "longtable-state";
46
- const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.28";
46
+ const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.30";
47
47
  const LONGTABLE_MCP_MARKER_START = "# LongTable state MCP START";
48
48
  const LONGTABLE_MCP_MARKER_END = "# LongTable state MCP END";
49
49
  function style(text, prefix) {
@@ -89,7 +89,10 @@ function usage() {
89
89
  " longtable show [--json] [--path <file>]",
90
90
  " longtable install [--json] [--path <file>] [--runtime-path <file>]",
91
91
  " longtable mcp install [--provider codex|claude|all] [--write] [--checkpoint-ui off|interactive|strong] [--json] [--codex-config <path>] [--claude-settings <path>] [--package <spec>]",
92
- " 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] [--record] [--cwd <path>] [--json]",
92
+ " 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]",
93
+ " longtable search setup [--doi <doi>] [--json]",
94
+ " longtable search doctor [--doi <doi>] [--publisher auto|elsevier|springer_nature|wiley|taylor_francis|all] [--json]",
95
+ " longtable search probe --doi <doi> [--publisher auto|elsevier|springer_nature|wiley|taylor_francis] [--json]",
93
96
  " longtable sentinel --prompt <text> [--cwd <path>] [--json] [--record]",
94
97
  " longtable team --prompt <text> [--role <role[,role]>] [--debate] [--rounds 3|5] [--cwd <path>] [--json]",
95
98
  " longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
@@ -136,6 +139,10 @@ function parseArgs(argv) {
136
139
  else if (command === "codex" || command === "claude" || command === "mcp") {
137
140
  startIndex = 2;
138
141
  }
142
+ else if (command === "search" && maybeSubcommand && !maybeSubcommand.startsWith("--")) {
143
+ subcommand = maybeSubcommand;
144
+ startIndex = 2;
145
+ }
139
146
  else if (directCommand) {
140
147
  subcommand = undefined;
141
148
  startIndex = 1;
@@ -2013,6 +2020,7 @@ function renderEvidenceRunSummary(run, recordedPath) {
2013
2020
  ].filter(Boolean).join(", ");
2014
2021
  lines.push(` - ${card.title}`);
2015
2022
  lines.push(` score: ${card.relevanceScore}; support: ${card.citationSupportStatus}; depth: ${card.evidenceDepth}; sources: ${card.sourceRoutes.join(", ")}`);
2023
+ lines.push(` access: ${card.accessStatus}; verification: ${card.verificationNote}`);
2016
2024
  if (identifiers) {
2017
2025
  lines.push(` ids: ${identifiers}`);
2018
2026
  }
@@ -2056,7 +2064,194 @@ async function recordEvidenceRun(run, workingDirectory) {
2056
2064
  await syncCurrentWorkspaceView(context);
2057
2065
  return evidencePath;
2058
2066
  }
2059
- async function runSearch(args) {
2067
+ function renderPublisherAccessRecord(record) {
2068
+ const envSummary = record.missingEnv.length > 0
2069
+ ? `missing ${record.missingEnv.join(", ")}`
2070
+ : `configured ${record.presentEnv.join(", ") || "none"}`;
2071
+ const lines = [
2072
+ `${record.publisher}:`,
2073
+ ` credential: ${record.credentialStatus} (${envSummary})`,
2074
+ ` entitlement: ${record.entitlementStatus}; tdm: ${record.tdmStatus}; depth: ${record.collectionDepth}`,
2075
+ ` verification: ${record.verificationNote}`
2076
+ ];
2077
+ if (record.testedDoi) {
2078
+ lines.push(` tested doi: ${record.testedDoi}`);
2079
+ }
2080
+ if (record.endpoint) {
2081
+ lines.push(` endpoint: ${record.endpoint}`);
2082
+ }
2083
+ if (record.licenseNote) {
2084
+ lines.push(` license: ${record.licenseNote}`);
2085
+ }
2086
+ if (record.setupHint) {
2087
+ lines.push(` setup: ${record.setupHint}`);
2088
+ }
2089
+ return lines.join("\n");
2090
+ }
2091
+ function renderPublisherAccessRecords(title, records, capabilityPath) {
2092
+ const lines = [title];
2093
+ if (capabilityPath) {
2094
+ lines.push(`- capability file: ${capabilityPath}`);
2095
+ }
2096
+ for (const record of records) {
2097
+ lines.push(renderPublisherAccessRecord(record));
2098
+ }
2099
+ return lines.join("\n");
2100
+ }
2101
+ async function saveSearchCapabilityRecords(records) {
2102
+ const snapshotPath = searchCapabilitySnapshotPath();
2103
+ await mkdir(dirname(snapshotPath), { recursive: true });
2104
+ await writeJsonFile(snapshotPath, buildSearchCapabilitySnapshot(records, env));
2105
+ return snapshotPath;
2106
+ }
2107
+ async function probeAllPublishers(doi) {
2108
+ const records = [];
2109
+ for (const publisher of publisherConfigs()) {
2110
+ records.push(await probePublisherAccess({
2111
+ doi,
2112
+ publisher: publisher.publisher,
2113
+ env
2114
+ }));
2115
+ }
2116
+ return records;
2117
+ }
2118
+ async function runSearchProbe(args) {
2119
+ if (typeof args.doi !== "string" || !args.doi.trim()) {
2120
+ throw new Error("`longtable search probe` requires --doi <doi>.");
2121
+ }
2122
+ const publisher = parsePublisherTarget(args.publisher);
2123
+ const record = await probePublisherAccess({
2124
+ doi: args.doi,
2125
+ publisher,
2126
+ env
2127
+ });
2128
+ if (args.json === true) {
2129
+ console.log(JSON.stringify({ record }, null, 2));
2130
+ }
2131
+ else {
2132
+ console.log(renderPublisherAccessRecord(record));
2133
+ }
2134
+ return [record];
2135
+ }
2136
+ async function runSearchDoctor(args) {
2137
+ let records;
2138
+ if (typeof args.doi === "string" && args.doi.trim()) {
2139
+ if (args.publisher === "all") {
2140
+ records = await probeAllPublishers(args.doi);
2141
+ }
2142
+ else {
2143
+ const publisher = parsePublisherTarget(args.publisher);
2144
+ records = [await probePublisherAccess({
2145
+ doi: args.doi,
2146
+ publisher,
2147
+ env
2148
+ })];
2149
+ }
2150
+ }
2151
+ else {
2152
+ records = summarizeConfiguredPublisherAccess(env);
2153
+ }
2154
+ const snapshotPath = searchCapabilitySnapshotPath();
2155
+ const snapshotExists = existsSync(snapshotPath);
2156
+ if (args.json === true) {
2157
+ console.log(JSON.stringify({
2158
+ capabilityFile: snapshotPath,
2159
+ capabilityFileExists: snapshotExists,
2160
+ records
2161
+ }, null, 2));
2162
+ }
2163
+ else {
2164
+ console.log(renderPublisherAccessRecords("LongTable Search Publisher Access Doctor", records, snapshotPath));
2165
+ if (!snapshotExists) {
2166
+ console.log("- saved capabilities: none yet; run `longtable search setup` to record non-secret capability status.");
2167
+ }
2168
+ }
2169
+ return records;
2170
+ }
2171
+ async function promptPublisherDoi(rl, label, defaultDoi) {
2172
+ const prompt = defaultDoi
2173
+ ? `${label} test DOI [${defaultDoi}, Enter to reuse, skip to skip]: `
2174
+ : `${label} test DOI (Enter to skip): `;
2175
+ const answer = (await rl.question(prompt)).trim();
2176
+ if (!answer && defaultDoi) {
2177
+ return defaultDoi;
2178
+ }
2179
+ if (!answer || /^skip$/i.test(answer)) {
2180
+ return undefined;
2181
+ }
2182
+ return answer;
2183
+ }
2184
+ async function runInteractiveSearchSetup(defaultDoi) {
2185
+ const rl = createInterface({ input, output });
2186
+ const records = [];
2187
+ try {
2188
+ console.log("LongTable publisher access setup");
2189
+ console.log("LongTable does not store API keys or TDM tokens. It reads environment variables and records only non-secret capability results.");
2190
+ console.log("");
2191
+ for (const publisher of publisherConfigs()) {
2192
+ console.log(`${publisher.label}`);
2193
+ console.log(` required env: ${publisher.requiredEnv.join(", ")}`);
2194
+ if (publisher.optionalEnv.length > 0) {
2195
+ console.log(` optional env: ${publisher.optionalEnv.join(", ")}`);
2196
+ }
2197
+ console.log(` ${publisher.setupHint}`);
2198
+ const doi = await promptPublisherDoi(rl, publisher.label, defaultDoi);
2199
+ if (doi) {
2200
+ records.push(await probePublisherAccess({
2201
+ doi,
2202
+ publisher: publisher.publisher,
2203
+ env
2204
+ }));
2205
+ }
2206
+ else {
2207
+ const summary = summarizeConfiguredPublisherAccess(env)
2208
+ .find((record) => record.publisher === publisher.publisher);
2209
+ if (summary) {
2210
+ records.push(summary);
2211
+ }
2212
+ }
2213
+ console.log(renderPublisherAccessRecord(records[records.length - 1]));
2214
+ console.log("");
2215
+ }
2216
+ }
2217
+ finally {
2218
+ rl.close();
2219
+ }
2220
+ return records;
2221
+ }
2222
+ async function runSearchSetup(args) {
2223
+ const defaultDoi = typeof args.doi === "string" ? args.doi : undefined;
2224
+ const records = input.isTTY && output.isTTY && args.json !== true
2225
+ ? await runInteractiveSearchSetup(defaultDoi)
2226
+ : defaultDoi
2227
+ ? await probeAllPublishers(defaultDoi)
2228
+ : summarizeConfiguredPublisherAccess(env);
2229
+ const snapshotPath = await saveSearchCapabilityRecords(records);
2230
+ if (args.json === true) {
2231
+ console.log(JSON.stringify({
2232
+ capabilityFile: snapshotPath,
2233
+ snapshot: buildSearchCapabilitySnapshot(records, env)
2234
+ }, null, 2));
2235
+ return;
2236
+ }
2237
+ console.log(renderPublisherAccessRecords("LongTable Search Publisher Access Setup", records, snapshotPath));
2238
+ }
2239
+ async function runSearch(subcommand, args) {
2240
+ if (subcommand === "probe") {
2241
+ await runSearchProbe(args);
2242
+ return;
2243
+ }
2244
+ if (subcommand === "doctor" || subcommand === "status") {
2245
+ await runSearchDoctor(args);
2246
+ return;
2247
+ }
2248
+ if (subcommand === "setup") {
2249
+ await runSearchSetup(args);
2250
+ return;
2251
+ }
2252
+ if (subcommand) {
2253
+ throw new Error(`Unknown search subcommand: ${subcommand}`);
2254
+ }
2060
2255
  const workingDirectory = typeof args.cwd === "string" ? args.cwd : cwd();
2061
2256
  const projectContext = await loadProjectContextFromDirectory(workingDirectory);
2062
2257
  const searchInput = {
@@ -2082,7 +2277,8 @@ async function runSearch(args) {
2082
2277
  const run = await runResearchSearch({
2083
2278
  ...searchInput,
2084
2279
  env,
2085
- allowPartial
2280
+ allowPartial,
2281
+ publisherAccess: args["publisher-access"] === true
2086
2282
  });
2087
2283
  let recordedPath;
2088
2284
  if (args.record === true && run.status !== "blocked") {
@@ -2907,7 +3103,7 @@ async function main() {
2907
3103
  return;
2908
3104
  }
2909
3105
  if (command === "search") {
2910
- await runSearch(values);
3106
+ await runSearch(subcommand, values);
2911
3107
  return;
2912
3108
  }
2913
3109
  if (command === "ask") {
@@ -0,0 +1,6 @@
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";
@@ -0,0 +1,6 @@
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";
@@ -0,0 +1,23 @@
1
+ import { type CrossrefTdmDiscovery, type EvidenceCard, type Publisher, type PublisherAccessRecord, type PublisherProbeInput, type PublisherProbeTarget, type SearchCapabilitySnapshot, 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 buildSearchCapabilitySnapshot(records: PublisherAccessRecord[], env?: Record<string, string | undefined>): SearchCapabilitySnapshot;
16
+ export declare function searchCapabilitySnapshotPath(home?: string): string;
17
+ export declare function enrichCardsWithPublisherAccess(input: {
18
+ cards: EvidenceCard[];
19
+ env?: Record<string, string | undefined>;
20
+ fetch?: SearchFetch;
21
+ limit?: number;
22
+ }): Promise<EvidenceCard[]>;
23
+ export {};