@webpresso/agent-kit 3.3.3 → 3.3.5

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.
Files changed (156) hide show
  1. package/THIRD-PARTY-NOTICES.md +12 -10
  2. package/catalog/agent/rules/changeset-release.md +15 -4
  3. package/catalog/agent/rules/managed-tool-binaries.md +67 -0
  4. package/catalog/agent/rules/parallel-orchestration-quota.md +99 -1
  5. package/catalog/agent/rules/pre-implementation.md +36 -20
  6. package/catalog/agent/rules/rtk-routing.md +22 -16
  7. package/catalog/agent/skills/autopilot/SKILL.md +4 -3
  8. package/catalog/agent/skills/ultragoal/SKILL.md +11 -9
  9. package/dist/esm/audit/agents.js +126 -26
  10. package/dist/esm/audit/hook-surface.js +5 -1
  11. package/dist/esm/audit/repo-guardrails.d.ts +8 -0
  12. package/dist/esm/audit/repo-guardrails.js +71 -1
  13. package/dist/esm/blueprint/core/parser.js +38 -12
  14. package/dist/esm/blueprint/execution/artifacts.js +8 -4
  15. package/dist/esm/blueprint/execution/frontmatter-keys.d.ts +17 -0
  16. package/dist/esm/blueprint/execution/frontmatter-keys.js +24 -0
  17. package/dist/esm/blueprint/execution/metadata.js +8 -4
  18. package/dist/esm/blueprint/lifecycle/audit.d.ts +21 -0
  19. package/dist/esm/blueprint/lifecycle/audit.js +89 -3
  20. package/dist/esm/blueprint/lifecycle/review-provenance.js +63 -10
  21. package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +22 -0
  22. package/dist/esm/blueprint/lifecycle/rollout-anchor.js +37 -1
  23. package/dist/esm/blueprint/markdown/blocked-line.d.ts +49 -0
  24. package/dist/esm/blueprint/markdown/blocked-line.js +63 -0
  25. package/dist/esm/blueprint/markdown/helpers.js +10 -4
  26. package/dist/esm/blueprint/tracked-document/parser.js +3 -3
  27. package/dist/esm/blueprint/trust/dossier.js +41 -1
  28. package/dist/esm/blueprint/trust/gate-row.d.ts +55 -0
  29. package/dist/esm/blueprint/trust/gate-row.js +113 -0
  30. package/dist/esm/blueprint/trust/promotion.d.ts +17 -0
  31. package/dist/esm/blueprint/trust/promotion.js +19 -19
  32. package/dist/esm/build/atomic-file-copy.d.ts +32 -0
  33. package/dist/esm/build/atomic-file-copy.js +44 -0
  34. package/dist/esm/build/vendor-oxlint-plugins.d.ts +11 -0
  35. package/dist/esm/build/vendor-oxlint-plugins.js +48 -0
  36. package/dist/esm/cli/commands/blueprint/mutations.d.ts +15 -0
  37. package/dist/esm/cli/commands/blueprint/mutations.js +8 -2
  38. package/dist/esm/cli/commands/compile.d.ts +13 -0
  39. package/dist/esm/cli/commands/compile.js +182 -62
  40. package/dist/esm/cli/commands/format.js +17 -1
  41. package/dist/esm/cli/commands/gain/index.js +10 -3
  42. package/dist/esm/cli/commands/init/gitignore-patcher.js +6 -0
  43. package/dist/esm/cli/commands/init/index.js +32 -52
  44. package/dist/esm/cli/commands/init/mcp-spec.d.ts +48 -13
  45. package/dist/esm/cli/commands/init/mcp-spec.js +86 -30
  46. package/dist/esm/cli/commands/init/merge.js +30 -10
  47. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +56 -31
  48. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +180 -43
  49. package/dist/esm/cli/commands/init/scaffolders/rtk/index.d.ts +17 -5
  50. package/dist/esm/cli/commands/init/scaffolders/rtk/index.js +56 -75
  51. package/dist/esm/cli/commands/init/scaffolders/runtime-check/index.js +7 -6
  52. package/dist/esm/cli/commands/init/untracked-collision.d.ts +12 -1
  53. package/dist/esm/cli/commands/init/untracked-collision.js +23 -1
  54. package/dist/esm/cli/commands/pr-help.js +1 -1
  55. package/dist/esm/cli/commands/pr.js +18 -4
  56. package/dist/esm/cli/commands/review.d.ts +1 -0
  57. package/dist/esm/cli/commands/review.js +137 -7
  58. package/dist/esm/cli/commands/sync.d.ts +8 -6
  59. package/dist/esm/cli/commands/sync.js +11 -9
  60. package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +24 -2
  61. package/dist/esm/cli/commands/worktree/router-dispatch.js +80 -48
  62. package/dist/esm/cli/commands/worktree/router.js +5 -2
  63. package/dist/esm/cli/direct-provider-launch.js +12 -4
  64. package/dist/esm/compiler/flatten.d.ts +10 -1
  65. package/dist/esm/compiler/flatten.js +14 -4
  66. package/dist/esm/compose/admin-blocks-registry-integrity.d.ts +22 -0
  67. package/dist/esm/compose/admin-blocks-registry-integrity.js +90 -0
  68. package/dist/esm/compose/compose.d.ts +8 -0
  69. package/dist/esm/compose/compose.js +115 -8
  70. package/dist/esm/compose/ops-report-schema.js +17 -3
  71. package/dist/esm/compose/registry-resolve.d.ts +17 -0
  72. package/dist/esm/compose/registry-resolve.js +78 -11
  73. package/dist/esm/config/oxlint/graphql-conventions.js +2 -5
  74. package/dist/esm/config/oxlint/import-hygiene.js +21 -8
  75. package/dist/esm/config/oxlint/oxlint-config.fixture.d.ts +1 -1
  76. package/dist/esm/config/oxlint/oxlint-config.fixture.js +2 -3
  77. package/dist/esm/config/oxlint/oxlintrc.d.ts +5 -7
  78. package/dist/esm/config/oxlint/oxlintrc.js +19 -31
  79. package/dist/esm/config/oxlint/path-roles.js +53 -0
  80. package/dist/esm/config/oxlint/query-patterns.js +2 -5
  81. package/dist/esm/content/dispatch.js +4 -0
  82. package/dist/esm/content/loader.d.ts +1 -1
  83. package/dist/esm/content/loader.js +10 -6
  84. package/dist/esm/content/skill-policy.d.ts +11 -0
  85. package/dist/esm/content/skill-policy.js +33 -0
  86. package/dist/esm/daemon/domains/blueprint-exec-ledger.d.ts +86 -0
  87. package/dist/esm/daemon/domains/blueprint-exec-ledger.js +134 -0
  88. package/dist/esm/daemon/domains/blueprint-exec.d.ts +106 -0
  89. package/dist/esm/daemon/domains/blueprint-exec.js +161 -0
  90. package/dist/esm/daemon/domains/launch.d.ts +25 -0
  91. package/dist/esm/daemon/domains/launch.js +30 -1
  92. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +12 -1
  93. package/dist/esm/daemon/handlers/blueprints-handlers.js +148 -6
  94. package/dist/esm/daemon/handlers/product.d.ts +10 -0
  95. package/dist/esm/daemon/handlers/product.js +10 -1
  96. package/dist/esm/daemon/methods.d.ts +21 -1
  97. package/dist/esm/daemon/methods.js +20 -0
  98. package/dist/esm/daemon/protocol/contract.d.ts +58 -0
  99. package/dist/esm/daemon/protocol/contract.js +38 -0
  100. package/dist/esm/daemon/server.js +7 -0
  101. package/dist/esm/docs-linter/blueprint-plan.js +2 -2
  102. package/dist/esm/errors/wp-error.js +21 -2
  103. package/dist/esm/hooks/doctor.d.ts +11 -0
  104. package/dist/esm/hooks/doctor.js +114 -45
  105. package/dist/esm/hooks/pretool-guard/dev-routing.js +68 -5
  106. package/dist/esm/hooks/pretool-guard/rtk-rewrite.d.ts +24 -0
  107. package/dist/esm/hooks/pretool-guard/rtk-rewrite.js +50 -0
  108. package/dist/esm/hooks/pretool-guard/runner.js +6 -0
  109. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +62 -3
  110. package/dist/esm/hooks/status/index.js +1 -1
  111. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +39 -11
  112. package/dist/esm/mcp/blueprint/handlers/review-log.d.ts +14 -7
  113. package/dist/esm/mcp/blueprint/handlers/review-log.js +46 -22
  114. package/dist/esm/mcp/blueprint/registration.js +1 -1
  115. package/dist/esm/mcp/tools/_shared/redact.d.ts +22 -0
  116. package/dist/esm/mcp/tools/_shared/redact.js +114 -8
  117. package/dist/esm/mcp/tools/gain.js +28 -2
  118. package/dist/esm/mcp/tools/worktree.js +65 -3
  119. package/dist/esm/mcp/tools/wp-ui-compose.js +38 -4
  120. package/dist/esm/mcp/tools/wp-ui-preview.js +14 -7
  121. package/dist/esm/review/authority.js +56 -1
  122. package/dist/esm/review/delivery-verifier.d.ts +4 -0
  123. package/dist/esm/review/delivery-verifier.js +9 -1
  124. package/dist/esm/review/lifecycle-writers.d.ts +76 -0
  125. package/dist/esm/review/lifecycle-writers.js +330 -0
  126. package/dist/esm/review/subject.js +66 -32
  127. package/dist/esm/status/snapshot.d.ts +5 -0
  128. package/dist/esm/status/snapshot.js +27 -2
  129. package/dist/esm/symlinker/consumers.d.ts +3 -1
  130. package/dist/esm/symlinker/consumers.js +7 -1
  131. package/dist/esm/symlinker/unified-sync.d.ts +3 -3
  132. package/dist/esm/symlinker/unified-sync.js +88 -11
  133. package/dist/esm/test/shard-durations.json +0 -2
  134. package/dist/esm/test-helpers/global-setup.js +38 -3
  135. package/dist/esm/tool-runtime/managed-rtk.d.ts +44 -0
  136. package/dist/esm/tool-runtime/managed-rtk.js +275 -0
  137. package/dist/esm/tool-runtime/resolve-runner.js +18 -194
  138. package/dist/esm/utils/package-root.d.ts +32 -0
  139. package/dist/esm/utils/package-root.js +82 -0
  140. package/dist/esm/worktrees/git-metadata.d.ts +41 -0
  141. package/dist/esm/worktrees/git-metadata.js +48 -0
  142. package/package.json +16 -21
  143. package/dist/esm/cli/commands/init/scaffolders/subagents/index.d.ts +0 -6
  144. package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +0 -77
  145. package/dist/esm/config/oxlint/code-safety.d.ts +0 -18
  146. package/dist/esm/config/oxlint/foundation-purity.d.ts +0 -21
  147. package/dist/esm/config/oxlint/graphql-conventions.d.ts +0 -23
  148. package/dist/esm/config/oxlint/import-hygiene.d.ts +0 -34
  149. package/dist/esm/config/oxlint/index.d.ts +0 -22
  150. package/dist/esm/config/oxlint/index.js +0 -28
  151. package/dist/esm/config/oxlint/monorepo-paths.d.ts +0 -22
  152. package/dist/esm/config/oxlint/query-patterns.d.ts +0 -24
  153. package/dist/esm/config/oxlint/testing-quality.d.ts +0 -35
  154. package/dist/esm/config/oxlint/tier-boundaries.d.ts +0 -35
  155. package/dist/esm/output-transforms/rulesync.d.ts +0 -2
  156. package/dist/esm/output-transforms/rulesync.js +0 -79
