@webpresso/agent-kit 0.28.0 → 0.29.1

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 (117) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/README.md +2 -2
  4. package/bin/_run.js +6 -0
  5. package/bin/wp +5 -0
  6. package/catalog/base-kit/.github/actions/setup-webpresso/action.yml.tmpl +21 -0
  7. package/catalog/base-kit/.github/workflows/{ci.webpresso.yml.tmpl → ci.yml.tmpl} +17 -7
  8. package/catalog/base-kit/tsconfig.json.tmpl +1 -1
  9. package/catalog/docs/templates/blueprint.yaml +1 -1
  10. package/dist/esm/audit/_budgets.d.ts +9 -1
  11. package/dist/esm/audit/_budgets.js +8 -1
  12. package/dist/esm/audit/blueprint-db-consistency.js +2 -2
  13. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +17 -7
  14. package/dist/esm/audit/blueprint-lifecycle-sql.js +298 -48
  15. package/dist/esm/audit/blueprint-readme-drift.d.ts +6 -0
  16. package/dist/esm/audit/blueprint-readme-drift.js +110 -0
  17. package/dist/esm/audit/no-first-party-mjs.js +5 -4
  18. package/dist/esm/audit/package-surface.js +79 -10
  19. package/dist/esm/audit/repo-guardrails.d.ts +1 -1
  20. package/dist/esm/audit/repo-guardrails.js +43 -3
  21. package/dist/esm/audit/tech-debt-cadence.js +2 -3
  22. package/dist/esm/audit/toolchain-isolation.js +2 -3
  23. package/dist/esm/blueprint/core/parser.js +3 -2
  24. package/dist/esm/blueprint/core/schema.d.ts +3 -2
  25. package/dist/esm/blueprint/core/schema.js +1 -1
  26. package/dist/esm/blueprint/cross-repo/audit.js +3 -4
  27. package/dist/esm/blueprint/db/cold-start.js +2 -3
  28. package/dist/esm/blueprint/db/enums.d.ts +1 -1
  29. package/dist/esm/blueprint/db/ephemeral-projection.d.ts +25 -0
  30. package/dist/esm/blueprint/db/ephemeral-projection.js +36 -0
  31. package/dist/esm/blueprint/db/gc.d.ts +11 -0
  32. package/dist/esm/blueprint/db/gc.js +55 -0
  33. package/dist/esm/blueprint/db/ingester.js +39 -1
  34. package/dist/esm/blueprint/db/migrations/run.js +5 -3
  35. package/dist/esm/blueprint/db/paths.d.ts +13 -24
  36. package/dist/esm/blueprint/db/paths.js +25 -33
  37. package/dist/esm/blueprint/execution/progress-bridge.js +5 -4
  38. package/dist/esm/blueprint/freshness.d.ts +2 -0
  39. package/dist/esm/blueprint/freshness.js +3 -1
  40. package/dist/esm/blueprint/lifecycle/audit.js +6 -6
  41. package/dist/esm/blueprint/lifecycle/engine.d.ts +1 -1
  42. package/dist/esm/blueprint/lifecycle/engine.js +13 -9
  43. package/dist/esm/blueprint/lifecycle/transition-matrix.d.ts +5 -0
  44. package/dist/esm/blueprint/lifecycle/transition-matrix.js +20 -0
  45. package/dist/esm/blueprint/markdown/helpers.d.ts +1 -1
  46. package/dist/esm/blueprint/projection-ready.js +2 -0
  47. package/dist/esm/blueprint/service/BlueprintService.js +1 -1
  48. package/dist/esm/blueprint/service/blueprint-records.js +1 -1
  49. package/dist/esm/blueprint/tracked-document/parser.js +1 -1
  50. package/dist/esm/blueprint/utils/archive.d.ts +2 -2
  51. package/dist/esm/blueprint/utils/archive.js +5 -2
  52. package/dist/esm/blueprint/utils/package-assets.d.ts +13 -0
  53. package/dist/esm/blueprint/utils/package-assets.js +38 -6
  54. package/dist/esm/build/normalize-tsconfig-json-exports.d.ts +13 -0
  55. package/dist/esm/build/normalize-tsconfig-json-exports.js +39 -0
  56. package/dist/esm/build/package-manifest.js +12 -4
  57. package/dist/esm/build/release-policy.d.ts +9 -18
  58. package/dist/esm/build/release-policy.js +10 -19
  59. package/dist/esm/build/runtime-surface-policy.d.ts +14 -0
  60. package/dist/esm/build/runtime-surface-policy.js +13 -0
  61. package/dist/esm/cli/commands/audit-core.d.ts +2 -2
  62. package/dist/esm/cli/commands/audit.js +7 -3
  63. package/dist/esm/cli/commands/blueprint/db-commands.js +0 -3
  64. package/dist/esm/cli/commands/blueprint/mutations.d.ts +3 -2
  65. package/dist/esm/cli/commands/blueprint/mutations.js +45 -39
  66. package/dist/esm/cli/commands/blueprint/router-output.js +2 -2
  67. package/dist/esm/cli/commands/doctor.d.ts +1 -1
  68. package/dist/esm/cli/commands/doctor.js +4 -5
  69. package/dist/esm/cli/commands/init/config.d.ts +6 -10
  70. package/dist/esm/cli/commands/init/config.js +36 -20
  71. package/dist/esm/cli/commands/init/gitignore-patcher.js +0 -1
  72. package/dist/esm/cli/commands/init/index.d.ts +8 -1
  73. package/dist/esm/cli/commands/init/index.js +17 -19
  74. package/dist/esm/cli/commands/init/package-root.d.ts +20 -0
  75. package/dist/esm/cli/commands/init/package-root.js +110 -0
  76. package/dist/esm/cli/commands/init/scaffold-base-kit.js +5 -1
  77. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +3 -0
  78. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +8 -24
  79. package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.d.ts +9 -0
  80. package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +79 -1
  81. package/dist/esm/cli/commands/init/scaffolders/claude-rules/index.js +2 -12
  82. package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +2 -12
  83. package/dist/esm/config/tsconfig/cloudflare.json +1 -1
  84. package/dist/esm/config/tsconfig/library.json +1 -1
  85. package/dist/esm/config/tsconfig/react-library.json +3 -2
  86. package/dist/esm/config/tsconfig/react-router.json +1 -1
  87. package/dist/esm/dev/restore-dev-links/index.js +3 -4
  88. package/dist/esm/docs-linter/blueprint-plan.js +46 -4
  89. package/dist/esm/hooks/check-dev-link/index.js +3 -4
  90. package/dist/esm/hooks/doctor.d.ts +11 -0
  91. package/dist/esm/hooks/doctor.js +174 -30
  92. package/dist/esm/hooks/guard-switch/index.js +3 -5
  93. package/dist/esm/hooks/post-tool/lint-after-edit.js +4 -5
  94. package/dist/esm/hooks/pretool-guard/index.js +2 -4
  95. package/dist/esm/hooks/pretool-guard/runner.js +2 -4
  96. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +47 -6
  97. package/dist/esm/hooks/sessionstart/index.js +3 -4
  98. package/dist/esm/hooks/shared/direct-entrypoint.d.ts +10 -0
  99. package/dist/esm/hooks/shared/direct-entrypoint.js +21 -0
  100. package/dist/esm/hooks/stop/qa-changed-files.js +3 -5
  101. package/dist/esm/hooks/test-quality-check.js +3 -4
  102. package/dist/esm/mcp/blueprint-server.js +26 -3
  103. package/dist/esm/mcp/cli.js +2 -6
  104. package/dist/esm/mcp/server.d.ts +2 -0
  105. package/dist/esm/mcp/server.js +18 -3
  106. package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
  107. package/dist/esm/mcp/tools/_shared/audit-kinds.js +1 -0
  108. package/dist/esm/mcp/tools/audit.d.ts +2 -1
  109. package/dist/esm/mcp/tools/audit.js +13 -3
  110. package/dist/esm/package.json +2 -0
  111. package/package.json +24 -15
  112. package/tsconfig/cloudflare.json +1 -1
  113. package/tsconfig/library.json +1 -1
  114. package/tsconfig/react-library.json +3 -2
  115. package/tsconfig/react-router.json +1 -1
  116. package/dist/esm/blueprint/db/legacy-migration.d.ts +0 -41
  117. package/dist/esm/blueprint/db/legacy-migration.js +0 -122
