@agentproto/corpus-cli 0.1.0-alpha.2 → 0.1.0-alpha.4

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.mjs CHANGED
@@ -7,11 +7,12 @@ import { fileURLToPath, pathToFileURL } from 'url';
7
7
  import { z } from 'zod';
8
8
  import { randomBytes, randomUUID } from 'crypto';
9
9
  import { readFileSync, existsSync } from 'fs';
10
- import { buildDistillPrompt, parseItems, isRefinedKind, SyncRunner, resolveKnowledge, DistillRunner, systemClock, WebImporter, ImporterRunner, CorpusEventEmitter, CorpusWorkspaceReader, CorpusLinter, CorpusValidator, normalizeLanguageTag } from '@agentproto/corpus';
10
+ import { buildDistillPrompt, parseItems, isRefinedKind, SyncRunner, resolveKnowledge, DistillRunner, systemClock, WebImporter, ImporterRunner, CorpusEventEmitter, CorpusWorkspaceReader, CorpusLinter, CorpusValidator, ReadOnlyFs, normalizeLanguageTag } from '@agentproto/corpus';
11
11
  import matter2 from 'gray-matter';
12
12
  import { execFile } from 'child_process';
13
13
  import { promisify } from 'util';
14
14
  import { parseClaudeJsonOutput, spawnWithStdin } from '@agentproto/cli-exec';
15
+ import { stitchReport, buildPacks, reportConfigSchema } from '@agentproto/corpus/report';
15
16
 
