@webpresso/agent-kit 0.21.4 → 0.21.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/README.md +105 -41
- package/catalog/agent/skills/plan-refine/SKILL.md +5 -4
- 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 +1 -0
- package/catalog/docs/templates/blueprint.yaml +6 -3
- 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 +37 -4
- package/dist/esm/audit/resolve-audit-script.d.ts +24 -0
- package/dist/esm/audit/resolve-audit-script.js +27 -0
- package/dist/esm/blueprint/index.d.ts +0 -1
- package/dist/esm/blueprint/index.js +0 -2
- package/dist/esm/blueprint/local.d.ts +0 -3
- package/dist/esm/blueprint/local.js +0 -2
- package/dist/esm/blueprint/service/BlueprintCreationService.js +5 -2
- 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/sync-catalog-doc-templates.d.ts +23 -0
- package/dist/esm/build/sync-catalog-doc-templates.js +93 -0
- package/dist/esm/cli/commands/audit.js +2 -7
- package/dist/esm/cli/commands/blueprint/router.js +5 -2
- package/dist/esm/cli/commands/blueprint/template-resolver.js +8 -4
- package/dist/esm/cli/commands/init/host-visibility.js +4 -2
- package/dist/esm/cli/commands/init/index.js +46 -7
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +141 -6
- package/dist/esm/cli/commands/typecheck.js +10 -4
- package/dist/esm/e2e/command-builder.js +26 -7
- package/dist/esm/e2e/execution.js +4 -0
- package/dist/esm/e2e/run-planner.js +1 -0
- package/dist/esm/e2e/types.d.ts +1 -0
- package/dist/esm/format/index.js +7 -1
- package/dist/esm/lint/index.js +3 -1
- package/dist/esm/mcp/blueprint-server.js +361 -66
- package/dist/esm/mcp/tools/audit.js +2 -8
- package/dist/esm/mcp/tools/e2e.d.ts +1 -1
- package/dist/esm/package.json +3 -0
- package/dist/esm/test/command-builder.d.ts +1 -0
- package/dist/esm/test/command-builder.js +8 -2
- 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 +23 -0
- package/dist/esm/tool-runtime/resolve-runner.d.ts +13 -0
- package/dist/esm/tool-runtime/resolve-runner.js +40 -0
- package/package.json +12 -19
- package/skills/plan-refine/SKILL.md +5 -4
- 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
|
@@ -4,7 +4,7 @@ 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 { resolvePackageAssetPreferred } from '#utils/package-assets';
|
|
8
8
|
const RESERVED_BLUEPRINT_SLUGS = new Set([
|
|
9
9
|
'draft',
|
|
10
10
|
'planned',
|
|
@@ -13,7 +13,10 @@ const RESERVED_BLUEPRINT_SLUGS = new Set([
|
|
|
13
13
|
'completed',
|
|
14
14
|
'archived',
|
|
15
15
|
]);
|
|
16
|
-
const DEFAULT_TEMPLATE_PATH =
|
|
16
|
+
const DEFAULT_TEMPLATE_PATH = resolvePackageAssetPreferred([
|
|
17
|
+
'docs/templates/blueprint.md',
|
|
18
|
+
'catalog/docs/templates/blueprint.md',
|
|
19
|
+
]);
|
|
17
20
|
function formatDate(date) {
|
|
18
21
|
return date.toISOString().split('T')[0] ?? date.toISOString();
|
|
19
22
|
}
|
|
@@ -3,4 +3,15 @@
|
|
|
3
3
|
* package root) is found. Works whether running from src/ or dist/esm/.
|
|
4
4
|
*/
|
|
5
5
|
export declare function resolvePackageAsset(relativeFromRoot: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the first existing candidate, in priority order. Use when an asset
|
|
8
|
+
* lives at one path in the source checkout but a different path in the
|
|
9
|
+
* published tarball — e.g. templates authored under repo-root `docs/templates/`
|
|
10
|
+
* but shipped under `catalog/docs/templates/` because the npm `files` list
|
|
11
|
+
* includes `catalog/` and not `docs/`. Prefers the dev/source location and
|
|
12
|
+
* falls back to the shipped one, mirroring `bin/_run.js`'s source→built path
|
|
13
|
+
* translation. Falls back to cwd-relative on the first candidate when none
|
|
14
|
+
* exist, matching `resolvePackageAsset`'s last-resort behavior.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolvePackageAssetPreferred(candidates: readonly string[]): string;
|
|
6
17
|
//# sourceMappingURL=package-assets.d.ts.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
/**
|
|
4
|
-
* Walk up from this file's location
|
|
5
|
-
*
|
|
4
|
+
* Walk up from this file's location looking for `relativeFromRoot`. Returns the
|
|
5
|
+
* first existing match, or `null` if none is found within the ancestor budget.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
function findPackageAsset(relativeFromRoot) {
|
|
8
8
|
let dir = path.dirname(new URL(import.meta.url).pathname);
|
|
9
9
|
for (let i = 0; i < 8; i++) {
|
|
10
10
|
const candidate = path.join(dir, relativeFromRoot);
|
|
@@ -15,6 +15,35 @@ export function resolvePackageAsset(relativeFromRoot) {
|
|
|
15
15
|
break;
|
|
16
16
|
dir = parent;
|
|
17
17
|
}
|
|
18
|
-
return
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Walk up from this file's location until the given path (relative to the
|
|
22
|
+
* package root) is found. Works whether running from src/ or dist/esm/.
|
|
23
|
+
*/
|
|
24
|
+
export function resolvePackageAsset(relativeFromRoot) {
|
|
25
|
+
return findPackageAsset(relativeFromRoot) ?? path.join(process.cwd(), relativeFromRoot);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the first existing candidate, in priority order. Use when an asset
|
|
29
|
+
* lives at one path in the source checkout but a different path in the
|
|
30
|
+
* published tarball — e.g. templates authored under repo-root `docs/templates/`
|
|
31
|
+
* but shipped under `catalog/docs/templates/` because the npm `files` list
|
|
32
|
+
* includes `catalog/` and not `docs/`. Prefers the dev/source location and
|
|
33
|
+
* falls back to the shipped one, mirroring `bin/_run.js`'s source→built path
|
|
34
|
+
* translation. Falls back to cwd-relative on the first candidate when none
|
|
35
|
+
* exist, matching `resolvePackageAsset`'s last-resort behavior.
|
|
36
|
+
*/
|
|
37
|
+
export function resolvePackageAssetPreferred(candidates) {
|
|
38
|
+
for (const relativeFromRoot of candidates) {
|
|
39
|
+
const found = findPackageAsset(relativeFromRoot);
|
|
40
|
+
if (found)
|
|
41
|
+
return found;
|
|
42
|
+
}
|
|
43
|
+
const primary = candidates[0];
|
|
44
|
+
if (primary === undefined) {
|
|
45
|
+
throw new Error('resolvePackageAssetPreferred requires at least one candidate path');
|
|
46
|
+
}
|
|
47
|
+
return path.join(process.cwd(), primary);
|
|
19
48
|
}
|
|
20
49
|
//# sourceMappingURL=package-assets.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* sync-catalog-doc-templates.ts
|
|
4
|
+
*
|
|
5
|
+
* `docs/templates/` (repo root) is the editable source of truth for the
|
|
6
|
+
* blueprint / doc templates. `catalog/docs/templates/` is the published +
|
|
7
|
+
* scaffolded mirror: the npm `files` list ships `catalog/` but NOT repo-root
|
|
8
|
+
* `docs/` (which holds internal docs that must stay private), and `wp init`
|
|
9
|
+
* scaffolds templates into a consumer from `catalog/docs/templates/`.
|
|
10
|
+
*
|
|
11
|
+
* The two MUST stay byte-identical. Nothing synced them before, so they
|
|
12
|
+
* drifted — this script regenerates the mirror from the source and is wired
|
|
13
|
+
* into the build. The colocated drift test fails CI if they ever diverge.
|
|
14
|
+
*
|
|
15
|
+
* Default: regenerate the mirror from the source.
|
|
16
|
+
* `--check`: exit non-zero and list drift without writing (CI / pre-publish).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Names that are out of sync between source and mirror: content mismatch,
|
|
20
|
+
* missing from the mirror, or orphaned in the mirror.
|
|
21
|
+
*/
|
|
22
|
+
export declare function diffDocTemplateMirror(): string[];
|
|
23
|
+
//# sourceMappingURL=sync-catalog-doc-templates.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* sync-catalog-doc-templates.ts
|
|
4
|
+
*
|
|
5
|
+
* `docs/templates/` (repo root) is the editable source of truth for the
|
|
6
|
+
* blueprint / doc templates. `catalog/docs/templates/` is the published +
|
|
7
|
+
* scaffolded mirror: the npm `files` list ships `catalog/` but NOT repo-root
|
|
8
|
+
* `docs/` (which holds internal docs that must stay private), and `wp init`
|
|
9
|
+
* scaffolds templates into a consumer from `catalog/docs/templates/`.
|
|
10
|
+
*
|
|
11
|
+
* The two MUST stay byte-identical. Nothing synced them before, so they
|
|
12
|
+
* drifted — this script regenerates the mirror from the source and is wired
|
|
13
|
+
* into the build. The colocated drift test fails CI if they ever diverge.
|
|
14
|
+
*
|
|
15
|
+
* Default: regenerate the mirror from the source.
|
|
16
|
+
* `--check`: exit non-zero and list drift without writing (CI / pre-publish).
|
|
17
|
+
*/
|
|
18
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
import { argv, exit } from 'node:process';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
const PACKAGE_ROOT = dirname(dirname(import.meta.dirname));
|
|
23
|
+
const SOURCE_DIR = join(PACKAGE_ROOT, 'docs', 'templates');
|
|
24
|
+
const MIRROR_DIR = join(PACKAGE_ROOT, 'catalog', 'docs', 'templates');
|
|
25
|
+
function listFiles(dir) {
|
|
26
|
+
if (!existsSync(dir))
|
|
27
|
+
return [];
|
|
28
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
29
|
+
.filter((entry) => entry.isFile())
|
|
30
|
+
.map((entry) => entry.name)
|
|
31
|
+
.sort();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Names that are out of sync between source and mirror: content mismatch,
|
|
35
|
+
* missing from the mirror, or orphaned in the mirror.
|
|
36
|
+
*/
|
|
37
|
+
export function diffDocTemplateMirror() {
|
|
38
|
+
const sourceFiles = listFiles(SOURCE_DIR);
|
|
39
|
+
const mirrorFiles = listFiles(MIRROR_DIR);
|
|
40
|
+
const names = [...new Set([...sourceFiles, ...mirrorFiles])].sort();
|
|
41
|
+
const drift = [];
|
|
42
|
+
for (const name of names) {
|
|
43
|
+
const inSource = sourceFiles.includes(name);
|
|
44
|
+
const inMirror = mirrorFiles.includes(name);
|
|
45
|
+
if (!inSource || !inMirror) {
|
|
46
|
+
drift.push(name);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (readFileSync(join(SOURCE_DIR, name), 'utf8') !== readFileSync(join(MIRROR_DIR, name), 'utf8')) {
|
|
50
|
+
drift.push(name);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return drift;
|
|
54
|
+
}
|
|
55
|
+
/** Regenerate the mirror from the source: copy every file, drop orphans. */
|
|
56
|
+
function syncDocTemplateMirror() {
|
|
57
|
+
mkdirSync(MIRROR_DIR, { recursive: true });
|
|
58
|
+
const sourceFiles = listFiles(SOURCE_DIR);
|
|
59
|
+
for (const name of listFiles(MIRROR_DIR)) {
|
|
60
|
+
if (!sourceFiles.includes(name))
|
|
61
|
+
rmSync(join(MIRROR_DIR, name));
|
|
62
|
+
}
|
|
63
|
+
for (const name of sourceFiles) {
|
|
64
|
+
cpSync(join(SOURCE_DIR, name), join(MIRROR_DIR, name));
|
|
65
|
+
}
|
|
66
|
+
return sourceFiles.length;
|
|
67
|
+
}
|
|
68
|
+
function main() {
|
|
69
|
+
if (!existsSync(SOURCE_DIR)) {
|
|
70
|
+
console.error(`doc templates source not found: ${SOURCE_DIR}`);
|
|
71
|
+
exit(1);
|
|
72
|
+
}
|
|
73
|
+
if (argv.includes('--check')) {
|
|
74
|
+
const drift = diffDocTemplateMirror();
|
|
75
|
+
if (drift.length > 0) {
|
|
76
|
+
console.error([
|
|
77
|
+
'catalog/docs/templates/ is out of sync with docs/templates/:',
|
|
78
|
+
...drift.map((name) => ` - ${name}`),
|
|
79
|
+
'Run `bun src/build/sync-catalog-doc-templates.ts` and commit the result.',
|
|
80
|
+
].join('\n'));
|
|
81
|
+
exit(1);
|
|
82
|
+
}
|
|
83
|
+
console.log('catalog/docs/templates/ is in sync with docs/templates/');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const count = syncDocTemplateMirror();
|
|
87
|
+
console.log(`Synced ${count} template(s) → catalog/docs/templates/`);
|
|
88
|
+
}
|
|
89
|
+
// Run as a script, but stay side-effect-free when imported (e.g. by the test).
|
|
90
|
+
if (fileURLToPath(import.meta.url) === argv[1]) {
|
|
91
|
+
main();
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=sync-catalog-doc-templates.js.map
|
|
@@ -9,6 +9,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { runAuditDispatch } from './audit-core.js';
|
|
11
11
|
import { runStryker } from '#audit/run-stryker';
|
|
12
|
+
import { resolveAuditScriptPath } from '#audit/resolve-audit-script';
|
|
12
13
|
const REPO_AUDIT_REGISTRY = {
|
|
13
14
|
'catalog-drift': async (root) => (await import('#audit/repo-guardrails')).auditCatalogDrift(root),
|
|
14
15
|
'package-surface': async (root) => (await import('#audit/package-surface')).auditPackageSurface(root),
|
|
@@ -119,13 +120,7 @@ const AUDIT_KINDS = [
|
|
|
119
120
|
];
|
|
120
121
|
const AUDIT_KIND_LIST = AUDIT_KINDS.join(', ');
|
|
121
122
|
function resolveAuditScript(name) {
|
|
122
|
-
|
|
123
|
-
if (existsSync(fromSource)) {
|
|
124
|
-
return fromSource.pathname;
|
|
125
|
-
}
|
|
126
|
-
const bundleDir = path.dirname(new URL(import.meta.url).pathname);
|
|
127
|
-
const packageRoot = path.resolve(bundleDir, '..');
|
|
128
|
-
return path.join(packageRoot, 'src', 'audit', name);
|
|
123
|
+
return resolveAuditScriptPath(name, { moduleUrl: import.meta.url });
|
|
129
124
|
}
|
|
130
125
|
async function runAuditScript(script, extraArgs) {
|
|
131
126
|
const runtime = process.env.BUN_INSTALL ? 'bun' : 'bun';
|
|
@@ -6,7 +6,7 @@ import { blueprintToSpecKit } from '#export/spec-kit/index';
|
|
|
6
6
|
import { getProjectRoot } from '#cli/utils';
|
|
7
7
|
import { resolveBlueprintRoot } from '#utils/blueprint-root';
|
|
8
8
|
import { applyBlueprintLifecycleToFile, BlueprintCreationService, BlueprintService, complexitySchema, relativeBlueprintSlug, parseBlueprint, planStatusSchema, runBlueprintAudit, resolveBlueprintFile, serializeBlueprint, validateAllTasksDone, } from '#local';
|
|
9
|
-
import {
|
|
9
|
+
import { resolvePackageAssetPreferred } from '#utils/package-assets';
|
|
10
10
|
import { describeBlueprintExecutionRuntime, buildBlueprintLaunchSpec, buildStoppedRuntimeEvidence, controlBlueprintExecution, initializeBlueprintExecutionProgressBridge, launchBlueprintExecution, persistBlueprintExecutionArtifacts, persistBlueprintExecutionMetadata, recordLaunchFailure, reconcileBlueprintRuntimeSnapshot, readBlueprintExecutionState, syncBlueprintExecutionProgress, writeBlueprintRuntimeSnapshot, } from './execution.js';
|
|
11
11
|
import { advanceTask as advanceTaskMutation, finalizeBlueprint as finalizeBlueprintMutation, promoteBlueprint as promoteBlueprintMutation, } from './mutations.js';
|
|
12
12
|
import { BlueprintAuditFailedError, executeBlueprintSubcommand } from './router-dispatch.js';
|
|
@@ -40,7 +40,10 @@ function assertBlueprintCanMoveToStatus(blueprint, nextStatus) {
|
|
|
40
40
|
* when the lookup fails in unrelated contexts (e.g. `wp --help`).
|
|
41
41
|
*/
|
|
42
42
|
function resolveRepoBlueprintTemplatePath() {
|
|
43
|
-
return
|
|
43
|
+
return resolvePackageAssetPreferred([
|
|
44
|
+
'docs/templates/blueprint.md',
|
|
45
|
+
'catalog/docs/templates/blueprint.md',
|
|
46
|
+
]);
|
|
44
47
|
}
|
|
45
48
|
function todayIsoDate() {
|
|
46
49
|
return new Date().toISOString().split('T')[0] ?? new Date().toISOString();
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { readdirSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { resolvePackageAssetPreferred } from '#utils/package-assets.js';
|
|
4
4
|
/**
|
|
5
|
-
* Default templates directory: docs/templates/
|
|
6
|
-
*
|
|
5
|
+
* Default templates directory: docs/templates/ in a source checkout, falling
|
|
6
|
+
* back to the shipped catalog/docs/templates/ in the published package (same
|
|
7
|
+
* strategy as resolveRepoBlueprintTemplatePath in router.ts).
|
|
7
8
|
*/
|
|
8
9
|
function defaultTemplatesDir() {
|
|
9
|
-
return path.dirname(
|
|
10
|
+
return path.dirname(resolvePackageAssetPreferred([
|
|
11
|
+
'docs/templates/blueprint.md',
|
|
12
|
+
'catalog/docs/templates/blueprint.md',
|
|
13
|
+
]));
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* List available templates from `templatesDir` (defaults to docs/templates/).
|
|
@@ -5,6 +5,8 @@ export const AGENT_HOSTS = ['codex', 'claude', 'opencode'];
|
|
|
5
5
|
export const REQUIRED_CORE_CAPABILITIES = ['verify', 'plan-refine'];
|
|
6
6
|
export const VISIBILITY_STATUSES = ['visible-now', 'visible-after-restart', 'not-visible'];
|
|
7
7
|
export function parseAgentHosts(value) {
|
|
8
|
+
if (value?.trim() === 'none')
|
|
9
|
+
return [];
|
|
8
10
|
if (!value || value.trim().length === 0 || value.trim() === 'all')
|
|
9
11
|
return [...AGENT_HOSTS];
|
|
10
12
|
const out = [];
|
|
@@ -19,7 +21,7 @@ export function parseAgentHosts(value) {
|
|
|
19
21
|
unknown.push(token);
|
|
20
22
|
}
|
|
21
23
|
if (unknown.length > 0) {
|
|
22
|
-
throw new Error(`Unknown host(s): ${unknown.join(', ')}. Expected one of: ${AGENT_HOSTS.join(', ')}, all.`);
|
|
24
|
+
throw new Error(`Unknown host(s): ${unknown.join(', ')}. Expected one of: ${AGENT_HOSTS.join(', ')}, all, none.`);
|
|
23
25
|
}
|
|
24
26
|
return [...new Set(out)];
|
|
25
27
|
}
|
|
@@ -54,7 +56,7 @@ export function hostSkillRoots(repoRoot, host, homeDir = homedir()) {
|
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
export function auditHostSkillVisibility(input) {
|
|
57
|
-
const selectedHosts = input.hosts
|
|
59
|
+
const selectedHosts = input.hosts ? [...input.hosts] : [...AGENT_HOSTS];
|
|
58
60
|
const requiredCapabilities = input.requiredCapabilities && input.requiredCapabilities.length > 0
|
|
59
61
|
? [...input.requiredCapabilities]
|
|
60
62
|
: [...REQUIRED_CORE_CAPABILITIES];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* config keys, and generated surfaces it owns while leaving consumer-owned
|
|
7
7
|
* divergent files untouched unless `--overwrite` is passed.
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { basename, dirname, join, relative } from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { isTelemetryEnabled, reportTthw } from '#telemetry/setup-tthw';
|
|
@@ -26,7 +26,7 @@ import { GENERATED_PATHS_BLOCK, patchGitignore } from './gitignore-patcher.js';
|
|
|
26
26
|
import { scaffoldAgentsMd } from './scaffold-agents-md.js';
|
|
27
27
|
import { scaffoldBlueprints } from './scaffold-blueprints.js';
|
|
28
28
|
import { scaffoldDocs } from './scaffold-docs.js';
|
|
29
|
-
import { scaffoldBaseKit } from './scaffold-base-kit.js';
|
|
29
|
+
import { BASE_KIT_QUALITY_TARGETS, collectRuntimeContractGuidance, scaffoldBaseKit, } from './scaffold-base-kit.js';
|
|
30
30
|
import { scaffoldMonorepoNav } from './scaffold-monorepo-nav.js';
|
|
31
31
|
import { REQUIRED_CORE_CAPABILITIES, auditHostSkillVisibility, parseAgentHosts, serializeHostVisibility, summarizeHostVisibility, } from './host-visibility.js';
|
|
32
32
|
import { scaffoldAgentHooks, trustCodexWebpressoHooksForRepo, trustCodexPresetHooksForUser, } from './scaffolders/agent-hooks/index.js';
|
|
@@ -103,6 +103,31 @@ function inferBlueprintsDirOverride(repoRoot, existingConfig) {
|
|
|
103
103
|
}
|
|
104
104
|
return relativePath;
|
|
105
105
|
}
|
|
106
|
+
function readPackageJsonSafe(repoRoot) {
|
|
107
|
+
const packageJsonPath = join(repoRoot, 'package.json');
|
|
108
|
+
if (!existsSync(packageJsonPath)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function printRuntimeContractGuidance(packageJson) {
|
|
119
|
+
const guidance = collectRuntimeContractGuidance(packageJson);
|
|
120
|
+
console.log('\nRuntime-owned tooling contract:');
|
|
121
|
+
console.log(' wp now owns execution for test, e2e, lint, format, and typecheck.');
|
|
122
|
+
console.log(' Keep local dependencies that your tests, configs, or tsconfig types import directly.');
|
|
123
|
+
if (guidance.keepLocalAuthoringDeps.length > 0) {
|
|
124
|
+
console.log(` Keep local authoring deps when imported directly: ${guidance.keepLocalAuthoringDeps.join(', ')}`);
|
|
125
|
+
}
|
|
126
|
+
if (guidance.reviewForRemovalDeps.length > 0) {
|
|
127
|
+
console.log(` Review execution-only deps for removal if they only powered local binaries: ${guidance.reviewForRemovalDeps.join(', ')}`);
|
|
128
|
+
}
|
|
129
|
+
console.log(' Do not blanket-remove devDependencies just because wp can execute the tool.');
|
|
130
|
+
}
|
|
106
131
|
export async function runInit(flags) {
|
|
107
132
|
const startMs = Date.now();
|
|
108
133
|
const cwd = flags.cwd ?? process.cwd();
|
|
@@ -612,6 +637,14 @@ export async function runInit(flags) {
|
|
|
612
637
|
console.log(` drifted: ${summary.drifted}`);
|
|
613
638
|
if (options.dryRun)
|
|
614
639
|
console.log(` would-change: ${summary['skipped-dry']}`);
|
|
640
|
+
if (tier3Selection.includes('base-kit')) {
|
|
641
|
+
const qualityTargets = new Set(BASE_KIT_QUALITY_TARGETS);
|
|
642
|
+
const qualityResults = baseKitResults.filter((result) => qualityTargets.has(relative(consumer.repoRoot, result.targetPath).replaceAll('\\', '/')));
|
|
643
|
+
const qualityCreated = qualityResults.filter((result) => result.action === 'created').length;
|
|
644
|
+
const qualityPreserved = qualityResults.filter((result) => result.action === 'identical').length;
|
|
645
|
+
const qualityDryRun = qualityResults.filter((result) => result.action === 'skipped-dry').length;
|
|
646
|
+
console.log(` repo quality scaffold: ${options.dryRun ? `${qualityDryRun} would be created` : `${qualityCreated} created, ${qualityPreserved} preserved`}`);
|
|
647
|
+
}
|
|
615
648
|
if (summary.drifted > 0) {
|
|
616
649
|
console.log('\n Note: some consumer-owned files exist with different content and were left unchanged.\n' +
|
|
617
650
|
' Review the drift or re-run with `--overwrite` to force eligible managed files.');
|
|
@@ -629,8 +662,13 @@ export async function runInit(flags) {
|
|
|
629
662
|
};
|
|
630
663
|
writeConfig(consumer.repoRoot, config);
|
|
631
664
|
console.log('\nHost skill visibility:');
|
|
632
|
-
|
|
633
|
-
console.log(
|
|
665
|
+
if (visibilityAudit.selectedHosts.length === 0) {
|
|
666
|
+
console.log(' hosts: - skipped (--host none)');
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
for (const line of summarizeHostVisibility(consumer.repoRoot, visibilityAudit)) {
|
|
670
|
+
console.log(line);
|
|
671
|
+
}
|
|
634
672
|
}
|
|
635
673
|
const missing = visibilityAudit.results.filter((result) => result.status === 'not-visible');
|
|
636
674
|
if (missing.length > 0) {
|
|
@@ -659,7 +697,8 @@ export async function runInit(flags) {
|
|
|
659
697
|
}
|
|
660
698
|
}
|
|
661
699
|
}
|
|
662
|
-
|
|
700
|
+
printRuntimeContractGuidance(options.dryRun ? consumer.packageJson : readPackageJsonSafe(consumer.repoRoot) ?? consumer.packageJson);
|
|
701
|
+
console.log('\nwp init: setup phases finished.');
|
|
663
702
|
if (omxFailure === 'not-found')
|
|
664
703
|
return EXIT_SETUP_FAIL;
|
|
665
704
|
if (omxFailure === 'spawn-failed')
|
|
@@ -698,7 +737,7 @@ export async function runInit(flags) {
|
|
|
698
737
|
if (!options.dryRun) {
|
|
699
738
|
console.log([
|
|
700
739
|
'',
|
|
701
|
-
'✅ Setup complete.',
|
|
740
|
+
'✅ Setup complete for the verified phases above.',
|
|
702
741
|
'',
|
|
703
742
|
' Next: wp blueprint new "your first task"',
|
|
704
743
|
' wp gain # token savings after your first session',
|
|
@@ -730,7 +769,7 @@ export function registerInitCommand(cli, commandName = 'init') {
|
|
|
730
769
|
.command(commandName, description)
|
|
731
770
|
.option('--with <skills>', withHelp)
|
|
732
771
|
.option('--without <skills>', withoutHelp)
|
|
733
|
-
.option('--host <hosts>', 'Comma-separated host targets: codex, claude, opencode, all')
|
|
772
|
+
.option('--host <hosts>', 'Comma-separated host targets: codex, claude, opencode, all, none')
|
|
734
773
|
.option('--all', 'Install every skill (Tier-1 + Tier-2 + all Tier-3)')
|
|
735
774
|
.option('--overwrite', 'Force full-file replacement for eligible managed files (default: reconcile owned content and preserve divergent consumer files)')
|
|
736
775
|
.option('--dry-run', 'Show what would change without writing anything')
|
|
@@ -5,5 +5,17 @@ export interface ScaffoldBaseKitInput {
|
|
|
5
5
|
options: MergeOptions;
|
|
6
6
|
globalInstall?: boolean;
|
|
7
7
|
}
|
|
8
|
+
export interface RuntimeContractGuidance {
|
|
9
|
+
keepLocalAuthoringDeps: string[];
|
|
10
|
+
reviewForRemovalDeps: string[];
|
|
11
|
+
}
|
|
12
|
+
interface PackageJsonLike {
|
|
13
|
+
dependencies?: Record<string, string>;
|
|
14
|
+
devDependencies?: Record<string, string>;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
export declare function collectRuntimeContractGuidance(packageJson: PackageJsonLike | null | undefined): RuntimeContractGuidance;
|
|
18
|
+
export declare const BASE_KIT_QUALITY_TARGETS: string[];
|
|
8
19
|
export declare function scaffoldBaseKit(input: ScaffoldBaseKitInput): MergeResult[];
|
|
20
|
+
export {};
|
|
9
21
|
//# sourceMappingURL=scaffold-base-kit.d.ts.map
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { writeFileMerged } from './merge.js';
|
|
4
|
+
const AUTHORING_TIME_DEPENDENCIES = [
|
|
5
|
+
'vitest',
|
|
6
|
+
'@playwright/test',
|
|
7
|
+
'@testing-library/jest-dom',
|
|
8
|
+
'typescript',
|
|
9
|
+
];
|
|
10
|
+
const EXECUTION_ONLY_REVIEW_DEPENDENCIES = [
|
|
11
|
+
'oxlint',
|
|
12
|
+
'oxfmt',
|
|
13
|
+
'prettier',
|
|
14
|
+
'markdownlint-cli2',
|
|
15
|
+
'stryker',
|
|
16
|
+
];
|
|
17
|
+
export function collectRuntimeContractGuidance(packageJson) {
|
|
18
|
+
const deps = {
|
|
19
|
+
...readDependencyBucket(packageJson?.['dependencies']),
|
|
20
|
+
...readDependencyBucket(packageJson?.['devDependencies']),
|
|
21
|
+
};
|
|
22
|
+
const installed = new Set(Object.keys(deps));
|
|
23
|
+
return {
|
|
24
|
+
keepLocalAuthoringDeps: AUTHORING_TIME_DEPENDENCIES.filter((name) => installed.has(name)),
|
|
25
|
+
reviewForRemovalDeps: EXECUTION_ONLY_REVIEW_DEPENDENCIES.filter((name) => installed.has(name)),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
4
28
|
/** Template files relative to `catalog/base-kit/`, and their target paths relative to repoRoot. */
|
|
5
29
|
const TEMPLATE_MAP = [
|
|
6
30
|
['.editorconfig.tmpl', '.editorconfig'],
|
|
@@ -18,6 +42,19 @@ const TEMPLATE_MAP = [
|
|
|
18
42
|
['test/.gitkeep.tmpl', 'test/.gitkeep'],
|
|
19
43
|
['e2e/.gitkeep.tmpl', 'e2e/.gitkeep'],
|
|
20
44
|
];
|
|
45
|
+
/** Consumer-owned quality scaffold: create for fresh repos, never clobber. */
|
|
46
|
+
const QUALITY_BOOTSTRAP_ONLY_MAP = [
|
|
47
|
+
['tsconfig.json.tmpl', 'tsconfig.json'],
|
|
48
|
+
['vitest.config.ts.tmpl', 'vitest.config.ts'],
|
|
49
|
+
['oxlint.config.ts.tmpl', 'oxlint.config.ts'],
|
|
50
|
+
['stryker.config.ts.tmpl', 'stryker.config.ts'],
|
|
51
|
+
['playwright.config.ts.tmpl', 'playwright.config.ts'],
|
|
52
|
+
['src/quality-sample.ts.tmpl', 'src/quality-sample.ts'],
|
|
53
|
+
['src/quality-sample.test.ts.tmpl', 'src/quality-sample.test.ts'],
|
|
54
|
+
['e2e/fixtures/smoke.html.tmpl', 'e2e/fixtures/smoke.html'],
|
|
55
|
+
['e2e/smoke.spec.ts.tmpl', 'e2e/smoke.spec.ts'],
|
|
56
|
+
];
|
|
57
|
+
export const BASE_KIT_QUALITY_TARGETS = QUALITY_BOOTSTRAP_ONLY_MAP.map(([, targetRel]) => targetRel);
|
|
21
58
|
/**
|
|
22
59
|
* Bootstrap-only templates: the scaffolder writes them when absent (so a
|
|
23
60
|
* fresh repo gets sane defaults) but NEVER overwrites them once they exist
|
|
@@ -75,35 +112,85 @@ function mergePackageJson(repoRoot, options, globalInstall = false) {
|
|
|
75
112
|
const hasVerifySecrets = typeof scripts['verify:secrets'] === 'string';
|
|
76
113
|
const hasSecretQuarantineAudit = typeof scripts['audit:secret-provider-quarantine'] === 'string';
|
|
77
114
|
const hasPrepareScript = typeof scripts['prepare'] === 'string';
|
|
115
|
+
const hasLintScript = typeof scripts['lint'] === 'string';
|
|
116
|
+
const hasTypecheckScript = typeof scripts['typecheck'] === 'string';
|
|
117
|
+
const hasTestScript = typeof scripts['test'] === 'string' && !isNpmInitPlaceholderTestScript(scripts['test']);
|
|
118
|
+
const hasMutationScript = typeof scripts['mutation'] === 'string';
|
|
119
|
+
const hasTestMutationScript = typeof scripts['test:mutation'] === 'string';
|
|
120
|
+
const hasE2eScript = typeof scripts['e2e'] === 'string';
|
|
121
|
+
const hasQaScript = typeof scripts['qa'] === 'string';
|
|
78
122
|
const verifyPathsScript = 'WP_SKIP_UPDATE_CHECK=1 wp audit absolute-path-policy --root .';
|
|
79
123
|
const verifySecretsScript = 'bun scripts/check-no-dev-vars.ts';
|
|
80
124
|
const secretQuarantineAuditScript = 'bun scripts/audit-secret-provider-quarantine.ts';
|
|
125
|
+
const lintScript = 'wp lint src e2e *.config.ts';
|
|
126
|
+
const typecheckScript = 'wp typecheck';
|
|
127
|
+
const testScript = 'wp test --file vitest.config.ts';
|
|
128
|
+
const mutationScript = 'wp test --mutation';
|
|
129
|
+
const testMutationScript = 'stryker run stryker.config.ts';
|
|
130
|
+
const e2eScript = 'wp e2e --config playwright.config.ts';
|
|
131
|
+
const qaScript = [
|
|
132
|
+
'wp lint src e2e *.config.ts',
|
|
133
|
+
'wp typecheck',
|
|
134
|
+
'wp test --file vitest.config.ts',
|
|
135
|
+
'wp test --mutation',
|
|
136
|
+
'wp e2e --config playwright.config.ts',
|
|
137
|
+
].join(' && ');
|
|
81
138
|
const devDeps = (pkg['devDependencies'] ?? {});
|
|
82
|
-
const hasAgentKitDevDep = typeof devDeps['webpresso'] === 'string';
|
|
83
|
-
const
|
|
139
|
+
const hasAgentKitDevDep = typeof devDeps['@webpresso/agent-kit'] === 'string';
|
|
140
|
+
const hasLegacyAgentKitDevDep = typeof devDeps['webpresso'] === 'string';
|
|
141
|
+
const shouldSkipSelfInstall = packageName === '@webpresso/agent-kit' || packageName === 'webpresso';
|
|
84
142
|
const shouldManageAgentKitAsGlobal = globalInstall && !shouldSkipSelfInstall;
|
|
143
|
+
const requiredAuthoringDeps = {
|
|
144
|
+
'@playwright/test': 'latest',
|
|
145
|
+
'@stryker-mutator/core': 'latest',
|
|
146
|
+
'@stryker-mutator/vitest-runner': 'latest',
|
|
147
|
+
'@types/node': 'latest',
|
|
148
|
+
typescript: 'latest',
|
|
149
|
+
vitest: 'latest',
|
|
150
|
+
};
|
|
85
151
|
if (alreadyHasEngines &&
|
|
86
152
|
alreadyHasPm &&
|
|
87
|
-
(shouldSkipSelfInstall ||
|
|
153
|
+
(shouldSkipSelfInstall ||
|
|
154
|
+
shouldManageAgentKitAsGlobal ||
|
|
155
|
+
hasAgentKitDevDep ||
|
|
156
|
+
hasLegacyAgentKitDevDep) &&
|
|
157
|
+
Object.keys(requiredAuthoringDeps).every((name) => typeof devDeps[name] === 'string') &&
|
|
88
158
|
(shouldSkipSelfInstall || hasSetupAgent) &&
|
|
89
159
|
(shouldSkipSelfInstall || hasVerifyPaths) &&
|
|
90
160
|
(shouldSkipSelfInstall || hasVerifySecrets) &&
|
|
91
161
|
(shouldSkipSelfInstall || hasSecretQuarantineAudit) &&
|
|
92
|
-
(shouldSkipSelfInstall || hasPrepareScript)
|
|
162
|
+
(shouldSkipSelfInstall || hasPrepareScript) &&
|
|
163
|
+
hasLintScript &&
|
|
164
|
+
hasTypecheckScript &&
|
|
165
|
+
hasTestScript &&
|
|
166
|
+
hasMutationScript &&
|
|
167
|
+
hasTestMutationScript &&
|
|
168
|
+
hasE2eScript &&
|
|
169
|
+
hasQaScript) {
|
|
93
170
|
return { targetPath: pkgPath, action: 'identical' };
|
|
94
171
|
}
|
|
95
172
|
pkg['engines'] = { ...existing, node: engines.node };
|
|
96
173
|
if (!alreadyHasPm)
|
|
97
174
|
pkg['packageManager'] = packageManager;
|
|
175
|
+
if (typeof pkg['type'] !== 'string')
|
|
176
|
+
pkg['type'] = 'module';
|
|
98
177
|
// Ensure husky is in devDependencies so `vp exec husky init` works
|
|
99
178
|
if (!devDeps['husky']) {
|
|
100
179
|
devDeps['husky'] = '^9.0.0';
|
|
101
180
|
}
|
|
102
|
-
if (!shouldSkipSelfInstall &&
|
|
181
|
+
if (!shouldSkipSelfInstall &&
|
|
182
|
+
!shouldManageAgentKitAsGlobal &&
|
|
183
|
+
!hasAgentKitDevDep &&
|
|
184
|
+
!hasLegacyAgentKitDevDep) {
|
|
103
185
|
// Keep consumers on the currently published dist-tag rather than a
|
|
104
186
|
// repo-internal path. Do not wire this through `prepare`: `wp` is not
|
|
105
187
|
// reliably on PATH during `vp install`, so `setup:agent` stays opt-in.
|
|
106
|
-
devDeps['webpresso'] = 'latest';
|
|
188
|
+
devDeps['@webpresso/agent-kit'] = 'latest';
|
|
189
|
+
}
|
|
190
|
+
for (const [name, version] of Object.entries(requiredAuthoringDeps)) {
|
|
191
|
+
if (!devDeps[name]) {
|
|
192
|
+
devDeps[name] = version;
|
|
193
|
+
}
|
|
107
194
|
}
|
|
108
195
|
pkg['devDependencies'] = devDeps;
|
|
109
196
|
if (!shouldSkipSelfInstall && !hasSetupAgent) {
|
|
@@ -121,6 +208,27 @@ function mergePackageJson(repoRoot, options, globalInstall = false) {
|
|
|
121
208
|
if (!shouldSkipSelfInstall && !hasPrepareScript) {
|
|
122
209
|
scripts['prepare'] = 'husky';
|
|
123
210
|
}
|
|
211
|
+
if (!hasLintScript) {
|
|
212
|
+
scripts['lint'] = lintScript;
|
|
213
|
+
}
|
|
214
|
+
if (!hasTypecheckScript) {
|
|
215
|
+
scripts['typecheck'] = typecheckScript;
|
|
216
|
+
}
|
|
217
|
+
if (!hasTestScript) {
|
|
218
|
+
scripts['test'] = testScript;
|
|
219
|
+
}
|
|
220
|
+
if (!hasMutationScript) {
|
|
221
|
+
scripts['mutation'] = mutationScript;
|
|
222
|
+
}
|
|
223
|
+
if (!hasTestMutationScript) {
|
|
224
|
+
scripts['test:mutation'] = testMutationScript;
|
|
225
|
+
}
|
|
226
|
+
if (!hasE2eScript) {
|
|
227
|
+
scripts['e2e'] = e2eScript;
|
|
228
|
+
}
|
|
229
|
+
if (!hasQaScript) {
|
|
230
|
+
scripts['qa'] = qaScript;
|
|
231
|
+
}
|
|
124
232
|
if (Object.keys(scripts).length > 0) {
|
|
125
233
|
pkg['scripts'] = scripts;
|
|
126
234
|
}
|
|
@@ -161,6 +269,24 @@ export function scaffoldBaseKit(input) {
|
|
|
161
269
|
writeFileSync(targetPath, content);
|
|
162
270
|
results.push({ targetPath, action: 'created' });
|
|
163
271
|
}
|
|
272
|
+
for (const [tmplRel, targetRel] of QUALITY_BOOTSTRAP_ONLY_MAP) {
|
|
273
|
+
const tmplPath = join(baseKitDir, tmplRel);
|
|
274
|
+
if (!existsSync(tmplPath))
|
|
275
|
+
continue;
|
|
276
|
+
const targetPath = join(repoRoot, targetRel);
|
|
277
|
+
if (existsSync(targetPath)) {
|
|
278
|
+
results.push({ targetPath, action: 'identical' });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const content = readFileSync(tmplPath, 'utf8');
|
|
282
|
+
if (options.dryRun) {
|
|
283
|
+
results.push({ targetPath, action: 'skipped-dry' });
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
287
|
+
writeFileSync(targetPath, content);
|
|
288
|
+
results.push({ targetPath, action: 'created' });
|
|
289
|
+
}
|
|
164
290
|
// Make husky hook files executable
|
|
165
291
|
if (!options.dryRun) {
|
|
166
292
|
for (const [tmplRel, targetRel] of TEMPLATE_MAP) {
|
|
@@ -180,4 +306,13 @@ export function scaffoldBaseKit(input) {
|
|
|
180
306
|
results.push(mergePackageJson(repoRoot, options, globalInstall));
|
|
181
307
|
return results;
|
|
182
308
|
}
|
|
309
|
+
function readDependencyBucket(value) {
|
|
310
|
+
if (!value || typeof value !== 'object') {
|
|
311
|
+
return {};
|
|
312
|
+
}
|
|
313
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[0] === 'string' && typeof entry[1] === 'string'));
|
|
314
|
+
}
|
|
315
|
+
function isNpmInitPlaceholderTestScript(value) {
|
|
316
|
+
return /^echo ['"]?Error: no test specified['"]? && exit 1$/u.test(value.trim());
|
|
317
|
+
}
|
|
183
318
|
//# sourceMappingURL=scaffold-base-kit.js.map
|