@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
@@ -64,3 +64,36 @@ jobs:
64
64
  WP="$(pwd)/node_modules/.bin/wp"
65
65
  [ -x "$WP" ] || WP=wp
66
66
  "$WP" audit guardrails
67
+
68
+ deploy-contract:
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@v5
72
+ - uses: pnpm/action-setup@v6
73
+ - uses: actions/setup-node@v5
74
+ with:
75
+ node-version: '24.16.0'
76
+ cache: pnpm
77
+ - uses: oven-sh/setup-bun@v2
78
+ with:
79
+ bun-version: latest
80
+ - run: pnpm install --frozen-lockfile
81
+ - name: Verify deploy-contract gate when production release metadata is present
82
+ run: |
83
+ set -euo pipefail
84
+
85
+ METADATA_PATH='infra/release-metadata.production.json'
86
+
87
+ if [ ! -f "$METADATA_PATH" ]; then
88
+ echo "No production release metadata present at $METADATA_PATH; skipping deploy-contract gate."
89
+ exit 0
90
+ fi
91
+
92
+ if ! node -e "const pkg=require('./package.json'); process.exit(pkg.scripts && pkg.scripts['verify:deploy-contract'] ? 0 : 1)"; then
93
+ echo "::error::Detected $METADATA_PATH but no verify:deploy-contract script."
94
+ echo "::error::Repos adopting the shared deploy contract must keep agent-kit provider-neutral and supply a repo-owned deploy verifier."
95
+ echo "::error::That verifier owns provider-specific env-name derivation, Preview-URL prohibitions for DO previews, and must fail closed for migration-bearing Durable Object releases."
96
+ exit 1
97
+ fi
98
+
99
+ pnpm run verify:deploy-contract
@@ -1,6 +1,4 @@
1
- import type { UserConfig } from '@commitlint/types'
2
-
3
- const config: UserConfig = {
1
+ const config = {
4
2
  extends: ['@commitlint/config-conventional'],
5
3
  }
6
4
 
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Agent Kit smoke page</title>
6
+ </head>
7
+ <body>
8
+ <main>
9
+ <h1>Agent Kit quality scaffold</h1>
10
+ <p data-testid="status">ready</p>
11
+ </main>
12
+ </body>
13
+ </html>
@@ -0,0 +1,13 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { join } from 'node:path'
4
+
5
+ const smokePagePath = join(process.cwd(), 'e2e', 'fixtures', 'smoke.html')
6
+
7
+ test('checks the file-based smoke page', async () => {
8
+ const html = await readFile(smokePagePath, 'utf8')
9
+
10
+ expect(html).toContain('Agent Kit quality scaffold')
11
+ expect(html).toContain('data-testid="status"')
12
+ expect(html).toContain('ready')
13
+ })
@@ -0,0 +1,26 @@
1
+ import { config } from '@webpresso/agent-kit/oxlint'
2
+
3
+ const oxlintConfig: Record<string, unknown> = {
4
+ ...config,
5
+ env: {
6
+ builtin: true,
7
+ node: true,
8
+ },
9
+ ignorePatterns: [
10
+ 'dist',
11
+ 'node_modules',
12
+ 'reports',
13
+ '.stryker-tmp',
14
+ '.agent',
15
+ '.agents',
16
+ '.claude',
17
+ '.codex',
18
+ '.cursor',
19
+ '.gemini',
20
+ '.opencode',
21
+ '.omx',
22
+ '.windsurf',
23
+ ],
24
+ }
25
+
26
+ export default oxlintConfig
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from '@playwright/test'
2
+
3
+ export default defineConfig({
4
+ testDir: './e2e',
5
+ fullyParallel: true,
6
+ reporter: [['list']],
7
+ use: {
8
+ trace: 'retain-on-failure',
9
+ },
10
+ })
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { add, clamp } from './quality-sample.js'
4
+
5
+ describe('quality sample', () => {
6
+ it('adds two numbers', () => {
7
+ expect(add(2, 3)).toBe(5)
8
+ })
9
+
10
+ it('clamps values below, inside, and above the range', () => {
11
+ expect(clamp(-1, 0, 10)).toBe(0)
12
+ expect(clamp(5, 0, 10)).toBe(5)
13
+ expect(clamp(11, 0, 10)).toBe(10)
14
+ })
15
+
16
+ it('rejects an invalid range', () => {
17
+ expect(() => clamp(1, 10, 0)).toThrow(RangeError)
18
+ })
19
+ })
@@ -0,0 +1,11 @@
1
+ export function add(left: number, right: number): number {
2
+ return left + right
3
+ }
4
+
5
+ export function clamp(value: number, min: number, max: number): number {
6
+ if (min > max) {
7
+ throw new RangeError('min must be less than or equal to max')
8
+ }
9
+
10
+ return Math.min(Math.max(value, min), max)
11
+ }
@@ -0,0 +1,14 @@
1
+ import { typescriptBaseConfig } from '@webpresso/agent-kit/stryker'
2
+
3
+ export default {
4
+ ...typescriptBaseConfig,
5
+ thresholds: {
6
+ high: 0,
7
+ low: 0,
8
+ break: 0,
9
+ },
10
+ vitest: {
11
+ configFile: 'vitest.config.ts',
12
+ },
13
+ mutate: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'],
14
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./node_modules/@webpresso/agent-kit/tsconfig/base.json",
3
+ "compilerOptions": {
4
+ "types": ["node", "vitest/globals"],
5
+ "noEmit": true
6
+ },
7
+ "include": ["src/**/*.ts", "test/**/*.ts", "e2e/**/*.ts", "*.config.ts"],
8
+ "exclude": ["node_modules", "dist", "reports", ".stryker-tmp"]
9
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ include: ['src/**/*.test.ts'],
8
+ exclude: ['e2e/**', 'node_modules/**', '.stryker-tmp/**'],
9
+ },
10
+ })
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  type: adr
3
- last_updated: "YYYY-MM-DD"
3
+ last_updated: "2026-05-27"
4
4
  ---
