@webpresso/agent-kit 0.21.4 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +93 -66
  4. package/bin/_run.js +143 -1
  5. package/bin/runtime-manifest.json +40 -0
  6. package/catalog/AGENTS.md.tpl +7 -6
  7. package/catalog/agent/commands/plan-refine.md +3 -3
  8. package/catalog/agent/commands/pll.md +2 -0
  9. package/catalog/agent/guides/parallel-execution.md +2 -0
  10. package/catalog/agent/rules/extraction-parity.md +27 -1
  11. package/catalog/agent/rules/public-package-safety.md +24 -1
  12. package/catalog/agent/skills/plan-refine/SKILL.md +5 -4
  13. package/catalog/agent/skills/pll/SKILL.md +1 -0
  14. package/catalog/base-kit/.github/workflows/ci.webpresso.yml.tmpl +33 -0
  15. package/catalog/base-kit/commitlint.config.ts.tmpl +1 -3
  16. package/catalog/base-kit/e2e/fixtures/smoke.html.tmpl +13 -0
  17. package/catalog/base-kit/e2e/smoke.spec.ts.tmpl +13 -0
  18. package/catalog/base-kit/oxlint.config.ts.tmpl +26 -0
  19. package/catalog/base-kit/playwright.config.ts.tmpl +10 -0
  20. package/catalog/base-kit/src/quality-sample.test.ts.tmpl +19 -0
  21. package/catalog/base-kit/src/quality-sample.ts.tmpl +11 -0
  22. package/catalog/base-kit/stryker.config.ts.tmpl +14 -0
  23. package/catalog/base-kit/tsconfig.json.tmpl +9 -0
  24. package/catalog/base-kit/vitest.config.ts.tmpl +10 -0
  25. package/catalog/docs/templates/adr.md +1 -1
  26. package/catalog/docs/templates/blueprint.md +2 -0
  27. package/catalog/docs/templates/blueprint.yaml +16 -15
  28. package/catalog/docs/templates/guide.md +1 -1
  29. package/catalog/docs/templates/postmortem.md +1 -1
  30. package/catalog/docs/templates/research.md +1 -1
  31. package/catalog/docs/templates/runbook.md +1 -1
  32. package/catalog/docs/templates/system.md +12 -3
  33. package/catalog/docs/templates/tech-debt.md +1 -0
  34. package/commands/blueprint.md +10 -12
  35. package/dist/esm/audit/blueprint-db-consistency.d.ts +1 -1
  36. package/dist/esm/audit/blueprint-db-consistency.js +6 -8
  37. package/dist/esm/audit/blueprint-lifecycle-sql.js +10 -3
  38. package/dist/esm/audit/cloudflare-deploy-contract.d.ts +3 -0
  39. package/dist/esm/audit/cloudflare-deploy-contract.js +64 -0
  40. package/dist/esm/audit/no-legacy-cli-bin.d.ts +3 -0
  41. package/dist/esm/audit/no-legacy-cli-bin.js +100 -0
  42. package/dist/esm/audit/package-surface.js +14 -1
  43. package/dist/esm/audit/repo-guardrails.js +40 -13
  44. package/dist/esm/audit/resolve-audit-script.d.ts +24 -0
  45. package/dist/esm/audit/resolve-audit-script.js +27 -0
  46. package/dist/esm/audit/roadmap-links.js +23 -10
  47. package/dist/esm/blueprint/core/schema.d.ts +8 -8
  48. package/dist/esm/blueprint/core/schema.js +2 -2
  49. package/dist/esm/blueprint/db/enums.d.ts +1 -1
  50. package/dist/esm/blueprint/db/ingester.js +18 -10
  51. package/dist/esm/blueprint/index.d.ts +0 -1
  52. package/dist/esm/blueprint/index.js +0 -2
  53. package/dist/esm/blueprint/lifecycle/audit.js +9 -2
  54. package/dist/esm/blueprint/lifecycle/local.js +15 -4
  55. package/dist/esm/blueprint/local.d.ts +0 -3
  56. package/dist/esm/blueprint/local.js +0 -2
  57. package/dist/esm/blueprint/service/BlueprintCreationService.js +16 -8
  58. package/dist/esm/blueprint/service/BlueprintService.js +37 -19
  59. package/dist/esm/blueprint/service/scanner.js +73 -9
  60. package/dist/esm/blueprint/tracked-document/schema.d.ts +2 -2
  61. package/dist/esm/blueprint/utils/document-paths.d.ts +23 -0
  62. package/dist/esm/blueprint/utils/document-paths.js +91 -0
  63. package/dist/esm/blueprint/utils/package-assets.d.ts +11 -0
  64. package/dist/esm/blueprint/utils/package-assets.js +33 -4
  65. package/dist/esm/build/package-manifest.js +7 -0
  66. package/dist/esm/build/release-policy.d.ts +27 -0
  67. package/dist/esm/build/release-policy.js +29 -0
  68. package/dist/esm/build/runtime-targets.d.ts +13 -0
  69. package/dist/esm/build/runtime-targets.js +48 -0
  70. package/dist/esm/build/sync-catalog-doc-templates.d.ts +23 -0
  71. package/dist/esm/build/sync-catalog-doc-templates.js +93 -0
  72. package/dist/esm/cli/auto-update/detect-pm.d.ts +15 -0
  73. package/dist/esm/cli/auto-update/detect-pm.js +24 -9
  74. package/dist/esm/cli/auto-update/skip.js +9 -1
  75. package/dist/esm/cli/bundle/agent-command-inventory.d.ts +120 -0
  76. package/dist/esm/cli/bundle/agent-command-inventory.js +100 -0
  77. package/dist/esm/cli/bundle/index.d.ts +17 -0
  78. package/dist/esm/cli/bundle/index.js +15 -0
  79. package/dist/esm/cli/cli.d.ts +1 -1
  80. package/dist/esm/cli/cli.js +49 -5
  81. package/dist/esm/cli/commands/audit-core.d.ts +1 -1
  82. package/dist/esm/cli/commands/audit.js +4 -7
  83. package/dist/esm/cli/commands/blueprint/router.js +16 -10
  84. package/dist/esm/cli/commands/blueprint/template-resolver.js +8 -4
  85. package/dist/esm/cli/commands/hook.d.ts +8 -0
  86. package/dist/esm/cli/commands/hook.js +47 -0
  87. package/dist/esm/cli/commands/init/host-visibility.js +4 -2
  88. package/dist/esm/cli/commands/init/index.js +80 -7
  89. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -0
  90. package/dist/esm/cli/commands/init/scaffold-base-kit.js +142 -7
  91. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/codex-ownership.js +9 -1
  92. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +130 -20
  93. package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.d.ts +65 -0
  94. package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +64 -0
  95. package/dist/esm/cli/commands/package-manager.d.ts +15 -0
  96. package/dist/esm/cli/commands/package-manager.js +42 -0
  97. package/dist/esm/cli/commands/test.d.ts +1 -0
  98. package/dist/esm/cli/commands/test.js +2 -1
  99. package/dist/esm/cli/commands/typecheck.js +10 -19
  100. package/dist/esm/cli/package-scripts.d.ts +12 -0
  101. package/dist/esm/cli/package-scripts.js +59 -0
  102. package/dist/esm/cli/utils.js +3 -22
  103. package/dist/esm/cli/wp-extensions.d.ts +14 -0
  104. package/dist/esm/cli/wp-extensions.js +34 -0
  105. package/dist/esm/config/docs-lint/schemas/common.d.ts +1 -1
  106. package/dist/esm/config/docs-lint/schemas/implementation-plan.d.ts +2 -2
  107. package/dist/esm/config/docs-lint/schemas/parent-roadmap.d.ts +1 -1
  108. package/dist/esm/config/stryker/index.d.ts +85 -0
  109. package/dist/esm/config/stryker/index.js +31 -0
  110. package/dist/esm/e2e/command-builder.js +35 -7
  111. package/dist/esm/e2e/config.d.ts +56 -0
  112. package/dist/esm/e2e/config.js +114 -0
  113. package/dist/esm/e2e/execution.js +8 -0
  114. package/dist/esm/e2e/run-planner.js +2 -0
  115. package/dist/esm/e2e/types.d.ts +3 -0
  116. package/dist/esm/format/index.js +5 -1
  117. package/dist/esm/hooks/guard-switch/index.d.ts +1 -1
  118. package/dist/esm/hooks/guard-switch/index.js +22 -14
  119. package/dist/esm/hooks/post-tool/lint-after-edit.d.ts +1 -0
  120. package/dist/esm/hooks/post-tool/lint-after-edit.js +5 -2
  121. package/dist/esm/hooks/pretool-guard/validators/file-conventions.js +1 -1
  122. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.d.ts +6 -0
  123. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +27 -2
  124. package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +2 -1
  125. package/dist/esm/hooks/pretool-guard/validators/path-contract.js +59 -34
  126. package/dist/esm/hooks/pretool-guard/validators/plan-frontmatter.js +3 -3
  127. package/dist/esm/hooks/shared/routing-block.js +18 -4
  128. package/dist/esm/hooks/shared/validators/blueprint.js +3 -0
  129. package/dist/esm/hooks/stop/qa-changed-files.d.ts +1 -0
  130. package/dist/esm/hooks/stop/qa-changed-files.js +5 -2
  131. package/dist/esm/lint/index.js +3 -1
  132. package/dist/esm/mcp/auto-discover.d.ts +2 -0
  133. package/dist/esm/mcp/auto-discover.js +14 -6
  134. package/dist/esm/mcp/blueprint-server.js +379 -80
  135. package/dist/esm/mcp/cli.js +21 -0
  136. package/dist/esm/mcp/runners/test.js +15 -0
  137. package/dist/esm/mcp/server.d.ts +7 -0
  138. package/dist/esm/mcp/server.js +16 -27
  139. package/dist/esm/mcp/tools/_registry.d.ts +3 -0
  140. package/dist/esm/mcp/tools/_registry.js +21 -0
  141. package/dist/esm/mcp/tools/audit.d.ts +1 -0
  142. package/dist/esm/mcp/tools/audit.js +13 -8
  143. package/dist/esm/mcp/tools/typecheck.js +4 -2
  144. package/dist/esm/mutation/affected.d.ts +9 -0
  145. package/dist/esm/mutation/affected.js +36 -0
  146. package/dist/esm/package.json +8 -0
  147. package/dist/esm/runtime/package-version.d.ts +2 -0
  148. package/dist/esm/runtime/package-version.js +43 -0
  149. package/dist/esm/test/command-builder.d.ts +4 -0
  150. package/dist/esm/test/command-builder.js +28 -3
  151. package/dist/esm/test-helpers/hermetic-env.d.ts +25 -0
  152. package/dist/esm/test-helpers/hermetic-env.js +31 -0
  153. package/dist/esm/tool-runtime/index.d.ts +5 -0
  154. package/dist/esm/tool-runtime/index.js +24 -0
  155. package/dist/esm/tool-runtime/resolve-runner.d.ts +16 -0
  156. package/dist/esm/tool-runtime/resolve-runner.js +42 -0
  157. package/dist/esm/typecheck/index.js +4 -2
  158. package/dist/esm/wp-extension/index.d.ts +50 -0
  159. package/dist/esm/wp-extension/index.js +268 -0
  160. package/package.json +75 -46
  161. package/skills/plan-refine/SKILL.md +5 -4
  162. package/skills/pll/SKILL.md +1 -0
  163. package/dist/esm/blueprint/dag/cycle-detector.d.ts +0 -12
  164. package/dist/esm/blueprint/dag/cycle-detector.js +0 -46
  165. package/dist/esm/blueprint/dag/executor.d.ts +0 -140
  166. package/dist/esm/blueprint/dag/executor.js +0 -292
  167. package/dist/esm/blueprint/dag/index.d.ts +0 -20
  168. package/dist/esm/blueprint/dag/index.js +0 -17
  169. package/dist/esm/blueprint/dag/interfaces.d.ts +0 -56
  170. package/dist/esm/blueprint/dag/interfaces.js +0 -13
  171. package/dist/esm/blueprint/dag/local/independence.d.ts +0 -107
  172. package/dist/esm/blueprint/dag/local/independence.js +0 -231
  173. package/dist/esm/blueprint/dag/local/index.d.ts +0 -14
  174. package/dist/esm/blueprint/dag/local/index.js +0 -14
  175. package/dist/esm/blueprint/dag/local/package-graph.d.ts +0 -66
  176. package/dist/esm/blueprint/dag/local/package-graph.js +0 -148
  177. package/dist/esm/blueprint/dag/plan-parser.d.ts +0 -54
  178. package/dist/esm/blueprint/dag/plan-parser.js +0 -236
  179. package/dist/esm/blueprint/dag/task-graph-algorithms.d.ts +0 -13
  180. package/dist/esm/blueprint/dag/task-graph-algorithms.js +0 -236
  181. package/dist/esm/blueprint/dag/task-graph.d.ts +0 -171
  182. package/dist/esm/blueprint/dag/task-graph.js +0 -370
  183. package/dist/esm/blueprint/dag/types.d.ts +0 -17
  184. package/dist/esm/blueprint/dag/types.js +0 -2
  185. package/dist/esm/blueprint/graph/index.d.ts +0 -5
  186. package/dist/esm/blueprint/graph/index.js +0 -5
  187. package/dist/esm/blueprint/graph/mermaid-parser.d.ts +0 -3
  188. package/dist/esm/blueprint/graph/mermaid-parser.js +0 -93
  189. package/dist/esm/blueprint/graph/mermaid-serializer.d.ts +0 -3
  190. package/dist/esm/blueprint/graph/mermaid-serializer.js +0 -20
  191. package/dist/esm/blueprint/graph/schema.d.ts +0 -89
  192. package/dist/esm/blueprint/graph/schema.js +0 -104
  193. package/dist/esm/blueprint/graph/task-graph-adapter.d.ts +0 -6
  194. package/dist/esm/blueprint/graph/task-graph-adapter.js +0 -30
