@webpresso/agent-kit 0.21.5 → 0.23.0
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 +1 -1
- package/README.md +87 -124
- package/bin/_run.js +143 -1
- package/bin/runtime-manifest.json +40 -0
- package/catalog/AGENTS.md.tpl +7 -6
- package/catalog/agent/commands/plan-refine.md +3 -3
- package/catalog/agent/commands/pll.md +2 -0
- package/catalog/agent/guides/parallel-execution.md +2 -0
- package/catalog/agent/rules/extraction-parity.md +27 -1
- package/catalog/agent/rules/public-package-safety.md +24 -1
- package/catalog/agent/skills/pll/SKILL.md +1 -0
- package/catalog/base-kit/.github/workflows/ci.webpresso.yml.tmpl +33 -0
- package/catalog/base-kit/stryker.config.ts.tmpl +2 -2
- package/catalog/docs/templates/blueprint.md +1 -0
- package/catalog/docs/templates/blueprint.yaml +10 -12
- package/commands/blueprint.md +8 -43
- package/dist/esm/audit/blueprint-db-consistency.d.ts +1 -1
- package/dist/esm/audit/blueprint-db-consistency.js +6 -8
- package/dist/esm/audit/blueprint-lifecycle-sql.js +10 -3
- package/dist/esm/audit/cloudflare-deploy-contract.d.ts +3 -0
- package/dist/esm/audit/cloudflare-deploy-contract.js +64 -0
- package/dist/esm/audit/no-legacy-cli-bin.d.ts +3 -0
- package/dist/esm/audit/no-legacy-cli-bin.js +100 -0
- package/dist/esm/audit/package-surface.js +14 -1
- package/dist/esm/audit/repo-guardrails.js +40 -13
- package/dist/esm/audit/roadmap-links.js +23 -10
- package/dist/esm/blueprint/core/schema.d.ts +8 -8
- package/dist/esm/blueprint/core/schema.js +2 -2
- package/dist/esm/blueprint/db/enums.d.ts +1 -1
- package/dist/esm/blueprint/db/ingester.js +18 -10
- package/dist/esm/blueprint/lifecycle/audit.js +9 -2
- package/dist/esm/blueprint/lifecycle/local.js +15 -4
- package/dist/esm/blueprint/service/BlueprintCreationService.js +11 -6
- package/dist/esm/blueprint/service/BlueprintService.js +37 -19
- package/dist/esm/blueprint/service/scanner.js +73 -9
- package/dist/esm/blueprint/tracked-document/schema.d.ts +2 -2
- package/dist/esm/blueprint/utils/document-paths.d.ts +23 -0
- package/dist/esm/blueprint/utils/document-paths.js +91 -0
- package/dist/esm/build/package-manifest.js +7 -0
- package/dist/esm/build/release-policy.d.ts +27 -0
- package/dist/esm/build/release-policy.js +29 -0
- package/dist/esm/build/runtime-targets.d.ts +13 -0
- package/dist/esm/build/runtime-targets.js +48 -0
- package/dist/esm/cli/auto-update/detect-pm.d.ts +15 -0
- package/dist/esm/cli/auto-update/detect-pm.js +24 -9
- package/dist/esm/cli/auto-update/skip.js +9 -1
- package/dist/esm/cli/bundle/agent-command-inventory.d.ts +120 -0
- package/dist/esm/cli/bundle/agent-command-inventory.js +100 -0
- package/dist/esm/cli/bundle/index.d.ts +17 -0
- package/dist/esm/cli/bundle/index.js +15 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +49 -5
- package/dist/esm/cli/commands/audit-core.d.ts +1 -1
- package/dist/esm/cli/commands/audit.js +2 -0
- package/dist/esm/cli/commands/blueprint/router.js +11 -8
- package/dist/esm/cli/commands/hook.d.ts +8 -0
- package/dist/esm/cli/commands/hook.js +47 -0
- package/dist/esm/cli/commands/init/index.js +35 -1
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +1 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/codex-ownership.js +9 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +130 -20
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.d.ts +65 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +64 -0
- package/dist/esm/cli/commands/package-manager.d.ts +15 -0
- package/dist/esm/cli/commands/package-manager.js +42 -0
- package/dist/esm/cli/commands/test.d.ts +1 -0
- package/dist/esm/cli/commands/test.js +2 -1
- package/dist/esm/cli/commands/typecheck.js +5 -20
- package/dist/esm/cli/package-scripts.d.ts +12 -0
- package/dist/esm/cli/package-scripts.js +59 -0
- package/dist/esm/cli/utils.js +3 -22
- package/dist/esm/cli/wp-extensions.d.ts +14 -0
- package/dist/esm/cli/wp-extensions.js +34 -0
- package/dist/esm/config/docs-lint/schemas/common.d.ts +1 -1
- package/dist/esm/config/docs-lint/schemas/implementation-plan.d.ts +2 -2
- package/dist/esm/config/docs-lint/schemas/parent-roadmap.d.ts +1 -1
- package/dist/esm/config/stryker/index.d.ts +85 -0
- package/dist/esm/config/stryker/index.js +31 -0
- package/dist/esm/e2e/command-builder.js +11 -2
- package/dist/esm/e2e/config.d.ts +56 -0
- package/dist/esm/e2e/config.js +114 -0
- package/dist/esm/e2e/execution.js +4 -0
- package/dist/esm/e2e/run-planner.js +1 -0
- package/dist/esm/e2e/types.d.ts +2 -0
- package/dist/esm/format/index.js +1 -3
- package/dist/esm/hooks/guard-switch/index.d.ts +1 -1
- package/dist/esm/hooks/guard-switch/index.js +22 -14
- package/dist/esm/hooks/post-tool/lint-after-edit.d.ts +1 -0
- package/dist/esm/hooks/post-tool/lint-after-edit.js +5 -2
- package/dist/esm/hooks/pretool-guard/validators/file-conventions.js +1 -1
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.d.ts +6 -0
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +27 -2
- package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +2 -1
- package/dist/esm/hooks/pretool-guard/validators/path-contract.js +59 -34
- package/dist/esm/hooks/pretool-guard/validators/plan-frontmatter.js +3 -3
- package/dist/esm/hooks/shared/routing-block.js +18 -4
- package/dist/esm/hooks/shared/validators/blueprint.js +3 -0
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +1 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +5 -2
- package/dist/esm/lint/index.js +1 -1
- package/dist/esm/mcp/auto-discover.d.ts +2 -0
- package/dist/esm/mcp/auto-discover.js +14 -6
- package/dist/esm/mcp/blueprint-server.js +30 -26
- package/dist/esm/mcp/cli.js +21 -0
- package/dist/esm/mcp/runners/test.js +15 -0
- package/dist/esm/mcp/server.d.ts +7 -0
- package/dist/esm/mcp/server.js +16 -27
- package/dist/esm/mcp/tools/_registry.d.ts +3 -0
- package/dist/esm/mcp/tools/_registry.js +21 -0
- package/dist/esm/mcp/tools/audit.d.ts +1 -0
- package/dist/esm/mcp/tools/audit.js +11 -0
- package/dist/esm/mcp/tools/e2e.d.ts +1 -1
- package/dist/esm/mcp/tools/typecheck.js +4 -2
- package/dist/esm/mutation/affected.d.ts +9 -0
- package/dist/esm/mutation/affected.js +36 -0
- package/dist/esm/package.json +5 -0
- package/dist/esm/runtime/package-version.d.ts +2 -0
- package/dist/esm/runtime/package-version.js +43 -0
- package/dist/esm/test/command-builder.d.ts +3 -0
- package/dist/esm/test/command-builder.js +22 -3
- package/dist/esm/tool-runtime/index.d.ts +2 -2
- package/dist/esm/tool-runtime/index.js +2 -1
- package/dist/esm/tool-runtime/resolve-runner.d.ts +3 -0
- package/dist/esm/tool-runtime/resolve-runner.js +7 -5
- package/dist/esm/typecheck/index.js +4 -2
- package/dist/esm/wp-extension/index.d.ts +50 -0
- package/dist/esm/wp-extension/index.js +268 -0
- package/package.json +67 -31
- package/skills/pll/SKILL.md +1 -0
|
@@ -5,6 +5,7 @@ import { parseBlueprintForDb } from '#db/parser/blueprint-db-parser';
|
|
|
5
5
|
import { blueprintToSpecKit } from '#export/spec-kit/index';
|
|
6
6
|
import { getProjectRoot } from '#cli/utils';
|
|
7
7
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
8
|
+
import { getBlueprintDocumentPaths } from '#utils/document-paths.js';
|
|
8
9
|
import { applyBlueprintLifecycleToFile, BlueprintCreationService, BlueprintService, complexitySchema, relativeBlueprintSlug, parseBlueprint, planStatusSchema, runBlueprintAudit, resolveBlueprintFile, serializeBlueprint, validateAllTasksDone, } from '#local';
|
|
9
10
|
import { resolvePackageAssetPreferred } from '#utils/package-assets';
|
|
10
11
|
import { describeBlueprintExecutionRuntime, buildBlueprintLaunchSpec, buildStoppedRuntimeEvidence, controlBlueprintExecution, initializeBlueprintExecutionProgressBridge, launchBlueprintExecution, persistBlueprintExecutionArtifacts, persistBlueprintExecutionMetadata, recordLaunchFailure, reconcileBlueprintRuntimeSnapshot, readBlueprintExecutionState, syncBlueprintExecutionProgress, writeBlueprintRuntimeSnapshot, } from './execution.js';
|
|
@@ -309,10 +310,12 @@ export async function moveBlueprint(slug, status, options = {}) {
|
|
|
309
310
|
const projectRoot = resolveProjectRoot(options.projectRoot);
|
|
310
311
|
const nextStatus = normalizeBlueprintStatus(status);
|
|
311
312
|
const location = await resolveBlueprintLocation(slug, projectRoot);
|
|
313
|
+
const isFlatFile = path.basename(location.path) !== '_overview.md';
|
|
312
314
|
const sourceDir = path.dirname(location.path);
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
315
|
+
const targetPaths = getBlueprintDocumentPaths(resolveBlueprintRoot(projectRoot), nextStatus, relativeBlueprintSlug(location.slug));
|
|
316
|
+
const targetDir = targetPaths.directory;
|
|
317
|
+
const targetPath = isFlatFile ? targetPaths.flat : targetPaths.folder;
|
|
318
|
+
if (location.path === targetPath && location.blueprint.status === nextStatus) {
|
|
316
319
|
return {
|
|
317
320
|
fromPath: location.path,
|
|
318
321
|
fromStatus: location.blueprint.status,
|
|
@@ -328,18 +331,18 @@ export async function moveBlueprint(slug, status, options = {}) {
|
|
|
328
331
|
throw new Error('Blueprint move is recovery-only. Use wp blueprint start/task/finalize for normal lifecycle changes, or pass --force-recovery.');
|
|
329
332
|
}
|
|
330
333
|
assertBlueprintCanMoveToStatus(location.blueprint, nextStatus);
|
|
331
|
-
if (
|
|
332
|
-
await mkdir(path.dirname(
|
|
333
|
-
await rename(sourceDir, targetDir);
|
|
334
|
+
if (location.path !== targetPath) {
|
|
335
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
336
|
+
await rename(isFlatFile ? location.path : sourceDir, isFlatFile ? targetPath : targetDir);
|
|
334
337
|
}
|
|
335
338
|
const updated = await writeBlueprintWithStatus(targetPath, location.blueprint, nextStatus);
|
|
336
339
|
return {
|
|
337
340
|
fromPath: location.path,
|
|
338
341
|
fromStatus: location.blueprint.status,
|
|
339
|
-
message:
|
|
342
|
+
message: location.path === targetPath
|
|
340
343
|
? `Updated blueprint ${location.slug} to ${nextStatus}.`
|
|
341
344
|
: `Moved blueprint ${location.slug} to ${nextStatus}.`,
|
|
342
|
-
moved:
|
|
345
|
+
moved: location.path !== targetPath,
|
|
343
346
|
slug: location.slug,
|
|
344
347
|
toPath: targetPath,
|
|
345
348
|
toStatus: nextStatus,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CAC } from 'cac';
|
|
2
|
+
declare const HOOK_NAMES: readonly ["pretool-guard", "post-tool", "stop-qa", "guard-switch", "sessionstart-routing"];
|
|
3
|
+
export type HookName = (typeof HOOK_NAMES)[number];
|
|
4
|
+
export declare function isHookName(value: string): value is HookName;
|
|
5
|
+
export declare function runHookCommand(name: string): Promise<void>;
|
|
6
|
+
export declare function registerHookCommand(cli: CAC): void;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const HOOK_NAMES = [
|
|
2
|
+
'pretool-guard',
|
|
3
|
+
'post-tool',
|
|
4
|
+
'stop-qa',
|
|
5
|
+
'guard-switch',
|
|
6
|
+
'sessionstart-routing',
|
|
7
|
+
];
|
|
8
|
+
const HOOK_HANDLERS = {
|
|
9
|
+
'pretool-guard': async () => {
|
|
10
|
+
const { main } = await import('#hooks/pretool-guard/index');
|
|
11
|
+
await main();
|
|
12
|
+
},
|
|
13
|
+
'post-tool': async () => {
|
|
14
|
+
const { main } = await import('#hooks/post-tool/lint-after-edit');
|
|
15
|
+
await main();
|
|
16
|
+
},
|
|
17
|
+
'stop-qa': async () => {
|
|
18
|
+
const { main } = await import('#hooks/stop/qa-changed-files');
|
|
19
|
+
await main();
|
|
20
|
+
},
|
|
21
|
+
'guard-switch': async () => {
|
|
22
|
+
const { main } = await import('#hooks/guard-switch/index');
|
|
23
|
+
await main();
|
|
24
|
+
},
|
|
25
|
+
'sessionstart-routing': async () => {
|
|
26
|
+
const { main } = await import('#hooks/sessionstart/index');
|
|
27
|
+
await main();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export function isHookName(value) {
|
|
31
|
+
return value in HOOK_HANDLERS;
|
|
32
|
+
}
|
|
33
|
+
export async function runHookCommand(name) {
|
|
34
|
+
if (!isHookName(name)) {
|
|
35
|
+
throw new Error(`Unknown hook "${name}". Expected one of: ${HOOK_NAMES.join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
await HOOK_HANDLERS[name]();
|
|
38
|
+
}
|
|
39
|
+
export function registerHookCommand(cli) {
|
|
40
|
+
cli
|
|
41
|
+
.command('hook <name>', 'Run an internal plugin hook entrypoint')
|
|
42
|
+
.action(async (name) => {
|
|
43
|
+
await runHookCommand(name);
|
|
44
|
+
return 0;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -30,6 +30,7 @@ import { BASE_KIT_QUALITY_TARGETS, collectRuntimeContractGuidance, scaffoldBaseK
|
|
|
30
30
|
import { scaffoldMonorepoNav } from './scaffold-monorepo-nav.js';
|
|
31
31
|
import { REQUIRED_CORE_CAPABILITIES, auditHostSkillVisibility, parseAgentHosts, serializeHostVisibility, summarizeHostVisibility, } from './host-visibility.js';
|
|
32
32
|
import { scaffoldAgentHooks, trustCodexWebpressoHooksForRepo, trustCodexPresetHooksForUser, } from './scaffolders/agent-hooks/index.js';
|
|
33
|
+
import { ensureAgentKitGlobal } from './scaffolders/agent-kit-global/index.js';
|
|
33
34
|
import { scaffoldAuditHooks } from './scaffolders/audit-hooks/index.js';
|
|
34
35
|
import { ensureClaudeCodeUserPlugin } from './scaffolders/claude-plugin/index.js';
|
|
35
36
|
import { scaffoldClaudeRules } from './scaffolders/claude-rules/index.js';
|
|
@@ -495,6 +496,37 @@ export async function runInit(flags) {
|
|
|
495
496
|
console.log(` codex webpresso mcp: ⚠ no install root found (checked ${webpressoMcpResult.checked.length} paths). Install webpresso globally (\`bun add -g webpresso\`) or via the Claude plugin to wire up codex MCP.`);
|
|
496
497
|
break;
|
|
497
498
|
}
|
|
499
|
+
// Self-update the ONE globally-distributed agent-kit binary (PATH `wp`,
|
|
500
|
+
// plugin MCP, hooks all resolve to it), mirroring omx/omc/codex/claude.
|
|
501
|
+
// Non-fatal: a failed refresh never fails consumer setup, and it skips
|
|
502
|
+
// cleanly on a source/git clone, on `WP_SKIP_AUTO_INSTALL=1`, and in CI.
|
|
503
|
+
if (isCiEnvironment) {
|
|
504
|
+
console.log(' agent-kit global: - skipped (CI environment)');
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
const agentKitGlobalResult = ensureAgentKitGlobal({ options });
|
|
508
|
+
switch (agentKitGlobalResult.kind) {
|
|
509
|
+
case 'agent-kit-global-updated':
|
|
510
|
+
console.log(' agent-kit global: ✓ refreshed via vp install -g');
|
|
511
|
+
break;
|
|
512
|
+
case 'agent-kit-global-skipped-dry-run':
|
|
513
|
+
console.log(' agent-kit global: skipped (--dry-run)');
|
|
514
|
+
break;
|
|
515
|
+
case 'agent-kit-global-skipped-opt-out':
|
|
516
|
+
console.log(' agent-kit global: skipped (WP_SKIP_AUTO_INSTALL=1)');
|
|
517
|
+
break;
|
|
518
|
+
case 'agent-kit-global-skipped-source-clone':
|
|
519
|
+
console.log(` agent-kit global: - skipped (running from source clone ${agentKitGlobalResult.repoRoot})`);
|
|
520
|
+
break;
|
|
521
|
+
case 'agent-kit-global-skipped-no-vp':
|
|
522
|
+
console.warn(` agent-kit global: ⚠ ${agentKitGlobalResult.hint}`);
|
|
523
|
+
break;
|
|
524
|
+
case 'agent-kit-global-failed':
|
|
525
|
+
console.warn(` agent-kit global: ⚠ \`${agentKitGlobalResult.command.join(' ')}\` exited with ${agentKitGlobalResult.exitCode}; ` +
|
|
526
|
+
'the existing global binary is unchanged. Re-run `wp setup` once the registry is reachable.');
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
498
530
|
const claudePluginResult = ensureClaudeCodeUserPlugin({
|
|
499
531
|
options,
|
|
500
532
|
packageRoot,
|
|
@@ -697,7 +729,9 @@ export async function runInit(flags) {
|
|
|
697
729
|
}
|
|
698
730
|
}
|
|
699
731
|
}
|
|
700
|
-
printRuntimeContractGuidance(options.dryRun
|
|
732
|
+
printRuntimeContractGuidance(options.dryRun
|
|
733
|
+
? consumer.packageJson
|
|
734
|
+
: (readPackageJsonSafe(consumer.repoRoot) ?? consumer.packageJson));
|
|
701
735
|
console.log('\nwp init: setup phases finished.');
|
|
702
736
|
if (omxFailure === 'not-found')
|
|
703
737
|
return EXIT_SETUP_FAIL;
|
|
@@ -119,7 +119,7 @@ function mergePackageJson(repoRoot, options, globalInstall = false) {
|
|
|
119
119
|
const hasTestMutationScript = typeof scripts['test:mutation'] === 'string';
|
|
120
120
|
const hasE2eScript = typeof scripts['e2e'] === 'string';
|
|
121
121
|
const hasQaScript = typeof scripts['qa'] === 'string';
|
|
122
|
-
const verifyPathsScript = '
|
|
122
|
+
const verifyPathsScript = 'wp audit absolute-path-policy --root .';
|
|
123
123
|
const verifySecretsScript = 'bun scripts/check-no-dev-vars.ts';
|
|
124
124
|
const secretQuarantineAuditScript = 'bun scripts/audit-secret-provider-quarantine.ts';
|
|
125
125
|
const lintScript = 'wp lint src e2e *.config.ts';
|
|
@@ -10,6 +10,8 @@ export const KNOWN_WEBPRESSO_CODEX_BINS = [
|
|
|
10
10
|
const KNOWN_WEBPRESSO_CODEX_BIN_SET = new Set(KNOWN_WEBPRESSO_CODEX_BINS);
|
|
11
11
|
const NODE_MODULES_BIN_PATTERN = /^(?:\.\/|\/.*\/)?node_modules\/\.bin\/([\w-]+)$/u;
|
|
12
12
|
const GUARDED_NODE_MODULES_BIN_PATTERN = /^\[ -x (["']?)((?:\.\/|\/.*\/)?node_modules\/\.bin\/([\w-]+))\1 \] && \1\2\1 \|\| (?:true|printf .+)$/u;
|
|
13
|
+
const MANAGED_LAUNCHER_PATTERN = /^(?:["']?)((?:\.\/|\/.*\/)?\.codex\/managed-hooks\/((?:wp|ak)-[\w-]+)\.sh)(?:["']?)$/u;
|
|
14
|
+
const GUARDED_MANAGED_LAUNCHER_PATTERN = /^\[ -x (["']?)((?:\.\/|\/.*\/)?\.codex\/managed-hooks\/((?:wp|ak)-[\w-]+)\.sh)\1 \] && \1\2\1 \|\| (?:true|printf .+)$/u;
|
|
13
15
|
export function isWebpressoOwnedCodexHook(metadata, expectedSourcePaths) {
|
|
14
16
|
if (!isObject(metadata))
|
|
15
17
|
return false;
|
|
@@ -46,8 +48,14 @@ function extractDirectNodeModulesBin(command) {
|
|
|
46
48
|
const match = NODE_MODULES_BIN_PATTERN.exec(normalizedCommand);
|
|
47
49
|
if (match?.[1])
|
|
48
50
|
return match[1];
|
|
51
|
+
const managedLauncherMatch = MANAGED_LAUNCHER_PATTERN.exec(normalizedCommand);
|
|
52
|
+
if (managedLauncherMatch?.[2])
|
|
53
|
+
return managedLauncherMatch[2];
|
|
49
54
|
const guardedMatch = GUARDED_NODE_MODULES_BIN_PATTERN.exec(command.trim());
|
|
50
|
-
|
|
55
|
+
if (guardedMatch?.[3])
|
|
56
|
+
return guardedMatch[3];
|
|
57
|
+
const guardedManagedLauncherMatch = GUARDED_MANAGED_LAUNCHER_PATTERN.exec(command.trim());
|
|
58
|
+
return guardedManagedLauncherMatch?.[3] ?? null;
|
|
51
59
|
}
|
|
52
60
|
function stripSingleShellQuotePair(value) {
|
|
53
61
|
if (value.length < 2)
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Runs by default on every `wp setup`.
|
|
11
11
|
*/
|
|
12
|
-
import { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
|
-
import { join, resolve } from 'node:path';
|
|
14
|
+
import { dirname, join, resolve } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
15
16
|
import { patchJsonFile } from '#cli/commands/init/merge';
|
|
16
17
|
import { CodexAppServerClient } from '#codex/app-server/client.js';
|
|
17
18
|
import { normalizeGlobalCodexHooksFile, resolveBinaryOnPath, } from '#cli/commands/init/scaffolders/agent-hooks/codex-global-normalize';
|
|
@@ -28,16 +29,32 @@ import { buildSkillTag, extractSkillHooks, isTaggedSkillHook, } from './skill-ho
|
|
|
28
29
|
// bypass when the guard binary is missing/non-executable.
|
|
29
30
|
const PRETOOL_GUARD_BIN = 'wp-pretool-guard';
|
|
30
31
|
const PRETOOL_GUARD_MISSING_DENY = `printf '%s\\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"wp-pretool-guard is unavailable. Run vp install or wp setup."}}'`;
|
|
32
|
+
const CLAUDE_MANAGED_HOOK_SUBDIR = '.claude/hooks/managed';
|
|
33
|
+
const CODEX_MANAGED_HOOK_SUBDIR = '.codex/managed-hooks';
|
|
34
|
+
function claudeManagedHookLauncherPath(name) {
|
|
35
|
+
return `$CLAUDE_PROJECT_DIR/${CLAUDE_MANAGED_HOOK_SUBDIR}/${name}.sh`;
|
|
36
|
+
}
|
|
37
|
+
function codexManagedHookLauncherPath(repoRoot, name) {
|
|
38
|
+
return resolve(repoRoot, CODEX_MANAGED_HOOK_SUBDIR, `${name}.sh`);
|
|
39
|
+
}
|
|
40
|
+
function quoteShell(value) {
|
|
41
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
42
|
+
}
|
|
43
|
+
function quoteHookCommandPath(value) {
|
|
44
|
+
if (value.startsWith('$CLAUDE_PROJECT_DIR/'))
|
|
45
|
+
return `"${value}"`;
|
|
46
|
+
return quoteShell(value);
|
|
47
|
+
}
|
|
31
48
|
function buildGuardedHookCommand(binPath, name) {
|
|
49
|
+
const quotedBinPath = quoteHookCommandPath(binPath);
|
|
32
50
|
if (name === PRETOOL_GUARD_BIN) {
|
|
33
|
-
return `[ -x
|
|
51
|
+
return `[ -x ${quotedBinPath} ] && ${quotedBinPath} || ${PRETOOL_GUARD_MISSING_DENY}`;
|
|
34
52
|
}
|
|
35
|
-
return `[ -x
|
|
53
|
+
return `[ -x ${quotedBinPath} ] && ${quotedBinPath} || true`;
|
|
36
54
|
}
|
|
37
|
-
const CC_BIN = (name) => buildGuardedHookCommand(
|
|
55
|
+
const CC_BIN = (name) => buildGuardedHookCommand(claudeManagedHookLauncherPath(name), name);
|
|
38
56
|
const CODEX_BIN = (repoRoot) => (name) => {
|
|
39
|
-
|
|
40
|
-
return buildGuardedHookCommand(binPath, name);
|
|
57
|
+
return buildGuardedHookCommand(codexManagedHookLauncherPath(repoRoot, name), name);
|
|
41
58
|
};
|
|
42
59
|
// Canonical hook event names recognised by both Claude Code and Codex CLI.
|
|
43
60
|
// Used by `hoistTopLevelEvents` to identify legacy flat-form keys to migrate.
|
|
@@ -77,6 +94,8 @@ const DIRECT_NODE_MODULES_BIN_PATTERN = /^(?:\.\/|\/.*\/)?node_modules\/\.bin\/(
|
|
|
77
94
|
const GUARDED_NODE_MODULES_BIN_PATTERN = /^\[ -x (["']?)((?:\.\/|\/.*\/)?node_modules\/\.bin\/([\w-]+))\1 \] && \1\2\1 \|\| (?:true|printf .+)$/u;
|
|
78
95
|
const DIRECT_CLAUDE_NODE_MODULES_BIN_PATTERN = /^["']?\$CLAUDE_PROJECT_DIR\/node_modules\/\.bin\/([\w-]+)["']?$/u;
|
|
79
96
|
const GUARDED_CLAUDE_NODE_MODULES_BIN_PATTERN = /^\[ -x (["']?)\$CLAUDE_PROJECT_DIR\/node_modules\/\.bin\/([\w-]+)\1 \] && \1\$CLAUDE_PROJECT_DIR\/node_modules\/\.bin\/\2\1 \|\| (?:true|printf .+)$/u;
|
|
97
|
+
const DIRECT_MANAGED_HOOK_LAUNCHER_PATTERN = /^(?:["']?)((?:\$CLAUDE_PROJECT_DIR\/\.claude\/hooks\/managed|(?:\.\/|\/.*\/)?\.claude\/hooks\/managed|(?:\.\/|\/.*\/)?\.codex\/managed-hooks)\/((?:wp|ak)-[\w-]+)\.sh)(?:["']?)$/u;
|
|
98
|
+
const GUARDED_MANAGED_HOOK_LAUNCHER_PATTERN = /^\[ -x (["']?)((?:\$CLAUDE_PROJECT_DIR\/\.claude\/hooks\/managed|(?:\.\/|\/.*\/)?\.claude\/hooks\/managed|(?:\.\/|\/.*\/)?\.codex\/managed-hooks)\/((?:wp|ak)-[\w-]+)\.sh)\1 \] && \1\2\1 \|\| (?:true|printf .+)$/u;
|
|
80
99
|
// Capture the basename of any path that ends in a known script extension.
|
|
81
100
|
// Handles trailing chars (quote, space, end-of-string).
|
|
82
101
|
const SCRIPT_BASENAME_PATTERN = new RegExp(String.raw `([\w-]+\.(?:${SCRIPT_EXTENSIONS.join('|')}))(?=$|["'\s])`, 'u');
|
|
@@ -110,9 +129,15 @@ function extractAgentKitCodexBinName(command) {
|
|
|
110
129
|
const directBinMatch = DIRECT_NODE_MODULES_BIN_PATTERN.exec(normalizedCommand);
|
|
111
130
|
if (directBinMatch !== null)
|
|
112
131
|
return directBinMatch[1] ?? null;
|
|
132
|
+
const directManagedLauncherMatch = DIRECT_MANAGED_HOOK_LAUNCHER_PATTERN.exec(normalizedCommand);
|
|
133
|
+
if (directManagedLauncherMatch !== null)
|
|
134
|
+
return directManagedLauncherMatch[2] ?? null;
|
|
113
135
|
const guardedBinMatch = GUARDED_NODE_MODULES_BIN_PATTERN.exec(command.trim());
|
|
114
136
|
if (guardedBinMatch !== null)
|
|
115
137
|
return guardedBinMatch[3] ?? null;
|
|
138
|
+
const guardedManagedLauncherMatch = GUARDED_MANAGED_HOOK_LAUNCHER_PATTERN.exec(command.trim());
|
|
139
|
+
if (guardedManagedLauncherMatch !== null)
|
|
140
|
+
return guardedManagedLauncherMatch[3] ?? null;
|
|
116
141
|
return null;
|
|
117
142
|
}
|
|
118
143
|
function extractClaudeBinName(command) {
|
|
@@ -120,9 +145,15 @@ function extractClaudeBinName(command) {
|
|
|
120
145
|
const directBinMatch = DIRECT_CLAUDE_NODE_MODULES_BIN_PATTERN.exec(normalizedCommand);
|
|
121
146
|
if (directBinMatch !== null)
|
|
122
147
|
return directBinMatch[1] ?? null;
|
|
148
|
+
const directManagedLauncherMatch = DIRECT_MANAGED_HOOK_LAUNCHER_PATTERN.exec(normalizedCommand);
|
|
149
|
+
if (directManagedLauncherMatch !== null)
|
|
150
|
+
return directManagedLauncherMatch[2] ?? null;
|
|
123
151
|
const guardedBinMatch = GUARDED_CLAUDE_NODE_MODULES_BIN_PATTERN.exec(command.trim());
|
|
124
152
|
if (guardedBinMatch !== null)
|
|
125
153
|
return guardedBinMatch[2] ?? null;
|
|
154
|
+
const guardedManagedLauncherMatch = GUARDED_MANAGED_HOOK_LAUNCHER_PATTERN.exec(command.trim());
|
|
155
|
+
if (guardedManagedLauncherMatch !== null)
|
|
156
|
+
return guardedManagedLauncherMatch[3] ?? null;
|
|
126
157
|
return null;
|
|
127
158
|
}
|
|
128
159
|
function stripSingleShellQuotePair(value) {
|
|
@@ -305,20 +336,33 @@ function normalizeCodexAgentKitCommands(hooks, repoRoot) {
|
|
|
305
336
|
}
|
|
306
337
|
return normalized;
|
|
307
338
|
}
|
|
308
|
-
function
|
|
339
|
+
function normalizeClaudeAgentKitCommands(hooks) {
|
|
309
340
|
const normalized = {};
|
|
310
341
|
for (const [event, groups] of Object.entries(hooks)) {
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
342
|
+
const normalizedGroups = groups.reduce((dedupedGroups, group) => {
|
|
343
|
+
const nextGroup = {
|
|
344
|
+
...group,
|
|
345
|
+
hooks: group.hooks.flatMap((hook) => {
|
|
346
|
+
const command = hook.command;
|
|
347
|
+
if (typeof command !== 'string')
|
|
348
|
+
return hook;
|
|
349
|
+
const classification = classifyWebpressoHookBin(extractClaudeBinName(command));
|
|
350
|
+
if (classification === null)
|
|
351
|
+
return hook;
|
|
352
|
+
if (classification.kind === 'legacy')
|
|
353
|
+
return [];
|
|
354
|
+
return {
|
|
355
|
+
...hook,
|
|
356
|
+
command: CC_BIN(classification.binName),
|
|
357
|
+
};
|
|
358
|
+
}),
|
|
359
|
+
};
|
|
360
|
+
if (nextGroup.hooks.length === 0)
|
|
361
|
+
return dedupedGroups;
|
|
362
|
+
return ensureGroup(dedupedGroups, nextGroup);
|
|
363
|
+
}, []);
|
|
364
|
+
if (normalizedGroups.length > 0)
|
|
365
|
+
normalized[event] = normalizedGroups;
|
|
322
366
|
}
|
|
323
367
|
return normalized;
|
|
324
368
|
}
|
|
@@ -380,7 +424,7 @@ function patchClaudeUserSettings(existing) {
|
|
|
380
424
|
return next;
|
|
381
425
|
}
|
|
382
426
|
function patchClaudeSettings(existing, skillHooks) {
|
|
383
|
-
const existingHooks =
|
|
427
|
+
const existingHooks = normalizeClaudeAgentKitCommands((existing.hooks ?? {}));
|
|
384
428
|
const withSkills = mergeSkillHooks(existingHooks, skillHooks);
|
|
385
429
|
const webpresso = buildWebpressoHookGroups({
|
|
386
430
|
resolveBin: CC_BIN,
|
|
@@ -560,8 +604,74 @@ function ensureGstackHooks(repoRoot, options = {}) {
|
|
|
560
604
|
chmodSync(sessionPath, 0o755);
|
|
561
605
|
}
|
|
562
606
|
}
|
|
607
|
+
function resolveProjectHookBinPath(repoRoot, binName) {
|
|
608
|
+
return resolve(repoRoot, 'node_modules', '@webpresso', 'agent-kit', 'bin', `${binName}.js`);
|
|
609
|
+
}
|
|
610
|
+
function resolvePackageHookBin(binName) {
|
|
611
|
+
return join(resolvePackageRoot(), 'bin', `${binName}.js`);
|
|
612
|
+
}
|
|
613
|
+
function resolvePackageRoot() {
|
|
614
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
615
|
+
for (let depth = 0; depth < 10; depth++) {
|
|
616
|
+
if (existsSync(join(dir, 'package.json')) && existsSync(join(dir, 'bin', 'wp.js'))) {
|
|
617
|
+
return dir;
|
|
618
|
+
}
|
|
619
|
+
const parent = dirname(dir);
|
|
620
|
+
if (parent === dir)
|
|
621
|
+
break;
|
|
622
|
+
dir = parent;
|
|
623
|
+
}
|
|
624
|
+
throw new Error('wp setup: could not locate @webpresso/agent-kit package root for hook launchers.');
|
|
625
|
+
}
|
|
626
|
+
function renderManagedWebpressoHookLauncher(repoRoot, binName) {
|
|
627
|
+
const nodeBinary = quoteShell(process.execPath);
|
|
628
|
+
const projectBinPath = quoteShell(resolveProjectHookBinPath(repoRoot, binName));
|
|
629
|
+
const fallbackBinPath = quoteShell(resolvePackageHookBin(binName));
|
|
630
|
+
const missingFallback = binName === PRETOOL_GUARD_BIN ? PRETOOL_GUARD_MISSING_DENY : 'exit 0';
|
|
631
|
+
return `#!/bin/sh
|
|
632
|
+
NODE_BINARY=${nodeBinary}
|
|
633
|
+
PROJECT_BIN_PATH=${projectBinPath}
|
|
634
|
+
FALLBACK_BIN_PATH=${fallbackBinPath}
|
|
635
|
+
|
|
636
|
+
if [ ! -x "$NODE_BINARY" ]; then
|
|
637
|
+
${missingFallback}
|
|
638
|
+
exit 0
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
if [ -f "$PROJECT_BIN_PATH" ]; then
|
|
642
|
+
exec "$NODE_BINARY" "$PROJECT_BIN_PATH" "$@"
|
|
643
|
+
fi
|
|
644
|
+
|
|
645
|
+
if [ -f "$FALLBACK_BIN_PATH" ]; then
|
|
646
|
+
exec "$NODE_BINARY" "$FALLBACK_BIN_PATH" "$@"
|
|
647
|
+
fi
|
|
648
|
+
|
|
649
|
+
${missingFallback}
|
|
650
|
+
exit 0
|
|
651
|
+
`;
|
|
652
|
+
}
|
|
653
|
+
function ensureManagedWebpressoHookLaunchers(repoRoot, options = {}) {
|
|
654
|
+
if (options.dryRun)
|
|
655
|
+
return;
|
|
656
|
+
const launcherTargets = [
|
|
657
|
+
join(repoRoot, CLAUDE_MANAGED_HOOK_SUBDIR),
|
|
658
|
+
join(repoRoot, CODEX_MANAGED_HOOK_SUBDIR),
|
|
659
|
+
];
|
|
660
|
+
for (const directory of launcherTargets) {
|
|
661
|
+
mkdirSync(directory, { recursive: true });
|
|
662
|
+
for (const binName of WEBPRESSO_HOOK_BIN_NAMES) {
|
|
663
|
+
const launcherPath = join(directory, `${binName}.sh`);
|
|
664
|
+
const content = renderManagedWebpressoHookLauncher(repoRoot, binName);
|
|
665
|
+
if (!existsSync(launcherPath) || readFileSync(launcherPath, 'utf8') !== content) {
|
|
666
|
+
writeFileSync(launcherPath, content, 'utf8');
|
|
667
|
+
}
|
|
668
|
+
chmodSync(launcherPath, 0o755);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
563
672
|
export async function scaffoldAgentHooks(input) {
|
|
564
673
|
ensureGstackHooks(input.repoRoot, input.options);
|
|
674
|
+
ensureManagedWebpressoHookLaunchers(input.repoRoot, input.options);
|
|
565
675
|
const skillHooks = extractSkillHooks(join(input.repoRoot, '.agent', 'skills'));
|
|
566
676
|
const result = {
|
|
567
677
|
claude: patchJsonFile(join(input.repoRoot, '.claude', 'settings.json'), (existing) => patchClaudeSettings(existing, skillHooks), input.options),
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agent-kit-global` self-update scaffolder.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the ONE globally-distributed `@webpresso/agent-kit` binary fresh on
|
|
5
|
+
* every `wp setup`, mirroring how omx / omc / codex / claude self-update their
|
|
6
|
+
* own global installs. The PATH `wp`, the Claude plugin MCP, and the agent
|
|
7
|
+
* hooks all resolve to this single global binary, so refreshing it here means
|
|
8
|
+
* the next invocation everywhere runs the latest published release.
|
|
9
|
+
*
|
|
10
|
+
* Uses the exact same command the auto-update installer infers
|
|
11
|
+
* (`buildVpGlobalInstallCommand` — single source of truth), so there is no
|
|
12
|
+
* second place that can drift on the install incantation.
|
|
13
|
+
*
|
|
14
|
+
* Skipped (no-op, non-fatal) when:
|
|
15
|
+
* - `--dry-run` (no writes anywhere),
|
|
16
|
+
* - `WP_SKIP_AUTO_INSTALL=1` (the documented opt-out, surfaced in the update
|
|
17
|
+
* banner),
|
|
18
|
+
* - the running binary resolves into a webpresso source/git clone — a global
|
|
19
|
+
* install would clobber the developer's working clone with a published
|
|
20
|
+
* tarball (`detectGitInstall`),
|
|
21
|
+
* - `vp` is not on PATH (nothing to install with).
|
|
22
|
+
*
|
|
23
|
+
* A failed refresh is reported but NEVER fails consumer setup: keeping the
|
|
24
|
+
* global tool current is ancillary to scaffolding the consumer repo (same
|
|
25
|
+
* warn-only contract as the codex-cli scaffolder).
|
|
26
|
+
*/
|
|
27
|
+
import { spawnSync } from 'node:child_process';
|
|
28
|
+
import type { MergeOptions } from '#cli/commands/init/merge';
|
|
29
|
+
import { type SpinnerFactory } from '#cli/commands/init/scaffolders/spinner';
|
|
30
|
+
export interface EnsureAgentKitGlobalInput {
|
|
31
|
+
options: MergeOptions;
|
|
32
|
+
/** DI seam for child_process.spawnSync. */
|
|
33
|
+
spawn?: typeof spawnSync;
|
|
34
|
+
/** DI seam for environment-backed opt-out. */
|
|
35
|
+
env?: NodeJS.ProcessEnv;
|
|
36
|
+
/** The running binary path (defaults to process.argv[1]). Used for source-clone detection. */
|
|
37
|
+
argv1?: string;
|
|
38
|
+
/** DI seam for source/git-clone detection. */
|
|
39
|
+
detectGit?: (argv1: string) => string | null;
|
|
40
|
+
/** DI seam for spinner. Defaults to noop when !process.stdout.isTTY. */
|
|
41
|
+
spinnerFactory?: SpinnerFactory;
|
|
42
|
+
}
|
|
43
|
+
export type EnsureAgentKitGlobalResult = {
|
|
44
|
+
kind: 'agent-kit-global-updated';
|
|
45
|
+
command: readonly string[];
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'agent-kit-global-skipped-dry-run';
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'agent-kit-global-skipped-opt-out';
|
|
50
|
+
} | {
|
|
51
|
+
kind: 'agent-kit-global-skipped-source-clone';
|
|
52
|
+
repoRoot: string;
|
|
53
|
+
} | {
|
|
54
|
+
kind: 'agent-kit-global-skipped-no-vp';
|
|
55
|
+
hint: string;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'agent-kit-global-failed';
|
|
58
|
+
exitCode: number;
|
|
59
|
+
command: readonly string[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Refresh the single global `@webpresso/agent-kit` install via `vp install -g`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ensureAgentKitGlobal(input: EnsureAgentKitGlobalInput): EnsureAgentKitGlobalResult;
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agent-kit-global` self-update scaffolder.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the ONE globally-distributed `@webpresso/agent-kit` binary fresh on
|
|
5
|
+
* every `wp setup`, mirroring how omx / omc / codex / claude self-update their
|
|
6
|
+
* own global installs. The PATH `wp`, the Claude plugin MCP, and the agent
|
|
7
|
+
* hooks all resolve to this single global binary, so refreshing it here means
|
|
8
|
+
* the next invocation everywhere runs the latest published release.
|
|
9
|
+
*
|
|
10
|
+
* Uses the exact same command the auto-update installer infers
|
|
11
|
+
* (`buildVpGlobalInstallCommand` — single source of truth), so there is no
|
|
12
|
+
* second place that can drift on the install incantation.
|
|
13
|
+
*
|
|
14
|
+
* Skipped (no-op, non-fatal) when:
|
|
15
|
+
* - `--dry-run` (no writes anywhere),
|
|
16
|
+
* - `WP_SKIP_AUTO_INSTALL=1` (the documented opt-out, surfaced in the update
|
|
17
|
+
* banner),
|
|
18
|
+
* - the running binary resolves into a webpresso source/git clone — a global
|
|
19
|
+
* install would clobber the developer's working clone with a published
|
|
20
|
+
* tarball (`detectGitInstall`),
|
|
21
|
+
* - `vp` is not on PATH (nothing to install with).
|
|
22
|
+
*
|
|
23
|
+
* A failed refresh is reported but NEVER fails consumer setup: keeping the
|
|
24
|
+
* global tool current is ancillary to scaffolding the consumer repo (same
|
|
25
|
+
* warn-only contract as the codex-cli scaffolder).
|
|
26
|
+
*/
|
|
27
|
+
import { spawnSync } from 'node:child_process';
|
|
28
|
+
import { makeNoopSpinnerFactory } from '#cli/commands/init/scaffolders/spinner';
|
|
29
|
+
import { buildVpGlobalInstallCommand, detectGitInstall, PUBLIC_PACKAGE_NAME, } from '#cli/auto-update/detect-pm.js';
|
|
30
|
+
const NO_VP_HINT = 'vp (vite-plus) is not on PATH; cannot refresh the global ' +
|
|
31
|
+
`${PUBLIC_PACKAGE_NAME}. Install vite-plus, then re-run \`wp setup\`.`;
|
|
32
|
+
/**
|
|
33
|
+
* Refresh the single global `@webpresso/agent-kit` install via `vp install -g`.
|
|
34
|
+
*/
|
|
35
|
+
export function ensureAgentKitGlobal(input) {
|
|
36
|
+
if (input.options.dryRun)
|
|
37
|
+
return { kind: 'agent-kit-global-skipped-dry-run' };
|
|
38
|
+
const env = input.env ?? process.env;
|
|
39
|
+
if (env.WP_SKIP_AUTO_INSTALL === '1') {
|
|
40
|
+
return { kind: 'agent-kit-global-skipped-opt-out' };
|
|
41
|
+
}
|
|
42
|
+
const argv1 = input.argv1 ?? process.argv[1] ?? '';
|
|
43
|
+
const detectGit = input.detectGit ?? detectGitInstall;
|
|
44
|
+
const sourceCloneRoot = argv1.length > 0 ? detectGit(argv1) : null;
|
|
45
|
+
if (sourceCloneRoot !== null) {
|
|
46
|
+
return { kind: 'agent-kit-global-skipped-source-clone', repoRoot: sourceCloneRoot };
|
|
47
|
+
}
|
|
48
|
+
const spawn = input.spawn ?? spawnSync;
|
|
49
|
+
const spinner = (input.spinnerFactory ?? makeNoopSpinnerFactory())('agent-kit-global');
|
|
50
|
+
const probe = spawn('vp', ['--version'], { encoding: 'utf8' });
|
|
51
|
+
if (probe.error || (probe.status !== null && probe.status !== 0)) {
|
|
52
|
+
return { kind: 'agent-kit-global-skipped-no-vp', hint: NO_VP_HINT };
|
|
53
|
+
}
|
|
54
|
+
const command = buildVpGlobalInstallCommand();
|
|
55
|
+
spinner.start();
|
|
56
|
+
const install = spawn(command[0], command.slice(1), { stdio: 'inherit' });
|
|
57
|
+
if (install.status !== 0) {
|
|
58
|
+
spinner.fail('agent-kit global refresh failed');
|
|
59
|
+
return { kind: 'agent-kit-global-failed', exitCode: install.status ?? -1, command };
|
|
60
|
+
}
|
|
61
|
+
spinner.succeed('agent-kit global up to date');
|
|
62
|
+
return { kind: 'agent-kit-global-updated', command };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CAC } from 'cac';
|
|
2
|
+
import type { SpawnSyncReturns } from 'node:child_process';
|
|
3
|
+
export declare const PACKAGE_MANAGER_VERBS: readonly ["install", "add", "remove", "update", "exec", "run"];
|
|
4
|
+
export type PackageManagerVerb = (typeof PACKAGE_MANAGER_VERBS)[number];
|
|
5
|
+
export interface PackageManagerCommandConfig {
|
|
6
|
+
readonly command: string;
|
|
7
|
+
readonly args: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export interface PackageManagerCommandDeps {
|
|
10
|
+
readonly run?: (command: string, args: readonly string[]) => SpawnSyncReturns<string>;
|
|
11
|
+
}
|
|
12
|
+
export declare function registerPackageManagerCommand(cli: CAC, verb: PackageManagerVerb): void;
|
|
13
|
+
export declare function buildPackageManagerCommand(verb: PackageManagerVerb, argv?: readonly string[]): PackageManagerCommandConfig;
|
|
14
|
+
export declare function runPackageManagerCommand(verb: PackageManagerVerb, deps?: PackageManagerCommandDeps): number;
|
|
15
|
+
//# sourceMappingURL=package-manager.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { getManagedRunner } from '#tool-runtime';
|
|
3
|
+
export const PACKAGE_MANAGER_VERBS = ['install', 'add', 'remove', 'update', 'exec', 'run'];
|
|
4
|
+
const HELP_BY_VERB = {
|
|
5
|
+
install: 'Install dependencies through the managed vp facade.',
|
|
6
|
+
add: 'Add dependencies through the managed vp facade.',
|
|
7
|
+
remove: 'Remove dependencies through the managed vp facade.',
|
|
8
|
+
update: 'Update dependencies through the managed vp facade.',
|
|
9
|
+
exec: 'Run a binary through the managed vp facade.',
|
|
10
|
+
run: 'Run a package script through the managed vp facade.',
|
|
11
|
+
};
|
|
12
|
+
export function registerPackageManagerCommand(cli, verb) {
|
|
13
|
+
cli
|
|
14
|
+
.command(`${verb} [...args]`, HELP_BY_VERB[verb])
|
|
15
|
+
.allowUnknownOptions()
|
|
16
|
+
.action(() => runPackageManagerCommand(verb));
|
|
17
|
+
}
|
|
18
|
+
export function buildPackageManagerCommand(verb, argv = process.argv) {
|
|
19
|
+
const resolution = getManagedRunner('vp', { outputPolicy: 'structured' });
|
|
20
|
+
return {
|
|
21
|
+
command: resolution.command,
|
|
22
|
+
args: [...resolution.args, verb, ...extractVerbArgs(verb, argv)],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function runPackageManagerCommand(verb, deps = {}) {
|
|
26
|
+
const command = buildPackageManagerCommand(verb);
|
|
27
|
+
const result = (deps.run ?? defaultRun)(command.command, command.args);
|
|
28
|
+
return typeof result.status === 'number' ? result.status : 1;
|
|
29
|
+
}
|
|
30
|
+
function extractVerbArgs(verb, argv) {
|
|
31
|
+
const verbIndex = argv.findIndex((arg, index) => index >= 2 && arg === verb);
|
|
32
|
+
return verbIndex === -1 ? [] : argv.slice(verbIndex + 1);
|
|
33
|
+
}
|
|
34
|
+
function defaultRun(command, args) {
|
|
35
|
+
return spawnSync(command, [...args], {
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
env: process.env,
|
|
38
|
+
stdio: 'inherit',
|
|
39
|
+
windowsHide: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=package-manager.js.map
|
|
@@ -2,6 +2,7 @@ import type { CommandConfig, TestCommandOptions } from '#test';
|
|
|
2
2
|
import type { CAC } from 'cac';
|
|
3
3
|
export declare const TEST_COMMAND_HELP: string;
|
|
4
4
|
export interface AkTestCommandInput extends TestCommandOptions {
|
|
5
|
+
cwd?: string;
|
|
5
6
|
package?: readonly string[] | string;
|
|
6
7
|
file?: readonly string[] | string;
|
|
7
8
|
targets?: readonly string[] | string;
|
|
@@ -14,7 +14,7 @@ export function createAkTestCommandConfig(input) {
|
|
|
14
14
|
file: toArray(input.file),
|
|
15
15
|
positional: toArray(input.targets),
|
|
16
16
|
});
|
|
17
|
-
return buildTestCommand(target, input);
|
|
17
|
+
return buildTestCommand(target, { ...input, cwd: input.cwd });
|
|
18
18
|
}
|
|
19
19
|
export function registerTestCommand(cli) {
|
|
20
20
|
cli
|
|
@@ -35,6 +35,7 @@ export function registerTestCommand(cli) {
|
|
|
35
35
|
.action((targets, flags) => {
|
|
36
36
|
const rawArgv = process.argv.slice(2);
|
|
37
37
|
const command = createAkTestCommandConfig({
|
|
38
|
+
cwd: process.cwd(),
|
|
38
39
|
package: flags.package,
|
|
39
40
|
file: flags.file,
|
|
40
41
|
targets: targets ?? [],
|