5
5
 
6
6
  # ADR NNNN: {{Title}}
@@ -6,6 +6,7 @@ created: '2026-04-22'
6
6
  last_updated: '2026-04-22'
7
7
  progress: '0% (drafted)'
8
8
  depends_on: []
9
+ cross_repo_depends_on: []
9
10
  tags: []
10
11
  ---
11
12
 
@@ -20,6 +21,7 @@ tags: []
20
21
  - Draft slug: `{{slug}}`
21
22
  - Output path: `{{output_path}}`
22
23
  - Generated command: `wp blueprint new "{{description}}" --complexity {{complexity}}`
24
+ - Default shape: flat file (`blueprints/<status>/<slug>.md`)
23
25
  - Validation scope: parser compliance before write
24
26
 
25
27
  ## Architecture Overview
@@ -4,6 +4,7 @@ description: Blueprint template for features/initiatives
4
4
  notes:
5
5
  - 'This file describes the current preferred blueprint structure, not an immutable final schema.'
6
6
  - 'Repo-wide validity is determined by the live blueprint parser/audit rules, not by requiring every optional section below.'
7
+ - 'Default creation shape: blueprints/<status>/<slug>.md. Folder-based blueprints remain valid at blueprints/<status>/<slug>/_overview.md when sibling docs are needed.'
7
8
 
8
9
  frontmatter:
9
10
  required:
@@ -12,7 +13,7 @@ frontmatter:
12
13
  description: Must be exactly "blueprint"
13
14
  status:
14
15
  enum: [draft, planned, parked, in-progress, completed, archived]
15
- description: Current plan status (must match folder placement)
16
+ description: Current plan status (must match lifecycle placement)
16
17
  complexity:
17
18
  enum: [XS, S, M, L, XL]