@@ -16,19 +16,33 @@ export const opsReportSectionSchema = z
16
16
  });
17
17
  }
18
18
  });
19
+ const finiteNumber = z.number().finite();
20
+ const cellValue = z.union([z.string(), finiteNumber]);
19
21
  export const opsReportMetricSchema = z
20
22
  .object({
21
23
  label: z.string().min(1),
22
- value: z.union([z.string(), z.number()]),
24
+ value: cellValue,
23
25
  })
24
26
  .strict();
25
27
  export const opsReportTableSchema = z
26
28
  .object({
27
29
  title: z.string().optional(),
28
30
  headers: z.array(z.string()).min(1),
29
- rows: z.array(z.array(z.union([z.string(), z.number()]))),
31
+ rows: z.array(z.array(cellValue)),
30
32
  })
31
- .strict();
33
+ .strict()
34
+ .superRefine((table, ctx) => {
35
+ const expected = table.headers.length;
36
+ for (const [rowIndex, row] of table.rows.entries()) {
37
+ if (row.length !== expected) {
38
+ ctx.addIssue({
39
+ code: "custom",
40
+ message: `row has ${row.length} cell(s); expected ${expected} to match headers`,
41
+ path: ["rows", rowIndex],
42
+ });
43
+ }
44
+ }
45
+ });
32
46
  /**
33
47
  * Minimal ops-report IR. Not a general page AST.
34
48
  * Nesting under sections is rejected by the section schema (flat only).
@@ -1,4 +1,10 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * Local reader capability floor for project registries (agent-kit consumer).
4
+ * Bump in the same PR as catalog/compose/admin-blocks-registry.json major/minor
5
+ * advances. Does not import @webpresso/schema (SEA / nondeterminism lock).
6
+ */
7
+ export declare const READER_REGISTRY_VERSION: "1.0.0";
2
8
  export declare const blockRegistrySchema: z.ZodObject<{
3
9
  $schema: z.ZodOptional<z.ZodString>;
4
10
  version: z.ZodString;
@@ -30,6 +36,17 @@ export interface ResolvedBlockRegistry {
30
36
  readonly webpressoPresentWithoutRegistry: boolean;
31
37
  }
32
38
  export declare function defaultSnapshotPath(): string;
39
+ /**
40
+ * Whether a loaded project registry is readable by this agent-kit consumer.
41
+ * - registry.version major must match the reader major
42
+ * - if registry.minVersion is set, reader must be >= that floor (semver order)
43
+ */
44
+ export declare function isProjectRegistryCompatibleWithReader(registry: BlockRegistry, readerVersion?: string): {
45
+ readonly ok: true;
46
+ } | {
47
+ readonly ok: false;
48
+ readonly reason: string;
49
+ };
33
50
  /**
34
51
  * Two-tier registry resolution (v0):
35
52
  * 1. Nearest project `.webpresso/` (within git/webpressorc boundary) — if it has
@@ -5,6 +5,12 @@ import { resolvePackageAssetPreferred } from "#utils/package-assets.js";
5
5
  import { z } from "zod";
6
6
  const REGISTRY_RELATIVE = join("generated", "frontend", "blocks", "registry.json");
7
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";
8
14
  /** Stop climbing past these markers (mirrors project-root clamps elsewhere). */
9
15
  function isProjectBoundary(dir) {
10
16
  return existsSync(join(dir, ".git")) || existsSync(join(dir, ".webpressorc.json"));
@@ -82,6 +88,53 @@ function loadRegistryFile(path) {
82
88
  throw new Error(`Failed to load block registry at ${path}: ${detail}`, { cause: error });
83
89
  }
84
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
+ }
85
138
  /**
86
139
  * Two-tier registry resolution (v0):
87
140
  * 1. Nearest project `.webpresso/` (within git/webpressorc boundary) — if it has
@@ -103,20 +156,34 @@ export function resolveBlockRegistry(options = {}) {
103
156
  if (webpressoRoot) {
104
157
  const projectPath = join(webpressoRoot, REGISTRY_RELATIVE);
105
158
  if (existsSync(projectPath)) {
106
- const registry = loadRegistryFile(projectPath);
107
- return {
108
- registry,
109
- source: "project",
110
- registryVersion: registry.version,
111
- path: projectPath,
112
- warnings,
113
- webpressoPresentWithoutRegistry: false,
114
- };
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
+ }
115
182
  }
116
183
  }
117
184
  const snapshotPath = options.snapshotPath ?? defaultSnapshotPath();
118
185
  const registry = loadRegistryFile(snapshotPath);
119
- if (webpressoRoot) {
186
+ if (webpressoRoot && warnings.length === 0) {
120
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.`);
121
188
  }
122
189
  return {
@@ -125,7 +192,7 @@ export function resolveBlockRegistry(options = {}) {
125
192
  registryVersion: registry.version,
126
193
  path: snapshotPath,
127
194
  warnings,
128
- webpressoPresentWithoutRegistry: webpressoRoot !== null,
195
+ webpressoPresentWithoutRegistry: webpressoRoot !== null && !existsSync(join(webpressoRoot, REGISTRY_RELATIVE)),
129
196
  };
130
197
  }
131
198
  export function blockNamesForView(registry, view) {
@@ -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
- function isPlatformAppFile(filename) {
50
- return filename.includes("/apps/workers/") || filename.includes("/apps/web/");
51
- }
52
- function isSdkPackageFile(filename) {
53
- return filename.includes("/packages/sdk/");
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 "./index.js";
1
+ import { config } from "@webpresso/agent-config/oxlint";
2
2
  export default config;
@@ -1,4 +1,3 @@
1
- import { config } from "./index.js";
2
- // Consumer repos can use the same shape from the public subpath:
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
- * Kept in lockstep with the plugin set in `index.ts` (guarded by the parity
4
- * test in `oxlintrc.test.ts`, which asserts one entry per registered plugin).
3
+ * SSOT: `@webpresso/agent-config/oxlint` `OXLINT_POLICY_PLUGIN_BASENAMES`.
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. These
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, and the
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,44 +1,31 @@
1
1
  /**
2
2
  * Resolved, consumer-ready oxlint configuration (Tier-1 DRY model).
3
3
  *
4
- * agent-kit ships ONE resolved `.oxlintrc.json` — generated from this builder
5
- * at build time into `dist/esm/config/oxlint/oxlintrc.json` — and `wp lint`
6
- * points oxlint at it via `--config`. Consumers therefore carry no
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
- * Two oxlint resolution facts (verified against oxlint 1.67) make this work:
11
- * - `jsPlugins` are resolved relative to the CONFIG FILE's directory, so the
12
- * specifiers below are sibling-relative (`./<plugin>.js`) to the shipped JSON
13
- * zero package-resolution machinery, no dependency on @webpresso/agent-kit
14
- * being resolvable from the config's location.
15
- * - `ignorePatterns` are applied relative to the LINTED project (cwd), not the
16
- * config-file directory, so `dist`/`node_modules`/etc. ignore the consumer's
17
- * tree even though the config lives inside node_modules.
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 "./index.js";
18
+ import { OXLINT_POLICY_PLUGIN_BASENAMES, rules as agentConfigRules, } from "@webpresso/agent-config/oxlint";
20
19
  /**
21
20
  * Compiled plugin module basenames shipped next to the generated oxlintrc.json.
22
- * Kept in lockstep with the plugin set in `index.ts` (guarded by the parity
23
- * test in `oxlintrc.test.ts`, which asserts one entry per registered plugin).
21
+ * SSOT: `@webpresso/agent-config/oxlint` `OXLINT_POLICY_PLUGIN_BASENAMES`.
24
22
  */
25
- export const OXLINT_PLUGIN_BASENAMES = [
26
- "code-safety",
27
- "foundation-purity",
28
- "graphql-conventions",
29
- "import-hygiene",
30
- "monorepo-paths",
31
- "query-patterns",
32
- "testing-quality",
33
- "tier-boundaries",
34
- ];
23
+ export const OXLINT_PLUGIN_BASENAMES = OXLINT_POLICY_PLUGIN_BASENAMES;
35
24
  /** jsPlugins specifiers, sibling-relative to the shipped oxlintrc.json. */
36
25
  export const OXLINT_JS_PLUGIN_FILES = OXLINT_PLUGIN_BASENAMES.map((name) => `./${name}.js`);
37
26
  /**
38
27
  * Standard ignore patterns for every webpresso consumer: build output, vendored
39
- * deps, generated reports, and the regenerated agent-surface directories. These
40
- * are project-relative. Truly repo-specific extras belong in a consumer-local
41
- * `.oxlintignore`, never here.
28
+ * deps, generated reports, and the regenerated agent-surface directories.
42
29
  */
43
30
  export const STANDARD_IGNORE_PATTERNS = [
44
31
  "dist",
@@ -53,10 +40,11 @@ export const STANDARD_IGNORE_PATTERNS = [
53
40
  ".opencode",
54
41
  ".runtime",
55
42
  ];
43
+ /** Rule map from agent-config SSOT (error severity for inject). */
44
+ export const rules = agentConfigRules;
56
45
  /**
57
46
  * Build the resolved oxlint config object that ships as `oxlintrc.json`. Pure:
58
- * the build-time generator serializes this return value verbatim, and the
59
- * parity test asserts it against the live `rules`/`plugins` exports.
47
+ * the build-time generator serializes this return value verbatim.
60
48
  */
61
49
  export function buildOxlintrc() {
62
50
  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)
@@ -12,6 +12,10 @@ import { loadContent } from "./loader.js";
12
12
  const CONSUMER_DIR_BY_KIND = {
13
13
  rule: "agent-rules",
14
14
  skill: "agent-skills",
15
+ // Not reachable today (no `wp agent` CRUD command dispatches here), kept
16
+ // only to satisfy Record<ContentKind, string> exhaustiveness now that
17
+ // "agent" is a ContentKind for the unified-sync projection pipeline.
18
+ agent: "agent-agents",
15
19
  };
16
20
  function todayIsoDate() {
17
21
  return new Date().toISOString().slice(0, 10);
@@ -14,7 +14,7 @@
14
14
  * gray-matter object in both `rawFrontmatter` and `parsedFrontmatter` so the
15
15
  * shape stays stable for downstream callers writing tests against it.
16
16
  */
17
- export type ContentKind = "rule" | "skill";
17
+ export type ContentKind = "rule" | "skill" | "agent";
18
18
  export type ContentSource = "canonical" | "consumer";
19
19
  /**
20
20
  * Stub raw frontmatter type. Replaced with schema output in Task 2.x.
@@ -21,10 +21,12 @@ const DEFAULT_KINDS = ["rule", "skill"];
21
21
  const CONSUMER_DIR_BY_KIND = {
22
22
  rule: "agent-rules",
23
23
  skill: "agent-skills",
24
+ agent: "agent-agents",
24
25
  };
25
26
  const CATALOG_DIR_BY_KIND = {
26
27
  rule: "rules",
27
28
  skill: "skills",
29
+ agent: "agents",
28
30
  };
29
31
  export function loadContent(options) {
30
32
  const { catalogDir, consumerRoot, kinds = DEFAULT_KINDS } = options;
@@ -83,9 +85,11 @@ function readKind(args) {
83
85
  const stat = statSync(root);
84
86
  if (!stat.isDirectory())
85
87
  return [];
86
- return kind === "rule" ? readRules(root, source) : readSkills(root, source);
88
+ // agent kind shares the rule shape: flat `<slug>.md` files, README.md
89
+ // excluded. Only `skill` is directory-shaped (`<slug>/SKILL.md`).
90
+ return kind === "skill" ? readSkills(root, source) : readFlatMarkdown(kind, root, source);
87
91
  }
88
- function readRules(root, source) {
92
+ function readFlatMarkdown(kind, root, source) {
89
93
  const out = [];
90
94
  for (const entry of readdirSync(root, { withFileTypes: true })) {
91
95
  if (!entry.isFile())
@@ -93,9 +97,9 @@ function readRules(root, source) {
93
97
  if (!entry.name.endsWith(".md"))
94
98
  continue;
95
99
  // Skip directory-documentation files. README.md exists in both the
96
- // canonical catalog and the consumer-owned agent-rules/ scaffolder
97
- // surface; treating it as a rule would produce a spurious slug
98
- // collision and would also try to project it into per-IDE surfaces.
100
+ // canonical catalog and the consumer-owned agent-rules/ (or agent-agents/)
101
+ // scaffolder surface; treating it as content would produce a spurious
102
+ // slug collision and would also try to project it into per-IDE surfaces.
99
103
  if (entry.name === "README.md")
100
104
  continue;
101
105
  const filePath = realpathSync(join(root, entry.name));
@@ -103,7 +107,7 @@ function readRules(root, source) {
103
107
  const parsed = matter(readFileSync(filePath, "utf8"));
104
108
  const raw = { ...parsed.data };
105
109
  out.push({
106
- kind: "rule",
110
+ kind,
107
111
  slug,
108
112
  source,
109
113
  filePath,
@@ -1,6 +1,17 @@
1
1
  export declare const DEFAULT_PACKAGED_SKILL_SLUGS: readonly ["verify", "fix", "investigate", "ai-deslop", "plan-refine", "ralplan", "best-practice-research", "testing-philosophy", "tph", "claude", "codex", "grok", "opencode-go", "plan-eng-review", "plan-ceo-review", "plan-design-review", "plan-devex-review", "browse", "devex-review", "design-review", "hooks-doctor", "tech-debt", "lore-protocol", "deep-interview", "deep-research", "autoresearch", "ultragoal", "autopilot", "team"];
2
2
  export type DefaultPackagedSkillSlug = (typeof DEFAULT_PACKAGED_SKILL_SLUGS)[number];
3
3
  export declare const DEFAULT_PACKAGED_SKILL_SLUG_SET: ReadonlySet<string>;
4
+ /**
5
+ * Catalog skills that are deliberately NOT in DEFAULT_PACKAGED_SKILL_SLUGS,
6
+ * with a one-line reason each. Every slug under `catalog/agent/skills/` must
7
+ * appear in either that list or this one (enforced by
8
+ * `checkCatalogSkillClassification` in `src/audit/agents.ts`) — an
9
+ * unclassified slug means a new catalog skill shipped without a deliberate
10
+ * packaging decision.
11
+ */
12
+ export declare const UNPACKAGED_SKILL_SLUGS: Readonly<Record<string, string>>;
13
+ export declare const UNPACKAGED_SKILL_SLUG_SET: ReadonlySet<string>;
14
+ export declare function isUnpackagedSkillSlug(slug: string): boolean;
4
15
  export declare const MERGED_DELETED_SKILL_SLUGS: readonly ["autoplan", "health", "systematic-debugging", "test-driven-development"];
5
16
  export declare const MERGED_DELETED_SKILL_SLUG_SET: ReadonlySet<string>;
6
17
  /**
@@ -30,6 +30,39 @@ export const DEFAULT_PACKAGED_SKILL_SLUGS = [
30
30
  "team",
31
31
  ];
32
32
  export const DEFAULT_PACKAGED_SKILL_SLUG_SET = new Set(DEFAULT_PACKAGED_SKILL_SLUGS);
33
+ /**
34
+ * Catalog skills that are deliberately NOT in DEFAULT_PACKAGED_SKILL_SLUGS,
35
+ * with a one-line reason each. Every slug under `catalog/agent/skills/` must
36
+ * appear in either that list or this one (enforced by
37
+ * `checkCatalogSkillClassification` in `src/audit/agents.ts`) — an
38
+ * unclassified slug means a new catalog skill shipped without a deliberate
39
+ * packaging decision.
40
+ */
41
+ export const UNPACKAGED_SKILL_SLUGS = {
42
+ qa: "source-catalog QA skill, not package-visible in the default plugin skill set today (see workflow-skills-routing.md)",
43
+ "qa-only": "source-catalog QA skill, not package-visible in the default plugin skill set today (see workflow-skills-routing.md)",
44
+ review: "listed as a default workflow skill in workflow-skills-routing.md but not yet added to DEFAULT_PACKAGED_SKILL_SLUGS",
45
+ pll: "ultragoal's parallel-execute companion (ready-queue/worktrees driver); not independently default-packaged",
46
+ "frontend-design": "opt-in design-guidance skill, not part of the default packaged workflow set",
47
+ "monorepo-navigation": "tier-3 opt-in preset (wp setup --with monorepo-navigation), not default-packaged",
48
+ "tanstack-query": "tier-3 opt-in preset (wp setup --with tanstack-query), not default-packaged",
49
+ "react-doctor": "opt-in framework best-practice skill, not default-packaged",
50
+ "better-auth-best-practices": "opt-in best-practice reference skill, not default-packaged",
51
+ "logging-best-practices": "opt-in best-practice reference skill, not default-packaged",
52
+ "vercel-react-best-practices": "opt-in best-practice reference skill, not default-packaged",
53
+ "web-design-guidelines": "opt-in best-practice reference skill, not default-packaged",
54
+ deepseek: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
55
+ glm: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
56
+ hy3: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
57
+ kimi: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
58
+ mimo: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
59
+ minimax: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
60
+ qwen: "individual OpenCode-Go model reviewer skill; only the aggregate opencode-go skill is default-packaged",
61
+ };
62
+ export const UNPACKAGED_SKILL_SLUG_SET = new Set(Object.keys(UNPACKAGED_SKILL_SLUGS));
63
+ export function isUnpackagedSkillSlug(slug) {
64
+ return UNPACKAGED_SKILL_SLUG_SET.has(slug);
65
+ }
33
66
  export const MERGED_DELETED_SKILL_SLUGS = [
34
67
  "autoplan",
35
68
  "health",