@webpresso/agent-kit 0.26.2 → 0.27.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/catalog/AGENTS.md.tpl +3 -3
- package/dist/esm/audit/no-first-party-mjs.d.ts +5 -0
- package/dist/esm/audit/no-first-party-mjs.js +83 -0
- package/dist/esm/audit/repo-guardrails.d.ts +17 -0
- package/dist/esm/audit/repo-guardrails.js +75 -0
- package/dist/esm/blueprint/aggregate.d.ts +1 -1
- package/dist/esm/blueprint/core/schema.d.ts +1 -1
- package/dist/esm/blueprint/db/enums.d.ts +1 -1
- 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/init/config.d.ts +8 -0
- package/dist/esm/cli/commands/init/config.js +24 -0
- package/dist/esm/cli/commands/init/detect-consumer.d.ts +11 -0
- package/dist/esm/cli/commands/init/detect-consumer.js +13 -0
- package/dist/esm/cli/commands/init/index.d.ts +1 -0
- package/dist/esm/cli/commands/init/index.js +15 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -1
- package/dist/esm/cli/commands/init/scaffolders/omx/index.d.ts +5 -3
- package/dist/esm/cli/commands/init/scaffolders/omx/index.js +10 -5
- package/dist/esm/config/docs-lint/schemas/agents.d.ts +2 -2
- package/dist/esm/config/docs-lint/schemas/audit.d.ts +1 -1
- package/dist/esm/config/docs-lint/schemas/common.d.ts +1 -1
- package/dist/esm/config/docs-lint/schemas/cookbook.d.ts +1 -1
- package/dist/esm/config/docs-lint/schemas/core.d.ts +4 -4
- package/dist/esm/config/docs-lint/schemas/evaluation.d.ts +1 -1
- package/dist/esm/config/docs-lint/schemas/ongoing-initiative.d.ts +2 -2
- package/dist/esm/config/docs-lint/schemas/rule.d.ts +1 -1
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +66 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +13 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.js +34 -0
- package/dist/esm/mcp/tools/_shared/project-root.d.ts +9 -4
- package/dist/esm/mcp/tools/_shared/project-root.js +35 -11
- package/dist/esm/mcp/tools/_shared/result.d.ts +2 -2
- package/dist/esm/mcp/tools/audit.d.ts +7 -5
- package/dist/esm/mcp/tools/audit.js +23 -20
- package/package.json +6 -6
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Webpresso agent-kit Claude Code plugin: blueprints, skills, hooks, MCP server",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.27.0"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -23,5 +23,5 @@
|
|
|
23
23
|
]
|
|
24
24
|
}
|
|
25
25
|
],
|
|
26
|
-
"version": "0.
|
|
26
|
+
"version": "0.27.0"
|
|
27
27
|
}
|
package/catalog/AGENTS.md.tpl
CHANGED
|
@@ -36,9 +36,9 @@ vp install && vp run setup:agent # setup:agent runs wp setup, which scaffolds .
|
|
|
36
36
|
agent-kit's catalog is the single source of truth for generated agent surfaces.
|
|
37
37
|
Agent-kit owns the generated agent surfaces in this file; the Webpresso CLI host owns the end-user command surface.
|
|
38
38
|
To customize skills, commands, or workflows, edit them in agent-kit's catalog
|
|
39
|
-
and publish — not in individual repos. The default `omx` preset
|
|
40
|
-
`omx setup --yes --scope user
|
|
41
|
-
`vp install -g oh-my-codex`. The default `omc` preset ensures OMC through
|
|
39
|
+
and publish — not in individual repos. The default `omx` preset refreshes
|
|
40
|
+
Vite+ through `vp upgrade`, chains `omx setup --yes --scope user`, and installs
|
|
41
|
+
missing OMX through `vp install -g oh-my-codex`. The default `omc` preset ensures OMC through
|
|
42
42
|
Claude Code's plugin marketplace in user scope when `claude` is on `PATH`.
|
|
43
43
|
`wp setup` also repairs the managed `.gitignore` block for regenerated agent
|
|
44
44
|
surfaces so repo-local `.codex/`,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RepoAuditResult } from './repo-guardrails.js';
|
|
2
|
+
export declare function isIgnoredNoFirstPartyMjsPath(relativePath: string): boolean;
|
|
3
|
+
export declare function findTrackedFirstPartyMjsPaths(trackedPaths: readonly string[]): string[];
|
|
4
|
+
export declare function auditNoFirstPartyMjs(rootDirectory?: string): RepoAuditResult;
|
|
5
|
+
//# sourceMappingURL=no-first-party-mjs.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
const IGNORED_PATH_PREFIXES = [
|
|
5
|
+
'node_modules',
|
|
6
|
+
'dist',
|
|
7
|
+
'build',
|
|
8
|
+
'coverage',
|
|
9
|
+
'.wrangler',
|
|
10
|
+
'.codex',
|
|
11
|
+
'.omx',
|
|
12
|
+
'.omc',
|
|
13
|
+
'logs',
|
|
14
|
+
'.test-reports',
|
|
15
|
+
'.webpresso/generated',
|
|
16
|
+
];
|
|
17
|
+
export function isIgnoredNoFirstPartyMjsPath(relativePath) {
|
|
18
|
+
const normalized = relativePath.replace(/\\/gu, '/');
|
|
19
|
+
return IGNORED_PATH_PREFIXES.some((prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`));
|
|
20
|
+
}
|
|
21
|
+
export function findTrackedFirstPartyMjsPaths(trackedPaths) {
|
|
22
|
+
return trackedPaths
|
|
23
|
+
.map((path) => path.replace(/\\/gu, '/'))
|
|
24
|
+
.filter((path) => path.endsWith('.mjs'))
|
|
25
|
+
.filter((path) => !isIgnoredNoFirstPartyMjsPath(path))
|
|
26
|
+
.toSorted();
|
|
27
|
+
}
|
|
28
|
+
function fail(root, message) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
title: 'no first-party .mjs',
|
|
32
|
+
checked: 0,
|
|
33
|
+
violations: [{ file: root, message }],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function resolveCanonicalRepoRoot(rootDirectory) {
|
|
37
|
+
const requestedRoot = resolve(rootDirectory);
|
|
38
|
+
let gitRoot;
|
|
39
|
+
try {
|
|
40
|
+
gitRoot = resolve(execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
41
|
+
cwd: requestedRoot,
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
44
|
+
}).trim());
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return fail(requestedRoot, `run no-first-party-mjs from a canonical repo root; ${requestedRoot} is not a git repository root`);
|
|
48
|
+
}
|
|
49
|
+
if (requestedRoot !== gitRoot) {
|
|
50
|
+
return fail(requestedRoot, `run no-first-party-mjs from the canonical repo root ${gitRoot}; refusing to scan ${requestedRoot}`);
|
|
51
|
+
}
|
|
52
|
+
if (!existsSync(join(gitRoot, 'package.json'))) {
|
|
53
|
+
return fail(requestedRoot, `run no-first-party-mjs from a canonical repo root with package.json; ${gitRoot} does not qualify`);
|
|
54
|
+
}
|
|
55
|
+
return { ok: true, root: gitRoot };
|
|
56
|
+
}
|
|
57
|
+
function listTrackedFiles(root) {
|
|
58
|
+
const output = execFileSync('git', ['ls-files'], {
|
|
59
|
+
cwd: root,
|
|
60
|
+
encoding: 'utf8',
|
|
61
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
62
|
+
});
|
|
63
|
+
return output
|
|
64
|
+
.split('\n')
|
|
65
|
+
.map((line) => line.trim())
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
}
|
|
68
|
+
export function auditNoFirstPartyMjs(rootDirectory = process.cwd()) {
|
|
69
|
+
const canonical = resolveCanonicalRepoRoot(rootDirectory);
|
|
70
|
+
if ('violations' in canonical)
|
|
71
|
+
return canonical;
|
|
72
|
+
const violations = findTrackedFirstPartyMjsPaths(listTrackedFiles(canonical.root)).map((file) => ({
|
|
73
|
+
file,
|
|
74
|
+
message: 'tracked first-party .mjs files are forbidden; rename this file to .ts',
|
|
75
|
+
}));
|
|
76
|
+
return {
|
|
77
|
+
ok: violations.length === 0,
|
|
78
|
+
title: 'no first-party .mjs',
|
|
79
|
+
checked: violations.length,
|
|
80
|
+
violations,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=no-first-party-mjs.js.map
|
|
@@ -73,6 +73,23 @@ export interface NoRelativeParentImportsOptions {
|
|
|
73
73
|
* tsconfig `extends`, `paths`, `references`, etc.
|
|
74
74
|
*/
|
|
75
75
|
export declare function auditNoRelativeParentImports(root: string, options?: NoRelativeParentImportsOptions): RepoAuditResult;
|
|
76
|
+
export interface TestIsolationOptions {
|
|
77
|
+
/** Directory to scan for test files. Defaults to `src`. */
|
|
78
|
+
srcDir?: string;
|
|
79
|
+
/** Test-file suffixes to inspect. Defaults to `.test.ts` / `.test.tsx`. */
|
|
80
|
+
extensions?: readonly string[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Fail if any `*.test.ts` reaches the repo's own `catalog/` template source
|
|
84
|
+
* through `process.cwd()`. The catalog tree is the source of every
|
|
85
|
+
* agent-surface template, so a `process.cwd()`-anchored path is both brittle
|
|
86
|
+
* (it depends on the runner's working directory) and the exact pattern behind
|
|
87
|
+
* the scaffold-agents-md footgun, where a test read the live
|
|
88
|
+
* `catalog/AGENTS.md.tpl` off cwd. Anchor catalog reads to the package via the
|
|
89
|
+
* import.meta-based `resolveCatalogDir()` helper instead, and write only into
|
|
90
|
+
* `mkdtemp`/`tmpdir` sandboxes.
|
|
91
|
+
*/
|
|
92
|
+
export declare function auditTestIsolation(root: string, options?: TestIsolationOptions): RepoAuditResult;
|
|
76
93
|
export interface NoRelativePackageScriptsOptions {
|
|
77
94
|
/** Glob-style subdirectory patterns relative to root to skip. */
|
|
78
95
|
excludeDirs?: readonly string[];
|
|
@@ -809,6 +809,81 @@ function walkTsconfigParentPaths(startDir, reportRoot, violations) {
|
|
|
809
809
|
walk(startDir);
|
|
810
810
|
return checked;
|
|
811
811
|
}
|
|
812
|
+
const TEST_ISOLATION_SKIP_DIRS = new Set([
|
|
813
|
+
'node_modules',
|
|
814
|
+
'dist',
|
|
815
|
+
'build',
|
|
816
|
+
'.git',
|
|
817
|
+
'.cache',
|
|
818
|
+
'.next',
|
|
819
|
+
'.turbo',
|
|
820
|
+
'.omx',
|
|
821
|
+
'.stryker-tmp',
|
|
822
|
+
'.claude',
|
|
823
|
+
// Scaffolding templates become a downstream consumer's tree, not ours.
|
|
824
|
+
'template',
|
|
825
|
+
// Workspace-level scratch/archive space.
|
|
826
|
+
'_sandbox',
|
|
827
|
+
]);
|
|
828
|
+
// `(join|resolve)(process.cwd(), ... 'catalog' ...)` — a test reaching into the
|
|
829
|
+
// live catalog template source off the runner's working directory.
|
|
830
|
+
const CWD_CATALOG_PATH_PATTERN = /\b(?:join|resolve)\s*\(\s*process\.cwd\(\)[^)]*\bcatalog\b/;
|
|
831
|
+
/**
|
|
832
|
+
* Fail if any `*.test.ts` reaches the repo's own `catalog/` template source
|
|
833
|
+
* through `process.cwd()`. The catalog tree is the source of every
|
|
834
|
+
* agent-surface template, so a `process.cwd()`-anchored path is both brittle
|
|
835
|
+
* (it depends on the runner's working directory) and the exact pattern behind
|
|
836
|
+
* the scaffold-agents-md footgun, where a test read the live
|
|
837
|
+
* `catalog/AGENTS.md.tpl` off cwd. Anchor catalog reads to the package via the
|
|
838
|
+
* import.meta-based `resolveCatalogDir()` helper instead, and write only into
|
|
839
|
+
* `mkdtemp`/`tmpdir` sandboxes.
|
|
840
|
+
*/
|
|
841
|
+
export function auditTestIsolation(root, options = {}) {
|
|
842
|
+
const srcDir = resolve(root, options.srcDir ?? 'src');
|
|
843
|
+
const extensions = options.extensions ?? ['.test.ts', '.test.tsx'];
|
|
844
|
+
const violations = [];
|
|
845
|
+
let checked = 0;
|
|
846
|
+
function walk(dir) {
|
|
847
|
+
if (!existsSync(dir))
|
|
848
|
+
return;
|
|
849
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
850
|
+
const full = join(dir, entry.name);
|
|
851
|
+
if (entry.isDirectory()) {
|
|
852
|
+
if (TEST_ISOLATION_SKIP_DIRS.has(entry.name))
|
|
853
|
+
continue;
|
|
854
|
+
walk(full);
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
if (!entry.isFile())
|
|
858
|
+
continue;
|
|
859
|
+
if (!extensions.some((ext) => entry.name.endsWith(ext)))
|
|
860
|
+
continue;
|
|
861
|
+
checked++;
|
|
862
|
+
const content = readFileSync(full, 'utf-8');
|
|
863
|
+
const rel = relativePath(root, full);
|
|
864
|
+
const lines = content.split('\n');
|
|
865
|
+
for (let i = 0; i < lines.length; i++) {
|
|
866
|
+
const line = lines[i] ?? '';
|
|
867
|
+
// Skip comment lines so prose referencing the pattern doesn't trip it.
|
|
868
|
+
if (/^\s*(?:\/\/|\/\*|\*)/.test(line))
|
|
869
|
+
continue;
|
|
870
|
+
if (CWD_CATALOG_PATH_PATTERN.test(line)) {
|
|
871
|
+
violations.push({
|
|
872
|
+
file: rel,
|
|
873
|
+
message: `Line ${i + 1}: test reads the catalog template source via process.cwd() — anchor catalog reads to the package with resolveCatalogDir() (import.meta-based) so the test does not depend on the runner's working directory: ${line.trim()}`,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
walk(srcDir);
|
|
880
|
+
return {
|
|
881
|
+
ok: violations.length === 0,
|
|
882
|
+
title: 'test-isolation',
|
|
883
|
+
checked,
|
|
884
|
+
violations,
|
|
885
|
+
};
|
|
886
|
+
}
|
|
812
887
|
function withFilePrefix(file, auditResult) {
|
|
813
888
|
return {
|
|
814
889
|
...auditResult,
|
|
@@ -50,9 +50,9 @@ export declare const readTargetSchema: z.ZodObject<{
|
|
|
50
50
|
project_id: z.ZodOptional<z.ZodString>;
|
|
51
51
|
scope: z.ZodOptional<z.ZodEnum<{
|
|
52
52
|
all: "all";
|
|
53
|
+
workspace: "workspace";
|
|
53
54
|
current: "current";
|
|
54
55
|
roots: "roots";
|
|
55
|
-
workspace: "workspace";
|
|
56
56
|
}>>;
|
|
57
57
|
}, z.core.$strict>;
|
|
58
58
|
export type ReadTarget = z.infer<typeof readTargetSchema>;
|
|
@@ -27,8 +27,8 @@ export declare const blueprintComplexitySchema: z.ZodEnum<{
|
|
|
27
27
|
export declare const taskStatusSchema: z.ZodEnum<{
|
|
28
28
|
blocked: "blocked";
|
|
29
29
|
"in-progress": "in-progress";
|
|
30
|
-
todo: "todo";
|
|
31
30
|
done: "done";
|
|
31
|
+
todo: "todo";
|
|
32
32
|
dropped: "dropped";
|
|
33
33
|
}>;
|
|
34
34
|
export declare const techDebtStatusSchema: z.ZodEnum<{
|
|
@@ -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' | 'cloudflare-deploy-contract' | 'toolchain-isolation' | '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' | 'toolchain-isolation' | 'absolute-path-policy' | 'no-first-party-mjs' | '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;
|
|
@@ -32,6 +32,7 @@ const REPO_AUDIT_REGISTRY = {
|
|
|
32
32
|
}),
|
|
33
33
|
'no-link-protocol': async (root) => (await import('#audit/repo-guardrails')).auditNoLinkProtocol(root),
|
|
34
34
|
'no-relative-package-scripts': async (root) => (await import('#audit/repo-guardrails')).auditNoRelativePackageScripts(root),
|
|
35
|
+
'test-isolation': async (root) => (await import('#audit/repo-guardrails')).auditTestIsolation(root),
|
|
35
36
|
'bucket-boundary': async (root, options) => (await import('#audit/bucket-boundary')).auditBucketBoundary(root, {
|
|
36
37
|
changedOnly: options.changedOnly,
|
|
37
38
|
strict: options.strict,
|
|
@@ -61,6 +62,7 @@ const REPO_AUDIT_REGISTRY = {
|
|
|
61
62
|
'cloudflare-deploy-contract': async (root) => (await import('#audit/cloudflare-deploy-contract')).auditCloudflareDeployContract(root),
|
|
62
63
|
'toolchain-isolation': async (root) => (await import('#audit/toolchain-isolation')).auditToolchainIsolation(root),
|
|
63
64
|
'absolute-path-policy': async (root) => (await import('#audit/absolute-path-policy')).auditAbsolutePathPolicy(root),
|
|
65
|
+
'no-first-party-mjs': async (root) => (await import('#audit/no-first-party-mjs')).auditNoFirstPartyMjs(root),
|
|
64
66
|
'agent-cost': async (root) => (await import('#audit/agent-cost')).auditAgentCost(root),
|
|
65
67
|
'blueprint-db-consistency': async (root) => (await import('#audit/blueprint-db-consistency')).auditBlueprintDbConsistency(root),
|
|
66
68
|
'blueprint-lifecycle-sql': async (root) => (await import('#audit/blueprint-lifecycle-sql')).auditBlueprintLifecycleSql(root),
|
|
@@ -16,6 +16,14 @@ export interface AgentkitConfig {
|
|
|
16
16
|
serverName?: string;
|
|
17
17
|
toolPrefix?: string;
|
|
18
18
|
};
|
|
19
|
+
/** Pretool-guard routing policy. `mechanism` lives in agent-kit; this is the
|
|
20
|
+
* per-repo `data`. `scriptRoutes` maps a package-script name (e.g.
|
|
21
|
+
* `docs:check`) to a `wp_audit` kind; `packageManager: 'vp-only'` opts into
|
|
22
|
+
* routing all raw `pnpm`/`npm` invocations to the `vp` facade. */
|
|
23
|
+
guard?: {
|
|
24
|
+
packageManager?: 'vp-only';
|
|
25
|
+
scriptRoutes?: Record<string, string>;
|
|
26
|
+
};
|
|
19
27
|
rules: {
|
|
20
28
|
overrides: string[];
|
|
21
29
|
};
|
|
@@ -48,6 +48,18 @@ export function readConfig(repoRoot) {
|
|
|
48
48
|
const normalizedMcp = serverName || toolPrefix
|
|
49
49
|
? { ...(serverName ? { serverName } : {}), ...(toolPrefix ? { toolPrefix } : {}) }
|
|
50
50
|
: undefined;
|
|
51
|
+
const guard = parsed.guard;
|
|
52
|
+
const packageManager = guard?.packageManager === 'vp-only' ? 'vp-only' : undefined;
|
|
53
|
+
const rawScriptRoutes = guard?.scriptRoutes && typeof guard.scriptRoutes === 'object'
|
|
54
|
+
? Object.fromEntries(Object.entries(guard.scriptRoutes).filter(([key, value]) => typeof key === 'string' && typeof value === 'string'))
|
|
55
|
+
: undefined;
|
|
56
|
+
const scriptRoutes = rawScriptRoutes && Object.keys(rawScriptRoutes).length > 0 ? rawScriptRoutes : undefined;
|
|
57
|
+
const normalizedGuard = packageManager || scriptRoutes
|
|
58
|
+
? {
|
|
59
|
+
...(packageManager ? { packageManager } : {}),
|
|
60
|
+
...(scriptRoutes ? { scriptRoutes } : {}),
|
|
61
|
+
}
|
|
62
|
+
: undefined;
|
|
51
63
|
const selectedHosts = Array.isArray(hosts?.selected)
|
|
52
64
|
? hosts.selected.filter((s) => ['codex', 'claude', 'opencode'].includes(String(s)))
|
|
53
65
|
: [];
|
|
@@ -66,6 +78,7 @@ export function readConfig(repoRoot) {
|
|
|
66
78
|
...(visibility ? { visibility } : {}),
|
|
67
79
|
},
|
|
68
80
|
...(normalizedMcp ? { mcp: normalizedMcp } : {}),
|
|
81
|
+
...(normalizedGuard ? { guard: normalizedGuard } : {}),
|
|
69
82
|
rules: { overrides: overrides.filter((s) => typeof s === 'string') },
|
|
70
83
|
scripts: {
|
|
71
84
|
'setup-agent': readOptionalString(scripts?.['setup-agent']),
|
|
@@ -93,11 +106,22 @@ export function mergeConfig(existing, incoming) {
|
|
|
93
106
|
...incoming.mcp,
|
|
94
107
|
}
|
|
95
108
|
: undefined;
|
|
109
|
+
const mergedScriptRoutes = existing.guard?.scriptRoutes || incoming.guard?.scriptRoutes
|
|
110
|
+
? { ...existing.guard?.scriptRoutes, ...incoming.guard?.scriptRoutes }
|
|
111
|
+
: undefined;
|
|
112
|
+
const mergedGuard = existing.guard || incoming.guard
|
|
113
|
+
? {
|
|
114
|
+
...existing.guard,
|
|
115
|
+
...incoming.guard,
|
|
116
|
+
...(mergedScriptRoutes ? { scriptRoutes: mergedScriptRoutes } : {}),
|
|
117
|
+
}
|
|
118
|
+
: undefined;
|
|
96
119
|
return {
|
|
97
120
|
version: incoming.version,
|
|
98
121
|
installed: { tier3Skills: tier3 },
|
|
99
122
|
hosts: incoming.hosts ?? existing.hosts,
|
|
100
123
|
...(mergedMcp ? { mcp: mergedMcp } : {}),
|
|
124
|
+
...(mergedGuard ? { guard: mergedGuard } : {}),
|
|
101
125
|
rules: { overrides },
|
|
102
126
|
scripts: {
|
|
103
127
|
'setup-agent': incoming.scripts['setup-agent'] ?? existing.scripts['setup-agent'],
|
|
@@ -45,5 +45,16 @@ export declare function discoverWorkspacePackages(repoRoot: string, globs: strin
|
|
|
45
45
|
* class that the catch-wrap doesn't surface.
|
|
46
46
|
*/
|
|
47
47
|
export declare function warnIfNonLocalCli(repoRoot: string, cliUrl?: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* agent-kit's own package name — the source repo for every agent-surface
|
|
50
|
+
* template (`catalog/`, the tracked `.agent/`/`.claude/` surfaces). Scaffolding
|
|
51
|
+
* into this repo overwrites the canonical sources, so `wp setup` refuses it
|
|
52
|
+
* unless explicitly overridden. Distinct from base-kit's broader
|
|
53
|
+
* `SELF_PACKAGE_NAMES` (which also covers the legacy `webpresso` framework
|
|
54
|
+
* identity); only agent-kit hosts the catalog templates.
|
|
55
|
+
*/
|
|
56
|
+
export declare const AGENT_KIT_PACKAGE_NAME = "@webpresso/agent-kit";
|
|
57
|
+
/** True when the consumer being scaffolded is agent-kit's own template-source repo. */
|
|
58
|
+
export declare function isAgentKitTemplateSourceRepo(packageName: string | undefined): boolean;
|
|
48
59
|
export declare function detectConsumer(startDir?: string): ConsumerContext | null;
|
|
49
60
|
//# sourceMappingURL=detect-consumer.d.ts.map
|
|
@@ -261,6 +261,19 @@ export function warnIfNonLocalCli(repoRoot, cliUrl = import.meta.url) {
|
|
|
261
261
|
? 'This repo already pins `webpresso`; rerun via the repo-local CLI (`vp run setup:agent` or `vp exec wp setup`).'
|
|
262
262
|
: 'Pin `webpresso` as a local dep for reproducible setup.'));
|
|
263
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* agent-kit's own package name — the source repo for every agent-surface
|
|
266
|
+
* template (`catalog/`, the tracked `.agent/`/`.claude/` surfaces). Scaffolding
|
|
267
|
+
* into this repo overwrites the canonical sources, so `wp setup` refuses it
|
|
268
|
+
* unless explicitly overridden. Distinct from base-kit's broader
|
|
269
|
+
* `SELF_PACKAGE_NAMES` (which also covers the legacy `webpresso` framework
|
|
270
|
+
* identity); only agent-kit hosts the catalog templates.
|
|
271
|
+
*/
|
|
272
|
+
export const AGENT_KIT_PACKAGE_NAME = '@webpresso/agent-kit';
|
|
273
|
+
/** True when the consumer being scaffolded is agent-kit's own template-source repo. */
|
|
274
|
+
export function isAgentKitTemplateSourceRepo(packageName) {
|
|
275
|
+
return packageName === AGENT_KIT_PACKAGE_NAME;
|
|
276
|
+
}
|
|
264
277
|
export function detectConsumer(startDir = process.cwd()) {
|
|
265
278
|
const repoRoot = findGitRoot(startDir);
|
|
266
279
|
if (!repoRoot)
|
|
@@ -14,7 +14,7 @@ import { readPackageVersion } from '#cli/utils';
|
|
|
14
14
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
15
15
|
import { runUnifiedSync } from '#symlinker/unified-sync';
|
|
16
16
|
import { defaultConfig, mergeConfig, readConfig, writeConfig, } from './config.js';
|
|
17
|
-
import { detectConsumer, warnIfNonLocalCli } from './detect-consumer.js';
|
|
17
|
+
import { detectConsumer, isAgentKitTemplateSourceRepo, warnIfNonLocalCli, } from './detect-consumer.js';
|
|
18
18
|
import { runPreflight, DOCS_URL } from './preflight.js';
|
|
19
19
|
import { summarizeResults } from './merge.js';
|
|
20
20
|
import { resolveTier3Selection } from './prompts.js';
|
|
@@ -138,6 +138,19 @@ export async function runInit(flags) {
|
|
|
138
138
|
`Run \`git init\` first, or pass --cwd pointing at a git working tree.`);
|
|
139
139
|
return EXIT_SETUP_FAIL;
|
|
140
140
|
}
|
|
141
|
+
// Self-repo guard: agent-kit is the SOURCE of every agent-surface template
|
|
142
|
+
// (catalog/, the tracked .agent/.claude surfaces). Scaffolding into its own
|
|
143
|
+
// working tree overwrites those canonical sources — the footgun where a stray
|
|
144
|
+
// `wp setup` reported `overwritten: 2, drifted: 11, git index cleanup: 6
|
|
145
|
+
// untracked` against the live repo. Refuse loudly and write nothing unless the
|
|
146
|
+
// maintainer explicitly opts in with --allow-self-scaffold.
|
|
147
|
+
if (isAgentKitTemplateSourceRepo(consumer.packageJson?.name) && flags.allowSelfScaffold !== true) {
|
|
148
|
+
console.error(`wp setup: refusing to scaffold @webpresso/agent-kit's own repo (${consumer.repoRoot}).\n` +
|
|
149
|
+
` This repo is the source of the agent-surface templates; running setup here\n` +
|
|
150
|
+
` overwrites the canonical sources under catalog/ and the tracked .agent/.claude surfaces.\n` +
|
|
151
|
+
` To deliberately regenerate agent-kit's own surfaces, re-run with --allow-self-scaffold.`);
|
|
152
|
+
return EXIT_SETUP_FAIL;
|
|
153
|
+
}
|
|
141
154
|
warnIfNonLocalCli(consumer.repoRoot);
|
|
142
155
|
// Run the 5-point compatibility preflight before any scaffolders fire.
|
|
143
156
|
const preflightResult = await runPreflight(consumer.repoRoot, flags.strict ?? false);
|
|
@@ -839,6 +852,7 @@ export function registerInitCommand(cli, commandName = 'init') {
|
|
|
839
852
|
.option('--cwd <dir>', 'Working tree to scaffold into (default: process.cwd())')
|
|
840
853
|
.option('--strict', 'Abort if any compatibility check fails (default: warn and continue)')
|
|
841
854
|
.option('--project', 'Configure OMX/OMC in project scope instead of the default user scope')
|
|
855
|
+
.option('--allow-self-scaffold', "Override the self-repo guard to scaffold @webpresso/agent-kit's own template-source repo (maintainers only)")
|
|
842
856
|
.action(async (flags) => {
|
|
843
857
|
const code = await runInit(flags);
|
|
844
858
|
if (code !== EXIT_SUCCESS) {
|
|
@@ -13,6 +13,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'n
|
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
14
|
import { dirname, join, resolve } from 'node:path';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { isHookName } from '#cli/commands/hook.js';
|
|
16
17
|
import { patchJsonFile } from '#cli/commands/init/merge';
|
|
17
18
|
import { CodexAppServerClient } from '#codex/app-server/client.js';
|
|
18
19
|
import { normalizeGlobalCodexHooksFile, resolveBinaryOnPath, } from '#cli/commands/init/scaffolders/agent-hooks/codex-global-normalize';
|
|
@@ -623,13 +624,65 @@ function resolvePackageRoot() {
|
|
|
623
624
|
}
|
|
624
625
|
throw new Error('wp setup: could not locate @webpresso/agent-kit package root for hook launchers.');
|
|
625
626
|
}
|
|
627
|
+
// Consumer-side runtime package directory for the current platform. Mirrors the
|
|
628
|
+
// canonical target table in `src/build/runtime-targets.ts`; kept inline here to
|
|
629
|
+
// avoid a deep cross-tree import for a small lookup. If that target list ever
|
|
630
|
+
// changes, update both. Returns undefined for platforms with no compiled
|
|
631
|
+
// runtime package.
|
|
632
|
+
function compiledRuntimePackageDir() {
|
|
633
|
+
const osLabel = process.platform === 'win32' ? 'windows' : process.platform;
|
|
634
|
+
const id = `${osLabel}-${process.arch}`;
|
|
635
|
+
const known = new Set(['darwin-arm64', 'darwin-x64', 'linux-x64', 'linux-arm64', 'windows-x64']);
|
|
636
|
+
return known.has(id) ? `agent-kit-runtime-${id}` : undefined;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Absolute path to the consumer's self-contained compiled `wp` binary, when the
|
|
640
|
+
* platform runtime package (`@webpresso/agent-kit-runtime-<platform>`) is
|
|
641
|
+
* installed. The compiled binary bundles its own runtime, so preferring it makes
|
|
642
|
+
* the hook launcher survive node-path staleness — an nvm/version change
|
|
643
|
+
* invalidates the captured absolute node path but not a self-contained binary.
|
|
644
|
+
* Returns undefined when no compiled runtime is installed (today's default), so
|
|
645
|
+
* the launcher keeps its absolute-node fallback unchanged.
|
|
646
|
+
*/
|
|
647
|
+
function resolveCompiledWpBinary(repoRoot) {
|
|
648
|
+
const packageDir = compiledRuntimePackageDir();
|
|
649
|
+
if (!packageDir)
|
|
650
|
+
return undefined;
|
|
651
|
+
const filename = process.platform === 'win32' ? 'wp.exe' : 'wp';
|
|
652
|
+
const candidate = join(repoRoot, 'node_modules', '@webpresso', packageDir, 'bin', filename);
|
|
653
|
+
return existsSync(candidate) ? candidate : undefined;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* The `wp hook <sub>` subcommand a managed launcher should dispatch to via the
|
|
657
|
+
* compiled binary, or undefined when `binName` is not a dispatchable hook (e.g.
|
|
658
|
+
* `wp-check-dev-link`, which has no `wp hook` handler). The names map 1:1 by
|
|
659
|
+
* stripping the `wp-` prefix; `isHookName` is the single source of truth.
|
|
660
|
+
*/
|
|
661
|
+
function hookSubcommandFor(binName) {
|
|
662
|
+
const sub = binName.startsWith('wp-') ? binName.slice(3) : binName;
|
|
663
|
+
return isHookName(sub) ? sub : undefined;
|
|
664
|
+
}
|
|
626
665
|
function renderManagedWebpressoHookLauncher(repoRoot, binName) {
|
|
627
666
|
const nodeBinary = quoteShell(process.execPath);
|
|
628
667
|
const projectBinPath = quoteShell(resolveProjectHookBinPath(repoRoot, binName));
|
|
629
668
|
const fallbackBinPath = quoteShell(resolvePackageHookBin(binName));
|
|
630
669
|
const missingFallback = binName === PRETOOL_GUARD_BIN ? PRETOOL_GUARD_MISSING_DENY : 'exit 0';
|
|
670
|
+
// Prefer the self-contained compiled `wp` binary when it resolves: it bundles
|
|
671
|
+
// its own runtime, so it is immune to the node-path staleness that would break
|
|
672
|
+
// the absolute `$NODE_BINARY` path below. Only emitted for dispatchable hooks
|
|
673
|
+
// with an installed compiled runtime; otherwise the node path is unchanged.
|
|
674
|
+
const hookSub = hookSubcommandFor(binName);
|
|
675
|
+
const compiledWp = hookSub ? resolveCompiledWpBinary(repoRoot) : undefined;
|
|
676
|
+
const compiledPreamble = compiledWp !== undefined
|
|
677
|
+
? `WP_BIN=${quoteShell(compiledWp)}
|
|
678
|
+
if [ -x "$WP_BIN" ]; then
|
|
679
|
+
exec "$WP_BIN" hook ${hookSub} "$@"
|
|
680
|
+
fi
|
|
681
|
+
|
|
682
|
+
`
|
|
683
|
+
: '';
|
|
631
684
|
return `#!/bin/sh
|
|
632
|
-
NODE_BINARY=${nodeBinary}
|
|
685
|
+
${compiledPreamble}NODE_BINARY=${nodeBinary}
|
|
633
686
|
PROJECT_BIN_PATH=${projectBinPath}
|
|
634
687
|
FALLBACK_BIN_PATH=${fallbackBinPath}
|
|
635
688
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `omx` scaffolder preset.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Refreshes Vite+ (`vp`), ensures `omx` is installed, then chains
|
|
5
|
+
* `omx setup --yes --scope user` after the webpresso scaffold completes.
|
|
6
|
+
* OMX (oh-my-codex) is the operator-workflow
|
|
6
7
|
* execution layer; it manages its own scaffolding idempotently.
|
|
7
8
|
*
|
|
8
9
|
* Required when downstream features rely on `omx team` (see
|
|
@@ -55,7 +56,8 @@ type OmxSetupScope = 'user' | 'project';
|
|
|
55
56
|
export declare function deduplicateCodexHookTrustState(config: string): string;
|
|
56
57
|
export declare function migrateDeprecatedCodexHooksFeatureFlag(raw: string): string;
|
|
57
58
|
/**
|
|
58
|
-
*
|
|
59
|
+
* Refresh `vp`, ensure `omx` is on PATH, then run
|
|
60
|
+
* `omx setup --yes --scope user` in the consumer repo.
|
|
59
61
|
* Idempotent: safe to run on every `wp setup`.
|
|
60
62
|
*/
|
|
61
63
|
export declare function ensureOmx(input: EnsureOmxInput): EnsureOmxResult;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `omx` scaffolder preset.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Refreshes Vite+ (`vp`), ensures `omx` is installed, then chains
|
|
5
|
+
* `omx setup --yes --scope user` after the webpresso scaffold completes.
|
|
6
|
+
* OMX (oh-my-codex) is the operator-workflow
|
|
6
7
|
* execution layer; it manages its own scaffolding idempotently.
|
|
7
8
|
*
|
|
8
9
|
* Required when downstream features rely on `omx team` (see
|
|
@@ -14,7 +15,7 @@ import { homedir } from 'node:os';
|
|
|
14
15
|
import { dirname, join, resolve, sep } from 'node:path';
|
|
15
16
|
import { defaultCodexHooksPathFromConfig, normalizeGlobalCodexHooksFile, resolveBinaryOnPath, } from '#cli/commands/init/scaffolders/agent-hooks/codex-global-normalize';
|
|
16
17
|
const NOT_FOUND_HINT = 'omx (oh-my-codex) is not on PATH after `vp install -g oh-my-codex`. Install it manually and re-run.';
|
|
17
|
-
function
|
|
18
|
+
function shouldSkipManagedToolRefresh(env = process.env) {
|
|
18
19
|
return env.WP_SKIP_UPDATE_CHECK === '1';
|
|
19
20
|
}
|
|
20
21
|
function defaultCodexConfigPath() {
|
|
@@ -190,7 +191,8 @@ function pruneEmptyProjectScopedDirs(repoRoot, relativeFile) {
|
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
/**
|
|
193
|
-
*
|
|
194
|
+
* Refresh `vp`, ensure `omx` is on PATH, then run
|
|
195
|
+
* `omx setup --yes --scope user` in the consumer repo.
|
|
194
196
|
* Idempotent: safe to run on every `wp setup`.
|
|
195
197
|
*/
|
|
196
198
|
export function ensureOmx(input) {
|
|
@@ -210,6 +212,9 @@ export function ensureOmx(input) {
|
|
|
210
212
|
}
|
|
211
213
|
}
|
|
212
214
|
let installed = false;
|
|
215
|
+
if (!shouldSkipManagedToolRefresh()) {
|
|
216
|
+
spawn('vp', ['upgrade'], { stdio: 'inherit' });
|
|
217
|
+
}
|
|
213
218
|
let probe = spawn('omx', ['--version'], { encoding: 'utf8' });
|
|
214
219
|
if (probe.error || (probe.status !== null && probe.status !== 0)) {
|
|
215
220
|
const install = spawn('vp', ['install', '-g', 'oh-my-codex'], { stdio: 'inherit' });
|
|
@@ -222,7 +227,7 @@ export function ensureOmx(input) {
|
|
|
222
227
|
return { kind: 'omx-not-found', hint: NOT_FOUND_HINT };
|
|
223
228
|
}
|
|
224
229
|
}
|
|
225
|
-
else if (!
|
|
230
|
+
else if (!shouldSkipManagedToolRefresh()) {
|
|
226
231
|
spawn('vp', ['update', '-g', 'oh-my-codex'], { stdio: 'inherit' });
|
|
227
232
|
}
|
|
228
233
|
const result = spawn('omx', ['setup', '--yes', '--scope', scope], {
|
|
@@ -17,8 +17,8 @@ export declare const agentsFrontmatter: z.ZodObject<{
|
|
|
17
17
|
monitoring: "monitoring";
|
|
18
18
|
resolved: "resolved";
|
|
19
19
|
open: "open";
|
|
20
|
-
deprecated: "deprecated";
|
|
21
20
|
current: "current";
|
|
21
|
+
deprecated: "deprecated";
|
|
22
22
|
complete: "complete";
|
|
23
23
|
active: "active";
|
|
24
24
|
review: "review";
|
|
@@ -57,8 +57,8 @@ export declare const agentEntryFrontmatter: z.ZodObject<{
|
|
|
57
57
|
monitoring: "monitoring";
|
|
58
58
|
resolved: "resolved";
|
|
59
59
|
open: "open";
|
|
60
|
-
deprecated: "deprecated";
|
|
61
60
|
current: "current";
|
|
61
|
+
deprecated: "deprecated";
|
|
62
62
|
complete: "complete";
|
|
63
63
|
active: "active";
|
|
64
64
|
review: "review";
|
|
@@ -18,8 +18,8 @@ export declare const auditFrontmatter: z.ZodObject<{
|
|
|
18
18
|
monitoring: "monitoring";
|
|
19
19
|
resolved: "resolved";
|
|
20
20
|
open: "open";
|
|
21
|
-
deprecated: "deprecated";
|
|
22
21
|
current: "current";
|
|
22
|
+
deprecated: "deprecated";
|
|
23
23
|
complete: "complete";
|
|
24
24
|
active: "active";
|
|
25
25
|
review: "review";
|
|
@@ -25,8 +25,8 @@ export declare const baseFrontmatter: z.ZodObject<{
|
|
|
25
25
|
monitoring: "monitoring";
|
|
26
26
|
resolved: "resolved";
|
|
27
27
|
open: "open";
|
|
28
|
-
deprecated: "deprecated";
|
|
29
28
|
current: "current";
|
|
29
|
+
deprecated: "deprecated";
|
|
30
30
|
complete: "complete";
|
|
31
31
|
active: "active";
|
|
32
32
|
review: "review";
|
|
@@ -19,8 +19,8 @@ export declare const cookbookFrontmatter: z.ZodObject<{
|
|
|
19
19
|
monitoring: "monitoring";
|
|
20
20
|
resolved: "resolved";
|
|
21
21
|
open: "open";
|
|
22
|
-
deprecated: "deprecated";
|
|
23
22
|
current: "current";
|
|
23
|
+
deprecated: "deprecated";
|
|
24
24
|
complete: "complete";
|
|
25
25
|
active: "active";
|
|
26
26
|
review: "review";
|
|
@@ -18,8 +18,8 @@ export declare const coreFrontmatter: z.ZodObject<{
|
|
|
18
18
|
monitoring: "monitoring";
|
|
19
19
|
resolved: "resolved";
|
|
20
20
|
open: "open";
|
|
21
|
-
deprecated: "deprecated";
|
|
22
21
|
current: "current";
|
|
22
|
+
deprecated: "deprecated";
|
|
23
23
|
complete: "complete";
|
|
24
24
|
active: "active";
|
|
25
25
|
review: "review";
|
|
@@ -54,8 +54,8 @@ export declare const readmeFrontmatter: z.ZodObject<{
|
|
|
54
54
|
monitoring: "monitoring";
|
|
55
55
|
resolved: "resolved";
|
|
56
56
|
open: "open";
|
|
57
|
-
deprecated: "deprecated";
|
|
58
57
|
current: "current";
|
|
58
|
+
deprecated: "deprecated";
|
|
59
59
|
complete: "complete";
|
|
60
60
|
active: "active";
|
|
61
61
|
review: "review";
|
|
@@ -88,8 +88,8 @@ export declare const securityFrontmatter: z.ZodObject<{
|
|
|
88
88
|
monitoring: "monitoring";
|
|
89
89
|
resolved: "resolved";
|
|
90
90
|
open: "open";
|
|
91
|
-
deprecated: "deprecated";
|
|
92
91
|
current: "current";
|
|
92
|
+
deprecated: "deprecated";
|
|
93
93
|
complete: "complete";
|
|
94
94
|
active: "active";
|
|
95
95
|
review: "review";
|
|
@@ -169,8 +169,8 @@ export declare const agentsFrontmatter: z.ZodObject<{
|
|
|
169
169
|
monitoring: "monitoring";
|
|
170
170
|
resolved: "resolved";
|
|
171
171
|
open: "open";
|
|
172
|
-
deprecated: "deprecated";
|
|
173
172
|
current: "current";
|
|
173
|
+
deprecated: "deprecated";
|
|
174
174
|
complete: "complete";
|
|
175
175
|
active: "active";
|
|
176
176
|
review: "review";
|
|
@@ -19,8 +19,8 @@ export declare const evaluationFrontmatter: z.ZodObject<{
|
|
|
19
19
|
monitoring: "monitoring";
|
|
20
20
|
resolved: "resolved";
|
|
21
21
|
open: "open";
|
|
22
|
-
deprecated: "deprecated";
|
|
23
22
|
current: "current";
|
|
23
|
+
deprecated: "deprecated";
|
|
24
24
|
complete: "complete";
|
|
25
25
|
active: "active";
|
|
26
26
|
review: "review";
|
|
@@ -38,8 +38,8 @@ export declare const planArtifactFrontmatter: z.ZodObject<{
|
|
|
38
38
|
monitoring: "monitoring";
|
|
39
39
|
resolved: "resolved";
|
|
40
40
|
open: "open";
|
|
41
|
-
deprecated: "deprecated";
|
|
42
41
|
current: "current";
|
|
42
|
+
deprecated: "deprecated";
|
|
43
43
|
complete: "complete";
|
|
44
44
|
active: "active";
|
|
45
45
|
review: "review";
|
|
@@ -79,8 +79,8 @@ export declare const planReportFrontmatter: z.ZodObject<{
|
|
|
79
79
|
monitoring: "monitoring";
|
|
80
80
|
resolved: "resolved";
|
|
81
81
|
open: "open";
|
|
82
|
-
deprecated: "deprecated";
|
|
83
82
|
current: "current";
|
|
83
|
+
deprecated: "deprecated";
|
|
84
84
|
complete: "complete";
|
|
85
85
|
active: "active";
|
|
86
86
|
review: "review";
|
|
@@ -18,8 +18,8 @@ export declare const ruleFrontmatter: z.ZodObject<{
|
|
|
18
18
|
monitoring: "monitoring";
|
|
19
19
|
resolved: "resolved";
|
|
20
20
|
open: "open";
|
|
21
|
-
deprecated: "deprecated";
|
|
22
21
|
current: "current";
|
|
22
|
+
deprecated: "deprecated";
|
|
23
23
|
complete: "complete";
|
|
24
24
|
active: "active";
|
|
25
25
|
review: "review";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readConfig } from '#cli/commands/init/config';
|
|
2
2
|
import { getCommand, isBashInput } from '#hooks/shared/types';
|
|
3
|
+
import { AUDIT_KINDS } from '#mcp/tools/_shared/audit-kinds';
|
|
3
4
|
import { createSkipResult } from './skip-result.js';
|
|
4
5
|
import { buildRedirectMessage } from './mcp-redirect.js';
|
|
5
6
|
export const VALIDATOR_NAME = 'forbidden-commands';
|
|
@@ -445,6 +446,57 @@ export function createAuditResult(command, rule, options = {}) {
|
|
|
445
446
|
matchedPattern: rule.pattern.source,
|
|
446
447
|
};
|
|
447
448
|
}
|
|
449
|
+
const AUDIT_KIND_SET = new Set(AUDIT_KINDS);
|
|
450
|
+
const WP_AUDIT_RE = /^wp\s+audit\s+([a-z0-9-]+)\b/u;
|
|
451
|
+
const SCRIPT_INVOCATION_RE = /^(?:pnpm run|vp run|npm run|pnpm|npm)\s+([A-Za-z0-9:_-]+)/u;
|
|
452
|
+
const RAW_PM_RE = /^(?:pnpm|npm)\b/u;
|
|
453
|
+
function loadGuardConfig() {
|
|
454
|
+
const repoRoot = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
455
|
+
return readConfig(repoRoot)?.guard;
|
|
456
|
+
}
|
|
457
|
+
/** Build a guard redirect result; non-blocking ("[AUDIT] Would block") in audit mode. */
|
|
458
|
+
function guardRedirect(message) {
|
|
459
|
+
if (process.env[AUDIT_MODE_ENV] === '1') {
|
|
460
|
+
return { validator: VALIDATOR_NAME, passed: true, message: `[AUDIT] Would block:\n${message}` };
|
|
461
|
+
}
|
|
462
|
+
return { validator: VALIDATOR_NAME, passed: false, message };
|
|
463
|
+
}
|
|
464
|
+
/** `wp audit <kind>` (CLI) → `wp_audit(kind=...)` (MCP). Generic; not gated on config. */
|
|
465
|
+
function findWpAuditRedirect(command) {
|
|
466
|
+
for (const variant of getCommandVariants(command)) {
|
|
467
|
+
const kind = WP_AUDIT_RE.exec(variant)?.[1];
|
|
468
|
+
if (kind && AUDIT_KIND_SET.has(kind)) {
|
|
469
|
+
return `"${variant}" denied — use the MCP audit tool: mcp__webpresso__wp_audit(kind="${kind}"). Returns structured, summary-first results.`;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return undefined;
|
|
473
|
+
}
|
|
474
|
+
/** Repo-declared `guard.scriptRoutes`: a package script mapped to an audit kind. */
|
|
475
|
+
function findScriptRouteRedirect(command, routes) {
|
|
476
|
+
for (const variant of getCommandVariants(command)) {
|
|
477
|
+
const script = SCRIPT_INVOCATION_RE.exec(variant)?.[1];
|
|
478
|
+
if (!script)
|
|
479
|
+
continue;
|
|
480
|
+
const kind = routes[script];
|
|
481
|
+
if (!kind)
|
|
482
|
+
continue;
|
|
483
|
+
if (!AUDIT_KIND_SET.has(kind)) {
|
|
484
|
+
process.stderr.write(`[forbidden-commands] guard.scriptRoutes["${script}"] -> "${kind}" is not a known audit kind; ignoring\n`);
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
return `"${variant}" denied — this repo routes \`${script}\` to an audit: mcp__webpresso__wp_audit(kind="${kind}").`;
|
|
488
|
+
}
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
/** `guard.packageManager: 'vp-only'`: route any remaining raw pnpm/npm to the vp facade. */
|
|
492
|
+
function findVpOnlyRedirect(command) {
|
|
493
|
+
for (const variant of getCommandVariants(command)) {
|
|
494
|
+
if (RAW_PM_RE.test(variant)) {
|
|
495
|
+
return `"${variant}" denied — this repo is vp-only. Use the vp facade (\`vp install\`, \`vp run <script>\`, \`vp exec <bin>\`) or the matching wp_* MCP tool.`;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
448
500
|
export function validateForbiddenCommands(input) {
|
|
449
501
|
if (process.env[SKIP_ENV_VAR] === '1')
|
|
450
502
|
return createSkipResult(VALIDATOR_NAME);
|
|
@@ -459,6 +511,20 @@ export function validateForbiddenCommands(input) {
|
|
|
459
511
|
return createAuditResult(command, rule);
|
|
460
512
|
return createBlockedResult(command, rule);
|
|
461
513
|
}
|
|
514
|
+
const wpAuditRedirect = findWpAuditRedirect(command);
|
|
515
|
+
if (wpAuditRedirect)
|
|
516
|
+
return guardRedirect(wpAuditRedirect);
|
|
517
|
+
const guard = loadGuardConfig();
|
|
518
|
+
if (guard?.scriptRoutes) {
|
|
519
|
+
const redirect = findScriptRouteRedirect(command, guard.scriptRoutes);
|
|
520
|
+
if (redirect)
|
|
521
|
+
return guardRedirect(redirect);
|
|
522
|
+
}
|
|
523
|
+
if (guard?.packageManager === 'vp-only') {
|
|
524
|
+
const redirect = findVpOnlyRedirect(command);
|
|
525
|
+
if (redirect)
|
|
526
|
+
return guardRedirect(redirect);
|
|
527
|
+
}
|
|
462
528
|
return { validator: VALIDATOR_NAME, passed: true };
|
|
463
529
|
}
|
|
464
530
|
//# sourceMappingURL=forbidden-commands.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical list of `wp_audit` kinds — single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* Imported by the `wp_audit` MCP tool (for its `kind` enum + dispatch) and by
|
|
5
|
+
* the pretool-guard (to validate repo `guard.scriptRoutes` targets and to
|
|
6
|
+
* redirect `wp audit <kind>` CLI calls to the MCP tool). Kept as a tiny
|
|
7
|
+
* dependency-free module so the hook runtime, which runs on every tool call,
|
|
8
|
+
* doesn't pull in the whole audit tool graph.
|
|
9
|
+
*/
|
|
10
|
+
export declare const AUDIT_KINDS: readonly ["tph", "tph-e2e", "agents", "catalog-drift", "package-surface", "docs-frontmatter", "blueprint-lifecycle", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "hook-surface", "ai-contracts", "no-relative-package-scripts", "toolchain-isolation"];
|
|
11
|
+
export type AuditKind = (typeof AUDIT_KINDS)[number];
|
|
12
|
+
export declare function isAuditKind(value: string): value is AuditKind;
|
|
13
|
+
//# sourceMappingURL=audit-kinds.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical list of `wp_audit` kinds — single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* Imported by the `wp_audit` MCP tool (for its `kind` enum + dispatch) and by
|
|
5
|
+
* the pretool-guard (to validate repo `guard.scriptRoutes` targets and to
|
|
6
|
+
* redirect `wp audit <kind>` CLI calls to the MCP tool). Kept as a tiny
|
|
7
|
+
* dependency-free module so the hook runtime, which runs on every tool call,
|
|
8
|
+
* doesn't pull in the whole audit tool graph.
|
|
9
|
+
*/
|
|
10
|
+
export const AUDIT_KINDS = [
|
|
11
|
+
'tph',
|
|
12
|
+
'tph-e2e',
|
|
13
|
+
'agents',
|
|
14
|
+
'catalog-drift',
|
|
15
|
+
'package-surface',
|
|
16
|
+
'docs-frontmatter',
|
|
17
|
+
'blueprint-lifecycle',
|
|
18
|
+
'architecture-drift',
|
|
19
|
+
'cloudflare-deploy-contract',
|
|
20
|
+
'absolute-path-policy',
|
|
21
|
+
'no-first-party-mjs',
|
|
22
|
+
'roadmap-links',
|
|
23
|
+
'bundle-budget',
|
|
24
|
+
'commit-message',
|
|
25
|
+
'tech-debt',
|
|
26
|
+
'hook-surface',
|
|
27
|
+
'ai-contracts',
|
|
28
|
+
'no-relative-package-scripts',
|
|
29
|
+
'toolchain-isolation',
|
|
30
|
+
];
|
|
31
|
+
export function isAuditKind(value) {
|
|
32
|
+
return AUDIT_KINDS.includes(value);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=audit-kinds.js.map
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
* `oxlint` spawned with the inherited cwd would lint the wrong tree.
|
|
9
9
|
*
|
|
10
10
|
* Resolution order, first hit wins:
|
|
11
|
-
* 1. `
|
|
12
|
-
* 2.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* 1. `explicitCwd` — caller says "use exactly this", no walk.
|
|
12
|
+
* 2. An explicitly-passed `cwd` walked up to a marker (`.git`,
|
|
13
|
+
* `pnpm-workspace.yaml`, then `package.json`). A deliberate caller cwd
|
|
14
|
+
* outranks `CLAUDE_PROJECT_DIR`, which for a plugin-scope MCP server is
|
|
15
|
+
* the whole session/workspace root. If the passed cwd has no marker, fall
|
|
16
|
+
* back to `CLAUDE_PROJECT_DIR`, then throw — do not widen to `process.cwd()`.
|
|
17
|
+
* 3. `CLAUDE_PROJECT_DIR` env var (when no explicit cwd was passed).
|
|
18
|
+
* 4. Walk up from `process.cwd()` looking for a marker.
|
|
19
|
+
* 5. Loud throw — diagnosing a wrong-tree lint silently is worse than
|
|
15
20
|
* forcing the caller to pass an explicit cwd.
|
|
16
21
|
*
|
|
17
22
|
* The walk searches `.git` and `pnpm-workspace.yaml` *before* `package.json`
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
* `oxlint` spawned with the inherited cwd would lint the wrong tree.
|
|
9
9
|
*
|
|
10
10
|
* Resolution order, first hit wins:
|
|
11
|
-
* 1. `
|
|
12
|
-
* 2.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* 1. `explicitCwd` — caller says "use exactly this", no walk.
|
|
12
|
+
* 2. An explicitly-passed `cwd` walked up to a marker (`.git`,
|
|
13
|
+
* `pnpm-workspace.yaml`, then `package.json`). A deliberate caller cwd
|
|
14
|
+
* outranks `CLAUDE_PROJECT_DIR`, which for a plugin-scope MCP server is
|
|
15
|
+
* the whole session/workspace root. If the passed cwd has no marker, fall
|
|
16
|
+
* back to `CLAUDE_PROJECT_DIR`, then throw — do not widen to `process.cwd()`.
|
|
17
|
+
* 3. `CLAUDE_PROJECT_DIR` env var (when no explicit cwd was passed).
|
|
18
|
+
* 4. Walk up from `process.cwd()` looking for a marker.
|
|
19
|
+
* 5. Loud throw — diagnosing a wrong-tree lint silently is worse than
|
|
15
20
|
* forcing the caller to pass an explicit cwd.
|
|
16
21
|
*
|
|
17
22
|
* The walk searches `.git` and `pnpm-workspace.yaml` *before* `package.json`
|
|
@@ -42,20 +47,39 @@ function walkUp(start, markers) {
|
|
|
42
47
|
}
|
|
43
48
|
return null;
|
|
44
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Walk up from `start` to the nearest project root. Strong markers
|
|
52
|
+
* (`.git`, `pnpm-workspace.yaml`) anchor at the workspace root in preference to
|
|
53
|
+
* a closer weak marker (`package.json`) in a nested package dir.
|
|
54
|
+
*/
|
|
55
|
+
function walkToProjectRoot(start) {
|
|
56
|
+
return walkUp(start, STRONG_MARKERS) ?? walkUp(start, WEWP_MARKERS);
|
|
57
|
+
}
|
|
45
58
|
export function resolveProjectRoot(options = {}) {
|
|
46
59
|
if (options.explicitCwd)
|
|
47
60
|
return options.explicitCwd;
|
|
48
61
|
const env = options.env ?? process.env;
|
|
49
62
|
const fromEnv = env.CLAUDE_PROJECT_DIR;
|
|
63
|
+
// A caller-supplied cwd is a deliberate "scope to this project" signal and
|
|
64
|
+
// must outrank the ambient CLAUDE_PROJECT_DIR — for a plugin-scope MCP server
|
|
65
|
+
// that env var is the whole session/workspace root, so without this an
|
|
66
|
+
// explicit `wp_lint`/`wp_test` cwd would scan every sibling repo. Anchor at
|
|
67
|
+
// the cwd's project root; if it has no marker, defer to CLAUDE_PROJECT_DIR,
|
|
68
|
+
// then throw — never silently widen the search to process.cwd().
|
|
69
|
+
if (options.cwd) {
|
|
70
|
+
const fromCwd = walkToProjectRoot(options.cwd);
|
|
71
|
+
if (fromCwd)
|
|
72
|
+
return fromCwd;
|
|
73
|
+
if (fromEnv && fromEnv.length > 0)
|
|
74
|
+
return fromEnv;
|
|
75
|
+
throw new ProjectRootNotFoundError(options.cwd);
|
|
76
|
+
}
|
|
50
77
|
if (fromEnv && fromEnv.length > 0)
|
|
51
78
|
return fromEnv;
|
|
52
|
-
const start =
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
56
|
-
const fromWeak = walkUp(start, WEWP_MARKERS);
|
|
57
|
-
if (fromWeak)
|
|
58
|
-
return fromWeak;
|
|
79
|
+
const start = process.cwd();
|
|
80
|
+
const fromCwd = walkToProjectRoot(start);
|
|
81
|
+
if (fromCwd)
|
|
82
|
+
return fromCwd;
|
|
59
83
|
throw new ProjectRootNotFoundError(start);
|
|
60
84
|
}
|
|
61
85
|
//# sourceMappingURL=project-root.js.map
|
|
@@ -9,8 +9,8 @@ export declare const transformMetadataSchema: z.ZodObject<{
|
|
|
9
9
|
toolName: z.ZodString;
|
|
10
10
|
normalizedToolName: z.ZodString;
|
|
11
11
|
tier: z.ZodEnum<{
|
|
12
|
-
registered: "registered";
|
|
13
12
|
passthrough: "passthrough";
|
|
13
|
+
registered: "registered";
|
|
14
14
|
}>;
|
|
15
15
|
rawBytes: z.ZodNumber;
|
|
16
16
|
}, z.core.$strip>;
|
|
@@ -38,8 +38,8 @@ export declare const summaryFirstResultSchema: z.ZodObject<{
|
|
|
38
38
|
toolName: z.ZodString;
|
|
39
39
|
normalizedToolName: z.ZodString;
|
|
40
40
|
tier: z.ZodEnum<{
|
|
41
|
-
registered: "registered";
|
|
42
41
|
passthrough: "passthrough";
|
|
42
|
+
registered: "registered";
|
|
43
43
|
}>;
|
|
44
44
|
rawBytes: z.ZodNumber;
|
|
45
45
|
}, z.core.$strip>>;
|
|
@@ -24,17 +24,19 @@ declare const inputSchema: z.ZodObject<{
|
|
|
24
24
|
"tech-debt": "tech-debt";
|
|
25
25
|
tph: "tph";
|
|
26
26
|
"tph-e2e": "tph-e2e";
|
|
27
|
-
"bundle-budget": "bundle-budget";
|
|
28
|
-
"commit-message": "commit-message";
|
|
29
|
-
"blueprint-lifecycle": "blueprint-lifecycle";
|
|
30
|
-
"roadmap-links": "roadmap-links";
|
|
31
|
-
"docs-frontmatter": "docs-frontmatter";
|
|
32
27
|
"catalog-drift": "catalog-drift";
|
|
33
28
|
"package-surface": "package-surface";
|
|
29
|
+
"docs-frontmatter": "docs-frontmatter";
|
|
30
|
+
"blueprint-lifecycle": "blueprint-lifecycle";
|
|
34
31
|
"architecture-drift": "architecture-drift";
|
|
35
32
|
"cloudflare-deploy-contract": "cloudflare-deploy-contract";
|
|
36
33
|
"absolute-path-policy": "absolute-path-policy";
|
|
34
|
+
"no-first-party-mjs": "no-first-party-mjs";
|
|
35
|
+
"roadmap-links": "roadmap-links";
|
|
36
|
+
"bundle-budget": "bundle-budget";
|
|
37
|
+
"commit-message": "commit-message";
|
|
37
38
|
"ai-contracts": "ai-contracts";
|
|
39
|
+
"toolchain-isolation": "toolchain-isolation";
|
|
38
40
|
}>;
|
|
39
41
|
cwd: z.ZodOptional<z.ZodString>;
|
|
40
42
|
directory: z.ZodOptional<z.ZodString>;
|
|
@@ -18,26 +18,9 @@ import { spawn } from 'node:child_process';
|
|
|
18
18
|
import { z } from 'zod';
|
|
19
19
|
import { resolveAuditScriptPath } from '#audit/resolve-audit-script';
|
|
20
20
|
import { applyOutputTransform } from '#output-transforms/index';
|
|
21
|
+
import { AUDIT_KINDS } from './_shared/audit-kinds.js';
|
|
21
22
|
import { createSummaryOutputSchema, createSummaryResult } from './_shared/result.js';
|
|
22
|
-
const KINDS =
|
|
23
|
-
'tph',
|
|
24
|
-
'tph-e2e',
|
|
25
|
-
'agents',
|
|
26
|
-
'catalog-drift',
|
|
27
|
-
'package-surface',
|
|
28
|
-
'docs-frontmatter',
|
|
29
|
-
'blueprint-lifecycle',
|
|
30
|
-
'architecture-drift',
|
|
31
|
-
'cloudflare-deploy-contract',
|
|
32
|
-
'absolute-path-policy',
|
|
33
|
-
'roadmap-links',
|
|
34
|
-
'bundle-budget',
|
|
35
|
-
'commit-message',
|
|
36
|
-
'tech-debt',
|
|
37
|
-
'hook-surface',
|
|
38
|
-
'ai-contracts',
|
|
39
|
-
'no-relative-package-scripts',
|
|
40
|
-
];
|
|
23
|
+
const KINDS = AUDIT_KINDS;
|
|
41
24
|
const inputSchema = z.object({
|
|
42
25
|
kind: z.enum(KINDS),
|
|
43
26
|
/** Working tree to run the audit against. Alias kept as `directory` for back-compat. */
|
|
@@ -176,6 +159,26 @@ async function dispatch(input) {
|
|
|
176
159
|
details: auditResult,
|
|
177
160
|
};
|
|
178
161
|
}
|
|
162
|
+
case 'no-first-party-mjs': {
|
|
163
|
+
const { auditNoFirstPartyMjs } = await import('#audit/no-first-party-mjs');
|
|
164
|
+
const auditResult = auditNoFirstPartyMjs(input.cwd ?? input.directory ?? process.cwd());
|
|
165
|
+
return {
|
|
166
|
+
passed: auditResult.ok,
|
|
167
|
+
summary: summarizeRepoAudit(kind, auditResult),
|
|
168
|
+
kind,
|
|
169
|
+
details: auditResult,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
case 'toolchain-isolation': {
|
|
173
|
+
const { auditToolchainIsolation } = await import('#audit/toolchain-isolation');
|
|
174
|
+
const auditResult = auditToolchainIsolation(input.cwd ?? input.directory ?? process.cwd());
|
|
175
|
+
return {
|
|
176
|
+
passed: auditResult.ok,
|
|
177
|
+
summary: summarizeRepoAudit(kind, auditResult),
|
|
178
|
+
kind,
|
|
179
|
+
details: auditResult,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
179
182
|
case 'roadmap-links': {
|
|
180
183
|
const { auditRoadmapLinks } = await import('#audit/roadmap-links');
|
|
181
184
|
const auditResult = auditRoadmapLinks(input.cwd ?? input.directory ?? process.cwd());
|
|
@@ -304,7 +307,7 @@ async function dispatch(input) {
|
|
|
304
307
|
}
|
|
305
308
|
const tool = {
|
|
306
309
|
name: 'wp_audit',
|
|
307
|
-
description: 'Run a packaged repo audit. `kind` selects the audit (tph, tph-e2e, catalog-drift, docs-frontmatter, blueprint-lifecycle, architecture-drift, absolute-path-policy, roadmap-links, bundle-budget, commit-message, tech-debt, hook-surface, package-surface, no-relative-package-scripts). Returns {passed, kind, details}.',
|
|
310
|
+
description: 'Run a packaged repo audit. `kind` selects the audit (tph, tph-e2e, catalog-drift, docs-frontmatter, blueprint-lifecycle, architecture-drift, absolute-path-policy, no-first-party-mjs, roadmap-links, bundle-budget, commit-message, tech-debt, hook-surface, package-surface, no-relative-package-scripts). Returns {passed, kind, details}.',
|
|
308
311
|
inputSchema,
|
|
309
312
|
outputSchema,
|
|
310
313
|
annotations: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpresso/agent-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -710,10 +710,10 @@
|
|
|
710
710
|
"@stryker-mutator/typescript-checker": "^9.6.1",
|
|
711
711
|
"@playwright/test": "^1.55.0",
|
|
712
712
|
"wrangler": "^4.50.0",
|
|
713
|
-
"@webpresso/agent-kit-runtime-darwin-arm64": "0.
|
|
714
|
-
"@webpresso/agent-kit-runtime-darwin-x64": "0.
|
|
715
|
-
"@webpresso/agent-kit-runtime-linux-x64": "0.
|
|
716
|
-
"@webpresso/agent-kit-runtime-linux-arm64": "0.
|
|
717
|
-
"@webpresso/agent-kit-runtime-windows-x64": "0.
|
|
713
|
+
"@webpresso/agent-kit-runtime-darwin-arm64": "0.27.0",
|
|
714
|
+
"@webpresso/agent-kit-runtime-darwin-x64": "0.27.0",
|
|
715
|
+
"@webpresso/agent-kit-runtime-linux-x64": "0.27.0",
|
|
716
|
+
"@webpresso/agent-kit-runtime-linux-arm64": "0.27.0",
|
|
717
|
+
"@webpresso/agent-kit-runtime-windows-x64": "0.27.0"
|
|
718
718
|
}
|
|
719
719
|
}
|