@webpresso/agent-kit 0.28.0 → 0.29.1
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -3
- package/README.md +2 -2
- package/bin/_run.js +6 -0
- package/bin/wp +5 -0
- package/catalog/base-kit/.github/actions/setup-webpresso/action.yml.tmpl +21 -0
- package/catalog/base-kit/.github/workflows/{ci.webpresso.yml.tmpl → ci.yml.tmpl} +17 -7
- package/catalog/base-kit/tsconfig.json.tmpl +1 -1
- package/catalog/docs/templates/blueprint.yaml +1 -1
- package/dist/esm/audit/_budgets.d.ts +9 -1
- package/dist/esm/audit/_budgets.js +8 -1
- package/dist/esm/audit/blueprint-db-consistency.js +2 -2
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +17 -7
- package/dist/esm/audit/blueprint-lifecycle-sql.js +298 -48
- package/dist/esm/audit/blueprint-readme-drift.d.ts +6 -0
- package/dist/esm/audit/blueprint-readme-drift.js +110 -0
- package/dist/esm/audit/no-first-party-mjs.js +5 -4
- package/dist/esm/audit/package-surface.js +79 -10
- package/dist/esm/audit/repo-guardrails.d.ts +1 -1
- package/dist/esm/audit/repo-guardrails.js +43 -3
- package/dist/esm/audit/tech-debt-cadence.js +2 -3
- package/dist/esm/audit/toolchain-isolation.js +2 -3
- package/dist/esm/blueprint/core/parser.js +3 -2
- package/dist/esm/blueprint/core/schema.d.ts +3 -2
- package/dist/esm/blueprint/core/schema.js +1 -1
- package/dist/esm/blueprint/cross-repo/audit.js +3 -4
- package/dist/esm/blueprint/db/cold-start.js +2 -3
- package/dist/esm/blueprint/db/enums.d.ts +1 -1
- package/dist/esm/blueprint/db/ephemeral-projection.d.ts +25 -0
- package/dist/esm/blueprint/db/ephemeral-projection.js +36 -0
- package/dist/esm/blueprint/db/gc.d.ts +11 -0
- package/dist/esm/blueprint/db/gc.js +55 -0
- package/dist/esm/blueprint/db/ingester.js +39 -1
- package/dist/esm/blueprint/db/migrations/run.js +5 -3
- package/dist/esm/blueprint/db/paths.d.ts +13 -24
- package/dist/esm/blueprint/db/paths.js +25 -33
- package/dist/esm/blueprint/execution/progress-bridge.js +5 -4
- package/dist/esm/blueprint/freshness.d.ts +2 -0
- package/dist/esm/blueprint/freshness.js +3 -1
- package/dist/esm/blueprint/lifecycle/audit.js +6 -6
- package/dist/esm/blueprint/lifecycle/engine.d.ts +1 -1
- package/dist/esm/blueprint/lifecycle/engine.js +13 -9
- package/dist/esm/blueprint/lifecycle/transition-matrix.d.ts +5 -0
- package/dist/esm/blueprint/lifecycle/transition-matrix.js +20 -0
- package/dist/esm/blueprint/markdown/helpers.d.ts +1 -1
- package/dist/esm/blueprint/projection-ready.js +2 -0
- package/dist/esm/blueprint/service/BlueprintService.js +1 -1
- package/dist/esm/blueprint/service/blueprint-records.js +1 -1
- package/dist/esm/blueprint/tracked-document/parser.js +1 -1
- package/dist/esm/blueprint/utils/archive.d.ts +2 -2
- package/dist/esm/blueprint/utils/archive.js +5 -2
- package/dist/esm/blueprint/utils/package-assets.d.ts +13 -0
- package/dist/esm/blueprint/utils/package-assets.js +38 -6
- package/dist/esm/build/normalize-tsconfig-json-exports.d.ts +13 -0
- package/dist/esm/build/normalize-tsconfig-json-exports.js +39 -0
- package/dist/esm/build/package-manifest.js +12 -4
- package/dist/esm/build/release-policy.d.ts +9 -18
- package/dist/esm/build/release-policy.js +10 -19
- package/dist/esm/build/runtime-surface-policy.d.ts +14 -0
- package/dist/esm/build/runtime-surface-policy.js +13 -0
- package/dist/esm/cli/commands/audit-core.d.ts +2 -2
- package/dist/esm/cli/commands/audit.js +7 -3
- package/dist/esm/cli/commands/blueprint/db-commands.js +0 -3
- package/dist/esm/cli/commands/blueprint/mutations.d.ts +3 -2
- package/dist/esm/cli/commands/blueprint/mutations.js +45 -39
- package/dist/esm/cli/commands/blueprint/router-output.js +2 -2
- package/dist/esm/cli/commands/doctor.d.ts +1 -1
- package/dist/esm/cli/commands/doctor.js +4 -5
- package/dist/esm/cli/commands/init/config.d.ts +6 -10
- package/dist/esm/cli/commands/init/config.js +36 -20
- package/dist/esm/cli/commands/init/gitignore-patcher.js +0 -1
- package/dist/esm/cli/commands/init/index.d.ts +8 -1
- package/dist/esm/cli/commands/init/index.js +17 -19
- package/dist/esm/cli/commands/init/package-root.d.ts +20 -0
- package/dist/esm/cli/commands/init/package-root.js +110 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +5 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +3 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +8 -24
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.d.ts +9 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +79 -1
- package/dist/esm/cli/commands/init/scaffolders/claude-rules/index.js +2 -12
- package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +2 -12
- package/dist/esm/config/tsconfig/cloudflare.json +1 -1
- package/dist/esm/config/tsconfig/library.json +1 -1
- package/dist/esm/config/tsconfig/react-library.json +3 -2
- package/dist/esm/config/tsconfig/react-router.json +1 -1
- package/dist/esm/dev/restore-dev-links/index.js +3 -4
- package/dist/esm/docs-linter/blueprint-plan.js +46 -4
- package/dist/esm/hooks/check-dev-link/index.js +3 -4
- package/dist/esm/hooks/doctor.d.ts +11 -0
- package/dist/esm/hooks/doctor.js +174 -30
- package/dist/esm/hooks/guard-switch/index.js +3 -5
- package/dist/esm/hooks/post-tool/lint-after-edit.js +4 -5
- package/dist/esm/hooks/pretool-guard/index.js +2 -4
- package/dist/esm/hooks/pretool-guard/runner.js +2 -4
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +47 -6
- package/dist/esm/hooks/sessionstart/index.js +3 -4
- package/dist/esm/hooks/shared/direct-entrypoint.d.ts +10 -0
- package/dist/esm/hooks/shared/direct-entrypoint.js +21 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +3 -5
- package/dist/esm/hooks/test-quality-check.js +3 -4
- package/dist/esm/mcp/blueprint-server.js +26 -3
- package/dist/esm/mcp/cli.js +2 -6
- package/dist/esm/mcp/server.d.ts +2 -0
- package/dist/esm/mcp/server.js +18 -3
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/_shared/audit-kinds.js +1 -0
- package/dist/esm/mcp/tools/audit.d.ts +2 -1
- package/dist/esm/mcp/tools/audit.js +13 -3
- package/dist/esm/package.json +2 -0
- package/package.json +24 -15
- package/tsconfig/cloudflare.json +1 -1
- package/tsconfig/library.json +1 -1
- package/tsconfig/react-library.json +3 -2
- package/tsconfig/react-router.json +1 -1
- package/dist/esm/blueprint/db/legacy-migration.d.ts +0 -41
- package/dist/esm/blueprint/db/legacy-migration.js +0 -122
|
@@ -28,8 +28,19 @@ export interface RunHooksDoctorOptions {
|
|
|
28
28
|
/** Override the working directory used to detect RTK marker files. Defaults to process.cwd(). */
|
|
29
29
|
cwd?: string;
|
|
30
30
|
}
|
|
31
|
+
export interface ResolvePackageRootForRuntimeOptions {
|
|
32
|
+
readonly moduleUrl?: string;
|
|
33
|
+
readonly execPath?: string;
|
|
34
|
+
readonly argv0?: string;
|
|
35
|
+
readonly argv1?: string;
|
|
36
|
+
readonly pathEnv?: string;
|
|
37
|
+
readonly pathExtEnv?: string;
|
|
38
|
+
readonly platform?: NodeJS.Platform;
|
|
39
|
+
}
|
|
40
|
+
export declare function resolvePackageRootForRuntime(options?: ResolvePackageRootForRuntimeOptions): string | null;
|
|
31
41
|
export declare function findOwningPackageRoot(startDir: string): string | null;
|
|
32
42
|
export declare function checkRtkOnPath(cwd?: string): Promise<DoctorCheck | null>;
|
|
43
|
+
export declare function checkNativePluginRuntime(): DoctorCheck;
|
|
33
44
|
/**
|
|
34
45
|
* Verify the consumer's `.claude/settings.json` carries the managed agent-kit
|
|
35
46
|
* hook launchers. Since the hooks are single-sourced there (not in the plugin
|
package/dist/esm/hooks/doctor.js
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
* - MCP server starts and responds to tools/list (soft-fail)
|
|
10
10
|
* - installed host CLIs (Codex/OpenCode/Claude) can see the expected surfaces
|
|
11
11
|
*/
|
|
12
|
-
import { accessSync, constants, readFileSync, statSync } from 'node:fs';
|
|
12
|
+
import { accessSync, constants, lstatSync, readFileSync, statSync } from 'node:fs';
|
|
13
13
|
import { spawn } from 'node:child_process';
|
|
14
14
|
import { platform } from 'node:os';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { join, resolve } from 'node:path';
|
|
16
|
+
import { findAgentKitPackageRoot, resolveAgentKitPackageRoot, } from '#cli/commands/init/package-root';
|
|
17
17
|
import { STATE_FILE_RELATIVE_PATH, readDevLinkState } from '#dev/dev-link-state';
|
|
18
18
|
import { detectDevLinkBreakage, formatBreakageMessage } from '#hooks/check-dev-link/index';
|
|
19
19
|
import { isMcpReady } from './shared/mcp-sentinel.js';
|
|
@@ -30,35 +30,13 @@ const HOOK_BINS = [
|
|
|
30
30
|
{ name: 'test-quality-check', binName: 'wp-test-quality-check', checkStdin: false },
|
|
31
31
|
];
|
|
32
32
|
function resolvePackageRoot() {
|
|
33
|
-
return
|
|
33
|
+
return resolvePackageRootForRuntime();
|
|
34
34
|
}
|
|
35
|
-
export function
|
|
36
|
-
|
|
37
|
-
let fallback = null;
|
|
38
|
-
while (dir !== dirname(dir)) {
|
|
39
|
-
if (tryAccess(join(dir, 'package.json'))) {
|
|
40
|
-
if (fallback === null)
|
|
41
|
-
fallback = dir;
|
|
42
|
-
if (isOwningPackageRoot(dir))
|
|
43
|
-
return dir;
|
|
44
|
-
}
|
|
45
|
-
dir = dirname(dir);
|
|
46
|
-
}
|
|
47
|
-
return fallback;
|
|
35
|
+
export function resolvePackageRootForRuntime(options = {}) {
|
|
36
|
+
return resolveAgentKitPackageRoot(options);
|
|
48
37
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'));
|
|
52
|
-
if (typeof pkg.bin?.['wp'] === 'string')
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Ignore malformed package.json here and fall back to structural markers below.
|
|
57
|
-
}
|
|
58
|
-
return (tryAccess(join(dir, '.claude-plugin', 'plugin.json')) ||
|
|
59
|
-
tryAccess(join(dir, 'bin', 'wp.js')) ||
|
|
60
|
-
tryAccess(join(dir, 'src', 'cli', 'cli.ts')) ||
|
|
61
|
-
tryAccess(join(dir, 'dist', 'esm', 'cli', 'cli.js')));
|
|
38
|
+
export function findOwningPackageRoot(startDir) {
|
|
39
|
+
return findAgentKitPackageRoot(startDir);
|
|
62
40
|
}
|
|
63
41
|
function resolveHookBin(binName) {
|
|
64
42
|
try {
|
|
@@ -371,6 +349,171 @@ function checkPluginJson() {
|
|
|
371
349
|
return { ok: false, detail: `failed to read plugin.json: ${String(err)}` };
|
|
372
350
|
}
|
|
373
351
|
}
|
|
352
|
+
function formatNativeRuntimeDetail(status) {
|
|
353
|
+
return [
|
|
354
|
+
`launchMode=${status.launchMode}`,
|
|
355
|
+
status.targetId ? `targetId=${status.targetId}` : null,
|
|
356
|
+
status.manifestPath ? `manifest=${status.manifestPath}` : null,
|
|
357
|
+
status.stagedBinPath ? `stagedBin=${status.stagedBinPath}` : null,
|
|
358
|
+
status.runtimeTargetPath ? `targetBin=${status.runtimeTargetPath}` : null,
|
|
359
|
+
status.reason ? `reason=${status.reason}` : null,
|
|
360
|
+
]
|
|
361
|
+
.filter((value) => value !== null)
|
|
362
|
+
.join(', ');
|
|
363
|
+
}
|
|
364
|
+
export function checkNativePluginRuntime() {
|
|
365
|
+
const root = resolvePluginRoot();
|
|
366
|
+
if (!root) {
|
|
367
|
+
return {
|
|
368
|
+
name: 'native plugin runtime',
|
|
369
|
+
ok: false,
|
|
370
|
+
detail: formatNativeRuntimeDetail({
|
|
371
|
+
launchMode: 'missing',
|
|
372
|
+
reason: 'plugin root not found',
|
|
373
|
+
}),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const pluginJsonPath = join(root, '.claude-plugin', 'plugin.json');
|
|
377
|
+
const manifestPath = join(root, 'bin', 'runtime-manifest.json');
|
|
378
|
+
const stagedBinPath = join(root, 'bin', 'wp');
|
|
379
|
+
if (!tryAccess(pluginJsonPath)) {
|
|
380
|
+
return {
|
|
381
|
+
name: 'native plugin runtime',
|
|
382
|
+
ok: false,
|
|
383
|
+
detail: formatNativeRuntimeDetail({
|
|
384
|
+
launchMode: 'missing',
|
|
385
|
+
manifestPath,
|
|
386
|
+
stagedBinPath,
|
|
387
|
+
reason: 'plugin manifest missing',
|
|
388
|
+
}),
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
const pluginManifest = JSON.parse(readFileSync(pluginJsonPath, 'utf-8'));
|
|
393
|
+
const server = pluginManifest.mcpServers?.webpresso;
|
|
394
|
+
const launchMode = server?.command === '${CLAUDE_PLUGIN_ROOT}/bin/wp' &&
|
|
395
|
+
Array.isArray(server.args) &&
|
|
396
|
+
server.args.length === 1 &&
|
|
397
|
+
server.args[0] === 'mcp'
|
|
398
|
+
? 'native'
|
|
399
|
+
: server?.command === 'node' ||
|
|
400
|
+
(server?.args ?? []).some((arg) => arg.endsWith('wp.js'))
|
|
401
|
+
? 'legacy-js'
|
|
402
|
+
: server
|
|
403
|
+
? 'custom'
|
|
404
|
+
: 'missing';
|
|
405
|
+
if (!tryAccess(manifestPath)) {
|
|
406
|
+
return {
|
|
407
|
+
name: 'native plugin runtime',
|
|
408
|
+
ok: false,
|
|
409
|
+
detail: formatNativeRuntimeDetail({
|
|
410
|
+
launchMode,
|
|
411
|
+
manifestPath,
|
|
412
|
+
stagedBinPath,
|
|
413
|
+
reason: 'runtime manifest missing',
|
|
414
|
+
}),
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
418
|
+
const target = manifest.targets?.find((candidate) => candidate.os === process.platform && candidate.cpu === process.arch);
|
|
419
|
+
const targetId = target?.id;
|
|
420
|
+
const targetFilename = target?.os === 'win32' ? `${manifest.binaryName ?? 'wp'}.exe` : manifest.binaryName ?? 'wp';
|
|
421
|
+
const runtimeTargetPath = targetId
|
|
422
|
+
? join(root, 'bin', 'runtime', targetId, targetFilename)
|
|
423
|
+
: undefined;
|
|
424
|
+
if (!targetId || !runtimeTargetPath) {
|
|
425
|
+
return {
|
|
426
|
+
name: 'native plugin runtime',
|
|
427
|
+
ok: false,
|
|
428
|
+
detail: formatNativeRuntimeDetail({
|
|
429
|
+
launchMode,
|
|
430
|
+
manifestPath,
|
|
431
|
+
stagedBinPath,
|
|
432
|
+
reason: `no runtime target for ${process.platform}/${process.arch}`,
|
|
433
|
+
}),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
if (!tryAccess(stagedBinPath)) {
|
|
437
|
+
return {
|
|
438
|
+
name: 'native plugin runtime',
|
|
439
|
+
ok: false,
|
|
440
|
+
detail: formatNativeRuntimeDetail({
|
|
441
|
+
launchMode,
|
|
442
|
+
targetId,
|
|
443
|
+
manifestPath,
|
|
444
|
+
stagedBinPath,
|
|
445
|
+
runtimeTargetPath,
|
|
446
|
+
reason: 'staged native launcher missing',
|
|
447
|
+
}),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (lstatSync(stagedBinPath).isSymbolicLink()) {
|
|
451
|
+
return {
|
|
452
|
+
name: 'native plugin runtime',
|
|
453
|
+
ok: false,
|
|
454
|
+
detail: formatNativeRuntimeDetail({
|
|
455
|
+
launchMode,
|
|
456
|
+
targetId,
|
|
457
|
+
manifestPath,
|
|
458
|
+
stagedBinPath,
|
|
459
|
+
runtimeTargetPath,
|
|
460
|
+
reason: 'staged native launcher is a symlink',
|
|
461
|
+
}),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
if (!tryAccess(runtimeTargetPath)) {
|
|
465
|
+
return {
|
|
466
|
+
name: 'native plugin runtime',
|
|
467
|
+
ok: false,
|
|
468
|
+
detail: formatNativeRuntimeDetail({
|
|
469
|
+
launchMode,
|
|
470
|
+
targetId,
|
|
471
|
+
manifestPath,
|
|
472
|
+
stagedBinPath,
|
|
473
|
+
runtimeTargetPath,
|
|
474
|
+
reason: 'target runtime binary missing',
|
|
475
|
+
}),
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
if (launchMode !== 'native') {
|
|
479
|
+
return {
|
|
480
|
+
name: 'native plugin runtime',
|
|
481
|
+
ok: false,
|
|
482
|
+
detail: formatNativeRuntimeDetail({
|
|
483
|
+
launchMode,
|
|
484
|
+
targetId,
|
|
485
|
+
manifestPath,
|
|
486
|
+
stagedBinPath,
|
|
487
|
+
runtimeTargetPath,
|
|
488
|
+
reason: 'plugin manifest is not using the native launcher',
|
|
489
|
+
}),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
name: 'native plugin runtime',
|
|
494
|
+
ok: true,
|
|
495
|
+
detail: formatNativeRuntimeDetail({
|
|
496
|
+
launchMode,
|
|
497
|
+
targetId,
|
|
498
|
+
manifestPath,
|
|
499
|
+
stagedBinPath,
|
|
500
|
+
runtimeTargetPath,
|
|
501
|
+
}),
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
return {
|
|
506
|
+
name: 'native plugin runtime',
|
|
507
|
+
ok: false,
|
|
508
|
+
detail: formatNativeRuntimeDetail({
|
|
509
|
+
launchMode: 'missing',
|
|
510
|
+
manifestPath,
|
|
511
|
+
stagedBinPath,
|
|
512
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
513
|
+
}),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
374
517
|
async function checkMcpServer() {
|
|
375
518
|
if (isMcpReady()) {
|
|
376
519
|
return { ok: true, detail: 'MCP server already running (sentinel found)', skipped: true };
|
|
@@ -643,6 +786,7 @@ export async function runHooksDoctor(opts = {}) {
|
|
|
643
786
|
}
|
|
644
787
|
checks.push(checkConsumerCodexHookPaths(opts.cwd));
|
|
645
788
|
checks.push({ name: 'plugin.json integrity', ...checkPluginJson() });
|
|
789
|
+
checks.push({ advisory: true, ...checkNativePluginRuntime() });
|
|
646
790
|
checks.push({
|
|
647
791
|
name: 'managed hooks installed (.claude/settings.json)',
|
|
648
792
|
advisory: true,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { runHook } from '#hooks/shared/hook-bootstrap';
|
|
3
|
-
import { realpathSync } from 'node:fs';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
3
|
import { setGuardEnabled } from './state.js';
|
|
4
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
6
5
|
export async function main() {
|
|
7
|
-
runHook((input) => {
|
|
6
|
+
await runHook((input) => {
|
|
8
7
|
const normalized = (input.prompt ?? '').toLowerCase().trim();
|
|
9
8
|
if (normalized === 'guard off') {
|
|
10
9
|
setGuardEnabled(false);
|
|
@@ -19,8 +18,7 @@ export async function main() {
|
|
|
19
18
|
return null;
|
|
20
19
|
}, () => '{}');
|
|
21
20
|
}
|
|
22
|
-
if (
|
|
23
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
21
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
24
22
|
void main();
|
|
25
23
|
}
|
|
26
24
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { existsSync
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
3
|
import { extname } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
4
|
import { runHook } from '#hooks/shared/hook-bootstrap';
|
|
6
5
|
import { getFilePath } from '#hooks/shared/types';
|
|
6
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
7
7
|
export const LINTABLE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.json', '.css'];
|
|
8
8
|
export const SKIP_PATTERNS = [
|
|
9
9
|
/\/node_modules\//,
|
|
@@ -47,14 +47,13 @@ export function processPostToolUse(input, projectDir) {
|
|
|
47
47
|
return lintFile(filePath, projectDir);
|
|
48
48
|
}
|
|
49
49
|
export async function main() {
|
|
50
|
-
runHook((input) => {
|
|
50
|
+
await runHook((input) => {
|
|
51
51
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
52
52
|
processPostToolUse(input, projectDir);
|
|
53
53
|
return null;
|
|
54
54
|
}, () => '{}');
|
|
55
55
|
}
|
|
56
|
-
if (
|
|
57
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
56
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
58
57
|
void main();
|
|
59
58
|
}
|
|
60
59
|
//# sourceMappingURL=lint-after-edit.js.map
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { realpathSync } from 'node:fs';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
2
|
import { main as runMain } from './runner.js';
|
|
3
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
5
4
|
export { getTarget, getToolType, handleParseError, logValidationResult, main, processValidation, runAllValidators, } from './runner.js';
|
|
6
5
|
export { VALIDATORS } from './validators/index.js';
|
|
7
|
-
if (
|
|
8
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
6
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
9
7
|
runMain();
|
|
10
8
|
}
|
|
11
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { realpathSync } from 'node:fs';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
2
|
import { isGuardEnabled } from '#hooks/guard-switch/state';
|
|
5
3
|
import { readStdinJson, suppressStderr } from '#hooks/shared/hook-bootstrap';
|
|
6
4
|
import { getCommand, getFilePath, isBashInput, parseToolInput } from '#hooks/shared/types';
|
|
7
5
|
import { logRun } from './logger.js';
|
|
8
6
|
import { extractRoutableCommandsFromToolInput, routeCommand } from './dev-routing.js';
|
|
9
7
|
import { VALIDATORS } from './validators/index.js';
|
|
8
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
10
9
|
const RED = '\x1b[31m';
|
|
11
10
|
const YELLOW = '\x1b[33m';
|
|
12
11
|
const DIM = '\x1b[2m';
|
|
@@ -147,8 +146,7 @@ export async function main() {
|
|
|
147
146
|
handleParseError(error, inputJson);
|
|
148
147
|
}
|
|
149
148
|
}
|
|
150
|
-
if (
|
|
151
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
149
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
152
150
|
main();
|
|
153
151
|
}
|
|
154
152
|
//# sourceMappingURL=runner.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readConfig } from '#cli/commands/init/config';
|
|
2
|
+
import { getLegalLifecycleTargets, isLegalLifecycleTransition, parseLifecycleBlueprintStatus, } from '#lifecycle/transition-matrix.js';
|
|
2
3
|
import { getCommand, isBashInput } from '#hooks/shared/types';
|
|
3
4
|
import { AUDIT_KINDS } from '#mcp/tools/_shared/audit-kinds';
|
|
4
5
|
import { createSkipResult } from './skip-result.js';
|
|
@@ -9,7 +10,12 @@ export const AUDIT_MODE_ENV = 'FORBIDDEN_COMMANDS_AUDIT';
|
|
|
9
10
|
export const DOCS_REF = 'AGENTS.md "Forbidden Commands (CRITICAL)" section';
|
|
10
11
|
const DB_HINT = 'Use the database MCP/tooling entrypoint instead of direct CLI execution';
|
|
11
12
|
const BLUEPRINT_HINT = 'wp blueprint new|list|audit — use wp_blueprint MCP tool for lifecycle transitions';
|
|
12
|
-
const BLUEPRINT_LIFECYCLE_DIRS = '(draft|planned|in-progress|completed|archived)';
|
|
13
|
+
const BLUEPRINT_LIFECYCLE_DIRS = '(draft|planned|in-progress|parked|completed|archived)';
|
|
14
|
+
const BLUEPRINT_GIT_MV_RULE = {
|
|
15
|
+
pattern: /^git\s+mv\b/,
|
|
16
|
+
category: 'blueprint',
|
|
17
|
+
suggestion: BLUEPRINT_HINT,
|
|
18
|
+
};
|
|
13
19
|
const LINT_BASE = 'wp_lint MCP tool with package/file scope';
|
|
14
20
|
const LINT_HINT = `${LINT_BASE} [--fix] [--fix-unsafe]`;
|
|
15
21
|
const FORMAT_HINT = 'wp_format MCP tool';
|
|
@@ -142,11 +148,6 @@ export function generateRules() {
|
|
|
142
148
|
pattern: new RegExp(`^mkdir\\b.*blueprints\\/${BLUEPRINT_LIFECYCLE_DIRS}`),
|
|
143
149
|
category: 'blueprint',
|
|
144
150
|
suggestion: BLUEPRINT_HINT,
|
|
145
|
-
}, {
|
|
146
|
-
// git mv is an alternative to bare mv for tracked files — same bypass vector.
|
|
147
|
-
pattern: new RegExp(`^git\\s+mv\\b.*blueprints\\/${BLUEPRINT_LIFECYCLE_DIRS}`),
|
|
148
|
-
category: 'blueprint',
|
|
149
|
-
suggestion: BLUEPRINT_HINT,
|
|
150
151
|
}, { pattern: /^doppler run/, category: 'unknown', suggestion: ENV_HINT }, { pattern: /^DATABASE_URL=/, category: 'unknown', suggestion: ENV_HINT }, { pattern: /^vp exec\b/, category: 'unknown', suggestion: TASK_TARGET_HINT }, { pattern: /^vp run\b/, category: 'unknown', suggestion: TASK_TARGET_HINT });
|
|
151
152
|
return rules;
|
|
152
153
|
}
|
|
@@ -342,6 +343,34 @@ export function splitTopLevelCommands(command) {
|
|
|
342
343
|
segments.push(last);
|
|
343
344
|
return segments;
|
|
344
345
|
}
|
|
346
|
+
function splitShellArgs(command) {
|
|
347
|
+
return command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
348
|
+
}
|
|
349
|
+
function unquoteShellArg(arg) {
|
|
350
|
+
return arg.replace(/^['"]|['"]$/g, '');
|
|
351
|
+
}
|
|
352
|
+
function extractBlueprintLifecycleStatusFromPathArg(arg) {
|
|
353
|
+
const normalized = unquoteShellArg(arg).replace(/\\/g, '/');
|
|
354
|
+
const match = normalized.match(/(?:^|\/)blueprints\/(draft|planned|in-progress|parked|completed|archived)(?:\/|$)/);
|
|
355
|
+
return parseLifecycleBlueprintStatus(match?.[1] ?? '');
|
|
356
|
+
}
|
|
357
|
+
function findIllegalBlueprintGitMv(command) {
|
|
358
|
+
for (const segment of splitTopLevelCommands(command.trim())) {
|
|
359
|
+
if (!/^git\s+mv\b/.test(segment))
|
|
360
|
+
continue;
|
|
361
|
+
const args = splitShellArgs(segment);
|
|
362
|
+
if (args.length < 4)
|
|
363
|
+
continue;
|
|
364
|
+
const from = extractBlueprintLifecycleStatusFromPathArg(args[2] ?? '');
|
|
365
|
+
const to = extractBlueprintLifecycleStatusFromPathArg(args[3] ?? '');
|
|
366
|
+
if (!from || !to)
|
|
367
|
+
continue;
|
|
368
|
+
if (isLegalLifecycleTransition(from, to))
|
|
369
|
+
return null;
|
|
370
|
+
return { segment, from, to };
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
345
374
|
export function findMatchingRule(command) {
|
|
346
375
|
for (const variant of getCommandVariants(command)) {
|
|
347
376
|
const rule = COMMAND_RULES.find((r) => r.pattern.test(variant));
|
|
@@ -505,6 +534,18 @@ export function validateForbiddenCommands(input) {
|
|
|
505
534
|
const command = getCommand(input);
|
|
506
535
|
if (!command)
|
|
507
536
|
return createSkipResult(VALIDATOR_NAME, 'No command found');
|
|
537
|
+
const illegalBlueprintGitMv = findIllegalBlueprintGitMv(command);
|
|
538
|
+
if (illegalBlueprintGitMv) {
|
|
539
|
+
const decoratedRule = {
|
|
540
|
+
...BLUEPRINT_GIT_MV_RULE,
|
|
541
|
+
suggestion: `${BLUEPRINT_HINT}. Illegal transition ${illegalBlueprintGitMv.from} → ` +
|
|
542
|
+
`${illegalBlueprintGitMv.to}; legal targets: ` +
|
|
543
|
+
`${getLegalLifecycleTargets(illegalBlueprintGitMv.from).join(', ') || '(none)'}`,
|
|
544
|
+
};
|
|
545
|
+
if (process.env[AUDIT_MODE_ENV] === '1')
|
|
546
|
+
return createAuditResult(illegalBlueprintGitMv.segment, decoratedRule);
|
|
547
|
+
return createBlockedResult(illegalBlueprintGitMv.segment, decoratedRule);
|
|
548
|
+
}
|
|
508
549
|
const rule = findMatchingRule(command);
|
|
509
550
|
if (rule) {
|
|
510
551
|
if (process.env[AUDIT_MODE_ENV] === '1')
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
* Always emits — never returns null. WP_ROUTING_BLOCK is always prepended.
|
|
16
16
|
* If `.agent/routing.md` exists and is non-empty, it is appended after the block.
|
|
17
17
|
*/
|
|
18
|
-
import { existsSync, readFileSync,
|
|
19
|
-
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
20
19
|
import { homedir } from 'node:os';
|
|
21
20
|
import { join } from 'node:path';
|
|
22
21
|
import { WP_ROUTING_BLOCK } from '#hooks/shared/routing-block';
|
|
23
22
|
import { readUpdateBanner } from './update-banner.js';
|
|
23
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
24
24
|
export { WP_ROUTING_BLOCK };
|
|
25
25
|
export const MAX_BYTES = 200 * 1024;
|
|
26
26
|
export const TRUNCATION_NOTICE = '\n\n[truncated: file exceeded 200KB limit]';
|
|
@@ -115,8 +115,7 @@ export async function main() {
|
|
|
115
115
|
}
|
|
116
116
|
process.exit(0);
|
|
117
117
|
}
|
|
118
|
-
if (
|
|
119
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
118
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
120
119
|
void main();
|
|
121
120
|
}
|
|
122
121
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return true when a module is being executed directly rather than imported.
|
|
3
|
+
*
|
|
4
|
+
* Bun single-file executables expose virtual `/$bunfs/...` paths while loading
|
|
5
|
+
* bundled modules. Those paths are not real filesystem entries, so direct
|
|
6
|
+
* `realpathSync` checks must degrade instead of throwing during native runtime
|
|
7
|
+
* imports.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isDirectEntrypoint(moduleUrl: string, argvPath?: string | undefined): boolean;
|
|
10
|
+
//# sourceMappingURL=direct-entrypoint.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
/**
|
|
4
|
+
* Return true when a module is being executed directly rather than imported.
|
|
5
|
+
*
|
|
6
|
+
* Bun single-file executables expose virtual `/$bunfs/...` paths while loading
|
|
7
|
+
* bundled modules. Those paths are not real filesystem entries, so direct
|
|
8
|
+
* `realpathSync` checks must degrade instead of throwing during native runtime
|
|
9
|
+
* imports.
|
|
10
|
+
*/
|
|
11
|
+
export function isDirectEntrypoint(moduleUrl, argvPath = process.argv[1]) {
|
|
12
|
+
if (!argvPath)
|
|
13
|
+
return false;
|
|
14
|
+
try {
|
|
15
|
+
return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(argvPath);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=direct-entrypoint.js.map
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { globSync } from 'glob';
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
|
-
import { realpathSync } from 'node:fs';
|
|
5
4
|
import { basename, dirname, extname, join } from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
5
|
import { runHook } from '#hooks/shared/hook-bootstrap';
|
|
8
6
|
import { isLintableFile, isSkippedPath } from '#hooks/post-tool/lint-after-edit';
|
|
7
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
9
8
|
const TYPECHECKABLE_EXTENSIONS = new Set(['.ts', '.tsx']);
|
|
10
9
|
export function getChangedFiles(projectDir) {
|
|
11
10
|
const unstaged = execSync('git diff --name-only', { cwd: projectDir, encoding: 'utf-8' }).trim();
|
|
@@ -86,14 +85,13 @@ export function formatStopHookOutput(result) {
|
|
|
86
85
|
return JSON.stringify(result);
|
|
87
86
|
}
|
|
88
87
|
export async function main() {
|
|
89
|
-
runHook(
|
|
88
|
+
await runHook(
|
|
90
89
|
// `Stop` is latency-sensitive and user-visible. Until webpresso grows a
|
|
91
90
|
// deferred execution plane, broad typecheck/test sweeps stay off the hot
|
|
92
91
|
// path instead of shelling synchronously at turn end.
|
|
93
92
|
(_input) => null, formatStopHookOutput);
|
|
94
93
|
}
|
|
95
|
-
if (
|
|
96
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
94
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
97
95
|
void main();
|
|
98
96
|
}
|
|
99
97
|
//# sourceMappingURL=qa-changed-files.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { resolveActiveWorktreeRoot } from './shared/worktree-root.js';
|
|
3
|
-
import { readFileSync
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
5
4
|
import { isAbsolute, join } from 'node:path';
|
|
5
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
6
6
|
import { findMutationGamingPatterns, findTautologicalAssertions, MUTATION_GAMING_PATTERNS, TAUTOLOGICAL_PATTERNS, } from './pretool-guard/validators/test-quality.js';
|
|
7
7
|
const testFileRegex = /\.test\.(ts|tsx|js|jsx)$/;
|
|
8
8
|
export function getTestQualityCheckCwd() {
|
|
@@ -54,8 +54,7 @@ export function runTestQualityCheck(argv = process.argv.slice(2), cwd = getTestQ
|
|
|
54
54
|
process.exit(1);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
if (
|
|
58
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
|
57
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
59
58
|
runTestQualityCheck();
|
|
60
59
|
}
|
|
61
60
|
//# sourceMappingURL=test-quality-check.js.map
|
|
@@ -461,11 +461,23 @@ function runValidate(filePath) {
|
|
|
461
461
|
catch (e) {
|
|
462
462
|
return { valid: false, gaps: [`Frontmatter parse error: ${toStr(e)}`] };
|
|
463
463
|
}
|
|
464
|
-
|
|
464
|
+
const blueprintStatus = String(fm.data['status'] ?? '').trim();
|
|
465
|
+
const requiredFields = blueprintStatus === 'draft'
|
|
466
|
+
? ['type', 'status']
|
|
467
|
+
: ['type', 'title', 'status', 'complexity', 'owner', 'last_updated'];
|
|
468
|
+
for (const f of requiredFields) {
|
|
465
469
|
const v = fm.data[f];
|
|
466
470
|
if (!v || String(v).trim() === '')
|
|
467
471
|
gaps.push(`Missing or empty frontmatter field: ${f}`);
|
|
468
472
|
}
|
|
473
|
+
const complexity = String(fm.data['complexity'] ?? '').trim();
|
|
474
|
+
if (complexity && !['XS', 'S', 'M', 'L', 'XL'].includes(complexity)) {
|
|
475
|
+
gaps.push(`Invalid frontmatter field: complexity (${complexity})`);
|
|
476
|
+
}
|
|
477
|
+
const lastUpdated = String(fm.data['last_updated'] ?? '').trim();
|
|
478
|
+
if (lastUpdated && !/^\d{4}-\d{2}-\d{2}$/.test(lastUpdated.replace(/^['"]|['"]$/g, ''))) {
|
|
479
|
+
gaps.push(`Invalid frontmatter field: last_updated (${lastUpdated})`);
|
|
480
|
+
}
|
|
469
481
|
const body = fm.content;
|
|
470
482
|
const taskHeaderRegex = /^####\s+(?:\[[^\]]+\]\s+)?Task\s+\S/m;
|
|
471
483
|
if (!taskHeaderRegex.test(body))
|
|
@@ -1145,12 +1157,12 @@ async function handleFinalize(projectResolver, cwd, raw) {
|
|
|
1145
1157
|
function assertBlueprintCanComplete(overviewPath, slug) {
|
|
1146
1158
|
const markdown = readFileSync(overviewPath, 'utf8');
|
|
1147
1159
|
const blueprint = parseBlueprint(markdown, slug);
|
|
1148
|
-
const unfinished = blueprint.tasks.filter((task) => task.status !== 'done');
|
|
1160
|
+
const unfinished = blueprint.tasks.filter((task) => task.status !== 'done' && task.status !== 'dropped');
|
|
1149
1161
|
if (unfinished.length > 0) {
|
|
1150
1162
|
const list = unfinished.map((task) => `${task.id} (${task.status})`).join(', ');
|
|
1151
1163
|
throw new Error(`Cannot complete "${slug}": the following tasks are not done: ${list}`);
|
|
1152
1164
|
}
|
|
1153
|
-
assertAllTasksHaveCanonicalPassingEvidence(markdown, blueprint.tasks.map((task) => task.id));
|
|
1165
|
+
assertAllTasksHaveCanonicalPassingEvidence(markdown, blueprint.tasks.filter((task) => task.status !== 'dropped').map((task) => task.id));
|
|
1154
1166
|
}
|
|
1155
1167
|
const depgraphSchema = z.object({ from: z.string() });
|
|
1156
1168
|
async function handleDepgraph(cwd, raw) {
|
|
@@ -1880,6 +1892,17 @@ async function handleBlueprintTransition(projectResolver, cwd, raw) {
|
|
|
1880
1892
|
const found = findBlueprintDir(root, slug, ALL_STATES);
|
|
1881
1893
|
if (!found)
|
|
1882
1894
|
return err('wp_blueprint_transition failed', `Blueprint "${slug}" not found on disk`);
|
|
1895
|
+
// Transitioning to `completed` must satisfy the same open-task gate as
|
|
1896
|
+
// finalize/promote — otherwise this path is a hole that completes a blueprint
|
|
1897
|
+
// with unfinished work. (terminal = done ∪ dropped, see assertBlueprintCanComplete.)
|
|
1898
|
+
if (to_state === 'completed') {
|
|
1899
|
+
try {
|
|
1900
|
+
assertBlueprintCanComplete(found.path, slug);
|
|
1901
|
+
}
|
|
1902
|
+
catch (error) {
|
|
1903
|
+
return err('wp_blueprint_transition failed', error instanceof Error ? error.message : String(error));
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1883
1906
|
try {
|
|
1884
1907
|
const refreshed = await applyLocalBlueprintTransition({
|
|
1885
1908
|
found,
|
package/dist/esm/mcp/cli.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* `dist/esm/mcp/tools/*.js` (post-build) or `src/mcp/tools/*.ts` (dev).
|
|
8
8
|
*/
|
|
9
9
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { isDirectEntrypoint } from '#hooks/shared/direct-entrypoint';
|
|
10
11
|
import { deleteSentinel, writeSentinel } from '#hooks/shared/mcp-sentinel';
|
|
11
12
|
import { createServer } from './server.js';
|
|
12
13
|
export async function runStdioServer() {
|
|
@@ -55,12 +56,7 @@ export async function runStdioServer() {
|
|
|
55
56
|
writeSentinel();
|
|
56
57
|
await settle.promise;
|
|
57
58
|
}
|
|
58
|
-
import {
|
|
59
|
-
import { fileURLToPath } from 'node:url';
|
|
60
|
-
const isDirectInvocation = typeof process !== 'undefined' &&
|
|
61
|
-
process.argv[1] !== undefined &&
|
|
62
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
|
|
63
|
-
if (isDirectInvocation) {
|
|
59
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
64
60
|
runStdioServer().catch((err) => {
|
|
65
61
|
process.stderr.write(`wp mcp: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
66
62
|
process.exit(1);
|
package/dist/esm/mcp/server.d.ts
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* — no edits required here.
|
|
8
8
|
*/
|
|
9
9
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
|
+
export declare function isBunSingleFileModuleUrl(moduleUrl: string): boolean;
|
|
10
11
|
export type ToolLoadMode = 'filesystem' | 'registry';
|
|
12
|
+
export declare function resolveDefaultToolLoadMode(moduleUrl?: string): ToolLoadMode;
|
|
11
13
|
export interface CreateServerOptions {
|
|
12
14
|
/**
|
|
13
15
|
* Directory to scan for tool descriptors. Defaults to `./tools` relative to
|