@@ -11,5 +11,6 @@ export declare function getNonCanonicalPlanningPathViolation(filePath: string, b
11
11
  * accepted blueprints root layout (or the explicitly provided root).
12
12
  */
13
13
  export declare function isCanonicalBlueprintOverviewPath(filePath: string, blueprintsRoot?: string): boolean;
14
- export declare function getBlueprintPathViolation(filePath: string, blueprintsRoot?: string): string | null;
14
+ export declare function isCanonicalBlueprintDocumentPath(filePath: string, blueprintsRoot?: string): boolean;
15
+ export declare function getBlueprintPathViolation(filePath: string, blueprintsRoot?: string, cwd?: string): string | null;
15
16
  //# sourceMappingURL=path-contract.d.ts.map
@@ -1,16 +1,10 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { BLUEPRINT_OVERVIEW_FILENAME, getBlueprintAlternateDocumentPath, isBlueprintSupportingMarkdownRelativePath, isBlueprintStatus, parseBlueprintDocumentRelativePath, } from '#utils/document-paths.js';
1
4
  export const BLUEPRINTS_ROOT = 'webpresso/blueprints';
2
5
  const DEFAULT_BLUEPRINTS_ROOT = 'blueprints';
3
6
  export const TECH_DEBT_ROOT = 'webpresso/tech-debt';
4
7
  const DEFAULT_TECH_DEBT_ROOT = 'tech-debt';
5
- const BLUEPRINT_STATUSES = new Set([
6
- 'draft',
7
- 'planned',
8
- 'parked',
9
- 'in-progress',
10
- 'completed',
11
- 'archived',
12
- ]);
13
- const KEBAB_CASE_SEGMENT = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
14
8
  // Both canonical blueprint-root layouts accepted by default.
15
9
  const CANONICAL_BLUEPRINTS_ROOTS = [BLUEPRINTS_ROOT, DEFAULT_BLUEPRINTS_ROOT];
16
10
  const CANONICAL_TECH_DEBT_ROOTS = [TECH_DEBT_ROOT, DEFAULT_TECH_DEBT_ROOT];
@@ -20,6 +14,16 @@ function normalizePlanningPath(filePath) {
20
14
  function matchesRoot(normalized, root) {
21
15
  return normalized === root || normalized.startsWith(`${root}/`);
22
16
  }
17
+ function stripBlueprintRoot(normalized, roots) {
18
+ for (const root of roots) {
19
+ if (normalized === root)
20
+ return { relativePath: '', root };
21
+ if (normalized.startsWith(`${root}/`)) {
22
+ return { relativePath: normalized.slice(root.length + 1), root };
23
+ }
24
+ }
25
+ return null;
26
+ }
23
27
  /**
24
28
  * Returns true if the path is under any accepted blueprints root.
25
29
  * Pass `blueprintsRoot` to restrict to a single configured root.
@@ -66,39 +70,60 @@ export function getNonCanonicalPlanningPathViolation(filePath, blueprintsRoot, t
66
70
  * accepted blueprints root layout (or the explicitly provided root).
67
71
  */
68
72
  export function isCanonicalBlueprintOverviewPath(filePath, blueprintsRoot) {
73
+ const parsed = getCanonicalBlueprintDocument(filePath, blueprintsRoot);
74
+ return parsed?.shape === 'folder';
75
+ }
76
+ export function isCanonicalBlueprintDocumentPath(filePath, blueprintsRoot) {
77
+ return getCanonicalBlueprintDocument(filePath, blueprintsRoot) !== null;
78
+ }
79
+ function getCanonicalBlueprintDocument(filePath, blueprintsRoot) {
69
80
  const normalized = normalizePlanningPath(filePath);
70
81
  const roots = blueprintsRoot ? [blueprintsRoot] : CANONICAL_BLUEPRINTS_ROOTS;
71
- return roots.some((root) => {
72
- const rootParts = root.split('/');
73
- const parts = normalized.split('/');
74
- const n = rootParts.length;
75
- return (parts.length === n + 3 &&
76
- parts.slice(0, n).join('/') === root &&
77
- BLUEPRINT_STATUSES.has(parts[n] ?? '') &&
78
- KEBAB_CASE_SEGMENT.test(parts[n + 1] ?? '') &&
79
- parts[n + 2] === '_overview.md');
80
- });
82
+ const stripped = stripBlueprintRoot(normalized, roots);
83
+ return stripped ? parseBlueprintDocumentRelativePath(stripped.relativePath) : null;
81
84
  }
82
- export function getBlueprintPathViolation(filePath, blueprintsRoot) {
85
+ export function getBlueprintPathViolation(filePath, blueprintsRoot, cwd = process.cwd()) {
83
86
  const normalized = normalizePlanningPath(filePath);
84
87
  if (!isBlueprintPath(normalized, blueprintsRoot))
85
88
  return null;
86
- if (normalized.endsWith('/_overview.md') &&
87
- !isCanonicalBlueprintOverviewPath(normalized, blueprintsRoot)) {
88
- const root = blueprintsRoot ?? BLUEPRINTS_ROOT;
89
- return `Blueprint overview files must live at ${root}/<status>/<slug>/_overview.md. Got: ${normalized}`;
90
- }
91
89
  const roots = blueprintsRoot ? [blueprintsRoot] : CANONICAL_BLUEPRINTS_ROOTS;
92
- for (const root of roots) {
93
- const rootParts = root.split('/');
94
- const parts = normalized.split('/');
95
- const n = rootParts.length;
96
- if (parts.length === n + 2 &&
97
- parts.slice(0, n).join('/') === root &&
98
- BLUEPRINT_STATUSES.has(parts[n] ?? '') &&
99
- normalized.endsWith('.md')) {
100
- return `Blueprint markdown files cannot live directly under a status directory. Move this file to ${root}/${parts[n]}/<slug>/_overview.md or place supporting docs inside ${root}/${parts[n]}/<slug>/. Got: ${normalized}`;
90
+ const stripped = stripBlueprintRoot(normalized, roots);
91
+ if (!stripped) {
92
+ return null;
93
+ }
94
+ const parsed = parseBlueprintDocumentRelativePath(stripped.relativePath);
95
+ if (parsed) {
96
+ const blueprintRoot = path.isAbsolute(filePath)
97
+ ? path.join(path.parse(filePath).root, stripped.root)
98
+ : path.join(cwd, stripped.root);
99
+ const currentPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, normalized);
100
+ const alternate = getBlueprintAlternateDocumentPath(blueprintRoot, currentPath);
101
+ if (alternate && existsSync(alternate)) {
102
+ return `Blueprint slug "${parsed.state}/${parsed.slug}" cannot exist in both flat and folder forms. Remove either ${path.relative(cwd, filePath).replace(/\\/g, '/')} or ${path.relative(cwd, alternate).replace(/\\/g, '/')}.`;
101
103
  }
104
+ return null;
105
+ }
106
+ const parts = stripped.relativePath.split('/').filter((segment) => segment.length > 0);
107
+ const [state, slug, doc] = parts;
108
+ const root = stripped.root;
109
+ if (parts.length === 2 &&
110
+ typeof doc === 'undefined' &&
111
+ typeof slug === 'string' &&
112
+ slug.endsWith('.md')) {
113
+ return `Blueprint markdown under ${root}/<status>/ must be either <slug>.md or <slug>/${BLUEPRINT_OVERVIEW_FILENAME}. Got: ${normalized}`;
114
+ }
115
+ if (parts.length === 3 && doc === BLUEPRINT_OVERVIEW_FILENAME) {
116
+ return `Blueprint overview files must live at ${root}/<status>/<slug>/${BLUEPRINT_OVERVIEW_FILENAME}. Got: ${normalized}`;
117
+ }
118
+ if (parts.length === 3 && isBlueprintSupportingMarkdownRelativePath(stripped.relativePath)) {
119
+ const canonicalOverviewPath = path.join(cwd, root, state ?? '', slug ?? '', BLUEPRINT_OVERVIEW_FILENAME);
120
+ if (!existsSync(canonicalOverviewPath)) {
121
+ return `Supporting blueprint markdown requires ${root}/${state}/${slug}/${BLUEPRINT_OVERVIEW_FILENAME}. Got: ${normalized}`;
122
+ }
123
+ return null;
124
+ }
125
+ if (parts.length >= 3 && isBlueprintStatus(state)) {
126
+ return `Blueprint markdown must use one of ${root}/<status>/<slug>.md or ${root}/<status>/<slug>/${BLUEPRINT_OVERVIEW_FILENAME}. Supporting markdown is only allowed beside ${BLUEPRINT_OVERVIEW_FILENAME}. Got: ${normalized}`;
102
127
  }
103
128
  return null;
104
129
  }
@@ -1,6 +1,6 @@
1
1
  import jsYaml from 'js-yaml';
2
2
  import { getContent, getFilePath } from '#hooks/shared/types';
3
- import { getNonCanonicalPlanningPathViolation, isBlueprintPath } from './path-contract.js';
3
+ import { getNonCanonicalPlanningPathViolation, isBlueprintPath, isCanonicalBlueprintDocumentPath, } from './path-contract.js';
4
4
  import { createSkipResult } from './skip-result.js';
5
5
  // Keep aligned with webpresso/blueprint planStatusSchema + plan type enum.
6
6
  const VALID_TYPES = ['blueprint', 'parent-roadmap'];
@@ -10,8 +10,8 @@ function shouldValidatePath(filePath) {
10
10
  const normalized = filePath.startsWith('/') ? filePath.slice(1) : filePath;
11
11
  const nonCanonicalPlanningPath = getNonCanonicalPlanningPathViolation(normalized);
12
12
  const currentPath = isBlueprintPath(normalized);
13
- const isOverviewFile = normalized.endsWith('/README.md') || normalized.endsWith('/_overview.md');
14
- return !nonCanonicalPlanningPath && currentPath && isOverviewFile;
13
+ const isCanonicalBlueprintDoc = isCanonicalBlueprintDocumentPath(normalized);
14
+ return !nonCanonicalPlanningPath && currentPath && isCanonicalBlueprintDoc;
15
15
  }
16
16
  export function extractFrontmatterBlock(content) {
17
17
  const match = content.match(/^---\n([\s\S]*?)\n---/);
@@ -8,6 +8,7 @@ export const WP_ROUTING_BLOCK = `<wp_routing>
8
8
  <description>
9
9
  Use the wp_* MCP tools for all test, lint, typecheck, qa, audit, local CI act,
10
10
  and Cloudflare Worker tail operations.
11
+ If a wp_* MCP tool is stale or unavailable, use the matching wp CLI command.
11
12
  If context-mode plugin routing is present, let it own ctx_* data-processing nudges.
12
13
  These tools return structured, summary-first results and keep output concise.
13
14
  </description>
@@ -55,7 +56,7 @@ export const WP_ROUTING_BLOCK = `<wp_routing>
55
56
  <tool name="wp_test">
56
57
  <category>dev-workflow</category>
57
58
  <trigger>running tests, verifying test suite, check if tests pass</trigger>
58
- <forbidden>just test, pnpm test, vitest</forbidden>
59
+ <forbidden>just test, pnpm test, vitest, npx vitest, npm exec -- vitest, yarn vitest, bunx vitest, node ./node_modules/vitest/vitest.mjs</forbidden>
59
60
  </tool>
60
61
  <tool name="wp_e2e">
61
62
  <category>dev-workflow</category>
@@ -65,12 +66,12 @@ export const WP_ROUTING_BLOCK = `<wp_routing>
65
66
  <tool name="wp_lint">
66
67
  <category>dev-workflow</category>
67
68
  <trigger>linting, code style checks, lint errors</trigger>
68
- <forbidden>just lint, oxlint</forbidden>
69
+ <forbidden>just lint, oxlint, node ./node_modules/oxlint/bin/oxlint</forbidden>
69
70
  </tool>
70
71
  <tool name="wp_typecheck">
71
72
  <category>dev-workflow</category>
72
73
  <trigger>type checking, TypeScript errors, type errors</trigger>
73
- <forbidden>tsc</forbidden>
74
+ <forbidden>tsc, node ./node_modules/typescript/bin/tsc</forbidden>
74
75
  </tool>
75
76
  <tool name="wp_qa">
76
77
  <category>dev-workflow</category>
@@ -102,6 +103,11 @@ export const WP_ROUTING_BLOCK = `<wp_routing>
102
103
  <rule>Context-mode owns ctx_* routing when that plugin is installed.</rule>
103
104
  </ownership_boundary>
104
105
 
106
+ <hook_diagnostics>
107
+ <rule>Prefer wp hook &lt;name&gt; over direct wp-&lt;hook-bin&gt; calls when a wp hook command exists.</rule>
108
+ <rule>Direct wp-* hook bins remain generated-hook runtime internals, not recommended agent diagnostics.</rule>
109
+ </hook_diagnostics>
110
+
105
111
  <package_guidance>
106
112
  <rule>Consumers add @webpresso/agent-kit and import config helpers through @webpresso/agent-kit/* subpath exports such as @webpresso/agent-kit/oxlint, @webpresso/agent-kit/vitest/node, @webpresso/agent-kit/test-preset, @webpresso/agent-kit/e2e-preset, @webpresso/agent-kit/tsconfig/base.json, @webpresso/agent-kit/docs-lint, @webpresso/agent-kit/stryker, @webpresso/agent-kit/launch, and @webpresso/agent-kit/workers-test.</rule>
107
113
  <rule>Do not recommend adding retired split agent config packages for consumer projects; keep wp_* MCP tool names and wp-* hook bin names unchanged.</rule>
@@ -114,9 +120,16 @@ export const WP_ROUTING_BLOCK = `<wp_routing>
114
120
  <command>just qa</command>
115
121
  <command>just lint-md</command>
116
122
  <command>vitest</command>
123
+ <command>npx vitest</command>
124
+ <command>npm exec -- vitest</command>
125
+ <command>yarn vitest</command>
126
+ <command>bunx vitest</command>
127
+ <command>node ./node_modules/vitest/vitest.mjs</command>
117
128
  <command>oxlint</command>
129
+ <command>node ./node_modules/oxlint/bin/oxlint</command>
118
130
  <command>markdownlint-cli2</command>
119
131
  <command>tsc</command>
132
+ <command>node ./node_modules/typescript/bin/tsc</command>
120
133
  <command>act</command>
121
134
  <command>vp exec act</command>
122
135
  <command>pnpm exec act</command>
@@ -133,7 +146,8 @@ export const WP_ROUTING_BLOCK = `<wp_routing>
133
146
  </output_format>
134
147
 
135
148
  <fallback>
136
- When MCP tools are unavailable, use just recipes directly and keep output brief.
149
+ When MCP tools are unavailable or stale, use the matching wp CLI command and keep output brief.
150
+ Do not fall through to raw tool bins under node_modules when a wp wrapper exists.
137
151
  .omx is runtime/state only; it is not a direct hook surface.
138
152
  </fallback>
139
153
  </wp_routing>`;
@@ -26,6 +26,9 @@ export function shouldSkipFile(filePath) {
26
26
  if (!filePath)
27
27
  return false;
28
28
  const normalized = filePath.startsWith('/') ? filePath.slice(1) : filePath;
29
+ if (normalized.startsWith('blueprints/') || normalized.startsWith('webpresso/blueprints/')) {
30
+ return false;
31
+ }
29
32
  return SKIP_PATTERNS.some((pattern) => pattern.test(normalized));
30
33
  }
31
34
  export function getSkipReason(filePath) {
@@ -11,4 +11,5 @@ export type StopHookResult = {
11
11
  systemMessage: string;
12
12
  };
13
13
  export declare function formatStopHookOutput(result: StopHookResult): string;
14
+ export declare function main(): Promise<void>;
14
15
  //# sourceMappingURL=qa-changed-files.d.ts.map
@@ -85,12 +85,15 @@ export function runQaChecks(qaFiles, projectDir) {
85
85
  export function formatStopHookOutput(result) {
86
86
  return JSON.stringify(result);
87
87
  }
88
- if (process.argv[1] &&
89
- realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
88
+ export async function main() {
90
89
  runHook(
91
90
  // `Stop` is latency-sensitive and user-visible. Until webpresso grows a
92
91
  // deferred execution plane, broad typecheck/test sweeps stay off the hot
93
92
  // path instead of shelling synchronously at turn end.
94
93
  (_input) => null, formatStopHookOutput);
95
94
  }
95
+ if (process.argv[1] &&
96
+ realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
97
+ void main();
98
+ }
96
99
  //# sourceMappingURL=qa-changed-files.js.map
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { isRunFailure, runCommand } from '#mcp/tools/_shared/run-command';
10
10
  import { resolveProjectRoot } from '#mcp/tools/_shared/project-root';
11
+ import { getManagedRunner } from '#tool-runtime';
11
12
  const DEFAULT_LINT_TIMEOUT_MS = 5 * 60 * 1_000;
12
13
  /**
13
14
  * Parse oxlint's `--format=json` output (ESLint-compatible array shape) into
@@ -92,7 +93,8 @@ export async function runLint(options = {}) {
92
93
  else {
93
94
  lintArgs.push('.');
94
95
  }
95
- const vpOutcome = await runCommand('vp', lintArgs, runOptions);
96
+ const resolution = getManagedRunner('vp', { outputPolicy: 'structured' });
97
+ const vpOutcome = await runCommand(resolution.command, [...resolution.args, ...lintArgs], runOptions);
96
98
  if (isRunFailure(vpOutcome)) {
97
99
  return {
98
100
  passed: false,
@@ -61,5 +61,7 @@ export interface ToolDescriptor {
61
61
  export interface ToolRegistrar {
62
62
  registerTool(name: string, description: string, jsonSchema: Record<string, unknown>, outputSchema: Record<string, unknown> | undefined, handler: ToolHandler, annotations?: ToolAnnotations): void;
63
63
  }
64
+ export declare function registerToolDescriptor(server: ToolRegistrar, descriptor: ToolDescriptor): ToolDescriptor;
65
+ export declare function registerToolDescriptors(server: ToolRegistrar, descriptors: readonly ToolDescriptor[]): ToolDescriptor[];
64
66
  export declare function discoverTools(server: ToolRegistrar, toolsDir: string): Promise<ToolDescriptor[]>;
65
67
  //# sourceMappingURL=auto-discover.d.ts.map
@@ -15,9 +15,20 @@ import { extname, join } from 'node:path';
15
15
  import { pathToFileURL } from 'node:url';
16
16
  import { z } from 'zod';
17
17
  import { zodToJsonSchema } from 'zod-to-json-schema';
18
+ export function registerToolDescriptor(server, descriptor) {
19
+ const jsonSchema = toJsonSchema(descriptor.inputSchema);
20
+ const outputSchema = descriptor.outputSchema ? toJsonSchema(descriptor.outputSchema) : undefined;
21
+ server.registerTool(descriptor.name, descriptor.description, jsonSchema, outputSchema, descriptor.handler, descriptor.annotations);
22
+ return descriptor;
23
+ }
24
+ export function registerToolDescriptors(server, descriptors) {
25
+ return descriptors.map((descriptor) => registerToolDescriptor(server, descriptor));
26
+ }
18
27
  const SKIP_SUFFIXES = ['.test.ts', '.test.js', '.integration.test.ts', '.integration.test.js'];
19
28
  const SUPPORTED_EXTENSIONS = new Set(['.ts', '.js', '.mjs', '.cjs']);
20
29
  function shouldSkip(file) {
30
+ if (file.startsWith('_'))
31
+ return true;
21
32
  if (file.endsWith('.d.ts') || file.endsWith('.d.ts.map'))
22
33
  return true;
23
34
  if (file.endsWith('.js.map') || file.endsWith('.ts.map'))
@@ -82,7 +93,7 @@ function toJsonSchema(schema) {
82
93
  }
83
94
  export async function discoverTools(server, toolsDir) {
84
95
  const entries = await readdir(toolsDir, { withFileTypes: true });
85
- const registered = [];
96
+ const loaded = [];
86
97
  for (const entry of entries) {
87
98
  if (!entry.isFile())
88
99
  continue;
@@ -98,11 +109,8 @@ export async function discoverTools(server, toolsDir) {
98
109
  if (typeof descriptor.name !== 'string' || typeof descriptor.handler !== 'function') {
99
110
  throw new Error(`Tool file ${fullPath} default export is malformed (missing name or handler)`);
100
111
  }
101
- const jsonSchema = toJsonSchema(descriptor.inputSchema);
102
- const outputSchema = descriptor.outputSchema ? toJsonSchema(descriptor.outputSchema) : undefined;
103
- server.registerTool(descriptor.name, descriptor.description, jsonSchema, outputSchema, descriptor.handler, descriptor.annotations);
104
- registered.push(descriptor);
112
+ loaded.push(descriptor);
105
113
  }
106
- return registered;
114
+ return registerToolDescriptors(server, loaded);
107
115
  }
108
116
  //# sourceMappingURL=auto-discover.js.map