@webpresso/agent-kit 3.3.2 → 3.3.4
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/THIRD-PARTY-NOTICES.md +12 -10
- package/catalog/AGENTS.md.tpl +3 -5
- package/catalog/agent/rules/changeset-release.md +15 -4
- package/catalog/agent/rules/managed-tool-binaries.md +67 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +99 -1
- package/catalog/agent/rules/pre-implementation.md +36 -20
- package/catalog/agent/rules/rtk-routing.md +22 -16
- package/catalog/agent/skills/autopilot/SKILL.md +4 -3
- package/catalog/agent/skills/ultragoal/SKILL.md +11 -9
- package/catalog/agent/skills/verify/SKILL.md +12 -12
- package/catalog/compose/admin-blocks-registry.json +659 -0
- package/dist/esm/audit/hook-surface.js +5 -1
- package/dist/esm/audit/repo-guardrails.d.ts +8 -0
- package/dist/esm/audit/repo-guardrails.js +71 -1
- package/dist/esm/blueprint/core/parser.js +40 -16
- package/dist/esm/blueprint/core/validation/criteria.js +2 -1
- package/dist/esm/blueprint/execution/artifacts.js +8 -4
- package/dist/esm/blueprint/execution/frontmatter-keys.d.ts +17 -0
- package/dist/esm/blueprint/execution/frontmatter-keys.js +24 -0
- package/dist/esm/blueprint/execution/metadata.js +8 -4
- package/dist/esm/blueprint/lifecycle/audit.d.ts +21 -0
- package/dist/esm/blueprint/lifecycle/audit.js +89 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.js +148 -40
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +22 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +37 -1
- package/dist/esm/blueprint/markdown/blocked-line.d.ts +49 -0
- package/dist/esm/blueprint/markdown/blocked-line.js +63 -0
- package/dist/esm/blueprint/markdown/helpers.js +10 -4
- package/dist/esm/blueprint/tracked-document/parser.js +3 -3
- package/dist/esm/blueprint/trust/gate-row.d.ts +55 -0
- package/dist/esm/blueprint/trust/gate-row.js +113 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +17 -0
- package/dist/esm/blueprint/trust/promotion.js +19 -19
- package/dist/esm/build/atomic-file-copy.d.ts +32 -0
- package/dist/esm/build/atomic-file-copy.js +44 -0
- package/dist/esm/build/cli-mcp-parity.js +3 -2
- package/dist/esm/build/package-manifest.js +7 -2
- package/dist/esm/build/vendor-oxlint-plugins.d.ts +8 -0
- package/dist/esm/build/vendor-oxlint-plugins.js +53 -0
- package/dist/esm/cli/commands/audit-core.js +9 -1
- package/dist/esm/cli/commands/audit.js +16 -5
- package/dist/esm/cli/commands/blueprint/mutations.d.ts +15 -0
- package/dist/esm/cli/commands/blueprint/mutations.js +8 -2
- package/dist/esm/cli/commands/compile.d.ts +13 -0
- package/dist/esm/cli/commands/compile.js +182 -62
- package/dist/esm/cli/commands/format.js +17 -1
- package/dist/esm/cli/commands/gain/index.js +10 -3
- package/dist/esm/cli/commands/init/gitignore-patcher.js +6 -0
- package/dist/esm/cli/commands/init/index.js +29 -48
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +54 -4
- package/dist/esm/cli/commands/init/mcp-spec.js +126 -22
- package/dist/esm/cli/commands/init/merge.js +30 -10
- package/dist/esm/cli/commands/init/scaffold-agents-md.js +5 -7
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +56 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +180 -43
- package/dist/esm/cli/commands/init/scaffolders/rtk/index.d.ts +17 -5
- package/dist/esm/cli/commands/init/scaffolders/rtk/index.js +56 -75
- package/dist/esm/cli/commands/init/scaffolders/runtime-check/index.js +7 -6
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +12 -1
- package/dist/esm/cli/commands/init/untracked-collision.js +23 -1
- package/dist/esm/cli/commands/package-manager.js +8 -2
- package/dist/esm/cli/commands/pr-help.js +1 -1
- package/dist/esm/cli/commands/pr.js +18 -4
- package/dist/esm/cli/commands/review.js +60 -6
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +24 -2
- package/dist/esm/cli/commands/worktree/router-dispatch.js +80 -48
- package/dist/esm/cli/commands/worktree/router.js +5 -2
- package/dist/esm/cli/direct-provider-launch.js +12 -4
- package/dist/esm/compiler/flatten.d.ts +10 -1
- package/dist/esm/compiler/flatten.js +14 -4
- package/dist/esm/compose/admin-blocks-registry-integrity.d.ts +22 -0
- package/dist/esm/compose/admin-blocks-registry-integrity.js +90 -0
- package/dist/esm/compose/compose.d.ts +46 -0
- package/dist/esm/compose/compose.js +250 -0
- package/dist/esm/compose/escape-html.d.ts +5 -0
- package/dist/esm/compose/escape-html.js +12 -0
- package/dist/esm/compose/ops-report-render.d.ts +5 -0
- package/dist/esm/compose/ops-report-render.js +71 -0
- package/dist/esm/compose/ops-report-schema.d.ts +42 -0
- package/dist/esm/compose/ops-report-schema.js +60 -0
- package/dist/esm/compose/preview.d.ts +14 -0
- package/dist/esm/compose/preview.js +32 -0
- package/dist/esm/compose/registry-resolve.d.ts +64 -0
- package/dist/esm/compose/registry-resolve.js +200 -0
- package/dist/esm/config/oxlint/graphql-conventions.js +2 -5
- package/dist/esm/config/oxlint/import-hygiene.js +21 -8
- package/dist/esm/config/oxlint/oxlint-config.fixture.d.ts +1 -1
- package/dist/esm/config/oxlint/oxlint-config.fixture.js +2 -3
- package/dist/esm/config/oxlint/oxlintrc.d.ts +5 -7
- package/dist/esm/config/oxlint/oxlintrc.js +18 -21
- package/dist/esm/config/oxlint/path-roles.js +53 -0
- package/dist/esm/config/oxlint/query-patterns.js +2 -5
- package/dist/esm/daemon/domains/blueprint-exec-ledger.d.ts +86 -0
- package/dist/esm/daemon/domains/blueprint-exec-ledger.js +134 -0
- package/dist/esm/daemon/domains/blueprint-exec.d.ts +106 -0
- package/dist/esm/daemon/domains/blueprint-exec.js +161 -0
- package/dist/esm/daemon/domains/launch.d.ts +25 -0
- package/dist/esm/daemon/domains/launch.js +30 -1
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +12 -1
- package/dist/esm/daemon/handlers/blueprints-handlers.js +148 -6
- package/dist/esm/daemon/handlers/product.d.ts +10 -0
- package/dist/esm/daemon/handlers/product.js +10 -1
- package/dist/esm/daemon/methods.d.ts +21 -1
- package/dist/esm/daemon/methods.js +20 -0
- package/dist/esm/daemon/protocol/contract.d.ts +58 -0
- package/dist/esm/daemon/protocol/contract.js +38 -0
- package/dist/esm/daemon/server.js +7 -0
- package/dist/esm/docs-linter/blueprint-plan.js +2 -2
- package/dist/esm/errors/wp-error.js +21 -2
- package/dist/esm/git/changed-files.d.ts +20 -0
- package/dist/esm/git/changed-files.js +63 -0
- package/dist/esm/hooks/doctor.d.ts +11 -0
- package/dist/esm/hooks/doctor.js +114 -45
- package/dist/esm/hooks/pretool-guard/dev-routing.js +1 -1
- package/dist/esm/hooks/pretool-guard/rtk-rewrite.d.ts +24 -0
- package/dist/esm/hooks/pretool-guard/rtk-rewrite.js +50 -0
- package/dist/esm/hooks/pretool-guard/runner.js +6 -0
- package/dist/esm/hooks/status/index.js +1 -1
- package/dist/esm/hooks/stop/qa-changed-files.js +6 -1
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +39 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.d.ts +14 -7
- package/dist/esm/mcp/blueprint/handlers/review-log.js +46 -22
- package/dist/esm/mcp/blueprint/handlers/validate.js +6 -0
- package/dist/esm/mcp/blueprint/registration.js +1 -1
- package/dist/esm/mcp/tools/_names.d.ts +1 -1
- package/dist/esm/mcp/tools/_names.js +4 -0
- package/dist/esm/mcp/tools/_registry.js +8 -0
- package/dist/esm/mcp/tools/_shared/redact.d.ts +22 -0
- package/dist/esm/mcp/tools/_shared/redact.js +114 -8
- package/dist/esm/mcp/tools/gain.js +28 -2
- package/dist/esm/mcp/tools/release-progress.d.ts +9 -0
- package/dist/esm/mcp/tools/release-progress.js +110 -0
- package/dist/esm/mcp/tools/worktree.js +65 -3
- package/dist/esm/mcp/tools/wp-ui-blocks-list.d.ts +3 -0
- package/dist/esm/mcp/tools/wp-ui-blocks-list.js +43 -0
- package/dist/esm/mcp/tools/wp-ui-compose.d.ts +3 -0
- package/dist/esm/mcp/tools/wp-ui-compose.js +96 -0
- package/dist/esm/mcp/tools/wp-ui-preview.d.ts +3 -0
- package/dist/esm/mcp/tools/wp-ui-preview.js +59 -0
- package/dist/esm/package.json +2 -0
- package/dist/esm/review/lifecycle-writers.d.ts +76 -0
- package/dist/esm/review/lifecycle-writers.js +330 -0
- package/dist/esm/review/subject.js +66 -32
- package/dist/esm/status/snapshot.d.ts +5 -0
- package/dist/esm/status/snapshot.js +27 -2
- package/dist/esm/test/shard-durations.json +0 -1
- package/dist/esm/test-helpers/global-setup.js +38 -3
- package/dist/esm/tool-runtime/managed-rtk.d.ts +44 -0
- package/dist/esm/tool-runtime/managed-rtk.js +275 -0
- package/dist/esm/tool-runtime/resolve-runner.js +18 -194
- package/dist/esm/utils/package-root.d.ts +32 -0
- package/dist/esm/utils/package-root.js +82 -0
- package/dist/esm/worktrees/git-metadata.d.ts +41 -0
- package/dist/esm/worktrees/git-metadata.js +48 -0
- package/package.json +27 -21
- package/dist/esm/config/oxlint/code-safety.d.ts +0 -18
- package/dist/esm/config/oxlint/foundation-purity.d.ts +0 -21
- package/dist/esm/config/oxlint/graphql-conventions.d.ts +0 -23
- package/dist/esm/config/oxlint/import-hygiene.d.ts +0 -34
- package/dist/esm/config/oxlint/index.d.ts +0 -22
- package/dist/esm/config/oxlint/index.js +0 -28
- package/dist/esm/config/oxlint/monorepo-paths.d.ts +0 -22
- package/dist/esm/config/oxlint/query-patterns.d.ts +0 -24
- package/dist/esm/config/oxlint/testing-quality.d.ts +0 -35
- package/dist/esm/config/oxlint/tier-boundaries.d.ts +0 -35
- package/dist/esm/output-transforms/rulesync.d.ts +0 -2
- package/dist/esm/output-transforms/rulesync.js +0 -79
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { resolvePackageAssetPreferred } from "#utils/package-assets.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const REGISTRY_RELATIVE = join("generated", "frontend", "blocks", "registry.json");
|
|
7
|
+
const MAX_WALK = 48;
|
|
8
|
+
/**
|
|
9
|
+
* Local reader capability floor for project registries (agent-kit consumer).
|
|
10
|
+
* Bump in the same PR as catalog/compose/admin-blocks-registry.json major/minor
|
|
11
|
+
* advances. Does not import @webpresso/schema (SEA / nondeterminism lock).
|
|
12
|
+
*/
|
|
13
|
+
export const READER_REGISTRY_VERSION = "1.0.0";
|
|
14
|
+
/** Stop climbing past these markers (mirrors project-root clamps elsewhere). */
|
|
15
|
+
function isProjectBoundary(dir) {
|
|
16
|
+
return existsSync(join(dir, ".git")) || existsSync(join(dir, ".webpressorc.json"));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `~/.webpresso` is the managed-worktree host, never a product project root.
|
|
20
|
+
* Matching it would mis-label every uninitialized checkout under $HOME.
|
|
21
|
+
*/
|
|
22
|
+
function isManagedHostWebpresso(candidate) {
|
|
23
|
+
return resolve(candidate) === resolve(join(homedir(), ".webpresso"));
|
|
24
|
+
}
|
|
25
|
+
const blockDefinitionSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
name: z.string(),
|
|
28
|
+
view: z.enum(["list", "detail", "form"]),
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
props: z.array(z.unknown()).optional(),
|
|
31
|
+
variants: z.array(z.string()).optional(),
|
|
32
|
+
slots: z.array(z.unknown()).optional(),
|
|
33
|
+
permissions: z.array(z.unknown()).optional(),
|
|
34
|
+
deprecated: z.boolean().optional(),
|
|
35
|
+
})
|
|
36
|
+
.passthrough();
|
|
37
|
+
export const blockRegistrySchema = z.object({
|
|
38
|
+
$schema: z.string().optional(),
|
|
39
|
+
version: z.string(),
|
|
40
|
+
minVersion: z.string().optional(),
|
|
41
|
+
blocks: z.array(blockDefinitionSchema).min(1),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Walk up for nearest project `.webpresso` directory (not a file).
|
|
45
|
+
* Clamped at the enclosing project boundary (`.git` / `.webpressorc.json`)
|
|
46
|
+
* and never treats `~/.webpresso` (managed worktree host) as a project root.
|
|
47
|
+
*/
|
|
48
|
+
function walkUpForWebpressoDir(startDir) {
|
|
49
|
+
let dir = resolve(startDir);
|
|
50
|
+
for (let i = 0; i < MAX_WALK; i++) {
|
|
51
|
+
const candidate = join(dir, ".webpresso");
|
|
52
|
+
if (existsSync(candidate)) {
|
|
53
|
+
try {
|
|
54
|
+
if (statSync(candidate).isDirectory() && !isManagedHostWebpresso(candidate)) {
|
|
55
|
+
return candidate;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// ignore unreadable markers
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Do not climb past the repo / consumer root into $HOME tooling dirs.
|
|
63
|
+
if (isProjectBoundary(dir))
|
|
64
|
+
return null;
|
|
65
|
+
const parent = dirname(dir);
|
|
66
|
+
if (parent === dir)
|
|
67
|
+
return null;
|
|
68
|
+
dir = parent;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
export function defaultSnapshotPath() {
|
|
73
|
+
// Portable across src/ and dist/esm/ via package-root walk (no fixed-depth joins).
|
|
74
|
+
const assetPath = resolvePackageAssetPreferred(["catalog/compose/admin-blocks-registry.json"]);
|
|
75
|
+
if (!existsSync(assetPath)) {
|
|
76
|
+
throw new Error(`admin-blocks-registry.json snapshot not found at package asset path: ${assetPath}`);
|
|
77
|
+
}
|
|
78
|
+
return assetPath;
|
|
79
|
+
}
|
|
80
|
+
function loadRegistryFile(path) {
|
|
81
|
+
try {
|
|
82
|
+
const raw = readFileSync(path, "utf8");
|
|
83
|
+
const parsed = JSON.parse(raw);
|
|
84
|
+
return blockRegistrySchema.parse(parsed);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
88
|
+
throw new Error(`Failed to load block registry at ${path}: ${detail}`, { cause: error });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function parseSemver(version) {
|
|
92
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
|
|
93
|
+
if (!match)
|
|
94
|
+
return null;
|
|
95
|
+
return {
|
|
96
|
+
major: Number(match[1]),
|
|
97
|
+
minor: Number(match[2]),
|
|
98
|
+
patch: Number(match[3]),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Whether a loaded project registry is readable by this agent-kit consumer.
|
|
103
|
+
* - registry.version major must match the reader major
|
|
104
|
+
* - if registry.minVersion is set, reader must be >= that floor (semver order)
|
|
105
|
+
*/
|
|
106
|
+
export function isProjectRegistryCompatibleWithReader(registry, readerVersion = READER_REGISTRY_VERSION) {
|
|
107
|
+
const reader = parseSemver(readerVersion);
|
|
108
|
+
if (!reader) {
|
|
109
|
+
return { ok: false, reason: `reader version "${readerVersion}" is not semver` };
|
|
110
|
+
}
|
|
111
|
+
const produced = parseSemver(registry.version);
|
|
112
|
+
if (!produced) {
|
|
113
|
+
return { ok: false, reason: `registry version "${registry.version}" is not semver` };
|
|
114
|
+
}
|
|
115
|
+
if (produced.major !== reader.major) {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
reason: `registry major ${produced.major} incompatible with reader major ${reader.major} (${readerVersion})`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (registry.minVersion !== undefined) {
|
|
122
|
+
const min = parseSemver(registry.minVersion);
|
|
123
|
+
if (!min) {
|
|
124
|
+
return { ok: false, reason: `registry minVersion "${registry.minVersion}" is not semver` };
|
|
125
|
+
}
|
|
126
|
+
const belowMin = reader.major < min.major ||
|
|
127
|
+
(reader.major === min.major && reader.minor < min.minor) ||
|
|
128
|
+
(reader.major === min.major && reader.minor === min.minor && reader.patch < min.patch);
|
|
129
|
+
if (belowMin) {
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
reason: `reader ${readerVersion} is below registry minVersion ${registry.minVersion}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { ok: true };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Two-tier registry resolution (v0):
|
|
140
|
+
* 1. Nearest project `.webpresso/` (within git/webpressorc boundary) — if it has
|
|
141
|
+
* generated blocks registry, use it
|
|
142
|
+
* 2. Else catalog snapshot (with warning when that project `.webpresso/` lacks registry)
|
|
143
|
+
*
|
|
144
|
+
* Never dynamically imports @webpresso/schema.
|
|
145
|
+
* Never climbs past the project boundary into `~/.webpresso` (managed worktree host)
|
|
146
|
+
* or other ancestor projects (avoids silent wrong-project catalog).
|
|
147
|
+
*/
|
|
148
|
+
export function resolveBlockRegistry(options = {}) {
|
|
149
|
+
const start = options.cwd
|
|
150
|
+
? isAbsolute(options.cwd)
|
|
151
|
+
? options.cwd
|
|
152
|
+
: resolve(process.cwd(), options.cwd)
|
|
153
|
+
: process.cwd();
|
|
154
|
+
const webpressoRoot = walkUpForWebpressoDir(start);
|
|
155
|
+
const warnings = [];
|
|
156
|
+
if (webpressoRoot) {
|
|
157
|
+
const projectPath = join(webpressoRoot, REGISTRY_RELATIVE);
|
|
158
|
+
if (existsSync(projectPath)) {
|
|
159
|
+
try {
|
|
160
|
+
const registry = loadRegistryFile(projectPath);
|
|
161
|
+
const compat = isProjectRegistryCompatibleWithReader(registry);
|
|
162
|
+
if (!compat.ok) {
|
|
163
|
+
warnings.push(`Project registry at ${projectPath} incompatible with reader ${READER_REGISTRY_VERSION} (${compat.reason}); using catalog snapshot.`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
return {
|
|
167
|
+
registry,
|
|
168
|
+
source: "project",
|
|
169
|
+
registryVersion: registry.version,
|
|
170
|
+
path: projectPath,
|
|
171
|
+
warnings,
|
|
172
|
+
webpressoPresentWithoutRegistry: false,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
// Producer codegen can advance ahead of this consumer's schema; prefer
|
|
178
|
+
// snapshot over hard-failing MCP tools on version skew.
|
|
179
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
180
|
+
warnings.push(`Project registry at ${projectPath} failed validation (${detail}); using catalog snapshot. Regenerate or align registry schema.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const snapshotPath = options.snapshotPath ?? defaultSnapshotPath();
|
|
185
|
+
const registry = loadRegistryFile(snapshotPath);
|
|
186
|
+
if (webpressoRoot && warnings.length === 0) {
|
|
187
|
+
warnings.push(`Project has .webpresso/ at ${webpressoRoot} but no ${join(".webpresso", REGISTRY_RELATIVE)}; using catalog snapshot ${registry.version}. Run schema frontend codegen / converge to materialize a project registry.`);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
registry,
|
|
191
|
+
source: "snapshot",
|
|
192
|
+
registryVersion: registry.version,
|
|
193
|
+
path: snapshotPath,
|
|
194
|
+
warnings,
|
|
195
|
+
webpressoPresentWithoutRegistry: webpressoRoot !== null && !existsSync(join(webpressoRoot, REGISTRY_RELATIVE)),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function blockNamesForView(registry, view) {
|
|
199
|
+
return new Set(registry.blocks.filter((block) => block.view === view).map((block) => block.name));
|
|
200
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// causes runtime errors: "field 'user' not found in type: 'query_root'"
|
|
8
8
|
//
|
|
9
9
|
// This rule scans template literals and string literals for known singular patterns and reports them.
|
|
10
|
+
import { isWebAppSurface, normalizeFilename } from "./path-roles.js";
|
|
10
11
|
// Mapping of singular table patterns to their correct plural/actual GraphQL field names.
|
|
11
12
|
// Format: [singular, correct, description]
|
|
12
13
|
const SINGULAR_TABLE_RULES = [
|
|
@@ -76,9 +77,6 @@ const INLINE_GRAPHQL_EXEMPT_PATH_SEGMENTS = [
|
|
|
76
77
|
"/packages/sdk/schema-engine/src/emitters/",
|
|
77
78
|
"/packages/feature/app-core/src/daemon/",
|
|
78
79
|
];
|
|
79
|
-
function normalizeFilename(filename) {
|
|
80
|
-
return typeof filename === "string" ? filename.replaceAll("\\", "/") : "";
|
|
81
|
-
}
|
|
82
80
|
function getFilename(context) {
|
|
83
81
|
if (typeof context.getFilename === "function") {
|
|
84
82
|
return normalizeFilename(context.getFilename());
|
|
@@ -92,9 +90,8 @@ function isClientQuerySurface(filename) {
|
|
|
92
90
|
if (INLINE_GRAPHQL_EXEMPT_PATH_SEGMENTS.some((segment) => normalized.includes(segment))) {
|
|
93
91
|
return false;
|
|
94
92
|
}
|
|
95
|
-
const isWebAppSurface = normalized.includes("/apps/web/") && normalized.includes("/app/");
|
|
96
93
|
const isFeatureSurface = normalized.includes("/packages/feature/") && normalized.includes("/src/");
|
|
97
|
-
return isWebAppSurface || isFeatureSurface;
|
|
94
|
+
return isWebAppSurface(normalized) || isFeatureSurface;
|
|
98
95
|
}
|
|
99
96
|
function getNodeStart(node) {
|
|
100
97
|
if (Array.isArray(node?.range) && typeof node.range[0] === "number") {
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// - no-src-path-imports
|
|
5
5
|
// - no-relative-mock-paths
|
|
6
6
|
// - no-forbidden-package-imports
|
|
7
|
+
import { isAgentKitSeaTreeFile, isPlatformAppFile, isSdkPackageFile, normalizeFilename, } from "./path-roles.js";
|
|
8
|
+
export { isAgentKitSeaTreeFile, isPlatformAppFile, isSdkPackageFile, normalizeFilename, } from "./path-roles.js";
|
|
7
9
|
const CROSS_PACKAGE_SEGMENT = /(?:^|\/)(packages|apps|tooling|infra|webpresso)\//;
|
|
8
10
|
function isRelativeParentPath(source) {
|
|
9
11
|
return typeof source === "string" && source.includes("../");
|
|
@@ -32,9 +34,6 @@ function reportCrossPackageImport(context, node) {
|
|
|
32
34
|
message: "Do not reach into another package/app with a relative filesystem import. Add a proper package export and import it via the package name.",
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
|
-
function normalizeFilename(filename) {
|
|
36
|
-
return typeof filename === "string" ? filename.replaceAll("\\", "/") : "";
|
|
37
|
-
}
|
|
38
37
|
function getFilename(context) {
|
|
39
38
|
if (typeof context.getFilename === "function") {
|
|
40
39
|
return normalizeFilename(context.getFilename());
|
|
@@ -46,12 +45,16 @@ function checkStaticModuleSource(node, callback) {
|
|
|
46
45
|
return;
|
|
47
46
|
callback(node.source.value, node.source);
|
|
48
47
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return
|
|
48
|
+
/** Package imports forbidden in agent-kit SEA surfaces (schema + ui paint kits). */
|
|
49
|
+
export function isSeaForbiddenPackageImport(source) {
|
|
50
|
+
if (typeof source !== "string")
|
|
51
|
+
return false;
|
|
52
|
+
return (source === "@webpresso/schema" ||
|
|
53
|
+
source.startsWith("@webpresso/schema/") ||
|
|
54
|
+
source === "@webpresso/ui" ||
|
|
55
|
+
source.startsWith("@webpresso/ui/"));
|
|
54
56
|
}
|
|
57
|
+
export const SEA_FORBIDDEN_IMPORT_MESSAGE = "Agent-kit SEA surfaces (compose / wp_ui_*) must not import `@webpresso/schema` or `@webpresso/ui`. Use the two-tier block registry snapshot/project file and local Zod/HTML paint instead (offline / nondeterminism lock).";
|
|
55
58
|
function isSchemaEngineImport(source) {
|
|
56
59
|
return typeof source === "string" && source.startsWith("@webpresso/schema/engine/");
|
|
57
60
|
}
|
|
@@ -147,6 +150,16 @@ const noForbiddenPackageImports = {
|
|
|
147
150
|
function checkImport(source, node) {
|
|
148
151
|
if (typeof source !== "string")
|
|
149
152
|
return;
|
|
153
|
+
// Agent-kit SEA / offline compose — ban before monorepo platform allow rules.
|
|
154
|
+
// Consumers still get this rule via shipped oxlintrc; monorepo apps do not match
|
|
155
|
+
// isAgentKitSeaTreeFile so public schema imports remain allowed there.
|
|
156
|
+
if (isAgentKitSeaTreeFile(filename) && isSeaForbiddenPackageImport(source)) {
|
|
157
|
+
context.report({
|
|
158
|
+
node,
|
|
159
|
+
message: SEA_FORBIDDEN_IMPORT_MESSAGE,
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
150
163
|
if (isPlatformAppFile(filename) &&
|
|
151
164
|
isSchemaEngineImport(source) &&
|
|
152
165
|
source !== "@webpresso/schema/engine/api") {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { config } from "
|
|
1
|
+
import { config } from "@webpresso/agent-config/oxlint";
|
|
2
2
|
export default config;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { config } from "
|
|
2
|
-
//
|
|
3
|
-
// import { config } from 'webpresso/oxlint'
|
|
1
|
+
import { config } from "@webpresso/agent-config/oxlint";
|
|
2
|
+
// Shape parity for folded config consumers (agent-config is the SSOT).
|
|
4
3
|
export default config;
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compiled plugin module basenames shipped next to the generated oxlintrc.json.
|
|
3
|
-
*
|
|
4
|
-
* test in `oxlintrc.test.ts`, which asserts one entry per registered plugin).
|
|
3
|
+
* Must match agent-config policy plugins (guarded by vendor + parity tests).
|
|
5
4
|
*/
|
|
6
5
|
export declare const OXLINT_PLUGIN_BASENAMES: readonly ["code-safety", "foundation-purity", "graphql-conventions", "import-hygiene", "monorepo-paths", "query-patterns", "testing-quality", "tier-boundaries"];
|
|
7
6
|
/** jsPlugins specifiers, sibling-relative to the shipped oxlintrc.json. */
|
|
8
7
|
export declare const OXLINT_JS_PLUGIN_FILES: readonly string[];
|
|
9
8
|
/**
|
|
10
9
|
* Standard ignore patterns for every webpresso consumer: build output, vendored
|
|
11
|
-
* deps, generated reports, and the regenerated agent-surface directories.
|
|
12
|
-
* are project-relative. Truly repo-specific extras belong in a consumer-local
|
|
13
|
-
* `.oxlintignore`, never here.
|
|
10
|
+
* deps, generated reports, and the regenerated agent-surface directories.
|
|
14
11
|
*/
|
|
15
12
|
export declare const STANDARD_IGNORE_PATTERNS: readonly string[];
|
|
16
13
|
export interface ResolvedOxlintRc {
|
|
@@ -18,9 +15,10 @@ export interface ResolvedOxlintRc {
|
|
|
18
15
|
readonly rules: Readonly<Record<string, "error">>;
|
|
19
16
|
readonly ignorePatterns: readonly string[];
|
|
20
17
|
}
|
|
18
|
+
/** Rule map from agent-config SSOT (error severity for inject). */
|
|
19
|
+
export declare const rules: Readonly<Record<string, "error">>;
|
|
21
20
|
/**
|
|
22
21
|
* Build the resolved oxlint config object that ships as `oxlintrc.json`. Pure:
|
|
23
|
-
* the build-time generator serializes this return value verbatim
|
|
24
|
-
* parity test asserts it against the live `rules`/`plugins` exports.
|
|
22
|
+
* the build-time generator serializes this return value verbatim.
|
|
25
23
|
*/
|
|
26
24
|
export declare function buildOxlintrc(): ResolvedOxlintRc;
|
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolved, consumer-ready oxlint configuration (Tier-1 DRY model).
|
|
3
3
|
*
|
|
4
|
-
* agent-kit ships ONE resolved `.oxlintrc.json` — generated
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `oxlint.config.ts` and no `oxlint` dependency; the linter version, plugins,
|
|
8
|
-
* rules, and standard ignores are all gated to @webpresso/agent-kit.
|
|
4
|
+
* agent-kit ships ONE resolved `.oxlintrc.json` — generated at build time into
|
|
5
|
+
* `dist/esm/config/oxlint/oxlintrc.json` — and `wp lint` points oxlint at it via
|
|
6
|
+
* `--config` for consumers without a local oxlint config.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
8
|
+
* Plugin *bodies* are authored in `@webpresso/agent-config/oxlint/*` (consumer
|
|
9
|
+
* SSOT). Build vendors compiled sibling `.js` next to oxlintrc.json so inject
|
|
10
|
+
* needs no runtime agent-config dependency. Monorepos with their own lint
|
|
11
|
+
* config load `@webpresso/agent-config/oxlint/*` package names directly.
|
|
12
|
+
*
|
|
13
|
+
* Two oxlint resolution facts (verified against oxlint 1.67) make inject work:
|
|
14
|
+
* - `jsPlugins` are resolved relative to the CONFIG FILE's directory
|
|
15
|
+
* (`./<plugin>.js` siblings of the shipped JSON).
|
|
16
|
+
* - `ignorePatterns` apply relative to the LINTED project (cwd).
|
|
18
17
|
*/
|
|
19
|
-
import { rules } from "
|
|
18
|
+
import { rules as agentConfigRules } from "@webpresso/agent-config/oxlint";
|
|
20
19
|
/**
|
|
21
20
|
* Compiled plugin module basenames shipped next to the generated oxlintrc.json.
|
|
22
|
-
*
|
|
23
|
-
* test in `oxlintrc.test.ts`, which asserts one entry per registered plugin).
|
|
21
|
+
* Must match agent-config policy plugins (guarded by vendor + parity tests).
|
|
24
22
|
*/
|
|
25
23
|
export const OXLINT_PLUGIN_BASENAMES = [
|
|
26
24
|
"code-safety",
|
|
@@ -36,9 +34,7 @@ export const OXLINT_PLUGIN_BASENAMES = [
|
|
|
36
34
|
export const OXLINT_JS_PLUGIN_FILES = OXLINT_PLUGIN_BASENAMES.map((name) => `./${name}.js`);
|
|
37
35
|
/**
|
|
38
36
|
* Standard ignore patterns for every webpresso consumer: build output, vendored
|
|
39
|
-
* deps, generated reports, and the regenerated agent-surface directories.
|
|
40
|
-
* are project-relative. Truly repo-specific extras belong in a consumer-local
|
|
41
|
-
* `.oxlintignore`, never here.
|
|
37
|
+
* deps, generated reports, and the regenerated agent-surface directories.
|
|
42
38
|
*/
|
|
43
39
|
export const STANDARD_IGNORE_PATTERNS = [
|
|
44
40
|
"dist",
|
|
@@ -53,10 +49,11 @@ export const STANDARD_IGNORE_PATTERNS = [
|
|
|
53
49
|
".opencode",
|
|
54
50
|
".runtime",
|
|
55
51
|
];
|
|
52
|
+
/** Rule map from agent-config SSOT (error severity for inject). */
|
|
53
|
+
export const rules = agentConfigRules;
|
|
56
54
|
/**
|
|
57
55
|
* Build the resolved oxlint config object that ships as `oxlintrc.json`. Pure:
|
|
58
|
-
* the build-time generator serializes this return value verbatim
|
|
59
|
-
* parity test asserts it against the live `rules`/`plugins` exports.
|
|
56
|
+
* the build-time generator serializes this return value verbatim.
|
|
60
57
|
*/
|
|
61
58
|
export function buildOxlintrc() {
|
|
62
59
|
return {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Shared path classifiers for webpresso oxlint policy plugins.
|
|
4
|
+
* Pure functions only — no AST. Consumers resolve plugins from
|
|
5
|
+
* `@webpresso/agent-config/oxlint/*`; these markers must match real trees.
|
|
6
|
+
*/
|
|
7
|
+
/** Normalize Windows paths for substring role checks. */
|
|
8
|
+
export function normalizeFilename(filename) {
|
|
9
|
+
return typeof filename === "string" ? filename.replaceAll("\\", "/") : "";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Offline / SEA-shaped agent-kit surfaces (compose + UI MCP tools).
|
|
13
|
+
* Monorepo apps never use these path segments.
|
|
14
|
+
*/
|
|
15
|
+
export function isAgentKitSeaTreeFile(filename) {
|
|
16
|
+
const n = normalizeFilename(filename);
|
|
17
|
+
if (n.includes("/node_modules/"))
|
|
18
|
+
return false;
|
|
19
|
+
return n.includes("/src/compose/") || n.includes("/src/mcp/tools/wp-ui-");
|
|
20
|
+
}
|
|
21
|
+
/** Platform web apps: real monorepo layout + legacy `/apps/web/`. */
|
|
22
|
+
export function isPlatformWebFile(filename) {
|
|
23
|
+
const n = normalizeFilename(filename);
|
|
24
|
+
if (n.includes("/node_modules/"))
|
|
25
|
+
return false;
|
|
26
|
+
return n.includes("/apps/platform/web/") || n.includes("/apps/web/");
|
|
27
|
+
}
|
|
28
|
+
/** Platform workers: real monorepo layout + legacy `/apps/workers/`. */
|
|
29
|
+
export function isPlatformWorkerFile(filename) {
|
|
30
|
+
const n = normalizeFilename(filename);
|
|
31
|
+
if (n.includes("/node_modules/"))
|
|
32
|
+
return false;
|
|
33
|
+
return n.includes("/apps/platform/workers/") || n.includes("/apps/workers/");
|
|
34
|
+
}
|
|
35
|
+
/** Platform apps (web or worker) for import-hygiene schema-surface rules. */
|
|
36
|
+
export function isPlatformAppFile(filename) {
|
|
37
|
+
return isPlatformWebFile(filename) || isPlatformWorkerFile(filename);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Client UI surfaces for GraphQL/query-pattern rules.
|
|
41
|
+
* Real monorepo: apps/platform/web/.../app/; legacy apps/web/.../app/.
|
|
42
|
+
*/
|
|
43
|
+
export function isWebAppSurface(filename) {
|
|
44
|
+
const n = normalizeFilename(filename);
|
|
45
|
+
if (n.includes("/node_modules/"))
|
|
46
|
+
return false;
|
|
47
|
+
if (!n.includes("/app/"))
|
|
48
|
+
return false;
|
|
49
|
+
return n.includes("/apps/platform/web/") || n.includes("/apps/web/");
|
|
50
|
+
}
|
|
51
|
+
export function isSdkPackageFile(filename) {
|
|
52
|
+
return normalizeFilename(filename).includes("/packages/sdk/");
|
|
53
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
+
import { isWebAppSurface, normalizeFilename } from "./path-roles.js";
|
|
2
3
|
const QUERY_HOOK_NAMES = new Set(["useQuery", "useSuspenseQuery", "useInfiniteQuery"]);
|
|
3
|
-
function normalizeFilename(filename) {
|
|
4
|
-
return typeof filename === "string" ? filename.replaceAll("\\", "/") : "";
|
|
5
|
-
}
|
|
6
4
|
function getFilename(context) {
|
|
7
5
|
if (typeof context.getFilename === "function") {
|
|
8
6
|
return normalizeFilename(context.getFilename());
|
|
@@ -15,9 +13,8 @@ function isClientQuerySurface(filename) {
|
|
|
15
13
|
return false;
|
|
16
14
|
if (normalized.includes("/.webpresso/generated/"))
|
|
17
15
|
return false;
|
|
18
|
-
const isWebAppSurface = normalized.includes("/apps/web/") && normalized.includes("/app/");
|
|
19
16
|
const isFeatureSurface = normalized.includes("/packages/feature/") && normalized.includes("/src/");
|
|
20
|
-
return isWebAppSurface || isFeatureSurface;
|
|
17
|
+
return isWebAppSurface(normalized) || isFeatureSurface;
|
|
21
18
|
}
|
|
22
19
|
function getCalleeName(callee) {
|
|
23
20
|
if (!callee)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blueprint exec run ledger: durable job ↔ PTY session ↔ blueprint attribution.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists separately from the launch ledger: a launch handle is
|
|
5
|
+
* single-use and is consumed the moment Rust claims it, and the PTY reaper
|
|
6
|
+
* reports that *a* session ended but not which blueprint it belonged to.
|
|
7
|
+
* Neither can answer "what is this terminal running?" after a UI restart. A
|
|
8
|
+
* job id that outlives both can.
|
|
9
|
+
*
|
|
10
|
+
* Rows are deliberately env-free and argv-free: this ledger is readable by the
|
|
11
|
+
* webview, so it must never carry launch material.
|
|
12
|
+
*/
|
|
13
|
+
export declare const BLUEPRINT_EXEC_JOB_STATES: readonly ["preparing", "running", "terminal"];
|
|
14
|
+
export type BlueprintExecJobState = (typeof BLUEPRINT_EXEC_JOB_STATES)[number];
|
|
15
|
+
export type BlueprintExecJob = {
|
|
16
|
+
readonly jobId: string;
|
|
17
|
+
readonly slug: string;
|
|
18
|
+
readonly taskId: string | null;
|
|
19
|
+
/** Opaque single-use launch handle; never the composed argv or env. */
|
|
20
|
+
readonly launchHandle: string;
|
|
21
|
+
readonly sessionId: number | null;
|
|
22
|
+
readonly startedAt: number;
|
|
23
|
+
readonly state: BlueprintExecJobState;
|
|
24
|
+
};
|
|
25
|
+
export type RegisterBlueprintExecJobInput = {
|
|
26
|
+
readonly slug: string;
|
|
27
|
+
readonly taskId: string | null;
|
|
28
|
+
readonly launchHandle: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Bounded, insertion-ordered job store.
|
|
32
|
+
*
|
|
33
|
+
* Complexity: `register`, `bindSession`, `markTerminal`, and `get` are all
|
|
34
|
+
* Map-keyed O(1). Eviction is O(1) too — terminal job ids are appended to a
|
|
35
|
+
* FIFO queue as they terminate, so making room pops the front instead of
|
|
36
|
+
* rescanning the map. `list` is O(#jobs) over a capped map.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Construction options. Shared by the class and {@link createBlueprintExecLedger}
|
|
40
|
+
* so the factory's public surface cannot drift away from what the constructor
|
|
41
|
+
* actually accepts.
|
|
42
|
+
*/
|
|
43
|
+
export type BlueprintExecLedgerOptions = {
|
|
44
|
+
readonly maxJobs?: number;
|
|
45
|
+
readonly staleUnboundMs?: number;
|
|
46
|
+
/** Injectable clock (tests). */
|
|
47
|
+
readonly now?: () => number;
|
|
48
|
+
};
|
|
49
|
+
export declare class BlueprintExecLedger {
|
|
50
|
+
private readonly byJobId;
|
|
51
|
+
/** FIFO of job ids that reached `terminal`, oldest first. */
|
|
52
|
+
private readonly terminalOrder;
|
|
53
|
+
private readonly maxJobs;
|
|
54
|
+
private readonly staleUnboundMs;
|
|
55
|
+
private readonly now;
|
|
56
|
+
constructor(options?: BlueprintExecLedgerOptions);
|
|
57
|
+
/** Evict the oldest terminal job. O(1) amortized via the FIFO queue. */
|
|
58
|
+
private evictOneTerminal;
|
|
59
|
+
/**
|
|
60
|
+
* Last-resort reclaim: the oldest job that never bound a session and whose
|
|
61
|
+
* launch handle can no longer be claimed.
|
|
62
|
+
*
|
|
63
|
+
* Without this, a daemon that only ever prepared launches which were never
|
|
64
|
+
* claimed (a crashing Rust peer) would wedge at the cap forever, because
|
|
65
|
+
* nothing would ever transition those jobs to terminal.
|
|
66
|
+
*
|
|
67
|
+
* O(#jobs) and only on the eviction path when the terminal queue is empty.
|
|
68
|
+
*/
|
|
69
|
+
private evictOneStaleUnbound;
|
|
70
|
+
register(input: RegisterBlueprintExecJobInput): {
|
|
71
|
+
readonly jobId: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Bind the spawned PTY session to a job. Rust-only caller.
|
|
75
|
+
*
|
|
76
|
+
* Single-assignment: a second bind for the same job is rejected so a
|
|
77
|
+
* compromised or buggy peer cannot re-point an existing job at another
|
|
78
|
+
* session.
|
|
79
|
+
*/
|
|
80
|
+
bindSession(jobId: string, sessionId: number): BlueprintExecJob;
|
|
81
|
+
markTerminal(jobId: string): BlueprintExecJob | null;
|
|
82
|
+
get(jobId: string): BlueprintExecJob | undefined;
|
|
83
|
+
list(): readonly BlueprintExecJob[];
|
|
84
|
+
size(): number;
|
|
85
|
+
}
|
|
86
|
+
export declare function createBlueprintExecLedger(options?: BlueprintExecLedgerOptions): BlueprintExecLedger;
|