@@ -8,6 +8,7 @@ const DEPENDENCY_SECTIONS = [
8
8
  'optionalDependencies',
9
9
  'peerDependencies',
10
10
  ];
11
+ const NON_PUBLISHABLE_DEPENDENCY_PROTOCOLS = ['link:', 'workspace:', 'file:'];
11
12
  const BACKUP_FILENAME = '.package.json.prepack.backup';
12
13
  const DIST_BACKUP_DIRNAME = '.dist-prepack-backup';
13
14
  function asStringMap(value) {
@@ -61,16 +62,23 @@ export function resolveCatalogSpecifier(dependencyName, version, workspaceCatalo
61
62
  }
62
63
  return resolved;
63
64
  }
65
+ function assertPublishableDependencySpecifier(section, dependencyName, version) {
66
+ const blockedProtocol = NON_PUBLISHABLE_DEPENDENCY_PROTOCOLS.find((protocol) => version.startsWith(protocol));
67
+ if (!blockedProtocol)
68
+ return;
69
+ throw new Error(`Cannot pack ${section}.${dependencyName} with non-publishable ${blockedProtocol} specifier ${JSON.stringify(version)}`);
70
+ }
64
71
  export function createPackedManifest(manifest, workspaceCatalogs) {
65
72
  const packedManifest = { ...manifest };
66
73
  for (const section of DEPENDENCY_SECTIONS) {
67
74
  const dependencies = manifest[section];
68
75
  if (!dependencies)
69
76
  continue;
70
- packedManifest[section] = Object.fromEntries(Object.entries(dependencies).map(([dependencyName, version]) => [
71
- dependencyName,
72
- resolveCatalogSpecifier(dependencyName, version, workspaceCatalogs),
73
- ]));
77
+ packedManifest[section] = Object.fromEntries(Object.entries(dependencies).map(([dependencyName, version]) => {
78
+ const resolvedVersion = resolveCatalogSpecifier(dependencyName, version, workspaceCatalogs);
79
+ assertPublishableDependencySpecifier(section, dependencyName, resolvedVersion);
80
+ return [dependencyName, resolvedVersion];
81
+ }));
74
82
  }
75
83
  if ('bin' in packedManifest) {
76
84
  packedManifest.bin = normalizePackedBinField(packedManifest.bin);
@@ -1,27 +1,18 @@
1
1
  /**
2
- * Release-time policy decisions for the publish pipeline.
2
+ * Release-time policy decisions for the native runtime publish pipeline.
3
3
  *
4
- * The per-platform native runtime packages (`@webpresso/agent-kit-runtime-*`)
5
- * are a deferred capability: they are not yet created on the registry, the main
6
- * package declares no `optionalDependencies` on them, and the plugin manifest
7
- * still launches via the node entrypoint rather than a staged native `bin/wp`.
8
- *
9
- * Until that native-distribution work is intentionally activated, the release
10
- * MUST NOT attempt to publish the matrix: a first-time `npm publish` of a
11
- * never-created scoped package returns 404, which previously aborted the entire
12
- * release before the main `@webpresso/agent-kit` package published (the root
13
- * cause of the 0.22.x publish stall).
14
- *
15
- * The matrix publish is therefore gated behind an explicit opt-in. When the
16
- * native-distribution feature lands, the release workflow sets
17
- * `WP_PUBLISH_RUNTIME_MATRIX=1` (after bootstrapping the scoped packages on the
18
- * registry) to turn it back on.
4
+ * The canonical package surface now includes the per-platform
5
+ * `@webpresso/agent-kit-runtime-*` packages plus the staged native `bin/wp`
6
+ * launcher, so release publishes the runtime matrix by default. Operators may
7
+ * still set `WP_PUBLISH_RUNTIME_MATRIX=0` to force a diagnostics-only dry lane
8
+ * when debugging a broken registry/bootstrap environment, but the normal
9
+ * shipping path is "publish the matrix".
19
10
  */
20
11
  export declare const PUBLISH_RUNTIME_MATRIX_ENV = "WP_PUBLISH_RUNTIME_MATRIX";
21
12
  /**
22
13
  * Whether the release pipeline should build, stage, and publish the per-platform
23
- * native runtime matrix. Defaults to `false` (matrix deferred); enabled only
24
- * when `WP_PUBLISH_RUNTIME_MATRIX=1` is set explicitly.
14
+ * native runtime matrix. Defaults to `true`; only an explicit
15
+ * `WP_PUBLISH_RUNTIME_MATRIX=0` disables the matrix.
25
16
  */
26
17
  export declare function shouldPublishRuntimeMatrix(env: NodeJS.ProcessEnv): boolean;
27
18
  //# sourceMappingURL=release-policy.d.ts.map
@@ -1,29 +1,20 @@
1
1
  /**
2
- * Release-time policy decisions for the publish pipeline.
2
+ * Release-time policy decisions for the native runtime publish pipeline.
3
3
  *
4
- * The per-platform native runtime packages (`@webpresso/agent-kit-runtime-*`)
5
- * are a deferred capability: they are not yet created on the registry, the main
6
- * package declares no `optionalDependencies` on them, and the plugin manifest
7
- * still launches via the node entrypoint rather than a staged native `bin/wp`.
8
- *
9
- * Until that native-distribution work is intentionally activated, the release
10
- * MUST NOT attempt to publish the matrix: a first-time `npm publish` of a
11
- * never-created scoped package returns 404, which previously aborted the entire
12
- * release before the main `@webpresso/agent-kit` package published (the root
13
- * cause of the 0.22.x publish stall).
14
- *
15
- * The matrix publish is therefore gated behind an explicit opt-in. When the
16
- * native-distribution feature lands, the release workflow sets
17
- * `WP_PUBLISH_RUNTIME_MATRIX=1` (after bootstrapping the scoped packages on the
18
- * registry) to turn it back on.
4
+ * The canonical package surface now includes the per-platform
5
+ * `@webpresso/agent-kit-runtime-*` packages plus the staged native `bin/wp`
6
+ * launcher, so release publishes the runtime matrix by default. Operators may
7
+ * still set `WP_PUBLISH_RUNTIME_MATRIX=0` to force a diagnostics-only dry lane
8
+ * when debugging a broken registry/bootstrap environment, but the normal
9
+ * shipping path is "publish the matrix".
19
10
  */
20
11
  export const PUBLISH_RUNTIME_MATRIX_ENV = 'WP_PUBLISH_RUNTIME_MATRIX';
21
12
  /**
22
13
  * Whether the release pipeline should build, stage, and publish the per-platform
23
- * native runtime matrix. Defaults to `false` (matrix deferred); enabled only
24
- * when `WP_PUBLISH_RUNTIME_MATRIX=1` is set explicitly.
14
+ * native runtime matrix. Defaults to `true`; only an explicit
15
+ * `WP_PUBLISH_RUNTIME_MATRIX=0` disables the matrix.
25
16
  */
26
17
  export function shouldPublishRuntimeMatrix(env) {
27
- return env[PUBLISH_RUNTIME_MATRIX_ENV] === '1';
18
+ return env[PUBLISH_RUNTIME_MATRIX_ENV] !== '0';
28
19
  }
29
20
  //# sourceMappingURL=release-policy.js.map
@@ -0,0 +1,14 @@
1
+ export declare const AGENT_KIT_TARBALL_SIZE_BUDGET_BYTES = 29704296;
2
+ export declare const AGENT_KIT_TARBALL_UNPACKED_SIZE_BUDGET_BYTES = 86643227;
3
+ export interface TarballSizeBudgetInput {
4
+ readonly size?: number;
5
+ readonly unpackedSize?: number;
6
+ }
7
+ export interface TarballSizeBudgetResult {
8
+ readonly sizeOk: boolean;
9
+ readonly unpackedOk: boolean;
10
+ readonly size: number;
11
+ readonly unpackedSize: number;
12
+ }
13
+ export declare function evaluateAgentKitTarballSizeBudget(packSummary: TarballSizeBudgetInput): TarballSizeBudgetResult;
14
+ //# sourceMappingURL=runtime-surface-policy.d.ts.map
@@ -0,0 +1,13 @@
1
+ export const AGENT_KIT_TARBALL_SIZE_BUDGET_BYTES = 29_704_296;
2
+ export const AGENT_KIT_TARBALL_UNPACKED_SIZE_BUDGET_BYTES = 86_643_227;
3
+ export function evaluateAgentKitTarballSizeBudget(packSummary) {
4
+ const size = packSummary.size ?? 0;
5
+ const unpackedSize = packSummary.unpackedSize ?? 0;
6
+ return {
7
+ sizeOk: size <= AGENT_KIT_TARBALL_SIZE_BUDGET_BYTES,
8
+ unpackedOk: unpackedSize <= AGENT_KIT_TARBALL_UNPACKED_SIZE_BUDGET_BYTES,
9
+ size,
10
+ unpackedSize,
11
+ };
12
+ }
13
+ //# sourceMappingURL=runtime-surface-policy.js.map
@@ -1,5 +1,5 @@
1
1
  import type { RepoAuditResult } from '#audit/repo-guardrails';
2
- export type AuditKind = 'tph' | 'tph-e2e' | 'bundle-budget' | 'commit-message' | 'blueprint-lifecycle' | 'roadmap-links' | 'docs-frontmatter' | 'catalog-drift' | 'package-surface' | 'agents' | 'tech-debt' | 'no-relative-parent-imports' | 'no-link-protocol' | 'vision' | 'bucket-boundary' | 'skill-sizes' | 'broken-refs' | 'memory-rotation' | 'gitignore-agent-surfaces' | 'memory-unified' | 'compile-drift' | 'architecture-drift' | 'cloudflare-deploy-contract' | 'toolchain-isolation' | 'absolute-path-policy' | 'no-first-party-mjs' | 'agent-cost' | 'blueprint-db-consistency' | 'blueprint-lifecycle-sql' | 'tech-debt-cadence' | 'cross-repo-correlation' | 'ai-contracts' | 'mutation' | 'quality' | 'guardrails' | 'hook-surface' | 'no-relative-package-scripts';
2
+ export type AuditKind = 'tph' | 'tph-e2e' | 'bundle-budget' | 'commit-message' | 'blueprint-readme-drift' | 'blueprint-lifecycle' | 'roadmap-links' | 'docs-frontmatter' | 'catalog-drift' | 'package-surface' | 'agents' | 'tech-debt' | 'no-relative-parent-imports' | 'no-link-protocol' | 'vision' | 'bucket-boundary' | 'skill-sizes' | 'broken-refs' | 'memory-rotation' | 'gitignore-agent-surfaces' | 'memory-unified' | 'compile-drift' | 'architecture-drift' | 'cloudflare-deploy-contract' | 'toolchain-isolation' | 'absolute-path-policy' | 'no-first-party-mjs' | 'agent-cost' | 'blueprint-db-consistency' | 'tech-debt-cadence' | 'cross-repo-correlation' | 'ai-contracts' | 'mutation' | 'quality' | 'guardrails' | 'hook-surface' | 'no-relative-package-scripts';
3
3
  export type AuditOutcome = {
4
4
  kind: 'invalid-usage';
5
5
  message: string;
@@ -34,7 +34,7 @@ export interface AuditActionOptions {
34
34
  htmlEntry?: string;
35
35
  ignore?: string | string[];
36
36
  json?: boolean;
37
- legacyOmx?: boolean;
37
+ omxPlans?: boolean;
38
38
  loreWarn?: boolean;
39
39
  maxHtmlEagerJsAssetBytes?: string;
40
40
  maxHtmlEagerJsTotalBytes?: string;
@@ -13,7 +13,12 @@ import { resolveAuditScriptPath } from '#audit/resolve-audit-script';
13
13
  const REPO_AUDIT_REGISTRY = {
14
14
  'catalog-drift': async (root) => (await import('#audit/repo-guardrails')).auditCatalogDrift(root),
15
15
  'package-surface': async (root) => (await import('#audit/package-surface')).auditPackageSurface(root),
16
- 'blueprint-lifecycle': async (root, _options) => (await import('#audit/blueprint-lifecycle-sql')).auditBlueprintLifecycleSql(root),
16
+ 'blueprint-readme-drift': async (root, options) => (await import('#audit/blueprint-readme-drift')).auditBlueprintReadmeDrift(root, {
17
+ fix: options.fix,
18
+ }),
19
+ 'blueprint-lifecycle': async (root, options) => (await import('#audit/blueprint-lifecycle-sql')).auditBlueprintLifecycleSql(root, {
20
+ includeOmxPlans: options.omxPlans,
21
+ }),
17
22
  'roadmap-links': async (root, options) => (await import('#audit/roadmap-links')).auditRoadmapLinks(root, {
18
23
  failOrphans: options.strict,
19
24
  }),
@@ -65,7 +70,6 @@ const REPO_AUDIT_REGISTRY = {
65
70
  'no-first-party-mjs': async (root) => (await import('#audit/no-first-party-mjs')).auditNoFirstPartyMjs(root),
66
71
  'agent-cost': async (root) => (await import('#audit/agent-cost')).auditAgentCost(root),
67
72
  'blueprint-db-consistency': async (root) => (await import('#audit/blueprint-db-consistency')).auditBlueprintDbConsistency(root),
68
- 'blueprint-lifecycle-sql': async (root) => (await import('#audit/blueprint-lifecycle-sql')).auditBlueprintLifecycleSql(root),
69
73
  'tech-debt-cadence': async (root) => (await import('#audit/tech-debt-cadence')).auditTechDebtCadence(root),
70
74
  'cross-repo-correlation': async (root) => (await import('#audit/cross-repo-correlation')).auditCrossRepoCorrelationAsRepoResult(root),
71
75
  'ai-contracts': async (root) => (await import('#audit/ai-contracts')).auditAiContracts(root),
@@ -189,7 +193,7 @@ export function registerAuditCommand(cli) {
189
193
  .option('--message-file <file>', 'Commit message file for commit-message')
190
194
  .option('--require-lore', 'Require Lore trailers (hard-fail on missing/malformed trailers)')
191
195
  .option('--lore-warn', 'Warn about missing Lore trailers but always exit 0 (soft adoption mode)')
192
- .option('--legacy-omx', 'Include legacy .omx plan checks for blueprint-lifecycle')
196
+ .option('--omx-plans', 'Also audit .omx/plans derived-handoff governance for blueprint-lifecycle')
193
197
  .option('--html-entry <file>', 'HTML entry relative to dist for bundle-budget')
194
198
  .option('--max-js-asset-bytes <bytes>', 'Max size for any generated JS asset')
195
199
  .option('--max-html-eager-js-asset-bytes <bytes>', 'Max size for any HTML-eager JS asset')
@@ -5,7 +5,6 @@ import path from 'node:path';
5
5
  import { coldStartIfNeeded } from '#db/cold-start.js';
6
6
  import { openDb } from '#db/connection.js';
7
7
  import { ingestAll } from '#db/ingester.js';
8
- import { migrateLegacyAgentDb } from '#db/legacy-migration.js';
9
8
  import { resolveBlueprintProjectionDbPath, withProjectionDbWriteLock } from '#db/paths.js';
10
9
  import { runTemplate } from '#db/template-runner.js';
11
10
  // ---------------------------------------------------------------------------
@@ -13,8 +12,6 @@ import { runTemplate } from '#db/template-runner.js';
13
12
  // ---------------------------------------------------------------------------
14
13
  const METADATA_FILENAME = '.datasette-metadata.json';
15
14
  function agentDbPath(projectRoot) {
16
- // Migrate any legacy DB (idempotent + memoized) before resolving canonical path.
17
- migrateLegacyAgentDb(projectRoot);
18
15
  return resolveBlueprintProjectionDbPath(projectRoot);
19
16
  }
20
17
  function agentMetadataPath(projectRoot) {
@@ -2,7 +2,8 @@
2
2
  * Blueprint mutation verbs — advanceTask, promoteBlueprint, finalizeBlueprint
3
3
  *
4
4
  * All mutations:
5
- * 1. Edit the canonical _overview.md on disk (atomic tmp+rename)
5
+ * 1. Edit the canonical blueprint markdown document on disk (flat `.md` or
6
+ * folder `_overview.md`) via atomic tmp+rename
6
7
  * 2. Re-ingest into the structured-store DB via ingestAll
7
8
  *
8
9
  * Platform-first sync (Tasks 2.6 + 2.7):
@@ -85,7 +86,7 @@ export interface PromoteBlueprintResult {
85
86
  readonly message: string;
86
87
  }
87
88
  /**
88
- * Advance a task's status in its blueprint's _overview.md, then re-ingest.
89
+ * Advance a task's status in its blueprint markdown document, then re-ingest.
89
90
  *
90
91
  * Atomic: writes to a temp file then renames onto the original.
91
92
  * Idempotent: if the task is already at `toStatus`, reports "already <toStatus>" and exits cleanly.
@@ -2,7 +2,8 @@
2
2
  * Blueprint mutation verbs — advanceTask, promoteBlueprint, finalizeBlueprint
3
3
  *
4
4
  * All mutations:
5
- * 1. Edit the canonical _overview.md on disk (atomic tmp+rename)
5
+ * 1. Edit the canonical blueprint markdown document on disk (flat `.md` or
6
+ * folder `_overview.md`) via atomic tmp+rename
6
7
  * 2. Re-ingest into the structured-store DB via ingestAll
7
8
  *
8
9
  * Platform-first sync (Tasks 2.6 + 2.7):
@@ -18,8 +19,8 @@ import path from 'node:path';
18
19
  import { parseBlueprint } from '#core/parser';
19
20
  import { openDb } from '#db/connection.js';
20
21
  import { ingestAll } from '#db/ingester.js';
21
- import { migrateLegacyAgentDb } from '#db/legacy-migration.js';
22
22
  import { resolveBlueprintProjectionDbPath, withMarkdownWriteLock, withProjectionDbWriteLock, } from '#db/paths.js';
23
+ import { getBlueprintDocumentPaths } from '#utils/document-paths.js';
23
24
  import { resolveBlueprintRoot } from '#utils/blueprint-root.js';
24
25
  import { assertAllTasksHaveCanonicalPassingEvidence } from '#verification.js';
25
26
  /**
@@ -80,16 +81,27 @@ const TASK_STATUSES = ['todo', 'in-progress', 'blocked', 'done', 'dropped'];
80
81
  // Helpers
81
82
  // ---------------------------------------------------------------------------
82
83
  function dbPath(cwd) {
83
- // Migrate any legacy `.agent/.blueprints.db` once per process per repo
84
- // before resolving the canonical worktree-scoped path.
85
- migrateLegacyAgentDb(cwd);
86
84
  return resolveBlueprintProjectionDbPath(cwd);
87
85
  }
88
- function findBlueprintDir(blueprintRoot, slug) {
86
+ function findBlueprintDocument(blueprintRoot, slug) {
89
87
  for (const state of ALL_STATES) {
90
- const d = path.join(blueprintRoot, state, slug);
91
- if (existsSync(d))
92
- return { dir: d, state };
88
+ const paths = getBlueprintDocumentPaths(blueprintRoot, state, slug);
89
+ if (existsSync(paths.flat)) {
90
+ return {
91
+ dir: path.dirname(paths.flat),
92
+ documentPath: paths.flat,
93
+ shape: 'flat',
94
+ state,
95
+ };
96
+ }
97
+ if (existsSync(paths.folder)) {
98
+ return {
99
+ dir: paths.directory,
100
+ documentPath: paths.folder,
101
+ shape: 'folder',
102
+ state,
103
+ };
104
+ }
93
105
  }
94
106
  return null;
95
107
  }
@@ -164,7 +176,7 @@ function findTaskStatusLine(lines, taskId) {
164
176
  // advanceTask
165
177
  // ---------------------------------------------------------------------------
166
178
  /**
167
- * Advance a task's status in its blueprint's _overview.md, then re-ingest.
179
+ * Advance a task's status in its blueprint markdown document, then re-ingest.
168
180
  *
169
181
  * Atomic: writes to a temp file then renames onto the original.
170
182
  * Idempotent: if the task is already at `toStatus`, reports "already <toStatus>" and exits cleanly.
@@ -175,15 +187,11 @@ export async function advanceTask(cwd, blueprintSlug, taskId, toStatus) {
175
187
  }
176
188
  async function advanceTaskLocked(cwd, blueprintSlug, taskId, toStatus) {
177
189
  const blueprintRoot = resolveBlueprintRoot(cwd);
178
- const found = findBlueprintDir(blueprintRoot, blueprintSlug);
190
+ const found = findBlueprintDocument(blueprintRoot, blueprintSlug);
179
191
  if (!found) {
180
192
  throw new Error(`Blueprint "${blueprintSlug}" not found in any state directory under ${blueprintRoot}`);
181
193
  }
182
- const overviewPath = path.join(found.dir, '_overview.md');
183
- if (!existsSync(overviewPath)) {
184
- throw new Error(`Blueprint overview not found: ${overviewPath}`);
185
- }
186
- const content = readFileSync(overviewPath, 'utf8');
194
+ const content = readFileSync(found.documentPath, 'utf8');
187
195
  const lines = content.split('\n');
188
196
  const result = findTaskStatusLine(lines, taskId);
189
197
  if (!result) {
@@ -226,7 +234,7 @@ async function advanceTaskLocked(cwd, blueprintSlug, taskId, toStatus) {
226
234
  const updatedLines = [...lines];
227
235
  updatedLines[lineIndex] = `**Status:** ${toStatus}`;
228
236
  const newContent = updatedLines.join('\n');
229
- atomicWriteFile(overviewPath, newContent);
237
+ atomicWriteFile(found.documentPath, newContent);
230
238
  await reIngestDb(cwd);
231
239
  return {
232
240
  blueprintSlug,
@@ -253,25 +261,21 @@ export async function promoteBlueprint(cwd, slug, toState) {
253
261
  }
254
262
  async function promoteBlueprintLocked(cwd, slug, toState) {
255
263
  const blueprintRoot = resolveBlueprintRoot(cwd);
256
- const found = findBlueprintDir(blueprintRoot, slug);
264
+ const found = findBlueprintDocument(blueprintRoot, slug);
257
265
  if (!found) {
258
266
  throw new Error(`Blueprint "${slug}" not found in any state directory under ${blueprintRoot}`);
259
267
  }
260
- const { dir: currentDir, state: currentState } = found;
261
- const overviewPath = path.join(currentDir, '_overview.md');
262
- if (!existsSync(overviewPath)) {
263
- throw new Error(`Blueprint overview not found: ${overviewPath}`);
264
- }
268
+ const { dir: currentDir, documentPath: currentDocumentPath, shape, state: currentState, } = found;
265
269
  // Guard: refuse to complete if any tasks are not done/dropped
266
270
  if (toState === 'completed') {
267
- const markdown = readFileSync(overviewPath, 'utf8');
271
+ const markdown = readFileSync(currentDocumentPath, 'utf8');
268
272
  const blueprint = parseBlueprint(markdown, slug);
269
- const unfinished = blueprint.tasks.filter((task) => task.status !== 'done');
273
+ const unfinished = blueprint.tasks.filter((task) => task.status !== 'done' && task.status !== 'dropped');
270
274
  if (unfinished.length > 0) {
271
275
  const list = unfinished.map((task) => `${task.id} (${task.status})`).join(', ');
272
- throw new Error(`Cannot promote "${slug}" to completed: the following tasks are not done: ${list}`);
276
+ throw new Error(`Cannot promote "${slug}" to completed: the following tasks are not done/dropped: ${list}`);
273
277
  }
274
- assertAllTasksHaveCanonicalPassingEvidence(markdown, blueprint.tasks.map((task) => task.id));
278
+ assertAllTasksHaveCanonicalPassingEvidence(markdown, blueprint.tasks.filter((task) => task.status === 'done').map((task) => task.id));
275
279
  const target = dbPath(cwd);
276
280
  if (existsSync(target)) {
277
281
  const conn = openDb(target);
@@ -311,37 +315,39 @@ async function promoteBlueprintLocked(cwd, slug, toState) {
311
315
  // Always update local markdown + SQLite.
312
316
  // Platform-first: these become derived artifacts; disabled: these are canonical.
313
317
  // Update frontmatter in the current location first, then move
314
- let content = readFileSync(overviewPath, 'utf8');
318
+ let content = readFileSync(currentDocumentPath, 'utf8');
315
319
  content = updateFrontmatterStatus(content, toState);
316
320
  if (toState === 'completed') {
317
321
  const today = new Date().toISOString().split('T')[0] ?? new Date().toISOString();
318
322
  content = upsertCompletedAt(content, today);
319
323
  }
320
- const destDir = path.join(blueprintRoot, toState, slug);
321
- const destOverviewPath = path.join(destDir, '_overview.md');
322
- if (currentDir === destDir) {
324
+ const targetPaths = getBlueprintDocumentPaths(blueprintRoot, toState, slug);
325
+ const destDir = targetPaths.directory;
326
+ const destDocumentPath = shape === 'flat' ? targetPaths.flat : targetPaths.folder;
327
+ if (currentDocumentPath === destDocumentPath) {
323
328
  // Same directory — only update frontmatter
324
- atomicWriteFile(overviewPath, content);
329
+ atomicWriteFile(currentDocumentPath, content);
325
330
  await reIngestDb(cwd);
326
331
  return {
327
332
  slug,
328
333
  oldState: currentState,
329
334
  newState: toState,
330
- newPath: overviewPath,
331
- message: `Promoted ${slug}: ${currentState} → ${toState} (path unchanged: ${overviewPath})`,
335
+ newPath: currentDocumentPath,
336
+ message: `Promoted ${slug}: ${currentState} → ${toState} (path unchanged: ${currentDocumentPath})`,
332
337
  };
333
338
  }
334
- // Write updated content to current location first, then move directory
335
- atomicWriteFile(overviewPath, content);
339
+ // Write updated content to the current location first, then move the owning
340
+ // file/directory according to the blueprint shape.
341
+ atomicWriteFile(currentDocumentPath, content);
336
342
  mkdirSync(path.dirname(destDir), { recursive: true });
337
- renameSync(currentDir, destDir);
343
+ renameSync(shape === 'flat' ? currentDocumentPath : currentDir, shape === 'flat' ? destDocumentPath : destDir);
338
344
  await reIngestDb(cwd);
339
345
  return {
340
346
  slug,
341
347
  oldState: currentState,
342
348
  newState: toState,
343
- newPath: destOverviewPath,
344
- message: `Promoted ${slug}: ${currentState} → ${toState} (new path: ${destOverviewPath})`,
349
+ newPath: destDocumentPath,
350
+ message: `Promoted ${slug}: ${currentState} → ${toState} (new path: ${destDocumentPath})`,
345
351
  };
346
352
  }
347
353
  // ---------------------------------------------------------------------------
@@ -22,7 +22,7 @@ const BLUEPRINT_HELP = [
22
22
  ' move <slug> <status> --force-recovery',
23
23
  ].join('\n');
24
24
  export function formatTaskLine(task) {
25
- const checkbox = task.status === 'done' ? 'x' : ' ';
25
+ const checkbox = task.status === 'done' || task.status === 'dropped' ? 'x' : ' ';
26
26
  return `- [${checkbox}] ${task.id} ${task.title}`;
27
27
  }
28
28
  export function getBlueprintHelpText() {
@@ -113,7 +113,7 @@ export function formatBlueprintSummaries(summaries) {
113
113
  return [...lines, inventorySummary].join('\n');
114
114
  }
115
115
  export function formatBlueprintDetails(result) {
116
- const doneTasks = result.blueprint.tasks.filter((task) => task.status === 'done').length;
116
+ const doneTasks = result.blueprint.tasks.filter((task) => task.status === 'done' || task.status === 'dropped').length;
117
117
  const header = [
118
118
  `title: ${result.blueprint.title}`,
119
119
  `slug: ${result.slug}`,
@@ -3,7 +3,7 @@ export interface RunDoctorOptions {
3
3
  root?: string;
4
4
  docsRoot?: string;
5
5
  fix?: boolean;
6
- legacyOmx?: boolean;
6
+ omxPlans?: boolean;
7
7
  }
8
8
  export declare function runDoctor(options?: RunDoctorOptions): Promise<number>;
9
9
  export declare function registerDoctorCommand(cli: CAC): void;
@@ -1,4 +1,5 @@
1
- import { auditBlueprintLifecycle, auditCatalogDrift, auditDocsFrontmatter, formatRepoAuditReport, } from '#audit/repo-guardrails';
1
+ import { auditBlueprintLifecycleSql } from '#audit/blueprint-lifecycle-sql';
2
+ import { auditCatalogDrift, auditDocsFrontmatter, formatRepoAuditReport, } from '#audit/repo-guardrails';
2
3
  const REMEDIATIONS = {
3
4
  'Catalog drift': 'wp audit catalog-drift',
4
5
  'Catalog drift — single package (no workspace file)': 'none needed',
@@ -14,9 +15,7 @@ export async function runDoctor(options = {}) {
14
15
  docsRoot: options.docsRoot,
15
16
  fix: options.fix,
16
17
  }),
17
- auditBlueprintLifecycle(root, {
18
- includeLegacyOmx: options.legacyOmx,
19
- }),
18
+ await auditBlueprintLifecycleSql(root, { includeOmxPlans: options.omxPlans }),
20
19
  ];
21
20
  let failed = false;
22
21
  for (const result of results) {
@@ -44,7 +43,7 @@ export function registerDoctorCommand(cli) {
44
43
  .option('--root <dir>', 'Repository root to inspect')
45
44
  .option('--docs-root <dir>', 'Docs directory for docs-frontmatter')
46
45
  .option('--fix', 'Apply supported safe fixes during doctor (currently docs-frontmatter)')
47
- .option('--legacy-omx', 'Include legacy .omx plan checks for blueprint-lifecycle')
46
+ .option('--omx-plans', 'Also audit .omx/plans derived-handoff governance for blueprint-lifecycle')
48
47
  .action(async (options) => {
49
48
  const code = await runDoctor(options);
50
49
  return code;
@@ -1,12 +1,18 @@
1
1
  import type { AgentHost, VisibilityStatus } from './host-visibility.js';
2
2
  export declare const CONFIG_VERSION = "1";
3
3
  export declare const CONFIG_FILENAME = ".webpressorc.json";
4
+ export declare const LEGACY_CONFIG_FILENAME = ".agent-kitrc.json";
4
5
  export declare const DEFAULT_DURABLE_PLANNING_ROOT = ".agent/planning/";
5
6
  export interface AgentkitConfig {
6
7
  version: string;
7
8
  installed: {
8
9
  tier3Skills: string[];
9
10
  };
11
+ audit?: {
12
+ toolchainIsolation?: {
13
+ allowDependencies?: string[];
14
+ };
15
+ };
10
16
  hosts?: {
11
17
  selected: AgentHost[];
12
18
  requiredCapabilities: string[];
@@ -24,16 +30,6 @@ export interface AgentkitConfig {
24
30
  packageManager?: 'vp-only';
25
31
  scriptRoutes?: Record<string, string>;
26
32
  };
27
- /** Audit policy overrides. `mechanism` lives in agent-kit; this is per-repo
28
- * `data`. `toolchainIsolation.allowDependencies` lists dependency names that
29
- * are exempt from the toolchain-isolation audit because they are legitimate
30
- * app-specific runtimes (e.g. `tsx` for a Pulumi program's TS loader,
31
- * `@playwright/test` imported by e2e specs), not generic toolchain. */
32
- audit?: {
33
- toolchainIsolation?: {
34
- allowDependencies?: string[];
35
- };
36
- };
37
33
  rules: {
38
34
  overrides: string[];
39
35
  };
@@ -7,6 +7,7 @@ import { join } from 'node:path';
7
7
  import { REQUIRED_CORE_CAPABILITIES } from './host-visibility.js';
8
8
  export const CONFIG_VERSION = '1';
9
9
  export const CONFIG_FILENAME = '.webpressorc.json';
10
+ export const LEGACY_CONFIG_FILENAME = '.agent-kitrc.json';
10
11
  export const DEFAULT_DURABLE_PLANNING_ROOT = '.agent/planning/';
11
12
  function readOptionalString(value) {
12
13
  if (typeof value !== 'string')
@@ -27,14 +28,12 @@ export function defaultConfig() {
27
28
  durablePlanningRoot: DEFAULT_DURABLE_PLANNING_ROOT,
28
29
  };
29
30
  }
30
- export function readConfig(repoRoot) {
31
- const path = join(repoRoot, CONFIG_FILENAME);
32
- if (!existsSync(path))
33
- return null;
31
+ function parseConfigFile(path) {
34
32
  try {
35
33
  const raw = readFileSync(path, 'utf8');
36
34
  const parsed = JSON.parse(raw);
37
35
  const installed = parsed.installed;
36
+ const audit = parsed.audit;
38
37
  const mcp = parsed.mcp;
39
38
  const hosts = parsed.hosts;
40
39
  const rules = parsed.rules;
@@ -60,12 +59,17 @@ export function readConfig(repoRoot) {
60
59
  ...(scriptRoutes ? { scriptRoutes } : {}),
61
60
  }
62
61
  : undefined;
63
- const auditConfig = parsed.audit;
64
- const rawAllowDeps = auditConfig?.toolchainIsolation?.allowDependencies;
65
- const allowDependencies = Array.isArray(rawAllowDeps)
66
- ? rawAllowDeps.filter((s) => typeof s === 'string' && s.length > 0)
67
- : [];
68
- const normalizedAudit = allowDependencies.length > 0 ? { toolchainIsolation: { allowDependencies } } : undefined;
62
+ const rawToolchainIsolation = audit?.toolchainIsolation;
63
+ const allowDependencies = Array.isArray(rawToolchainIsolation?.allowDependencies)
64
+ ? rawToolchainIsolation.allowDependencies.filter((value) => typeof value === 'string' && value.length > 0)
65
+ : undefined;
66
+ const normalizedAudit = allowDependencies && allowDependencies.length > 0
67
+ ? {
68
+ toolchainIsolation: {
69
+ allowDependencies,
70
+ },
71
+ }
72
+ : undefined;
69
73
  const selectedHosts = Array.isArray(hosts?.selected)
70
74
  ? hosts.selected.filter((s) => ['codex', 'claude', 'opencode'].includes(String(s)))
71
75
  : [];
@@ -83,9 +87,9 @@ export function readConfig(repoRoot) {
83
87
  requiredCapabilities,
84
88
  ...(visibility ? { visibility } : {}),
85
89
  },
90
+ ...(normalizedAudit ? { audit: normalizedAudit } : {}),
86
91
  ...(normalizedMcp ? { mcp: normalizedMcp } : {}),
87
92
  ...(normalizedGuard ? { guard: normalizedGuard } : {}),
88
- ...(normalizedAudit ? { audit: normalizedAudit } : {}),
89
93
  rules: { overrides: overrides.filter((s) => typeof s === 'string') },
90
94
  scripts: {
91
95
  'setup-agent': readOptionalString(scripts?.['setup-agent']),
@@ -102,6 +106,15 @@ export function readConfig(repoRoot) {
102
106
  return null;
103
107
  }
104
108
  }
109
+ export function readConfig(repoRoot) {
110
+ const configPath = join(repoRoot, CONFIG_FILENAME);
111
+ if (existsSync(configPath))
112
+ return parseConfigFile(configPath);
113
+ const legacyConfigPath = join(repoRoot, LEGACY_CONFIG_FILENAME);
114
+ if (existsSync(legacyConfigPath))
115
+ return parseConfigFile(legacyConfigPath);
116
+ return null;
117
+ }
105
118
  export function mergeConfig(existing, incoming) {
106
119
  if (!existing)
107
120
  return incoming;
@@ -113,6 +126,17 @@ export function mergeConfig(existing, incoming) {
113
126
  ...incoming.mcp,
114
127
  }
115
128
  : undefined;
129
+ const mergedAllowDependencies = Array.from(new Set([
130
+ ...(existing.audit?.toolchainIsolation?.allowDependencies ?? []),
131
+ ...(incoming.audit?.toolchainIsolation?.allowDependencies ?? []),
132
+ ])).toSorted();
133
+ const mergedAudit = mergedAllowDependencies.length > 0
134
+ ? {
135
+ toolchainIsolation: {
136
+ allowDependencies: mergedAllowDependencies,
137
+ },
138
+ }
139
+ : undefined;
116
140
  const mergedScriptRoutes = existing.guard?.scriptRoutes || incoming.guard?.scriptRoutes
117
141
  ? { ...existing.guard?.scriptRoutes, ...incoming.guard?.scriptRoutes }
118
142
  : undefined;
@@ -123,21 +147,13 @@ export function mergeConfig(existing, incoming) {
123
147
  ...(mergedScriptRoutes ? { scriptRoutes: mergedScriptRoutes } : {}),
124
148
  }
125
149
  : undefined;
126
- const existingAllowDeps = existing.audit?.toolchainIsolation?.allowDependencies;
127
- const incomingAllowDeps = incoming.audit?.toolchainIsolation?.allowDependencies;
128
- const mergedAllowDeps = existingAllowDeps || incomingAllowDeps
129
- ? Array.from(new Set([...(existingAllowDeps ?? []), ...(incomingAllowDeps ?? [])])).toSorted()
130
- : undefined;
131
- const mergedAudit = mergedAllowDeps
132
- ? { toolchainIsolation: { allowDependencies: mergedAllowDeps } }
133
- : undefined;
134
150
  return {
135
151
  version: incoming.version,
136
152
  installed: { tier3Skills: tier3 },
137
153
  hosts: incoming.hosts ?? existing.hosts,
154
+ ...(mergedAudit ? { audit: mergedAudit } : {}),
138
155
  ...(mergedMcp ? { mcp: mergedMcp } : {}),
139
156
  ...(mergedGuard ? { guard: mergedGuard } : {}),
140
- ...(mergedAudit ? { audit: mergedAudit } : {}),
141
157
  rules: { overrides },
142
158
  scripts: {
143
159
  'setup-agent': incoming.scripts['setup-agent'] ?? existing.scripts['setup-agent'],
@@ -73,7 +73,6 @@ export const GENERATED_PATHS_BLOCK = {
73
73
  '.agent/.merged.provenance.json',
74
74
  '.agent/.compile-manifest.json',
75
75
  '.agent/.rotation-log.jsonl',
76
- '.agent/.blueprints.db',
77
76
  '.agent/.blueprints.lock',
78
77
  '.agent/.blueprints.snapshot.sql',
79
78
  '.agent/.tail-hint-history.jsonl',
@@ -17,7 +17,14 @@ export declare const EXIT_SUCCESS = 0;
17
17
  export declare const EXIT_SETUP_FAIL = 1;
18
18
  export declare const EXIT_USER_ABORT = 2;
19
19
  export declare const EXIT_WRITE_FAIL = 3;
20
- export declare function resolveCatalogDir(): string;
20
+ export interface ResolveCatalogDirOptions {
21
+ readonly moduleUrl?: string;
22
+ readonly execPath?: string;
23
+ readonly argv0?: string;
24
+ readonly argv1?: string;
25
+ readonly pathEnv?: string;
26
+ }
27
+ export declare function resolveCatalogDir(options?: ResolveCatalogDirOptions): string;
21
28
  export declare function runInit(flags: InitFlags): Promise<number>;
22
29
  export type InitCommandName = 'setup' | 'init';
23
30
  export declare function registerInitCommand(cli: CAC, commandName?: InitCommandName): void;