@wrongstack/core 0.308.5 → 0.308.7
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/coordination/agents/index.js +1 -0
- package/dist/coordination/agents/role-skills.d.ts +1 -0
- package/dist/coordination/explore-companion.d.ts +191 -0
- package/dist/coordination/fleet.d.ts +14 -0
- package/dist/coordination/index.d.ts +1 -0
- package/dist/coordination/index.js +637 -268
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/defaults/index.js +32 -1
- package/dist/execution/compaction-core.d.ts +1 -1
- package/dist/execution/compaction-elision.d.ts +0 -10
- package/dist/execution/index.js +31 -0
- package/dist/goal/index.js +54 -27
- package/dist/goal/phase-orchestrator.d.ts +7 -0
- package/dist/goal/types.d.ts +1 -1
- package/dist/index.js +495 -100
- package/dist/plugin/discovery.d.ts +73 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.js +270 -29
- package/dist/plugin/loader.d.ts +5 -1
- package/dist/plugin/trust.d.ts +78 -0
- package/dist/tools/index.js +1 -0
- package/dist/types/config/mcp-features.d.ts +21 -0
- package/dist/types/config/skills-fleet-brain.d.ts +18 -0
- package/instructions/agents/explore-companion.md +35 -0
- package/package.json +4 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External plugin discovery — filesystem convention for third-party plugins.
|
|
3
|
+
*
|
|
4
|
+
* Two roots are scanned (when present):
|
|
5
|
+
* - `~/.wrongstack/plugins/` — user-global plugins
|
|
6
|
+
* - `<projectRoot>/.wrongstack/plugins/` — project-local plugins
|
|
7
|
+
*
|
|
8
|
+
* Each immediate child of a root is a candidate:
|
|
9
|
+
* - a directory named `<plugin-name>/` whose entry is resolved from
|
|
10
|
+
* `package.json` (`main` / `exports["."]`) or falls back to
|
|
11
|
+
* `index.js` / `index.mjs` / `index.cjs`,
|
|
12
|
+
* - or a single entry file (`<plugin-name>.js` / `.mjs` / `.cjs`).
|
|
13
|
+
*
|
|
14
|
+
* `node_modules` and dot-entries are skipped: packages installed by
|
|
15
|
+
* `wstack plugin add --install` are loaded through their explicit
|
|
16
|
+
* `path` config entry, not through directory discovery, so they are
|
|
17
|
+
* never double-loaded. Scan order is alphabetical per root for
|
|
18
|
+
* deterministic load order; roots are scanned in the order given.
|
|
19
|
+
*
|
|
20
|
+
* Discovery is read-only and never imports plugin code — resolving an
|
|
21
|
+
* entry does not execute it. The host decides what to do with the
|
|
22
|
+
* candidates (enablement, trust pinning, import).
|
|
23
|
+
*/
|
|
24
|
+
import type { Dirent } from 'node:fs';
|
|
25
|
+
export interface ExternalPluginCandidate {
|
|
26
|
+
/** Candidate name — the directory or file basename without extension. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Absolute path of the resolved JavaScript entry file. */
|
|
29
|
+
entryPath: string;
|
|
30
|
+
/** Discovery root the candidate was found under. */
|
|
31
|
+
root: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SkippedPluginCandidate {
|
|
34
|
+
name: string;
|
|
35
|
+
root: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
}
|
|
38
|
+
export interface PluginDiscoveryResult {
|
|
39
|
+
candidates: ExternalPluginCandidate[];
|
|
40
|
+
/** Candidates that were found but could not be resolved to an entry. */
|
|
41
|
+
skipped: SkippedPluginCandidate[];
|
|
42
|
+
}
|
|
43
|
+
/** Injectable filesystem access so discovery is unit-testable. */
|
|
44
|
+
export interface DiscoveryIo {
|
|
45
|
+
readdir(root: string): Promise<Dirent[]>;
|
|
46
|
+
stat(path: string): Promise<{
|
|
47
|
+
isFile(): boolean;
|
|
48
|
+
isDirectory(): boolean;
|
|
49
|
+
}>;
|
|
50
|
+
readFile(path: string): Promise<string>;
|
|
51
|
+
}
|
|
52
|
+
export declare const DEFAULT_PLUGIN_DISCOVERY_IO: DiscoveryIo;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the JavaScript entry file for a plugin directory: `package.json`
|
|
55
|
+
* `main`/`exports["."]` first, then `index.js`/`index.mjs`/`index.cjs`
|
|
56
|
+
* probing. Returns null when no entry can be resolved. Shared by directory
|
|
57
|
+
* discovery and explicit `config.plugins[].path` resolution. Paths are
|
|
58
|
+
* returned with forward separators on every platform so trust pin keys and
|
|
59
|
+
* import targets stay canonical.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolvePluginEntryPath(dir: string, io?: DiscoveryIo): Promise<string | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a configured target (entry file OR directory) to its entry file.
|
|
64
|
+
* Returns the input (forward-slash normalized) when it is already a file;
|
|
65
|
+
* null when it is a directory without a resolvable entry or does not exist.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolvePluginTarget(target: string, io?: DiscoveryIo): Promise<string | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Scan discovery roots for external plugin candidates. Missing roots are
|
|
70
|
+
* not an error — they simply contribute no candidates.
|
|
71
|
+
*/
|
|
72
|
+
export declare function discoverExternalPlugins(roots: readonly string[], io?: DiscoveryIo): Promise<PluginDiscoveryResult>;
|
|
73
|
+
//# sourceMappingURL=discovery.d.ts.map
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { DefaultPluginAPI, definePlugin, type PluginAPIInit } from './api.js';
|
|
2
|
+
export { DEFAULT_PLUGIN_DISCOVERY_IO, discoverExternalPlugins, type DiscoveryIo, type ExternalPluginCandidate, type PluginDiscoveryResult, resolvePluginEntryPath, resolvePluginTarget, type SkippedPluginCandidate, } from './discovery.js';
|
|
2
3
|
export { diffPluginConfig, type PluginConfigChange, type PluginConfigSource, type PluginEnablementSource, pluginEntryMatchesName, type ResolvePluginConfigInput, type ResolvePluginEnablementInput, type ResolvedPluginConfig, type ResolvedPluginEnablement, redactPluginConfig, resolvePluginConfig, resolvePluginEnablement, resolvePluginManifestConfig, validatePluginConfigMetadata, } from './config.js';
|
|
3
4
|
export { KERNEL_API_VERSION, loadPlugins, type LoadPluginsOptions, type PluginHostHandle, type PluginLoadFailure, unloadPlugins, } from './loader.js';
|
|
5
|
+
export { defaultPluginTrustPath, hashFileContents, normalizeTrustKey, type PluginTrustEntry, type PluginTrustStore, pinPluginTrust, readPluginTrustStore, unpinPluginTrust, verifyPluginTrust, type PluginTrustVerification, writePluginTrustStore, } from './trust.js';
|
|
4
6
|
export type { PluginAPI } from '../types/plugin.js';
|
|
5
7
|
export { buildReviewerModelPool, createAutoReviewPlugin, parseReviewSeverity, type ReviewerModelAssignment, selectRoundRobinReviewerAssignment, } from '../plugins/auto-review-plugin.js';
|
|
6
8
|
export { type CascadeAgentKind, type CascadeEvidenceCheckResult, type CascadeEvidenceStatus, CHIMERA_REVIEW_PROMPT, createChimeraPlugin, type ChimeraCascadeNeededPayload, type ChimeraReviewCompletePayload, type ChimeraReviewNeededPayload, type ReviewContextBundle, } from '../plugins/chimera-plugin.js';
|
package/dist/plugin/index.js
CHANGED
|
@@ -217,7 +217,7 @@ var init_atomic_write = __esm({
|
|
|
217
217
|
});
|
|
218
218
|
|
|
219
219
|
// src/plugins/review-finding-types.ts
|
|
220
|
-
import { createHash as
|
|
220
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
221
221
|
function normalizeFingerprintTitle(title) {
|
|
222
222
|
return title.trim().toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
|
|
223
223
|
}
|
|
@@ -225,7 +225,7 @@ function computeFindingFingerprint(file, line, title) {
|
|
|
225
225
|
const normalizedTitle = normalizeFingerprintTitle(title);
|
|
226
226
|
const normalizedFile = file.replace(/\\/g, "/").trim().toLowerCase();
|
|
227
227
|
const lineStr = line != null && line >= 0 ? String(line) : "0";
|
|
228
|
-
const hash =
|
|
228
|
+
const hash = createHash9("sha256");
|
|
229
229
|
hash.update(`${normalizedFile}:${lineStr}:${normalizedTitle}`);
|
|
230
230
|
return hash.digest("hex");
|
|
231
231
|
}
|
|
@@ -1664,7 +1664,8 @@ async function loadPlugins(plugins, opts) {
|
|
|
1664
1664
|
plugin,
|
|
1665
1665
|
resolution.options
|
|
1666
1666
|
);
|
|
1667
|
-
const
|
|
1667
|
+
const enforceForPlugin = typeof opts.enforceCapabilities === "function" ? opts.enforceCapabilities(plugin) : opts.enforceCapabilities ?? false;
|
|
1668
|
+
const api = plugin.capabilities ? wrapApiForCapabilityCheck(plugin, rawApi, opts.log, enforceForPlugin) : rawApi;
|
|
1668
1669
|
registration = {
|
|
1669
1670
|
plugin,
|
|
1670
1671
|
api,
|
|
@@ -2241,6 +2242,234 @@ function definePlugin(metadata, factory) {
|
|
|
2241
2242
|
};
|
|
2242
2243
|
}
|
|
2243
2244
|
|
|
2245
|
+
// src/plugin/discovery.ts
|
|
2246
|
+
var DEFAULT_PLUGIN_DISCOVERY_IO = {
|
|
2247
|
+
async readdir(root) {
|
|
2248
|
+
const { readdir: readdir8 } = await import("node:fs/promises");
|
|
2249
|
+
return readdir8(root, { withFileTypes: true });
|
|
2250
|
+
},
|
|
2251
|
+
async stat(path35) {
|
|
2252
|
+
const { stat: stat9 } = await import("node:fs/promises");
|
|
2253
|
+
return stat9(path35);
|
|
2254
|
+
},
|
|
2255
|
+
async readFile(path35) {
|
|
2256
|
+
const { readFile: readFile22 } = await import("node:fs/promises");
|
|
2257
|
+
return readFile22(path35, "utf8");
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
var ENTRY_EXTENSIONS = [".js", ".mjs", ".cjs"];
|
|
2261
|
+
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set(["node_modules"]);
|
|
2262
|
+
function isDotEntry(name) {
|
|
2263
|
+
return name.startsWith(".");
|
|
2264
|
+
}
|
|
2265
|
+
function hasEntryExtension(name) {
|
|
2266
|
+
return ENTRY_EXTENSIONS.some((ext) => name.endsWith(ext));
|
|
2267
|
+
}
|
|
2268
|
+
async function isFile(io, path35) {
|
|
2269
|
+
try {
|
|
2270
|
+
return (await io.stat(path35)).isFile();
|
|
2271
|
+
} catch {
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
async function isDirectory(io, path35) {
|
|
2276
|
+
try {
|
|
2277
|
+
return (await io.stat(path35)).isDirectory();
|
|
2278
|
+
} catch {
|
|
2279
|
+
return false;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
async function resolvePackageEntry(io, dir) {
|
|
2283
|
+
const pkgPath = `${dir}/package.json`;
|
|
2284
|
+
if (!await isFile(io, pkgPath)) {
|
|
2285
|
+
return { entry: void 0, problem: void 0 };
|
|
2286
|
+
}
|
|
2287
|
+
let parsed;
|
|
2288
|
+
try {
|
|
2289
|
+
parsed = JSON.parse(await io.readFile(pkgPath));
|
|
2290
|
+
} catch (err) {
|
|
2291
|
+
return {
|
|
2292
|
+
entry: void 0,
|
|
2293
|
+
problem: `invalid package.json (${err instanceof Error ? err.message : String(err)})`
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
2297
|
+
return { entry: void 0, problem: "package.json is not an object" };
|
|
2298
|
+
}
|
|
2299
|
+
const pkg = parsed;
|
|
2300
|
+
const candidates = [];
|
|
2301
|
+
const dotExport = pkg.exports?.["."];
|
|
2302
|
+
if (typeof dotExport === "string") {
|
|
2303
|
+
candidates.push(dotExport);
|
|
2304
|
+
} else if (dotExport !== null && typeof dotExport === "object") {
|
|
2305
|
+
const conditions = dotExport;
|
|
2306
|
+
for (const key of ["import", "module", "default"]) {
|
|
2307
|
+
const value = conditions[key];
|
|
2308
|
+
if (typeof value === "string") candidates.push(value);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
2312
|
+
candidates.push(pkg.main);
|
|
2313
|
+
}
|
|
2314
|
+
for (const candidate of candidates) {
|
|
2315
|
+
const normalized = candidate.startsWith(".") ? candidate : `./${candidate}`;
|
|
2316
|
+
const base = `${dir}/${normalized.slice(2)}`;
|
|
2317
|
+
if (await isFile(io, base)) return { entry: base, problem: void 0 };
|
|
2318
|
+
for (const ext of ENTRY_EXTENSIONS) {
|
|
2319
|
+
if (await isFile(io, `${base}${ext}`)) {
|
|
2320
|
+
return { entry: `${base}${ext}`, problem: void 0 };
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return {
|
|
2325
|
+
entry: void 0,
|
|
2326
|
+
problem: 'package.json declares no resolvable entry (checked main, exports["."], index fallbacks)'
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
async function resolvePluginEntryPath(dir, io = DEFAULT_PLUGIN_DISCOVERY_IO) {
|
|
2330
|
+
const { entry: resolved } = await resolvePackageEntry(io, dir);
|
|
2331
|
+
if (resolved) return resolved;
|
|
2332
|
+
for (const ext of ENTRY_EXTENSIONS) {
|
|
2333
|
+
const candidate = `${dir}/index${ext}`;
|
|
2334
|
+
if (await isFile(io, candidate)) return candidate;
|
|
2335
|
+
}
|
|
2336
|
+
return null;
|
|
2337
|
+
}
|
|
2338
|
+
function canon(path35) {
|
|
2339
|
+
return path35.replaceAll("\\", "/");
|
|
2340
|
+
}
|
|
2341
|
+
async function resolvePluginTarget(target, io = DEFAULT_PLUGIN_DISCOVERY_IO) {
|
|
2342
|
+
if (await isFile(io, target)) return canon(target);
|
|
2343
|
+
if (await isDirectory(io, target)) return resolvePluginEntryPath(canon(target), io);
|
|
2344
|
+
return null;
|
|
2345
|
+
}
|
|
2346
|
+
async function discoverExternalPlugins(roots, io = DEFAULT_PLUGIN_DISCOVERY_IO) {
|
|
2347
|
+
const candidates = [];
|
|
2348
|
+
const skipped = [];
|
|
2349
|
+
for (const root of roots) {
|
|
2350
|
+
let entries;
|
|
2351
|
+
try {
|
|
2352
|
+
entries = await io.readdir(root);
|
|
2353
|
+
} catch {
|
|
2354
|
+
continue;
|
|
2355
|
+
}
|
|
2356
|
+
const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
|
|
2357
|
+
for (const entry of sorted) {
|
|
2358
|
+
if (isDotEntry(entry.name) || SKIP_DIR_NAMES.has(entry.name)) continue;
|
|
2359
|
+
const full = canon(`${root}/${entry.name}`);
|
|
2360
|
+
if (entry.isDirectory() || await isDirectory(io, full)) {
|
|
2361
|
+
const resolved = await resolvePluginEntryPath(full, io);
|
|
2362
|
+
if (resolved) {
|
|
2363
|
+
candidates.push({ name: entry.name, entryPath: resolved, root });
|
|
2364
|
+
} else {
|
|
2365
|
+
skipped.push({
|
|
2366
|
+
name: entry.name,
|
|
2367
|
+
root,
|
|
2368
|
+
reason: "no resolvable entry (package.json main/exports or index fallbacks)"
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
} else if (entry.isFile() && hasEntryExtension(entry.name)) {
|
|
2372
|
+
candidates.push({
|
|
2373
|
+
name: entry.name.slice(0, entry.name.length - extOf(entry.name).length),
|
|
2374
|
+
entryPath: full,
|
|
2375
|
+
root
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
return { candidates, skipped };
|
|
2381
|
+
}
|
|
2382
|
+
function extOf(name) {
|
|
2383
|
+
const index = name.lastIndexOf(".");
|
|
2384
|
+
return index === -1 ? "" : name.slice(index);
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
// src/plugin/trust.ts
|
|
2388
|
+
init_errors();
|
|
2389
|
+
import { createHash } from "node:crypto";
|
|
2390
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2391
|
+
import { dirname, join } from "node:path";
|
|
2392
|
+
function defaultPluginTrustPath(globalRoot) {
|
|
2393
|
+
return join(globalRoot, "plugin-trust.json");
|
|
2394
|
+
}
|
|
2395
|
+
function normalizeTrustKey(path35) {
|
|
2396
|
+
return path35.replaceAll("\\", "/");
|
|
2397
|
+
}
|
|
2398
|
+
async function hashFileContents(entryPath, readFileFn = defaultReadFile) {
|
|
2399
|
+
const contents = await readFileFn(entryPath);
|
|
2400
|
+
return `sha256-${createHash("sha256").update(contents).digest("hex")}`;
|
|
2401
|
+
}
|
|
2402
|
+
async function defaultReadFile(path35) {
|
|
2403
|
+
return readFile(path35);
|
|
2404
|
+
}
|
|
2405
|
+
async function readPluginTrustStore(storePath, readFileFn = (path35) => readFile(path35, "utf8")) {
|
|
2406
|
+
let raw;
|
|
2407
|
+
try {
|
|
2408
|
+
raw = await readFileFn(storePath);
|
|
2409
|
+
} catch {
|
|
2410
|
+
return { pinned: {} };
|
|
2411
|
+
}
|
|
2412
|
+
let parsed;
|
|
2413
|
+
try {
|
|
2414
|
+
parsed = JSON.parse(raw);
|
|
2415
|
+
} catch (err) {
|
|
2416
|
+
throw new FsError({
|
|
2417
|
+
message: `Plugin trust store "${storePath}" is not valid JSON \u2014 fix or delete the file before loading external plugins (${err instanceof Error ? err.message : String(err)})`,
|
|
2418
|
+
code: ERROR_CODES.FS_READ_FAILED,
|
|
2419
|
+
path: storePath
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
2423
|
+
throw new FsError({
|
|
2424
|
+
message: `Plugin trust store "${storePath}" has an unexpected shape (expected { pinned: {...} }) \u2014 fix or delete the file`,
|
|
2425
|
+
code: ERROR_CODES.FS_READ_FAILED,
|
|
2426
|
+
path: storePath
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
const pinned = parsed.pinned;
|
|
2430
|
+
if (pinned === void 0) return { pinned: {} };
|
|
2431
|
+
if (pinned === null || typeof pinned !== "object" || Array.isArray(pinned)) {
|
|
2432
|
+
throw new FsError({
|
|
2433
|
+
message: `Plugin trust store "${storePath}" has an invalid "pinned" section \u2014 fix or delete the file`,
|
|
2434
|
+
code: ERROR_CODES.FS_READ_FAILED,
|
|
2435
|
+
path: storePath
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
return { pinned };
|
|
2439
|
+
}
|
|
2440
|
+
async function writePluginTrustStore(storePath, store) {
|
|
2441
|
+
await mkdir(dirname(storePath), { recursive: true });
|
|
2442
|
+
const tmp = `${storePath}.tmp`;
|
|
2443
|
+
await writeFile(tmp, `${JSON.stringify(store, null, 2)}
|
|
2444
|
+
`, { mode: 384 });
|
|
2445
|
+
await rename(tmp, storePath);
|
|
2446
|
+
}
|
|
2447
|
+
function verifyPluginTrust(name, integrity, store) {
|
|
2448
|
+
const pinned = store.pinned[name];
|
|
2449
|
+
if (!pinned) return { status: "unpinned", integrity };
|
|
2450
|
+
if (pinned.integrity === integrity) return { status: "trusted", integrity };
|
|
2451
|
+
return {
|
|
2452
|
+
status: "changed",
|
|
2453
|
+
expected: pinned.integrity,
|
|
2454
|
+
actual: integrity,
|
|
2455
|
+
pinnedAt: pinned.pinnedAt
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
async function pinPluginTrust(storePath, name, entry, integrity, spec) {
|
|
2459
|
+
const store = await readPluginTrustStore(storePath);
|
|
2460
|
+
store.pinned[name] = { entry, integrity, pinnedAt: (/* @__PURE__ */ new Date()).toISOString(), spec };
|
|
2461
|
+
await writePluginTrustStore(storePath, store);
|
|
2462
|
+
return store;
|
|
2463
|
+
}
|
|
2464
|
+
async function unpinPluginTrust(storePath, name) {
|
|
2465
|
+
const store = await readPluginTrustStore(storePath);
|
|
2466
|
+
if (store.pinned[name] !== void 0) {
|
|
2467
|
+
delete store.pinned[name];
|
|
2468
|
+
await writePluginTrustStore(storePath, store);
|
|
2469
|
+
}
|
|
2470
|
+
return store;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2244
2473
|
// src/plugins/auto-review-plugin.ts
|
|
2245
2474
|
init_error();
|
|
2246
2475
|
import * as fsp3 from "node:fs/promises";
|
|
@@ -2899,7 +3128,7 @@ function shouldCascade(cascadeOn, severities) {
|
|
|
2899
3128
|
|
|
2900
3129
|
// src/plugins/auto-review-git.ts
|
|
2901
3130
|
import { spawn } from "node:child_process";
|
|
2902
|
-
import { createHash } from "node:crypto";
|
|
3131
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2903
3132
|
import * as fsp from "node:fs/promises";
|
|
2904
3133
|
import * as path from "node:path";
|
|
2905
3134
|
var MAX_SNAPSHOT_FILE_BYTES = 256 * 1024;
|
|
@@ -2966,7 +3195,7 @@ async function snapshotChangedFiles(cwd) {
|
|
|
2966
3195
|
snapshots.push({
|
|
2967
3196
|
...file,
|
|
2968
3197
|
content,
|
|
2969
|
-
fingerprint:
|
|
3198
|
+
fingerprint: createHash2("sha256").update(content).digest("hex")
|
|
2970
3199
|
});
|
|
2971
3200
|
} catch {
|
|
2972
3201
|
}
|
|
@@ -2975,7 +3204,7 @@ async function snapshotChangedFiles(cwd) {
|
|
|
2975
3204
|
}
|
|
2976
3205
|
|
|
2977
3206
|
// src/plugins/review-claim-registry.ts
|
|
2978
|
-
import { createHash as
|
|
3207
|
+
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
2979
3208
|
import * as fsp2 from "node:fs/promises";
|
|
2980
3209
|
import { hostname } from "node:os";
|
|
2981
3210
|
import * as path2 from "node:path";
|
|
@@ -3119,7 +3348,7 @@ var claimsByEventBus = /* @__PURE__ */ new WeakMap();
|
|
|
3119
3348
|
var startedReviews = /* @__PURE__ */ new WeakMap();
|
|
3120
3349
|
var pendingStartedReviews = /* @__PURE__ */ new WeakMap();
|
|
3121
3350
|
function fingerprint(content) {
|
|
3122
|
-
return
|
|
3351
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
3123
3352
|
}
|
|
3124
3353
|
function normalizeKeyPart(p) {
|
|
3125
3354
|
const forward = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
@@ -3474,7 +3703,7 @@ import * as os4 from "node:os";
|
|
|
3474
3703
|
import * as path16 from "node:path";
|
|
3475
3704
|
|
|
3476
3705
|
// src/utils/wstack-paths.ts
|
|
3477
|
-
import { createHash as
|
|
3706
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3478
3707
|
import * as fs from "node:fs";
|
|
3479
3708
|
import * as os from "node:os";
|
|
3480
3709
|
import * as path3 from "node:path";
|
|
@@ -3500,12 +3729,12 @@ function canonicalProjectRoot(absRoot) {
|
|
|
3500
3729
|
}
|
|
3501
3730
|
}
|
|
3502
3731
|
function projectHash(absRoot) {
|
|
3503
|
-
return
|
|
3732
|
+
return createHash4("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
|
|
3504
3733
|
}
|
|
3505
3734
|
function projectSlug(absRoot) {
|
|
3506
3735
|
const identityRoot = canonicalProjectRoot(absRoot);
|
|
3507
3736
|
const base = slugify(path3.basename(identityRoot));
|
|
3508
|
-
const hash =
|
|
3737
|
+
const hash = createHash4("sha256").update(identityRoot).digest("hex").slice(0, 6);
|
|
3509
3738
|
return `${base}-${hash}`;
|
|
3510
3739
|
}
|
|
3511
3740
|
function slugify(name) {
|
|
@@ -3613,7 +3842,7 @@ function resolveWstackPaths(opts) {
|
|
|
3613
3842
|
}
|
|
3614
3843
|
|
|
3615
3844
|
// src/chronicle/identity.ts
|
|
3616
|
-
import { createHash as
|
|
3845
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
3617
3846
|
import * as os2 from "node:os";
|
|
3618
3847
|
import * as path4 from "node:path";
|
|
3619
3848
|
function resolveChronicleRuntimeLocation(input) {
|
|
@@ -3627,7 +3856,7 @@ function resolveChronicleRuntimeLocation(input) {
|
|
|
3627
3856
|
};
|
|
3628
3857
|
}
|
|
3629
3858
|
function stableId(prefix, value) {
|
|
3630
|
-
return `${prefix}_${
|
|
3859
|
+
return `${prefix}_${createHash5("sha256").update(value).digest("hex").slice(0, 24)}`;
|
|
3631
3860
|
}
|
|
3632
3861
|
|
|
3633
3862
|
// src/chronicle/journal.ts
|
|
@@ -3640,7 +3869,7 @@ import * as path6 from "node:path";
|
|
|
3640
3869
|
import { createInterface } from "node:readline";
|
|
3641
3870
|
|
|
3642
3871
|
// src/chronicle/event-hash.ts
|
|
3643
|
-
import { createHash as
|
|
3872
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3644
3873
|
var GENESIS_HASH = "0".repeat(64);
|
|
3645
3874
|
function stableStringify(value) {
|
|
3646
3875
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -3651,7 +3880,7 @@ function stableStringify(value) {
|
|
|
3651
3880
|
return `{${Object.keys(obj).sort().filter((key) => obj[key] !== void 0).map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`).join(",")}}`;
|
|
3652
3881
|
}
|
|
3653
3882
|
function hashValue(value) {
|
|
3654
|
-
return
|
|
3883
|
+
return createHash6("sha256").update(stableStringify(value), "utf8").digest("hex");
|
|
3655
3884
|
}
|
|
3656
3885
|
function chronicleEventHash(event) {
|
|
3657
3886
|
const { hash: _hash, ...unhashed } = event;
|
|
@@ -4278,7 +4507,7 @@ import * as fs7 from "node:fs/promises";
|
|
|
4278
4507
|
import * as path11 from "node:path";
|
|
4279
4508
|
|
|
4280
4509
|
// src/chronicle/query.ts
|
|
4281
|
-
import { createHash as
|
|
4510
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
4282
4511
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
4283
4512
|
import * as fs4 from "node:fs/promises";
|
|
4284
4513
|
import * as path8 from "node:path";
|
|
@@ -4937,7 +5166,7 @@ function orderKey(event) {
|
|
|
4937
5166
|
}
|
|
4938
5167
|
function hashQuery(query) {
|
|
4939
5168
|
const { cursor: _cursor, limit: _limit, order: _order, ...filters } = query;
|
|
4940
|
-
return
|
|
5169
|
+
return createHash7("sha256").update(stableStringify2(filters), "utf8").digest("base64url");
|
|
4941
5170
|
}
|
|
4942
5171
|
function encodeCursor(cursor) {
|
|
4943
5172
|
return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
|
|
@@ -4996,7 +5225,7 @@ async function resolveSnapshotFiles(files, snapshot) {
|
|
|
4996
5225
|
}));
|
|
4997
5226
|
}
|
|
4998
5227
|
function fileId(file) {
|
|
4999
|
-
return
|
|
5228
|
+
return createHash7("sha256").update(path8.resolve(file), "utf8").digest("base64url");
|
|
5000
5229
|
}
|
|
5001
5230
|
function stableStringify2(value) {
|
|
5002
5231
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -5533,10 +5762,10 @@ function instructionRootCandidates() {
|
|
|
5533
5762
|
path9.resolve(here, "../instructions"),
|
|
5534
5763
|
path9.resolve(here, "instructions")
|
|
5535
5764
|
];
|
|
5536
|
-
rootCandidates = candidates.sort((a, b) => Number(!
|
|
5765
|
+
rootCandidates = candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
|
|
5537
5766
|
return rootCandidates;
|
|
5538
5767
|
}
|
|
5539
|
-
function
|
|
5768
|
+
function isDirectory2(candidate) {
|
|
5540
5769
|
try {
|
|
5541
5770
|
return statSync3(candidate).isDirectory();
|
|
5542
5771
|
} catch {
|
|
@@ -7400,7 +7629,7 @@ import * as path15 from "node:path";
|
|
|
7400
7629
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7401
7630
|
|
|
7402
7631
|
// src/chronicle/project-server-endpoint.ts
|
|
7403
|
-
import { createHash as
|
|
7632
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
7404
7633
|
import * as os3 from "node:os";
|
|
7405
7634
|
import * as path14 from "node:path";
|
|
7406
7635
|
|
|
@@ -7419,7 +7648,7 @@ function normalizedPath(value) {
|
|
|
7419
7648
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
7420
7649
|
}
|
|
7421
7650
|
function chronicleProjectServerKey(projectDir) {
|
|
7422
|
-
return
|
|
7651
|
+
return createHash8("sha256").update(normalizedPath(path14.join(projectDir, "chronicle"))).digest("hex").slice(0, 24);
|
|
7423
7652
|
}
|
|
7424
7653
|
function chronicleProjectServerEndpoint(projectDir) {
|
|
7425
7654
|
const key = chronicleProjectServerKey(projectDir);
|
|
@@ -10925,7 +11154,7 @@ function resolveFindingPath(raw, cwd) {
|
|
|
10925
11154
|
async function verifyFindingsAgainstDisk(findings, opts) {
|
|
10926
11155
|
const window = opts.anchorWindow ?? DEFAULT_ANCHOR_WINDOW;
|
|
10927
11156
|
const cache = /* @__PURE__ */ new Map();
|
|
10928
|
-
const
|
|
11157
|
+
const readFile22 = async (abs) => {
|
|
10929
11158
|
const cached = cache.get(abs);
|
|
10930
11159
|
if (cached) return cached;
|
|
10931
11160
|
let result;
|
|
@@ -10947,7 +11176,7 @@ async function verifyFindingsAgainstDisk(findings, opts) {
|
|
|
10947
11176
|
if (abs === null) {
|
|
10948
11177
|
return { ...finding, verification: { status: "failed", reason: "outside_workspace" } };
|
|
10949
11178
|
}
|
|
10950
|
-
const file = await
|
|
11179
|
+
const file = await readFile22(abs);
|
|
10951
11180
|
if ("error" in file) {
|
|
10952
11181
|
return {
|
|
10953
11182
|
...finding,
|
|
@@ -12848,7 +13077,7 @@ init_atomic_write();
|
|
|
12848
13077
|
init_errors();
|
|
12849
13078
|
import * as fs18 from "node:fs/promises";
|
|
12850
13079
|
import * as path31 from "node:path";
|
|
12851
|
-
import { createHash as
|
|
13080
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
12852
13081
|
var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
|
|
12853
13082
|
var CloudSync = class {
|
|
12854
13083
|
constructor(paths, getConfig, setConfig, getSettingsConfigPath) {
|
|
@@ -13154,7 +13383,7 @@ var CloudSync = class {
|
|
|
13154
13383
|
} catch {
|
|
13155
13384
|
}
|
|
13156
13385
|
}
|
|
13157
|
-
const rev =
|
|
13386
|
+
const rev = createHash10("sha256").update(hashes.join("")).digest("hex").slice(0, 12);
|
|
13158
13387
|
return { treeEntries: entries, rev };
|
|
13159
13388
|
}
|
|
13160
13389
|
async hashLocalCategories(categories) {
|
|
@@ -13179,7 +13408,7 @@ var CloudSync = class {
|
|
|
13179
13408
|
} catch {
|
|
13180
13409
|
}
|
|
13181
13410
|
}
|
|
13182
|
-
return
|
|
13411
|
+
return createHash10("sha256").update(hashes.join("")).digest("hex").slice(0, 12);
|
|
13183
13412
|
}
|
|
13184
13413
|
categoryToPath(cat) {
|
|
13185
13414
|
switch (cat) {
|
|
@@ -13570,7 +13799,7 @@ function isSecretField(name) {
|
|
|
13570
13799
|
|
|
13571
13800
|
// src/storage/cloud-config-sync.ts
|
|
13572
13801
|
init_atomic_write();
|
|
13573
|
-
import { createHash as
|
|
13802
|
+
import { createHash as createHash11, randomUUID as nodeRandomUUID } from "node:crypto";
|
|
13574
13803
|
import * as fs19 from "node:fs/promises";
|
|
13575
13804
|
import * as path33 from "node:path";
|
|
13576
13805
|
|
|
@@ -14031,7 +14260,7 @@ function stableStringify3(value) {
|
|
|
14031
14260
|
return JSON.stringify(value) ?? "null";
|
|
14032
14261
|
}
|
|
14033
14262
|
function hashPayload(payload) {
|
|
14034
|
-
return
|
|
14263
|
+
return createHash11("sha256").update(stableStringify3(payload)).digest("hex");
|
|
14035
14264
|
}
|
|
14036
14265
|
function deepEquals(a, b) {
|
|
14037
14266
|
return stableStringify3(a) === stableStringify3(b);
|
|
@@ -14519,6 +14748,7 @@ ${first}` };
|
|
|
14519
14748
|
}
|
|
14520
14749
|
export {
|
|
14521
14750
|
CHIMERA_REVIEW_PROMPT,
|
|
14751
|
+
DEFAULT_PLUGIN_DISCOVERY_IO,
|
|
14522
14752
|
DefaultPluginAPI,
|
|
14523
14753
|
KERNEL_API_VERSION,
|
|
14524
14754
|
buildReviewerModelPool,
|
|
@@ -14529,26 +14759,37 @@ export {
|
|
|
14529
14759
|
createPromptsPlugin,
|
|
14530
14760
|
createSkillsPlugin,
|
|
14531
14761
|
createSyncPlugin,
|
|
14762
|
+
defaultPluginTrustPath,
|
|
14532
14763
|
definePlugin,
|
|
14533
14764
|
diffPluginConfig,
|
|
14765
|
+
discoverExternalPlugins,
|
|
14534
14766
|
emitReviewIfChanged,
|
|
14767
|
+
hashFileContents,
|
|
14535
14768
|
integrateFindings,
|
|
14536
14769
|
loadPlugins,
|
|
14537
14770
|
maybeCompactReviewStores,
|
|
14771
|
+
normalizeTrustKey,
|
|
14538
14772
|
parseChimeraReviewReport,
|
|
14539
14773
|
parseReviewSeverity,
|
|
14540
14774
|
persistReviewReport,
|
|
14775
|
+
pinPluginTrust,
|
|
14541
14776
|
pluginEntryMatchesName,
|
|
14777
|
+
readPluginTrustStore,
|
|
14542
14778
|
recordCompletedReview,
|
|
14543
14779
|
recordStartedReview,
|
|
14544
14780
|
redactPluginConfig,
|
|
14545
14781
|
resolvePluginConfig,
|
|
14546
14782
|
resolvePluginEnablement,
|
|
14783
|
+
resolvePluginEntryPath,
|
|
14547
14784
|
resolvePluginManifestConfig,
|
|
14785
|
+
resolvePluginTarget,
|
|
14548
14786
|
selectRoundRobinReviewerAssignment,
|
|
14549
14787
|
unloadPlugins,
|
|
14788
|
+
unpinPluginTrust,
|
|
14550
14789
|
updateReviewReportEvidence,
|
|
14551
14790
|
validatePluginConfigMetadata,
|
|
14552
|
-
verifyFindingsAgainstDisk
|
|
14791
|
+
verifyFindingsAgainstDisk,
|
|
14792
|
+
verifyPluginTrust,
|
|
14793
|
+
writePluginTrustStore
|
|
14553
14794
|
};
|
|
14554
14795
|
//# sourceMappingURL=index.js.map
|
package/dist/plugin/loader.d.ts
CHANGED
|
@@ -49,8 +49,12 @@ export interface LoadPluginsOptions {
|
|
|
49
49
|
* method that contradicts its declared `capabilities` — instead of
|
|
50
50
|
* just logging a warning. Use in CI/strict deployments to enforce
|
|
51
51
|
* manifest honesty. Default: false (log-only, backward-compatible).
|
|
52
|
+
*
|
|
53
|
+
* A predicate form is also accepted so hosts can enforce selectively —
|
|
54
|
+
* e.g. strictly for external (third-party) plugins while keeping
|
|
55
|
+
* first-party plugins warn-only.
|
|
52
56
|
*/
|
|
53
|
-
enforceCapabilities?: boolean | undefined;
|
|
57
|
+
enforceCapabilities?: boolean | ((plugin: Plugin) => boolean) | undefined;
|
|
54
58
|
/**
|
|
55
59
|
* Timeout in milliseconds for each plugin's `setup()` call. If the
|
|
56
60
|
* plugin's setup exceeds this deadline it is treated as a failure
|