@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +93 -66
- package/bin/_run.js +143 -1
- package/bin/runtime-manifest.json +40 -0
- package/catalog/AGENTS.md.tpl +7 -6
- package/catalog/agent/commands/plan-refine.md +3 -3
- package/catalog/agent/commands/pll.md +2 -0
- package/catalog/agent/guides/parallel-execution.md +2 -0
- package/catalog/agent/rules/extraction-parity.md +27 -1
- package/catalog/agent/rules/public-package-safety.md +24 -1
- package/catalog/agent/skills/plan-refine/SKILL.md +5 -4
- package/catalog/agent/skills/pll/SKILL.md +1 -0
- package/catalog/base-kit/.github/workflows/ci.webpresso.yml.tmpl +33 -0
- package/catalog/base-kit/commitlint.config.ts.tmpl +1 -3
- package/catalog/base-kit/e2e/fixtures/smoke.html.tmpl +13 -0
- package/catalog/base-kit/e2e/smoke.spec.ts.tmpl +13 -0
- package/catalog/base-kit/oxlint.config.ts.tmpl +26 -0
- package/catalog/base-kit/playwright.config.ts.tmpl +10 -0
- package/catalog/base-kit/src/quality-sample.test.ts.tmpl +19 -0
- package/catalog/base-kit/src/quality-sample.ts.tmpl +11 -0
- package/catalog/base-kit/stryker.config.ts.tmpl +14 -0
- package/catalog/base-kit/tsconfig.json.tmpl +9 -0
- package/catalog/base-kit/vitest.config.ts.tmpl +10 -0
- package/catalog/docs/templates/adr.md +1 -1
- package/catalog/docs/templates/blueprint.md +2 -0
- package/catalog/docs/templates/blueprint.yaml +16 -15
- package/catalog/docs/templates/guide.md +1 -1
- package/catalog/docs/templates/postmortem.md +1 -1
- package/catalog/docs/templates/research.md +1 -1
- package/catalog/docs/templates/runbook.md +1 -1
- package/catalog/docs/templates/system.md +12 -3
- package/catalog/docs/templates/tech-debt.md +1 -0
- package/commands/blueprint.md +10 -12
- package/dist/esm/audit/blueprint-db-consistency.d.ts +1 -1
- package/dist/esm/audit/blueprint-db-consistency.js +6 -8
- package/dist/esm/audit/blueprint-lifecycle-sql.js +10 -3
- package/dist/esm/audit/cloudflare-deploy-contract.d.ts +3 -0
- package/dist/esm/audit/cloudflare-deploy-contract.js +64 -0
- package/dist/esm/audit/no-legacy-cli-bin.d.ts +3 -0
- package/dist/esm/audit/no-legacy-cli-bin.js +100 -0
- package/dist/esm/audit/package-surface.js +14 -1
- package/dist/esm/audit/repo-guardrails.js +40 -13
- package/dist/esm/audit/resolve-audit-script.d.ts +24 -0
- package/dist/esm/audit/resolve-audit-script.js +27 -0
- package/dist/esm/audit/roadmap-links.js +23 -10
- package/dist/esm/blueprint/core/schema.d.ts +8 -8
- package/dist/esm/blueprint/core/schema.js +2 -2
- package/dist/esm/blueprint/db/enums.d.ts +1 -1
- package/dist/esm/blueprint/db/ingester.js +18 -10
- package/dist/esm/blueprint/index.d.ts +0 -1
- package/dist/esm/blueprint/index.js +0 -2
- package/dist/esm/blueprint/lifecycle/audit.js +9 -2
- package/dist/esm/blueprint/lifecycle/local.js +15 -4
- package/dist/esm/blueprint/local.d.ts +0 -3
- package/dist/esm/blueprint/local.js +0 -2
- package/dist/esm/blueprint/service/BlueprintCreationService.js +16 -8
- package/dist/esm/blueprint/service/BlueprintService.js +37 -19
- package/dist/esm/blueprint/service/scanner.js +73 -9
- package/dist/esm/blueprint/tracked-document/schema.d.ts +2 -2
- package/dist/esm/blueprint/utils/document-paths.d.ts +23 -0
- package/dist/esm/blueprint/utils/document-paths.js +91 -0
- package/dist/esm/blueprint/utils/package-assets.d.ts +11 -0
- package/dist/esm/blueprint/utils/package-assets.js +33 -4
- package/dist/esm/build/package-manifest.js +7 -0
- package/dist/esm/build/release-policy.d.ts +27 -0
- package/dist/esm/build/release-policy.js +29 -0
- package/dist/esm/build/runtime-targets.d.ts +13 -0
- package/dist/esm/build/runtime-targets.js +48 -0
- package/dist/esm/build/sync-catalog-doc-templates.d.ts +23 -0
- package/dist/esm/build/sync-catalog-doc-templates.js +93 -0
- package/dist/esm/cli/auto-update/detect-pm.d.ts +15 -0
- package/dist/esm/cli/auto-update/detect-pm.js +24 -9
- package/dist/esm/cli/auto-update/skip.js +9 -1
- package/dist/esm/cli/bundle/agent-command-inventory.d.ts +120 -0
- package/dist/esm/cli/bundle/agent-command-inventory.js +100 -0
- package/dist/esm/cli/bundle/index.d.ts +17 -0
- package/dist/esm/cli/bundle/index.js +15 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +49 -5
- package/dist/esm/cli/commands/audit-core.d.ts +1 -1
- package/dist/esm/cli/commands/audit.js +4 -7
- package/dist/esm/cli/commands/blueprint/router.js +16 -10
- package/dist/esm/cli/commands/blueprint/template-resolver.js +8 -4
- package/dist/esm/cli/commands/hook.d.ts +8 -0
- package/dist/esm/cli/commands/hook.js +47 -0
- package/dist/esm/cli/commands/init/host-visibility.js +4 -2
- package/dist/esm/cli/commands/init/index.js +80 -7
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +142 -7
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/codex-ownership.js +9 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +130 -20
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.d.ts +65 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +64 -0
- package/dist/esm/cli/commands/package-manager.d.ts +15 -0
- package/dist/esm/cli/commands/package-manager.js +42 -0
- package/dist/esm/cli/commands/test.d.ts +1 -0
- package/dist/esm/cli/commands/test.js +2 -1
- package/dist/esm/cli/commands/typecheck.js +10 -19
- package/dist/esm/cli/package-scripts.d.ts +12 -0
- package/dist/esm/cli/package-scripts.js +59 -0
- package/dist/esm/cli/utils.js +3 -22
- package/dist/esm/cli/wp-extensions.d.ts +14 -0
- package/dist/esm/cli/wp-extensions.js +34 -0
- package/dist/esm/config/docs-lint/schemas/common.d.ts +1 -1
- package/dist/esm/config/docs-lint/schemas/implementation-plan.d.ts +2 -2
- package/dist/esm/config/docs-lint/schemas/parent-roadmap.d.ts +1 -1
- package/dist/esm/config/stryker/index.d.ts +85 -0
- package/dist/esm/config/stryker/index.js +31 -0
- package/dist/esm/e2e/command-builder.js +35 -7
- package/dist/esm/e2e/config.d.ts +56 -0
- package/dist/esm/e2e/config.js +114 -0
- package/dist/esm/e2e/execution.js +8 -0
- package/dist/esm/e2e/run-planner.js +2 -0
- package/dist/esm/e2e/types.d.ts +3 -0
- package/dist/esm/format/index.js +5 -1
- package/dist/esm/hooks/guard-switch/index.d.ts +1 -1
- package/dist/esm/hooks/guard-switch/index.js +22 -14
- package/dist/esm/hooks/post-tool/lint-after-edit.d.ts +1 -0
- package/dist/esm/hooks/post-tool/lint-after-edit.js +5 -2
- package/dist/esm/hooks/pretool-guard/validators/file-conventions.js +1 -1
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.d.ts +6 -0
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +27 -2
- package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +2 -1
- package/dist/esm/hooks/pretool-guard/validators/path-contract.js +59 -34
- package/dist/esm/hooks/pretool-guard/validators/plan-frontmatter.js +3 -3
- package/dist/esm/hooks/shared/routing-block.js +18 -4
- package/dist/esm/hooks/shared/validators/blueprint.js +3 -0
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +1 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +5 -2
- package/dist/esm/lint/index.js +3 -1
- package/dist/esm/mcp/auto-discover.d.ts +2 -0
- package/dist/esm/mcp/auto-discover.js +14 -6
- package/dist/esm/mcp/blueprint-server.js +379 -80
- package/dist/esm/mcp/cli.js +21 -0
- package/dist/esm/mcp/runners/test.js +15 -0
- package/dist/esm/mcp/server.d.ts +7 -0
- package/dist/esm/mcp/server.js +16 -27
- package/dist/esm/mcp/tools/_registry.d.ts +3 -0
- package/dist/esm/mcp/tools/_registry.js +21 -0
- package/dist/esm/mcp/tools/audit.d.ts +1 -0
- package/dist/esm/mcp/tools/audit.js +13 -8
- package/dist/esm/mcp/tools/typecheck.js +4 -2
- package/dist/esm/mutation/affected.d.ts +9 -0
- package/dist/esm/mutation/affected.js +36 -0
- package/dist/esm/package.json +8 -0
- package/dist/esm/runtime/package-version.d.ts +2 -0
- package/dist/esm/runtime/package-version.js +43 -0
- package/dist/esm/test/command-builder.d.ts +4 -0
- package/dist/esm/test/command-builder.js +28 -3
- package/dist/esm/test-helpers/hermetic-env.d.ts +25 -0
- package/dist/esm/test-helpers/hermetic-env.js +31 -0
- package/dist/esm/tool-runtime/index.d.ts +5 -0
- package/dist/esm/tool-runtime/index.js +24 -0
- package/dist/esm/tool-runtime/resolve-runner.d.ts +16 -0
- package/dist/esm/tool-runtime/resolve-runner.js +42 -0
- package/dist/esm/typecheck/index.js +4 -2
- package/dist/esm/wp-extension/index.d.ts +50 -0
- package/dist/esm/wp-extension/index.js +268 -0
- package/package.json +75 -46
- package/skills/plan-refine/SKILL.md +5 -4
- package/skills/pll/SKILL.md +1 -0
- package/dist/esm/blueprint/dag/cycle-detector.d.ts +0 -12
- package/dist/esm/blueprint/dag/cycle-detector.js +0 -46
- package/dist/esm/blueprint/dag/executor.d.ts +0 -140
- package/dist/esm/blueprint/dag/executor.js +0 -292
- package/dist/esm/blueprint/dag/index.d.ts +0 -20
- package/dist/esm/blueprint/dag/index.js +0 -17
- package/dist/esm/blueprint/dag/interfaces.d.ts +0 -56
- package/dist/esm/blueprint/dag/interfaces.js +0 -13
- package/dist/esm/blueprint/dag/local/independence.d.ts +0 -107
- package/dist/esm/blueprint/dag/local/independence.js +0 -231
- package/dist/esm/blueprint/dag/local/index.d.ts +0 -14
- package/dist/esm/blueprint/dag/local/index.js +0 -14
- package/dist/esm/blueprint/dag/local/package-graph.d.ts +0 -66
- package/dist/esm/blueprint/dag/local/package-graph.js +0 -148
- package/dist/esm/blueprint/dag/plan-parser.d.ts +0 -54
- package/dist/esm/blueprint/dag/plan-parser.js +0 -236
- package/dist/esm/blueprint/dag/task-graph-algorithms.d.ts +0 -13
- package/dist/esm/blueprint/dag/task-graph-algorithms.js +0 -236
- package/dist/esm/blueprint/dag/task-graph.d.ts +0 -171
- package/dist/esm/blueprint/dag/task-graph.js +0 -370
- package/dist/esm/blueprint/dag/types.d.ts +0 -17
- package/dist/esm/blueprint/dag/types.js +0 -2
- package/dist/esm/blueprint/graph/index.d.ts +0 -5
- package/dist/esm/blueprint/graph/index.js +0 -5
- package/dist/esm/blueprint/graph/mermaid-parser.d.ts +0 -3
- package/dist/esm/blueprint/graph/mermaid-parser.js +0 -93
- package/dist/esm/blueprint/graph/mermaid-serializer.d.ts +0 -3
- package/dist/esm/blueprint/graph/mermaid-serializer.js +0 -20
- package/dist/esm/blueprint/graph/schema.d.ts +0 -89
- package/dist/esm/blueprint/graph/schema.js +0 -104
- package/dist/esm/blueprint/graph/task-graph-adapter.d.ts +0 -6
- package/dist/esm/blueprint/graph/task-graph-adapter.js +0 -30
|
@@ -47,6 +47,7 @@ const SKIP_DIRECTORIES = new Set([
|
|
|
47
47
|
'.omx',
|
|
48
48
|
'.omc',
|
|
49
49
|
'.stryker-tmp',
|
|
50
|
+
'.webpresso-packed-surface',
|
|
50
51
|
'node_modules',
|
|
51
52
|
'dist',
|
|
52
53
|
'build',
|
|
@@ -393,6 +394,7 @@ function stagePackedFiles(root, destinationRoot, candidate, packedFiles) {
|
|
|
393
394
|
function runSecretlint(stageRoot, packageRoot) {
|
|
394
395
|
const rcPath = findSecretlintRc(packageRoot);
|
|
395
396
|
const outputFile = join(stageRoot, '.secretlint-output.json');
|
|
397
|
+
const toolRoot = findSecretlintToolRoot(packageRoot) ?? findSecretlintToolRoot(process.cwd());
|
|
396
398
|
const args = ['exec', 'secretlint', '--format', 'json', '--output', outputFile, '--no-gitignore'];
|
|
397
399
|
if (rcPath) {
|
|
398
400
|
args.push('--secretlintrc', rcPath);
|
|
@@ -403,7 +405,7 @@ function runSecretlint(stageRoot, packageRoot) {
|
|
|
403
405
|
args.push(join(stageRoot, '**/*'));
|
|
404
406
|
try {
|
|
405
407
|
execFileSync('pnpm', args, {
|
|
406
|
-
cwd: packageRoot,
|
|
408
|
+
cwd: toolRoot ?? packageRoot,
|
|
407
409
|
encoding: 'utf8',
|
|
408
410
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
409
411
|
maxBuffer: 20 * 1024 * 1024,
|
|
@@ -421,6 +423,17 @@ function runSecretlint(stageRoot, packageRoot) {
|
|
|
421
423
|
rmSync(outputFile, { force: true });
|
|
422
424
|
}
|
|
423
425
|
}
|
|
426
|
+
function findSecretlintToolRoot(start) {
|
|
427
|
+
let current = resolve(start);
|
|
428
|
+
while (true) {
|
|
429
|
+
if (existsSync(join(current, 'node_modules', '.bin', 'secretlint')))
|
|
430
|
+
return current;
|
|
431
|
+
const parent = dirname(current);
|
|
432
|
+
if (parent === current)
|
|
433
|
+
return undefined;
|
|
434
|
+
current = parent;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
424
437
|
function findSecretlintRc(root) {
|
|
425
438
|
const candidates = [
|
|
426
439
|
'.secretlintrc.json',
|
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { join, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import matter from 'gray-matter';
|
|
4
4
|
import { blueprintDerivedHandoffSchema } from '#execution/types';
|
|
5
|
+
import { BLUEPRINT_OVERVIEW_FILENAME, isBlueprintSupportingMarkdownRelativePath, parseBlueprintDocumentRelativePath, } from '#utils/document-paths.js';
|
|
5
6
|
import { validateLoreTrailers } from './commit-message-lore.js';
|
|
6
7
|
const DEFAULT_COMMIT_TYPES = [
|
|
7
8
|
'feat',
|
|
@@ -209,33 +210,59 @@ export function auditBlueprintLifecycle(rootDirectory = process.cwd(), options =
|
|
|
209
210
|
if (!existsSync(statusRoot))
|
|
210
211
|
continue;
|
|
211
212
|
for (const entry of readdirSync(statusRoot, { withFileTypes: true })) {
|
|
212
|
-
if (!entry.isDirectory())
|
|
213
|
+
if (!entry.isDirectory() && !entry.isFile())
|
|
213
214
|
continue;
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
215
|
+
const canonicalPath = entry.isDirectory()
|
|
216
|
+
? join(statusRoot, entry.name, BLUEPRINT_OVERVIEW_FILENAME)
|
|
217
|
+
: join(statusRoot, entry.name);
|
|
218
|
+
const relativeBlueprintPath = relative(blueprintsRoot, canonicalPath);
|
|
219
|
+
const parsedPath = parseBlueprintDocumentRelativePath(relativeBlueprintPath);
|
|
220
|
+
if (entry.isDirectory()) {
|
|
221
|
+
checked += 1;
|
|
222
|
+
if (!existsSync(canonicalPath)) {
|
|
223
|
+
const hasNestedMarkdown = readdirSync(join(statusRoot, entry.name), {
|
|
224
|
+
withFileTypes: true,
|
|
225
|
+
}).some((nested) => nested.isFile() && nested.name.endsWith('.md') && nested.name !== 'README.md');
|
|
226
|
+
if (hasNestedMarkdown) {
|
|
227
|
+
violations.push({
|
|
228
|
+
file: relativePath(root, canonicalPath),
|
|
229
|
+
message: 'Blueprint folders with supporting markdown must include _overview.md',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
violations.push({
|
|
234
|
+
file: relativePath(root, canonicalPath),
|
|
235
|
+
message: 'Missing _overview.md',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
if (!parsedPath || isBlueprintSupportingMarkdownRelativePath(relativeBlueprintPath)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
checked += 1;
|
|
246
|
+
}
|
|
247
|
+
if (!parsedPath) {
|
|
221
248
|
continue;
|
|
222
249
|
}
|
|
223
|
-
const raw = readFileSync(
|
|
250
|
+
const raw = readFileSync(canonicalPath, 'utf8');
|
|
224
251
|
const frontmatter = matter(raw).data;
|
|
225
252
|
if (frontmatter.type !== 'blueprint' && frontmatter.type !== 'parent-roadmap') {
|
|
226
253
|
violations.push({
|
|
227
|
-
file: relativePath(root,
|
|
228
|
-
message: 'Blueprint
|
|
254
|
+
file: relativePath(root, canonicalPath),
|
|
255
|
+
message: 'Blueprint markdown must use type: blueprint or parent-roadmap',
|
|
229
256
|
});
|
|
230
257
|
}
|
|
231
258
|
if (frontmatter.status !== status) {
|
|
232
259
|
violations.push({
|
|
233
|
-
file: relativePath(root,
|
|
260
|
+
file: relativePath(root, canonicalPath),
|
|
234
261
|
message: `Blueprint status must match folder (${status})`,
|
|
235
262
|
});
|
|
236
263
|
}
|
|
237
264
|
violations.push(...validateBlueprintLinkingFrontmatter({
|
|
238
|
-
file: relativePath(root,
|
|
265
|
+
file: relativePath(root, canonicalPath),
|
|
239
266
|
frontmatter,
|
|
240
267
|
status,
|
|
241
268
|
}));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ResolveAuditScriptDeps {
|
|
2
|
+
/** `import.meta.url` of the calling module (`cli/commands/audit` or `mcp/tools/audit`). */
|
|
3
|
+
moduleUrl: string;
|
|
4
|
+
/** Existence probe; defaults to `fs.existsSync`. Injected in tests to model each layout. */
|
|
5
|
+
exists?: (url: URL) => boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the absolute path of a Bun audit script (`audit-tph.ts`,
|
|
9
|
+
* `audit-tph-e2e.ts`) living in the sibling `audit/` directory two levels up
|
|
10
|
+
* from the caller.
|
|
11
|
+
*
|
|
12
|
+
* `../../audit/` is correct relative to the caller in BOTH layouts:
|
|
13
|
+
* - dev: `src/{cli,mcp}/.../audit.ts` → `src/audit/`
|
|
14
|
+
* - published: `dist/esm/{cli,mcp}/.../audit.js` → `dist/esm/audit/`
|
|
15
|
+
*
|
|
16
|
+
* The npm tarball ships only `dist/` (never `src/`), so the dev `.ts` source is
|
|
17
|
+
* absent there — fall back to the compiled `.js` sibling the build emits. The
|
|
18
|
+
* previous CLI resolver instead hand-rolled a `<bundle>/src/audit/<name>.ts`
|
|
19
|
+
* path that does not exist in dist, which made `bun` fail with
|
|
20
|
+
* "Module not found"; the MCP twin reached for the unshipped `src/audit/` via
|
|
21
|
+
* `resolvePackageAsset` and failed the same way.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveAuditScriptPath(name: string, { moduleUrl, exists }: ResolveAuditScriptDeps): string;
|
|
24
|
+
//# sourceMappingURL=resolve-audit-script.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the absolute path of a Bun audit script (`audit-tph.ts`,
|
|
5
|
+
* `audit-tph-e2e.ts`) living in the sibling `audit/` directory two levels up
|
|
6
|
+
* from the caller.
|
|
7
|
+
*
|
|
8
|
+
* `../../audit/` is correct relative to the caller in BOTH layouts:
|
|
9
|
+
* - dev: `src/{cli,mcp}/.../audit.ts` → `src/audit/`
|
|
10
|
+
* - published: `dist/esm/{cli,mcp}/.../audit.js` → `dist/esm/audit/`
|
|
11
|
+
*
|
|
12
|
+
* The npm tarball ships only `dist/` (never `src/`), so the dev `.ts` source is
|
|
13
|
+
* absent there — fall back to the compiled `.js` sibling the build emits. The
|
|
14
|
+
* previous CLI resolver instead hand-rolled a `<bundle>/src/audit/<name>.ts`
|
|
15
|
+
* path that does not exist in dist, which made `bun` fail with
|
|
16
|
+
* "Module not found"; the MCP twin reached for the unshipped `src/audit/` via
|
|
17
|
+
* `resolvePackageAsset` and failed the same way.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveAuditScriptPath(name, { moduleUrl, exists = existsSync }) {
|
|
20
|
+
const sourceUrl = new URL(`../../audit/${name}`, moduleUrl);
|
|
21
|
+
if (exists(sourceUrl)) {
|
|
22
|
+
return fileURLToPath(sourceUrl);
|
|
23
|
+
}
|
|
24
|
+
const compiledUrl = new URL(`../../audit/${name.replace(/\.ts$/, '.js')}`, moduleUrl);
|
|
25
|
+
return fileURLToPath(compiledUrl);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=resolve-audit-script.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import matter from 'gray-matter';
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
3
|
import { join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { BLUEPRINT_OVERVIEW_FILENAME, isBlueprintSupportingMarkdownRelativePath, parseBlueprintDocumentRelativePath, } from '#utils/document-paths.js';
|
|
4
5
|
const BLUEPRINT_STATUSES = [
|
|
5
6
|
'draft',
|
|
6
7
|
'planned',
|
|
@@ -11,7 +12,7 @@ const BLUEPRINT_STATUSES = [
|
|
|
11
12
|
];
|
|
12
13
|
const BLUEPRINT_STATUS_PATTERN = BLUEPRINT_STATUSES.join('|');
|
|
13
14
|
const ACTIVE_BLUEPRINT_STATUSES = new Set(['draft', 'planned', 'in-progress', 'parked']);
|
|
14
|
-
const LOCAL_BLUEPRINT_REFERENCE_PATTERN = new RegExp(String.raw `^(?:blueprints/)?(?:${BLUEPRINT_STATUS_PATTERN})/[A-Za-z0-9._-]+(
|
|
15
|
+
const LOCAL_BLUEPRINT_REFERENCE_PATTERN = new RegExp(String.raw `^(?:blueprints/)?(?:${BLUEPRINT_STATUS_PATTERN})/[A-Za-z0-9._-]+(?:\.md|/_overview\.md)?$`);
|
|
15
16
|
const GITHUB_URL_PATTERN = /https?:\/\/github\.com\//i;
|
|
16
17
|
const ABSOLUTE_FILE_REFERENCE_PATTERN = /(?:^|[\s(])(?:\/|[A-Za-z]:[\\/]|file:\/\/)/i;
|
|
17
18
|
const LEGACY_CROSS_REPO_LABEL_PATTERN = /cross-repo:/i;
|
|
@@ -112,22 +113,32 @@ function readBlueprintRecords(root, blueprintsRoot) {
|
|
|
112
113
|
if (!existsSync(statusRoot))
|
|
113
114
|
continue;
|
|
114
115
|
for (const entry of readdirSync(statusRoot, { withFileTypes: true })) {
|
|
115
|
-
|
|
116
|
+
const canonicalPath = entry.isDirectory()
|
|
117
|
+
? join(statusRoot, entry.name, BLUEPRINT_OVERVIEW_FILENAME)
|
|
118
|
+
: entry.isFile() && entry.name.endsWith('.md')
|
|
119
|
+
? join(statusRoot, entry.name)
|
|
120
|
+
: null;
|
|
121
|
+
if (!canonicalPath || !existsSync(canonicalPath))
|
|
116
122
|
continue;
|
|
117
|
-
const
|
|
118
|
-
if (!
|
|
123
|
+
const relativeBlueprintPath = relative(blueprintsRoot, canonicalPath);
|
|
124
|
+
if (!parseBlueprintDocumentRelativePath(relativeBlueprintPath) ||
|
|
125
|
+
isBlueprintSupportingMarkdownRelativePath(relativeBlueprintPath)) {
|
|
119
126
|
continue;
|
|
120
|
-
|
|
127
|
+
}
|
|
128
|
+
const raw = readFileSync(canonicalPath, 'utf8');
|
|
121
129
|
const data = matter(raw).data;
|
|
122
130
|
const type = data.type === 'parent-roadmap' ? 'parent-roadmap' : 'blueprint';
|
|
123
131
|
const parentRoadmap = typeof data.parent_roadmap === 'string' && data.parent_roadmap.trim()
|
|
124
132
|
? data.parent_roadmap.trim()
|
|
125
133
|
: undefined;
|
|
126
|
-
const
|
|
134
|
+
const parsedPath = parseBlueprintDocumentRelativePath(relativeBlueprintPath);
|
|
135
|
+
if (!parsedPath)
|
|
136
|
+
continue;
|
|
137
|
+
const key = `${status}/${parsedPath.slug}`;
|
|
127
138
|
records.push({
|
|
128
|
-
file: relativePath(root,
|
|
139
|
+
file: relativePath(root, canonicalPath),
|
|
129
140
|
key,
|
|
130
|
-
name:
|
|
141
|
+
name: parsedPath.slug,
|
|
131
142
|
...(parentRoadmap ? { parentRoadmap } : {}),
|
|
132
143
|
raw,
|
|
133
144
|
slug: key,
|
|
@@ -146,16 +157,18 @@ function indexBlueprints(records) {
|
|
|
146
157
|
byKey.set(record.name, record);
|
|
147
158
|
byKey.set(`blueprints/${record.key}`, record);
|
|
148
159
|
byKey.set(`blueprints/${record.key}/_overview.md`, record);
|
|
160
|
+
byKey.set(`blueprints/${record.key}.md`, record);
|
|
149
161
|
byKey.set(`${record.key}/_overview.md`, record);
|
|
162
|
+
byKey.set(`${record.key}.md`, record);
|
|
150
163
|
}
|
|
151
164
|
return byKey;
|
|
152
165
|
}
|
|
153
166
|
function extractWaveMapChildren(markdown) {
|
|
154
167
|
const refs = new Set();
|
|
155
|
-
const pathPattern = new RegExp(String.raw `(?:blueprints/)?(${BLUEPRINT_STATUS_PATTERN})/([A-Za-z0-9._-]+)(
|
|
168
|
+
const pathPattern = new RegExp(String.raw `(?:blueprints/)?(${BLUEPRINT_STATUS_PATTERN})/([A-Za-z0-9._-]+)(?:\.md|/_overview\.md)?`, 'g');
|
|
156
169
|
for (const match of markdown.matchAll(pathPattern)) {
|
|
157
170
|
const status = match[1];
|
|
158
|
-
const slug = match[2];
|
|
171
|
+
const slug = match[2]?.replace(/\.md$/, '');
|
|
159
172
|
if (!status || !slug)
|
|
160
173
|
continue;
|
|
161
174
|
refs.add(`${status}/${slug}`);
|
|
@@ -19,8 +19,8 @@ export declare const planStatusSchema: z.ZodEnum<{
|
|
|
19
19
|
completed: "completed";
|
|
20
20
|
draft: "draft";
|
|
21
21
|
planned: "planned";
|
|
22
|
-
"in-progress": "in-progress";
|
|
23
22
|
parked: "parked";
|
|
23
|
+
"in-progress": "in-progress";
|
|
24
24
|
archived: "archived";
|
|
25
25
|
}>;
|
|
26
26
|
/**
|
|
@@ -30,8 +30,8 @@ export declare const lifecycleBlueprintStatusSchema: z.ZodEnum<{
|
|
|
30
30
|
completed: "completed";
|
|
31
31
|
draft: "draft";
|
|
32
32
|
planned: "planned";
|
|
33
|
-
"in-progress": "in-progress";
|
|
34
33
|
parked: "parked";
|
|
34
|
+
"in-progress": "in-progress";
|
|
35
35
|
archived: "archived";
|
|
36
36
|
}>;
|
|
37
37
|
/**
|
|
@@ -71,8 +71,8 @@ export declare const crossRepoDependencySchema: z.ZodObject<{
|
|
|
71
71
|
completed: "completed";
|
|
72
72
|
draft: "draft";
|
|
73
73
|
planned: "planned";
|
|
74
|
-
"in-progress": "in-progress";
|
|
75
74
|
parked: "parked";
|
|
75
|
+
"in-progress": "in-progress";
|
|
76
76
|
archived: "archived";
|
|
77
77
|
}>>;
|
|
78
78
|
}, z.core.$strip>;
|
|
@@ -82,7 +82,7 @@ export declare const crossRepoDependencySchema: z.ZodObject<{
|
|
|
82
82
|
* Required fields:
|
|
83
83
|
* - type: `blueprint` or `parent-roadmap`
|
|
84
84
|
* - status: Current plan status
|
|
85
|
-
* - complexity: Estimated effort using t-shirt sizing
|
|
85
|
+
* - complexity: Estimated effort using t-shirt sizing (defaults to `M` when omitted for legacy blueprints)
|
|
86
86
|
*
|
|
87
87
|
* Optional fields:
|
|
88
88
|
* - last_updated: Date plan was last modified (YYYY-MM-DD)
|
|
@@ -101,17 +101,17 @@ export declare const planFrontmatterSchema: z.ZodObject<{
|
|
|
101
101
|
completed: "completed";
|
|
102
102
|
draft: "draft";
|
|
103
103
|
planned: "planned";
|
|
104
|
-
"in-progress": "in-progress";
|
|
105
104
|
parked: "parked";
|
|
105
|
+
"in-progress": "in-progress";
|
|
106
106
|
archived: "archived";
|
|
107
107
|
}>;
|
|
108
|
-
complexity: z.ZodEnum<{
|
|
108
|
+
complexity: z.ZodDefault<z.ZodEnum<{
|
|
109
109
|
XS: "XS";
|
|
110
110
|
S: "S";
|
|
111
111
|
M: "M";
|
|
112
112
|
L: "L";
|
|
113
113
|
XL: "XL";
|
|
114
|
-
}
|
|
114
|
+
}>>;
|
|
115
115
|
last_updated: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
116
116
|
created: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
117
117
|
progress: z.ZodOptional<z.ZodString>;
|
|
@@ -146,8 +146,8 @@ export declare const planFrontmatterSchema: z.ZodObject<{
|
|
|
146
146
|
completed: "completed";
|
|
147
147
|
draft: "draft";
|
|
148
148
|
planned: "planned";
|
|
149
|
-
"in-progress": "in-progress";
|
|
150
149
|
parked: "parked";
|
|
150
|
+
"in-progress": "in-progress";
|
|
151
151
|
archived: "archived";
|
|
152
152
|
}>>;
|
|
153
153
|
}, z.core.$strip>>>;
|
|
@@ -64,7 +64,7 @@ export const crossRepoDependencySchema = z.object({
|
|
|
64
64
|
* Required fields:
|
|
65
65
|
* - type: `blueprint` or `parent-roadmap`
|
|
66
66
|
* - status: Current plan status
|
|
67
|
-
* - complexity: Estimated effort using t-shirt sizing
|
|
67
|
+
* - complexity: Estimated effort using t-shirt sizing (defaults to `M` when omitted for legacy blueprints)
|
|
68
68
|
*
|
|
69
69
|
* Optional fields:
|
|
70
70
|
* - last_updated: Date plan was last modified (YYYY-MM-DD)
|
|
@@ -77,7 +77,7 @@ export const planFrontmatterSchema = z.object({
|
|
|
77
77
|
title: z.string().optional(),
|
|
78
78
|
description: z.string().optional(),
|
|
79
79
|
status: planStatusSchema,
|
|
80
|
-
complexity: complexitySchema,
|
|
80
|
+
complexity: complexitySchema.default('M'),
|
|
81
81
|
last_updated: z.union([z.string(), z.date()]).optional(),
|
|
82
82
|
created: z.union([z.string(), z.date()]).optional(),
|
|
83
83
|
progress: z.string().optional(),
|
|
@@ -13,8 +13,8 @@ export declare const blueprintStatusSchema: z.ZodEnum<{
|
|
|
13
13
|
completed: "completed";
|
|
14
14
|
draft: "draft";
|
|
15
15
|
planned: "planned";
|
|
16
|
-
"in-progress": "in-progress";
|
|
17
16
|
parked: "parked";
|
|
17
|
+
"in-progress": "in-progress";
|
|
18
18
|
archived: "archived";
|
|
19
19
|
}>;
|
|
20
20
|
export declare const blueprintComplexitySchema: z.ZodEnum<{
|
|
@@ -2,18 +2,24 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { Database } from '#db/sqlite.js';
|
|
5
|
-
import { glob } from 'glob';
|
|
6
5
|
import { parseBlueprintForDb } from './parser/blueprint-db-parser.js';
|
|
7
6
|
import { parseTechDebtForDb } from './parser/tech-debt-db-parser.js';
|
|
8
7
|
import { resolvesCrossRepo } from '#cross-repo/resolver.js';
|
|
8
|
+
import { scanBlueprintDirectory } from '#service/scanner.js';
|
|
9
9
|
import { resolveBlueprintRoot } from '#utils/blueprint-root.js';
|
|
10
|
+
import { parseBlueprintDocumentRelativePath } from '#utils/document-paths.js';
|
|
10
11
|
import { resolveTechDebtRoot } from '#utils/tech-debt-root.js';
|
|
12
|
+
import { glob } from 'glob';
|
|
11
13
|
// ---------------------------------------------------------------------------
|
|
12
14
|
// Helpers
|
|
13
15
|
// ---------------------------------------------------------------------------
|
|
14
|
-
function deriveSlugFromBlueprintPath(filePath) {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
function deriveSlugFromBlueprintPath(filePath, blueprintRoot) {
|
|
17
|
+
const relativePath = path.relative(blueprintRoot, filePath);
|
|
18
|
+
const parsed = parseBlueprintDocumentRelativePath(relativePath);
|
|
19
|
+
if (!parsed) {
|
|
20
|
+
throw new Error(`Not a canonical blueprint document: ${filePath}`);
|
|
21
|
+
}
|
|
22
|
+
return parsed.slug;
|
|
17
23
|
}
|
|
18
24
|
function deriveSlugFromTechDebtPath(filePath) {
|
|
19
25
|
// tech-debt/<status>/h-NNN-slug.md → slug is the basename without extension
|
|
@@ -47,9 +53,9 @@ function isAllowedCrossOrg(db, sourceOrg, targetOrg) {
|
|
|
47
53
|
// ---------------------------------------------------------------------------
|
|
48
54
|
// Blueprint ingester
|
|
49
55
|
// ---------------------------------------------------------------------------
|
|
50
|
-
function upsertBlueprint(db, filePath,
|
|
56
|
+
function upsertBlueprint(db, filePath, blueprintRoot) {
|
|
51
57
|
const content = readFileSync(filePath, 'utf8');
|
|
52
|
-
const slug = deriveSlugFromBlueprintPath(filePath);
|
|
58
|
+
const slug = deriveSlugFromBlueprintPath(filePath, blueprintRoot);
|
|
53
59
|
const parsed = parseBlueprintForDb(content, filePath, slug);
|
|
54
60
|
const now = Date.now();
|
|
55
61
|
const upsertBp = db.prepare(`INSERT INTO blueprints
|
|
@@ -197,18 +203,20 @@ export async function ingestBlueprints(opts) {
|
|
|
197
203
|
const errors = [];
|
|
198
204
|
let ingested = 0;
|
|
199
205
|
const blueprintRoot = resolveBlueprintRoot(cwd);
|
|
200
|
-
const
|
|
201
|
-
|
|
206
|
+
const files = scanBlueprintDirectory({
|
|
207
|
+
baseDir: blueprintRoot,
|
|
208
|
+
includeSpecialFolders: true,
|
|
209
|
+
}).map((entry) => entry.path);
|
|
202
210
|
for (const filePath of files) {
|
|
203
211
|
try {
|
|
204
212
|
const content = readFileSync(filePath, 'utf8');
|
|
205
|
-
const slug = deriveSlugFromBlueprintPath(filePath);
|
|
213
|
+
const slug = deriveSlugFromBlueprintPath(filePath, blueprintRoot);
|
|
206
214
|
const newHash = createHash('sha256').update(content).digest('hex');
|
|
207
215
|
if (!dryRun) {
|
|
208
216
|
const existing = existingBlueprintHash(db, slug);
|
|
209
217
|
if (existing === newHash)
|
|
210
218
|
continue;
|
|
211
|
-
upsertBlueprint(db, filePath,
|
|
219
|
+
upsertBlueprint(db, filePath, blueprintRoot);
|
|
212
220
|
}
|
|
213
221
|
ingested++;
|
|
214
222
|
}
|
|
@@ -16,7 +16,6 @@ export { validatePlanTemplate } from './core/validation/template.js';
|
|
|
16
16
|
export { type BlueprintDiff, type DiffChange, type DiffFieldChange, formatDiffForDisplay, generateBlueprintDiff, } from './history/diff.js';
|
|
17
17
|
export { checkAllCheckboxes, checkFirstCheckbox, extractCodeBlocks, extractTaskSection, updateBlockedReason, updateTaskStatus, } from './markdown/helpers.js';
|
|
18
18
|
export { applyBlueprintLifecycle, type BlueprintLifecycleIntent, type BlueprintLifecycleResult, type LifecycleTaskStatus, } from './lifecycle/engine.js';
|
|
19
|
-
export { type GraphEdge, type GraphEdgeType, type GraphLayout, type GraphNode, type GraphNodeType, type NormalizedGraph, parseMermaidToGraph, serializeGraphToMermaid, taskGraphToNormalizedGraph, } from './graph/index.js';
|
|
20
19
|
export type { BlueprintQueryFilters, BlueprintQueryResult, BlueprintQuerySummary, BlueprintRecord, BlueprintSortField, BlueprintSortOptions, Complexity, SortDirection, TaskStatus, } from './query/types.js';
|
|
21
20
|
export { isBlueprintStatus, isComplexity, isTaskStatus } from './query/types.js';
|
|
22
21
|
export { BlueprintNotFoundError } from './utils/errors.js';
|
|
@@ -17,8 +17,6 @@ export { formatDiffForDisplay, generateBlueprintDiff, } from './history/diff.js'
|
|
|
17
17
|
// Markdown helpers (pure functions)
|
|
18
18
|
export { checkAllCheckboxes, checkFirstCheckbox, extractCodeBlocks, extractTaskSection, updateBlockedReason, updateTaskStatus, } from './markdown/helpers.js';
|
|
19
19
|
export { applyBlueprintLifecycle, } from './lifecycle/engine.js';
|
|
20
|
-
// Graph model + Mermaid integration
|
|
21
|
-
export { parseMermaidToGraph, serializeGraphToMermaid, taskGraphToNormalizedGraph, } from './graph/index.js';
|
|
22
20
|
export { isBlueprintStatus, isComplexity, isTaskStatus } from './query/types.js';
|
|
23
21
|
export { BlueprintNotFoundError } from './utils/errors.js';
|
|
24
22
|
// Evidence Contract (F10) — pin per-kind evidence rules at zod parse time.
|
|
@@ -8,11 +8,18 @@ import { readBlueprintExecutionMetadata } from '#execution/metadata';
|
|
|
8
8
|
import { BlueprintService } from '#service/BlueprintService';
|
|
9
9
|
import { scanBlueprintDirectory } from '#service/scanner';
|
|
10
10
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
11
|
+
import { parseBlueprintDocumentRelativePath } from '#utils/document-paths.js';
|
|
11
12
|
import { relativeBlueprintSlug } from './local.js';
|
|
12
13
|
function isBlueprintOverview(file) {
|
|
13
14
|
const normalized = file.replace(/\\/g, '/');
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const roots = ['webpresso/blueprints/', 'blueprints/'];
|
|
16
|
+
for (const root of roots) {
|
|
17
|
+
const index = normalized.indexOf(root);
|
|
18
|
+
if (index === -1)
|
|
19
|
+
continue;
|
|
20
|
+
return parseBlueprintDocumentRelativePath(normalized.slice(index + root.length)) !== null;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
16
23
|
}
|
|
17
24
|
function normalizePath(file) {
|
|
18
25
|
return file.replace(/\\/g, '/');
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { applyBlueprintLifecycle } from '#lifecycle/engine';
|
|
4
4
|
import { scanBlueprintDirectory } from '#service/scanner';
|
|
5
5
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
6
|
+
import { getBlueprintDocumentPaths } from '#utils/document-paths.js';
|
|
6
7
|
const BLUEPRINT_SLUG_SEGMENT_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7
8
|
function isStatusSegment(segment) {
|
|
8
9
|
return (segment === 'draft' ||
|
|
@@ -73,10 +74,20 @@ export async function applyBlueprintLifecycleToFile(projectRoot, slug, intent) {
|
|
|
73
74
|
const location = await resolveBlueprintFile(projectRoot, slug);
|
|
74
75
|
const raw = await readFile(location.path, 'utf-8');
|
|
75
76
|
const mutation = applyBlueprintLifecycle(raw, location.slug, intent);
|
|
77
|
+
const relativeSlug = relativeBlueprintSlug(location.slug);
|
|
78
|
+
const isFlatFile = path.basename(location.path) !== '_overview.md';
|
|
76
79
|
const sourceDir = path.dirname(location.path);
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
+
const targetDocumentPaths = getBlueprintDocumentPaths(baseDir, mutation.targetStatus, relativeSlug);
|
|
81
|
+
const targetDir = targetDocumentPaths.directory;
|
|
82
|
+
const targetPath = isFlatFile ? targetDocumentPaths.flat : targetDocumentPaths.folder;
|
|
83
|
+
if (isFlatFile) {
|
|
84
|
+
if (location.path !== targetPath) {
|
|
85
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
86
|
+
await rename(location.path, targetPath);
|
|
87
|
+
await tryRemoveEmptyParent(sourceDir);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (sourceDir !== targetDir) {
|
|
80
91
|
await mkdir(path.dirname(targetDir), { recursive: true });
|
|
81
92
|
await rename(sourceDir, targetDir);
|
|
82
93
|
await tryRemoveEmptyParent(path.dirname(sourceDir));
|
|
@@ -84,7 +95,7 @@ export async function applyBlueprintLifecycleToFile(projectRoot, slug, intent) {
|
|
|
84
95
|
await writeFile(targetPath, mutation.markdown, 'utf-8');
|
|
85
96
|
return {
|
|
86
97
|
...mutation,
|
|
87
|
-
moved: sourceDir !== targetDir,
|
|
98
|
+
moved: isFlatFile ? location.path !== targetPath : sourceDir !== targetDir,
|
|
88
99
|
path: targetPath,
|
|
89
100
|
slug: location.slug,
|
|
90
101
|
};
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
* These exports use Node.js APIs (fs, simple-git) and are NOT compatible with Cloudflare Workers.
|
|
5
5
|
* For Workers-safe functions, use the main 'webpresso/blueprint' entry point.
|
|
6
6
|
*/
|
|
7
|
-
export type { FalseDependency, ParallelizeResult, TaskFiles, TaskPairAnalysis, } from './dag/local/independence.js';
|
|
8
|
-
export { createMockPackageGraph, IndependenceDetector } from './dag/local/independence.js';
|
|
9
|
-
export { createMockFileSystem, PackageGraph, realFileSystem } from './dag/local/package-graph.js';
|
|
10
7
|
export { type AcceptanceCriteria, type Blueprint, buildRoadmapModel, buildBlueprintProgressBridgeState, type BlueprintStatus, type BlueprintTaskStatus, checkAcceptanceCriteria, checkAllCheckboxes, checkChangelog, checkFirstCheckbox, complexitySchema, type CriteriaResult, extractTaskSection, formatDiffForDisplay, generateBlueprintDiff, isBlueprintStatus, isComplexity, isTaskStatus, lifecycleBlueprintStatusSchema, normalizeOmxTeamTaskSnapshot, type OmxTeamTaskSnapshot, type Phase, parseBlueprint, type PlanComplexity, type PlanFrontmatter, planStatusSchema, projectBlueprintLifecycleFromRuntime, resolveBlueprintProgressBridgePath, type RoadmapModel, type RoadmapNode, type RoadmapRollup, type RoadmapLike, serializeBlueprint, type Task, taskStatusSchema, type TaskStatusValue, updateBlockedReason, updateTaskStatus, type ValidationResult, validateEmbeddedPhases, validatePlanLinks, validatePlanState, validatePlanTemplate, } from './index.js';
|
|
11
8
|
export { BlueprintCreationService, type BlueprintCreationServiceOptions, type BlueprintDraft, type CreateBlueprintInput, type CreatedBlueprint, } from './service/BlueprintCreationService.js';
|
|
12
9
|
export { type BlueprintQueryOptions, BlueprintService, type BlueprintSummary, } from './service/BlueprintService.js';
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
* These exports use Node.js APIs (fs, simple-git) and are NOT compatible with Cloudflare Workers.
|
|
5
5
|
* For Workers-safe functions, use the main 'webpresso/blueprint' entry point.
|
|
6
6
|
*/
|
|
7
|
-
export { createMockPackageGraph, IndependenceDetector } from './dag/local/independence.js';
|
|
8
|
-
export { createMockFileSystem, PackageGraph, realFileSystem } from './dag/local/package-graph.js';
|
|
9
7
|
// Workers-safe exports for convenience (explicit re-export to avoid wildcard)
|
|
10
8
|
export { buildRoadmapModel, buildBlueprintProgressBridgeState, checkAcceptanceCriteria, checkAllCheckboxes, checkChangelog, checkFirstCheckbox, complexitySchema, extractTaskSection, formatDiffForDisplay, generateBlueprintDiff, isBlueprintStatus, isComplexity, isTaskStatus, lifecycleBlueprintStatusSchema, normalizeOmxTeamTaskSnapshot, parseBlueprint, planStatusSchema, projectBlueprintLifecycleFromRuntime, resolveBlueprintProgressBridgePath, serializeBlueprint, taskStatusSchema, updateBlockedReason, updateTaskStatus, validateEmbeddedPhases, validatePlanLinks, validatePlanState, validatePlanTemplate, } from './index.js';
|
|
11
9
|
// Services (require filesystem/git)
|
|
@@ -4,7 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { parseBlueprint } from '#core/parser';
|
|
5
5
|
import { scanBlueprintDirectory } from '#service/scanner';
|
|
6
6
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
7
|
-
import {
|
|
7
|
+
import { getBlueprintDocumentPaths } from '#utils/document-paths.js';
|
|
8
|
+
import { resolvePackageAssetPreferred } from '#utils/package-assets';
|
|
8
9
|
const RESERVED_BLUEPRINT_SLUGS = new Set([
|
|
9
10
|
'draft',
|
|
10
11
|
'planned',
|
|
@@ -13,7 +14,10 @@ const RESERVED_BLUEPRINT_SLUGS = new Set([
|
|
|
13
14
|
'completed',
|
|
14
15
|
'archived',
|
|
15
16
|
]);
|
|
16
|
-
const DEFAULT_TEMPLATE_PATH =
|
|
17
|
+
const DEFAULT_TEMPLATE_PATH = resolvePackageAssetPreferred([
|
|
18
|
+
'docs/templates/blueprint.md',
|
|
19
|
+
'catalog/docs/templates/blueprint.md',
|
|
20
|
+
]);
|
|
17
21
|
function formatDate(date) {
|
|
18
22
|
return date.toISOString().split('T')[0] ?? date.toISOString();
|
|
19
23
|
}
|
|
@@ -164,7 +168,7 @@ export class BlueprintCreationService {
|
|
|
164
168
|
assertGoalProducesUsableSlug(goal, baseSlug);
|
|
165
169
|
const slug = this.resolveCollisionSafeSlug(baseSlug);
|
|
166
170
|
const title = sentenceCase(goal);
|
|
167
|
-
const outputPath =
|
|
171
|
+
const outputPath = getBlueprintDocumentPaths(this.blueprintsRoot, 'draft', slug).flat;
|
|
168
172
|
const relativeFilePath = toPortableRelativePath(this.projectRoot, outputPath);
|
|
169
173
|
const date = formatDate(this.now());
|
|
170
174
|
const template = type === 'blueprint' ? prepareTemplate(await readFile(this.templatePath, 'utf-8')) : undefined;
|
|
@@ -202,11 +206,11 @@ export class BlueprintCreationService {
|
|
|
202
206
|
async create(input) {
|
|
203
207
|
const draft = await this.compileDraft(input);
|
|
204
208
|
const draftRoot = path.join(this.blueprintsRoot, 'draft');
|
|
205
|
-
const
|
|
209
|
+
const finalPath = draft.outputPath;
|
|
206
210
|
await mkdir(draftRoot, { recursive: true });
|
|
207
|
-
await mkdir(path.dirname(
|
|
211
|
+
await mkdir(path.dirname(finalPath), { recursive: true });
|
|
208
212
|
const tempDir = await mkdtemp(path.join(draftRoot, `${draft.slug}.tmp-`));
|
|
209
|
-
const tempPath = path.join(tempDir,
|
|
213
|
+
const tempPath = path.join(tempDir, `${draft.slug}.md`);
|
|
210
214
|
try {
|
|
211
215
|
await writeFile(tempPath, draft.markdown, 'utf-8');
|
|
212
216
|
const writtenMarkdown = await readFile(tempPath, 'utf-8');
|
|
@@ -214,7 +218,8 @@ export class BlueprintCreationService {
|
|
|
214
218
|
if (!validation.valid || !validation.blueprint) {
|
|
215
219
|
throw new Error(validation.error ?? 'Generated blueprint failed validation.');
|
|
216
220
|
}
|
|
217
|
-
await rename(
|
|
221
|
+
await rename(tempPath, finalPath);
|
|
222
|
+
await rm(tempDir, { force: true, recursive: true });
|
|
218
223
|
return {
|
|
219
224
|
...draft,
|
|
220
225
|
blueprint: validation.blueprint,
|
|
@@ -244,7 +249,10 @@ export class BlueprintCreationService {
|
|
|
244
249
|
}
|
|
245
250
|
}
|
|
246
251
|
function blueprintDirectoryExists(blueprintsRoot, slug) {
|
|
247
|
-
return [...RESERVED_BLUEPRINT_SLUGS].some((status) =>
|
|
252
|
+
return [...RESERVED_BLUEPRINT_SLUGS].some((status) => {
|
|
253
|
+
const paths = getBlueprintDocumentPaths(blueprintsRoot, status, slug);
|
|
254
|
+
return existsSync(paths.directory) || existsSync(paths.flat);
|
|
255
|
+
});
|
|
248
256
|
}
|
|
249
257
|
async function removeIfEmpty(directory) {
|
|
250
258
|
try {
|