18
19
  description: Estimated complexity
@@ -21,12 +22,15 @@ frontmatter:
21
22
  format: YYYY-MM-DD
22
23
  description: Last modification date
23
24
  optional:
24
- parent:
25
+ parent_roadmap:
25
26
  type: string
26
- description: Path to parent epic (e.g., ../README.md)
27
+ description: Local parent-roadmap slug/path in the same repo
27
28
  depends_on:
28
29
  type: array
29
- description: Dependencies on other plans
30
+ description: Local blueprint dependencies in the same repo
31
+ cross_repo_depends_on:
32
+ type: array
33
+ description: Cross-repo blueprint dependencies expressed as { repo, slug, require_status? }
30
34
 
31
35
  sections:
32
36
  required: []
@@ -78,21 +82,18 @@ sections:
78
82
 
79
83
  location:
80
84
  patterns:
81
- - 'blueprints/*/_overview.md'
85
+ - 'blueprints/*/*.md'
82
86
  - 'blueprints/*/*/_overview.md'
83
- - 'blueprints/*/*/*/_overview.md'
84
- - 'blueprints/completed/*/_overview.md'
85
- - 'blueprints/planned/*/_overview.md'
86
- - 'blueprints/parked/*/_overview.md'
87
- - 'blueprints/in-progress/*/_overview.md'
88
- - 'blueprints/draft/*/_overview.md'
89
- exclude:
90
- - 'blueprints/draft/*.md' # Standalone draft trackers such as deferred backlog
87
+ - 'blueprints/completed/*.md'
88
+ - 'blueprints/planned/*.md'
89
+ - 'blueprints/parked/*.md'
90
+ - 'blueprints/in-progress/*.md'
91
+ - 'blueprints/draft/*.md'
91
92
 
92
93
  naming:
93
- pattern: '_overview.md'
94
+ pattern: '<slug>.md or _overview.md'
94
95
  case: exact
95
- notes: 'Use _overview.md for high-level summary. Detailed phases go in phase-N-name.md files.'
96
+ notes: 'Use <slug>.md by default. Use folder + _overview.md when the blueprint needs sibling markdown docs.'
96
97
 
97
98
  task_format:
98
99
  heading:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  type: guide
3
- last_updated: "YYYY-MM-DD"
3
+ last_updated: "2026-05-27"
4
4
  ---
5
5
 
6
6
  # {{Title}}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  type: postmortem
3
- last_updated: "YYYY-MM-DD"
3
+ last_updated: "2026-05-27"
4
4
  ---
5
5
 
6
6
  # Postmortem: {{Incident Title}}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  type: research
3
- last_updated: "YYYY-MM-DD"
3
+ last_updated: "2026-05-27"
4
4
  ---
5
5
 
6
6
  # {{Research Title}}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  type: runbook
3
- last_updated: "YYYY-MM-DD"
3
+ last_updated: "2026-05-27"
4
4
  ---
5
5
 
6
6
  # Runbook: {{Scenario}}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  type: system
3
- last_updated: "YYYY-MM-DD"
3
+ last_updated: "2026-05-27"
4
4
  ---
5
5
 
6
6
  # {{System Name}}
@@ -11,8 +11,17 @@ last_updated: "YYYY-MM-DD"
11
11
 
12
12
  ## Architecture
13
13
 
