@monte3l/groundwork 1.0.0-rc.2 → 1.0.0-rc.3
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/README.md +13 -5
- package/bin/m3l-groundwork.mjs +36 -2
- package/dist/assets.d.ts +10 -0
- package/dist/assets.js +14 -0
- package/dist/baseline-stage.d.ts +173 -0
- package/dist/baseline-stage.js +215 -0
- package/dist/caps.d.ts +4 -1
- package/dist/caps.js +17 -3
- package/dist/conflicts.d.ts +23 -2
- package/dist/conflicts.js +89 -11
- package/dist/customize-paths.d.ts +92 -0
- package/dist/customize-paths.js +115 -0
- package/dist/emit.d.ts +50 -1
- package/dist/emit.js +121 -13
- package/dist/fatal.d.ts +70 -0
- package/dist/fatal.js +132 -0
- package/dist/format-error.d.ts +113 -0
- package/dist/format-error.js +553 -0
- package/dist/fs-guard.d.ts +142 -0
- package/dist/fs-guard.js +222 -0
- package/dist/git.js +10 -1
- package/dist/harness/conformance.js +2 -0
- package/dist/harness/frontmatter.js +2 -13
- package/dist/harness/grade.js +37 -8
- package/dist/harness/rules.d.ts +20 -3
- package/dist/harness/rules.js +108 -6
- package/dist/harness/types.d.ts +13 -0
- package/dist/harness/types.js +3 -0
- package/dist/inventory.d.ts +77 -3
- package/dist/inventory.js +103 -12
- package/dist/jsonc.d.ts +49 -2
- package/dist/jsonc.js +140 -7
- package/dist/main.d.ts +62 -3
- package/dist/main.js +538 -67
- package/dist/merge-json.d.ts +47 -4
- package/dist/merge-json.js +120 -8
- package/dist/mode.js +12 -2
- package/dist/pack-stage.d.ts +210 -0
- package/dist/pack-stage.js +287 -0
- package/dist/packs.d.ts +19 -13
- package/dist/packs.js +231 -28
- package/dist/palette.d.ts +23 -0
- package/dist/palette.js +22 -0
- package/dist/plugin.d.ts +122 -6
- package/dist/plugin.js +687 -47
- package/dist/report.d.ts +21 -2
- package/dist/report.js +93 -5
- package/dist/staging.d.ts +176 -0
- package/dist/staging.js +375 -0
- package/dist/survey/fs-walk.d.ts +34 -2
- package/dist/survey/fs-walk.js +70 -5
- package/dist/survey/internal/blocked-path.d.ts +27 -0
- package/dist/survey/internal/blocked-path.js +129 -0
- package/dist/survey/internal/package-json.d.ts +13 -0
- package/dist/survey/internal/package-json.js +37 -0
- package/dist/survey/internal/read-guard.d.ts +178 -0
- package/dist/survey/internal/read-guard.js +276 -0
- package/dist/survey/survey-docs.d.ts +17 -2
- package/dist/survey/survey-docs.js +61 -21
- package/dist/survey/survey-harness.d.ts +20 -2
- package/dist/survey/survey-harness.js +87 -46
- package/dist/survey/survey-shape.d.ts +17 -2
- package/dist/survey/survey-shape.js +60 -46
- package/dist/survey/survey-toolchain.d.ts +16 -1
- package/dist/survey/survey-toolchain.js +69 -66
- package/dist/survey/survey.js +6 -4
- package/dist/survey/types.d.ts +79 -0
- package/dist/survey/types.js +2 -6
- package/dist/term.d.ts +80 -0
- package/dist/term.js +145 -0
- package/dist/tokens.js +2 -0
- package/dist/toolchain/conformance.js +2 -0
- package/dist/toolchain/grade.js +26 -8
- package/dist/toolchain/rules.d.ts +13 -4
- package/dist/toolchain/rules.js +16 -0
- package/dist/toolchain/tsconfig-chain.d.ts +2 -0
- package/dist/toolchain/tsconfig-chain.js +32 -8
- package/dist/toolchain/types.d.ts +16 -4
- package/dist/toolchain/types.js +3 -0
- package/package.json +4 -3
- package/plugin/skills/customize/SKILL.md +292 -18
- package/plugin/src/domain-map.ts +39 -14
- package/plugin/src/index.ts +5 -1
- package/plugin/src/kind-facet-map.ts +25 -8
- package/plugin/src/pack-map.ts +147 -25
- package/plugin/src/plugin-map.ts +236 -0
- package/templates/core/.claude/agents/Explore.md +0 -1
- package/templates/core/.claude/agents/code-implementer.md +4 -4
- package/templates/core/.claude/agents/code-reviewer.md +4 -4
- package/templates/core/.claude/agents/silent-failure-hunter.md +2 -2
- package/templates/core/.claude/agents/test-author.md +7 -5
- package/templates/core/.claude/hooks/guard-branch-isolation.mjs +56 -10
- package/templates/core/.claude/hooks/guard-double-background.mjs +13 -8
- package/templates/core/.claude/hooks/guard-git-push-signed.mjs +12 -9
- package/templates/core/.claude/hooks/guard-hub-src-writes.mjs +1890 -38
- package/templates/core/.claude/hooks/guard-js-extension.mjs +2 -1
- package/templates/core/.claude/hooks/guard-no-commonjs.mjs +7 -5
- package/templates/core/.claude/hooks/guard-secret-writes.mjs +7 -5
- package/templates/core/.claude/hooks/inject-decision-gate.mjs +12 -9
- package/templates/core/.claude/hooks/post-edit-verify.mjs +276 -98
- package/templates/core/.claude/rules/agent-dispatch.md +7 -0
- package/templates/core/.claude/rules/tests.md +2 -2
- package/templates/core/.claude/settings.json +5 -0
- package/templates/core/.claude/skills/finishing-work/SKILL.md +58 -10
- package/templates/core/.claude/skills/harness-guidance/SKILL.md +16 -7
- package/templates/core/.claude/skills/starting-work/SKILL.md +5 -0
- package/templates/core/.claude/skills/triaging-ci/SKILL.md +9 -8
- package/templates/core/.claude/skills/typescript-guidance/SKILL.md +137 -20
- package/templates/core/.claude/skills/typescript-guidance/references/area-catalog.md +135 -0
- package/templates/core/.claude/skills/typescript-guidance/references/tooling-sources.md +113 -0
- package/templates/core/.claude/skills/writing-commits/SKILL.md +2 -2
- package/templates/core/.github/dependabot.yml +18 -0
- package/templates/core/.github/workflows/ci.yml +15 -15
- package/templates/core/.github/workflows/dependency-review.yml +2 -2
- package/templates/core/.github/workflows/security-audit.yml +10 -4
- package/templates/core/.prettierignore +4 -0
- package/templates/core/CLAUDE.md +53 -3
- package/templates/core/README.md +30 -10
- package/templates/core/_gitignore +17 -0
- package/templates/core/bin/check-exports.mjs +11 -5
- package/templates/core/bin/lib/agent-roster.mjs +1 -1
- package/templates/core/bin/lib/frontmatter.mjs +4 -2
- package/templates/core/bin/lib/harness-rules.mjs +169 -20
- package/templates/core/bin/lib/protected-paths.mjs +93 -5
- package/templates/core/bin/lib/toolchain-rules.mjs +187 -26
- package/templates/core/bin/lib/verify-steps.mjs +29 -11
- package/templates/core/bin/verify.mjs +12 -0
- package/templates/core/eslint.config.js +5 -0
- package/templates/core/package.json +7 -7
- package/templates/core/vitest.config.ts +8 -1
- package/templates/packs/README.md +35 -24
- package/templates/packs/github/files/.claude/skills/reviewing-dependabot-prs/SKILL.md +133 -0
- package/templates/packs/github/files/.claude/skills/triaging-scan-alerts/SKILL.md +88 -0
- package/templates/packs/github/files/.claude/skills/watching-pr-checks/SKILL.md +94 -0
- package/templates/packs/github/files/.github/workflows/claude-pr-review.yml +267 -0
- package/templates/packs/github/files/.github/workflows/claude.yml +106 -0
- package/templates/packs/github/pack.json +19 -0
- package/templates/packs/harness-extras/files/.claude/hooks/guard-readonly-bash.mjs +1122 -65
- package/templates/packs/harness-extras/files/.claude/hooks/reinject-compact-handoff.mjs +147 -13
- package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/statusline.mjs +6 -2
- package/templates/packs/harness-extras/files/.claude/hooks/write-compact-handoff.mjs +227 -44
- package/templates/packs/harness-extras/pack.json +18 -13
- package/templates/packs/publishing/files/.changeset/README.md +25 -0
- package/templates/packs/publishing/files/.changeset/config.json +7 -0
- package/templates/packs/publishing/files/.github/release-tools/package.json +9 -0
- package/templates/packs/publishing/files/.github/workflows/release.yml +293 -0
- package/templates/packs/publishing/files/REUSE.toml +28 -0
- package/templates/packs/publishing/files/bin/check-dts-deps.mjs +234 -0
- package/templates/packs/publishing/files/bin/check-license-headers.mjs +217 -0
- package/templates/packs/publishing/files/bin/check-publish-version.mjs +149 -0
- package/templates/packs/publishing/files/bin/lib/npm-publish-args.mjs +86 -0
- package/templates/packs/publishing/files/bin/pnpm-publish-shim.mjs +86 -0
- package/templates/packs/publishing/pack.json +35 -0
- package/templates/packs/{harness-extras → quality}/files/bin/check-file-budget.mjs +6 -4
- package/templates/packs/quality/pack.json +29 -0
- package/templates/packs/supply-chain/files/.github/workflows/gitleaks.yml +52 -0
- package/templates/packs/supply-chain/files/.github/workflows/scorecard.yml +48 -0
- package/templates/packs/supply-chain/files/.gitleaks.toml +2 -0
- package/templates/packs/supply-chain/pack.json +19 -0
- package/templates/packs/worktrees/files/.claude/hooks/ensure-worktree-deps.mjs +283 -0
- package/templates/packs/worktrees/files/.claude/hooks/guard-worktree-only.mjs +245 -0
- package/templates/packs/worktrees/files/.claude/hooks/repair-core-bare.mjs +335 -0
- package/templates/packs/worktrees/files/.claude/skills/working-in-worktrees/SKILL.md +139 -0
- package/templates/packs/worktrees/files/.worktreeinclude +11 -0
- package/templates/packs/worktrees/pack.json +64 -0
- package/templates/packs/statusline/pack.json +0 -31
- /package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/statusline-layout.mjs +0 -0
- /package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/subagent-statusline.mjs +0 -0
- /package/templates/packs/{harness-extras → quality}/files/.claude/agents/type-design-analyzer.md +0 -0
- /package/templates/packs/{harness-extras → quality}/files/bin/file-budget-baseline.json +0 -0
package/README.md
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
# @monte3l/groundwork
|
|
2
2
|
|
|
3
|
+
**What this is:** a command-line tool that sets up a TypeScript project's
|
|
4
|
+
toolchain and Claude Code configuration, or reports on how an existing
|
|
5
|
+
project compares to that setup.
|
|
6
|
+
|
|
3
7
|
The offline bootstrapper behind [m3l-groundwork](https://github.com/monte3l/m3l-groundwork):
|
|
4
8
|
one deterministic CLI that either writes a baseline TypeScript toolchain and
|
|
5
|
-
Claude Code harness
|
|
6
|
-
|
|
7
|
-
project file
|
|
9
|
+
Claude Code **harness** (its agents, skills, hooks, and settings) into an
|
|
10
|
+
empty directory (**fresh** mode) or surveys an existing project and writes
|
|
11
|
+
only a report (**adopt mode**, which never touches a project file --
|
|
12
|
+
it's for a project that already exists and shouldn't be rewritten
|
|
13
|
+
automatically).
|
|
8
14
|
|
|
9
15
|
```bash
|
|
10
16
|
# currently a 1.0.0 release candidate, shipping on the `rc` dist-tag
|
|
11
17
|
npx @monte3l/groundwork@rc my-new-project
|
|
12
|
-
npx @monte3l/groundwork@rc my-new-project --pack
|
|
18
|
+
npx @monte3l/groundwork@rc my-new-project --pack harness-extras
|
|
13
19
|
npx @monte3l/groundwork@rc ../existing-project
|
|
14
20
|
```
|
|
15
21
|
|
|
@@ -18,6 +24,8 @@ fresh bootstrap ends with (`--skip-install` to skip it). Run with `--help` for
|
|
|
18
24
|
every flag, or `--list-packs` for the optional packs.
|
|
19
25
|
|
|
20
26
|
The `/customize` skill it installs, the packs, and the design are documented in
|
|
21
|
-
the [repository README](https://github.com/monte3l/m3l-groundwork
|
|
27
|
+
the [repository README](https://github.com/monte3l/m3l-groundwork/blob/main/README.md).
|
|
28
|
+
Unfamiliar terms (harness, adopt mode, pack, and the rest) are defined in the
|
|
29
|
+
[glossary](https://github.com/monte3l/m3l-groundwork/blob/main/docs/glossary.md).
|
|
22
30
|
|
|
23
31
|
MIT licensed.
|
package/bin/m3l-groundwork.mjs
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The published CLI's entry point: `npx @monte3l/groundwork <target-dir>`
|
|
7
|
+
* resolves here. Delegates straight to the built `main()` and turns a
|
|
8
|
+
* `CliUsageError` into exit code 2 (a bad invocation), any other thrown
|
|
9
|
+
* error into exit code 1 (a runtime failure) -- see `packages/cli/src/main.ts`.
|
|
10
|
+
* The error is printed with its full `cause` chain (`formatErrorChain`), so
|
|
11
|
+
* a wrapped failure never hides the underlying reason; `handleFatal` sets the
|
|
12
|
+
* exit code before printing and never throws, even when reporting fails --
|
|
13
|
+
* if the painted print throws, it falls back to a plain, unpainted
|
|
14
|
+
* `console.error` (`printRaw`). Not `process.stderr.write`: a write to a
|
|
15
|
+
* closed pipe reports EPIPE asynchronously as an `'error'` event on
|
|
16
|
+
* `process.stderr`, which nothing here listens for, so it would crash the
|
|
17
|
+
* process as an uncaught exception and turn exit code 2 into 1 after
|
|
18
|
+
* `handleFatal` returned; `console.error` swallows stream errors itself.
|
|
19
|
+
*/
|
|
2
20
|
import process from "node:process";
|
|
3
21
|
import { main, CliUsageError } from "../dist/main.js";
|
|
22
|
+
import { handleFatal } from "../dist/fatal.js";
|
|
23
|
+
import { paint } from "../dist/term.js";
|
|
4
24
|
|
|
5
25
|
try {
|
|
6
26
|
main(process.argv.slice(2));
|
|
7
27
|
} catch (error) {
|
|
8
|
-
|
|
9
|
-
|
|
28
|
+
handleFatal(
|
|
29
|
+
error,
|
|
30
|
+
{
|
|
31
|
+
setExitCode: (code) => {
|
|
32
|
+
process.exitCode = code;
|
|
33
|
+
},
|
|
34
|
+
print: (text) => {
|
|
35
|
+
console.error(paint(process.stderr, "danger", text));
|
|
36
|
+
},
|
|
37
|
+
printRaw: (text) => {
|
|
38
|
+
console.error(text);
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
(e) => e instanceof CliUsageError,
|
|
42
|
+
(process.env["M3L_DEBUG"] ?? "") !== "",
|
|
43
|
+
);
|
|
10
44
|
}
|
package/dist/assets.d.ts
CHANGED
|
@@ -11,6 +11,16 @@ export declare function escapeDotfileName(name: string): string;
|
|
|
11
11
|
export declare function restoreDotfileName(name: string): string;
|
|
12
12
|
/** `restoreDotfileName` applied to the final segment of a `/`- or `\`-separated relative path. */
|
|
13
13
|
export declare function restoreDotfilePath(relPath: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Normalizes a native relative path to forward slashes, so a path recorded
|
|
16
|
+
* in `inventory.json` reads the same whichever OS ran the CLI.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* toPosixPath("src\\index.ts"); // "src/index.ts"
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function toPosixPath(p: string): string;
|
|
14
24
|
/**
|
|
15
25
|
* Resolves an asset for whichever layout is running. `fromDir` is the
|
|
16
26
|
* directory of a module at `src/` or `dist/` depth; it defaults to this
|
package/dist/assets.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
1
3
|
/**
|
|
2
4
|
* The one place that decides where the CLI's data trees live: `templates/`
|
|
3
5
|
* (the baseline and its packs) and the `/customize` plugin payload.
|
|
@@ -43,6 +45,18 @@ export function restoreDotfilePath(relPath) {
|
|
|
43
45
|
const cut = Math.max(relPath.lastIndexOf("/"), relPath.lastIndexOf("\\"));
|
|
44
46
|
return relPath.slice(0, cut + 1) + restoreDotfileName(relPath.slice(cut + 1));
|
|
45
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Normalizes a native relative path to forward slashes, so a path recorded
|
|
50
|
+
* in `inventory.json` reads the same whichever OS ran the CLI.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* toPosixPath("src\\index.ts"); // "src/index.ts"
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function toPosixPath(p) {
|
|
58
|
+
return p.replaceAll("\\", "/");
|
|
59
|
+
}
|
|
46
60
|
/** True when `dir` is this project's own source checkout, by two independent markers. */
|
|
47
61
|
function isSourceCheckout(dir) {
|
|
48
62
|
if (!existsSync(join(dir, "pnpm-workspace.yaml"))) {
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { toPosixPath } from "./assets.js";
|
|
2
|
+
import type { FileConflict } from "./conflicts.js";
|
|
3
|
+
import { STAGED_SUFFIX, stagedNameFor } from "./staging.js";
|
|
4
|
+
import type { TokenTable } from "./tokens.js";
|
|
5
|
+
export { STAGED_SUFFIX, stagedNameFor, toPosixPath };
|
|
6
|
+
/**
|
|
7
|
+
* The directory name, under `.groundwork/`, that absent baseline files are
|
|
8
|
+
* staged into.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { join } from "node:path";
|
|
13
|
+
* const stagedDir = join(".groundwork", STAGED_BASELINE_DIR); // ".groundwork/baseline"
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare const STAGED_BASELINE_DIR = "baseline";
|
|
17
|
+
/**
|
|
18
|
+
* One staged baseline file: its install path, its staged name, and the
|
|
19
|
+
* sha256 of the staged bytes, so `/customize` can verify the copy it installs.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const file: StagedBaselineFile = {
|
|
24
|
+
* path: "eslint.config.js",
|
|
25
|
+
* staged: "eslint.config.js.staged",
|
|
26
|
+
* sha256: "e3b0c442...", // hex digest of the staged bytes
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export interface StagedBaselineFile {
|
|
31
|
+
/** The project-relative path the file installs to. */
|
|
32
|
+
path: string;
|
|
33
|
+
/** The staged file's name, relative to the staging directory: `path` + {@link STAGED_SUFFIX}. */
|
|
34
|
+
staged: string;
|
|
35
|
+
/** Lowercase hex sha256 of the staged bytes. */
|
|
36
|
+
sha256: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The validated plan for one {@link stageBaselineAdditions} run: every path
|
|
40
|
+
* it would write, plus each file's install path and template source, so
|
|
41
|
+
* staging never re-walks the template tree. Built by
|
|
42
|
+
* {@link planBaselineStaging}.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { planBaselineStaging, stageBaselineAdditions } from "./baseline-stage.js";
|
|
47
|
+
* const plan = planBaselineStaging(templateRoot, conflicts, groundworkDir, tokens);
|
|
48
|
+
* stageBaselineAdditions(templateRoot, conflicts, groundworkDir, tokens, plan);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export interface BaselineStagingPlan {
|
|
52
|
+
/** The `groundworkDir` the plan was computed for; {@link stageBaselineAdditions} refuses the plan for any other. */
|
|
53
|
+
readonly groundworkDir: string;
|
|
54
|
+
/** Every path the run writes: one `<groundworkDir>/baseline/<path>.staged` per absent conflict, in `conflicts` order. */
|
|
55
|
+
readonly paths: readonly string[];
|
|
56
|
+
/** Each absent conflict's install path and template source, in the same order. */
|
|
57
|
+
readonly files: readonly {
|
|
58
|
+
readonly path: string;
|
|
59
|
+
readonly sourcePath: string;
|
|
60
|
+
}[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validates the absent conflicts in `conflicts` against `templateRoot` and
|
|
64
|
+
* computes the plan {@link stageBaselineAdditions} writes from: every path
|
|
65
|
+
* under `<groundworkDir>/baseline/` -- one `<path>.staged` per absent
|
|
66
|
+
* conflict -- and each file's template source, so adopt mode can
|
|
67
|
+
* scope-check `paths` before anything under `.groundwork/` is deleted or
|
|
68
|
+
* written and then hand the same plan to {@link stageBaselineAdditions}.
|
|
69
|
+
* Reads the template tree (not at all when nothing is absent); writes
|
|
70
|
+
* nothing. Both arrays are empty when nothing is absent.
|
|
71
|
+
*
|
|
72
|
+
* @throws The same plan `Error`s as {@link stageBaselineAdditions}: a
|
|
73
|
+
* template with two files installing to one path, an absent conflict with no
|
|
74
|
+
* template counterpart or whose staged name would escape the staging
|
|
75
|
+
* directory, or two colliding staged names.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { assertAdoptWriteScope } from "./main.js";
|
|
80
|
+
* const plan = planBaselineStaging(templateRoot, conflicts, groundworkDir, tokens);
|
|
81
|
+
* assertAdoptWriteScope(targetDir, plan.paths);
|
|
82
|
+
* stageBaselineAdditions(templateRoot, conflicts, groundworkDir, tokens, plan);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function planBaselineStaging(templateRoot: string, conflicts: readonly FileConflict[], groundworkDir: string, tokens: TokenTable): BaselineStagingPlan;
|
|
86
|
+
/**
|
|
87
|
+
* Every path {@link stageBaselineAdditions} would write for `conflicts` -- a
|
|
88
|
+
* thin wrapper returning {@link planBaselineStaging}'s `paths`. Reads the
|
|
89
|
+
* template tree; writes nothing. Returns `[]` when nothing is absent.
|
|
90
|
+
*
|
|
91
|
+
* @throws The same plan `Error`s as {@link planBaselineStaging}.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { assertAdoptWriteScope } from "./main.js";
|
|
96
|
+
* const paths = plannedBaselineStagingPaths(templateRoot, conflicts, groundworkDir, tokens);
|
|
97
|
+
* assertAdoptWriteScope(targetDir, paths);
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare function plannedBaselineStagingPaths(templateRoot: string, conflicts: readonly FileConflict[], groundworkDir: string, tokens: TokenTable): string[];
|
|
101
|
+
/**
|
|
102
|
+
* Copies every template file `conflicts` marks "absent" from `templateRoot`
|
|
103
|
+
* into `<groundworkDir>/baseline/` as `<path>.staged`, byte-for-byte -- no
|
|
104
|
+
* token substitution into content, which is `/customize`'s job at install
|
|
105
|
+
* time. `tokens` is used only to match a tokenized template path
|
|
106
|
+
* (`__PROJECT_NAME__.txt`) to the substituted `relPath` the conflict plan
|
|
107
|
+
* reports. Recorded `path`/`staged` values use forward slashes
|
|
108
|
+
* ({@link toPosixPath}).
|
|
109
|
+
*
|
|
110
|
+
* What is guaranteed:
|
|
111
|
+
* - The plan is validated first, before anything is deleted or written (or,
|
|
112
|
+
* when `plan` is passed, was already validated by
|
|
113
|
+
* {@link planBaselineStaging} and the template tree is not walked again): a
|
|
114
|
+
* template with two files installing to the same path (a dotfile-escaped
|
|
115
|
+
* name beside its literal twin), an absent conflict with no template
|
|
116
|
+
* counterpart or whose staged name would escape the staging directory, or
|
|
117
|
+
* two staged names that would land on the same file (equal once
|
|
118
|
+
* NFC-normalized and case-folded, or one a directory prefix of the other)
|
|
119
|
+
* throws its own
|
|
120
|
+
* `Error`, leaving `.groundwork/` exactly as it was.
|
|
121
|
+
* - Then, still before anything is deleted or written, `groundworkDir` and
|
|
122
|
+
* `<groundworkDir>/baseline` are checked not to be symlinks, and every
|
|
123
|
+
* `.baseline-*` entry of the CLI-owned `groundworkDir` -- meant for work
|
|
124
|
+
* directories a crashed earlier run left, but removed whatever created
|
|
125
|
+
* it, so a concurrent run against the same directory can lose its
|
|
126
|
+
* in-progress work directory and fail with the incomplete/re-run error
|
|
127
|
+
* -- is removed (best effort; a failure to remove one only warns, a
|
|
128
|
+
* failure to list `groundworkDir` throws).
|
|
129
|
+
* - When nothing is absent, any previous staging is removed and nothing is
|
|
130
|
+
* created.
|
|
131
|
+
* - Files are then written into a temporary `.baseline-*` sibling directory
|
|
132
|
+
* and swapped in by rename only after every copy succeeded, so
|
|
133
|
+
* `baseline/` is never half-written and a copy failure leaves any previous
|
|
134
|
+
* `baseline/` intact. A previous staging is replaced wholesale (no stale
|
|
135
|
+
* file lingers).
|
|
136
|
+
* - With a previous `baseline/`, the swap is two renames: the previous one
|
|
137
|
+
* is parked inside the temporary directory, then the new one moved into
|
|
138
|
+
* place. A process killed between the two leaves `baseline/` absent and
|
|
139
|
+
* the previous copy at `.baseline-XXXXXX/previous`; the next run's sweep
|
|
140
|
+
* removes it and regenerates the staging.
|
|
141
|
+
* - If the final swap rename fails, the previous `baseline/` is renamed back
|
|
142
|
+
* into place. Only if that restore also fails is `baseline/` left absent:
|
|
143
|
+
* the previous staging then survives, parked inside the temporary
|
|
144
|
+
* directory, which is deliberately not removed (a later run's stale-dir
|
|
145
|
+
* sweep does remove it, regenerating the staging from scratch).
|
|
146
|
+
* - The temporary directory is otherwise always removed; a failure to remove
|
|
147
|
+
* it only warns, naming its path.
|
|
148
|
+
*
|
|
149
|
+
* @throws `Error` (no `cause`, no re-run advice) for an invalid plan, as
|
|
150
|
+
* above; `Error` before any delete or write when `groundworkDir` or
|
|
151
|
+
* `<groundworkDir>/baseline` is a symlink; `AggregateError` of the swap and
|
|
152
|
+
* restore failures, naming where the previous baseline is parked, when both
|
|
153
|
+
* renames fail; the `AssertionError` itself, unwrapped, if the staged-path
|
|
154
|
+
* containment invariant ever fails while writing; otherwise an `Error` with
|
|
155
|
+
* `cause`, including the cause's message, saying `.groundwork/` is
|
|
156
|
+
* incomplete and the CLI should be re-run.
|
|
157
|
+
*
|
|
158
|
+
* @param plan - The plan {@link planBaselineStaging} computed for these same
|
|
159
|
+
* `templateRoot`, `conflicts`, `groundworkDir` and `tokens`; computed here
|
|
160
|
+
* when omitted. A plan whose `groundworkDir` resolves to a different
|
|
161
|
+
* directory throws a plain `Error` naming both ("the plan was built for …")
|
|
162
|
+
* before anything is deleted or written.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* // conflicts: the plan `planConflicts` (conflicts.ts) computed for the target
|
|
167
|
+
* const conflicts = planConflicts(templateRoot, targetDir, tokens);
|
|
168
|
+
* const staged = stageBaselineAdditions(templateRoot, conflicts, ".groundwork", tokens);
|
|
169
|
+
* // staged: [{ path: "eslint.config.js", staged: "eslint.config.js.staged", sha256: "…" }, …]
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export declare function stageBaselineAdditions(templateRoot: string, conflicts: readonly FileConflict[], groundworkDir: string, tokens: TokenTable, plan?: BaselineStagingPlan): StagedBaselineFile[];
|
|
173
|
+
//# sourceMappingURL=baseline-stage.d.ts.map
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* Adopt mode's staging of the baseline files a project lacks entirely: the
|
|
5
|
+
* template files a conflict plan marked "absent" are copied verbatim into
|
|
6
|
+
* `.groundwork/baseline/`, each under an inert `<path>.staged` name, so
|
|
7
|
+
* `/customize`'s Step 0 can install them from a self-contained copy rather
|
|
8
|
+
* than from `templateRoot` -- an absolute path that may not exist by the time
|
|
9
|
+
* it runs -- and no toolchain globbing the project ever picks one up.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
import { toPosixPath } from "./assets.js";
|
|
14
|
+
import { isPathContained } from "./emit.js";
|
|
15
|
+
import { STAGED_SUFFIX, assertPlanBuiltFor, clearStaging, collectTemplateFiles, findStagedPathCollision, prepareStaging, stageAtomically, stagedNameFor, writeStagedBytes, } from "./staging.js";
|
|
16
|
+
// Re-exported so existing consumers of this module keep one import site.
|
|
17
|
+
export { STAGED_SUFFIX, stagedNameFor, toPosixPath };
|
|
18
|
+
/**
|
|
19
|
+
* The directory name, under `.groundwork/`, that absent baseline files are
|
|
20
|
+
* staged into.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { join } from "node:path";
|
|
25
|
+
* const stagedDir = join(".groundwork", STAGED_BASELINE_DIR); // ".groundwork/baseline"
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const STAGED_BASELINE_DIR = "baseline";
|
|
29
|
+
/**
|
|
30
|
+
* Pairs every absent conflict with its template source. Four defects are
|
|
31
|
+
* refused, each with its own `Error` (no `cause`) and never skipped: a
|
|
32
|
+
* template with two files mapping to one install path, a `relPath` whose
|
|
33
|
+
* staged name would land outside `destDir` (CWE-22, docs/assurance-case.md),
|
|
34
|
+
* an absent path with no template counterpart, and two staged names that
|
|
35
|
+
* would land on the same file ({@link findStagedPathCollision}: equal once
|
|
36
|
+
* NFC-normalized and case-folded, or one a directory prefix of the other).
|
|
37
|
+
*/
|
|
38
|
+
function planStaging(templateRoot, absent, tokens, destDir) {
|
|
39
|
+
const templateFiles = collectTemplateFiles(templateRoot, tokens);
|
|
40
|
+
const plan = absent.map(({ relPath }) => {
|
|
41
|
+
// No ":" refusal here: that check (a Windows drive letter / ADS) is packs-only, in pack-stage.ts.
|
|
42
|
+
const destPath = join(destDir, stagedNameFor(relPath));
|
|
43
|
+
if (!isPathContained(destPath, destDir)) {
|
|
44
|
+
throw new Error(`stageBaselineAdditions: staged path ${resolve(destPath)} for ${relPath} escapes ${resolve(destDir)}`);
|
|
45
|
+
}
|
|
46
|
+
const sourcePath = templateFiles.get(relPath);
|
|
47
|
+
if (sourcePath === undefined) {
|
|
48
|
+
throw new Error(`absent baseline file ${relPath} has no counterpart under ${templateRoot}`);
|
|
49
|
+
}
|
|
50
|
+
return { path: relPath, sourcePath };
|
|
51
|
+
});
|
|
52
|
+
const collision = findStagedPathCollision(plan.map(({ path }) => toPosixPath(stagedNameFor(path))));
|
|
53
|
+
if (collision !== undefined) {
|
|
54
|
+
throw new Error(`stageBaselineAdditions: two staged baseline files under ${destDir} collide: ${collision}; rename one of the template files under ${templateRoot}`);
|
|
55
|
+
}
|
|
56
|
+
return plan;
|
|
57
|
+
}
|
|
58
|
+
/** The absent conflicts in `conflicts` -- the files a baseline staging copies. */
|
|
59
|
+
function absentConflicts(conflicts) {
|
|
60
|
+
return conflicts.filter((c) => c.status === "absent");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validates the absent conflicts in `conflicts` against `templateRoot` and
|
|
64
|
+
* computes the plan {@link stageBaselineAdditions} writes from: every path
|
|
65
|
+
* under `<groundworkDir>/baseline/` -- one `<path>.staged` per absent
|
|
66
|
+
* conflict -- and each file's template source, so adopt mode can
|
|
67
|
+
* scope-check `paths` before anything under `.groundwork/` is deleted or
|
|
68
|
+
* written and then hand the same plan to {@link stageBaselineAdditions}.
|
|
69
|
+
* Reads the template tree (not at all when nothing is absent); writes
|
|
70
|
+
* nothing. Both arrays are empty when nothing is absent.
|
|
71
|
+
*
|
|
72
|
+
* @throws The same plan `Error`s as {@link stageBaselineAdditions}: a
|
|
73
|
+
* template with two files installing to one path, an absent conflict with no
|
|
74
|
+
* template counterpart or whose staged name would escape the staging
|
|
75
|
+
* directory, or two colliding staged names.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { assertAdoptWriteScope } from "./main.js";
|
|
80
|
+
* const plan = planBaselineStaging(templateRoot, conflicts, groundworkDir, tokens);
|
|
81
|
+
* assertAdoptWriteScope(targetDir, plan.paths);
|
|
82
|
+
* stageBaselineAdditions(templateRoot, conflicts, groundworkDir, tokens, plan);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function planBaselineStaging(templateRoot, conflicts, groundworkDir, tokens) {
|
|
86
|
+
const absent = absentConflicts(conflicts);
|
|
87
|
+
if (absent.length === 0) {
|
|
88
|
+
return { groundworkDir, paths: [], files: [] };
|
|
89
|
+
}
|
|
90
|
+
const destDir = join(groundworkDir, STAGED_BASELINE_DIR);
|
|
91
|
+
const files = planStaging(templateRoot, absent, tokens, destDir);
|
|
92
|
+
const paths = files.map(({ path }) => join(destDir, stagedNameFor(path)));
|
|
93
|
+
return { groundworkDir, paths, files };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Every path {@link stageBaselineAdditions} would write for `conflicts` -- a
|
|
97
|
+
* thin wrapper returning {@link planBaselineStaging}'s `paths`. Reads the
|
|
98
|
+
* template tree; writes nothing. Returns `[]` when nothing is absent.
|
|
99
|
+
*
|
|
100
|
+
* @throws The same plan `Error`s as {@link planBaselineStaging}.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { assertAdoptWriteScope } from "./main.js";
|
|
105
|
+
* const paths = plannedBaselineStagingPaths(templateRoot, conflicts, groundworkDir, tokens);
|
|
106
|
+
* assertAdoptWriteScope(targetDir, paths);
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export function plannedBaselineStagingPaths(templateRoot, conflicts, groundworkDir, tokens) {
|
|
110
|
+
return [
|
|
111
|
+
...planBaselineStaging(templateRoot, conflicts, groundworkDir, tokens)
|
|
112
|
+
.paths,
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Copies every template file `conflicts` marks "absent" from `templateRoot`
|
|
117
|
+
* into `<groundworkDir>/baseline/` as `<path>.staged`, byte-for-byte -- no
|
|
118
|
+
* token substitution into content, which is `/customize`'s job at install
|
|
119
|
+
* time. `tokens` is used only to match a tokenized template path
|
|
120
|
+
* (`__PROJECT_NAME__.txt`) to the substituted `relPath` the conflict plan
|
|
121
|
+
* reports. Recorded `path`/`staged` values use forward slashes
|
|
122
|
+
* ({@link toPosixPath}).
|
|
123
|
+
*
|
|
124
|
+
* What is guaranteed:
|
|
125
|
+
* - The plan is validated first, before anything is deleted or written (or,
|
|
126
|
+
* when `plan` is passed, was already validated by
|
|
127
|
+
* {@link planBaselineStaging} and the template tree is not walked again): a
|
|
128
|
+
* template with two files installing to the same path (a dotfile-escaped
|
|
129
|
+
* name beside its literal twin), an absent conflict with no template
|
|
130
|
+
* counterpart or whose staged name would escape the staging directory, or
|
|
131
|
+
* two staged names that would land on the same file (equal once
|
|
132
|
+
* NFC-normalized and case-folded, or one a directory prefix of the other)
|
|
133
|
+
* throws its own
|
|
134
|
+
* `Error`, leaving `.groundwork/` exactly as it was.
|
|
135
|
+
* - Then, still before anything is deleted or written, `groundworkDir` and
|
|
136
|
+
* `<groundworkDir>/baseline` are checked not to be symlinks, and every
|
|
137
|
+
* `.baseline-*` entry of the CLI-owned `groundworkDir` -- meant for work
|
|
138
|
+
* directories a crashed earlier run left, but removed whatever created
|
|
139
|
+
* it, so a concurrent run against the same directory can lose its
|
|
140
|
+
* in-progress work directory and fail with the incomplete/re-run error
|
|
141
|
+
* -- is removed (best effort; a failure to remove one only warns, a
|
|
142
|
+
* failure to list `groundworkDir` throws).
|
|
143
|
+
* - When nothing is absent, any previous staging is removed and nothing is
|
|
144
|
+
* created.
|
|
145
|
+
* - Files are then written into a temporary `.baseline-*` sibling directory
|
|
146
|
+
* and swapped in by rename only after every copy succeeded, so
|
|
147
|
+
* `baseline/` is never half-written and a copy failure leaves any previous
|
|
148
|
+
* `baseline/` intact. A previous staging is replaced wholesale (no stale
|
|
149
|
+
* file lingers).
|
|
150
|
+
* - With a previous `baseline/`, the swap is two renames: the previous one
|
|
151
|
+
* is parked inside the temporary directory, then the new one moved into
|
|
152
|
+
* place. A process killed between the two leaves `baseline/` absent and
|
|
153
|
+
* the previous copy at `.baseline-XXXXXX/previous`; the next run's sweep
|
|
154
|
+
* removes it and regenerates the staging.
|
|
155
|
+
* - If the final swap rename fails, the previous `baseline/` is renamed back
|
|
156
|
+
* into place. Only if that restore also fails is `baseline/` left absent:
|
|
157
|
+
* the previous staging then survives, parked inside the temporary
|
|
158
|
+
* directory, which is deliberately not removed (a later run's stale-dir
|
|
159
|
+
* sweep does remove it, regenerating the staging from scratch).
|
|
160
|
+
* - The temporary directory is otherwise always removed; a failure to remove
|
|
161
|
+
* it only warns, naming its path.
|
|
162
|
+
*
|
|
163
|
+
* @throws `Error` (no `cause`, no re-run advice) for an invalid plan, as
|
|
164
|
+
* above; `Error` before any delete or write when `groundworkDir` or
|
|
165
|
+
* `<groundworkDir>/baseline` is a symlink; `AggregateError` of the swap and
|
|
166
|
+
* restore failures, naming where the previous baseline is parked, when both
|
|
167
|
+
* renames fail; the `AssertionError` itself, unwrapped, if the staged-path
|
|
168
|
+
* containment invariant ever fails while writing; otherwise an `Error` with
|
|
169
|
+
* `cause`, including the cause's message, saying `.groundwork/` is
|
|
170
|
+
* incomplete and the CLI should be re-run.
|
|
171
|
+
*
|
|
172
|
+
* @param plan - The plan {@link planBaselineStaging} computed for these same
|
|
173
|
+
* `templateRoot`, `conflicts`, `groundworkDir` and `tokens`; computed here
|
|
174
|
+
* when omitted. A plan whose `groundworkDir` resolves to a different
|
|
175
|
+
* directory throws a plain `Error` naming both ("the plan was built for …")
|
|
176
|
+
* before anything is deleted or written.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* // conflicts: the plan `planConflicts` (conflicts.ts) computed for the target
|
|
181
|
+
* const conflicts = planConflicts(templateRoot, targetDir, tokens);
|
|
182
|
+
* const staged = stageBaselineAdditions(templateRoot, conflicts, ".groundwork", tokens);
|
|
183
|
+
* // staged: [{ path: "eslint.config.js", staged: "eslint.config.js.staged", sha256: "…" }, …]
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export function stageBaselineAdditions(templateRoot, conflicts, groundworkDir, tokens,
|
|
187
|
+
// Defaulted before the symlink check and stale-dir sweep below, so an
|
|
188
|
+
// invalid plan -- a template defect, with its own message rather than the
|
|
189
|
+
// "incomplete, re-run" staging failure -- deletes and writes nothing.
|
|
190
|
+
plan = planBaselineStaging(templateRoot, conflicts, groundworkDir, tokens)) {
|
|
191
|
+
assertPlanBuiltFor("stageBaselineAdditions", plan.groundworkDir, groundworkDir);
|
|
192
|
+
const target = {
|
|
193
|
+
groundworkDir,
|
|
194
|
+
dirName: STAGED_BASELINE_DIR,
|
|
195
|
+
noun: "baseline",
|
|
196
|
+
plural: false,
|
|
197
|
+
};
|
|
198
|
+
prepareStaging(target);
|
|
199
|
+
if (plan.files.length === 0) {
|
|
200
|
+
clearStaging(target);
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
return stageAtomically(target, (newDir) => plan.files.map(({ path, sourcePath }) => {
|
|
204
|
+
const stagedName = stagedNameFor(path);
|
|
205
|
+
// writeStagedBytes re-asserts the CWE-22 containment planStaging
|
|
206
|
+
// already checked, against the directory actually written to.
|
|
207
|
+
const sha256 = writeStagedBytes("stageBaselineAdditions", readFileSync(sourcePath), newDir, stagedName);
|
|
208
|
+
return {
|
|
209
|
+
path: toPosixPath(path),
|
|
210
|
+
staged: toPosixPath(stagedName),
|
|
211
|
+
sha256,
|
|
212
|
+
};
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=baseline-stage.js.map
|
package/dist/caps.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export declare function countBaselineCaps(templateRoot: string): CapCounts;
|
|
|
20
20
|
* the same shape as {@link countBaselineCaps} but without the `/customize`
|
|
21
21
|
* adjustment, since a pack never ships that skill. Used to verify a pack's
|
|
22
22
|
* declared `budget` matches what its own file tree actually contains.
|
|
23
|
+
* `packageScripts` is the pack's `wiring.packageScripts`, added to the
|
|
24
|
+
* `scripts` count -- those are merged into the target's real `package.json`
|
|
25
|
+
* at install time, never shipped as a file, so the tree walk can't see them.
|
|
23
26
|
*/
|
|
24
|
-
export declare function countPackBudget(packFilesDir: string): CapCounts;
|
|
27
|
+
export declare function countPackBudget(packFilesDir: string, packageScripts?: Record<string, string>): CapCounts;
|
|
25
28
|
//# sourceMappingURL=caps.d.ts.map
|
package/dist/caps.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
1
3
|
/**
|
|
2
4
|
* Counts the baseline's own `.claude/` + workflow + script artifacts
|
|
3
5
|
* against the five hard caps `templates/core`'s own `CLAUDE.md` states
|
|
4
6
|
* (≤5 agents, ≤8 skills, ≤10 hooks, ≤3 CI workflows, ≤12 root scripts).
|
|
5
|
-
*
|
|
7
|
+
* A cap is a fixed upper bound on how many artifacts of one category
|
|
8
|
+
* (agents, skills, hooks, CI workflows, root `package.json` scripts) the
|
|
9
|
+
* emitted baseline may contain -- kept deliberately low so a bootstrapped
|
|
10
|
+
* project's Claude Code harness and toolchain stay small enough for a human
|
|
11
|
+
* to read and reason about in full, rather than growing without bound as
|
|
12
|
+
* more capability gets added. The counting logic and the cap numbers both live here, once, so
|
|
6
13
|
* `report.ts` (the adoption-report table) and `main.ts` (the fresh-mode
|
|
7
14
|
* post-install summary) can't state a different number for the same cap.
|
|
8
15
|
*/
|
|
@@ -62,8 +69,15 @@ export function countBaselineCaps(templateRoot) {
|
|
|
62
69
|
* the same shape as {@link countBaselineCaps} but without the `/customize`
|
|
63
70
|
* adjustment, since a pack never ships that skill. Used to verify a pack's
|
|
64
71
|
* declared `budget` matches what its own file tree actually contains.
|
|
72
|
+
* `packageScripts` is the pack's `wiring.packageScripts`, added to the
|
|
73
|
+
* `scripts` count -- those are merged into the target's real `package.json`
|
|
74
|
+
* at install time, never shipped as a file, so the tree walk can't see them.
|
|
65
75
|
*/
|
|
66
|
-
export function countPackBudget(packFilesDir) {
|
|
67
|
-
|
|
76
|
+
export function countPackBudget(packFilesDir, packageScripts) {
|
|
77
|
+
const raw = countArtifacts(packFilesDir);
|
|
78
|
+
return {
|
|
79
|
+
...raw,
|
|
80
|
+
scripts: raw.scripts + Object.keys(packageScripts ?? {}).length,
|
|
81
|
+
};
|
|
68
82
|
}
|
|
69
83
|
//# sourceMappingURL=caps.js.map
|
package/dist/conflicts.d.ts
CHANGED
|
@@ -6,7 +6,28 @@ export interface FileConflict {
|
|
|
6
6
|
/** Present only for a key-level comparison (package.json / tsconfig*.json): the top-level keys that differ. */
|
|
7
7
|
keyDiffs: string[] | undefined;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Compares every file `templates/core` (or a pack's `files/`) would emit
|
|
11
|
+
* against what `targetDir` already has. A target path this process cannot
|
|
12
|
+
* reach (`EACCES`/`EPERM`/`ELOOP`) is never reported `absent`: it is
|
|
13
|
+
* `divergent`, and a note naming the errno is appended to `undetermined`
|
|
14
|
+
* once (adopt mode passes the survey's own list, so the report shows it) --
|
|
15
|
+
* naming the enclosing directory for a permission failure on the `stat`, the
|
|
16
|
+
* path itself for a symlink loop, or for a permission failure or a
|
|
17
|
+
* directory-where-a-file-was-expected (`EISDIR`) on the read. A dangling
|
|
18
|
+
* symlink at the target path is `divergent` too, recorded naming the path;
|
|
19
|
+
* so is a dangling symlink at one of its ancestors below `targetDir`,
|
|
20
|
+
* recorded once naming that ancestor. A regular file where an enclosing
|
|
21
|
+
* directory should be (`ENOTDIR`) is `divergent`, recorded once naming that
|
|
22
|
+
* blocking ancestor. A genuinely missing path is `absent`; any other errno
|
|
23
|
+
* throws.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const undetermined: string[] = [];
|
|
28
|
+
* const conflicts = planConflicts(templateRoot, targetDir, tokens, undetermined);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function planConflicts(templateRoot: string, targetDir: string, tokens: TokenTable, undetermined?: string[]): FileConflict[];
|
|
11
32
|
export {};
|
|
12
33
|
//# sourceMappingURL=conflicts.d.ts.map
|