@webpresso/agent-kit 0.21.5 → 0.24.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 +80 -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 +65 -0
- package/dist/esm/e2e/config.js +126 -0
- package/dist/esm/e2e/execution.js +4 -0
- package/dist/esm/e2e/load-host-adapter.d.ts +6 -1
- package/dist/esm/e2e/load-host-adapter.js +27 -9
- 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
package/dist/esm/cli/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import { resolve as pathResolve } from 'node:path';
|
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { bootstrapAk } from './bootstrap.js';
|
|
13
13
|
import { formatUnknownCommandError, normalizeArgv, readPackageVersion } from './utils.js';
|
|
14
|
+
import { registerWpExtensions, resolveWpCommandAlias } from './wp-extensions.js';
|
|
14
15
|
const VERSION = readPackageVersion(import.meta.url);
|
|
15
16
|
const SUPPORTED_COMMANDS = [
|
|
16
17
|
'blueprint',
|
|
@@ -37,9 +38,16 @@ const SUPPORTED_COMMANDS = [
|
|
|
37
38
|
'tech-debt',
|
|
38
39
|
'worktree',
|
|
39
40
|
'mcp',
|
|
41
|
+
'hook',
|
|
40
42
|
'hooks',
|
|
41
43
|
'gain',
|
|
42
44
|
'bench',
|
|
45
|
+
'install',
|
|
46
|
+
'add',
|
|
47
|
+
'remove',
|
|
48
|
+
'update',
|
|
49
|
+
'exec',
|
|
50
|
+
'run',
|
|
43
51
|
];
|
|
44
52
|
const ROOT_HELP = [
|
|
45
53
|
'Usage: wp [command] [options]',
|
|
@@ -52,6 +60,12 @@ const ROOT_HELP = [
|
|
|
52
60
|
' gain Show token savings from RTK — run after any AI session',
|
|
53
61
|
' sync Sync agent rules + skills across IDE surfaces (--kind, --check)',
|
|
54
62
|
' bench Run the session-memory benchmark harness',
|
|
63
|
+
' install Install dependencies through the managed vp facade',
|
|
64
|
+
' add Add dependencies through the managed vp facade',
|
|
65
|
+
' remove Remove dependencies through the managed vp facade',
|
|
66
|
+
' update Update dependencies through the managed vp facade',
|
|
67
|
+
' exec Run a binary through the managed vp facade',
|
|
68
|
+
' run Run a package script through the managed vp facade',
|
|
55
69
|
'',
|
|
56
70
|
'Quality:',
|
|
57
71
|
' audit Run packaged audits (bundle budgets, repo guardrails, TPH, tech-debt)',
|
|
@@ -123,7 +137,17 @@ export async function main() {
|
|
|
123
137
|
return 0;
|
|
124
138
|
}
|
|
125
139
|
await bootstrapAk(VERSION, argv);
|
|
126
|
-
|
|
140
|
+
const extensionRuntime = await registerWpExtensions({
|
|
141
|
+
cli,
|
|
142
|
+
cwd: process.cwd(),
|
|
143
|
+
env: process.env,
|
|
144
|
+
hostVersion: VERSION,
|
|
145
|
+
baseCommands: [...SUPPORTED_COMMANDS],
|
|
146
|
+
});
|
|
147
|
+
for (const warning of extensionRuntime.warnings)
|
|
148
|
+
console.error(warning);
|
|
149
|
+
const resolvedCommand = resolveWpCommandAlias(command, extensionRuntime.aliasMap);
|
|
150
|
+
switch (resolvedCommand) {
|
|
127
151
|
case 'blueprint': {
|
|
128
152
|
const { registerBlueprintRouter } = await import('./commands/blueprint/router.js');
|
|
129
153
|
registerBlueprintRouter(cli);
|
|
@@ -177,7 +201,7 @@ export async function main() {
|
|
|
177
201
|
case 'setup':
|
|
178
202
|
case 'init': {
|
|
179
203
|
const { registerInitCommand } = await import('./commands/init/index.js');
|
|
180
|
-
registerInitCommand(cli,
|
|
204
|
+
registerInitCommand(cli, resolvedCommand);
|
|
181
205
|
break;
|
|
182
206
|
}
|
|
183
207
|
case 'dev': {
|
|
@@ -235,6 +259,11 @@ export async function main() {
|
|
|
235
259
|
registerMcpCommand(cli);
|
|
236
260
|
break;
|
|
237
261
|
}
|
|
262
|
+
case 'hook': {
|
|
263
|
+
const { registerHookCommand } = await import('./commands/hook.js');
|
|
264
|
+
registerHookCommand(cli);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
238
267
|
case 'hooks': {
|
|
239
268
|
const { registerHooksCommand } = await import('./commands/hooks.js');
|
|
240
269
|
registerHooksCommand(cli);
|
|
@@ -250,20 +279,35 @@ export async function main() {
|
|
|
250
279
|
registerBenchCommand(cli);
|
|
251
280
|
break;
|
|
252
281
|
}
|
|
282
|
+
case 'install':
|
|
283
|
+
case 'add':
|
|
284
|
+
case 'remove':
|
|
285
|
+
case 'update':
|
|
286
|
+
case 'exec':
|
|
287
|
+
case 'run': {
|
|
288
|
+
const { registerPackageManagerCommand } = await import('./commands/package-manager.js');
|
|
289
|
+
registerPackageManagerCommand(cli, resolvedCommand);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
253
292
|
case 'worktree': {
|
|
254
293
|
const { registerWorktreeRouter } = await import('./commands/worktree/router.js');
|
|
255
294
|
registerWorktreeRouter(cli);
|
|
256
295
|
break;
|
|
257
296
|
}
|
|
258
297
|
default: {
|
|
259
|
-
|
|
260
|
-
|
|
298
|
+
if (!resolvedCommand || !extensionRuntime.commandNames.includes(resolvedCommand)) {
|
|
299
|
+
console.error(formatUnknownCommandError(command, SUPPORTED_COMMANDS));
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
261
302
|
}
|
|
262
303
|
}
|
|
263
304
|
cli.help();
|
|
264
305
|
cli.version(VERSION);
|
|
265
306
|
try {
|
|
266
|
-
|
|
307
|
+
const effectiveArgv = resolvedCommand && resolvedCommand !== command
|
|
308
|
+
? [argv[0] ?? 'node', argv[1] ?? 'wp', resolvedCommand, ...argv.slice(3)]
|
|
309
|
+
: argv;
|
|
310
|
+
cli.parse(effectiveArgv, { run: false });
|
|
267
311
|
const result = await cli.runMatchedCommand();
|
|
268
312
|
return typeof result === 'number' ? result : 0;
|
|
269
313
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RepoAuditResult } from '#audit/repo-guardrails';
|
|
2
|
-
export type AuditKind = 'tph' | 'tph-e2e' | 'bundle-budget' | 'commit-message' | 'blueprint-lifecycle' | 'roadmap-links' | 'docs-frontmatter' | 'catalog-drift' | 'package-surface' | 'agents' | 'tech-debt' | 'no-relative-parent-imports' | 'no-link-protocol' | 'vision' | 'bucket-boundary' | 'skill-sizes' | 'broken-refs' | 'memory-rotation' | 'gitignore-agent-surfaces' | 'memory-unified' | 'compile-drift' | 'architecture-drift' | 'absolute-path-policy' | 'agent-cost' | 'blueprint-db-consistency' | 'blueprint-lifecycle-sql' | 'tech-debt-cadence' | 'cross-repo-correlation' | 'ai-contracts' | 'mutation' | 'quality' | 'guardrails' | 'hook-surface' | 'no-relative-package-scripts';
|
|
2
|
+
export type AuditKind = 'tph' | 'tph-e2e' | 'bundle-budget' | 'commit-message' | 'blueprint-lifecycle' | 'roadmap-links' | 'docs-frontmatter' | 'catalog-drift' | 'package-surface' | 'agents' | 'tech-debt' | 'no-relative-parent-imports' | 'no-link-protocol' | 'vision' | 'bucket-boundary' | 'skill-sizes' | 'broken-refs' | 'memory-rotation' | 'gitignore-agent-surfaces' | 'memory-unified' | 'compile-drift' | 'architecture-drift' | 'cloudflare-deploy-contract' | 'absolute-path-policy' | 'agent-cost' | 'blueprint-db-consistency' | 'blueprint-lifecycle-sql' | 'tech-debt-cadence' | 'cross-repo-correlation' | 'ai-contracts' | 'mutation' | 'quality' | 'guardrails' | 'hook-surface' | 'no-relative-package-scripts';
|
|
3
3
|
export type AuditOutcome = {
|
|
4
4
|
kind: 'invalid-usage';
|
|
5
5
|
message: string;
|
|
@@ -56,7 +56,9 @@ const REPO_AUDIT_REGISTRY = {
|
|
|
56
56
|
'gitignore-agent-surfaces': async (root) => (await import('#audit/gitignore-agent-surfaces')).auditGitignoreAgentSurfaces(root),
|
|
57
57
|
'memory-unified': async (root) => (await import('#audit/memory-unified')).auditMemoryUnified(root),
|
|
58
58
|
'compile-drift': async (root) => (await import('#audit/compile-drift')).auditCompileDrift(root),
|
|
59
|
+
'no-legacy-cli-bin': async (root) => (await import('#audit/no-legacy-cli-bin')).auditNoLegacyCliBin(root),
|
|
59
60
|
'architecture-drift': async (root) => (await import('#audit/architecture-drift')).auditArchitectureDrift(root),
|
|
61
|
+
'cloudflare-deploy-contract': async (root) => (await import('#audit/cloudflare-deploy-contract')).auditCloudflareDeployContract(root),
|
|
60
62
|
'absolute-path-policy': async (root) => (await import('#audit/absolute-path-policy')).auditAbsolutePathPolicy(root),
|
|
61
63
|
'agent-cost': async (root) => (await import('#audit/agent-cost')).auditAgentCost(root),
|
|
62
64
|
'blueprint-db-consistency': async (root) => (await import('#audit/blueprint-db-consistency')).auditBlueprintDbConsistency(root),
|
|
@@ -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
|