14
- ```text
15
- [Diagram showing the main components and their edges]
14
+ ```mermaid
15
+ flowchart LR
16
+ A[Main component] --> B[Dependency or downstream system]
17
+ ```
18
+
19
+ ## Infrastructure / deployment
20
+
21
+ ```mermaid
22
+ flowchart LR
23
+ CI[Deploy entry point] --> RUNTIME[Runtime surface]
24
+ INFRA[Durable infrastructure owner] --> STATE[(Stateful resource)]
16
25
  ```
17
26
 
18
27
  ## Key invariants
@@ -5,6 +5,7 @@ severity: '{{severity}}'
5
5
  category: '{{category}}'
6
6
  review_cadence: quarterly
7
7
  last_reviewed: '{{date}}'
8
+ last_updated: '2026-05-27'
8
9
  created: '{{date}}'
9
10
  linked_blueprints: []
10
11
  affected_modules: []
@@ -8,21 +8,19 @@ Use the focused blueprint MCP tools.
8
8
  - `wp_blueprint_get` — fetch one blueprint with freshness metadata
9
9
  - `wp_blueprint_context` — assemble bounded task context
10
10
  - `wp_blueprint_create` — create a draft blueprint; requires `project_id` and accepts optional `request_id` and `head_at_ingest` for retry-safe, stale-write-safe creation
11
+ - `wp_blueprint_put` — whole-document structured authoring; writes the canonical blueprint markdown from typed input and returns revision metadata
12
+ - `wp_blueprint_transition` — optimistic-concurrency lifecycle transition; requires `expected_version` and returns updated revision metadata
11
13
  - `wp_blueprint_task_next` — return the next ready task; accepts optional `project_id` when the current cwd is a multi-repo workspace container
12
14
  - `wp_blueprint_task_advance` — change task status (non-`done`); requires `project_id` and accepts optional `request_id` and `head_at_ingest` for retry-safe mutation
13
15
  - `wp_blueprint_task_verify` — mark a task `done` with evidence; accepts optional `request_id` and `head_at_ingest` for retry-safe verification
14
16
  - `wp_blueprint_promote` / `wp_blueprint_finalize` — accept optional `project_id` for nested-workspace disambiguation
15
17
 
16
- Mutation guidance:
18
+ Guidance:
17
19
 
18
- - Use `request_id` on `wp_blueprint_create`, `wp_blueprint_task_advance`, and
19
- `wp_blueprint_task_verify` when the caller may retry the same request.
20
- - Prefer passing `project_id` from `wp_blueprint_projects` whenever the current
21
- working directory can see more than one blueprint-bearing repo.
22
- - Carry `head_at_ingest` from `wp_blueprint_list`, `wp_blueprint_get`, or
23
- `wp_blueprint_context` into mutation calls when the caller needs stale-write
24
- protection across retries or multi-agent handoff.
25
- - Reusing the same `request_id` with the same payload is idempotent.
26
- - Reusing the same `request_id` with a different payload is rejected.
27
- - If `head_at_ingest` is stale, the mutation is rejected and points the caller
28
- back to a canonical `wp_*` refresh path.
20
+ - Prefer `project_id` from `wp_blueprint_projects` when multiple repos are visible.
21
+ - Use `request_id` for retry-safe mutations and reuse it only with the same payload.
22
+ - Carry `head_at_ingest` from read/context tools into stale-write-sensitive mutations.
23
+ - Author documents through `wp_blueprint_put`, then lifecycle with `wp_blueprint_transition`.
24
+ - Deferred `wp_blueprint_patch` semantic ops (`add_task`, `update_task`, `set_summary`, `replace_decision`) are future layers; patch is **not** part of the v1 canonical surface.
25
+ - MCP Apps editor support is a follow-on enhancement over `wp_blueprint_put` / `wp_blueprint_transition`.
26
+ - Hosts without MCP Apps support keep using the structured tools above.
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Checks (when enabled):
9
9
  * 1. Every `blueprints` row's `file_path` actually exists on disk.
10
- * 2. Every blueprint `_overview.md` on disk has a corresponding DB row.
10
+ * 2. Every canonical blueprint markdown file on disk has a corresponding DB row.
11
11
  * 3. `content_hash` in DB matches the current SHA-256 of the file content.
12
12
  */
13
13
  import type { RepoAuditResult } from './repo-guardrails.js';
@@ -7,13 +7,13 @@
7
7
  *
8
8
  * Checks (when enabled):
9
9
  * 1. Every `blueprints` row's `file_path` actually exists on disk.
10
- * 2. Every blueprint `_overview.md` on disk has a corresponding DB row.
10
+ * 2. Every canonical blueprint markdown file on disk has a corresponding DB row.
11
11
  * 3. `content_hash` in DB matches the current SHA-256 of the file content.
12
12
  */
13
13
  import { createHash } from 'node:crypto';
14
14
  import { existsSync, readFileSync } from 'node:fs';
15
15
  import path from 'node:path';
16
- import { glob } from 'glob';
16
+ import { scanBlueprintDirectory } from '#service/scanner.js';
17
17
  const DB_PATH = path.join('.agent', '.blueprints.db');
18
18
  const _DISABLED_RESULT = {
19
19
  ok: true,
@@ -76,12 +76,10 @@ export async function auditBlueprintDbConsistency(cwd) {
76
76
  // 2: files on disk → verify each has a DB row
77
77
  // -----------------------------------------------------------------------
78
78
  const dbPaths = new Set(rows.map((r) => r.file_path));
79
- // Blueprints follow blueprints/<status>/<slug>/_overview.md convention
80
- const overviewFiles = await glob('blueprints/**/_overview.md', {
81
- cwd,
82
- absolute: false,
83
- ignore: ['node_modules/**'],
84
- });
79
+ const overviewFiles = scanBlueprintDirectory({
80
+ baseDir: path.join(cwd, 'blueprints'),
81
+ includeSpecialFolders: true,
82
+ }).map((entry) => path.relative(cwd, entry.path).replace(/\\/g, '/'));
85
83
  checked += overviewFiles.length;
86
84
  for (const rel of overviewFiles) {
87
85
  const normalised = rel.replace(/\\/g, '/');
@@ -45,6 +45,16 @@ export async function auditBlueprintLifecycleSql(cwd) {
45
45
  const violations = [];
46
46
  let checked = 0;
47
47
  try {
48
+ const allBlueprints = db
49
+ .prepare('SELECT slug, status, file_path, progress_pct FROM blueprints')
50
+ .all();
51
+ if (allBlueprints.length === 0) {
52
+ const { auditBlueprintLifecycle } = await import('./repo-guardrails.js');
53
+ const markdownAudit = auditBlueprintLifecycle(cwd);
54
+ if (markdownAudit.checked > 0) {
55
+ return markdownAudit;
56
+ }
57
+ }
48
58
  // -----------------------------------------------------------------------
49
59
  // 1. in-progress blueprints with 0 tasks
50
60
  // -----------------------------------------------------------------------
@@ -68,9 +78,6 @@ export async function auditBlueprintLifecycleSql(cwd) {
68
78
  // Derive the directory segment from the file_path and compare to status.
69
79
  // Blueprint file_path convention: blueprints/<status>/<slug>/_overview.md
70
80
  // -----------------------------------------------------------------------
71
- const allBlueprints = db
72
- .prepare('SELECT slug, status, file_path, progress_pct FROM blueprints')
73
- .all();
74
81
  checked += allBlueprints.length;
75
82
  for (const row of allBlueprints) {
76
83
  // Derive directory status from the path: second segment after 'blueprints/'
@@ -0,0 +1,3 @@
1
+ import type { RepoAuditResult } from './repo-guardrails.js';
2
+ export declare function auditCloudflareDeployContract(root: string): Promise<RepoAuditResult>;
3
+ //# sourceMappingURL=cloudflare-deploy-contract.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { loadWebpressoConfigSafe } from '#e2e/load-host-adapter';
4
+ function violation(file, message) {
5
+ return { file, message };
6
+ }
7
+ export async function auditCloudflareDeployContract(root) {
8
+ let loaded;
9
+ try {
10
+ loaded = await loadWebpressoConfigSafe({ cwd: root });
11
+ }
12
+ catch (error) {
13
+ const message = error instanceof Error ? error.message : String(error);
14
+ return {
15
+ ok: false,
16
+ title: 'Cloudflare deploy contract',
17
+ checked: 1,
18
+ violations: [violation(path.join(root, 'webpresso.config.ts'), message)],
19
+ };
20
+ }
21
+ const configPath = loaded?.configPath ?? path.join(root, 'webpresso.config.ts');
22
+ const cloudflare = loaded?.config.deploy?.cloudflare;
23
+ if (!cloudflare) {
24
+ return {
25
+ ok: true,
26
+ title: 'Cloudflare deploy contract',
27
+ checked: 0,
28
+ violations: [],
29
+ };
30
+ }
31
+ const violations = [];
32
+ const metadataPath = path.join(root, cloudflare.production.metadataPath);
33
+ if (!existsSync(metadataPath)) {
34
+ violations.push(violation(configPath, `shared deploy contract requires ${cloudflare.production.metadataPath} to exist`));
35
+ }
36
+ for (const target of cloudflare.targets) {
37
+ const isDurableObjectTarget = (target.durableObjectBindings?.length ?? 0) > 0;
38
+ if (target.previewTransport === 'custom_domain_env' && !target.routeSpec) {
39
+ violations.push(violation(configPath, `target ${target.id} uses custom_domain_env but does not declare routeSpec`));
40
+ }
41
+ if (target.durableObjectBindings && target.durableObjectBindings.length === 0) {
42
+ violations.push(violation(configPath, `target ${target.id} declares durableObjectBindings but provides no env-specific bindings`));
43
+ }
44
+ if (isDurableObjectTarget && target.previewTransport !== 'custom_domain_env') {
45
+ violations.push(violation(configPath, `target ${target.id} is a Durable Object consumer and must use previewTransport "custom_domain_env"`));
46
+ }
47
+ if (isDurableObjectTarget && Object.keys(target.vars).length === 0) {
48
+ violations.push(violation(configPath, `target ${target.id} is a Durable Object consumer and must declare at least one env-specific var`));
49
+ }
50
+ if (isDurableObjectTarget && target.requiredSecrets.length === 0) {
51
+ violations.push(violation(configPath, `target ${target.id} is a Durable Object consumer and must declare at least one required secret name`));
52
+ }
53
+ if (target.storageMode === 'shared_via_script_name' && !target.blastRadiusDoc) {
54
+ violations.push(violation(configPath, `target ${target.id} uses shared_via_script_name without blastRadiusDoc`));
55
+ }
56
+ }
57
+ return {
58
+ ok: violations.length === 0,
59
+ title: 'Cloudflare deploy contract',
60
+ checked: 1 + cloudflare.targets.length,
61
+ violations,
62
+ };
63
+ }
64
+ //# sourceMappingURL=cloudflare-deploy-contract.js.map
@@ -0,0 +1,3 @@
1
+ import type { RepoAuditResult } from './repo-guardrails.js';
2
+ export declare function auditNoLegacyCliBin(rootDirectory?: string): RepoAuditResult;
3
+ //# sourceMappingURL=no-legacy-cli-bin.d.ts.map
@@ -0,0 +1,100 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join, relative, resolve } from 'node:path';
3
+ // `wp` remains the canonical public CLI in AGENTS.md. This audit only guards
4
+ // retired compatibility aliases from leaking into active user-facing surfaces.
5
+ const LEGACY_COMMAND_PATTERN = /\b(?:ak|cli2|wk)\s+[a-z][\w:-]*(?:\s+[a-z][\w:-]*)*/giu;
6
+ const INTERNAL_HELPER_PATTERN = /\bwp-(?:pretool-guard|post-tool|stop-qa|guard-switch|sessionstart-routing|check-dev-link)\b/u;
7
+ const REPLACEMENT_PATTERN = /\bwebpresso agent [a-z][\w:-]*(?: [a-z][\w:-]*)*/iu;
8
+ const MIGRATION_MARKER_PATTERN = /\b(?:current-state|migration-only|replacement|future)\b/iu;
9
+ // Scan current user-facing/documentation inputs only. Source tests and completed/parked
10
+ // blueprints intentionally preserve historical command names as regression fixtures
11
+ // and audit evidence; blocking them would make this guardrail non-adoptable.
12
+ const SCAN_DIRS = [
13
+ 'catalog',
14
+ 'commands',
15
+ 'scripts',
16
+ 'test-fixtures',
17
+ 'blueprints/planned',
18
+ 'blueprints/in-progress',
19
+ ];
20
+ const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.agent', '.omx', '.codex']);
21
+ const TEXT_EXTENSIONS = new Set([
22
+ '.md',
23
+ '.mdx',
24
+ '.txt',
25
+ '.json',
26
+ '.yaml',
27
+ '.yml',
28
+ '.ts',
29
+ '.tsx',
30
+ '.js',
31
+ '.jsx',
32
+ '.sh',
33
+ ]);
34
+ export function auditNoLegacyCliBin(rootDirectory = process.cwd()) {
35
+ const root = resolve(rootDirectory);
36
+ const violations = [];
37
+ let checked = 0;
38
+ for (const dir of SCAN_DIRS) {
39
+ const absoluteDir = join(root, dir);
40
+ if (!existsSync(absoluteDir))
41
+ continue;
42
+ for (const absolutePath of walkTextFiles(absoluteDir)) {
43
+ checked += 1;
44
+ const relativePath = relative(root, absolutePath);
45
+ const content = readFileSync(absolutePath, 'utf8');
46
+ const lines = content.split('\n');
47
+ for (const [index, line] of lines.entries()) {
48
+ const matches = line.matchAll(LEGACY_COMMAND_PATTERN);
49
+ for (const match of matches) {
50
+ const snippet = match[0]?.trim();
51
+ if (!snippet)
52
+ continue;
53
+ if (isAllowedLegacyLine(line))
54
+ continue;
55
+ violations.push({
56
+ file: relativePath,
57
+ message: `Line ${index + 1} exposes legacy command ${JSON.stringify(snippet)} without current-state/migration context and an exact \`webpresso agent ...\` replacement.`,
58
+ });
59
+ }
60
+ }
61
+ }
62
+ }
63
+ return {
64
+ ok: violations.length === 0,
65
+ title: 'No retired CLI aliases in active docs/scripts',
66
+ checked,
67
+ violations,
68
+ };
69
+ }
70
+ function walkTextFiles(directory) {
71
+ const entries = readdirSync(directory, { withFileTypes: true });
72
+ const files = [];
73
+ for (const entry of entries) {
74
+ if (SKIP_DIRS.has(entry.name))
75
+ continue;
76
+ const absolutePath = join(directory, entry.name);
77
+ if (entry.isDirectory()) {
78
+ files.push(...walkTextFiles(absolutePath));
79
+ continue;
80
+ }
81
+ if (!entry.isFile())
82
+ continue;
83
+ const extension = entry.name.includes('.') ? `.${entry.name.split('.').pop()}` : '';
84
+ if (TEXT_EXTENSIONS.has(extension))
85
+ files.push(absolutePath);
86
+ }
87
+ return files;
88
+ }
89
+ function isAllowedLegacyLine(line) {
90
+ LEGACY_COMMAND_PATTERN.lastIndex = 0;
91
+ if (!LEGACY_COMMAND_PATTERN.test(line))
92
+ return true;
93
+ LEGACY_COMMAND_PATTERN.lastIndex = 0;
94
+ if (INTERNAL_HELPER_PATTERN.test(line))
95
+ return true;
96
+ if (!MIGRATION_MARKER_PATTERN.test(line))
97
+ return false;
98
+ return REPLACEMENT_PATTERN.test(line);
99
+ }
100
+ //# sourceMappingURL=no-legacy-cli-bin.js.map