@gobing-ai/knowledge-kit 0.0.3 → 0.0.5
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/index.js +313 -166
- package/package.json +1 -1
- package/plugins/ingestions/web-search/package.json +14 -0
- package/plugins/ingestions/web-search/plugin.json +7 -0
- package/plugins/ingestions/web-search/src/index.ts +144 -0
- package/plugins/ingestions/web-search/tsconfig.json +4 -0
- package/plugins/kk/skills/kk-judge/SKILL.md +1 -1
- package/plugins/kk/skills/kk-topic/SKILL.md +1 -1
package/dist/index.js
CHANGED
|
@@ -14308,12 +14308,6 @@ var logger = {
|
|
|
14308
14308
|
Bun.write(Bun.stdout, encoder.encode(text));
|
|
14309
14309
|
}
|
|
14310
14310
|
};
|
|
14311
|
-
|
|
14312
|
-
// ../../packages/utils/src/index.ts
|
|
14313
|
-
function add(a, b) {
|
|
14314
|
-
return a + b;
|
|
14315
|
-
}
|
|
14316
|
-
|
|
14317
14311
|
// ../../node_modules/.bun/commander@15.0.0/node_modules/commander/lib/error.js
|
|
14318
14312
|
class CommanderError extends Error {
|
|
14319
14313
|
constructor(exitCode, code, message) {
|
|
@@ -16358,15 +16352,6 @@ function useColor() {
|
|
|
16358
16352
|
// ../../node_modules/.bun/commander@15.0.0/node_modules/commander/index.js
|
|
16359
16353
|
var program = new Command;
|
|
16360
16354
|
|
|
16361
|
-
// src/commands/add.ts
|
|
16362
|
-
function registerAddCommand(program2) {
|
|
16363
|
-
const parseIntArg = (v) => Number.parseInt(v, 10);
|
|
16364
|
-
program2.command("add").description("Add two numbers").argument("<a>", "first number", parseIntArg).argument("<b>", "second number", parseIntArg).action((a, b) => {
|
|
16365
|
-
logger.out(`${add(a, b)}
|
|
16366
|
-
`);
|
|
16367
|
-
});
|
|
16368
|
-
}
|
|
16369
|
-
|
|
16370
16355
|
// src/fanin.ts
|
|
16371
16356
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
16372
16357
|
import { resolve as resolve2 } from "path";
|
|
@@ -16441,9 +16426,11 @@ var PluginManifestSchema = exports_external.object({
|
|
|
16441
16426
|
description: exports_external.string().optional()
|
|
16442
16427
|
}).passthrough();
|
|
16443
16428
|
// ../../packages/kk-core/src/registry.ts
|
|
16444
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
16445
|
-
import {
|
|
16446
|
-
|
|
16429
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
|
|
16430
|
+
import { homedir } from "os";
|
|
16431
|
+
import { basename, dirname, join, resolve } from "path";
|
|
16432
|
+
import { fileURLToPath } from "url";
|
|
16433
|
+
function discoverPlugins(pluginsRoot, origin = "path") {
|
|
16447
16434
|
const absRoot = resolve(pluginsRoot);
|
|
16448
16435
|
const plugins = [];
|
|
16449
16436
|
const diagnostics = [];
|
|
@@ -16454,7 +16441,7 @@ function discoverPlugins(pluginsRoot) {
|
|
|
16454
16441
|
}
|
|
16455
16442
|
const rootAsCollection = kindForCollection(basename(absRoot));
|
|
16456
16443
|
if (rootAsCollection) {
|
|
16457
|
-
scanCollection(absRoot, rootAsCollection, plugins, diagnostics, seenNames);
|
|
16444
|
+
scanCollection(absRoot, rootAsCollection, origin, plugins, diagnostics, seenNames);
|
|
16458
16445
|
return { plugins, diagnostics };
|
|
16459
16446
|
}
|
|
16460
16447
|
for (const entryName of entries.names) {
|
|
@@ -16467,7 +16454,7 @@ function discoverPlugins(pluginsRoot) {
|
|
|
16467
16454
|
}
|
|
16468
16455
|
const expectedKind = kindForCollection(entryName);
|
|
16469
16456
|
if (expectedKind) {
|
|
16470
|
-
scanCollection(dirPath, expectedKind, plugins, diagnostics, seenNames);
|
|
16457
|
+
scanCollection(dirPath, expectedKind, origin, plugins, diagnostics, seenNames);
|
|
16471
16458
|
continue;
|
|
16472
16459
|
}
|
|
16473
16460
|
const strayManifest = join(dirPath, "plugin.json");
|
|
@@ -16481,7 +16468,117 @@ function discoverPlugins(pluginsRoot) {
|
|
|
16481
16468
|
}
|
|
16482
16469
|
return { plugins, diagnostics };
|
|
16483
16470
|
}
|
|
16484
|
-
function
|
|
16471
|
+
function resolvePluginRoots(opts = {}) {
|
|
16472
|
+
if (opts.pluginsRoot !== undefined) {
|
|
16473
|
+
return [{ origin: "path", root: opts.pluginsRoot }];
|
|
16474
|
+
}
|
|
16475
|
+
const roots = [];
|
|
16476
|
+
const builtinRoot = resolveBuiltinRoot(opts.packagePluginsRoot);
|
|
16477
|
+
if (builtinRoot !== undefined) {
|
|
16478
|
+
roots.push({ origin: "builtin", root: builtinRoot });
|
|
16479
|
+
}
|
|
16480
|
+
const userRoot = join(opts.homeDir ?? homedir(), ".kk", "plugins");
|
|
16481
|
+
if (isDirectory(userRoot)) {
|
|
16482
|
+
roots.push({ origin: "user", root: userRoot });
|
|
16483
|
+
}
|
|
16484
|
+
const projectRoot = join(opts.cwd ?? process.cwd(), "plugins");
|
|
16485
|
+
if (isDirectory(projectRoot) && !(builtinRoot !== undefined && sameRealpath(projectRoot, builtinRoot))) {
|
|
16486
|
+
roots.push({ origin: "project", root: projectRoot });
|
|
16487
|
+
}
|
|
16488
|
+
const env = opts.env ?? process.env;
|
|
16489
|
+
for (const entry of (env.KK_PLUGIN_PATH ?? "").split(":").filter((e) => e !== "")) {
|
|
16490
|
+
if (isDirectory(entry)) {
|
|
16491
|
+
roots.push({ origin: "path", root: entry });
|
|
16492
|
+
}
|
|
16493
|
+
}
|
|
16494
|
+
for (const extra of opts.extraPaths ?? []) {
|
|
16495
|
+
if (extra !== "" && isDirectory(extra)) {
|
|
16496
|
+
roots.push({ origin: "path", root: extra });
|
|
16497
|
+
}
|
|
16498
|
+
}
|
|
16499
|
+
return roots;
|
|
16500
|
+
}
|
|
16501
|
+
function discoverAllPlugins(opts = {}) {
|
|
16502
|
+
const roots = resolvePluginRoots(opts);
|
|
16503
|
+
const diagnostics = [];
|
|
16504
|
+
const byName = new Map;
|
|
16505
|
+
const scannedRealpaths = new Set;
|
|
16506
|
+
for (const { origin, root } of roots) {
|
|
16507
|
+
let real;
|
|
16508
|
+
try {
|
|
16509
|
+
real = realpathSync(root);
|
|
16510
|
+
} catch {
|
|
16511
|
+
continue;
|
|
16512
|
+
}
|
|
16513
|
+
if (scannedRealpaths.has(real)) {
|
|
16514
|
+
continue;
|
|
16515
|
+
}
|
|
16516
|
+
scannedRealpaths.add(real);
|
|
16517
|
+
if (origin === "path" && existsSync(join(root, "plugin.json"))) {
|
|
16518
|
+
const records = [];
|
|
16519
|
+
const rootDiagnostics = [];
|
|
16520
|
+
consumeManifest(root, null, origin, records, rootDiagnostics, new Set);
|
|
16521
|
+
diagnostics.push(...rootDiagnostics);
|
|
16522
|
+
for (const record2 of records) {
|
|
16523
|
+
mergeByName(record2, byName, diagnostics);
|
|
16524
|
+
}
|
|
16525
|
+
continue;
|
|
16526
|
+
}
|
|
16527
|
+
const scan = discoverPlugins(root, origin);
|
|
16528
|
+
diagnostics.push(...scan.diagnostics);
|
|
16529
|
+
for (const record2 of scan.plugins) {
|
|
16530
|
+
mergeByName(record2, byName, diagnostics);
|
|
16531
|
+
}
|
|
16532
|
+
}
|
|
16533
|
+
return { plugins: [...byName.values()], diagnostics };
|
|
16534
|
+
}
|
|
16535
|
+
function mergeByName(record2, byName, diagnostics) {
|
|
16536
|
+
const existing = byName.get(record2.name);
|
|
16537
|
+
if (existing !== undefined) {
|
|
16538
|
+
diagnostics.push({
|
|
16539
|
+
code: "shadowed",
|
|
16540
|
+
path: existing.manifestPath,
|
|
16541
|
+
reason: `Plugin name "${record2.name}" shadowed by ` + `${record2.manifestPath} (origin: ${record2.origin})`
|
|
16542
|
+
});
|
|
16543
|
+
}
|
|
16544
|
+
byName.set(record2.name, record2);
|
|
16545
|
+
}
|
|
16546
|
+
function resolveBuiltinRoot(injected) {
|
|
16547
|
+
if (injected !== undefined) {
|
|
16548
|
+
return injected;
|
|
16549
|
+
}
|
|
16550
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
16551
|
+
for (;; ) {
|
|
16552
|
+
const candidate = join(dir, "plugins");
|
|
16553
|
+
if (isPluginsTree(candidate)) {
|
|
16554
|
+
return candidate;
|
|
16555
|
+
}
|
|
16556
|
+
const parent = dirname(dir);
|
|
16557
|
+
if (parent === dir) {
|
|
16558
|
+
return;
|
|
16559
|
+
}
|
|
16560
|
+
dir = parent;
|
|
16561
|
+
}
|
|
16562
|
+
}
|
|
16563
|
+
function isPluginsTree(dir) {
|
|
16564
|
+
if (!isDirectory(dir)) {
|
|
16565
|
+
return false;
|
|
16566
|
+
}
|
|
16567
|
+
for (const collection of Object.keys(PLUGIN_COLLECTION_KINDS)) {
|
|
16568
|
+
if (isDirectory(join(dir, collection))) {
|
|
16569
|
+
return true;
|
|
16570
|
+
}
|
|
16571
|
+
}
|
|
16572
|
+
return false;
|
|
16573
|
+
}
|
|
16574
|
+
function sameRealpath(a, b) {
|
|
16575
|
+
try {
|
|
16576
|
+
return realpathSync(a) === realpathSync(b);
|
|
16577
|
+
} catch {
|
|
16578
|
+
return false;
|
|
16579
|
+
}
|
|
16580
|
+
}
|
|
16581
|
+
function scanCollection(collectionDir, expectedKind, origin, plugins, diagnostics, seenNames) {
|
|
16485
16582
|
const entries = listSortedDir(collectionDir);
|
|
16486
16583
|
if (!entries.ok) {
|
|
16487
16584
|
diagnostics.push(entries.diagnostic);
|
|
@@ -16492,15 +16589,23 @@ function scanCollection(collectionDir, expectedKind, plugins, diagnostics, seenN
|
|
|
16492
16589
|
if (!isDirectory(dirPath)) {
|
|
16493
16590
|
continue;
|
|
16494
16591
|
}
|
|
16495
|
-
consumeManifest(dirPath, expectedKind, plugins, diagnostics, seenNames);
|
|
16592
|
+
consumeManifest(dirPath, expectedKind, origin, plugins, diagnostics, seenNames);
|
|
16496
16593
|
}
|
|
16497
16594
|
}
|
|
16498
|
-
function consumeManifest(dirPath, expectedKind, plugins, diagnostics, seenNames) {
|
|
16595
|
+
function consumeManifest(dirPath, expectedKind, origin, plugins, diagnostics, seenNames) {
|
|
16499
16596
|
const manifestPath = join(dirPath, "plugin.json");
|
|
16500
16597
|
let content;
|
|
16501
16598
|
try {
|
|
16502
16599
|
content = readFileSync(manifestPath, "utf-8");
|
|
16503
|
-
} catch {
|
|
16600
|
+
} catch (err) {
|
|
16601
|
+
if (err?.code === "ENOENT") {
|
|
16602
|
+
return;
|
|
16603
|
+
}
|
|
16604
|
+
diagnostics.push({
|
|
16605
|
+
code: "unreadable",
|
|
16606
|
+
path: manifestPath,
|
|
16607
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
16608
|
+
});
|
|
16504
16609
|
return;
|
|
16505
16610
|
}
|
|
16506
16611
|
let json2;
|
|
@@ -16525,7 +16630,7 @@ function consumeManifest(dirPath, expectedKind, plugins, diagnostics, seenNames)
|
|
|
16525
16630
|
return;
|
|
16526
16631
|
}
|
|
16527
16632
|
const manifest = parsed.data;
|
|
16528
|
-
if (manifest.kind !== expectedKind) {
|
|
16633
|
+
if (expectedKind !== null && manifest.kind !== expectedKind) {
|
|
16529
16634
|
diagnostics.push({
|
|
16530
16635
|
code: "kind_mismatch",
|
|
16531
16636
|
path: manifestPath,
|
|
@@ -16548,6 +16653,7 @@ function consumeManifest(dirPath, expectedKind, plugins, diagnostics, seenNames)
|
|
|
16548
16653
|
entry: manifest.entry,
|
|
16549
16654
|
dir: dirPath,
|
|
16550
16655
|
manifestPath,
|
|
16656
|
+
origin,
|
|
16551
16657
|
version: manifest.version,
|
|
16552
16658
|
description: manifest.description
|
|
16553
16659
|
});
|
|
@@ -16573,9 +16679,6 @@ function isDirectory(path2) {
|
|
|
16573
16679
|
return false;
|
|
16574
16680
|
}
|
|
16575
16681
|
}
|
|
16576
|
-
function listByKind(result, kind) {
|
|
16577
|
-
return result.plugins.filter((p) => p.kind === kind);
|
|
16578
|
-
}
|
|
16579
16682
|
function getByName(result, name) {
|
|
16580
16683
|
return result.plugins.find((p) => p.name === name);
|
|
16581
16684
|
}
|
|
@@ -16698,25 +16801,6 @@ async function runFanin(argv) {
|
|
|
16698
16801
|
return 0;
|
|
16699
16802
|
}
|
|
16700
16803
|
|
|
16701
|
-
// src/commands/fanin.ts
|
|
16702
|
-
function registerFaninCommand(program2) {
|
|
16703
|
-
const collectIn = (value, acc) => {
|
|
16704
|
-
acc.push(value);
|
|
16705
|
-
return acc;
|
|
16706
|
-
};
|
|
16707
|
-
program2.command("fanin").description("Merge N Doc[] JSON inputs into one deduplicated Doc[] output").requiredOption("--in <path>", "Input Doc[] JSON file (repeatable)", collectIn, []).requiredOption("--out <path>", "Output merged Doc[] JSON file path").action(async (options) => {
|
|
16708
|
-
const argv = [];
|
|
16709
|
-
for (const inPath of options.in) {
|
|
16710
|
-
argv.push("--in", inPath);
|
|
16711
|
-
}
|
|
16712
|
-
argv.push("--out", options.out);
|
|
16713
|
-
const code = await runFanin(argv);
|
|
16714
|
-
if (code !== 0) {
|
|
16715
|
-
throw new CommanderError(code, "kk.fanin.failed", `fanin failed with exit code ${code}`);
|
|
16716
|
-
}
|
|
16717
|
-
});
|
|
16718
|
-
}
|
|
16719
|
-
|
|
16720
16804
|
// src/fanout.ts
|
|
16721
16805
|
import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
16722
16806
|
import { tmpdir } from "os";
|
|
@@ -16740,11 +16824,10 @@ function spawnPluginEntry(opts) {
|
|
|
16740
16824
|
};
|
|
16741
16825
|
}
|
|
16742
16826
|
async function runInvoke(opts) {
|
|
16743
|
-
const
|
|
16744
|
-
const discovery = discoverPlugins(pluginsRoot);
|
|
16827
|
+
const discovery = discoverAllPlugins({ pluginsRoot: opts.pluginsRoot, extraPaths: opts.pluginsPath });
|
|
16745
16828
|
const record2 = getByName(discovery, opts.plugin);
|
|
16746
16829
|
if (!record2) {
|
|
16747
|
-
logger.error(`Plugin '${opts.plugin}' not found
|
|
16830
|
+
logger.error(`Plugin '${opts.plugin}' not found in the merged plugin registry`);
|
|
16748
16831
|
if (discovery.diagnostics.length > 0) {
|
|
16749
16832
|
for (const diag of discovery.diagnostics) {
|
|
16750
16833
|
logger.error(` [diagnostic] ${diag.code} at ${diag.path}: ${diag.reason}`);
|
|
@@ -16981,6 +17064,7 @@ function readAggregateResult(path2) {
|
|
|
16981
17064
|
}
|
|
16982
17065
|
function parseArgs2(argv) {
|
|
16983
17066
|
const targets = [];
|
|
17067
|
+
const pluginsPath = [];
|
|
16984
17068
|
let contentPath;
|
|
16985
17069
|
let outPath;
|
|
16986
17070
|
let retryFromPath;
|
|
@@ -17029,6 +17113,12 @@ function parseArgs2(argv) {
|
|
|
17029
17113
|
throw new Error("--plugins-root may be specified only once");
|
|
17030
17114
|
}
|
|
17031
17115
|
pluginsRoot = value;
|
|
17116
|
+
} else if (arg === "--plugins-path") {
|
|
17117
|
+
const value = argv[++i];
|
|
17118
|
+
if (value === undefined) {
|
|
17119
|
+
throw new Error("--plugins-path requires a directory path");
|
|
17120
|
+
}
|
|
17121
|
+
pluginsPath.push(value);
|
|
17032
17122
|
} else {
|
|
17033
17123
|
throw new Error(`Unknown argument: ${arg}`);
|
|
17034
17124
|
}
|
|
@@ -17047,13 +17137,14 @@ function parseArgs2(argv) {
|
|
|
17047
17137
|
targets,
|
|
17048
17138
|
outPath,
|
|
17049
17139
|
...retryFromPath !== undefined ? { retryFromPath } : {},
|
|
17050
|
-
...pluginsRoot !== undefined ? { pluginsRoot } : {}
|
|
17140
|
+
...pluginsRoot !== undefined ? { pluginsRoot } : {},
|
|
17141
|
+
...pluginsPath.length > 0 ? { pluginsPath } : {}
|
|
17051
17142
|
};
|
|
17052
17143
|
}
|
|
17053
|
-
async function pluginDispatch(target, contentPath,
|
|
17054
|
-
const record2 = getByName(
|
|
17144
|
+
async function pluginDispatch(target, contentPath, discovery) {
|
|
17145
|
+
const record2 = getByName(discovery, target);
|
|
17055
17146
|
if (record2?.kind !== "publish") {
|
|
17056
|
-
throw new Error(`Publish plugin '${target}' not found
|
|
17147
|
+
throw new Error(`Publish plugin '${target}' not found in the merged plugin registry`);
|
|
17057
17148
|
}
|
|
17058
17149
|
const entryPath = isAbsolute2(record2.entry) ? record2.entry : resolve4(record2.dir, record2.entry);
|
|
17059
17150
|
if (!existsSync4(entryPath)) {
|
|
@@ -17083,8 +17174,7 @@ async function pluginDispatch(target, contentPath, pluginsRoot) {
|
|
|
17083
17174
|
rmSync(outTmp, { force: true });
|
|
17084
17175
|
}
|
|
17085
17176
|
}
|
|
17086
|
-
function validateTargets(targets,
|
|
17087
|
-
const discovery = discoverPlugins(pluginsRoot);
|
|
17177
|
+
function validateTargets(targets, discovery) {
|
|
17088
17178
|
const missing = [];
|
|
17089
17179
|
for (const target of targets) {
|
|
17090
17180
|
const record2 = getByName(discovery, target);
|
|
@@ -17093,7 +17183,7 @@ function validateTargets(targets, pluginsRoot) {
|
|
|
17093
17183
|
}
|
|
17094
17184
|
}
|
|
17095
17185
|
if (missing.length > 0) {
|
|
17096
|
-
logger.error(`Unknown publish target(s): ${missing.join(", ")}
|
|
17186
|
+
logger.error(`Unknown publish target(s): ${missing.join(", ")}`);
|
|
17097
17187
|
for (const diag of discovery.diagnostics) {
|
|
17098
17188
|
logger.error(` [diagnostic] ${diag.code} at ${diag.path}: ${diag.reason}`);
|
|
17099
17189
|
}
|
|
@@ -17113,7 +17203,12 @@ async function runFanout(argv, deps = {}) {
|
|
|
17113
17203
|
if (content === null) {
|
|
17114
17204
|
return EXIT_INPUT2;
|
|
17115
17205
|
}
|
|
17116
|
-
|
|
17206
|
+
if (opts.pluginsRoot !== undefined && (opts.pluginsPath?.length ?? 0) > 0) {
|
|
17207
|
+
logger.error("--plugins-root and --plugins-path are mutually exclusive (ADR-012 R6)");
|
|
17208
|
+
return EXIT_INPUT2;
|
|
17209
|
+
}
|
|
17210
|
+
const pluginsRoot = deps.pluginsRoot ?? opts.pluginsRoot;
|
|
17211
|
+
const discovery = discoverAllPlugins({ pluginsRoot, extraPaths: opts.pluginsPath });
|
|
17117
17212
|
let prior;
|
|
17118
17213
|
let skipped = [];
|
|
17119
17214
|
let targets = opts.targets;
|
|
@@ -17133,8 +17228,8 @@ async function runFanout(argv, deps = {}) {
|
|
|
17133
17228
|
const carried = new Set(skipped.map((t) => t.target));
|
|
17134
17229
|
targets = opts.targets.length > 0 ? opts.targets.filter((t) => !carried.has(t)) : selectRetryTargets(prior);
|
|
17135
17230
|
}
|
|
17136
|
-
const dispatch = deps.dispatch ?? ((t) => pluginDispatch(t, opts.contentPath,
|
|
17137
|
-
if (deps.dispatch === undefined && !validateTargets(targets,
|
|
17231
|
+
const dispatch = deps.dispatch ?? ((t) => pluginDispatch(t, opts.contentPath, discovery));
|
|
17232
|
+
if (deps.dispatch === undefined && !validateTargets(targets, discovery)) {
|
|
17138
17233
|
return EXIT_INPUT2;
|
|
17139
17234
|
}
|
|
17140
17235
|
const fresh = await fanout(content, targets, dispatch);
|
|
@@ -17149,130 +17244,185 @@ async function runFanout(argv, deps = {}) {
|
|
|
17149
17244
|
return result.ok ? 0 : EXIT_FAILURE;
|
|
17150
17245
|
}
|
|
17151
17246
|
|
|
17152
|
-
// src/commands/
|
|
17153
|
-
function
|
|
17154
|
-
|
|
17155
|
-
|
|
17156
|
-
|
|
17157
|
-
|
|
17158
|
-
|
|
17159
|
-
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
|
|
17163
|
-
|
|
17164
|
-
|
|
17165
|
-
|
|
17166
|
-
|
|
17167
|
-
argv.push("--plugins-root", options.pluginsRoot);
|
|
17168
|
-
}
|
|
17169
|
-
const code = await runFanout(argv);
|
|
17170
|
-
if (code !== 0) {
|
|
17171
|
-
throw new CommanderError(code, "kk.fanout.failed", `fanout failed with exit code ${code}`);
|
|
17172
|
-
}
|
|
17247
|
+
// src/commands/executor.ts
|
|
17248
|
+
function collectPath(value, acc) {
|
|
17249
|
+
acc.push(value);
|
|
17250
|
+
return acc;
|
|
17251
|
+
}
|
|
17252
|
+
async function runInvokeAction(plugin, options) {
|
|
17253
|
+
if (options.pluginsRoot !== undefined && (options.pluginsPath?.length ?? 0) > 0) {
|
|
17254
|
+
throw new CommanderError(2, "kk.executor.run.root-path-conflict", "--plugins-root and --plugins-path are mutually exclusive");
|
|
17255
|
+
}
|
|
17256
|
+
const code = await runInvoke({
|
|
17257
|
+
plugin,
|
|
17258
|
+
inPath: options.in,
|
|
17259
|
+
outPath: options.out,
|
|
17260
|
+
pluginsRoot: options.pluginsRoot,
|
|
17261
|
+
pluginsPath: options.pluginsPath
|
|
17173
17262
|
});
|
|
17263
|
+
if (code !== 0) {
|
|
17264
|
+
throw new CommanderError(code, "kk.executor.run.failed", `invoke failed with exit code ${code}`);
|
|
17265
|
+
}
|
|
17174
17266
|
}
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
|
|
17181
|
-
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
}
|
|
17188
|
-
}
|
|
17267
|
+
function collectIn(value, acc) {
|
|
17268
|
+
acc.push(value);
|
|
17269
|
+
return acc;
|
|
17270
|
+
}
|
|
17271
|
+
async function runFaninAction(options) {
|
|
17272
|
+
const argv = [];
|
|
17273
|
+
for (const inPath of options.in) {
|
|
17274
|
+
argv.push("--in", inPath);
|
|
17275
|
+
}
|
|
17276
|
+
argv.push("--out", options.out);
|
|
17277
|
+
const code = await runFanin(argv);
|
|
17278
|
+
if (code !== 0) {
|
|
17279
|
+
throw new CommanderError(code, "kk.executor.fanin.failed", `fan-in failed with exit code ${code}`);
|
|
17280
|
+
}
|
|
17281
|
+
}
|
|
17282
|
+
function collectTarget(value, acc) {
|
|
17283
|
+
acc.push(value);
|
|
17284
|
+
return acc;
|
|
17285
|
+
}
|
|
17286
|
+
async function runFanoutAction(options) {
|
|
17287
|
+
const argv = ["--content", options.content, "--out", options.out];
|
|
17288
|
+
for (const target of options.target) {
|
|
17289
|
+
argv.push("--target", target);
|
|
17290
|
+
}
|
|
17291
|
+
if (options.retryFrom) {
|
|
17292
|
+
argv.push("--retry-from", options.retryFrom);
|
|
17293
|
+
}
|
|
17294
|
+
if (options.pluginsRoot) {
|
|
17295
|
+
argv.push("--plugins-root", options.pluginsRoot);
|
|
17296
|
+
}
|
|
17297
|
+
for (const path2 of options.pluginsPath ?? []) {
|
|
17298
|
+
argv.push("--plugins-path", path2);
|
|
17299
|
+
}
|
|
17300
|
+
const code = await runFanout(argv);
|
|
17301
|
+
if (code !== 0) {
|
|
17302
|
+
throw new CommanderError(code, "kk.executor.fanout.failed", `fan-out failed with exit code ${code}`);
|
|
17303
|
+
}
|
|
17304
|
+
}
|
|
17305
|
+
function registerExecutorCommand(program2) {
|
|
17306
|
+
const executor = program2.command("executor").description("Run knowledge-kit plugin steps (invoke, fan-in, fan-out)");
|
|
17307
|
+
executor.command("run").description("Invoke a plugin with input/output files and contract validation").argument("<plugin>", "Name of the plugin to invoke").requiredOption("--in <path>", "Input file path (or source path for ingestion)").requiredOption("--out <path>", "Output file path").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runInvokeAction);
|
|
17308
|
+
executor.command("fan-in").alias("fanin").description("Merge N Doc[] JSON inputs into one deduplicated Doc[] output").requiredOption("--in <path>", "Input Doc[] JSON file (repeatable)", collectIn, []).requiredOption("--out <path>", "Output merged Doc[] JSON file path").action(runFaninAction);
|
|
17309
|
+
executor.command("fan-out").alias("fanout").description("Publish one Content JSON to N targets, aggregating per-target results").requiredOption("--content <path>", "Source ContentSchema JSON file path").option("--target <plugin>", "Target publish plugin name (repeatable)", collectTarget, []).requiredOption("--out <path>", "Aggregate Result JSON file path").option("--retry-from <path>", "Prior Result JSON file to retry only previously failed targets").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runFanoutAction);
|
|
17310
|
+
program2.command("invoke", { hidden: true }).description("Alias for `executor run` (deprecated)").argument("<plugin>", "Name of the plugin to invoke").requiredOption("--in <path>", "Input file path (or source path for ingestion)").requiredOption("--out <path>", "Output file path").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runInvokeAction);
|
|
17311
|
+
program2.command("fanin", { hidden: true }).description("Alias for `executor fan-in` (deprecated)").requiredOption("--in <path>", "Input Doc[] JSON file (repeatable)", collectIn, []).requiredOption("--out <path>", "Output merged Doc[] JSON file path").action(runFaninAction);
|
|
17312
|
+
program2.command("fanout", { hidden: true }).description("Alias for `executor fan-out` (deprecated)").requiredOption("--content <path>", "Source ContentSchema JSON file path").option("--target <plugin>", "Target publish plugin name (repeatable)", collectTarget, []).requiredOption("--out <path>", "Aggregate Result JSON file path").option("--retry-from <path>", "Prior Result JSON file to retry only previously failed targets").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(runFanoutAction);
|
|
17189
17313
|
}
|
|
17190
17314
|
|
|
17191
|
-
// src/commands/
|
|
17315
|
+
// src/commands/plugin.ts
|
|
17192
17316
|
import { existsSync as existsSync5 } from "fs";
|
|
17193
17317
|
import { resolve as resolve5 } from "path";
|
|
17194
|
-
function
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
17202
|
-
|
|
17203
|
-
|
|
17204
|
-
|
|
17205
|
-
|
|
17206
|
-
|
|
17318
|
+
function collectPath2(value, acc) {
|
|
17319
|
+
acc.push(value);
|
|
17320
|
+
return acc;
|
|
17321
|
+
}
|
|
17322
|
+
function buildStatusReport(opts) {
|
|
17323
|
+
const discovery = discoverAllPlugins({ pluginsRoot: opts.pluginsRoot, extraPaths: opts.pluginsPath });
|
|
17324
|
+
const plugins = discovery.plugins.map((record2) => {
|
|
17325
|
+
const diagnostics = [];
|
|
17326
|
+
const entryPath = resolve5(record2.dir, record2.entry);
|
|
17327
|
+
if (!existsSync5(entryPath)) {
|
|
17328
|
+
diagnostics.push({
|
|
17329
|
+
code: "missing_entry",
|
|
17330
|
+
path: entryPath,
|
|
17331
|
+
reason: `Plugin "${record2.name}" entry file does not exist: ${record2.entry}`
|
|
17332
|
+
});
|
|
17207
17333
|
}
|
|
17208
|
-
|
|
17209
|
-
|
|
17334
|
+
return { ...record2, healthy: diagnostics.length === 0, diagnostics };
|
|
17335
|
+
});
|
|
17336
|
+
return {
|
|
17337
|
+
healthy: discovery.diagnostics.length === 0 && plugins.every((p) => p.healthy),
|
|
17338
|
+
plugins,
|
|
17339
|
+
diagnostics: [...discovery.diagnostics]
|
|
17340
|
+
};
|
|
17341
|
+
}
|
|
17342
|
+
function printDiagnostics(report) {
|
|
17343
|
+
const all = [...report.diagnostics];
|
|
17344
|
+
for (const p of report.plugins) {
|
|
17345
|
+
all.push(...p.diagnostics);
|
|
17346
|
+
}
|
|
17347
|
+
logger.error(`Found ${all.length} diagnostic issue(s):
|
|
17210
17348
|
`);
|
|
17211
|
-
|
|
17212
|
-
}
|
|
17213
|
-
if (records.length === 0) {
|
|
17214
|
-
logger.out(`No plugins found.
|
|
17349
|
+
for (const d of all) {
|
|
17350
|
+
logger.error(` [${d.code}] ${d.path}: ${d.reason}
|
|
17215
17351
|
`);
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
17220
|
-
|
|
17221
|
-
if (group.length === 0)
|
|
17222
|
-
continue;
|
|
17223
|
-
logger.out(`
|
|
17224
|
-
--- ${k.toUpperCase()} PLUGINS ---
|
|
17352
|
+
}
|
|
17353
|
+
}
|
|
17354
|
+
function statusAction(options) {
|
|
17355
|
+
if (options.pluginsRoot !== undefined && (options.pluginsPath?.length ?? 0) > 0) {
|
|
17356
|
+
logger.error(`--plugins-root and --plugins-path are mutually exclusive (ADR-012 R6)
|
|
17225
17357
|
`);
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
|
|
17358
|
+
throw new CommanderError(2, "kk.plugin.root-path-conflict", "--plugins-root and --plugins-path are mutually exclusive");
|
|
17359
|
+
}
|
|
17360
|
+
const report = buildStatusReport({ pluginsRoot: options.pluginsRoot, pluginsPath: options.pluginsPath });
|
|
17361
|
+
let records = report.plugins;
|
|
17362
|
+
if (options.kind) {
|
|
17363
|
+
if (!isPluginKind(options.kind)) {
|
|
17364
|
+
logger.error(`Invalid plugin kind: ${options.kind}
|
|
17230
17365
|
`);
|
|
17231
|
-
}
|
|
17232
|
-
}
|
|
17233
|
-
});
|
|
17234
|
-
pluginsCmd.command("doctor").description("Validate plugin manifests and entries under plugins root").option("--json", "Output machine-readable JSON object").option("--plugins-root <dir>", "Root directory containing plugins").action((options) => {
|
|
17235
|
-
const root = options.pluginsRoot ?? "./plugins";
|
|
17236
|
-
const discovery = discoverPlugins(root);
|
|
17237
|
-
const diagnostics = [...discovery.diagnostics];
|
|
17238
|
-
for (const p of discovery.plugins) {
|
|
17239
|
-
const entryPath = resolve5(p.dir, p.entry);
|
|
17240
|
-
if (!existsSync5(entryPath)) {
|
|
17241
|
-
diagnostics.push({
|
|
17242
|
-
code: "missing_entry",
|
|
17243
|
-
path: entryPath,
|
|
17244
|
-
reason: `Plugin "${p.name}" entry file does not exist: ${p.entry}`
|
|
17245
|
-
});
|
|
17246
|
-
}
|
|
17366
|
+
throw new CommanderError(1, "kk.plugin.invalid-kind", `invalid plugin kind: ${options.kind}`);
|
|
17247
17367
|
}
|
|
17248
|
-
|
|
17249
|
-
|
|
17250
|
-
|
|
17368
|
+
records = report.plugins.filter((p) => p.kind === options.kind);
|
|
17369
|
+
}
|
|
17370
|
+
if (options.json) {
|
|
17371
|
+
logger.out(`${JSON.stringify({ ...report, plugins: records }, null, 2)}
|
|
17251
17372
|
`);
|
|
17252
|
-
|
|
17253
|
-
|
|
17254
|
-
}
|
|
17255
|
-
return;
|
|
17373
|
+
if (!report.healthy) {
|
|
17374
|
+
throw new CommanderError(1, "kk.plugin.unhealthy", "plugin diagnostics found");
|
|
17256
17375
|
}
|
|
17257
|
-
|
|
17258
|
-
|
|
17376
|
+
return;
|
|
17377
|
+
}
|
|
17378
|
+
if (records.length === 0) {
|
|
17379
|
+
logger.out(`No plugins found.
|
|
17259
17380
|
`);
|
|
17260
|
-
|
|
17381
|
+
if (!report.healthy) {
|
|
17382
|
+
printDiagnostics(report);
|
|
17383
|
+
throw new CommanderError(1, "kk.plugin.unhealthy", "plugin diagnostics found");
|
|
17261
17384
|
}
|
|
17262
|
-
|
|
17385
|
+
return;
|
|
17386
|
+
}
|
|
17387
|
+
const kinds = options.kind ? [options.kind] : ["ingestion", "generator", "publish"];
|
|
17388
|
+
for (const k of kinds) {
|
|
17389
|
+
const group = records.filter((p) => p.kind === k);
|
|
17390
|
+
if (group.length === 0)
|
|
17391
|
+
continue;
|
|
17392
|
+
logger.out(`
|
|
17393
|
+
--- ${k.toUpperCase()} PLUGINS ---
|
|
17263
17394
|
`);
|
|
17264
|
-
for (const
|
|
17265
|
-
|
|
17395
|
+
for (const p of group) {
|
|
17396
|
+
const ver = p.version ? ` (v${p.version})` : "";
|
|
17397
|
+
const desc = p.description ? ` - ${p.description}` : "";
|
|
17398
|
+
const health = p.healthy ? "healthy" : "unhealthy";
|
|
17399
|
+
logger.out(` \u2022 ${p.name}${ver}: ${p.entry}${desc} [${health}]
|
|
17266
17400
|
`);
|
|
17267
17401
|
}
|
|
17268
|
-
|
|
17269
|
-
|
|
17402
|
+
}
|
|
17403
|
+
if (!report.healthy) {
|
|
17404
|
+
printDiagnostics(report);
|
|
17405
|
+
throw new CommanderError(1, "kk.plugin.unhealthy", "plugin diagnostics found");
|
|
17406
|
+
}
|
|
17407
|
+
}
|
|
17408
|
+
function registerStatusVerb(parent, name, description, hidden) {
|
|
17409
|
+
parent.command(name, { hidden }).description(description).option("--kind <kind>", "Filter plugins by kind (ingestion, generator, publish)").option("--json", "Output machine-readable unified health report").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath2, []).action(statusAction);
|
|
17410
|
+
}
|
|
17411
|
+
function registerPluginCommand(program2) {
|
|
17412
|
+
const plugin = program2.command("plugin").description("Inspect and validate knowledge-kit plugins");
|
|
17413
|
+
registerStatusVerb(plugin, "status", "List discovered plugins grouped by kind with a per-plugin health mark", false);
|
|
17414
|
+
registerStatusVerb(plugin, "list", "Alias for `plugin status` (deprecated)", true);
|
|
17415
|
+
registerStatusVerb(plugin, "doctor", "Alias for `plugin status` (deprecated)", true);
|
|
17416
|
+
const plugins = program2.command("plugins", { hidden: true }).description("Alias for `plugin status` (deprecated)");
|
|
17417
|
+
registerStatusVerb(plugins, "status", "Alias for `plugin status` (deprecated)", true);
|
|
17418
|
+
registerStatusVerb(plugins, "list", "Alias for `plugin status` (deprecated)", true);
|
|
17419
|
+
registerStatusVerb(plugins, "doctor", "Alias for `plugin status` (deprecated)", true);
|
|
17270
17420
|
}
|
|
17271
17421
|
|
|
17272
17422
|
// src/config.ts
|
|
17273
17423
|
import { readFileSync as readFileSync5 } from "fs";
|
|
17274
|
-
import { fileURLToPath } from "url";
|
|
17275
|
-
var pkgPath =
|
|
17424
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17425
|
+
var pkgPath = fileURLToPath2(new URL("../package.json", import.meta.url));
|
|
17276
17426
|
var pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
17277
17427
|
var CLI_CONFIG = {
|
|
17278
17428
|
binaryName: "kk",
|
|
@@ -17282,11 +17432,8 @@ var CLI_CONFIG = {
|
|
|
17282
17432
|
// src/cli.ts
|
|
17283
17433
|
function createProgram() {
|
|
17284
17434
|
const program2 = new Command().name(CLI_CONFIG.binaryName).description("knowledge-kit CLI").version(CLI_CONFIG.binaryVersion).option("-v, --verbose", "enable verbose diagnostics");
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
registerFaninCommand(program2);
|
|
17288
|
-
registerFanoutCommand(program2);
|
|
17289
|
-
registerPluginsCommand(program2);
|
|
17435
|
+
registerExecutorCommand(program2);
|
|
17436
|
+
registerPluginCommand(program2);
|
|
17290
17437
|
return program2;
|
|
17291
17438
|
}
|
|
17292
17439
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { type Doc, DocListSchema } from '@gobing-ai/kk-core';
|
|
6
|
+
|
|
7
|
+
/** Validated `--in` input per the 0054 contract (`topic` required; `maxResults`/`fixture` optional). */
|
|
8
|
+
interface InInput {
|
|
9
|
+
topic: string;
|
|
10
|
+
maxResults: number;
|
|
11
|
+
fixture: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Canned web-search results — the v1 fixture stub. Stable sourceUris → stable Doc ids. */
|
|
15
|
+
interface FixtureEntry {
|
|
16
|
+
sourceUri: string;
|
|
17
|
+
title: string;
|
|
18
|
+
body: string;
|
|
19
|
+
position: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const FIXTURE_ENTRIES: FixtureEntry[] = [
|
|
23
|
+
{
|
|
24
|
+
sourceUri: 'https://example.org/research/storm-overview',
|
|
25
|
+
title: 'STORM: Topic Research via Composed Plugins — Overview',
|
|
26
|
+
body: '# STORM Overview\n\nSTORM chains ingestion and generation plugins over a single research topic.\n\n- **Ingest**: a web-search plugin materializes a `topic` into `Doc[]`\n- **Generate**: a generator plugin (content-gen) turns the `Doc[]` into grounded Content\n\nThis is fixture content emitted by the `web-search` v1 stub; no live Firecrawl call is made.',
|
|
27
|
+
position: 1,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
sourceUri: 'https://example.org/research/storm-contract',
|
|
31
|
+
title: 'The web-search Ingest Contract (0054)',
|
|
32
|
+
body: '# Ingest Contract\n\nThe web-search `--in` JSON carries `topic` (required, non-empty string), `maxResults` (optional positive integer, default 8), and `fixture` (optional boolean, reserved for the stub).\n\nDoc `id` is the sha256 hex prefix (16 chars) of the canonical `sourceUri`, keeping ids deterministic across re-runs.',
|
|
33
|
+
position: 2,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
sourceUri: 'https://example.org/research/storm-dry-run',
|
|
37
|
+
title: 'Dry-Running kk-storm-research',
|
|
38
|
+
body: '# Dry-Run\n\nThe `kk-storm-research` state machine materializes the topic `--vars` into the ingest `--in` JSON, runs `web-search`, then `content-gen`, and terminates at the Content-written `done` state — no publish.',
|
|
39
|
+
position: 3,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/** Deterministic Doc id: sha256(sourceUri) hex prefix, same idea as karakeep-local (0054 Block 4). */
|
|
44
|
+
function computeId(uri: string): string {
|
|
45
|
+
return createHash('sha256').update(uri).digest('hex').slice(0, 16);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build the fixture `Doc[]` for a topic (0054 Block 4 mapping). Always emits
|
|
50
|
+
* canned docs — the v1 plugin has no live Firecrawl path.
|
|
51
|
+
*/
|
|
52
|
+
export function buildFixtureDocs(topic: string, maxResults: number): Doc[] {
|
|
53
|
+
// maxResults caps the list per 0054 (cap semantics); `maxResults: 1` deliberately yields a
|
|
54
|
+
// single Doc. The Design's "always ≥2 items" wording applies to the default path (maxResults ≥ 2 → 3 canned entries).
|
|
55
|
+
const entries = FIXTURE_ENTRIES.slice(0, Math.max(0, Math.min(FIXTURE_ENTRIES.length, maxResults)));
|
|
56
|
+
return entries.map((entry, rank) => ({
|
|
57
|
+
id: computeId(entry.sourceUri),
|
|
58
|
+
body: entry.body,
|
|
59
|
+
title: entry.title,
|
|
60
|
+
sourceUri: entry.sourceUri,
|
|
61
|
+
mediaType: 'text/markdown',
|
|
62
|
+
metadata: {
|
|
63
|
+
query: topic,
|
|
64
|
+
rank,
|
|
65
|
+
position: entry.position,
|
|
66
|
+
// Per-run timestamp by design (0054 Block 4): determinism is required only for `id`.
|
|
67
|
+
scraped_at: new Date().toISOString(),
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Parse and validate the `--in` JSON; missing/invalid topic → throws (fail-loud row 1). */
|
|
73
|
+
export function parseInput(raw: string): InInput {
|
|
74
|
+
let data: unknown;
|
|
75
|
+
try {
|
|
76
|
+
data = JSON.parse(raw);
|
|
77
|
+
} catch {
|
|
78
|
+
throw new Error('--in is not valid JSON');
|
|
79
|
+
}
|
|
80
|
+
if (typeof data !== 'object' || data === null) {
|
|
81
|
+
throw new Error('--in must be a JSON object');
|
|
82
|
+
}
|
|
83
|
+
const input = data as Record<string, unknown>;
|
|
84
|
+
if (typeof input.topic !== 'string' || input.topic.trim() === '') {
|
|
85
|
+
throw new Error('--in "topic" is required and must be a non-empty string');
|
|
86
|
+
}
|
|
87
|
+
let maxResults = 8;
|
|
88
|
+
if (input.maxResults !== undefined) {
|
|
89
|
+
if (typeof input.maxResults !== 'number' || !Number.isInteger(input.maxResults) || input.maxResults <= 0) {
|
|
90
|
+
throw new Error('--in "maxResults" must be a positive integer when present');
|
|
91
|
+
}
|
|
92
|
+
maxResults = input.maxResults;
|
|
93
|
+
}
|
|
94
|
+
return { topic: input.topic, maxResults, fixture: input.fixture === true };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function main(): Promise<number> {
|
|
98
|
+
const { values } = parseArgs({
|
|
99
|
+
options: {
|
|
100
|
+
in: { type: 'string' },
|
|
101
|
+
out: { type: 'string' },
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!values.in || !values.out) {
|
|
106
|
+
console.error('Error: Missing required arguments --in and --out');
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let input: InInput;
|
|
111
|
+
try {
|
|
112
|
+
input = parseInput(await readFile(values.in, 'utf-8'));
|
|
113
|
+
} catch (err: unknown) {
|
|
114
|
+
console.error(`web-search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!input.fixture) {
|
|
119
|
+
// 0054 Block 1/Block 8: `fixture` absent or false selects the LIVE Firecrawl path; v1 has no
|
|
120
|
+
// live client. Fail loud (Block 5 row-2 semantics: exit 1, no --out) instead of silently
|
|
121
|
+
// fabricating research docs — an obvious failure beats a surprising recovery.
|
|
122
|
+
console.error(
|
|
123
|
+
'web-search failed: live Firecrawl not implemented in v1 (pass "fixture": true to use the fixture stub)',
|
|
124
|
+
);
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const docs = DocListSchema.parse(buildFixtureDocs(input.topic, input.maxResults));
|
|
130
|
+
const outDir = dirname(values.out);
|
|
131
|
+
if (outDir && outDir !== '.') {
|
|
132
|
+
await mkdir(outDir, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
await writeFile(values.out, JSON.stringify(docs, null, 2), 'utf-8');
|
|
135
|
+
return 0;
|
|
136
|
+
} catch (err: unknown) {
|
|
137
|
+
console.error(`web-search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (import.meta.main) {
|
|
143
|
+
process.exit(await main());
|
|
144
|
+
}
|
|
@@ -60,7 +60,7 @@ Every evaluation writes a JSON verdict file. The default path is
|
|
|
60
60
|
## Evaluation procedure
|
|
61
61
|
|
|
62
62
|
1. **Read the Content** from the path given in the evaluation request (the `content.json` written
|
|
63
|
-
by the preceding `kk
|
|
63
|
+
by the preceding `kk executor run <generator>` step).
|
|
64
64
|
2. **Select the rubric** — either named in the request (`tech-accuracy`, `brand-tone`,
|
|
65
65
|
`compliance`, `general`) or loaded by a thin wrapper. Default: `general`.
|
|
66
66
|
3. **Evaluate each criterion** in the rubric. For each, produce a `pass` / `fail` / `warn` status
|
|
@@ -31,7 +31,7 @@ Research, illustration, platform adaptation, and publishing are deliberately **o
|
|
|
31
31
|
|
|
32
32
|
Do **not** use this skill for: materials extraction or research, image generation, platform
|
|
33
33
|
adaptation, publishing, style-profile infrastructure, or anything requiring a runtime
|
|
34
|
-
plugin (`kk
|
|
34
|
+
plugin (`kk executor run`, `plugin.json`, `GeneratorInput → Content`).
|
|
35
35
|
|
|
36
36
|
## Invocation contract
|
|
37
37
|
|