16
17
  /**
17
18
  * @agentproto/corpus-cli v0.1.0-alpha
@@ -103,9 +104,9 @@ function resolvePackageJson(packageName) {
103
104
  }
104
105
  return null;
105
106
  }
106
- async function readJsonIfExists(path4) {
107
+ async function readJsonIfExists(path5) {
107
108
  try {
108
- const raw = await readFile(path4, "utf8");
109
+ const raw = await readFile(path5, "utf8");
109
110
  return JSON.parse(raw);
110
111
  } catch (err) {
111
112
  if (err.code === "ENOENT") return void 0;
@@ -114,9 +115,9 @@ async function readJsonIfExists(path4) {
114
115
  }
115
116
  async function readConfiguredPresetPackages() {
116
117
  const base = process.env["AGENTPROTO_HOME"] ?? join(homedir(), ".agentproto");
117
- const path4 = join(base, "config.json");
118
+ const path5 = join(base, "config.json");
118
119
  try {
119
- const raw = await readFile(path4, "utf8");
120
+ const raw = await readFile(path5, "utf8");
120
121
  const parsed = JSON.parse(raw);
121
122
  const list = parsed.corpusPresetPackages;
122
123
  if (Array.isArray(list) && list.length > 0) return list;
@@ -453,6 +454,10 @@ Run \`corpus init --list\` for available presets.`,
453
454
  2
454
455
  );
455
456
  }
457
+ if (!presetSlug) {
458
+ const nameErr = invalidCorpusName(name);
459
+ if (nameErr) return fail(nameErr, 2);
460
+ }
456
461
  const target = resolveWorkspacePath(pathArg);
457
462
  const fs = new NodeFsAdapter({ root: target });
458
463
  if (await fs.exists("KNOWLEDGE.md")) {
@@ -507,6 +512,19 @@ Try: corpus import-web ${pathArg ?? "."} --urls-file urls.txt
507
512
  );
508
513
  return 0;
509
514
  }
515
+ var CORPUS_NAME_RE = /^[a-z][a-z0-9-]*[a-z0-9]$/;
516
+ function invalidCorpusName(name) {
517
+ if (name.length >= 2 && name.length <= 96 && CORPUS_NAME_RE.test(name)) {
518
+ return null;
519
+ }
520
+ const suggestion = slugifyName(name);
521
+ const hint = suggestion.length >= 2 && CORPUS_NAME_RE.test(suggestion) ? `
522
+ Did you mean: ${suggestion}` : "";
523
+ return `init: invalid corpus name "${name}". A corpus name is a kebab-case identifier \u2014 lowercase letters, digits and hyphens, 2\u201396 chars, starting with a letter and ending alphanumerically (^[a-z][a-z0-9-]*[a-z0-9]$).${hint}`;
524
+ }
525
+ function slugifyName(raw) {
526
+ return raw.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
527
+ }
510
528
  function splitList(v) {
511
529
  return (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
512
530
  }
@@ -1126,18 +1144,18 @@ var AssemblyAiStt = class {
1126
1144
  ...job.language_code ? { language: job.language_code } : {}
1127
1145
  };
1128
1146
  }
1129
- async post(path4, body) {
1130
- const r = await fetch(`${this.baseUrl}${path4}`, {
1147
+ async post(path5, body) {
1148
+ const r = await fetch(`${this.baseUrl}${path5}`, {
1131
1149
  method: "POST",
1132
1150
  headers: { authorization: this.apiKey, "content-type": "application/json" },
1133
1151
  body: JSON.stringify(body)
1134
1152
  });
1135
- if (!r.ok) throw new Error(`AssemblyAI POST ${path4} ${r.status}: ${(await r.text()).slice(0, 200)}`);
1153
+ if (!r.ok) throw new Error(`AssemblyAI POST ${path5} ${r.status}: ${(await r.text()).slice(0, 200)}`);
1136
1154
  return AAI_TRANSCRIPT.parse(await r.json());
1137
1155
  }
1138
- async get(path4) {
1139
- const r = await fetch(`${this.baseUrl}${path4}`, { headers: { authorization: this.apiKey } });
1140
- if (!r.ok) throw new Error(`AssemblyAI GET ${path4} ${r.status}: ${(await r.text()).slice(0, 200)}`);
1156
+ async get(path5) {
1157
+ const r = await fetch(`${this.baseUrl}${path5}`, { headers: { authorization: this.apiKey } });
1158
+ if (!r.ok) throw new Error(`AssemblyAI GET ${path5} ${r.status}: ${(await r.text()).slice(0, 200)}`);
1141
1159
  return AAI_TRANSCRIPT.parse(await r.json());
1142
1160
  }
1143
1161
  };
@@ -2003,13 +2021,13 @@ async function readSources(root) {
2003
2021
  const out = [];
2004
2022
  for (const e of entries) {
2005
2023
  if (!e.isFile() || !e.name.endsWith(".md")) continue;
2006
- const path4 = join(e.parentPath, e.name);
2024
+ const path5 = join(e.parentPath, e.name);
2007
2025
  try {
2008
- const parsed = matter2(await readFile(path4, "utf-8"));
2026
+ const parsed = matter2(await readFile(path5, "utf-8"));
2009
2027
  const fm = SOURCE_FRONTMATTER.parse(parsed.data);
2010
2028
  if (!fm.id || !parsed.content.trim()) continue;
2011
2029
  out.push({
2012
- path: path4,
2030
+ path: path5,
2013
2031
  id: fm.id,
2014
2032
  title: fm.title ?? fm.id,
2015
2033
  body: parsed.content.trim(),
@@ -2168,6 +2186,126 @@ async function runKnowledge(args) {
2168
2186
  }
2169
2187
  return 0;
2170
2188
  }
2189
+ async function runReport(args) {
2190
+ const [sub, ...rest] = args;
2191
+ switch (sub) {
2192
+ case "packs":
2193
+ return await runReportPacks(rest);
2194
+ case "stitch":
2195
+ return await runReportStitch(rest);
2196
+ case void 0:
2197
+ return fail("report needs a subcommand (packs | stitch). Try --help.", 2);
2198
+ default:
2199
+ return fail(`unknown report subcommand "${sub}". Try --help.`, 2);
2200
+ }
2201
+ }
2202
+ function loadReportConfig(configPath) {
2203
+ const raw = readFileSync(path.resolve(process.cwd(), configPath), "utf8");
2204
+ return reportConfigSchema.parse(JSON.parse(raw));
2205
+ }
2206
+ async function runReportPacks(args) {
2207
+ let dataset;
2208
+ let configPath;
2209
+ let out;
2210
+ let viewsDir;
2211
+ for (let i = 0; i < args.length; i++) {
2212
+ const a = args[i];
2213
+ const next = () => args[++i];
2214
+ switch (a) {
2215
+ case "--config":
2216
+ configPath = next();
2217
+ break;
2218
+ case "--out":
2219
+ out = next();
2220
+ break;
2221
+ case "--views-dir":
2222
+ viewsDir = next();
2223
+ break;
2224
+ default:
2225
+ if (!a.startsWith("-") && dataset === void 0) dataset = a;
2226
+ }
2227
+ }
2228
+ const datasetPath = resolveWorkspacePath(dataset);
2229
+ if (!configPath) {
2230
+ return fail("report packs needs --config <report.config.json>.", 2);
2231
+ }
2232
+ let config;
2233
+ try {
2234
+ config = loadReportConfig(configPath);
2235
+ } catch (err) {
2236
+ return fail(
2237
+ `could not load/validate config "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
2238
+ 2
2239
+ );
2240
+ }
2241
+ const reportRoot = out ? path.resolve(process.cwd(), out) : datasetPath;
2242
+ const datasetFs = new ReadOnlyFs(new NodeFsAdapter({ root: datasetPath }));
2243
+ const reportFs = new NodeFsAdapter({ root: reportRoot });
2244
+ const result = await buildPacks({
2245
+ dataset: datasetFs,
2246
+ config,
2247
+ ...viewsDir ? { viewsDir } : {}
2248
+ });
2249
+ for (const file of result.files) {
2250
+ await reportFs.writeFile(file.path, file.content);
2251
+ }
2252
+ process.stdout.write(
2253
+ `report packs \u2192 ${result.files.length} files \xB7 ${result.bibliography} sources
2254
+ ` + result.chapters.map((c) => ` ${c.id}: ${c.entryCount}`).join("\n") + "\n"
2255
+ );
2256
+ return 0;
2257
+ }
2258
+ async function runReportStitch(args) {
2259
+ let reportRoot;
2260
+ let configPath;
2261
+ let out;
2262
+ let chaptersDir;
2263
+ let viewsDir;
2264
+ for (let i = 0; i < args.length; i++) {
2265
+ const a = args[i];
2266
+ const next = () => args[++i];
2267
+ switch (a) {
2268
+ case "--config":
2269
+ configPath = next();
2270
+ break;
2271
+ case "--out":
2272
+ out = next();
2273
+ break;
2274
+ case "--chapters-dir":
2275
+ chaptersDir = next();
2276
+ break;
2277
+ case "--views-dir":
2278
+ viewsDir = next();
2279
+ break;
2280
+ default:
2281
+ if (!a.startsWith("-") && reportRoot === void 0) reportRoot = a;
2282
+ }
2283
+ }
2284
+ const root = resolveWorkspacePath(reportRoot);
2285
+ if (!configPath) {
2286
+ return fail("report stitch needs --config <report.config.json>.", 2);
2287
+ }
2288
+ let config;
2289
+ try {
2290
+ config = loadReportConfig(configPath);
2291
+ } catch (err) {
2292
+ return fail(
2293
+ `could not load/validate config "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
2294
+ 2
2295
+ );
2296
+ }
2297
+ const reportFs = new NodeFsAdapter({ root });
2298
+ const { content, wordCount } = await stitchReport({
2299
+ config,
2300
+ report: reportFs,
2301
+ ...chaptersDir ? { chaptersDir } : {},
2302
+ ...viewsDir ? { viewsDir } : {}
2303
+ });
2304
+ await reportFs.writeFile(out ?? "REPORT.md", content);
2305
+ process.stdout.write(`report stitch \u2192 ${out ?? "REPORT.md"} \xB7 ${wordCount} words
2306
+ `);
2307
+ return 0;
2308
+ }
2171
2309
  var RPC_RESPONSE2 = z.object({ result: z.unknown().optional(), error: z.unknown().optional() }).loose();
2172
2310
  var RPC_MESSAGE = z.object({
2173
2311
  id: z.number().optional(),
@@ -2566,6 +2704,14 @@ Commands:
2566
2704
  knowledge [path] --tags a,b [--kind k --access scope --max n]
2567
2705
  Preview what a skill's knowledge: binding resolves to
2568
2706
  \u2014 refined entries + their provenance (filesystem).
2707
+ report packs [dataset] --config <f> [--out <dir>] [--views-dir <n>]
2708
+ Build per-chapter knowledge views + the global
2709
+ citation bibliography from a dataset. Dataset is
2710
+ mounted read-only; --out (default: dataset) is the
2711
+ only writer; --views-dir defaults to "views".
2712
+ report stitch [report] --config <f> [--out <REPORT.md>] [--chapters-dir <d> --views-dir <n>]
2713
+ Stitch front + parts + chapters + annexes + Sources
2714
+ into one REPORT.md (report-side reads only).
2569
2715
  sync [path] --config <sink.json> [--tags a,b --kind k --throttle ms]
2570
2716
  Push refined entries to an external store via a
2571
2717
  config-driven MCP sink (host-agnostic).
@@ -2605,6 +2751,8 @@ async function main(argv2) {
2605
2751
  return await runDistill(rest);
2606
2752
  case "knowledge":
2607
2753
  return await runKnowledge(rest);
2754
+ case "report":
2755
+ return await runReport(rest);
2608
2756
  case "sync":
2609
2757
  return await runSync(rest);
2610
2758
  default: